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