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