1 /* 2 * dcssblk.c -- the S/390 block driver for dcss memory 3 * 4 * Authors: Carsten Otte, Stefan Weinhuber, Gerald Schaefer 5 */ 6 7 #define KMSG_COMPONENT "dcssblk" 8 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt 9 10 #include <linux/module.h> 11 #include <linux/moduleparam.h> 12 #include <linux/ctype.h> 13 #include <linux/errno.h> 14 #include <linux/init.h> 15 #include <linux/slab.h> 16 #include <linux/blkdev.h> 17 #include <asm/extmem.h> 18 #include <asm/io.h> 19 #include <linux/completion.h> 20 #include <linux/interrupt.h> 21 22 #define DCSSBLK_NAME "dcssblk" 23 #define DCSSBLK_MINORS_PER_DISK 1 24 #define DCSSBLK_PARM_LEN 400 25 #define DCSS_BUS_ID_SIZE 20 26 27 static int dcssblk_open(struct block_device *bdev, fmode_t mode); 28 static int dcssblk_release(struct gendisk *disk, fmode_t mode); 29 static int dcssblk_make_request(struct request_queue *q, struct bio *bio); 30 static int dcssblk_direct_access(struct block_device *bdev, sector_t secnum, 31 void **kaddr, unsigned long *pfn); 32 33 static char dcssblk_segments[DCSSBLK_PARM_LEN] = "\0"; 34 35 static int dcssblk_major; 36 static struct block_device_operations dcssblk_devops = { 37 .owner = THIS_MODULE, 38 .open = dcssblk_open, 39 .release = dcssblk_release, 40 .direct_access = dcssblk_direct_access, 41 }; 42 43 struct dcssblk_dev_info { 44 struct list_head lh; 45 struct device dev; 46 char segment_name[DCSS_BUS_ID_SIZE]; 47 atomic_t use_count; 48 struct gendisk *gd; 49 unsigned long start; 50 unsigned long end; 51 int segment_type; 52 unsigned char save_pending; 53 unsigned char is_shared; 54 struct request_queue *dcssblk_queue; 55 int num_of_segments; 56 struct list_head seg_list; 57 }; 58 59 struct segment_info { 60 struct list_head lh; 61 char segment_name[DCSS_BUS_ID_SIZE]; 62 unsigned long start; 63 unsigned long end; 64 int segment_type; 65 }; 66 67 static ssize_t dcssblk_add_store(struct device * dev, struct device_attribute *attr, const char * buf, 68 size_t count); 69 static ssize_t dcssblk_remove_store(struct device * dev, struct device_attribute *attr, const char * buf, 70 size_t count); 71 static ssize_t dcssblk_save_store(struct device * dev, struct device_attribute *attr, const char * buf, 72 size_t count); 73 static ssize_t dcssblk_save_show(struct device *dev, struct device_attribute *attr, char *buf); 74 static ssize_t dcssblk_shared_store(struct device * dev, struct device_attribute *attr, const char * buf, 75 size_t count); 76 static ssize_t dcssblk_shared_show(struct device *dev, struct device_attribute *attr, char *buf); 77 static ssize_t dcssblk_seglist_show(struct device *dev, 78 struct device_attribute *attr, 79 char *buf); 80 81 static DEVICE_ATTR(add, S_IWUSR, NULL, dcssblk_add_store); 82 static DEVICE_ATTR(remove, S_IWUSR, NULL, dcssblk_remove_store); 83 static DEVICE_ATTR(save, S_IWUSR | S_IRUSR, dcssblk_save_show, 84 dcssblk_save_store); 85 static DEVICE_ATTR(shared, S_IWUSR | S_IRUSR, dcssblk_shared_show, 86 dcssblk_shared_store); 87 static DEVICE_ATTR(seglist, S_IRUSR, dcssblk_seglist_show, NULL); 88 89 static struct device *dcssblk_root_dev; 90 91 static LIST_HEAD(dcssblk_devices); 92 static struct rw_semaphore dcssblk_devices_sem; 93 94 /* 95 * release function for segment device. 96 */ 97 static void 98 dcssblk_release_segment(struct device *dev) 99 { 100 struct dcssblk_dev_info *dev_info; 101 struct segment_info *entry, *temp; 102 103 dev_info = container_of(dev, struct dcssblk_dev_info, dev); 104 list_for_each_entry_safe(entry, temp, &dev_info->seg_list, lh) { 105 list_del(&entry->lh); 106 kfree(entry); 107 } 108 kfree(dev_info); 109 module_put(THIS_MODULE); 110 } 111 112 /* 113 * get a minor number. needs to be called with 114 * down_write(&dcssblk_devices_sem) and the 115 * device needs to be enqueued before the semaphore is 116 * freed. 117 */ 118 static int 119 dcssblk_assign_free_minor(struct dcssblk_dev_info *dev_info) 120 { 121 int minor, found; 122 struct dcssblk_dev_info *entry; 123 124 if (dev_info == NULL) 125 return -EINVAL; 126 for (minor = 0; minor < (1<<MINORBITS); minor++) { 127 found = 0; 128 // test if minor available 129 list_for_each_entry(entry, &dcssblk_devices, lh) 130 if (minor == MINOR(disk_devt(entry->gd))) 131 found++; 132 if (!found) break; // got unused minor 133 } 134 if (found) 135 return -EBUSY; 136 dev_info->gd->first_minor = minor; 137 return 0; 138 } 139 140 /* 141 * get the struct dcssblk_dev_info from dcssblk_devices 142 * for the given name. 143 * down_read(&dcssblk_devices_sem) must be held. 144 */ 145 static struct dcssblk_dev_info * 146 dcssblk_get_device_by_name(char *name) 147 { 148 struct dcssblk_dev_info *entry; 149 150 list_for_each_entry(entry, &dcssblk_devices, lh) { 151 if (!strcmp(name, entry->segment_name)) { 152 return entry; 153 } 154 } 155 return NULL; 156 } 157 158 /* 159 * get the struct segment_info from seg_list 160 * for the given name. 161 * down_read(&dcssblk_devices_sem) must be held. 162 */ 163 static struct segment_info * 164 dcssblk_get_segment_by_name(char *name) 165 { 166 struct dcssblk_dev_info *dev_info; 167 struct segment_info *entry; 168 169 list_for_each_entry(dev_info, &dcssblk_devices, lh) { 170 list_for_each_entry(entry, &dev_info->seg_list, lh) { 171 if (!strcmp(name, entry->segment_name)) 172 return entry; 173 } 174 } 175 return NULL; 176 } 177 178 /* 179 * get the highest address of the multi-segment block. 180 */ 181 static unsigned long 182 dcssblk_find_highest_addr(struct dcssblk_dev_info *dev_info) 183 { 184 unsigned long highest_addr; 185 struct segment_info *entry; 186 187 highest_addr = 0; 188 list_for_each_entry(entry, &dev_info->seg_list, lh) { 189 if (highest_addr < entry->end) 190 highest_addr = entry->end; 191 } 192 return highest_addr; 193 } 194 195 /* 196 * get the lowest address of the multi-segment block. 197 */ 198 static unsigned long 199 dcssblk_find_lowest_addr(struct dcssblk_dev_info *dev_info) 200 { 201 int set_first; 202 unsigned long lowest_addr; 203 struct segment_info *entry; 204 205 set_first = 0; 206 lowest_addr = 0; 207 list_for_each_entry(entry, &dev_info->seg_list, lh) { 208 if (set_first == 0) { 209 lowest_addr = entry->start; 210 set_first = 1; 211 } else { 212 if (lowest_addr > entry->start) 213 lowest_addr = entry->start; 214 } 215 } 216 return lowest_addr; 217 } 218 219 /* 220 * Check continuity of segments. 221 */ 222 static int 223 dcssblk_is_continuous(struct dcssblk_dev_info *dev_info) 224 { 225 int i, j, rc; 226 struct segment_info *sort_list, *entry, temp; 227 228 if (dev_info->num_of_segments <= 1) 229 return 0; 230 231 sort_list = kzalloc( 232 sizeof(struct segment_info) * dev_info->num_of_segments, 233 GFP_KERNEL); 234 if (sort_list == NULL) 235 return -ENOMEM; 236 i = 0; 237 list_for_each_entry(entry, &dev_info->seg_list, lh) { 238 memcpy(&sort_list[i], entry, sizeof(struct segment_info)); 239 i++; 240 } 241 242 /* sort segments */ 243 for (i = 0; i < dev_info->num_of_segments; i++) 244 for (j = 0; j < dev_info->num_of_segments; j++) 245 if (sort_list[j].start > sort_list[i].start) { 246 memcpy(&temp, &sort_list[i], 247 sizeof(struct segment_info)); 248 memcpy(&sort_list[i], &sort_list[j], 249 sizeof(struct segment_info)); 250 memcpy(&sort_list[j], &temp, 251 sizeof(struct segment_info)); 252 } 253 254 /* check continuity */ 255 for (i = 0; i < dev_info->num_of_segments - 1; i++) { 256 if ((sort_list[i].end + 1) != sort_list[i+1].start) { 257 pr_err("Adjacent DCSSs %s and %s are not " 258 "contiguous\n", sort_list[i].segment_name, 259 sort_list[i+1].segment_name); 260 rc = -EINVAL; 261 goto out; 262 } 263 /* EN and EW are allowed in a block device */ 264 if (sort_list[i].segment_type != sort_list[i+1].segment_type) { 265 if (!(sort_list[i].segment_type & SEGMENT_EXCLUSIVE) || 266 (sort_list[i].segment_type == SEG_TYPE_ER) || 267 !(sort_list[i+1].segment_type & 268 SEGMENT_EXCLUSIVE) || 269 (sort_list[i+1].segment_type == SEG_TYPE_ER)) { 270 pr_err("DCSS %s and DCSS %s have " 271 "incompatible types\n", 272 sort_list[i].segment_name, 273 sort_list[i+1].segment_name); 274 rc = -EINVAL; 275 goto out; 276 } 277 } 278 } 279 rc = 0; 280 out: 281 kfree(sort_list); 282 return rc; 283 } 284 285 /* 286 * Load a segment 287 */ 288 static int 289 dcssblk_load_segment(char *name, struct segment_info **seg_info) 290 { 291 int rc; 292 293 /* already loaded? */ 294 down_read(&dcssblk_devices_sem); 295 *seg_info = dcssblk_get_segment_by_name(name); 296 up_read(&dcssblk_devices_sem); 297 if (*seg_info != NULL) 298 return -EEXIST; 299 300 /* get a struct segment_info */ 301 *seg_info = kzalloc(sizeof(struct segment_info), GFP_KERNEL); 302 if (*seg_info == NULL) 303 return -ENOMEM; 304 305 strcpy((*seg_info)->segment_name, name); 306 307 /* load the segment */ 308 rc = segment_load(name, SEGMENT_SHARED, 309 &(*seg_info)->start, &(*seg_info)->end); 310 if (rc < 0) { 311 segment_warning(rc, (*seg_info)->segment_name); 312 kfree(*seg_info); 313 } else { 314 INIT_LIST_HEAD(&(*seg_info)->lh); 315 (*seg_info)->segment_type = rc; 316 } 317 return rc; 318 } 319 320 static void dcssblk_unregister_callback(struct device *dev) 321 { 322 device_unregister(dev); 323 put_device(dev); 324 } 325 326 /* 327 * device attribute for switching shared/nonshared (exclusive) 328 * operation (show + store) 329 */ 330 static ssize_t 331 dcssblk_shared_show(struct device *dev, struct device_attribute *attr, char *buf) 332 { 333 struct dcssblk_dev_info *dev_info; 334 335 dev_info = container_of(dev, struct dcssblk_dev_info, dev); 336 return sprintf(buf, dev_info->is_shared ? "1\n" : "0\n"); 337 } 338 339 static ssize_t 340 dcssblk_shared_store(struct device *dev, struct device_attribute *attr, const char *inbuf, size_t count) 341 { 342 struct dcssblk_dev_info *dev_info; 343 struct segment_info *entry, *temp; 344 int rc; 345 346 if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0')) 347 return -EINVAL; 348 down_write(&dcssblk_devices_sem); 349 dev_info = container_of(dev, struct dcssblk_dev_info, dev); 350 if (atomic_read(&dev_info->use_count)) { 351 rc = -EBUSY; 352 goto out; 353 } 354 if (inbuf[0] == '1') { 355 /* reload segments in shared mode */ 356 list_for_each_entry(entry, &dev_info->seg_list, lh) { 357 rc = segment_modify_shared(entry->segment_name, 358 SEGMENT_SHARED); 359 if (rc < 0) { 360 BUG_ON(rc == -EINVAL); 361 if (rc != -EAGAIN) 362 goto removeseg; 363 } 364 } 365 dev_info->is_shared = 1; 366 switch (dev_info->segment_type) { 367 case SEG_TYPE_SR: 368 case SEG_TYPE_ER: 369 case SEG_TYPE_SC: 370 set_disk_ro(dev_info->gd, 1); 371 } 372 } else if (inbuf[0] == '0') { 373 /* reload segments in exclusive mode */ 374 if (dev_info->segment_type == SEG_TYPE_SC) { 375 pr_err("DCSS %s is of type SC and cannot be " 376 "loaded as exclusive-writable\n", 377 dev_info->segment_name); 378 rc = -EINVAL; 379 goto out; 380 } 381 list_for_each_entry(entry, &dev_info->seg_list, lh) { 382 rc = segment_modify_shared(entry->segment_name, 383 SEGMENT_EXCLUSIVE); 384 if (rc < 0) { 385 BUG_ON(rc == -EINVAL); 386 if (rc != -EAGAIN) 387 goto removeseg; 388 } 389 } 390 dev_info->is_shared = 0; 391 set_disk_ro(dev_info->gd, 0); 392 } else { 393 rc = -EINVAL; 394 goto out; 395 } 396 rc = count; 397 goto out; 398 399 removeseg: 400 pr_err("DCSS device %s is removed after a failed access mode " 401 "change\n", dev_info->segment_name); 402 temp = entry; 403 list_for_each_entry(entry, &dev_info->seg_list, lh) { 404 if (entry != temp) 405 segment_unload(entry->segment_name); 406 } 407 list_del(&dev_info->lh); 408 409 del_gendisk(dev_info->gd); 410 blk_cleanup_queue(dev_info->dcssblk_queue); 411 dev_info->gd->queue = NULL; 412 put_disk(dev_info->gd); 413 rc = device_schedule_callback(dev, dcssblk_unregister_callback); 414 out: 415 up_write(&dcssblk_devices_sem); 416 return rc; 417 } 418 419 /* 420 * device attribute for save operation on current copy 421 * of the segment. If the segment is busy, saving will 422 * become pending until it gets released, which can be 423 * undone by storing a non-true value to this entry. 424 * (show + store) 425 */ 426 static ssize_t 427 dcssblk_save_show(struct device *dev, struct device_attribute *attr, char *buf) 428 { 429 struct dcssblk_dev_info *dev_info; 430 431 dev_info = container_of(dev, struct dcssblk_dev_info, dev); 432 return sprintf(buf, dev_info->save_pending ? "1\n" : "0\n"); 433 } 434 435 static ssize_t 436 dcssblk_save_store(struct device *dev, struct device_attribute *attr, const char *inbuf, size_t count) 437 { 438 struct dcssblk_dev_info *dev_info; 439 struct segment_info *entry; 440 441 if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0')) 442 return -EINVAL; 443 dev_info = container_of(dev, struct dcssblk_dev_info, dev); 444 445 down_write(&dcssblk_devices_sem); 446 if (inbuf[0] == '1') { 447 if (atomic_read(&dev_info->use_count) == 0) { 448 // device is idle => we save immediately 449 pr_info("All DCSSs that map to device %s are " 450 "saved\n", dev_info->segment_name); 451 list_for_each_entry(entry, &dev_info->seg_list, lh) { 452 segment_save(entry->segment_name); 453 } 454 } else { 455 // device is busy => we save it when it becomes 456 // idle in dcssblk_release 457 pr_info("Device %s is in use, its DCSSs will be " 458 "saved when it becomes idle\n", 459 dev_info->segment_name); 460 dev_info->save_pending = 1; 461 } 462 } else if (inbuf[0] == '0') { 463 if (dev_info->save_pending) { 464 // device is busy & the user wants to undo his save 465 // request 466 dev_info->save_pending = 0; 467 pr_info("A pending save request for device %s " 468 "has been canceled\n", 469 dev_info->segment_name); 470 } 471 } else { 472 up_write(&dcssblk_devices_sem); 473 return -EINVAL; 474 } 475 up_write(&dcssblk_devices_sem); 476 return count; 477 } 478 479 /* 480 * device attribute for showing all segments in a device 481 */ 482 static ssize_t 483 dcssblk_seglist_show(struct device *dev, struct device_attribute *attr, 484 char *buf) 485 { 486 int i; 487 488 struct dcssblk_dev_info *dev_info; 489 struct segment_info *entry; 490 491 down_read(&dcssblk_devices_sem); 492 dev_info = container_of(dev, struct dcssblk_dev_info, dev); 493 i = 0; 494 buf[0] = '\0'; 495 list_for_each_entry(entry, &dev_info->seg_list, lh) { 496 strcpy(&buf[i], entry->segment_name); 497 i += strlen(entry->segment_name); 498 buf[i] = '\n'; 499 i++; 500 } 501 up_read(&dcssblk_devices_sem); 502 return i; 503 } 504 505 /* 506 * device attribute for adding devices 507 */ 508 static ssize_t 509 dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 510 { 511 int rc, i, j, num_of_segments; 512 struct dcssblk_dev_info *dev_info; 513 struct segment_info *seg_info, *temp; 514 char *local_buf; 515 unsigned long seg_byte_size; 516 517 dev_info = NULL; 518 seg_info = NULL; 519 if (dev != dcssblk_root_dev) { 520 rc = -EINVAL; 521 goto out_nobuf; 522 } 523 if ((count < 1) || (buf[0] == '\0') || (buf[0] == '\n')) { 524 rc = -ENAMETOOLONG; 525 goto out_nobuf; 526 } 527 528 local_buf = kmalloc(count + 1, GFP_KERNEL); 529 if (local_buf == NULL) { 530 rc = -ENOMEM; 531 goto out_nobuf; 532 } 533 534 /* 535 * parse input 536 */ 537 num_of_segments = 0; 538 for (i = 0; ((buf[i] != '\0') && (buf[i] != '\n') && i < count); i++) { 539 for (j = i; (buf[j] != ':') && 540 (buf[j] != '\0') && 541 (buf[j] != '\n') && 542 j < count; j++) { 543 local_buf[j-i] = toupper(buf[j]); 544 } 545 local_buf[j-i] = '\0'; 546 if (((j - i) == 0) || ((j - i) > 8)) { 547 rc = -ENAMETOOLONG; 548 goto seg_list_del; 549 } 550 551 rc = dcssblk_load_segment(local_buf, &seg_info); 552 if (rc < 0) 553 goto seg_list_del; 554 /* 555 * get a struct dcssblk_dev_info 556 */ 557 if (num_of_segments == 0) { 558 dev_info = kzalloc(sizeof(struct dcssblk_dev_info), 559 GFP_KERNEL); 560 if (dev_info == NULL) { 561 rc = -ENOMEM; 562 goto out; 563 } 564 strcpy(dev_info->segment_name, local_buf); 565 dev_info->segment_type = seg_info->segment_type; 566 INIT_LIST_HEAD(&dev_info->seg_list); 567 } 568 list_add_tail(&seg_info->lh, &dev_info->seg_list); 569 num_of_segments++; 570 i = j; 571 572 if ((buf[j] == '\0') || (buf[j] == '\n')) 573 break; 574 } 575 576 /* no trailing colon at the end of the input */ 577 if ((i > 0) && (buf[i-1] == ':')) { 578 rc = -ENAMETOOLONG; 579 goto seg_list_del; 580 } 581 strlcpy(local_buf, buf, i + 1); 582 dev_info->num_of_segments = num_of_segments; 583 rc = dcssblk_is_continuous(dev_info); 584 if (rc < 0) 585 goto seg_list_del; 586 587 dev_info->start = dcssblk_find_lowest_addr(dev_info); 588 dev_info->end = dcssblk_find_highest_addr(dev_info); 589 590 dev_set_name(&dev_info->dev, dev_info->segment_name); 591 dev_info->dev.release = dcssblk_release_segment; 592 INIT_LIST_HEAD(&dev_info->lh); 593 dev_info->gd = alloc_disk(DCSSBLK_MINORS_PER_DISK); 594 if (dev_info->gd == NULL) { 595 rc = -ENOMEM; 596 goto seg_list_del; 597 } 598 dev_info->gd->major = dcssblk_major; 599 dev_info->gd->fops = &dcssblk_devops; 600 dev_info->dcssblk_queue = blk_alloc_queue(GFP_KERNEL); 601 dev_info->gd->queue = dev_info->dcssblk_queue; 602 dev_info->gd->private_data = dev_info; 603 dev_info->gd->driverfs_dev = &dev_info->dev; 604 blk_queue_make_request(dev_info->dcssblk_queue, dcssblk_make_request); 605 blk_queue_hardsect_size(dev_info->dcssblk_queue, 4096); 606 607 seg_byte_size = (dev_info->end - dev_info->start + 1); 608 set_capacity(dev_info->gd, seg_byte_size >> 9); // size in sectors 609 pr_info("Loaded %s with total size %lu bytes and capacity %lu " 610 "sectors\n", local_buf, seg_byte_size, seg_byte_size >> 9); 611 612 dev_info->save_pending = 0; 613 dev_info->is_shared = 1; 614 dev_info->dev.parent = dcssblk_root_dev; 615 616 /* 617 *get minor, add to list 618 */ 619 down_write(&dcssblk_devices_sem); 620 if (dcssblk_get_segment_by_name(local_buf)) { 621 rc = -EEXIST; 622 goto release_gd; 623 } 624 rc = dcssblk_assign_free_minor(dev_info); 625 if (rc) 626 goto release_gd; 627 sprintf(dev_info->gd->disk_name, "dcssblk%d", 628 MINOR(disk_devt(dev_info->gd))); 629 list_add_tail(&dev_info->lh, &dcssblk_devices); 630 631 if (!try_module_get(THIS_MODULE)) { 632 rc = -ENODEV; 633 goto dev_list_del; 634 } 635 /* 636 * register the device 637 */ 638 rc = device_register(&dev_info->dev); 639 if (rc) { 640 module_put(THIS_MODULE); 641 goto dev_list_del; 642 } 643 get_device(&dev_info->dev); 644 rc = device_create_file(&dev_info->dev, &dev_attr_shared); 645 if (rc) 646 goto unregister_dev; 647 rc = device_create_file(&dev_info->dev, &dev_attr_save); 648 if (rc) 649 goto unregister_dev; 650 rc = device_create_file(&dev_info->dev, &dev_attr_seglist); 651 if (rc) 652 goto unregister_dev; 653 654 add_disk(dev_info->gd); 655 656 switch (dev_info->segment_type) { 657 case SEG_TYPE_SR: 658 case SEG_TYPE_ER: 659 case SEG_TYPE_SC: 660 set_disk_ro(dev_info->gd,1); 661 break; 662 default: 663 set_disk_ro(dev_info->gd,0); 664 break; 665 } 666 up_write(&dcssblk_devices_sem); 667 rc = count; 668 goto out; 669 670 unregister_dev: 671 list_del(&dev_info->lh); 672 blk_cleanup_queue(dev_info->dcssblk_queue); 673 dev_info->gd->queue = NULL; 674 put_disk(dev_info->gd); 675 device_unregister(&dev_info->dev); 676 list_for_each_entry(seg_info, &dev_info->seg_list, lh) { 677 segment_unload(seg_info->segment_name); 678 } 679 put_device(&dev_info->dev); 680 up_write(&dcssblk_devices_sem); 681 goto out; 682 dev_list_del: 683 list_del(&dev_info->lh); 684 release_gd: 685 blk_cleanup_queue(dev_info->dcssblk_queue); 686 dev_info->gd->queue = NULL; 687 put_disk(dev_info->gd); 688 up_write(&dcssblk_devices_sem); 689 seg_list_del: 690 if (dev_info == NULL) 691 goto out; 692 list_for_each_entry_safe(seg_info, temp, &dev_info->seg_list, lh) { 693 list_del(&seg_info->lh); 694 segment_unload(seg_info->segment_name); 695 kfree(seg_info); 696 } 697 kfree(dev_info); 698 out: 699 kfree(local_buf); 700 out_nobuf: 701 return rc; 702 } 703 704 /* 705 * device attribute for removing devices 706 */ 707 static ssize_t 708 dcssblk_remove_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 709 { 710 struct dcssblk_dev_info *dev_info; 711 struct segment_info *entry; 712 int rc, i; 713 char *local_buf; 714 715 if (dev != dcssblk_root_dev) { 716 return -EINVAL; 717 } 718 local_buf = kmalloc(count + 1, GFP_KERNEL); 719 if (local_buf == NULL) { 720 return -ENOMEM; 721 } 722 /* 723 * parse input 724 */ 725 for (i = 0; ((*(buf+i)!='\0') && (*(buf+i)!='\n') && i < count); i++) { 726 local_buf[i] = toupper(buf[i]); 727 } 728 local_buf[i] = '\0'; 729 if ((i == 0) || (i > 8)) { 730 rc = -ENAMETOOLONG; 731 goto out_buf; 732 } 733 734 down_write(&dcssblk_devices_sem); 735 dev_info = dcssblk_get_device_by_name(local_buf); 736 if (dev_info == NULL) { 737 up_write(&dcssblk_devices_sem); 738 pr_warning("Device %s cannot be removed because it is not a " 739 "known device\n", local_buf); 740 rc = -ENODEV; 741 goto out_buf; 742 } 743 if (atomic_read(&dev_info->use_count) != 0) { 744 up_write(&dcssblk_devices_sem); 745 pr_warning("Device %s cannot be removed while it is in " 746 "use\n", local_buf); 747 rc = -EBUSY; 748 goto out_buf; 749 } 750 751 list_del(&dev_info->lh); 752 del_gendisk(dev_info->gd); 753 blk_cleanup_queue(dev_info->dcssblk_queue); 754 dev_info->gd->queue = NULL; 755 put_disk(dev_info->gd); 756 device_unregister(&dev_info->dev); 757 758 /* unload all related segments */ 759 list_for_each_entry(entry, &dev_info->seg_list, lh) 760 segment_unload(entry->segment_name); 761 762 put_device(&dev_info->dev); 763 up_write(&dcssblk_devices_sem); 764 765 rc = count; 766 out_buf: 767 kfree(local_buf); 768 return rc; 769 } 770 771 static int 772 dcssblk_open(struct block_device *bdev, fmode_t mode) 773 { 774 struct dcssblk_dev_info *dev_info; 775 int rc; 776 777 dev_info = bdev->bd_disk->private_data; 778 if (NULL == dev_info) { 779 rc = -ENODEV; 780 goto out; 781 } 782 atomic_inc(&dev_info->use_count); 783 bdev->bd_block_size = 4096; 784 rc = 0; 785 out: 786 return rc; 787 } 788 789 static int 790 dcssblk_release(struct gendisk *disk, fmode_t mode) 791 { 792 struct dcssblk_dev_info *dev_info = disk->private_data; 793 struct segment_info *entry; 794 int rc; 795 796 if (!dev_info) { 797 rc = -ENODEV; 798 goto out; 799 } 800 down_write(&dcssblk_devices_sem); 801 if (atomic_dec_and_test(&dev_info->use_count) 802 && (dev_info->save_pending)) { 803 pr_info("Device %s has become idle and is being saved " 804 "now\n", dev_info->segment_name); 805 list_for_each_entry(entry, &dev_info->seg_list, lh) { 806 segment_save(entry->segment_name); 807 } 808 dev_info->save_pending = 0; 809 } 810 up_write(&dcssblk_devices_sem); 811 rc = 0; 812 out: 813 return rc; 814 } 815 816 static int 817 dcssblk_make_request(struct request_queue *q, struct bio *bio) 818 { 819 struct dcssblk_dev_info *dev_info; 820 struct bio_vec *bvec; 821 unsigned long index; 822 unsigned long page_addr; 823 unsigned long source_addr; 824 unsigned long bytes_done; 825 int i; 826 827 bytes_done = 0; 828 dev_info = bio->bi_bdev->bd_disk->private_data; 829 if (dev_info == NULL) 830 goto fail; 831 if ((bio->bi_sector & 7) != 0 || (bio->bi_size & 4095) != 0) 832 /* Request is not page-aligned. */ 833 goto fail; 834 if (((bio->bi_size >> 9) + bio->bi_sector) 835 > get_capacity(bio->bi_bdev->bd_disk)) { 836 /* Request beyond end of DCSS segment. */ 837 goto fail; 838 } 839 /* verify data transfer direction */ 840 if (dev_info->is_shared) { 841 switch (dev_info->segment_type) { 842 case SEG_TYPE_SR: 843 case SEG_TYPE_ER: 844 case SEG_TYPE_SC: 845 /* cannot write to these segments */ 846 if (bio_data_dir(bio) == WRITE) { 847 pr_warning("Writing to %s failed because it " 848 "is a read-only device\n", 849 dev_name(&dev_info->dev)); 850 goto fail; 851 } 852 } 853 } 854 855 index = (bio->bi_sector >> 3); 856 bio_for_each_segment(bvec, bio, i) { 857 page_addr = (unsigned long) 858 page_address(bvec->bv_page) + bvec->bv_offset; 859 source_addr = dev_info->start + (index<<12) + bytes_done; 860 if (unlikely((page_addr & 4095) != 0) || (bvec->bv_len & 4095) != 0) 861 // More paranoia. 862 goto fail; 863 if (bio_data_dir(bio) == READ) { 864 memcpy((void*)page_addr, (void*)source_addr, 865 bvec->bv_len); 866 } else { 867 memcpy((void*)source_addr, (void*)page_addr, 868 bvec->bv_len); 869 } 870 bytes_done += bvec->bv_len; 871 } 872 bio_endio(bio, 0); 873 return 0; 874 fail: 875 bio_io_error(bio); 876 return 0; 877 } 878 879 static int 880 dcssblk_direct_access (struct block_device *bdev, sector_t secnum, 881 void **kaddr, unsigned long *pfn) 882 { 883 struct dcssblk_dev_info *dev_info; 884 unsigned long pgoff; 885 886 dev_info = bdev->bd_disk->private_data; 887 if (!dev_info) 888 return -ENODEV; 889 if (secnum % (PAGE_SIZE/512)) 890 return -EINVAL; 891 pgoff = secnum / (PAGE_SIZE / 512); 892 if ((pgoff+1)*PAGE_SIZE-1 > dev_info->end - dev_info->start) 893 return -ERANGE; 894 *kaddr = (void *) (dev_info->start+pgoff*PAGE_SIZE); 895 *pfn = virt_to_phys(*kaddr) >> PAGE_SHIFT; 896 897 return 0; 898 } 899 900 static void 901 dcssblk_check_params(void) 902 { 903 int rc, i, j, k; 904 char buf[DCSSBLK_PARM_LEN + 1]; 905 struct dcssblk_dev_info *dev_info; 906 907 for (i = 0; (i < DCSSBLK_PARM_LEN) && (dcssblk_segments[i] != '\0'); 908 i++) { 909 for (j = i; (dcssblk_segments[j] != ',') && 910 (dcssblk_segments[j] != '\0') && 911 (dcssblk_segments[j] != '(') && 912 (j < DCSSBLK_PARM_LEN); j++) 913 { 914 buf[j-i] = dcssblk_segments[j]; 915 } 916 buf[j-i] = '\0'; 917 rc = dcssblk_add_store(dcssblk_root_dev, NULL, buf, j-i); 918 if ((rc >= 0) && (dcssblk_segments[j] == '(')) { 919 for (k = 0; (buf[k] != ':') && (buf[k] != '\0'); k++) 920 buf[k] = toupper(buf[k]); 921 buf[k] = '\0'; 922 if (!strncmp(&dcssblk_segments[j], "(local)", 7)) { 923 down_read(&dcssblk_devices_sem); 924 dev_info = dcssblk_get_device_by_name(buf); 925 up_read(&dcssblk_devices_sem); 926 if (dev_info) 927 dcssblk_shared_store(&dev_info->dev, 928 NULL, "0\n", 2); 929 } 930 } 931 while ((dcssblk_segments[j] != ',') && 932 (dcssblk_segments[j] != '\0')) 933 { 934 j++; 935 } 936 if (dcssblk_segments[j] == '\0') 937 break; 938 i = j; 939 } 940 } 941 942 /* 943 * The init/exit functions. 944 */ 945 static void __exit 946 dcssblk_exit(void) 947 { 948 root_device_unregister(dcssblk_root_dev); 949 unregister_blkdev(dcssblk_major, DCSSBLK_NAME); 950 } 951 952 static int __init 953 dcssblk_init(void) 954 { 955 int rc; 956 957 dcssblk_root_dev = root_device_register("dcssblk"); 958 if (IS_ERR(dcssblk_root_dev)) 959 return PTR_ERR(dcssblk_root_dev); 960 rc = device_create_file(dcssblk_root_dev, &dev_attr_add); 961 if (rc) { 962 root_device_unregister(dcssblk_root_dev); 963 return rc; 964 } 965 rc = device_create_file(dcssblk_root_dev, &dev_attr_remove); 966 if (rc) { 967 root_device_unregister(dcssblk_root_dev); 968 return rc; 969 } 970 rc = register_blkdev(0, DCSSBLK_NAME); 971 if (rc < 0) { 972 root_device_unregister(dcssblk_root_dev); 973 return rc; 974 } 975 dcssblk_major = rc; 976 init_rwsem(&dcssblk_devices_sem); 977 978 dcssblk_check_params(); 979 980 return 0; 981 } 982 983 module_init(dcssblk_init); 984 module_exit(dcssblk_exit); 985 986 module_param_string(segments, dcssblk_segments, DCSSBLK_PARM_LEN, 0444); 987 MODULE_PARM_DESC(segments, "Name of DCSS segment(s) to be loaded, " 988 "comma-separated list, names in each set separated " 989 "by commas are separated by colons, each set contains " 990 "names of contiguous segments and each name max. 8 chars.\n" 991 "Adding \"(local)\" to the end of each set equals echoing 0 " 992 "to /sys/devices/dcssblk/<device name>/shared after loading " 993 "the contiguous segments - \n" 994 "e.g. segments=\"mydcss1,mydcss2:mydcss3,mydcss4(local)\""); 995 996 MODULE_LICENSE("GPL"); 997