Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.12 257 lines 5.8 kB view raw
1/* 2 * File...........: linux/include/asm-s390x/idals.h 3 * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> 4 * Martin Schwidefsky <schwidefsky@de.ibm.com> 5 * Bugreports.to..: <Linux390@de.ibm.com> 6 * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000a 7 8 * History of changes 9 * 07/24/00 new file 10 * 05/04/02 code restructuring. 11 */ 12 13#ifndef _S390_IDALS_H 14#define _S390_IDALS_H 15 16#include <linux/config.h> 17#include <linux/errno.h> 18#include <linux/err.h> 19#include <linux/types.h> 20#include <linux/slab.h> 21#include <asm/cio.h> 22#include <asm/uaccess.h> 23 24#ifdef __s390x__ 25#define IDA_SIZE_LOG 12 /* 11 for 2k , 12 for 4k */ 26#else 27#define IDA_SIZE_LOG 11 /* 11 for 2k , 12 for 4k */ 28#endif 29#define IDA_BLOCK_SIZE (1L<<IDA_SIZE_LOG) 30 31/* 32 * Test if an address/length pair needs an idal list. 33 */ 34static inline int 35idal_is_needed(void *vaddr, unsigned int length) 36{ 37#ifdef __s390x__ 38 return ((__pa(vaddr) + length - 1) >> 31) != 0; 39#else 40 return 0; 41#endif 42} 43 44 45/* 46 * Return the number of idal words needed for an address/length pair. 47 */ 48static inline unsigned int 49idal_nr_words(void *vaddr, unsigned int length) 50{ 51#ifdef __s390x__ 52 if (idal_is_needed(vaddr, length)) 53 return ((__pa(vaddr) & (IDA_BLOCK_SIZE-1)) + length + 54 (IDA_BLOCK_SIZE-1)) >> IDA_SIZE_LOG; 55#endif 56 return 0; 57} 58 59/* 60 * Create the list of idal words for an address/length pair. 61 */ 62static inline unsigned long * 63idal_create_words(unsigned long *idaws, void *vaddr, unsigned int length) 64{ 65#ifdef __s390x__ 66 unsigned long paddr; 67 unsigned int cidaw; 68 69 paddr = __pa(vaddr); 70 cidaw = ((paddr & (IDA_BLOCK_SIZE-1)) + length + 71 (IDA_BLOCK_SIZE-1)) >> IDA_SIZE_LOG; 72 *idaws++ = paddr; 73 paddr &= -IDA_BLOCK_SIZE; 74 while (--cidaw > 0) { 75 paddr += IDA_BLOCK_SIZE; 76 *idaws++ = paddr; 77 } 78#endif 79 return idaws; 80} 81 82/* 83 * Sets the address of the data in CCW. 84 * If necessary it allocates an IDAL and sets the appropriate flags. 85 */ 86static inline int 87set_normalized_cda(struct ccw1 * ccw, void *vaddr) 88{ 89#ifdef __s390x__ 90 unsigned int nridaws; 91 unsigned long *idal; 92 93 if (ccw->flags & CCW_FLAG_IDA) 94 return -EINVAL; 95 nridaws = idal_nr_words(vaddr, ccw->count); 96 if (nridaws > 0) { 97 idal = kmalloc(nridaws * sizeof(unsigned long), 98 GFP_ATOMIC | GFP_DMA ); 99 if (idal == NULL) 100 return -ENOMEM; 101 idal_create_words(idal, vaddr, ccw->count); 102 ccw->flags |= CCW_FLAG_IDA; 103 vaddr = idal; 104 } 105#endif 106 ccw->cda = (__u32)(unsigned long) vaddr; 107 return 0; 108} 109 110/* 111 * Releases any allocated IDAL related to the CCW. 112 */ 113static inline void 114clear_normalized_cda(struct ccw1 * ccw) 115{ 116#ifdef __s390x__ 117 if (ccw->flags & CCW_FLAG_IDA) { 118 kfree((void *)(unsigned long) ccw->cda); 119 ccw->flags &= ~CCW_FLAG_IDA; 120 } 121#endif 122 ccw->cda = 0; 123} 124 125/* 126 * Idal buffer extension 127 */ 128struct idal_buffer { 129 size_t size; 130 size_t page_order; 131 void *data[0]; 132}; 133 134/* 135 * Allocate an idal buffer 136 */ 137static inline struct idal_buffer * 138idal_buffer_alloc(size_t size, int page_order) 139{ 140 struct idal_buffer *ib; 141 int nr_chunks, nr_ptrs, i; 142 143 nr_ptrs = (size + IDA_BLOCK_SIZE - 1) >> IDA_SIZE_LOG; 144 nr_chunks = (4096 << page_order) >> IDA_SIZE_LOG; 145 ib = kmalloc(sizeof(struct idal_buffer) + nr_ptrs*sizeof(void *), 146 GFP_DMA | GFP_KERNEL); 147 if (ib == NULL) 148 return ERR_PTR(-ENOMEM); 149 ib->size = size; 150 ib->page_order = page_order; 151 for (i = 0; i < nr_ptrs; i++) { 152 if ((i & (nr_chunks - 1)) != 0) { 153 ib->data[i] = ib->data[i-1] + IDA_BLOCK_SIZE; 154 continue; 155 } 156 ib->data[i] = (void *) 157 __get_free_pages(GFP_KERNEL, page_order); 158 if (ib->data[i] != NULL) 159 continue; 160 // Not enough memory 161 while (i >= nr_chunks) { 162 i -= nr_chunks; 163 free_pages((unsigned long) ib->data[i], 164 ib->page_order); 165 } 166 kfree(ib); 167 return ERR_PTR(-ENOMEM); 168 } 169 return ib; 170} 171 172/* 173 * Free an idal buffer. 174 */ 175static inline void 176idal_buffer_free(struct idal_buffer *ib) 177{ 178 int nr_chunks, nr_ptrs, i; 179 180 nr_ptrs = (ib->size + IDA_BLOCK_SIZE - 1) >> IDA_SIZE_LOG; 181 nr_chunks = (4096 << ib->page_order) >> IDA_SIZE_LOG; 182 for (i = 0; i < nr_ptrs; i += nr_chunks) 183 free_pages((unsigned long) ib->data[i], ib->page_order); 184 kfree(ib); 185} 186 187/* 188 * Test if a idal list is really needed. 189 */ 190static inline int 191__idal_buffer_is_needed(struct idal_buffer *ib) 192{ 193#ifdef __s390x__ 194 return ib->size > (4096ul << ib->page_order) || 195 idal_is_needed(ib->data[0], ib->size); 196#else 197 return ib->size > (4096ul << ib->page_order); 198#endif 199} 200 201/* 202 * Set channel data address to idal buffer. 203 */ 204static inline void 205idal_buffer_set_cda(struct idal_buffer *ib, struct ccw1 *ccw) 206{ 207 if (__idal_buffer_is_needed(ib)) { 208 // setup idals; 209 ccw->cda = (u32)(addr_t) ib->data; 210 ccw->flags |= CCW_FLAG_IDA; 211 } else 212 // we do not need idals - use direct addressing 213 ccw->cda = (u32)(addr_t) ib->data[0]; 214 ccw->count = ib->size; 215} 216 217/* 218 * Copy count bytes from an idal buffer to user memory 219 */ 220static inline size_t 221idal_buffer_to_user(struct idal_buffer *ib, void __user *to, size_t count) 222{ 223 size_t left; 224 int i; 225 226 BUG_ON(count > ib->size); 227 for (i = 0; count > IDA_BLOCK_SIZE; i++) { 228 left = copy_to_user(to, ib->data[i], IDA_BLOCK_SIZE); 229 if (left) 230 return left + count - IDA_BLOCK_SIZE; 231 to = (void __user *) to + IDA_BLOCK_SIZE; 232 count -= IDA_BLOCK_SIZE; 233 } 234 return copy_to_user(to, ib->data[i], count); 235} 236 237/* 238 * Copy count bytes from user memory to an idal buffer 239 */ 240static inline size_t 241idal_buffer_from_user(struct idal_buffer *ib, const void __user *from, size_t count) 242{ 243 size_t left; 244 int i; 245 246 BUG_ON(count > ib->size); 247 for (i = 0; count > IDA_BLOCK_SIZE; i++) { 248 left = copy_from_user(ib->data[i], from, IDA_BLOCK_SIZE); 249 if (left) 250 return left + count - IDA_BLOCK_SIZE; 251 from = (void __user *) from + IDA_BLOCK_SIZE; 252 count -= IDA_BLOCK_SIZE; 253 } 254 return copy_from_user(ib->data[i], from, count); 255} 256 257#endif