patch-2.4.2 linux/drivers/s390/ccwcache.c
Next file: linux/drivers/s390/char/Makefile
Previous file: linux/drivers/s390/block/xpram.h
Back to the patch index
Back to the overall index
- Lines: 304
- Date:
Tue Feb 13 14:13:44 2001
- Orig file:
v2.4.1/linux/drivers/s390/ccwcache.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.4.1/linux/drivers/s390/ccwcache.c linux/drivers/s390/ccwcache.c
@@ -0,0 +1,303 @@
+/*
+ * 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/malloc.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
+
+#ifdef PRINTK_HEADER
+#undef PRINTK_HEADER
+#endif
+#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 );
+}
+
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)