root/arch/s390/include/asm/idals.h

/* [<][>][^][v][top][bottom][index][help] */

INCLUDED FROM


DEFINITIONS

This source file includes following definitions.
  1. idal_is_needed
  2. idal_nr_words
  3. idal_create_words
  4. set_normalized_cda
  5. clear_normalized_cda
  6. idal_buffer_alloc
  7. idal_buffer_free
  8. __idal_buffer_is_needed
  9. idal_buffer_set_cda
  10. idal_buffer_to_user
  11. idal_buffer_from_user

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

/* [<][>][^][v][top][bottom][index][help] */