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