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
enchain(ccw_req_t * request)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
dechain(ccw_req_t * request)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 *
ccw_alloc_request(char * magic,int cplength,int datasize)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
ccw_free_request(ccw_req_t * request)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
ccwcache_init(void)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
ccwcache_cleanup(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 
308