diff options
-rw-r--r-- | arch/s390/mm/extmem.c | 251 | ||||
-rw-r--r-- | drivers/s390/block/dcssblk.c | 515 |
2 files changed, 596 insertions, 170 deletions
diff --git a/arch/s390/mm/extmem.c b/arch/s390/mm/extmem.c index f231f5ec74b6..580fc64cc735 100644 --- a/arch/s390/mm/extmem.c +++ b/arch/s390/mm/extmem.c | |||
@@ -43,20 +43,40 @@ | |||
43 | #define DCSS_FINDSEG 0x0c | 43 | #define DCSS_FINDSEG 0x0c |
44 | #define DCSS_LOADNOLY 0x10 | 44 | #define DCSS_LOADNOLY 0x10 |
45 | #define DCSS_SEGEXT 0x18 | 45 | #define DCSS_SEGEXT 0x18 |
46 | #define DCSS_LOADSHRX 0x20 | ||
47 | #define DCSS_LOADNSRX 0x24 | ||
48 | #define DCSS_FINDSEGX 0x2c | ||
49 | #define DCSS_SEGEXTX 0x38 | ||
46 | #define DCSS_FINDSEGA 0x0c | 50 | #define DCSS_FINDSEGA 0x0c |
47 | 51 | ||
48 | struct qrange { | 52 | struct qrange { |
49 | unsigned int start; // 3byte start address, 1 byte type | 53 | unsigned long start; /* last byte type */ |
50 | unsigned int end; // 3byte end address, 1 byte reserved | 54 | unsigned long end; /* last byte reserved */ |
51 | }; | 55 | }; |
52 | 56 | ||
53 | struct qout64 { | 57 | struct qout64 { |
58 | unsigned long segstart; | ||
59 | unsigned long segend; | ||
60 | int segcnt; | ||
61 | int segrcnt; | ||
62 | struct qrange range[6]; | ||
63 | }; | ||
64 | |||
65 | #ifdef CONFIG_64BIT | ||
66 | struct qrange_old { | ||
67 | unsigned int start; /* last byte type */ | ||
68 | unsigned int end; /* last byte reserved */ | ||
69 | }; | ||
70 | |||
71 | /* output area format for the Diag x'64' old subcode x'18' */ | ||
72 | struct qout64_old { | ||
54 | int segstart; | 73 | int segstart; |
55 | int segend; | 74 | int segend; |
56 | int segcnt; | 75 | int segcnt; |
57 | int segrcnt; | 76 | int segrcnt; |
58 | struct qrange range[6]; | 77 | struct qrange_old range[6]; |
59 | }; | 78 | }; |
79 | #endif | ||
60 | 80 | ||
61 | struct qin64 { | 81 | struct qin64 { |
62 | char qopcode; | 82 | char qopcode; |
@@ -86,6 +106,55 @@ static DEFINE_MUTEX(dcss_lock); | |||
86 | static LIST_HEAD(dcss_list); | 106 | static LIST_HEAD(dcss_list); |
87 | static char *segtype_string[] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC", | 107 | static char *segtype_string[] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC", |
88 | "EW/EN-MIXED" }; | 108 | "EW/EN-MIXED" }; |
109 | static int loadshr_scode, loadnsr_scode, findseg_scode; | ||
110 | static int segext_scode, purgeseg_scode; | ||
111 | static int scode_set; | ||
112 | |||
113 | /* set correct Diag x'64' subcodes. */ | ||
114 | static int | ||
115 | dcss_set_subcodes(void) | ||
116 | { | ||
117 | #ifdef CONFIG_64BIT | ||
118 | char *name = kmalloc(8 * sizeof(char), GFP_DMA); | ||
119 | unsigned long rx, ry; | ||
120 | int rc; | ||
121 | |||
122 | if (name == NULL) | ||
123 | return -ENOMEM; | ||
124 | |||
125 | rx = (unsigned long) name; | ||
126 | ry = DCSS_FINDSEGX; | ||
127 | |||
128 | strcpy(name, "dummy"); | ||
129 | asm volatile( | ||
130 | " diag %0,%1,0x64\n" | ||
131 | "0: ipm %2\n" | ||
132 | " srl %2,28\n" | ||
133 | " j 2f\n" | ||
134 | "1: la %2,3\n" | ||
135 | "2:\n" | ||
136 | EX_TABLE(0b, 1b) | ||
137 | : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc"); | ||
138 | |||
139 | kfree(name); | ||
140 | /* Diag x'64' new subcodes are supported, set to new subcodes */ | ||
141 | if (rc != 3) { | ||
142 | loadshr_scode = DCSS_LOADSHRX; | ||
143 | loadnsr_scode = DCSS_LOADNSRX; | ||
144 | purgeseg_scode = DCSS_PURGESEG; | ||
145 | findseg_scode = DCSS_FINDSEGX; | ||
146 | segext_scode = DCSS_SEGEXTX; | ||
147 | return 0; | ||
148 | } | ||
149 | #endif | ||
150 | /* Diag x'64' new subcodes are not supported, set to old subcodes */ | ||
151 | loadshr_scode = DCSS_LOADNOLY; | ||
152 | loadnsr_scode = DCSS_LOADNSR; | ||
153 | purgeseg_scode = DCSS_PURGESEG; | ||
154 | findseg_scode = DCSS_FINDSEG; | ||
155 | segext_scode = DCSS_SEGEXT; | ||
156 | return 0; | ||
157 | } | ||
89 | 158 | ||
90 | /* | 159 | /* |
91 | * Create the 8 bytes, ebcdic VM segment name from | 160 | * Create the 8 bytes, ebcdic VM segment name from |
@@ -135,25 +204,45 @@ segment_by_name (char *name) | |||
135 | * Perform a function on a dcss segment. | 204 | * Perform a function on a dcss segment. |
136 | */ | 205 | */ |
137 | static inline int | 206 | static inline int |
138 | dcss_diag (__u8 func, void *parameter, | 207 | dcss_diag(int *func, void *parameter, |
139 | unsigned long *ret1, unsigned long *ret2) | 208 | unsigned long *ret1, unsigned long *ret2) |
140 | { | 209 | { |
141 | unsigned long rx, ry; | 210 | unsigned long rx, ry; |
142 | int rc; | 211 | int rc; |
143 | 212 | ||
213 | if (scode_set == 0) { | ||
214 | rc = dcss_set_subcodes(); | ||
215 | if (rc < 0) | ||
216 | return rc; | ||
217 | scode_set = 1; | ||
218 | } | ||
144 | rx = (unsigned long) parameter; | 219 | rx = (unsigned long) parameter; |
145 | ry = (unsigned long) func; | 220 | ry = (unsigned long) *func; |
146 | asm volatile( | 221 | |
147 | #ifdef CONFIG_64BIT | 222 | #ifdef CONFIG_64BIT |
148 | " sam31\n" | 223 | /* 64-bit Diag x'64' new subcode, keep in 64-bit addressing mode */ |
149 | " diag %0,%1,0x64\n" | 224 | if (*func > DCSS_SEGEXT) |
150 | " sam64\n" | 225 | asm volatile( |
226 | " diag %0,%1,0x64\n" | ||
227 | " ipm %2\n" | ||
228 | " srl %2,28\n" | ||
229 | : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc"); | ||
230 | /* 31-bit Diag x'64' old subcode, switch to 31-bit addressing mode */ | ||
231 | else | ||
232 | asm volatile( | ||
233 | " sam31\n" | ||
234 | " diag %0,%1,0x64\n" | ||
235 | " sam64\n" | ||
236 | " ipm %2\n" | ||
237 | " srl %2,28\n" | ||
238 | : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc"); | ||
151 | #else | 239 | #else |
240 | asm volatile( | ||
152 | " diag %0,%1,0x64\n" | 241 | " diag %0,%1,0x64\n" |
153 | #endif | ||
154 | " ipm %2\n" | 242 | " ipm %2\n" |
155 | " srl %2,28\n" | 243 | " srl %2,28\n" |
156 | : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc"); | 244 | : "+d" (rx), "+d" (ry), "=d" (rc) : : "cc"); |
245 | #endif | ||
157 | *ret1 = rx; | 246 | *ret1 = rx; |
158 | *ret2 = ry; | 247 | *ret2 = ry; |
159 | return rc; | 248 | return rc; |
@@ -190,14 +279,45 @@ query_segment_type (struct dcss_segment *seg) | |||
190 | qin->qoutlen = sizeof(struct qout64); | 279 | qin->qoutlen = sizeof(struct qout64); |
191 | memcpy (qin->qname, seg->dcss_name, 8); | 280 | memcpy (qin->qname, seg->dcss_name, 8); |
192 | 281 | ||
193 | diag_cc = dcss_diag (DCSS_SEGEXT, qin, &dummy, &vmrc); | 282 | diag_cc = dcss_diag(&segext_scode, qin, &dummy, &vmrc); |
194 | 283 | ||
284 | if (diag_cc < 0) { | ||
285 | rc = diag_cc; | ||
286 | goto out_free; | ||
287 | } | ||
195 | if (diag_cc > 1) { | 288 | if (diag_cc > 1) { |
196 | PRINT_WARN ("segment_type: diag returned error %ld\n", vmrc); | 289 | PRINT_WARN ("segment_type: diag returned error %ld\n", vmrc); |
197 | rc = dcss_diag_translate_rc (vmrc); | 290 | rc = dcss_diag_translate_rc (vmrc); |
198 | goto out_free; | 291 | goto out_free; |
199 | } | 292 | } |
200 | 293 | ||
294 | #ifdef CONFIG_64BIT | ||
295 | /* Only old format of output area of Diagnose x'64' is supported, | ||
296 | copy data for the new format. */ | ||
297 | if (segext_scode == DCSS_SEGEXT) { | ||
298 | struct qout64_old *qout_old; | ||
299 | qout_old = kzalloc(sizeof(struct qout64_old), GFP_DMA); | ||
300 | if (qout_old == NULL) { | ||
301 | rc = -ENOMEM; | ||
302 | goto out_free; | ||
303 | } | ||
304 | memcpy(qout_old, qout, sizeof(struct qout64_old)); | ||
305 | qout->segstart = (unsigned long) qout_old->segstart; | ||
306 | qout->segend = (unsigned long) qout_old->segend; | ||
307 | qout->segcnt = qout_old->segcnt; | ||
308 | qout->segrcnt = qout_old->segrcnt; | ||
309 | |||
310 | if (qout->segcnt > 6) | ||
311 | qout->segrcnt = 6; | ||
312 | for (i = 0; i < qout->segrcnt; i++) { | ||
313 | qout->range[i].start = | ||
314 | (unsigned long) qout_old->range[i].start; | ||
315 | qout->range[i].end = | ||
316 | (unsigned long) qout_old->range[i].end; | ||
317 | } | ||
318 | kfree(qout_old); | ||
319 | } | ||
320 | #endif | ||
201 | if (qout->segcnt > 6) { | 321 | if (qout->segcnt > 6) { |
202 | rc = -ENOTSUPP; | 322 | rc = -ENOTSUPP; |
203 | goto out_free; | 323 | goto out_free; |
@@ -269,6 +389,30 @@ segment_type (char* name) | |||
269 | } | 389 | } |
270 | 390 | ||
271 | /* | 391 | /* |
392 | * check if segment collides with other segments that are currently loaded | ||
393 | * returns 1 if this is the case, 0 if no collision was found | ||
394 | */ | ||
395 | static int | ||
396 | segment_overlaps_others (struct dcss_segment *seg) | ||
397 | { | ||
398 | struct list_head *l; | ||
399 | struct dcss_segment *tmp; | ||
400 | |||
401 | BUG_ON(!mutex_is_locked(&dcss_lock)); | ||
402 | list_for_each(l, &dcss_list) { | ||
403 | tmp = list_entry(l, struct dcss_segment, list); | ||
404 | if ((tmp->start_addr >> 20) > (seg->end >> 20)) | ||
405 | continue; | ||
406 | if ((tmp->end >> 20) < (seg->start_addr >> 20)) | ||
407 | continue; | ||
408 | if (seg == tmp) | ||
409 | continue; | ||
410 | return 1; | ||
411 | } | ||
412 | return 0; | ||
413 | } | ||
414 | |||
415 | /* | ||
272 | * real segment loading function, called from segment_load | 416 | * real segment loading function, called from segment_load |
273 | */ | 417 | */ |
274 | static int | 418 | static int |
@@ -276,7 +420,8 @@ __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long | |||
276 | { | 420 | { |
277 | struct dcss_segment *seg = kmalloc(sizeof(struct dcss_segment), | 421 | struct dcss_segment *seg = kmalloc(sizeof(struct dcss_segment), |
278 | GFP_DMA); | 422 | GFP_DMA); |
279 | int dcss_command, rc, diag_cc; | 423 | int rc, diag_cc; |
424 | unsigned long start_addr, end_addr, dummy; | ||
280 | 425 | ||
281 | if (seg == NULL) { | 426 | if (seg == NULL) { |
282 | rc = -ENOMEM; | 427 | rc = -ENOMEM; |
@@ -287,6 +432,13 @@ __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long | |||
287 | if (rc < 0) | 432 | if (rc < 0) |
288 | goto out_free; | 433 | goto out_free; |
289 | 434 | ||
435 | if (loadshr_scode == DCSS_LOADSHRX) { | ||
436 | if (segment_overlaps_others(seg)) { | ||
437 | rc = -EBUSY; | ||
438 | goto out_free; | ||
439 | } | ||
440 | } | ||
441 | |||
290 | rc = vmem_add_mapping(seg->start_addr, seg->end - seg->start_addr + 1); | 442 | rc = vmem_add_mapping(seg->start_addr, seg->end - seg->start_addr + 1); |
291 | 443 | ||
292 | if (rc) | 444 | if (rc) |
@@ -316,20 +468,28 @@ __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long | |||
316 | } | 468 | } |
317 | 469 | ||
318 | if (do_nonshared) | 470 | if (do_nonshared) |
319 | dcss_command = DCSS_LOADNSR; | 471 | diag_cc = dcss_diag(&loadnsr_scode, seg->dcss_name, |
472 | &start_addr, &end_addr); | ||
320 | else | 473 | else |
321 | dcss_command = DCSS_LOADNOLY; | 474 | diag_cc = dcss_diag(&loadshr_scode, seg->dcss_name, |
322 | 475 | &start_addr, &end_addr); | |
323 | diag_cc = dcss_diag(dcss_command, seg->dcss_name, | 476 | if (diag_cc < 0) { |
324 | &seg->start_addr, &seg->end); | 477 | dcss_diag(&purgeseg_scode, seg->dcss_name, |
478 | &dummy, &dummy); | ||
479 | rc = diag_cc; | ||
480 | goto out_resource; | ||
481 | } | ||
325 | if (diag_cc > 1) { | 482 | if (diag_cc > 1) { |
326 | PRINT_WARN ("segment_load: could not load segment %s - " | 483 | PRINT_WARN ("segment_load: could not load segment %s - " |
327 | "diag returned error (%ld)\n",name,seg->end); | 484 | "diag returned error (%ld)\n", |
328 | rc = dcss_diag_translate_rc (seg->end); | 485 | name, end_addr); |
329 | dcss_diag(DCSS_PURGESEG, seg->dcss_name, | 486 | rc = dcss_diag_translate_rc(end_addr); |
330 | &seg->start_addr, &seg->end); | 487 | dcss_diag(&purgeseg_scode, seg->dcss_name, |
488 | &dummy, &dummy); | ||
331 | goto out_resource; | 489 | goto out_resource; |
332 | } | 490 | } |
491 | seg->start_addr = start_addr; | ||
492 | seg->end = end_addr; | ||
333 | seg->do_nonshared = do_nonshared; | 493 | seg->do_nonshared = do_nonshared; |
334 | atomic_set(&seg->ref_count, 1); | 494 | atomic_set(&seg->ref_count, 1); |
335 | list_add(&seg->list, &dcss_list); | 495 | list_add(&seg->list, &dcss_list); |
@@ -423,8 +583,8 @@ int | |||
423 | segment_modify_shared (char *name, int do_nonshared) | 583 | segment_modify_shared (char *name, int do_nonshared) |
424 | { | 584 | { |
425 | struct dcss_segment *seg; | 585 | struct dcss_segment *seg; |
426 | unsigned long dummy; | 586 | unsigned long start_addr, end_addr, dummy; |
427 | int dcss_command, rc, diag_cc; | 587 | int rc, diag_cc; |
428 | 588 | ||
429 | mutex_lock(&dcss_lock); | 589 | mutex_lock(&dcss_lock); |
430 | seg = segment_by_name (name); | 590 | seg = segment_by_name (name); |
@@ -445,38 +605,51 @@ segment_modify_shared (char *name, int do_nonshared) | |||
445 | goto out_unlock; | 605 | goto out_unlock; |
446 | } | 606 | } |
447 | release_resource(seg->res); | 607 | release_resource(seg->res); |
448 | if (do_nonshared) { | 608 | if (do_nonshared) |
449 | dcss_command = DCSS_LOADNSR; | ||
450 | seg->res->flags &= ~IORESOURCE_READONLY; | 609 | seg->res->flags &= ~IORESOURCE_READONLY; |
451 | } else { | 610 | else |
452 | dcss_command = DCSS_LOADNOLY; | ||
453 | if (seg->vm_segtype == SEG_TYPE_SR || | 611 | if (seg->vm_segtype == SEG_TYPE_SR || |
454 | seg->vm_segtype == SEG_TYPE_ER) | 612 | seg->vm_segtype == SEG_TYPE_ER) |
455 | seg->res->flags |= IORESOURCE_READONLY; | 613 | seg->res->flags |= IORESOURCE_READONLY; |
456 | } | 614 | |
457 | if (request_resource(&iomem_resource, seg->res)) { | 615 | if (request_resource(&iomem_resource, seg->res)) { |
458 | PRINT_WARN("segment_modify_shared: could not reload segment %s" | 616 | PRINT_WARN("segment_modify_shared: could not reload segment %s" |
459 | " - overlapping resources\n", name); | 617 | " - overlapping resources\n", name); |
460 | rc = -EBUSY; | 618 | rc = -EBUSY; |
461 | kfree(seg->res); | 619 | kfree(seg->res); |
462 | goto out_del; | 620 | goto out_del_mem; |
621 | } | ||
622 | |||
623 | dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy); | ||
624 | if (do_nonshared) | ||
625 | diag_cc = dcss_diag(&loadnsr_scode, seg->dcss_name, | ||
626 | &start_addr, &end_addr); | ||
627 | else | ||
628 | diag_cc = dcss_diag(&loadshr_scode, seg->dcss_name, | ||
629 | &start_addr, &end_addr); | ||
630 | if (diag_cc < 0) { | ||
631 | rc = diag_cc; | ||
632 | goto out_del_res; | ||
463 | } | 633 | } |
464 | dcss_diag(DCSS_PURGESEG, seg->dcss_name, &dummy, &dummy); | ||
465 | diag_cc = dcss_diag(dcss_command, seg->dcss_name, | ||
466 | &seg->start_addr, &seg->end); | ||
467 | if (diag_cc > 1) { | 634 | if (diag_cc > 1) { |
468 | PRINT_WARN ("segment_modify_shared: could not reload segment %s" | 635 | PRINT_WARN ("segment_modify_shared: could not reload segment %s" |
469 | " - diag returned error (%ld)\n",name,seg->end); | 636 | " - diag returned error (%ld)\n", |
470 | rc = dcss_diag_translate_rc (seg->end); | 637 | name, end_addr); |
471 | goto out_del; | 638 | rc = dcss_diag_translate_rc(end_addr); |
639 | goto out_del_res; | ||
472 | } | 640 | } |
641 | seg->start_addr = start_addr; | ||
642 | seg->end = end_addr; | ||
473 | seg->do_nonshared = do_nonshared; | 643 | seg->do_nonshared = do_nonshared; |
474 | rc = 0; | 644 | rc = 0; |
475 | goto out_unlock; | 645 | goto out_unlock; |
476 | out_del: | 646 | out_del_res: |
647 | release_resource(seg->res); | ||
648 | kfree(seg->res); | ||
649 | out_del_mem: | ||
477 | vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1); | 650 | vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1); |
478 | list_del(&seg->list); | 651 | list_del(&seg->list); |
479 | dcss_diag(DCSS_PURGESEG, seg->dcss_name, &dummy, &dummy); | 652 | dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy); |
480 | kfree(seg); | 653 | kfree(seg); |
481 | out_unlock: | 654 | out_unlock: |
482 | mutex_unlock(&dcss_lock); | 655 | mutex_unlock(&dcss_lock); |
@@ -510,7 +683,7 @@ segment_unload(char *name) | |||
510 | kfree(seg->res); | 683 | kfree(seg->res); |
511 | vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1); | 684 | vmem_remove_mapping(seg->start_addr, seg->end - seg->start_addr + 1); |
512 | list_del(&seg->list); | 685 | list_del(&seg->list); |
513 | dcss_diag(DCSS_PURGESEG, seg->dcss_name, &dummy, &dummy); | 686 | dcss_diag(&purgeseg_scode, seg->dcss_name, &dummy, &dummy); |
514 | kfree(seg); | 687 | kfree(seg); |
515 | out_unlock: | 688 | out_unlock: |
516 | mutex_unlock(&dcss_lock); | 689 | mutex_unlock(&dcss_lock); |
@@ -545,7 +718,7 @@ segment_save(char *name) | |||
545 | endpfn = (seg->end) >> PAGE_SHIFT; | 718 | endpfn = (seg->end) >> PAGE_SHIFT; |
546 | sprintf(cmd1, "DEFSEG %s", name); | 719 | sprintf(cmd1, "DEFSEG %s", name); |
547 | for (i=0; i<seg->segcnt; i++) { | 720 | for (i=0; i<seg->segcnt; i++) { |
548 | sprintf(cmd1+strlen(cmd1), " %X-%X %s", | 721 | sprintf(cmd1+strlen(cmd1), " %lX-%lX %s", |
549 | seg->range[i].start >> PAGE_SHIFT, | 722 | seg->range[i].start >> PAGE_SHIFT, |
550 | seg->range[i].end >> PAGE_SHIFT, | 723 | seg->range[i].end >> PAGE_SHIFT, |
551 | segtype_string[seg->range[i].start & 0xff]); | 724 | segtype_string[seg->range[i].start & 0xff]); |
diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c index ea4272c8c677..a7ff167d5b81 100644 --- a/drivers/s390/block/dcssblk.c +++ b/drivers/s390/block/dcssblk.c | |||
@@ -31,7 +31,6 @@ | |||
31 | #define PRINT_WARN(x...) printk(KERN_WARNING DCSSBLK_NAME " warning: " x) | 31 | #define PRINT_WARN(x...) printk(KERN_WARNING DCSSBLK_NAME " warning: " x) |
32 | #define PRINT_ERR(x...) printk(KERN_ERR DCSSBLK_NAME " error: " x) | 32 | #define PRINT_ERR(x...) printk(KERN_ERR DCSSBLK_NAME " error: " x) |
33 | 33 | ||
34 | |||
35 | static int dcssblk_open(struct inode *inode, struct file *filp); | 34 | static int dcssblk_open(struct inode *inode, struct file *filp); |
36 | static int dcssblk_release(struct inode *inode, struct file *filp); | 35 | static int dcssblk_release(struct inode *inode, struct file *filp); |
37 | static int dcssblk_make_request(struct request_queue *q, struct bio *bio); | 36 | static int dcssblk_make_request(struct request_queue *q, struct bio *bio); |
@@ -48,6 +47,30 @@ static struct block_device_operations dcssblk_devops = { | |||
48 | .direct_access = dcssblk_direct_access, | 47 | .direct_access = dcssblk_direct_access, |
49 | }; | 48 | }; |
50 | 49 | ||
50 | struct dcssblk_dev_info { | ||
51 | struct list_head lh; | ||
52 | struct device dev; | ||
53 | char segment_name[BUS_ID_SIZE]; | ||
54 | atomic_t use_count; | ||
55 | struct gendisk *gd; | ||
56 | unsigned long start; | ||
57 | unsigned long end; | ||
58 | int segment_type; | ||
59 | unsigned char save_pending; | ||
60 | unsigned char is_shared; | ||
61 | struct request_queue *dcssblk_queue; | ||
62 | int num_of_segments; | ||
63 | struct list_head seg_list; | ||
64 | }; | ||
65 | |||
66 | struct segment_info { | ||
67 | struct list_head lh; | ||
68 | char segment_name[BUS_ID_SIZE]; | ||
69 | unsigned long start; | ||
70 | unsigned long end; | ||
71 | int segment_type; | ||
72 | }; | ||
73 | |||
51 | static ssize_t dcssblk_add_store(struct device * dev, struct device_attribute *attr, const char * buf, | 74 | static ssize_t dcssblk_add_store(struct device * dev, struct device_attribute *attr, const char * buf, |
52 | size_t count); | 75 | size_t count); |
53 | static ssize_t dcssblk_remove_store(struct device * dev, struct device_attribute *attr, const char * buf, | 76 | static ssize_t dcssblk_remove_store(struct device * dev, struct device_attribute *attr, const char * buf, |
@@ -58,30 +81,20 @@ static ssize_t dcssblk_save_show(struct device *dev, struct device_attribute *at | |||
58 | static ssize_t dcssblk_shared_store(struct device * dev, struct device_attribute *attr, const char * buf, | 81 | static ssize_t dcssblk_shared_store(struct device * dev, struct device_attribute *attr, const char * buf, |
59 | size_t count); | 82 | size_t count); |
60 | static ssize_t dcssblk_shared_show(struct device *dev, struct device_attribute *attr, char *buf); | 83 | static ssize_t dcssblk_shared_show(struct device *dev, struct device_attribute *attr, char *buf); |
84 | static ssize_t dcssblk_seglist_show(struct device *dev, | ||
85 | struct device_attribute *attr, | ||
86 | char *buf); | ||
61 | 87 | ||
62 | static DEVICE_ATTR(add, S_IWUSR, NULL, dcssblk_add_store); | 88 | static DEVICE_ATTR(add, S_IWUSR, NULL, dcssblk_add_store); |
63 | static DEVICE_ATTR(remove, S_IWUSR, NULL, dcssblk_remove_store); | 89 | static DEVICE_ATTR(remove, S_IWUSR, NULL, dcssblk_remove_store); |
64 | static DEVICE_ATTR(save, S_IWUSR | S_IRUGO, dcssblk_save_show, | 90 | static DEVICE_ATTR(save, S_IWUSR | S_IRUSR, dcssblk_save_show, |
65 | dcssblk_save_store); | 91 | dcssblk_save_store); |
66 | static DEVICE_ATTR(shared, S_IWUSR | S_IRUGO, dcssblk_shared_show, | 92 | static DEVICE_ATTR(shared, S_IWUSR | S_IRUSR, dcssblk_shared_show, |
67 | dcssblk_shared_store); | 93 | dcssblk_shared_store); |
94 | static DEVICE_ATTR(seglist, S_IRUSR, dcssblk_seglist_show, NULL); | ||
68 | 95 | ||
69 | static struct device *dcssblk_root_dev; | 96 | static struct device *dcssblk_root_dev; |
70 | 97 | ||
71 | struct dcssblk_dev_info { | ||
72 | struct list_head lh; | ||
73 | struct device dev; | ||
74 | char segment_name[BUS_ID_SIZE]; | ||
75 | atomic_t use_count; | ||
76 | struct gendisk *gd; | ||
77 | unsigned long start; | ||
78 | unsigned long end; | ||
79 | int segment_type; | ||
80 | unsigned char save_pending; | ||
81 | unsigned char is_shared; | ||
82 | struct request_queue *dcssblk_queue; | ||
83 | }; | ||
84 | |||
85 | static LIST_HEAD(dcssblk_devices); | 98 | static LIST_HEAD(dcssblk_devices); |
86 | static struct rw_semaphore dcssblk_devices_sem; | 99 | static struct rw_semaphore dcssblk_devices_sem; |
87 | 100 | ||
@@ -91,8 +104,15 @@ static struct rw_semaphore dcssblk_devices_sem; | |||
91 | static void | 104 | static void |
92 | dcssblk_release_segment(struct device *dev) | 105 | dcssblk_release_segment(struct device *dev) |
93 | { | 106 | { |
94 | PRINT_DEBUG("segment release fn called for %s\n", dev_name(dev)); | 107 | struct dcssblk_dev_info *dev_info; |
95 | kfree(container_of(dev, struct dcssblk_dev_info, dev)); | 108 | struct segment_info *entry, *temp; |
109 | |||
110 | dev_info = container_of(dev, struct dcssblk_dev_info, dev); | ||
111 | list_for_each_entry_safe(entry, temp, &dev_info->seg_list, lh) { | ||
112 | list_del(&entry->lh); | ||
113 | kfree(entry); | ||
114 | } | ||
115 | kfree(dev_info); | ||
96 | module_put(THIS_MODULE); | 116 | module_put(THIS_MODULE); |
97 | } | 117 | } |
98 | 118 | ||
@@ -142,6 +162,169 @@ dcssblk_get_device_by_name(char *name) | |||
142 | return NULL; | 162 | return NULL; |
143 | } | 163 | } |
144 | 164 | ||
165 | /* | ||
166 | * get the struct segment_info from seg_list | ||
167 | * for the given name. | ||
168 | * down_read(&dcssblk_devices_sem) must be held. | ||
169 | */ | ||
170 | static struct segment_info * | ||
171 | dcssblk_get_segment_by_name(char *name) | ||
172 | { | ||
173 | struct dcssblk_dev_info *dev_info; | ||
174 | struct segment_info *entry; | ||
175 | |||
176 | list_for_each_entry(dev_info, &dcssblk_devices, lh) { | ||
177 | list_for_each_entry(entry, &dev_info->seg_list, lh) { | ||
178 | if (!strcmp(name, entry->segment_name)) | ||
179 | return entry; | ||
180 | } | ||
181 | } | ||
182 | return NULL; | ||
183 | } | ||
184 | |||
185 | /* | ||
186 | * get the highest address of the multi-segment block. | ||
187 | */ | ||
188 | static unsigned long | ||
189 | dcssblk_find_highest_addr(struct dcssblk_dev_info *dev_info) | ||
190 | { | ||
191 | unsigned long highest_addr; | ||
192 | struct segment_info *entry; | ||
193 | |||
194 | highest_addr = 0; | ||
195 | list_for_each_entry(entry, &dev_info->seg_list, lh) { | ||
196 | if (highest_addr < entry->end) | ||
197 | highest_addr = entry->end; | ||
198 | } | ||
199 | return highest_addr; | ||
200 | } | ||
201 | |||
202 | /* | ||
203 | * get the lowest address of the multi-segment block. | ||
204 | */ | ||
205 | static unsigned long | ||
206 | dcssblk_find_lowest_addr(struct dcssblk_dev_info *dev_info) | ||
207 | { | ||
208 | int set_first; | ||
209 | unsigned long lowest_addr; | ||
210 | struct segment_info *entry; | ||
211 | |||
212 | set_first = 0; | ||
213 | lowest_addr = 0; | ||
214 | list_for_each_entry(entry, &dev_info->seg_list, lh) { | ||
215 | if (set_first == 0) { | ||
216 | lowest_addr = entry->start; | ||
217 | set_first = 1; | ||
218 | } else { | ||
219 | if (lowest_addr > entry->start) | ||
220 | lowest_addr = entry->start; | ||
221 | } | ||
222 | } | ||
223 | return lowest_addr; | ||
224 | } | ||
225 | |||
226 | /* | ||
227 | * Check continuity of segments. | ||
228 | */ | ||
229 | static int | ||
230 | dcssblk_is_continuous(struct dcssblk_dev_info *dev_info) | ||
231 | { | ||
232 | int i, j, rc; | ||
233 | struct segment_info *sort_list, *entry, temp; | ||
234 | |||
235 | if (dev_info->num_of_segments <= 1) | ||
236 | return 0; | ||
237 | |||
238 | sort_list = kzalloc( | ||
239 | sizeof(struct segment_info) * dev_info->num_of_segments, | ||
240 | GFP_KERNEL); | ||
241 | if (sort_list == NULL) | ||
242 | return -ENOMEM; | ||
243 | i = 0; | ||
244 | list_for_each_entry(entry, &dev_info->seg_list, lh) { | ||
245 | memcpy(&sort_list[i], entry, sizeof(struct segment_info)); | ||
246 | i++; | ||
247 | } | ||
248 | |||
249 | /* sort segments */ | ||
250 | for (i = 0; i < dev_info->num_of_segments; i++) | ||
251 | for (j = 0; j < dev_info->num_of_segments; j++) | ||
252 | if (sort_list[j].start > sort_list[i].start) { | ||
253 | memcpy(&temp, &sort_list[i], | ||
254 | sizeof(struct segment_info)); | ||
255 | memcpy(&sort_list[i], &sort_list[j], | ||
256 | sizeof(struct segment_info)); | ||
257 | memcpy(&sort_list[j], &temp, | ||
258 | sizeof(struct segment_info)); | ||
259 | } | ||
260 | |||
261 | /* check continuity */ | ||
262 | for (i = 0; i < dev_info->num_of_segments - 1; i++) { | ||
263 | if ((sort_list[i].end + 1) != sort_list[i+1].start) { | ||
264 | PRINT_ERR("Segment %s is not contiguous with " | ||
265 | "segment %s\n", | ||
266 | sort_list[i].segment_name, | ||
267 | sort_list[i+1].segment_name); | ||
268 | rc = -EINVAL; | ||
269 | goto out; | ||
270 | } | ||
271 | /* EN and EW are allowed in a block device */ | ||
272 | if (sort_list[i].segment_type != sort_list[i+1].segment_type) { | ||
273 | if (!(sort_list[i].segment_type & SEGMENT_EXCLUSIVE) || | ||
274 | (sort_list[i].segment_type == SEG_TYPE_ER) || | ||
275 | !(sort_list[i+1].segment_type & | ||
276 | SEGMENT_EXCLUSIVE) || | ||
277 | (sort_list[i+1].segment_type == SEG_TYPE_ER)) { | ||
278 | PRINT_ERR("Segment %s has different type from " | ||
279 | "segment %s\n", | ||
280 | sort_list[i].segment_name, | ||
281 | sort_list[i+1].segment_name); | ||
282 | rc = -EINVAL; | ||
283 | goto out; | ||
284 | } | ||
285 | } | ||
286 | } | ||
287 | rc = 0; | ||
288 | out: | ||
289 | kfree(sort_list); | ||
290 | return rc; | ||
291 | } | ||
292 | |||
293 | /* | ||
294 | * Load a segment | ||
295 | */ | ||
296 | static int | ||
297 | dcssblk_load_segment(char *name, struct segment_info **seg_info) | ||
298 | { | ||
299 | int rc; | ||
300 | |||
301 | /* already loaded? */ | ||
302 | down_read(&dcssblk_devices_sem); | ||
303 | *seg_info = dcssblk_get_segment_by_name(name); | ||
304 | up_read(&dcssblk_devices_sem); | ||
305 | if (*seg_info != NULL) | ||
306 | return -EEXIST; | ||
307 | |||
308 | /* get a struct segment_info */ | ||
309 | *seg_info = kzalloc(sizeof(struct segment_info), GFP_KERNEL); | ||
310 | if (*seg_info == NULL) | ||
311 | return -ENOMEM; | ||
312 | |||
313 | strcpy((*seg_info)->segment_name, name); | ||
314 | |||
315 | /* load the segment */ | ||
316 | rc = segment_load(name, SEGMENT_SHARED, | ||
317 | &(*seg_info)->start, &(*seg_info)->end); | ||
318 | if (rc < 0) { | ||
319 | segment_warning(rc, (*seg_info)->segment_name); | ||
320 | kfree(*seg_info); | ||
321 | } else { | ||
322 | INIT_LIST_HEAD(&(*seg_info)->lh); | ||
323 | (*seg_info)->segment_type = rc; | ||
324 | } | ||
325 | return rc; | ||
326 | } | ||
327 | |||
145 | static void dcssblk_unregister_callback(struct device *dev) | 328 | static void dcssblk_unregister_callback(struct device *dev) |
146 | { | 329 | { |
147 | device_unregister(dev); | 330 | device_unregister(dev); |
@@ -165,6 +348,7 @@ static ssize_t | |||
165 | dcssblk_shared_store(struct device *dev, struct device_attribute *attr, const char *inbuf, size_t count) | 348 | dcssblk_shared_store(struct device *dev, struct device_attribute *attr, const char *inbuf, size_t count) |
166 | { | 349 | { |
167 | struct dcssblk_dev_info *dev_info; | 350 | struct dcssblk_dev_info *dev_info; |
351 | struct segment_info *entry, *temp; | ||
168 | int rc; | 352 | int rc; |
169 | 353 | ||
170 | if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0')) | 354 | if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0')) |
@@ -172,46 +356,46 @@ dcssblk_shared_store(struct device *dev, struct device_attribute *attr, const ch | |||
172 | down_write(&dcssblk_devices_sem); | 356 | down_write(&dcssblk_devices_sem); |
173 | dev_info = container_of(dev, struct dcssblk_dev_info, dev); | 357 | dev_info = container_of(dev, struct dcssblk_dev_info, dev); |
174 | if (atomic_read(&dev_info->use_count)) { | 358 | if (atomic_read(&dev_info->use_count)) { |
175 | PRINT_ERR("share: segment %s is busy!\n", | ||
176 | dev_info->segment_name); | ||
177 | rc = -EBUSY; | 359 | rc = -EBUSY; |
178 | goto out; | 360 | goto out; |
179 | } | 361 | } |
180 | if (inbuf[0] == '1') { | 362 | if (inbuf[0] == '1') { |
181 | // reload segment in shared mode | 363 | /* reload segments in shared mode */ |
182 | rc = segment_modify_shared(dev_info->segment_name, | 364 | list_for_each_entry(entry, &dev_info->seg_list, lh) { |
183 | SEGMENT_SHARED); | 365 | rc = segment_modify_shared(entry->segment_name, |
184 | if (rc < 0) { | 366 | SEGMENT_SHARED); |
185 | BUG_ON(rc == -EINVAL); | 367 | if (rc < 0) { |
186 | if (rc != -EAGAIN) | 368 | BUG_ON(rc == -EINVAL); |
187 | goto removeseg; | 369 | if (rc != -EAGAIN) |
188 | } else { | 370 | goto removeseg; |
189 | dev_info->is_shared = 1; | ||
190 | switch (dev_info->segment_type) { | ||
191 | case SEG_TYPE_SR: | ||
192 | case SEG_TYPE_ER: | ||
193 | case SEG_TYPE_SC: | ||
194 | set_disk_ro(dev_info->gd,1); | ||
195 | } | 371 | } |
196 | } | 372 | } |
373 | dev_info->is_shared = 1; | ||
374 | switch (dev_info->segment_type) { | ||
375 | case SEG_TYPE_SR: | ||
376 | case SEG_TYPE_ER: | ||
377 | case SEG_TYPE_SC: | ||
378 | set_disk_ro(dev_info->gd, 1); | ||
379 | } | ||
197 | } else if (inbuf[0] == '0') { | 380 | } else if (inbuf[0] == '0') { |
198 | // reload segment in exclusive mode | 381 | /* reload segments in exclusive mode */ |
199 | if (dev_info->segment_type == SEG_TYPE_SC) { | 382 | if (dev_info->segment_type == SEG_TYPE_SC) { |
200 | PRINT_ERR("Segment type SC (%s) cannot be loaded in " | 383 | PRINT_ERR("Segment type SC (%s) cannot be loaded in " |
201 | "non-shared mode\n", dev_info->segment_name); | 384 | "non-shared mode\n", dev_info->segment_name); |
202 | rc = -EINVAL; | 385 | rc = -EINVAL; |
203 | goto out; | 386 | goto out; |
204 | } | 387 | } |
205 | rc = segment_modify_shared(dev_info->segment_name, | 388 | list_for_each_entry(entry, &dev_info->seg_list, lh) { |
206 | SEGMENT_EXCLUSIVE); | 389 | rc = segment_modify_shared(entry->segment_name, |
207 | if (rc < 0) { | 390 | SEGMENT_EXCLUSIVE); |
208 | BUG_ON(rc == -EINVAL); | 391 | if (rc < 0) { |
209 | if (rc != -EAGAIN) | 392 | BUG_ON(rc == -EINVAL); |
210 | goto removeseg; | 393 | if (rc != -EAGAIN) |
211 | } else { | 394 | goto removeseg; |
212 | dev_info->is_shared = 0; | 395 | } |
213 | set_disk_ro(dev_info->gd, 0); | ||
214 | } | 396 | } |
397 | dev_info->is_shared = 0; | ||
398 | set_disk_ro(dev_info->gd, 0); | ||
215 | } else { | 399 | } else { |
216 | rc = -EINVAL; | 400 | rc = -EINVAL; |
217 | goto out; | 401 | goto out; |
@@ -220,8 +404,14 @@ dcssblk_shared_store(struct device *dev, struct device_attribute *attr, const ch | |||
220 | goto out; | 404 | goto out; |
221 | 405 | ||
222 | removeseg: | 406 | removeseg: |
223 | PRINT_ERR("Could not reload segment %s, removing it now!\n", | 407 | PRINT_ERR("Could not reload segment(s) of the device %s, removing " |
224 | dev_info->segment_name); | 408 | "segment(s) now!\n", |
409 | dev_info->segment_name); | ||
410 | temp = entry; | ||
411 | list_for_each_entry(entry, &dev_info->seg_list, lh) { | ||
412 | if (entry != temp) | ||
413 | segment_unload(entry->segment_name); | ||
414 | } | ||
225 | list_del(&dev_info->lh); | 415 | list_del(&dev_info->lh); |
226 | 416 | ||
227 | del_gendisk(dev_info->gd); | 417 | del_gendisk(dev_info->gd); |
@@ -254,6 +444,7 @@ static ssize_t | |||
254 | dcssblk_save_store(struct device *dev, struct device_attribute *attr, const char *inbuf, size_t count) | 444 | dcssblk_save_store(struct device *dev, struct device_attribute *attr, const char *inbuf, size_t count) |
255 | { | 445 | { |
256 | struct dcssblk_dev_info *dev_info; | 446 | struct dcssblk_dev_info *dev_info; |
447 | struct segment_info *entry; | ||
257 | 448 | ||
258 | if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0')) | 449 | if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0')) |
259 | return -EINVAL; | 450 | return -EINVAL; |
@@ -263,14 +454,16 @@ dcssblk_save_store(struct device *dev, struct device_attribute *attr, const char | |||
263 | if (inbuf[0] == '1') { | 454 | if (inbuf[0] == '1') { |
264 | if (atomic_read(&dev_info->use_count) == 0) { | 455 | if (atomic_read(&dev_info->use_count) == 0) { |
265 | // device is idle => we save immediately | 456 | // device is idle => we save immediately |
266 | PRINT_INFO("Saving segment %s\n", | 457 | PRINT_INFO("Saving segment(s) of the device %s\n", |
267 | dev_info->segment_name); | 458 | dev_info->segment_name); |
268 | segment_save(dev_info->segment_name); | 459 | list_for_each_entry(entry, &dev_info->seg_list, lh) { |
460 | segment_save(entry->segment_name); | ||
461 | } | ||
269 | } else { | 462 | } else { |
270 | // device is busy => we save it when it becomes | 463 | // device is busy => we save it when it becomes |
271 | // idle in dcssblk_release | 464 | // idle in dcssblk_release |
272 | PRINT_INFO("Segment %s is currently busy, it will " | 465 | PRINT_INFO("Device %s is currently busy, segment(s) " |
273 | "be saved when it becomes idle...\n", | 466 | "will be saved when it becomes idle...\n", |
274 | dev_info->segment_name); | 467 | dev_info->segment_name); |
275 | dev_info->save_pending = 1; | 468 | dev_info->save_pending = 1; |
276 | } | 469 | } |
@@ -279,7 +472,8 @@ dcssblk_save_store(struct device *dev, struct device_attribute *attr, const char | |||
279 | // device is busy & the user wants to undo his save | 472 | // device is busy & the user wants to undo his save |
280 | // request | 473 | // request |
281 | dev_info->save_pending = 0; | 474 | dev_info->save_pending = 0; |
282 | PRINT_INFO("Pending save for segment %s deactivated\n", | 475 | PRINT_INFO("Pending save for segment(s) of the device " |
476 | "%s deactivated\n", | ||
283 | dev_info->segment_name); | 477 | dev_info->segment_name); |
284 | } | 478 | } |
285 | } else { | 479 | } else { |
@@ -291,66 +485,123 @@ dcssblk_save_store(struct device *dev, struct device_attribute *attr, const char | |||
291 | } | 485 | } |
292 | 486 | ||
293 | /* | 487 | /* |
488 | * device attribute for showing all segments in a device | ||
489 | */ | ||
490 | static ssize_t | ||
491 | dcssblk_seglist_show(struct device *dev, struct device_attribute *attr, | ||
492 | char *buf) | ||
493 | { | ||
494 | int i; | ||
495 | |||
496 | struct dcssblk_dev_info *dev_info; | ||
497 | struct segment_info *entry; | ||
498 | |||
499 | down_read(&dcssblk_devices_sem); | ||
500 | dev_info = container_of(dev, struct dcssblk_dev_info, dev); | ||
501 | i = 0; | ||
502 | buf[0] = '\0'; | ||
503 | list_for_each_entry(entry, &dev_info->seg_list, lh) { | ||
504 | strcpy(&buf[i], entry->segment_name); | ||
505 | i += strlen(entry->segment_name); | ||
506 | buf[i] = '\n'; | ||
507 | i++; | ||
508 | } | ||
509 | up_read(&dcssblk_devices_sem); | ||
510 | return i; | ||
511 | } | ||
512 | |||
513 | /* | ||
294 | * device attribute for adding devices | 514 | * device attribute for adding devices |
295 | */ | 515 | */ |
296 | static ssize_t | 516 | static ssize_t |
297 | dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) | 517 | dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) |
298 | { | 518 | { |
299 | int rc, i; | 519 | int rc, i, j, num_of_segments; |
300 | struct dcssblk_dev_info *dev_info; | 520 | struct dcssblk_dev_info *dev_info; |
521 | struct segment_info *seg_info, *temp; | ||
301 | char *local_buf; | 522 | char *local_buf; |
302 | unsigned long seg_byte_size; | 523 | unsigned long seg_byte_size; |
303 | 524 | ||
304 | dev_info = NULL; | 525 | dev_info = NULL; |
526 | seg_info = NULL; | ||
305 | if (dev != dcssblk_root_dev) { | 527 | if (dev != dcssblk_root_dev) { |
306 | rc = -EINVAL; | 528 | rc = -EINVAL; |
307 | goto out_nobuf; | 529 | goto out_nobuf; |
308 | } | 530 | } |
531 | if ((count < 1) || (buf[0] == '\0') || (buf[0] == '\n')) { | ||
532 | rc = -ENAMETOOLONG; | ||
533 | goto out_nobuf; | ||
534 | } | ||
535 | |||
309 | local_buf = kmalloc(count + 1, GFP_KERNEL); | 536 | local_buf = kmalloc(count + 1, GFP_KERNEL); |
310 | if (local_buf == NULL) { | 537 | if (local_buf == NULL) { |
311 | rc = -ENOMEM; | 538 | rc = -ENOMEM; |
312 | goto out_nobuf; | 539 | goto out_nobuf; |
313 | } | 540 | } |
541 | |||
314 | /* | 542 | /* |
315 | * parse input | 543 | * parse input |
316 | */ | 544 | */ |
545 | num_of_segments = 0; | ||
317 | for (i = 0; ((buf[i] != '\0') && (buf[i] != '\n') && i < count); i++) { | 546 | for (i = 0; ((buf[i] != '\0') && (buf[i] != '\n') && i < count); i++) { |
318 | local_buf[i] = toupper(buf[i]); | 547 | for (j = i; (buf[j] != ':') && |
548 | (buf[j] != '\0') && | ||
549 | (buf[j] != '\n') && | ||
550 | j < count; j++) { | ||
551 | local_buf[j-i] = toupper(buf[j]); | ||
552 | } | ||
553 | local_buf[j-i] = '\0'; | ||
554 | if (((j - i) == 0) || ((j - i) > 8)) { | ||
555 | rc = -ENAMETOOLONG; | ||
556 | goto seg_list_del; | ||
557 | } | ||
558 | |||
559 | rc = dcssblk_load_segment(local_buf, &seg_info); | ||
560 | if (rc < 0) | ||
561 | goto seg_list_del; | ||
562 | /* | ||
563 | * get a struct dcssblk_dev_info | ||
564 | */ | ||
565 | if (num_of_segments == 0) { | ||
566 | dev_info = kzalloc(sizeof(struct dcssblk_dev_info), | ||
567 | GFP_KERNEL); | ||
568 | if (dev_info == NULL) { | ||
569 | rc = -ENOMEM; | ||
570 | goto out; | ||
571 | } | ||
572 | strcpy(dev_info->segment_name, local_buf); | ||
573 | dev_info->segment_type = seg_info->segment_type; | ||
574 | INIT_LIST_HEAD(&dev_info->seg_list); | ||
575 | } | ||
576 | list_add_tail(&seg_info->lh, &dev_info->seg_list); | ||
577 | num_of_segments++; | ||
578 | i = j; | ||
579 | |||
580 | if ((buf[j] == '\0') || (buf[j] == '\n')) | ||
581 | break; | ||
319 | } | 582 | } |
320 | local_buf[i] = '\0'; | 583 | |
321 | if ((i == 0) || (i > 8)) { | 584 | /* no trailing colon at the end of the input */ |
585 | if ((i > 0) && (buf[i-1] == ':')) { | ||
322 | rc = -ENAMETOOLONG; | 586 | rc = -ENAMETOOLONG; |
323 | goto out; | 587 | goto seg_list_del; |
324 | } | ||
325 | /* | ||
326 | * already loaded? | ||
327 | */ | ||
328 | down_read(&dcssblk_devices_sem); | ||
329 | dev_info = dcssblk_get_device_by_name(local_buf); | ||
330 | up_read(&dcssblk_devices_sem); | ||
331 | if (dev_info != NULL) { | ||
332 | PRINT_WARN("Segment %s already loaded!\n", local_buf); | ||
333 | rc = -EEXIST; | ||
334 | goto out; | ||
335 | } | ||
336 | /* | ||
337 | * get a struct dcssblk_dev_info | ||
338 | */ | ||
339 | dev_info = kzalloc(sizeof(struct dcssblk_dev_info), GFP_KERNEL); | ||
340 | if (dev_info == NULL) { | ||
341 | rc = -ENOMEM; | ||
342 | goto out; | ||
343 | } | 588 | } |
589 | strlcpy(local_buf, buf, i + 1); | ||
590 | dev_info->num_of_segments = num_of_segments; | ||
591 | rc = dcssblk_is_continuous(dev_info); | ||
592 | if (rc < 0) | ||
593 | goto seg_list_del; | ||
594 | |||
595 | dev_info->start = dcssblk_find_lowest_addr(dev_info); | ||
596 | dev_info->end = dcssblk_find_highest_addr(dev_info); | ||
344 | 597 | ||
345 | strcpy(dev_info->segment_name, local_buf); | 598 | dev_set_name(&dev_info->dev, dev_info->segment_name); |
346 | dev_set_name(&dev_info->dev, local_buf); | ||
347 | dev_info->dev.release = dcssblk_release_segment; | 599 | dev_info->dev.release = dcssblk_release_segment; |
348 | INIT_LIST_HEAD(&dev_info->lh); | 600 | INIT_LIST_HEAD(&dev_info->lh); |
349 | |||
350 | dev_info->gd = alloc_disk(DCSSBLK_MINORS_PER_DISK); | 601 | dev_info->gd = alloc_disk(DCSSBLK_MINORS_PER_DISK); |
351 | if (dev_info->gd == NULL) { | 602 | if (dev_info->gd == NULL) { |
352 | rc = -ENOMEM; | 603 | rc = -ENOMEM; |
353 | goto free_dev_info; | 604 | goto seg_list_del; |
354 | } | 605 | } |
355 | dev_info->gd->major = dcssblk_major; | 606 | dev_info->gd->major = dcssblk_major; |
356 | dev_info->gd->fops = &dcssblk_devops; | 607 | dev_info->gd->fops = &dcssblk_devops; |
@@ -360,59 +611,43 @@ dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char | |||
360 | dev_info->gd->driverfs_dev = &dev_info->dev; | 611 | dev_info->gd->driverfs_dev = &dev_info->dev; |
361 | blk_queue_make_request(dev_info->dcssblk_queue, dcssblk_make_request); | 612 | blk_queue_make_request(dev_info->dcssblk_queue, dcssblk_make_request); |
362 | blk_queue_hardsect_size(dev_info->dcssblk_queue, 4096); | 613 | blk_queue_hardsect_size(dev_info->dcssblk_queue, 4096); |
363 | /* | 614 | |
364 | * load the segment | ||
365 | */ | ||
366 | rc = segment_load(local_buf, SEGMENT_SHARED, | ||
367 | &dev_info->start, &dev_info->end); | ||
368 | if (rc < 0) { | ||
369 | segment_warning(rc, dev_info->segment_name); | ||
370 | goto dealloc_gendisk; | ||
371 | } | ||
372 | seg_byte_size = (dev_info->end - dev_info->start + 1); | 615 | seg_byte_size = (dev_info->end - dev_info->start + 1); |
373 | set_capacity(dev_info->gd, seg_byte_size >> 9); // size in sectors | 616 | set_capacity(dev_info->gd, seg_byte_size >> 9); // size in sectors |
374 | PRINT_INFO("Loaded segment %s, size = %lu Byte, " | 617 | PRINT_INFO("Loaded segment(s) %s, size = %lu Byte, " |
375 | "capacity = %lu (512 Byte) sectors\n", local_buf, | 618 | "capacity = %lu (512 Byte) sectors\n", local_buf, |
376 | seg_byte_size, seg_byte_size >> 9); | 619 | seg_byte_size, seg_byte_size >> 9); |
377 | 620 | ||
378 | dev_info->segment_type = rc; | ||
379 | dev_info->save_pending = 0; | 621 | dev_info->save_pending = 0; |
380 | dev_info->is_shared = 1; | 622 | dev_info->is_shared = 1; |
381 | dev_info->dev.parent = dcssblk_root_dev; | 623 | dev_info->dev.parent = dcssblk_root_dev; |
382 | 624 | ||
383 | /* | 625 | /* |
384 | * get minor, add to list | 626 | *get minor, add to list |
385 | */ | 627 | */ |
386 | down_write(&dcssblk_devices_sem); | 628 | down_write(&dcssblk_devices_sem); |
387 | if (dcssblk_get_device_by_name(local_buf)) { | 629 | if (dcssblk_get_segment_by_name(local_buf)) { |
388 | up_write(&dcssblk_devices_sem); | ||
389 | rc = -EEXIST; | 630 | rc = -EEXIST; |
390 | goto unload_seg; | 631 | goto release_gd; |
391 | } | 632 | } |
392 | rc = dcssblk_assign_free_minor(dev_info); | 633 | rc = dcssblk_assign_free_minor(dev_info); |
393 | if (rc) { | 634 | if (rc) |
394 | up_write(&dcssblk_devices_sem); | 635 | goto release_gd; |
395 | PRINT_ERR("No free minor number available! " | ||
396 | "Unloading segment...\n"); | ||
397 | goto unload_seg; | ||
398 | } | ||
399 | sprintf(dev_info->gd->disk_name, "dcssblk%d", | 636 | sprintf(dev_info->gd->disk_name, "dcssblk%d", |
400 | MINOR(disk_devt(dev_info->gd))); | 637 | MINOR(disk_devt(dev_info->gd))); |
401 | list_add_tail(&dev_info->lh, &dcssblk_devices); | 638 | list_add_tail(&dev_info->lh, &dcssblk_devices); |
402 | 639 | ||
403 | if (!try_module_get(THIS_MODULE)) { | 640 | if (!try_module_get(THIS_MODULE)) { |
404 | rc = -ENODEV; | 641 | rc = -ENODEV; |
405 | goto list_del; | 642 | goto dev_list_del; |
406 | } | 643 | } |
407 | /* | 644 | /* |
408 | * register the device | 645 | * register the device |
409 | */ | 646 | */ |
410 | rc = device_register(&dev_info->dev); | 647 | rc = device_register(&dev_info->dev); |
411 | if (rc) { | 648 | if (rc) { |
412 | PRINT_ERR("Segment %s could not be registered RC=%d\n", | ||
413 | local_buf, rc); | ||
414 | module_put(THIS_MODULE); | 649 | module_put(THIS_MODULE); |
415 | goto list_del; | 650 | goto dev_list_del; |
416 | } | 651 | } |
417 | get_device(&dev_info->dev); | 652 | get_device(&dev_info->dev); |
418 | rc = device_create_file(&dev_info->dev, &dev_attr_shared); | 653 | rc = device_create_file(&dev_info->dev, &dev_attr_shared); |
@@ -421,6 +656,9 @@ dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char | |||
421 | rc = device_create_file(&dev_info->dev, &dev_attr_save); | 656 | rc = device_create_file(&dev_info->dev, &dev_attr_save); |
422 | if (rc) | 657 | if (rc) |
423 | goto unregister_dev; | 658 | goto unregister_dev; |
659 | rc = device_create_file(&dev_info->dev, &dev_attr_seglist); | ||
660 | if (rc) | ||
661 | goto unregister_dev; | ||
424 | 662 | ||
425 | add_disk(dev_info->gd); | 663 | add_disk(dev_info->gd); |
426 | 664 | ||
@@ -434,7 +672,6 @@ dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char | |||
434 | set_disk_ro(dev_info->gd,0); | 672 | set_disk_ro(dev_info->gd,0); |
435 | break; | 673 | break; |
436 | } | 674 | } |
437 | PRINT_DEBUG("Segment %s loaded successfully\n", local_buf); | ||
438 | up_write(&dcssblk_devices_sem); | 675 | up_write(&dcssblk_devices_sem); |
439 | rc = count; | 676 | rc = count; |
440 | goto out; | 677 | goto out; |
@@ -445,20 +682,27 @@ unregister_dev: | |||
445 | dev_info->gd->queue = NULL; | 682 | dev_info->gd->queue = NULL; |
446 | put_disk(dev_info->gd); | 683 | put_disk(dev_info->gd); |
447 | device_unregister(&dev_info->dev); | 684 | device_unregister(&dev_info->dev); |
448 | segment_unload(dev_info->segment_name); | 685 | list_for_each_entry(seg_info, &dev_info->seg_list, lh) { |
686 | segment_unload(seg_info->segment_name); | ||
687 | } | ||
449 | put_device(&dev_info->dev); | 688 | put_device(&dev_info->dev); |
450 | up_write(&dcssblk_devices_sem); | 689 | up_write(&dcssblk_devices_sem); |
451 | goto out; | 690 | goto out; |
452 | list_del: | 691 | dev_list_del: |
453 | list_del(&dev_info->lh); | 692 | list_del(&dev_info->lh); |
454 | up_write(&dcssblk_devices_sem); | 693 | release_gd: |
455 | unload_seg: | ||
456 | segment_unload(local_buf); | ||
457 | dealloc_gendisk: | ||
458 | blk_cleanup_queue(dev_info->dcssblk_queue); | 694 | blk_cleanup_queue(dev_info->dcssblk_queue); |
459 | dev_info->gd->queue = NULL; | 695 | dev_info->gd->queue = NULL; |
460 | put_disk(dev_info->gd); | 696 | put_disk(dev_info->gd); |
461 | free_dev_info: | 697 | up_write(&dcssblk_devices_sem); |
698 | seg_list_del: | ||
699 | if (dev_info == NULL) | ||
700 | goto out; | ||
701 | list_for_each_entry_safe(seg_info, temp, &dev_info->seg_list, lh) { | ||
702 | list_del(&seg_info->lh); | ||
703 | segment_unload(seg_info->segment_name); | ||
704 | kfree(seg_info); | ||
705 | } | ||
462 | kfree(dev_info); | 706 | kfree(dev_info); |
463 | out: | 707 | out: |
464 | kfree(local_buf); | 708 | kfree(local_buf); |
@@ -473,6 +717,7 @@ static ssize_t | |||
473 | dcssblk_remove_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) | 717 | dcssblk_remove_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) |
474 | { | 718 | { |
475 | struct dcssblk_dev_info *dev_info; | 719 | struct dcssblk_dev_info *dev_info; |
720 | struct segment_info *entry; | ||
476 | int rc, i; | 721 | int rc, i; |
477 | char *local_buf; | 722 | char *local_buf; |
478 | 723 | ||
@@ -499,26 +744,28 @@ dcssblk_remove_store(struct device *dev, struct device_attribute *attr, const ch | |||
499 | dev_info = dcssblk_get_device_by_name(local_buf); | 744 | dev_info = dcssblk_get_device_by_name(local_buf); |
500 | if (dev_info == NULL) { | 745 | if (dev_info == NULL) { |
501 | up_write(&dcssblk_devices_sem); | 746 | up_write(&dcssblk_devices_sem); |
502 | PRINT_WARN("Segment %s is not loaded!\n", local_buf); | 747 | PRINT_WARN("Device %s is not loaded!\n", local_buf); |
503 | rc = -ENODEV; | 748 | rc = -ENODEV; |
504 | goto out_buf; | 749 | goto out_buf; |
505 | } | 750 | } |
506 | if (atomic_read(&dev_info->use_count) != 0) { | 751 | if (atomic_read(&dev_info->use_count) != 0) { |
507 | up_write(&dcssblk_devices_sem); | 752 | up_write(&dcssblk_devices_sem); |
508 | PRINT_WARN("Segment %s is in use!\n", local_buf); | 753 | PRINT_WARN("Device %s is in use!\n", local_buf); |
509 | rc = -EBUSY; | 754 | rc = -EBUSY; |
510 | goto out_buf; | 755 | goto out_buf; |
511 | } | 756 | } |
512 | list_del(&dev_info->lh); | ||
513 | 757 | ||
758 | list_del(&dev_info->lh); | ||
514 | del_gendisk(dev_info->gd); | 759 | del_gendisk(dev_info->gd); |
515 | blk_cleanup_queue(dev_info->dcssblk_queue); | 760 | blk_cleanup_queue(dev_info->dcssblk_queue); |
516 | dev_info->gd->queue = NULL; | 761 | dev_info->gd->queue = NULL; |
517 | put_disk(dev_info->gd); | 762 | put_disk(dev_info->gd); |
518 | device_unregister(&dev_info->dev); | 763 | device_unregister(&dev_info->dev); |
519 | segment_unload(dev_info->segment_name); | 764 | |
520 | PRINT_DEBUG("Segment %s unloaded successfully\n", | 765 | /* unload all related segments */ |
521 | dev_info->segment_name); | 766 | list_for_each_entry(entry, &dev_info->seg_list, lh) |
767 | segment_unload(entry->segment_name); | ||
768 | |||
522 | put_device(&dev_info->dev); | 769 | put_device(&dev_info->dev); |
523 | up_write(&dcssblk_devices_sem); | 770 | up_write(&dcssblk_devices_sem); |
524 | 771 | ||
@@ -550,6 +797,7 @@ static int | |||
550 | dcssblk_release(struct inode *inode, struct file *filp) | 797 | dcssblk_release(struct inode *inode, struct file *filp) |
551 | { | 798 | { |
552 | struct dcssblk_dev_info *dev_info; | 799 | struct dcssblk_dev_info *dev_info; |
800 | struct segment_info *entry; | ||
553 | int rc; | 801 | int rc; |
554 | 802 | ||
555 | dev_info = inode->i_bdev->bd_disk->private_data; | 803 | dev_info = inode->i_bdev->bd_disk->private_data; |
@@ -560,9 +808,11 @@ dcssblk_release(struct inode *inode, struct file *filp) | |||
560 | down_write(&dcssblk_devices_sem); | 808 | down_write(&dcssblk_devices_sem); |
561 | if (atomic_dec_and_test(&dev_info->use_count) | 809 | if (atomic_dec_and_test(&dev_info->use_count) |
562 | && (dev_info->save_pending)) { | 810 | && (dev_info->save_pending)) { |
563 | PRINT_INFO("Segment %s became idle and is being saved now\n", | 811 | PRINT_INFO("Device %s became idle and is being saved now\n", |
564 | dev_info->segment_name); | 812 | dev_info->segment_name); |
565 | segment_save(dev_info->segment_name); | 813 | list_for_each_entry(entry, &dev_info->seg_list, lh) { |
814 | segment_save(entry->segment_name); | ||
815 | } | ||
566 | dev_info->save_pending = 0; | 816 | dev_info->save_pending = 0; |
567 | } | 817 | } |
568 | up_write(&dcssblk_devices_sem); | 818 | up_write(&dcssblk_devices_sem); |
@@ -602,7 +852,7 @@ dcssblk_make_request(struct request_queue *q, struct bio *bio) | |||
602 | case SEG_TYPE_SC: | 852 | case SEG_TYPE_SC: |
603 | /* cannot write to these segments */ | 853 | /* cannot write to these segments */ |
604 | if (bio_data_dir(bio) == WRITE) { | 854 | if (bio_data_dir(bio) == WRITE) { |
605 | PRINT_WARN("rejecting write to ro segment %s\n", | 855 | PRINT_WARN("rejecting write to ro device %s\n", |
606 | dev_name(&dev_info->dev)); | 856 | dev_name(&dev_info->dev)); |
607 | goto fail; | 857 | goto fail; |
608 | } | 858 | } |
@@ -658,7 +908,7 @@ static void | |||
658 | dcssblk_check_params(void) | 908 | dcssblk_check_params(void) |
659 | { | 909 | { |
660 | int rc, i, j, k; | 910 | int rc, i, j, k; |
661 | char buf[9]; | 911 | char buf[DCSSBLK_PARM_LEN + 1]; |
662 | struct dcssblk_dev_info *dev_info; | 912 | struct dcssblk_dev_info *dev_info; |
663 | 913 | ||
664 | for (i = 0; (i < DCSSBLK_PARM_LEN) && (dcssblk_segments[i] != '\0'); | 914 | for (i = 0; (i < DCSSBLK_PARM_LEN) && (dcssblk_segments[i] != '\0'); |
@@ -666,15 +916,16 @@ dcssblk_check_params(void) | |||
666 | for (j = i; (dcssblk_segments[j] != ',') && | 916 | for (j = i; (dcssblk_segments[j] != ',') && |
667 | (dcssblk_segments[j] != '\0') && | 917 | (dcssblk_segments[j] != '\0') && |
668 | (dcssblk_segments[j] != '(') && | 918 | (dcssblk_segments[j] != '(') && |
669 | (j - i) < 8; j++) | 919 | (j < DCSSBLK_PARM_LEN); j++) |
670 | { | 920 | { |
671 | buf[j-i] = dcssblk_segments[j]; | 921 | buf[j-i] = dcssblk_segments[j]; |
672 | } | 922 | } |
673 | buf[j-i] = '\0'; | 923 | buf[j-i] = '\0'; |
674 | rc = dcssblk_add_store(dcssblk_root_dev, NULL, buf, j-i); | 924 | rc = dcssblk_add_store(dcssblk_root_dev, NULL, buf, j-i); |
675 | if ((rc >= 0) && (dcssblk_segments[j] == '(')) { | 925 | if ((rc >= 0) && (dcssblk_segments[j] == '(')) { |
676 | for (k = 0; buf[k] != '\0'; k++) | 926 | for (k = 0; (buf[k] != ':') && (buf[k] != '\0'); k++) |
677 | buf[k] = toupper(buf[k]); | 927 | buf[k] = toupper(buf[k]); |
928 | buf[k] = '\0'; | ||
678 | if (!strncmp(&dcssblk_segments[j], "(local)", 7)) { | 929 | if (!strncmp(&dcssblk_segments[j], "(local)", 7)) { |
679 | down_read(&dcssblk_devices_sem); | 930 | down_read(&dcssblk_devices_sem); |
680 | dev_info = dcssblk_get_device_by_name(buf); | 931 | dev_info = dcssblk_get_device_by_name(buf); |
@@ -741,10 +992,12 @@ module_exit(dcssblk_exit); | |||
741 | 992 | ||
742 | module_param_string(segments, dcssblk_segments, DCSSBLK_PARM_LEN, 0444); | 993 | module_param_string(segments, dcssblk_segments, DCSSBLK_PARM_LEN, 0444); |
743 | MODULE_PARM_DESC(segments, "Name of DCSS segment(s) to be loaded, " | 994 | MODULE_PARM_DESC(segments, "Name of DCSS segment(s) to be loaded, " |
744 | "comma-separated list, each name max. 8 chars.\n" | 995 | "comma-separated list, names in each set separated " |
745 | "Adding \"(local)\" to segment name equals echoing 0 to " | 996 | "by commas are separated by colons, each set contains " |
746 | "/sys/devices/dcssblk/<segment name>/shared after loading " | 997 | "names of contiguous segments and each name max. 8 chars.\n" |
747 | "the segment - \n" | 998 | "Adding \"(local)\" to the end of each set equals echoing 0 " |
748 | "e.g. segments=\"mydcss1,mydcss2,mydcss3(local)\""); | 999 | "to /sys/devices/dcssblk/<device name>/shared after loading " |
1000 | "the contiguous segments - \n" | ||
1001 | "e.g. segments=\"mydcss1,mydcss2:mydcss3,mydcss4(local)\""); | ||
749 | 1002 | ||
750 | MODULE_LICENSE("GPL"); | 1003 | MODULE_LICENSE("GPL"); |