Viewing file: ccwcache.c (8.41 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
/* * File...........: linux/drivers/s390/ccwcache.c * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> * Martin Schiwdefsky <schwidefsky@de.ibm.com> * Bugreports.to..: <Linux390@de.ibm.com> * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000a * History of changes * 11/14/00 redesign by Martin Schwidefsky */
#include <linux/module.h> #include <linux/slab.h> #include <linux/version.h>
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98)) #include <linux/spinlock.h> #else #include <asm/spinlock.h> #endif
#include <asm/debug.h> #include <asm/ccwcache.h> #include <asm/ebcdic.h>
#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98)) #define CCW_CACHE_SLAB_TYPE (SLAB_HWCACHE_ALIGN | SLAB_CACHE_DMA) #define CCW_CACHE_TYPE (GFP_ATOMIC | GFP_DMA) #else #define CCW_CACHE_SLAB_TYPE (SLAB_HWCACHE_ALIGN) #define CCW_CACHE_TYPE (GFP_ATOMIC) #define kmem_cache_destroy(x) do {} while(0) #endif
#undef PRINTK_HEADER #define PRINTK_HEADER "ccwcache"
/* pointer to list of allocated requests */ static ccw_req_t *ccwreq_actual = NULL; static spinlock_t ccwchain_lock;
/* pointer to debug area */ static debug_info_t *debug_area = NULL;
/* SECTION: Handling of the dynamically allocated kmem slabs */
/* a name template for the cache-names */ static char ccw_name_template[] = "ccwcache-\0\0\0\0"; /* fill name with zeroes! */ /* the cache's names */ static char ccw_cache_name[CCW_NUMBER_CACHES][sizeof(ccw_name_template)+1]; /* the caches itself*/ static kmem_cache_t *ccw_cache[CCW_NUMBER_CACHES];
/* SECTION: (de)allocation of ccw_req_t */
/* * void enchain ( ccw_req_t *request ) * enchains the request to the ringbuffer */ static inline void enchain ( ccw_req_t *request ) { unsigned long flags;
/* Sanity checks */ if ( request == NULL ) BUG(); spin_lock_irqsave(&ccwchain_lock,flags); if ( ccwreq_actual == NULL ) { /* queue empty */ ccwreq_actual = request; request->int_prev = ccwreq_actual; request->int_next = ccwreq_actual; } else { request->int_next = ccwreq_actual; request->int_prev = ccwreq_actual->int_prev; request->int_prev->int_next = request; request->int_next->int_prev = request; } spin_unlock_irqrestore(&ccwchain_lock,flags); }
/* * void dechain ( ccw_req_t *request ) * dechains the request from the ringbuffer */ static inline void dechain ( ccw_req_t *request ) { unsigned long flags;
/* Sanity checks */ if ( request == NULL || request->int_next == NULL || request->int_prev == NULL) BUG(); /* first deallocate request from list of allocates requests */ spin_lock_irqsave(&ccwchain_lock,flags); if ( request -> int_next == request -> int_prev ) { ccwreq_actual = NULL; } else { if ( ccwreq_actual == request ) { ccwreq_actual = request->int_next; } request->int_prev->int_next = request->int_next; request->int_next->int_prev = request->int_prev; } spin_unlock_irqrestore(&ccwchain_lock,flags); }
/* * ccw_req_t *ccw_alloc_request ( int cplength, int datasize ) * allocates a ccw_req_t, that * - can hold a CP of cplength CCWS * - can hold additional data up to datasize */ ccw_req_t * ccw_alloc_request ( char *magic, int cplength, int datasize ) { ccw_req_t * request = NULL; int size_needed; int data_offset, ccw_offset; int cachind;
/* Sanity checks */ if ( magic == NULL || datasize > PAGE_SIZE || cplength == 0 || (cplength*sizeof(ccw1_t)) > PAGE_SIZE) BUG(); debug_text_event ( debug_area, 1, "ALLC"); debug_text_event ( debug_area, 1, magic); debug_int_event ( debug_area, 1, cplength); debug_int_event ( debug_area, 1, datasize);
/* We try to keep things together in memory */ size_needed = (sizeof (ccw_req_t) + 7) & -8; data_offset = ccw_offset = 0; if (size_needed + datasize <= PAGE_SIZE) { /* Keep data with the request */ data_offset = size_needed; size_needed += (datasize + 7) & -8; } if (size_needed + cplength*sizeof(ccw1_t) <= PAGE_SIZE) { /* Keep CCWs with request */ ccw_offset = size_needed; size_needed += cplength*sizeof(ccw1_t); }
/* determine cache index for the requested size */ for (cachind = 0; cachind < CCW_NUMBER_CACHES; cachind ++ ) if ( size_needed < (SMALLEST_SLAB << cachind) ) break;
/* Try to fulfill the request from a cache */ if ( ccw_cache[cachind] == NULL ) BUG(); request = kmem_cache_alloc ( ccw_cache[cachind], CCW_CACHE_TYPE ); if (request == NULL) return NULL; memset ( request, 0, (SMALLEST_SLAB << cachind)); request->cache = ccw_cache[cachind];
/* Allocate memory for the extra data */ if (data_offset == 0) { /* Allocated memory for extra data with kmalloc */ request->data = (void *) kmalloc(datasize, CCW_CACHE_TYPE ); if (request->data == NULL) { printk(KERN_WARNING PRINTK_HEADER "Couldn't allocate data area\n"); kmem_cache_free(request->cache, request); return NULL; } } else /* Extra data already allocated with the request */ request->data = (void *) ((addr_t) request + data_offset);
/* Allocate memory for the channel program */ if (ccw_offset == 0) { /* Allocated memory for the channel program with kmalloc */ request->cpaddr = (ccw1_t *) kmalloc(cplength*sizeof(ccw1_t), CCW_CACHE_TYPE); if (request->cpaddr == NULL) { printk (KERN_DEBUG PRINTK_HEADER "Couldn't allocate ccw area\n"); if (data_offset == 0) kfree(request->data); kmem_cache_free(request->cache, request); return NULL; } } else /* Channel program already allocated with the request */ request->cpaddr = (ccw1_t *) ((addr_t) request + ccw_offset);
memset ( request->data, 0, datasize ); memset ( request->cpaddr, 0, cplength*sizeof(ccw1_t) ); strncpy ( (char *)(&request->magic), magic, 4);
ASCEBC((char *)(&request->magic),4); request -> cplength = cplength; request -> datasize = datasize; /* enqueue request to list of allocated requests */ enchain(request); debug_int_event ( debug_area, 1, (long)request); return request; }
/* * void ccw_free_request ( ccw_req_t * ) * deallocates the ccw_req_t, given as argument */
void ccw_free_request ( ccw_req_t * request ) { int size_needed;
debug_text_event ( debug_area, 1, "FREE"); debug_int_event ( debug_area, 1, (long)request);
/* Sanity checks */ if ( request == NULL || request->cache == NULL) BUG();
dechain ( request); /* Free memory allocated with kmalloc * make the same decisions as in ccw_alloc_requets */ size_needed = (sizeof (ccw_req_t) + 7) & -8; if (size_needed + request->datasize <= PAGE_SIZE) /* We kept the data with the request */ size_needed += (request->datasize + 7) & -8; else kfree(request->data); if (size_needed + request->cplength*sizeof(ccw1_t) > PAGE_SIZE) /* We kept the CCWs with request */ kfree(request->cpaddr); kmem_cache_free(request -> cache, request); }
/* SECTION: initialization and cleanup functions */
/* * ccwcache_init * called as an initializer function for the ccw memory management */
int ccwcache_init (void) { int rc = 0; int cachind;
/* initialize variables */ spin_lock_init(&ccwchain_lock);
/* allocate a debug area */ debug_area = debug_register( "ccwcache", 2, 4,sizeof(void*)); if ( debug_area == NULL ) BUG();
debug_register_view(debug_area,&debug_hex_ascii_view); debug_register_view(debug_area,&debug_raw_view); debug_text_event ( debug_area, 0, "INIT");
/* First allocate the kmem caches */ for ( cachind = 0; cachind < CCW_NUMBER_CACHES; cachind ++ ) { int slabsize = SMALLEST_SLAB << cachind; debug_text_event ( debug_area, 1, "allc"); debug_int_event ( debug_area, 1, slabsize); sprintf ( ccw_cache_name[cachind], "%s%d%c", ccw_name_template, slabsize, 0); ccw_cache[cachind] = kmem_cache_create( ccw_cache_name[cachind], slabsize, 0, CCW_CACHE_SLAB_TYPE, NULL, NULL ); debug_int_event ( debug_area, 1, (long)ccw_cache[cachind]); if (ccw_cache[cachind] == NULL) panic ("Allocation of CCW cache failed\n"); } return rc; }
/* * ccwcache_cleanup * called as a cleanup function for the ccw memory management */
void ccwcache_cleanup (void) { int cachind;
/* Shrink the caches, if available */ for ( cachind = 0; cachind < CCW_NUMBER_CACHES; cachind ++ ) { if ( ccw_cache[cachind] ) { if ( kmem_cache_shrink(ccw_cache[cachind]) == 0 ) { ccw_cache[cachind] = NULL; } kmem_cache_destroy(ccw_cache[cachind]); } } debug_unregister( debug_area ); }
EXPORT_SYMBOL(ccw_alloc_request); EXPORT_SYMBOL(ccw_free_request);
|