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