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(sizeof(struct idal_buffer) + nr_ptrs*sizeof(void *),
 126                     GFP_DMA | GFP_KERNEL);
 127        if (ib == NULL)
 128                return ERR_PTR(-ENOMEM);
 129        ib->size = size;
 130        ib->page_order = page_order;
 131        for (i = 0; i < nr_ptrs; i++) {
 132                if ((i & (nr_chunks - 1)) != 0) {
 133                        ib->data[i] = ib->data[i-1] + IDA_BLOCK_SIZE;
 134                        continue;
 135                }
 136                ib->data[i] = (void *)
 137                        __get_free_pages(GFP_KERNEL, page_order);
 138                if (ib->data[i] != NULL)
 139                        continue;
 140                // Not enough memory
 141                while (i >= nr_chunks) {
 142                        i -= nr_chunks;
 143                        free_pages((unsigned long) ib->data[i],
 144                                   ib->page_order);
 145                }
 146                kfree(ib);
 147                return ERR_PTR(-ENOMEM);
 148        }
 149        return ib;
 150}
 151
 152/*
 153 * Free an idal buffer.
 154 */
 155static inline void
 156idal_buffer_free(struct idal_buffer *ib)
 157{
 158        int nr_chunks, nr_ptrs, i;
 159
 160        nr_ptrs = (ib->size + IDA_BLOCK_SIZE - 1) >> IDA_SIZE_LOG;
 161        nr_chunks = (4096 << ib->page_order) >> IDA_SIZE_LOG;
 162        for (i = 0; i < nr_ptrs; i += nr_chunks)
 163                free_pages((unsigned long) ib->data[i], ib->page_order);
 164        kfree(ib);
 165}
 166
 167/*
 168 * Test if a idal list is really needed.
 169 */
 170static inline int
 171__idal_buffer_is_needed(struct idal_buffer *ib)
 172{
 173        return ib->size > (4096ul << ib->page_order) ||
 174                idal_is_needed(ib->data[0], ib->size);
 175}
 176
 177/*
 178 * Set channel data address to idal buffer.
 179 */
 180static inline void
 181idal_buffer_set_cda(struct idal_buffer *ib, struct ccw1 *ccw)
 182{
 183        if (__idal_buffer_is_needed(ib)) {
 184                // setup idals;
 185                ccw->cda = (u32)(addr_t) ib->data;
 186                ccw->flags |= CCW_FLAG_IDA;
 187        } else
 188                // we do not need idals - use direct addressing
 189                ccw->cda = (u32)(addr_t) ib->data[0];
 190        ccw->count = ib->size;
 191}
 192
 193/*
 194 * Copy count bytes from an idal buffer to user memory
 195 */
 196static inline size_t
 197idal_buffer_to_user(struct idal_buffer *ib, void __user *to, size_t count)
 198{
 199        size_t left;
 200        int i;
 201
 202        BUG_ON(count > ib->size);
 203        for (i = 0; count > IDA_BLOCK_SIZE; i++) {
 204                left = copy_to_user(to, ib->data[i], IDA_BLOCK_SIZE);
 205                if (left)
 206                        return left + count - IDA_BLOCK_SIZE;
 207                to = (void __user *) to + IDA_BLOCK_SIZE;
 208                count -= IDA_BLOCK_SIZE;
 209        }
 210        return copy_to_user(to, ib->data[i], count);
 211}
 212
 213/*
 214 * Copy count bytes from user memory to an idal buffer
 215 */
 216static inline size_t
 217idal_buffer_from_user(struct idal_buffer *ib, const void __user *from, size_t count)
 218{
 219        size_t left;
 220        int i;
 221
 222        BUG_ON(count > ib->size);
 223        for (i = 0; count > IDA_BLOCK_SIZE; i++) {
 224                left = copy_from_user(ib->data[i], from, IDA_BLOCK_SIZE);
 225                if (left)
 226                        return left + count - IDA_BLOCK_SIZE;
 227                from = (void __user *) from + IDA_BLOCK_SIZE;
 228                count -= IDA_BLOCK_SIZE;
 229        }
 230        return copy_from_user(ib->data[i], from, count);
 231}
 232
 233#endif
 234