import of upstream 2.4.34.4 from kernel.org
[linux-2.4.git] / 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 0 /* this is useless and could cause an OOPS in the worst case */
295                         if ( kmem_cache_shrink(ccw_cache[cachind]) == 0 ) {
296                                 ccw_cache[cachind] = NULL;
297                         }
298 #endif
299                         kmem_cache_destroy(ccw_cache[cachind]);
300                 }
301         }
302         debug_unregister( debug_area );
303 }
304
305 EXPORT_SYMBOL(ccw_alloc_request);
306 EXPORT_SYMBOL(ccw_free_request);
307