linux/arch/s390/include/asm/idals.h
<<
>>
Prefs
   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 */
  29static inline int
  30idal_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 */
  39static 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 */
  48static 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 */
  70static inline int
  71set_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 */
  95static inline void
  96clear_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 */
 108struct idal_buffer {
 109        size_t size;
 110        size_t page_order;
 111        void *data[0];
 112};
 113
 114/*
 115 * Allocate an idal buffer
 116 */
 117static inline struct idal_buffer *
 118idal_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 */
 154static inline void
 155idal_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 */
 169static 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 */
 179static inline void
 180idal_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 */
 195static inline size_t
 196idal_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 */
 215static inline size_t
 216idal_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
 233