]>
Commit | Line | Data |
---|---|---|
00e5a55c BS |
1 | From: Gerald Schaefer <geraldsc@de.ibm.com> |
2 | Subject: dcssblk (new function): Add support for >2G DCSS and stacked | |
3 | contiguous DCSS support. | |
4 | References: bnc#417246 | |
5 | ||
6 | Description: The DCSS block device driver is modified to add >2G DCSS support | |
7 | and allow a DCSS block device to map to a set of contiguous DCSSs. | |
8 | The extmem code is also modified to use new Diagnose x'64' subcodes | |
9 | for >2G DCSSs. | |
10 | ||
11 | Acked-by: John Jolly <jjolly@suse.de> | |
12 | ||
13 | --- | |
14 | ||
15 | arch/s390/mm/extmem.c | 251 +++++++++++++++++--- | |
16 | drivers/s390/block/dcssblk.c | 516 ++++++++++++++++++++++++++++++++----------- | |
17 | 2 files changed, 597 insertions(+), 170 deletions(-) | |
18 | ||
19 | Index: linux-sles11/arch/s390/mm/extmem.c | |
20 | =================================================================== | |
21 | --- linux-sles11.orig/arch/s390/mm/extmem.c | |
22 | +++ linux-sles11/arch/s390/mm/extmem.c | |
23 | @@ -43,20 +43,40 @@ | |
24 | #define DCSS_FINDSEG 0x0c | |
25 | #define DCSS_LOADNOLY 0x10 | |
26 | #define DCSS_SEGEXT 0x18 | |
27 | +#define DCSS_LOADSHRX 0x20 | |
28 | +#define DCSS_LOADNSRX 0x24 | |
29 | +#define DCSS_FINDSEGX 0x2c | |
30 | +#define DCSS_SEGEXTX 0x38 | |
31 | #define DCSS_FINDSEGA 0x0c | |
32 | ||
33 | struct qrange { | |
34 | - unsigned int start; // 3byte start address, 1 byte type | |
35 | - unsigned int end; // 3byte end address, 1 byte reserved | |
36 | + unsigned long start; /* last byte type */ | |
37 | + unsigned long end; /* last byte reserved */ | |
38 | }; | |
39 | ||
40 | struct qout64 { | |
41 | + unsigned long segstart; | |
42 | + unsigned long segend; | |
43 | + int segcnt; | |
44 | + int segrcnt; | |
45 | + struct qrange range[6]; | |
46 | +}; | |
47 | + | |
48 | +#ifdef CONFIG_64BIT | |
49 | +struct qrange_old { | |
50 | + unsigned int start; /* last byte type */ | |
51 | + unsigned int end; /* last byte reserved */ | |
52 | +}; | |
53 | + | |
54 | +/* output area format for the Diag x'64' old subcode x'18' */ | |
55 | +struct qout64_old { | |
56 | int segstart; | |
57 | int segend; | |
58 | int segcnt; | |
59 | int segrcnt; | |
60 | - struct qrange range[6]; | |
61 | + struct qrange_old range[6]; | |
62 | }; | |
63 | +#endif | |
64 | ||
65 | struct qin64 { | |
66 | char qopcode; | |
67 | @@ -86,6 +106,55 @@ static DEFINE_MUTEX(dcss_lock); | |
68 | static LIST_HEAD(dcss_list); | |
69 | static char *segtype_string[] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC", | |
70 | "EW/EN-MIXED" }; | |
71 | +static int loadshr_scode, loadnsr_scode, findseg_scode; | |
72 | +static int segext_scode, purgeseg_scode; | |
73 | +static int scode_set; | |
74 | + | |
75 | +/* set correct Diag x'64' subcodes. */ | |
76 | +static int | |
77 | +dcss_set_subcodes(void) | |
78 | +{ | |
79 | +#ifdef CONFIG_64BIT | |
80 | + char *name = kmalloc(8 * sizeof(char), GFP_DMA); | |
81 | + unsigned long rx, ry; | |
82 | + int rc; | |
83 | + | |
84 | + if (name == NULL) | |
85 | + return -ENOMEM; | |
86 | + | |
87 | + rx = (unsigned long) name; | |
88 | + ry = DCSS_FINDSEGX; | |
89 | + | |
90 | + strcpy(name, "dummy"); | |
91 | + asm volatile( | |
92 | + " diag %0,%1,0x64\n" | |
93 | + "0: ipm %2\n" | |
94 | + " srl %2,28\n" | |
95 | + " j 2f\n" | |
96 | + "1: la %2,3\n" | |
97 | + "2:\n" | |
98 | + EX_TABLE(0b, 1b) | |
99 | + : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc"); | |
100 | + | |
101 | + kfree(name); | |
102 | + /* Diag x'64' new subcodes are supported, set to new subcodes */ | |
103 | + if (rc != 3) { | |
104 | + loadshr_scode = DCSS_LOADSHRX; | |
105 | + loadnsr_scode = DCSS_LOADNSRX; | |
106 | + purgeseg_scode = DCSS_PURGESEG; | |
107 | + findseg_scode = DCSS_FINDSEGX; | |
108 | + segext_scode = DCSS_SEGEXTX; | |
109 | + return 0; | |
110 | + } | |
111 | +#endif | |
112 | + /* Diag x'64' new subcodes are not supported, set to old subcodes */ | |
113 | + loadshr_scode = DCSS_LOADNOLY; | |
114 | + loadnsr_scode = DCSS_LOADNSR; | |
115 | + purgeseg_scode = DCSS_PURGESEG; | |
116 | + findseg_scode = DCSS_FINDSEG; | |
117 | + segext_scode = DCSS_SEGEXT; | |
118 | + return 0; | |
119 | +} | |
120 | ||
121 | /* | |
122 | * Create the 8 bytes, ebcdic VM segment name from | |
123 | @@ -135,25 +204,45 @@ segment_by_name (char *name) | |
124 | * Perform a function on a dcss segment. | |
125 | */ | |
126 | static inline int | |
127 | -dcss_diag (__u8 func, void *parameter, | |
128 | +dcss_diag(int *func, void *parameter, | |
129 | unsigned long *ret1, unsigned long *ret2) | |
130 | { | |
131 | unsigned long rx, ry; | |
132 | int rc; | |
133 | ||
134 | + if (scode_set == 0) { | |
135 | + rc = dcss_set_subcodes(); | |
136 | + if (rc < 0) | |
137 | + return rc; | |
138 | + scode_set = 1; | |
139 | + } | |
140 | rx = (unsigned long) parameter; | |
141 | - ry = (unsigned long) func; | |
142 | - asm volatile( | |
143 | + ry = (unsigned long) *func; | |
144 | + | |
145 | #ifdef CONFIG_64BIT | |
146 | - " sam31\n" | |
147 | - " diag %0,%1,0x64\n" | |
148 | - " sam64\n" | |
149 | + /* 64-bit Diag x'64' new subcode, keep in 64-bit addressing mode */ | |
150 | + if (*func > DCSS_SEGEXT) | |
151 | + asm volatile( | |
152 | + " diag %0,%1,0x64\n" | |
153 | + " ipm %2\n" | |
154 | + " srl %2,28\n" | |
155 | + : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc"); | |
156 | + /* 31-bit Diag x'64' old subcode, switch to 31-bit addressing mode */ | |
157 | + else | |
158 | + asm volatile( | |
159 | + " sam31\n" | |
160 | + " diag %0,%1,0x64\n" | |
161 | + " sam64\n" | |
162 | + " ipm %2\n" | |
163 | + " srl %2,28\n" | |
164 | + : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc"); | |
165 | #else | |
166 | + asm volatile( | |
167 | " diag %0,%1,0x64\n" | |
168 | -#endif | |
169 | " ipm %2\n" | |
170 | " srl %2,28\n" | |
171 | : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc"); | |
172 | +#endif | |
173 | *ret1 = rx; | |
174 | *ret2 = ry; | |
175 | return rc; | |
176 | @@ -190,14 +279,45 @@ query_segment_type (struct dcss_segment | |
177 | qin->qoutlen = sizeof(struct qout64); | |
178 | memcpy (qin->qname, seg->dcss_name, 8); | |
179 | ||
180 | - diag_cc = dcss_diag (DCSS_SEGEXT, qin, &dummy, &vmrc); | |
181 | + diag_cc = dcss_diag(&segext_scode, qin, &dummy, &vmrc); | |
182 | ||
183 | + if (diag_cc < 0) { | |
184 | + rc = diag_cc; | |
185 | + goto out_free; | |
186 | + } | |
187 | if (diag_cc > 1) { | |
188 | PRINT_WARN ("segment_type: diag returned error %ld\n", vmrc); | |
189 | rc = dcss_diag_translate_rc (vmrc); | |
190 | goto out_free; | |
191 | } | |
192 | ||
193 | +#ifdef CONFIG_64BIT | |
194 | + /* Only old format of output area of Diagnose x'64' is supported, | |
195 | + copy data for the new format. */ | |
196 | + if (segext_scode == DCSS_SEGEXT) { | |
197 | + struct qout64_old *qout_old; | |
198 | + qout_old = kzalloc(sizeof(struct qout64_old), GFP_DMA); | |
199 | + if (qout_old == NULL) { | |
200 | + rc = -ENOMEM; | |
201 | + goto out_free; | |
202 | + } | |
203 | + memcpy(qout_old, qout, sizeof(struct qout64_old)); | |
204 | + qout->segstart = (unsigned long) qout_old->segstart; | |
205 | + qout->segend = (unsigned long) qout_old->segend; | |
206 | + qout->segcnt = qout_old->segcnt; | |
207 | + qout->segrcnt = qout_old->segrcnt; | |
208 | + | |
209 | + if (qout->segcnt > 6) | |
210 | + qout->segrcnt = 6; | |
211 | + for (i = 0; i < qout->segrcnt; i++) { | |
212 | + qout->range[i].start = | |
213 | + (unsigned long) qout_old->range[i].start; | |
214 | + qout->range[i].end = | |
215 | + (unsigned long) qout_old->range[i].end; | |
216 | + } | |
217 | + kfree(qout_old); | |
218 | + } | |
219 | +#endif | |
220 | if (qout->segcnt > 6) { | |
221 | rc = -ENOTSUPP; | |
222 | goto out_free; | |
223 | @@ -269,6 +389,30 @@ segment_type (char* name) | |
224 | } | |
225 | ||
226 | /* | |
227 | + * check if segment collides with other segments that are currently loaded | |
228 | + * returns 1 if this is the case, 0 if no collision was found | |
229 | + */ | |
230 | +static int | |
231 | +segment_overlaps_others (struct dcss_segment *seg) | |
232 | +{ | |
233 | + struct list_head *l; | |
234 | + struct dcss_segment *tmp; | |
235 | + | |
236 | + BUG_ON(!mutex_is_locked(&dcss_lock)); | |
237 | + list_for_each(l, &dcss_list) { | |
238 | + tmp = list_entry(l, struct dcss_segment, list); | |
239 | + if ((tmp->start_addr >> 20) > (seg->end >> 20)) | |
240 | + continue; | |
241 | + if ((tmp->end >> 20) < (seg->start_addr >> 20)) | |
242 | + continue; | |
243 | + if (seg == tmp) | |
244 | + continue; | |
245 | + return 1; | |
246 | + } | |
247 | + return 0; | |
248 | +} | |
249 | + | |
250 | +/* | |
251 | * real segment loading function, called from segment_load | |
252 | */ | |
253 | static int | |
254 | @@ -276,7 +420,8 @@ __segment_load (char *name, int do_nonsh | |
255 | { | |
256 | struct dcss_segment *seg = kmalloc(sizeof(struct dcss_segment), | |
257 | GFP_DMA); | |
258 | - int dcss_command, rc, diag_cc; | |
259 | + int rc, diag_cc; | |
260 | + unsigned long start_addr, end_addr, dummy; | |
261 | ||
262 | if (seg == NULL) { | |
263 | rc = -ENOMEM; | |
264 | @@ -287,6 +432,13 @@ __segment_load (char *name, int do_nonsh | |
265 | if (rc < 0) | |
266 | goto out_free; | |
267 | ||
268 | + if (loadshr_scode == DCSS_LOADSHRX) { | |
269 | + if (segment_overlaps_others(seg)) { | |
270 | + rc = -EBUSY; | |
271 | + goto out_free; | |
272 | + } | |
273 | + } | |
274 | + | |
275 | rc = vmem_add_mapping(seg->start_addr, seg->end - seg->start_addr + 1); | |
276 | ||
277 | if (rc) | |
278 | @@ -316,20 +468,28 @@ __segment_load (char *name, int do_nonsh | |
279 | } | |
280 | ||
281 | if (do_nonshared) | |
282 | - dcss_command = DCSS_LOADNSR; | |
283 | + diag_cc = dcss_diag(&loadnsr_scode, seg->dcss_name, | |
284 | + &start_addr, &end_addr); | |
285 | else | |
286 | - dcss_command = DCSS_LOADNOLY; | |
287 | - | |
288 | - diag_cc = dcss_diag(dcss_command, seg->dcss_name, | |
289 | - &seg->start_addr, &seg->end); | |
290 | + diag_cc = dcss_diag(&loadshr_scode, seg->dcss_name, | |
291 | + &start_addr, &end_addr); | |
292 | + if (diag_cc < 0) { | |
293 | + dcss_diag(&purgeseg_scode, seg->dcss_name, | |
294 | + &dummy, &dummy); | |
295 | + rc = diag_cc; | |
296 | + goto out_resource; | |
297 | + } | |
298 | if (diag_cc > 1) { | |
299 | PRINT_WARN ("segment_load: could not load segment %s - " | |
300 | - "diag returned error (%ld)\n",name,seg->end); | |
301 | - rc = dcss_diag_translate_rc (seg->end); | |
302 | - dcss_diag(DCSS_PURGESEG, seg->dcss_name, | |
303 | - &seg->start_addr, &seg->end); | |
304 | + "diag returned error (%ld)\n", | |
305 | + name, end_addr); | |
306 | + rc = dcss_diag_translate_rc(end_addr); | |
307 | + dcss_diag(&purgeseg_scode, seg->dcss_name, | |
308 | + &dummy, &dummy); | |
309 | goto out_resource; | |
310 | } | |
311 | + seg->start_addr = start_addr; | |
312 | + seg->end = end_addr; | |
313 | seg->do_nonshared = do_nonshared; | |
314 | atomic_set(&seg->ref_count, 1); | |
315 | list_add(&seg->list, &dcss_list); | |
316 | @@ -423,8 +583,8 @@ int | |
317 | segment_modify_shared (char *name, int do_nonshared) | |
318 | { | |
319 | struct dcss_segment *seg; | |
320 | - unsigned long dummy; | |
321 | - int dcss_command, rc, diag_cc; | |
322 | + unsigned long start_addr, end_addr, dummy; | |
323 | + int rc, diag_cc; | |
324 | ||
325 | mutex_lock(&dcss_lock); | |
326 | seg = segment_by_name (name); | |
327 | @@ -445,38 +605,51 @@ segment_modify_shared (char *name, int d | |
328 | goto out_unlock; | |
329 | } | |
330 | release_resource(seg->res); | |
331 | - if (do_nonshared) { | |
332 | - dcss_command = DCSS_LOADNSR; | |
333 | + if (do_nonshared) | |
334 | seg->res->flags &= ~IORESOURCE_READONLY; | |
335 | - } else { | |
336 | - dcss_command = DCSS_LOADNOLY; | |
337 | + else | |
338 | if (seg->vm_segtype == SEG_TYPE_SR || | |
339 | seg->vm_segtype == SEG_TYPE_ER) | |
340 | seg->res->flags |= IORESOURCE_READONLY; | |
341 | - } | |
342 | + | |
343 | if (request_resource(&iomem_resource, seg->res)) { | |
344 | PRINT_WARN("segment_modify_shared: could not reload segment %s" | |
345 | " - overlapping resources\n", name); | |
346 | rc = -EBUSY; | |
347 | kfree(seg->res); | |
348 | - goto out_del; | |
349 | + goto out_del_mem; | |
350 | + } | |
351 | + | |
352 | + dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy); | |
353 | + if (do_nonshared) | |
354 | + diag_cc = dcss_diag(&loadnsr_scode, seg->dcss_name, | |
355 | + &start_addr, &end_addr); | |
356 | + else | |
357 | + diag_cc = dcss_diag(&loadshr_scode, seg->dcss_name, | |
358 | + &start_addr, &end_addr); | |
359 | + if (diag_cc < 0) { | |
360 | + rc = diag_cc; | |
361 | + goto out_del_res; | |
362 | } | |
363 | - dcss_diag(DCSS_PURGESEG, seg->dcss_name, &dummy, &dummy); | |
364 | - diag_cc = dcss_diag(dcss_command, seg->dcss_name, | |
365 | - &seg->start_addr, &seg->end); | |
366 | if (diag_cc > 1) { | |
367 | PRINT_WARN ("segment_modify_shared: could not reload segment %s" | |
368 | - " - diag returned error (%ld)\n",name,seg->end); | |
369 | - rc = dcss_diag_translate_rc (seg->end); | |
370 | - goto out_del; | |
371 | + " - diag returned error (%ld)\n", | |
372 | + name, end_addr); | |
373 | + rc = dcss_diag_translate_rc(end_addr); | |
374 | + goto out_del_res; | |
375 | } | |
376 | + seg->start_addr = start_addr; | |
377 | + seg->end = end_addr; | |
378 | seg->do_nonshared = do_nonshared; | |
379 | rc = 0; | |
380 | goto out_unlock; | |
381 | - out_del: | |
382 | + out_del_res: | |
383 | + release_resource(seg->res); | |
384 | + kfree(seg->res); | |
385 | + out_del_mem: | |
386 | vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1); | |
387 | list_del(&seg->list); | |
388 | - dcss_diag(DCSS_PURGESEG, seg->dcss_name, &dummy, &dummy); | |
389 | + dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy); | |
390 | kfree(seg); | |
391 | out_unlock: | |
392 | mutex_unlock(&dcss_lock); | |
393 | @@ -510,7 +683,7 @@ segment_unload(char *name) | |
394 | kfree(seg->res); | |
395 | vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1); | |
396 | list_del(&seg->list); | |
397 | - dcss_diag(DCSS_PURGESEG, seg->dcss_name, &dummy, &dummy); | |
398 | + dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy); | |
399 | kfree(seg); | |
400 | out_unlock: | |
401 | mutex_unlock(&dcss_lock); | |
402 | @@ -545,7 +718,7 @@ segment_save(char *name) | |
403 | endpfn = (seg->end) >> PAGE_SHIFT; | |
404 | sprintf(cmd1, "DEFSEG %s", name); | |
405 | for (i=0; i<seg->segcnt; i++) { | |
406 | - sprintf(cmd1+strlen(cmd1), " %X-%X %s", | |
407 | + sprintf(cmd1+strlen(cmd1), " %lX-%lX %s", | |
408 | seg->range[i].start >> PAGE_SHIFT, | |
409 | seg->range[i].end >> PAGE_SHIFT, | |
410 | segtype_string[seg->range[i].start & 0xff]); | |
411 | Index: linux-sles11/drivers/s390/block/dcssblk.c | |
412 | =================================================================== | |
413 | --- linux-sles11.orig/drivers/s390/block/dcssblk.c | |
414 | +++ linux-sles11/drivers/s390/block/dcssblk.c | |
415 | @@ -31,7 +31,6 @@ | |
416 | #define PRINT_WARN(x...) printk(KERN_WARNING DCSSBLK_NAME " warning: " x) | |
417 | #define PRINT_ERR(x...) printk(KERN_ERR DCSSBLK_NAME " error: " x) | |
418 | ||
419 | - | |
420 | static int dcssblk_open(struct inode *inode, struct file *filp); | |
421 | static int dcssblk_release(struct inode *inode, struct file *filp); | |
422 | static int dcssblk_make_request(struct request_queue *q, struct bio *bio); | |
423 | @@ -48,6 +47,30 @@ static struct block_device_operations dc | |
424 | .direct_access = dcssblk_direct_access, | |
425 | }; | |
426 | ||
427 | +struct dcssblk_dev_info { | |
428 | + struct list_head lh; | |
429 | + struct device dev; | |
430 | + char segment_name[BUS_ID_SIZE]; | |
431 | + atomic_t use_count; | |
432 | + struct gendisk *gd; | |
433 | + unsigned long start; | |
434 | + unsigned long end; | |
435 | + int segment_type; | |
436 | + unsigned char save_pending; | |
437 | + unsigned char is_shared; | |
438 | + struct request_queue *dcssblk_queue; | |
439 | + int num_of_segments; | |
440 | + struct list_head seg_list; | |
441 | +}; | |
442 | + | |
443 | +struct segment_info { | |
444 | + struct list_head lh; | |
445 | + char segment_name[BUS_ID_SIZE]; | |
446 | + unsigned long start; | |
447 | + unsigned long end; | |
448 | + int segment_type; | |
449 | +}; | |
450 | + | |
451 | static ssize_t dcssblk_add_store(struct device * dev, struct device_attribute *attr, const char * buf, | |
452 | size_t count); | |
453 | static ssize_t dcssblk_remove_store(struct device * dev, struct device_attribute *attr, const char * buf, | |
454 | @@ -58,30 +81,20 @@ static ssize_t dcssblk_save_show(struct | |
455 | static ssize_t dcssblk_shared_store(struct device * dev, struct device_attribute *attr, const char * buf, | |
456 | size_t count); | |
457 | static ssize_t dcssblk_shared_show(struct device *dev, struct device_attribute *attr, char *buf); | |
458 | +static ssize_t dcssblk_seglist_show(struct device *dev, | |
459 | + struct device_attribute *attr, | |
460 | + char *buf); | |
461 | ||
462 | static DEVICE_ATTR(add, S_IWUSR, NULL, dcssblk_add_store); | |
463 | static DEVICE_ATTR(remove, S_IWUSR, NULL, dcssblk_remove_store); | |
464 | -static DEVICE_ATTR(save, S_IWUSR | S_IRUGO, dcssblk_save_show, | |
465 | +static DEVICE_ATTR(save, S_IWUSR | S_IRUSR, dcssblk_save_show, | |
466 | dcssblk_save_store); | |
467 | -static DEVICE_ATTR(shared, S_IWUSR | S_IRUGO, dcssblk_shared_show, | |
468 | +static DEVICE_ATTR(shared, S_IWUSR | S_IRUSR, dcssblk_shared_show, | |
469 | dcssblk_shared_store); | |
470 | +static DEVICE_ATTR(seglist, S_IRUSR, dcssblk_seglist_show, NULL); | |
471 | ||
472 | static struct device *dcssblk_root_dev; | |
473 | ||
474 | -struct dcssblk_dev_info { | |
475 | - struct list_head lh; | |
476 | - struct device dev; | |
477 | - char segment_name[BUS_ID_SIZE]; | |
478 | - atomic_t use_count; | |
479 | - struct gendisk *gd; | |
480 | - unsigned long start; | |
481 | - unsigned long end; | |
482 | - int segment_type; | |
483 | - unsigned char save_pending; | |
484 | - unsigned char is_shared; | |
485 | - struct request_queue *dcssblk_queue; | |
486 | -}; | |
487 | - | |
488 | static LIST_HEAD(dcssblk_devices); | |
489 | static struct rw_semaphore dcssblk_devices_sem; | |
490 | ||
491 | @@ -91,8 +104,15 @@ static struct rw_semaphore dcssblk_devic | |
492 | static void | |
493 | dcssblk_release_segment(struct device *dev) | |
494 | { | |
495 | - PRINT_DEBUG("segment release fn called for %s\n", dev->bus_id); | |
496 | - kfree(container_of(dev, struct dcssblk_dev_info, dev)); | |
497 | + struct dcssblk_dev_info *dev_info; | |
498 | + struct segment_info *entry, *temp; | |
499 | + | |
500 | + dev_info = container_of(dev, struct dcssblk_dev_info, dev); | |
501 | + list_for_each_entry_safe(entry, temp, &dev_info->seg_list, lh) { | |
502 | + list_del(&entry->lh); | |
503 | + kfree(entry); | |
504 | + } | |
505 | + kfree(dev_info); | |
506 | module_put(THIS_MODULE); | |
507 | } | |
508 | ||
509 | @@ -142,6 +162,169 @@ dcssblk_get_device_by_name(char *name) | |
510 | return NULL; | |
511 | } | |
512 | ||
513 | +/* | |
514 | + * get the struct segment_info from seg_list | |
515 | + * for the given name. | |
516 | + * down_read(&dcssblk_devices_sem) must be held. | |
517 | + */ | |
518 | +static struct segment_info * | |
519 | +dcssblk_get_segment_by_name(char *name) | |
520 | +{ | |
521 | + struct dcssblk_dev_info *dev_info; | |
522 | + struct segment_info *entry; | |
523 | + | |
524 | + list_for_each_entry(dev_info, &dcssblk_devices, lh) { | |
525 | + list_for_each_entry(entry, &dev_info->seg_list, lh) { | |
526 | + if (!strcmp(name, entry->segment_name)) | |
527 | + return entry; | |
528 | + } | |
529 | + } | |
530 | + return NULL; | |
531 | +} | |
532 | + | |
533 | +/* | |
534 | + * get the highest address of the multi-segment block. | |
535 | + */ | |
536 | +static unsigned long | |
537 | +dcssblk_find_highest_addr(struct dcssblk_dev_info *dev_info) | |
538 | +{ | |
539 | + unsigned long highest_addr; | |
540 | + struct segment_info *entry; | |
541 | + | |
542 | + highest_addr = 0; | |
543 | + list_for_each_entry(entry, &dev_info->seg_list, lh) { | |
544 | + if (highest_addr < entry->end) | |
545 | + highest_addr = entry->end; | |
546 | + } | |
547 | + return highest_addr; | |
548 | +} | |
549 | + | |
550 | +/* | |
551 | + * get the lowest address of the multi-segment block. | |
552 | + */ | |
553 | +static unsigned long | |
554 | +dcssblk_find_lowest_addr(struct dcssblk_dev_info *dev_info) | |
555 | +{ | |
556 | + int set_first; | |
557 | + unsigned long lowest_addr; | |
558 | + struct segment_info *entry; | |
559 | + | |
560 | + set_first = 0; | |
561 | + lowest_addr = 0; | |
562 | + list_for_each_entry(entry, &dev_info->seg_list, lh) { | |
563 | + if (set_first == 0) { | |
564 | + lowest_addr = entry->start; | |
565 | + set_first = 1; | |
566 | + } else { | |
567 | + if (lowest_addr > entry->start) | |
568 | + lowest_addr = entry->start; | |
569 | + } | |
570 | + } | |
571 | + return lowest_addr; | |
572 | +} | |
573 | + | |
574 | +/* | |
575 | + * Check continuity of segments. | |
576 | + */ | |
577 | +static int | |
578 | +dcssblk_is_continuous(struct dcssblk_dev_info *dev_info) | |
579 | +{ | |
580 | + int i, j, rc; | |
581 | + struct segment_info *sort_list, *entry, temp; | |
582 | + | |
583 | + if (dev_info->num_of_segments <= 1) | |
584 | + return 0; | |
585 | + | |
586 | + sort_list = kzalloc( | |
587 | + sizeof(struct segment_info) * dev_info->num_of_segments, | |
588 | + GFP_KERNEL); | |
589 | + if (sort_list == NULL) | |
590 | + return -ENOMEM; | |
591 | + i = 0; | |
592 | + list_for_each_entry(entry, &dev_info->seg_list, lh) { | |
593 | + memcpy(&sort_list[i], entry, sizeof(struct segment_info)); | |
594 | + i++; | |
595 | + } | |
596 | + | |
597 | + /* sort segments */ | |
598 | + for (i = 0; i < dev_info->num_of_segments; i++) | |
599 | + for (j = 0; j < dev_info->num_of_segments; j++) | |
600 | + if (sort_list[j].start > sort_list[i].start) { | |
601 | + memcpy(&temp, &sort_list[i], | |
602 | + sizeof(struct segment_info)); | |
603 | + memcpy(&sort_list[i], &sort_list[j], | |
604 | + sizeof(struct segment_info)); | |
605 | + memcpy(&sort_list[j], &temp, | |
606 | + sizeof(struct segment_info)); | |
607 | + } | |
608 | + | |
609 | + /* check continuity */ | |
610 | + for (i = 0; i < dev_info->num_of_segments - 1; i++) { | |
611 | + if ((sort_list[i].end + 1) != sort_list[i+1].start) { | |
612 | + PRINT_ERR("Segment %s is not contiguous with " | |
613 | + "segment %s\n", | |
614 | + sort_list[i].segment_name, | |
615 | + sort_list[i+1].segment_name); | |
616 | + rc = -EINVAL; | |
617 | + goto out; | |
618 | + } | |
619 | + /* EN and EW are allowed in a block device */ | |
620 | + if (sort_list[i].segment_type != sort_list[i+1].segment_type) { | |
621 | + if (!(sort_list[i].segment_type & SEGMENT_EXCLUSIVE) || | |
622 | + (sort_list[i].segment_type == SEG_TYPE_ER) || | |
623 | + !(sort_list[i+1].segment_type & | |
624 | + SEGMENT_EXCLUSIVE) || | |
625 | + (sort_list[i+1].segment_type == SEG_TYPE_ER)) { | |
626 | + PRINT_ERR("Segment %s has different type from " | |
627 | + "segment %s\n", | |
628 | + sort_list[i].segment_name, | |
629 | + sort_list[i+1].segment_name); | |
630 | + rc = -EINVAL; | |
631 | + goto out; | |
632 | + } | |
633 | + } | |
634 | + } | |
635 | + rc = 0; | |
636 | +out: | |
637 | + kfree(sort_list); | |
638 | + return rc; | |
639 | +} | |
640 | + | |
641 | +/* | |
642 | + * Load a segment | |
643 | + */ | |
644 | +static int | |
645 | +dcssblk_load_segment(char *name, struct segment_info **seg_info) | |
646 | +{ | |
647 | + int rc; | |
648 | + | |
649 | + /* already loaded? */ | |
650 | + down_read(&dcssblk_devices_sem); | |
651 | + *seg_info = dcssblk_get_segment_by_name(name); | |
652 | + up_read(&dcssblk_devices_sem); | |
653 | + if (*seg_info != NULL) | |
654 | + return -EEXIST; | |
655 | + | |
656 | + /* get a struct segment_info */ | |
657 | + *seg_info = kzalloc(sizeof(struct segment_info), GFP_KERNEL); | |
658 | + if (*seg_info == NULL) | |
659 | + return -ENOMEM; | |
660 | + | |
661 | + strcpy((*seg_info)->segment_name, name); | |
662 | + | |
663 | + /* load the segment */ | |
664 | + rc = segment_load(name, SEGMENT_SHARED, | |
665 | + &(*seg_info)->start, &(*seg_info)->end); | |
666 | + if (rc < 0) { | |
667 | + segment_warning(rc, (*seg_info)->segment_name); | |
668 | + kfree(*seg_info); | |
669 | + } else { | |
670 | + INIT_LIST_HEAD(&(*seg_info)->lh); | |
671 | + (*seg_info)->segment_type = rc; | |
672 | + } | |
673 | + return rc; | |
674 | +} | |
675 | + | |
676 | static void dcssblk_unregister_callback(struct device *dev) | |
677 | { | |
678 | device_unregister(dev); | |
679 | @@ -165,6 +348,7 @@ static ssize_t | |
680 | dcssblk_shared_store(struct device *dev, struct device_attribute *attr, const char *inbuf, size_t count) | |
681 | { | |
682 | struct dcssblk_dev_info *dev_info; | |
683 | + struct segment_info *entry, *temp; | |
684 | int rc; | |
685 | ||
686 | if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0')) | |
687 | @@ -172,46 +356,46 @@ dcssblk_shared_store(struct device *dev, | |
688 | down_write(&dcssblk_devices_sem); | |
689 | dev_info = container_of(dev, struct dcssblk_dev_info, dev); | |
690 | if (atomic_read(&dev_info->use_count)) { | |
691 | - PRINT_ERR("share: segment %s is busy!\n", | |
692 | - dev_info->segment_name); | |
693 | rc = -EBUSY; | |
694 | goto out; | |
695 | } | |
696 | if (inbuf[0] == '1') { | |
697 | - // reload segment in shared mode | |
698 | - rc = segment_modify_shared(dev_info->segment_name, | |
699 | - SEGMENT_SHARED); | |
700 | - if (rc < 0) { | |
701 | - BUG_ON(rc == -EINVAL); | |
702 | - if (rc != -EAGAIN) | |
703 | - goto removeseg; | |
704 | - } else { | |
705 | - dev_info->is_shared = 1; | |
706 | - switch (dev_info->segment_type) { | |
707 | - case SEG_TYPE_SR: | |
708 | - case SEG_TYPE_ER: | |
709 | - case SEG_TYPE_SC: | |
710 | - set_disk_ro(dev_info->gd,1); | |
711 | + /* reload segments in shared mode */ | |
712 | + list_for_each_entry(entry, &dev_info->seg_list, lh) { | |
713 | + rc = segment_modify_shared(entry->segment_name, | |
714 | + SEGMENT_SHARED); | |
715 | + if (rc < 0) { | |
716 | + BUG_ON(rc == -EINVAL); | |
717 | + if (rc != -EAGAIN) | |
718 | + goto removeseg; | |
719 | } | |
720 | } | |
721 | + dev_info->is_shared = 1; | |
722 | + switch (dev_info->segment_type) { | |
723 | + case SEG_TYPE_SR: | |
724 | + case SEG_TYPE_ER: | |
725 | + case SEG_TYPE_SC: | |
726 | + set_disk_ro(dev_info->gd, 1); | |
727 | + } | |
728 | } else if (inbuf[0] == '0') { | |
729 | - // reload segment in exclusive mode | |
730 | + /* reload segments in exclusive mode */ | |
731 | if (dev_info->segment_type == SEG_TYPE_SC) { | |
732 | PRINT_ERR("Segment type SC (%s) cannot be loaded in " | |
733 | - "non-shared mode\n", dev_info->segment_name); | |
734 | + "non-shared mode\n", dev_info->segment_name); | |
735 | rc = -EINVAL; | |
736 | goto out; | |
737 | } | |
738 | - rc = segment_modify_shared(dev_info->segment_name, | |
739 | - SEGMENT_EXCLUSIVE); | |
740 | - if (rc < 0) { | |
741 | - BUG_ON(rc == -EINVAL); | |
742 | - if (rc != -EAGAIN) | |
743 | - goto removeseg; | |
744 | - } else { | |
745 | - dev_info->is_shared = 0; | |
746 | - set_disk_ro(dev_info->gd, 0); | |
747 | + list_for_each_entry(entry, &dev_info->seg_list, lh) { | |
748 | + rc = segment_modify_shared(entry->segment_name, | |
749 | + SEGMENT_EXCLUSIVE); | |
750 | + if (rc < 0) { | |
751 | + BUG_ON(rc == -EINVAL); | |
752 | + if (rc != -EAGAIN) | |
753 | + goto removeseg; | |
754 | + } | |
755 | } | |
756 | + dev_info->is_shared = 0; | |
757 | + set_disk_ro(dev_info->gd, 0); | |
758 | } else { | |
759 | rc = -EINVAL; | |
760 | goto out; | |
761 | @@ -220,8 +404,14 @@ dcssblk_shared_store(struct device *dev, | |
762 | goto out; | |
763 | ||
764 | removeseg: | |
765 | - PRINT_ERR("Could not reload segment %s, removing it now!\n", | |
766 | - dev_info->segment_name); | |
767 | + PRINT_ERR("Could not reload segment(s) of the device %s, removing " | |
768 | + "segment(s) now!\n", | |
769 | + dev_info->segment_name); | |
770 | + temp = entry; | |
771 | + list_for_each_entry(entry, &dev_info->seg_list, lh) { | |
772 | + if (entry != temp) | |
773 | + segment_unload(entry->segment_name); | |
774 | + } | |
775 | list_del(&dev_info->lh); | |
776 | ||
777 | del_gendisk(dev_info->gd); | |
778 | @@ -254,6 +444,7 @@ static ssize_t | |
779 | dcssblk_save_store(struct device *dev, struct device_attribute *attr, const char *inbuf, size_t count) | |
780 | { | |
781 | struct dcssblk_dev_info *dev_info; | |
782 | + struct segment_info *entry; | |
783 | ||
784 | if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0')) | |
785 | return -EINVAL; | |
786 | @@ -263,14 +454,16 @@ dcssblk_save_store(struct device *dev, s | |
787 | if (inbuf[0] == '1') { | |
788 | if (atomic_read(&dev_info->use_count) == 0) { | |
789 | // device is idle => we save immediately | |
790 | - PRINT_INFO("Saving segment %s\n", | |
791 | + PRINT_INFO("Saving segment(s) of the device %s\n", | |
792 | dev_info->segment_name); | |
793 | - segment_save(dev_info->segment_name); | |
794 | + list_for_each_entry(entry, &dev_info->seg_list, lh) { | |
795 | + segment_save(entry->segment_name); | |
796 | + } | |
797 | } else { | |
798 | // device is busy => we save it when it becomes | |
799 | // idle in dcssblk_release | |
800 | - PRINT_INFO("Segment %s is currently busy, it will " | |
801 | - "be saved when it becomes idle...\n", | |
802 | + PRINT_INFO("Device %s is currently busy, segment(s) " | |
803 | + "will be saved when it becomes idle...\n", | |
804 | dev_info->segment_name); | |
805 | dev_info->save_pending = 1; | |
806 | } | |
807 | @@ -279,7 +472,8 @@ dcssblk_save_store(struct device *dev, s | |
808 | // device is busy & the user wants to undo his save | |
809 | // request | |
810 | dev_info->save_pending = 0; | |
811 | - PRINT_INFO("Pending save for segment %s deactivated\n", | |
812 | + PRINT_INFO("Pending save for segment(s) of the device " | |
813 | + "%s deactivated\n", | |
814 | dev_info->segment_name); | |
815 | } | |
816 | } else { | |
817 | @@ -291,66 +485,123 @@ dcssblk_save_store(struct device *dev, s | |
818 | } | |
819 | ||
820 | /* | |
821 | + * device attribute for showing all segments in a device | |
822 | + */ | |
823 | +static ssize_t | |
824 | +dcssblk_seglist_show(struct device *dev, struct device_attribute *attr, | |
825 | + char *buf) | |
826 | +{ | |
827 | + int i; | |
828 | + | |
829 | + struct dcssblk_dev_info *dev_info; | |
830 | + struct segment_info *entry; | |
831 | + | |
832 | + down_read(&dcssblk_devices_sem); | |
833 | + dev_info = container_of(dev, struct dcssblk_dev_info, dev); | |
834 | + i = 0; | |
835 | + buf[0] = '\0'; | |
836 | + list_for_each_entry(entry, &dev_info->seg_list, lh) { | |
837 | + strcpy(&buf[i], entry->segment_name); | |
838 | + i += strlen(entry->segment_name); | |
839 | + buf[i] = '\n'; | |
840 | + i++; | |
841 | + } | |
842 | + up_read(&dcssblk_devices_sem); | |
843 | + return i; | |
844 | +} | |
845 | + | |
846 | +/* | |
847 | * device attribute for adding devices | |
848 | */ | |
849 | static ssize_t | |
850 | dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) | |
851 | { | |
852 | - int rc, i; | |
853 | + int rc, i, j, num_of_segments; | |
854 | struct dcssblk_dev_info *dev_info; | |
855 | + struct segment_info *seg_info, *temp; | |
856 | char *local_buf; | |
857 | unsigned long seg_byte_size; | |
858 | ||
859 | dev_info = NULL; | |
860 | + seg_info = NULL; | |
861 | if (dev != dcssblk_root_dev) { | |
862 | rc = -EINVAL; | |
863 | goto out_nobuf; | |
864 | } | |
865 | + if ((count < 1) || (buf[0] == '\0') || (buf[0] == '\n')) { | |
866 | + rc = -ENAMETOOLONG; | |
867 | + goto out_nobuf; | |
868 | + } | |
869 | + | |
870 | local_buf = kmalloc(count + 1, GFP_KERNEL); | |
871 | if (local_buf == NULL) { | |
872 | rc = -ENOMEM; | |
873 | goto out_nobuf; | |
874 | } | |
875 | + | |
876 | /* | |
877 | * parse input | |
878 | */ | |
879 | + num_of_segments = 0; | |
880 | for (i = 0; ((buf[i] != '\0') && (buf[i] != '\n') && i < count); i++) { | |
881 | - local_buf[i] = toupper(buf[i]); | |
882 | + for (j = i; (buf[j] != ':') && | |
883 | + (buf[j] != '\0') && | |
884 | + (buf[j] != '\n') && | |
885 | + j < count; j++) { | |
886 | + local_buf[j-i] = toupper(buf[j]); | |
887 | + } | |
888 | + local_buf[j-i] = '\0'; | |
889 | + if (((j - i) == 0) || ((j - i) > 8)) { | |
890 | + rc = -ENAMETOOLONG; | |
891 | + goto seg_list_del; | |
892 | + } | |
893 | + | |
894 | + rc = dcssblk_load_segment(local_buf, &seg_info); | |
895 | + if (rc < 0) | |
896 | + goto seg_list_del; | |
897 | + /* | |
898 | + * get a struct dcssblk_dev_info | |
899 | + */ | |
900 | + if (num_of_segments == 0) { | |
901 | + dev_info = kzalloc(sizeof(struct dcssblk_dev_info), | |
902 | + GFP_KERNEL); | |
903 | + if (dev_info == NULL) { | |
904 | + rc = -ENOMEM; | |
905 | + goto out; | |
906 | + } | |
907 | + strcpy(dev_info->segment_name, local_buf); | |
908 | + dev_info->segment_type = seg_info->segment_type; | |
909 | + INIT_LIST_HEAD(&dev_info->seg_list); | |
910 | + } | |
911 | + list_add_tail(&seg_info->lh, &dev_info->seg_list); | |
912 | + num_of_segments++; | |
913 | + i = j; | |
914 | + | |
915 | + if ((buf[j] == '\0') || (buf[j] == '\n')) | |
916 | + break; | |
917 | } | |
918 | - local_buf[i] = '\0'; | |
919 | - if ((i == 0) || (i > 8)) { | |
920 | + | |
921 | + /* no trailing colon at the end of the input */ | |
922 | + if ((i > 0) && (buf[i-1] == ':')) { | |
923 | rc = -ENAMETOOLONG; | |
924 | - goto out; | |
925 | - } | |
926 | - /* | |
927 | - * already loaded? | |
928 | - */ | |
929 | - down_read(&dcssblk_devices_sem); | |
930 | - dev_info = dcssblk_get_device_by_name(local_buf); | |
931 | - up_read(&dcssblk_devices_sem); | |
932 | - if (dev_info != NULL) { | |
933 | - PRINT_WARN("Segment %s already loaded!\n", local_buf); | |
934 | - rc = -EEXIST; | |
935 | - goto out; | |
936 | - } | |
937 | - /* | |
938 | - * get a struct dcssblk_dev_info | |
939 | - */ | |
940 | - dev_info = kzalloc(sizeof(struct dcssblk_dev_info), GFP_KERNEL); | |
941 | - if (dev_info == NULL) { | |
942 | - rc = -ENOMEM; | |
943 | - goto out; | |
944 | + goto seg_list_del; | |
945 | } | |
946 | + strlcpy(local_buf, buf, i + 1); | |
947 | + dev_info->num_of_segments = num_of_segments; | |
948 | + rc = dcssblk_is_continuous(dev_info); | |
949 | + if (rc < 0) | |
950 | + goto seg_list_del; | |
951 | ||
952 | - strcpy(dev_info->segment_name, local_buf); | |
953 | - strlcpy(dev_info->dev.bus_id, local_buf, BUS_ID_SIZE); | |
954 | + dev_info->start = dcssblk_find_lowest_addr(dev_info); | |
955 | + dev_info->end = dcssblk_find_highest_addr(dev_info); | |
956 | + | |
957 | + strlcpy(dev_info->dev.bus_id, dev_info->segment_name, BUS_ID_SIZE); | |
958 | dev_info->dev.release = dcssblk_release_segment; | |
959 | INIT_LIST_HEAD(&dev_info->lh); | |
960 | - | |
961 | dev_info->gd = alloc_disk(DCSSBLK_MINORS_PER_DISK); | |
962 | if (dev_info->gd == NULL) { | |
963 | rc = -ENOMEM; | |
964 | - goto free_dev_info; | |
965 | + goto seg_list_del; | |
966 | } | |
967 | dev_info->gd->major = dcssblk_major; | |
968 | dev_info->gd->fops = &dcssblk_devops; | |
969 | @@ -360,59 +611,43 @@ dcssblk_add_store(struct device *dev, st | |
970 | dev_info->gd->driverfs_dev = &dev_info->dev; | |
971 | blk_queue_make_request(dev_info->dcssblk_queue, dcssblk_make_request); | |
972 | blk_queue_hardsect_size(dev_info->dcssblk_queue, 4096); | |
973 | - /* | |
974 | - * load the segment | |
975 | - */ | |
976 | - rc = segment_load(local_buf, SEGMENT_SHARED, | |
977 | - &dev_info->start, &dev_info->end); | |
978 | - if (rc < 0) { | |
979 | - segment_warning(rc, dev_info->segment_name); | |
980 | - goto dealloc_gendisk; | |
981 | - } | |
982 | + | |
983 | seg_byte_size = (dev_info->end - dev_info->start + 1); | |
984 | set_capacity(dev_info->gd, seg_byte_size >> 9); // size in sectors | |
985 | - PRINT_INFO("Loaded segment %s, size = %lu Byte, " | |
986 | + PRINT_INFO("Loaded segment(s) %s, size = %lu Byte, " | |
987 | "capacity = %lu (512 Byte) sectors\n", local_buf, | |
988 | seg_byte_size, seg_byte_size >> 9); | |
989 | ||
990 | - dev_info->segment_type = rc; | |
991 | dev_info->save_pending = 0; | |
992 | dev_info->is_shared = 1; | |
993 | dev_info->dev.parent = dcssblk_root_dev; | |
994 | ||
995 | /* | |
996 | - * get minor, add to list | |
997 | + *get minor, add to list | |
998 | */ | |
999 | down_write(&dcssblk_devices_sem); | |
1000 | - if (dcssblk_get_device_by_name(local_buf)) { | |
1001 | - up_write(&dcssblk_devices_sem); | |
1002 | + if (dcssblk_get_segment_by_name(local_buf)) { | |
1003 | rc = -EEXIST; | |
1004 | - goto unload_seg; | |
1005 | + goto release_gd; | |
1006 | } | |
1007 | rc = dcssblk_assign_free_minor(dev_info); | |
1008 | - if (rc) { | |
1009 | - up_write(&dcssblk_devices_sem); | |
1010 | - PRINT_ERR("No free minor number available! " | |
1011 | - "Unloading segment...\n"); | |
1012 | - goto unload_seg; | |
1013 | - } | |
1014 | + if (rc) | |
1015 | + goto release_gd; | |
1016 | sprintf(dev_info->gd->disk_name, "dcssblk%d", | |
1017 | dev_info->gd->first_minor); | |
1018 | list_add_tail(&dev_info->lh, &dcssblk_devices); | |
1019 | ||
1020 | if (!try_module_get(THIS_MODULE)) { | |
1021 | rc = -ENODEV; | |
1022 | - goto list_del; | |
1023 | + goto dev_list_del; | |
1024 | } | |
1025 | /* | |
1026 | * register the device | |
1027 | */ | |
1028 | rc = device_register(&dev_info->dev); | |
1029 | if (rc) { | |
1030 | - PRINT_ERR("Segment %s could not be registered RC=%d\n", | |
1031 | - local_buf, rc); | |
1032 | module_put(THIS_MODULE); | |
1033 | - goto list_del; | |
1034 | + goto dev_list_del; | |
1035 | } | |
1036 | get_device(&dev_info->dev); | |
1037 | rc = device_create_file(&dev_info->dev, &dev_attr_shared); | |
1038 | @@ -421,6 +656,9 @@ dcssblk_add_store(struct device *dev, st | |
1039 | rc = device_create_file(&dev_info->dev, &dev_attr_save); | |
1040 | if (rc) | |
1041 | goto unregister_dev; | |
1042 | + rc = device_create_file(&dev_info->dev, &dev_attr_seglist); | |
1043 | + if (rc) | |
1044 | + goto unregister_dev; | |
1045 | ||
1046 | add_disk(dev_info->gd); | |
1047 | ||
1048 | @@ -434,7 +672,6 @@ dcssblk_add_store(struct device *dev, st | |
1049 | set_disk_ro(dev_info->gd,0); | |
1050 | break; | |
1051 | } | |
1052 | - PRINT_DEBUG("Segment %s loaded successfully\n", local_buf); | |
1053 | up_write(&dcssblk_devices_sem); | |
1054 | rc = count; | |
1055 | goto out; | |
1056 | @@ -445,20 +682,27 @@ unregister_dev: | |
1057 | dev_info->gd->queue = NULL; | |
1058 | put_disk(dev_info->gd); | |
1059 | device_unregister(&dev_info->dev); | |
1060 | - segment_unload(dev_info->segment_name); | |
1061 | + list_for_each_entry(seg_info, &dev_info->seg_list, lh) { | |
1062 | + segment_unload(seg_info->segment_name); | |
1063 | + } | |
1064 | put_device(&dev_info->dev); | |
1065 | up_write(&dcssblk_devices_sem); | |
1066 | goto out; | |
1067 | -list_del: | |
1068 | +dev_list_del: | |
1069 | list_del(&dev_info->lh); | |
1070 | - up_write(&dcssblk_devices_sem); | |
1071 | -unload_seg: | |
1072 | - segment_unload(local_buf); | |
1073 | -dealloc_gendisk: | |
1074 | +release_gd: | |
1075 | blk_cleanup_queue(dev_info->dcssblk_queue); | |
1076 | dev_info->gd->queue = NULL; | |
1077 | put_disk(dev_info->gd); | |
1078 | -free_dev_info: | |
1079 | + up_write(&dcssblk_devices_sem); | |
1080 | +seg_list_del: | |
1081 | + if (dev_info == NULL) | |
1082 | + goto out; | |
1083 | + list_for_each_entry_safe(seg_info, temp, &dev_info->seg_list, lh) { | |
1084 | + list_del(&seg_info->lh); | |
1085 | + segment_unload(seg_info->segment_name); | |
1086 | + kfree(seg_info); | |
1087 | + } | |
1088 | kfree(dev_info); | |
1089 | out: | |
1090 | kfree(local_buf); | |
1091 | @@ -473,6 +717,7 @@ static ssize_t | |
1092 | dcssblk_remove_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) | |
1093 | { | |
1094 | struct dcssblk_dev_info *dev_info; | |
1095 | + struct segment_info *entry; | |
1096 | int rc, i; | |
1097 | char *local_buf; | |
1098 | ||
1099 | @@ -499,26 +744,28 @@ dcssblk_remove_store(struct device *dev, | |
1100 | dev_info = dcssblk_get_device_by_name(local_buf); | |
1101 | if (dev_info == NULL) { | |
1102 | up_write(&dcssblk_devices_sem); | |
1103 | - PRINT_WARN("Segment %s is not loaded!\n", local_buf); | |
1104 | + PRINT_WARN("Device %s is not loaded!\n", local_buf); | |
1105 | rc = -ENODEV; | |
1106 | goto out_buf; | |
1107 | } | |
1108 | if (atomic_read(&dev_info->use_count) != 0) { | |
1109 | up_write(&dcssblk_devices_sem); | |
1110 | - PRINT_WARN("Segment %s is in use!\n", local_buf); | |
1111 | + PRINT_WARN("Device %s is in use!\n", local_buf); | |
1112 | rc = -EBUSY; | |
1113 | goto out_buf; | |
1114 | } | |
1115 | - list_del(&dev_info->lh); | |
1116 | ||
1117 | + list_del(&dev_info->lh); | |
1118 | del_gendisk(dev_info->gd); | |
1119 | blk_cleanup_queue(dev_info->dcssblk_queue); | |
1120 | dev_info->gd->queue = NULL; | |
1121 | put_disk(dev_info->gd); | |
1122 | device_unregister(&dev_info->dev); | |
1123 | - segment_unload(dev_info->segment_name); | |
1124 | - PRINT_DEBUG("Segment %s unloaded successfully\n", | |
1125 | - dev_info->segment_name); | |
1126 | + | |
1127 | + /* unload all related segments */ | |
1128 | + list_for_each_entry(entry, &dev_info->seg_list, lh) | |
1129 | + segment_unload(entry->segment_name); | |
1130 | + | |
1131 | put_device(&dev_info->dev); | |
1132 | up_write(&dcssblk_devices_sem); | |
1133 | ||
1134 | @@ -550,6 +797,7 @@ static int | |
1135 | dcssblk_release(struct inode *inode, struct file *filp) | |
1136 | { | |
1137 | struct dcssblk_dev_info *dev_info; | |
1138 | + struct segment_info *entry; | |
1139 | int rc; | |
1140 | ||
1141 | dev_info = inode->i_bdev->bd_disk->private_data; | |
1142 | @@ -560,9 +808,11 @@ dcssblk_release(struct inode *inode, str | |
1143 | down_write(&dcssblk_devices_sem); | |
1144 | if (atomic_dec_and_test(&dev_info->use_count) | |
1145 | && (dev_info->save_pending)) { | |
1146 | - PRINT_INFO("Segment %s became idle and is being saved now\n", | |
1147 | + PRINT_INFO("Device %s became idle and is being saved now\n", | |
1148 | dev_info->segment_name); | |
1149 | - segment_save(dev_info->segment_name); | |
1150 | + list_for_each_entry(entry, &dev_info->seg_list, lh) { | |
1151 | + segment_save(entry->segment_name); | |
1152 | + } | |
1153 | dev_info->save_pending = 0; | |
1154 | } | |
1155 | up_write(&dcssblk_devices_sem); | |
1156 | @@ -602,7 +852,8 @@ dcssblk_make_request(struct request_queu | |
1157 | case SEG_TYPE_SC: | |
1158 | /* cannot write to these segments */ | |
1159 | if (bio_data_dir(bio) == WRITE) { | |
1160 | - PRINT_WARN("rejecting write to ro segment %s\n", dev_info->dev.bus_id); | |
1161 | + PRINT_WARN("rejecting write to ro device %s\n", | |
1162 | + dev_info->dev.bus_id); | |
1163 | goto fail; | |
1164 | } | |
1165 | } | |
1166 | @@ -657,7 +908,7 @@ static void | |
1167 | dcssblk_check_params(void) | |
1168 | { | |
1169 | int rc, i, j, k; | |
1170 | - char buf[9]; | |
1171 | + char buf[DCSSBLK_PARM_LEN + 1]; | |
1172 | struct dcssblk_dev_info *dev_info; | |
1173 | ||
1174 | for (i = 0; (i < DCSSBLK_PARM_LEN) && (dcssblk_segments[i] != '\0'); | |
1175 | @@ -665,15 +916,16 @@ dcssblk_check_params(void) | |
1176 | for (j = i; (dcssblk_segments[j] != ',') && | |
1177 | (dcssblk_segments[j] != '\0') && | |
1178 | (dcssblk_segments[j] != '(') && | |
1179 | - (j - i) < 8; j++) | |
1180 | + (j < DCSSBLK_PARM_LEN); j++) | |
1181 | { | |
1182 | buf[j-i] = dcssblk_segments[j]; | |
1183 | } | |
1184 | buf[j-i] = '\0'; | |
1185 | rc = dcssblk_add_store(dcssblk_root_dev, NULL, buf, j-i); | |
1186 | if ((rc >= 0) && (dcssblk_segments[j] == '(')) { | |
1187 | - for (k = 0; buf[k] != '\0'; k++) | |
1188 | + for (k = 0; (buf[k] != ':') && (buf[k] != '\0'); k++) | |
1189 | buf[k] = toupper(buf[k]); | |
1190 | + buf[k] = '\0'; | |
1191 | if (!strncmp(&dcssblk_segments[j], "(local)", 7)) { | |
1192 | down_read(&dcssblk_devices_sem); | |
1193 | dev_info = dcssblk_get_device_by_name(buf); | |
1194 | @@ -740,10 +992,12 @@ module_exit(dcssblk_exit); | |
1195 | ||
1196 | module_param_string(segments, dcssblk_segments, DCSSBLK_PARM_LEN, 0444); | |
1197 | MODULE_PARM_DESC(segments, "Name of DCSS segment(s) to be loaded, " | |
1198 | - "comma-separated list, each name max. 8 chars.\n" | |
1199 | - "Adding \"(local)\" to segment name equals echoing 0 to " | |
1200 | - "/sys/devices/dcssblk/<segment name>/shared after loading " | |
1201 | - "the segment - \n" | |
1202 | - "e.g. segments=\"mydcss1,mydcss2,mydcss3(local)\""); | |
1203 | + "comma-separated list, names in each set separated " | |
1204 | + "by commas are separated by colons, each set contains " | |
1205 | + "names of contiguous segments and each name max. 8 chars.\n" | |
1206 | + "Adding \"(local)\" to the end of each set equals echoing 0 " | |
1207 | + "to /sys/devices/dcssblk/<device name>/shared after loading " | |
1208 | + "the contiguous segments - \n" | |
1209 | + "e.g. segments=\"mydcss1,mydcss2:mydcss3,mydcss4(local)\""); | |
1210 | ||
1211 | MODULE_LICENSE("GPL"); |