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