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