File: /usr/src/linux/drivers/s390/ccwcache.c

1     /* 
2      * File...........: linux/drivers/s390/ccwcache.c
3      * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
4      *                  Martin Schiwdefsky <schwidefsky@de.ibm.com>
5      * Bugreports.to..: <Linux390@de.ibm.com>
6      * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000a
7      
8      * History of changes
9      * 11/14/00 redesign by Martin Schwidefsky
10      */
11     
12     #include <linux/module.h>
13     #include <linux/slab.h>
14     #include <linux/version.h>
15     
16     #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
17     #include <linux/spinlock.h>
18     #else
19     #include <asm/spinlock.h>
20     #endif
21     
22     #include <asm/debug.h>
23     #include <asm/ccwcache.h>
24     #include <asm/ebcdic.h>
25     
26     #if (LINUX_VERSION_CODE > KERNEL_VERSION(2,3,98))
27     #define CCW_CACHE_SLAB_TYPE (SLAB_HWCACHE_ALIGN | SLAB_CACHE_DMA)
28     #define CCW_CACHE_TYPE (GFP_ATOMIC | GFP_DMA)
29     #else
30     #define CCW_CACHE_SLAB_TYPE (SLAB_HWCACHE_ALIGN)
31     #define CCW_CACHE_TYPE (GFP_ATOMIC)
32     #define kmem_cache_destroy(x) do {} while(0)
33     #endif
34     
35     #undef PRINTK_HEADER
36     #define PRINTK_HEADER "ccwcache"
37     
38     /* pointer to list of allocated requests */
39     static ccw_req_t *ccwreq_actual = NULL;
40     static spinlock_t ccwchain_lock;
41     
42     /* pointer to debug area */
43     static debug_info_t *debug_area = NULL;
44     
45     /* SECTION: Handling of the dynamically allocated kmem slabs */
46     
47     /* a name template for the cache-names */
48     static char ccw_name_template[] = "ccwcache-\0\0\0\0"; /* fill name with zeroes! */
49     /* the cache's names */
50     static char ccw_cache_name[CCW_NUMBER_CACHES][sizeof(ccw_name_template)+1]; 
51     /* the caches itself*/
52     static kmem_cache_t *ccw_cache[CCW_NUMBER_CACHES]; 
53     
54     /* SECTION: (de)allocation of ccw_req_t */
55     
56     /* 
57      * void enchain ( ccw_req_t *request )
58      * enchains the request to the ringbuffer
59      */
60     static inline void 
61     enchain ( ccw_req_t *request )
62     {
63     	unsigned long flags;
64     
65     	/* Sanity checks */
66     	if ( request == NULL )
67     		BUG();
68     	spin_lock_irqsave(&ccwchain_lock,flags);
69     	if ( ccwreq_actual == NULL ) { /* queue empty */
70     		ccwreq_actual = request;
71     		request->int_prev = ccwreq_actual;
72     		request->int_next = ccwreq_actual;
73     	} else {
74     		request->int_next = ccwreq_actual;
75     		request->int_prev = ccwreq_actual->int_prev;
76     		request->int_prev->int_next = request;
77     		request->int_next->int_prev = request;
78     	}
79     	spin_unlock_irqrestore(&ccwchain_lock,flags);
80     }
81     
82     /* 
83      * void dechain ( ccw_req_t *request )
84      * dechains the request from the ringbuffer
85      */
86     static inline void 
87     dechain ( ccw_req_t *request )
88     {
89     	unsigned long flags;
90     
91     	/* Sanity checks */
92     	if ( request == NULL || 
93     	     request->int_next == NULL ||
94     	     request->int_prev == NULL)
95     		BUG();
96     	/* first deallocate request from list of allocates requests */
97     	spin_lock_irqsave(&ccwchain_lock,flags);
98     	if ( request -> int_next == request -> int_prev ) {
99     		ccwreq_actual = NULL;
100     	} else {
101     		if ( ccwreq_actual == request ) {
102     			ccwreq_actual = request->int_next;
103     		}
104     		request->int_prev->int_next = request->int_next;
105     		request->int_next->int_prev = request->int_prev;
106     	}
107     	spin_unlock_irqrestore(&ccwchain_lock,flags);
108     }
109     
110     /* 
111      * ccw_req_t *ccw_alloc_request ( int cplength, int datasize )
112      * allocates a ccw_req_t, that 
113      * - can hold a CP of cplength CCWS
114      * - can hold additional data up to datasize 
115      */
116     ccw_req_t *
117     ccw_alloc_request ( char *magic, int cplength, int datasize )
118     {
119     	ccw_req_t * request = NULL;
120             int size_needed;
121     	int data_offset, ccw_offset;
122     	int cachind;
123     
124     	/* Sanity checks */
125     	if ( magic == NULL || datasize > PAGE_SIZE ||
126     	     cplength == 0 || (cplength*sizeof(ccw1_t)) > PAGE_SIZE)
127     		BUG();
128     	debug_text_event ( debug_area, 1, "ALLC");
129     	debug_text_event ( debug_area, 1, magic);
130     	debug_int_event ( debug_area, 1, cplength);
131     	debug_int_event ( debug_area, 1, datasize);
132     
133     	/* We try to keep things together in memory */
134     	size_needed = (sizeof (ccw_req_t) + 7) & -8;
135     	data_offset = ccw_offset = 0;
136     	if (size_needed + datasize <= PAGE_SIZE) {
137     		/* Keep data with the request */
138     		data_offset = size_needed;
139     		size_needed += (datasize + 7) & -8;
140     	}
141     	if (size_needed + cplength*sizeof(ccw1_t) <= PAGE_SIZE) {
142     		/* Keep CCWs with request */
143     		ccw_offset = size_needed;
144     		size_needed += cplength*sizeof(ccw1_t);
145     	}
146     
147     	/* determine cache index for the requested size */
148     	for (cachind = 0; cachind < CCW_NUMBER_CACHES; cachind ++ )
149     	   if ( size_needed < (SMALLEST_SLAB << cachind) ) 
150     			break;
151     
152     	/* Try to fulfill the request from a cache */
153     	if ( ccw_cache[cachind] == NULL )
154     		BUG();
155     	request = kmem_cache_alloc ( ccw_cache[cachind], CCW_CACHE_TYPE );
156     	if (request == NULL)
157     		return NULL;
158     	memset ( request, 0, (SMALLEST_SLAB << cachind));
159     	request->cache = ccw_cache[cachind];
160     
161     	/* Allocate memory for the extra data */
162     	if (data_offset == 0) {
163     		/* Allocated memory for extra data with kmalloc */
164     	    request->data = (void *) kmalloc(datasize, CCW_CACHE_TYPE );
165     		if (request->data == NULL) {
166     			printk(KERN_WARNING PRINTK_HEADER 
167     			       "Couldn't allocate data area\n");
168     			kmem_cache_free(request->cache, request);
169     			return NULL;
170     		}
171     	} else
172     		/* Extra data already allocated with the request */
173     		request->data = (void *) ((addr_t) request + data_offset);
174     
175     	/* Allocate memory for the channel program */
176     	if (ccw_offset == 0) {
177     		/* Allocated memory for the channel program with kmalloc */
178     		request->cpaddr = (ccw1_t *) kmalloc(cplength*sizeof(ccw1_t),
179     						     CCW_CACHE_TYPE);
180     		if (request->cpaddr == NULL) {
181     			printk (KERN_DEBUG PRINTK_HEADER
182     				"Couldn't allocate ccw area\n");
183     			if (data_offset == 0)
184     				kfree(request->data);
185     			kmem_cache_free(request->cache, request);
186     			return NULL;
187     		}
188     	} else
189     		/* Channel program already allocated with the request */
190     		request->cpaddr = (ccw1_t *) ((addr_t) request + ccw_offset);
191     
192     	memset ( request->data, 0, datasize );
193     	memset ( request->cpaddr, 0, cplength*sizeof(ccw1_t) );
194     	strncpy ( (char *)(&request->magic), magic, 4);
195     
196     	ASCEBC((char *)(&request->magic),4);
197     	request -> cplength = cplength;
198     	request -> datasize = datasize;
199     	/* enqueue request to list of allocated requests */
200     	enchain(request);
201     	debug_int_event ( debug_area, 1, (long)request);
202     	return request;
203     }
204     
205     /* 
206      * void ccw_free_request ( ccw_req_t * )
207      * deallocates the ccw_req_t, given as argument
208      */
209     
210     void
211     ccw_free_request ( ccw_req_t * request )
212     {
213             int size_needed;
214     
215     	debug_text_event ( debug_area, 1, "FREE");
216     	debug_int_event ( debug_area, 1, (long)request);
217     
218     	/* Sanity checks */
219     	if ( request == NULL || request->cache == NULL)
220                     BUG();
221     
222             dechain ( request);
223     	/* Free memory allocated with kmalloc
224              * make the same decisions as in ccw_alloc_requets */
225     	size_needed = (sizeof (ccw_req_t) + 7) & -8;
226     	if (size_needed + request->datasize <= PAGE_SIZE)
227     		/* We kept the data with the request */
228     		size_needed += (request->datasize + 7) & -8;
229     	else
230     		kfree(request->data);
231     	if (size_needed + request->cplength*sizeof(ccw1_t) > PAGE_SIZE)
232     		/* We kept the CCWs with request */
233                     kfree(request->cpaddr);
234             kmem_cache_free(request -> cache, request);
235     }
236     
237     /* SECTION: initialization and cleanup functions */
238     
239     /* 
240      * ccwcache_init
241      * called as an initializer function for the ccw memory management
242      */
243     
244     int
245     ccwcache_init (void)
246     {
247     	int rc = 0;
248     	int cachind;
249     
250             /* initialize variables */
251     	spin_lock_init(&ccwchain_lock);
252     
253     	/* allocate a debug area */
254     	debug_area = debug_register( "ccwcache", 2, 4,sizeof(void*));
255     	if ( debug_area == NULL )
256                     BUG();
257     
258             debug_register_view(debug_area,&debug_hex_ascii_view);
259             debug_register_view(debug_area,&debug_raw_view);
260     	debug_text_event ( debug_area, 0, "INIT");
261     
262     	/* First allocate the kmem caches */
263     	for ( cachind = 0; cachind < CCW_NUMBER_CACHES; cachind ++ ) {
264     		int slabsize = SMALLEST_SLAB << cachind;
265     		debug_text_event ( debug_area, 1, "allc");
266     		debug_int_event ( debug_area, 1, slabsize);
267     		sprintf ( ccw_cache_name[cachind], 
268     			  "%s%d%c", ccw_name_template, slabsize, 0);
269     		ccw_cache[cachind] = 
270     			kmem_cache_create( ccw_cache_name[cachind], 
271     					   slabsize, 0,
272     					   CCW_CACHE_SLAB_TYPE,
273     					   NULL, NULL );
274     		debug_int_event ( debug_area, 1, (long)ccw_cache[cachind]);
275     		if (ccw_cache[cachind] == NULL)
276     			panic ("Allocation of CCW cache failed\n");
277     	}
278     	return rc;
279     }
280     
281     /* 
282      * ccwcache_cleanup
283      * called as a cleanup function for the ccw memory management
284      */
285     
286     void
287     ccwcache_cleanup (void)
288     {
289     	int cachind;
290     
291     	/* Shrink the caches, if available */
292     	for ( cachind = 0; cachind < CCW_NUMBER_CACHES; cachind ++ ) {
293     		if ( ccw_cache[cachind] ) {
294     			if ( kmem_cache_shrink(ccw_cache[cachind]) == 0 ) {
295     				ccw_cache[cachind] = NULL;
296     			}
297     			kmem_cache_destroy(ccw_cache[cachind]);
298     		}
299     	}
300     	debug_unregister( debug_area );
301     }
302     
303     EXPORT_SYMBOL(ccw_alloc_request);
304     EXPORT_SYMBOL(ccw_free_request);
305     
306