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 25 #ifdef DCSSBLK_DEBUG 26 #define PRINT_DEBUG(x...) printk(KERN_DEBUG DCSSBLK_NAME " debug: " x) 27 #else 28 #define PRINT_DEBUG(x...) do {} while (0) 29 #endif 30 #define PRINT_INFO(x...) printk(KERN_INFO DCSSBLK_NAME " info: " x) 31 #define PRINT_WARN(x...) printk(KERN_WARNING DCSSBLK_NAME " warning: " x) 32 #define PRINT_ERR(x...) printk(KERN_ERR DCSSBLK_NAME " error: " x) 33 34 35 static int dcssblk_open(struct inode *inode, struct file *filp); 36 static int dcssblk_release(struct inode *inode, struct file *filp); 37 static int dcssblk_make_request(struct request_queue *q, struct bio *bio); 38 static int dcssblk_direct_access(struct block_device *bdev, sector_t secnum, 39 unsigned long *data); 40 41 static char dcssblk_segments[DCSSBLK_PARM_LEN] = "\0"; 42 43 static int dcssblk_major; 44 static 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 51 static ssize_t dcssblk_add_store(struct device * dev, struct device_attribute *attr, const char * buf, 52 size_t count); 53 static ssize_t dcssblk_remove_store(struct device * dev, struct device_attribute *attr, const char * buf, 54 size_t count); 55 static ssize_t dcssblk_save_store(struct device * dev, struct device_attribute *attr, const char * buf, 56 size_t count); 57 static ssize_t dcssblk_save_show(struct device *dev, struct device_attribute *attr, char *buf); 58 static ssize_t dcssblk_shared_store(struct device * dev, struct device_attribute *attr, const char * buf, 59 size_t count); 60 static ssize_t dcssblk_shared_show(struct device *dev, struct device_attribute *attr, char *buf); 61 62 static DEVICE_ATTR(add, S_IWUSR, NULL, dcssblk_add_store); 63 static DEVICE_ATTR(remove, S_IWUSR, NULL, dcssblk_remove_store); 64 static DEVICE_ATTR(save, S_IWUSR | S_IRUGO, dcssblk_save_show, 65 dcssblk_save_store); 66 static DEVICE_ATTR(shared, S_IWUSR | S_IRUGO, dcssblk_shared_show, 67 dcssblk_shared_store); 68 69 static struct device *dcssblk_root_dev; 70 71 struct dcssblk_dev_info { 72 struct list_head lh; 73 struct device dev; 74 char segment_name[BUS_ID_SIZE]; 75 atomic_t use_count; 76 struct gendisk *gd; 77 unsigned long start; 78 unsigned long end; 79 int segment_type; 80 unsigned char save_pending; 81 unsigned char is_shared; 82 struct request_queue *dcssblk_queue; 83 }; 84 85 static LIST_HEAD(dcssblk_devices); 86 static struct rw_semaphore dcssblk_devices_sem; 87 88 /* 89 * release function for segment device. 90 */ 91 static void 92 dcssblk_release_segment(struct device *dev) 93 { 94 PRINT_DEBUG("segment release fn called for %s\n", dev->bus_id); 95 kfree(container_of(dev, struct dcssblk_dev_info, dev)); 96 module_put(THIS_MODULE); 97 } 98 99 /* 100 * get a minor number. needs to be called with 101 * down_write(&dcssblk_devices_sem) and the 102 * device needs to be enqueued before the semaphore is 103 * freed. 104 */ 105 static int 106 dcssblk_assign_free_minor(struct dcssblk_dev_info *dev_info) 107 { 108 int minor, found; 109 struct dcssblk_dev_info *entry; 110 111 if (dev_info == NULL) 112 return -EINVAL; 113 for (minor = 0; minor < (1<<MINORBITS); minor++) { 114 found = 0; 115 // test if minor available 116 list_for_each_entry(entry, &dcssblk_devices, lh) 117 if (minor == entry->gd->first_minor) 118 found++; 119 if (!found) break; // got unused minor 120 } 121 if (found) 122 return -EBUSY; 123 dev_info->gd->first_minor = minor; 124 return 0; 125 } 126 127 /* 128 * get the struct dcssblk_dev_info from dcssblk_devices 129 * for the given name. 130 * down_read(&dcssblk_devices_sem) must be held. 131 */ 132 static struct dcssblk_dev_info * 133 dcssblk_get_device_by_name(char *name) 134 { 135 struct dcssblk_dev_info *entry; 136 137 list_for_each_entry(entry, &dcssblk_devices, lh) { 138 if (!strcmp(name, entry->segment_name)) { 139 return entry; 140 } 141 } 142 return NULL; 143 } 144 145 /* 146 * print appropriate error message for segment_load()/segment_type() 147 * return code 148 */ 149 static void 150 dcssblk_segment_warn(int rc, char* seg_name) 151 { 152 switch (rc) { 153 case -ENOENT: 154 PRINT_WARN("cannot load/query segment %s, does not exist\n", 155 seg_name); 156 break; 157 case -ENOSYS: 158 PRINT_WARN("cannot load/query segment %s, not running on VM\n", 159 seg_name); 160 break; 161 case -EIO: 162 PRINT_WARN("cannot load/query segment %s, hardware error\n", 163 seg_name); 164 break; 165 case -ENOTSUPP: 166 PRINT_WARN("cannot load/query segment %s, is a multi-part " 167 "segment\n", seg_name); 168 break; 169 case -ENOSPC: 170 PRINT_WARN("cannot load/query segment %s, overlaps with " 171 "storage\n", seg_name); 172 break; 173 case -EBUSY: 174 PRINT_WARN("cannot load/query segment %s, overlaps with " 175 "already loaded dcss\n", seg_name); 176 break; 177 case -EPERM: 178 PRINT_WARN("cannot load/query segment %s, already loaded in " 179 "incompatible mode\n", seg_name); 180 break; 181 case -ENOMEM: 182 PRINT_WARN("cannot load/query segment %s, out of memory\n", 183 seg_name); 184 break; 185 case -ERANGE: 186 PRINT_WARN("cannot load/query segment %s, exceeds kernel " 187 "mapping range\n", seg_name); 188 break; 189 default: 190 PRINT_WARN("cannot load/query segment %s, return value %i\n", 191 seg_name, rc); 192 break; 193 } 194 } 195 196 static void dcssblk_unregister_callback(struct device *dev) 197 { 198 device_unregister(dev); 199 put_device(dev); 200 } 201 202 /* 203 * device attribute for switching shared/nonshared (exclusive) 204 * operation (show + store) 205 */ 206 static ssize_t 207 dcssblk_shared_show(struct device *dev, struct device_attribute *attr, char *buf) 208 { 209 struct dcssblk_dev_info *dev_info; 210 211 dev_info = container_of(dev, struct dcssblk_dev_info, dev); 212 return sprintf(buf, dev_info->is_shared ? "1\n" : "0\n"); 213 } 214 215 static ssize_t 216 dcssblk_shared_store(struct device *dev, struct device_attribute *attr, const char *inbuf, size_t count) 217 { 218 struct dcssblk_dev_info *dev_info; 219 int rc; 220 221 if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0')) { 222 PRINT_WARN("Invalid value, must be 0 or 1\n"); 223 return -EINVAL; 224 } 225 down_write(&dcssblk_devices_sem); 226 dev_info = container_of(dev, struct dcssblk_dev_info, dev); 227 if (atomic_read(&dev_info->use_count)) { 228 PRINT_ERR("share: segment %s is busy!\n", 229 dev_info->segment_name); 230 rc = -EBUSY; 231 goto out; 232 } 233 if (inbuf[0] == '1') { 234 // reload segment in shared mode 235 rc = segment_modify_shared(dev_info->segment_name, 236 SEGMENT_SHARED); 237 if (rc < 0) { 238 BUG_ON(rc == -EINVAL); 239 if (rc != -EAGAIN) 240 goto removeseg; 241 } else { 242 dev_info->is_shared = 1; 243 switch (dev_info->segment_type) { 244 case SEG_TYPE_SR: 245 case SEG_TYPE_ER: 246 case SEG_TYPE_SC: 247 set_disk_ro(dev_info->gd,1); 248 } 249 } 250 } else if (inbuf[0] == '0') { 251 // reload segment in exclusive mode 252 if (dev_info->segment_type == SEG_TYPE_SC) { 253 PRINT_ERR("Segment type SC (%s) cannot be loaded in " 254 "non-shared mode\n", dev_info->segment_name); 255 rc = -EINVAL; 256 goto out; 257 } 258 rc = segment_modify_shared(dev_info->segment_name, 259 SEGMENT_EXCLUSIVE); 260 if (rc < 0) { 261 BUG_ON(rc == -EINVAL); 262 if (rc != -EAGAIN) 263 goto removeseg; 264 } else { 265 dev_info->is_shared = 0; 266 set_disk_ro(dev_info->gd, 0); 267 } 268 } else { 269 PRINT_WARN("Invalid value, must be 0 or 1\n"); 270 rc = -EINVAL; 271 goto out; 272 } 273 rc = count; 274 goto out; 275 276 removeseg: 277 PRINT_ERR("Could not reload segment %s, removing it now!\n", 278 dev_info->segment_name); 279 list_del(&dev_info->lh); 280 281 del_gendisk(dev_info->gd); 282 blk_cleanup_queue(dev_info->dcssblk_queue); 283 dev_info->gd->queue = NULL; 284 put_disk(dev_info->gd); 285 rc = device_schedule_callback(dev, dcssblk_unregister_callback); 286 out: 287 up_write(&dcssblk_devices_sem); 288 return rc; 289 } 290 291 /* 292 * device attribute for save operation on current copy 293 * of the segment. If the segment is busy, saving will 294 * become pending until it gets released, which can be 295 * undone by storing a non-true value to this entry. 296 * (show + store) 297 */ 298 static ssize_t 299 dcssblk_save_show(struct device *dev, struct device_attribute *attr, char *buf) 300 { 301 struct dcssblk_dev_info *dev_info; 302 303 dev_info = container_of(dev, struct dcssblk_dev_info, dev); 304 return sprintf(buf, dev_info->save_pending ? "1\n" : "0\n"); 305 } 306 307 static ssize_t 308 dcssblk_save_store(struct device *dev, struct device_attribute *attr, const char *inbuf, size_t count) 309 { 310 struct dcssblk_dev_info *dev_info; 311 312 if ((count > 1) && (inbuf[1] != '\n') && (inbuf[1] != '\0')) { 313 PRINT_WARN("Invalid value, must be 0 or 1\n"); 314 return -EINVAL; 315 } 316 dev_info = container_of(dev, struct dcssblk_dev_info, dev); 317 318 down_write(&dcssblk_devices_sem); 319 if (inbuf[0] == '1') { 320 if (atomic_read(&dev_info->use_count) == 0) { 321 // device is idle => we save immediately 322 PRINT_INFO("Saving segment %s\n", 323 dev_info->segment_name); 324 segment_save(dev_info->segment_name); 325 } else { 326 // device is busy => we save it when it becomes 327 // idle in dcssblk_release 328 PRINT_INFO("Segment %s is currently busy, it will " 329 "be saved when it becomes idle...\n", 330 dev_info->segment_name); 331 dev_info->save_pending = 1; 332 } 333 } else if (inbuf[0] == '0') { 334 if (dev_info->save_pending) { 335 // device is busy & the user wants to undo his save 336 // request 337 dev_info->save_pending = 0; 338 PRINT_INFO("Pending save for segment %s deactivated\n", 339 dev_info->segment_name); 340 } 341 } else { 342 up_write(&dcssblk_devices_sem); 343 PRINT_WARN("Invalid value, must be 0 or 1\n"); 344 return -EINVAL; 345 } 346 up_write(&dcssblk_devices_sem); 347 return count; 348 } 349 350 /* 351 * device attribute for adding devices 352 */ 353 static ssize_t 354 dcssblk_add_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 355 { 356 int rc, i; 357 struct dcssblk_dev_info *dev_info; 358 char *local_buf; 359 unsigned long seg_byte_size; 360 361 dev_info = NULL; 362 if (dev != dcssblk_root_dev) { 363 rc = -EINVAL; 364 goto out_nobuf; 365 } 366 local_buf = kmalloc(count + 1, GFP_KERNEL); 367 if (local_buf == NULL) { 368 rc = -ENOMEM; 369 goto out_nobuf; 370 } 371 /* 372 * parse input 373 */ 374 for (i = 0; ((buf[i] != '\0') && (buf[i] != '\n') && i < count); i++) { 375 local_buf[i] = toupper(buf[i]); 376 } 377 local_buf[i] = '\0'; 378 if ((i == 0) || (i > 8)) { 379 rc = -ENAMETOOLONG; 380 goto out; 381 } 382 /* 383 * already loaded? 384 */ 385 down_read(&dcssblk_devices_sem); 386 dev_info = dcssblk_get_device_by_name(local_buf); 387 up_read(&dcssblk_devices_sem); 388 if (dev_info != NULL) { 389 PRINT_WARN("Segment %s already loaded!\n", local_buf); 390 rc = -EEXIST; 391 goto out; 392 } 393 /* 394 * get a struct dcssblk_dev_info 395 */ 396 dev_info = kzalloc(sizeof(struct dcssblk_dev_info), GFP_KERNEL); 397 if (dev_info == NULL) { 398 rc = -ENOMEM; 399 goto out; 400 } 401 402 strcpy(dev_info->segment_name, local_buf); 403 strlcpy(dev_info->dev.bus_id, local_buf, BUS_ID_SIZE); 404 dev_info->dev.release = dcssblk_release_segment; 405 INIT_LIST_HEAD(&dev_info->lh); 406 407 dev_info->gd = alloc_disk(DCSSBLK_MINORS_PER_DISK); 408 if (dev_info->gd == NULL) { 409 rc = -ENOMEM; 410 goto free_dev_info; 411 } 412 dev_info->gd->major = dcssblk_major; 413 dev_info->gd->fops = &dcssblk_devops; 414 dev_info->dcssblk_queue = blk_alloc_queue(GFP_KERNEL); 415 dev_info->gd->queue = dev_info->dcssblk_queue; 416 dev_info->gd->private_data = dev_info; 417 dev_info->gd->driverfs_dev = &dev_info->dev; 418 /* 419 * load the segment 420 */ 421 rc = segment_load(local_buf, SEGMENT_SHARED, 422 &dev_info->start, &dev_info->end); 423 if (rc < 0) { 424 dcssblk_segment_warn(rc, dev_info->segment_name); 425 goto dealloc_gendisk; 426 } 427 seg_byte_size = (dev_info->end - dev_info->start + 1); 428 set_capacity(dev_info->gd, seg_byte_size >> 9); // size in sectors 429 PRINT_INFO("Loaded segment %s, size = %lu Byte, " 430 "capacity = %lu (512 Byte) sectors\n", local_buf, 431 seg_byte_size, seg_byte_size >> 9); 432 433 dev_info->segment_type = rc; 434 dev_info->save_pending = 0; 435 dev_info->is_shared = 1; 436 dev_info->dev.parent = dcssblk_root_dev; 437 438 /* 439 * get minor, add to list 440 */ 441 down_write(&dcssblk_devices_sem); 442 rc = dcssblk_assign_free_minor(dev_info); 443 if (rc) { 444 up_write(&dcssblk_devices_sem); 445 PRINT_ERR("No free minor number available! " 446 "Unloading segment...\n"); 447 goto unload_seg; 448 } 449 sprintf(dev_info->gd->disk_name, "dcssblk%d", 450 dev_info->gd->first_minor); 451 list_add_tail(&dev_info->lh, &dcssblk_devices); 452 453 if (!try_module_get(THIS_MODULE)) { 454 rc = -ENODEV; 455 goto list_del; 456 } 457 /* 458 * register the device 459 */ 460 rc = device_register(&dev_info->dev); 461 if (rc) { 462 PRINT_ERR("Segment %s could not be registered RC=%d\n", 463 local_buf, rc); 464 module_put(THIS_MODULE); 465 goto list_del; 466 } 467 get_device(&dev_info->dev); 468 rc = device_create_file(&dev_info->dev, &dev_attr_shared); 469 if (rc) 470 goto unregister_dev; 471 rc = device_create_file(&dev_info->dev, &dev_attr_save); 472 if (rc) 473 goto unregister_dev; 474 475 blk_queue_make_request(dev_info->dcssblk_queue, dcssblk_make_request); 476 blk_queue_hardsect_size(dev_info->dcssblk_queue, 4096); 477 478 add_disk(dev_info->gd); 479 480 switch (dev_info->segment_type) { 481 case SEG_TYPE_SR: 482 case SEG_TYPE_ER: 483 case SEG_TYPE_SC: 484 set_disk_ro(dev_info->gd,1); 485 break; 486 default: 487 set_disk_ro(dev_info->gd,0); 488 break; 489 } 490 PRINT_DEBUG("Segment %s loaded successfully\n", local_buf); 491 up_write(&dcssblk_devices_sem); 492 rc = count; 493 goto out; 494 495 unregister_dev: 496 PRINT_ERR("device_create_file() failed!\n"); 497 list_del(&dev_info->lh); 498 blk_cleanup_queue(dev_info->dcssblk_queue); 499 dev_info->gd->queue = NULL; 500 put_disk(dev_info->gd); 501 device_unregister(&dev_info->dev); 502 segment_unload(dev_info->segment_name); 503 put_device(&dev_info->dev); 504 up_write(&dcssblk_devices_sem); 505 goto out; 506 list_del: 507 list_del(&dev_info->lh); 508 up_write(&dcssblk_devices_sem); 509 unload_seg: 510 segment_unload(local_buf); 511 dealloc_gendisk: 512 blk_cleanup_queue(dev_info->dcssblk_queue); 513 dev_info->gd->queue = NULL; 514 put_disk(dev_info->gd); 515 free_dev_info: 516 kfree(dev_info); 517 out: 518 kfree(local_buf); 519 out_nobuf: 520 return rc; 521 } 522 523 /* 524 * device attribute for removing devices 525 */ 526 static ssize_t 527 dcssblk_remove_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 528 { 529 struct dcssblk_dev_info *dev_info; 530 int rc, i; 531 char *local_buf; 532 533 if (dev != dcssblk_root_dev) { 534 return -EINVAL; 535 } 536 local_buf = kmalloc(count + 1, GFP_KERNEL); 537 if (local_buf == NULL) { 538 return -ENOMEM; 539 } 540 /* 541 * parse input 542 */ 543 for (i = 0; ((*(buf+i)!='\0') && (*(buf+i)!='\n') && i < count); i++) { 544 local_buf[i] = toupper(buf[i]); 545 } 546 local_buf[i] = '\0'; 547 if ((i == 0) || (i > 8)) { 548 rc = -ENAMETOOLONG; 549 goto out_buf; 550 } 551 552 down_write(&dcssblk_devices_sem); 553 dev_info = dcssblk_get_device_by_name(local_buf); 554 if (dev_info == NULL) { 555 up_write(&dcssblk_devices_sem); 556 PRINT_WARN("Segment %s is not loaded!\n", local_buf); 557 rc = -ENODEV; 558 goto out_buf; 559 } 560 if (atomic_read(&dev_info->use_count) != 0) { 561 up_write(&dcssblk_devices_sem); 562 PRINT_WARN("Segment %s is in use!\n", local_buf); 563 rc = -EBUSY; 564 goto out_buf; 565 } 566 list_del(&dev_info->lh); 567 568 del_gendisk(dev_info->gd); 569 blk_cleanup_queue(dev_info->dcssblk_queue); 570 dev_info->gd->queue = NULL; 571 put_disk(dev_info->gd); 572 device_unregister(&dev_info->dev); 573 segment_unload(dev_info->segment_name); 574 PRINT_DEBUG("Segment %s unloaded successfully\n", 575 dev_info->segment_name); 576 put_device(&dev_info->dev); 577 up_write(&dcssblk_devices_sem); 578 579 rc = count; 580 out_buf: 581 kfree(local_buf); 582 return rc; 583 } 584 585 static int 586 dcssblk_open(struct inode *inode, struct file *filp) 587 { 588 struct dcssblk_dev_info *dev_info; 589 int rc; 590 591 dev_info = inode->i_bdev->bd_disk->private_data; 592 if (NULL == dev_info) { 593 rc = -ENODEV; 594 goto out; 595 } 596 atomic_inc(&dev_info->use_count); 597 inode->i_bdev->bd_block_size = 4096; 598 rc = 0; 599 out: 600 return rc; 601 } 602 603 static int 604 dcssblk_release(struct inode *inode, struct file *filp) 605 { 606 struct dcssblk_dev_info *dev_info; 607 int rc; 608 609 dev_info = inode->i_bdev->bd_disk->private_data; 610 if (NULL == dev_info) { 611 rc = -ENODEV; 612 goto out; 613 } 614 down_write(&dcssblk_devices_sem); 615 if (atomic_dec_and_test(&dev_info->use_count) 616 && (dev_info->save_pending)) { 617 PRINT_INFO("Segment %s became idle and is being saved now\n", 618 dev_info->segment_name); 619 segment_save(dev_info->segment_name); 620 dev_info->save_pending = 0; 621 } 622 up_write(&dcssblk_devices_sem); 623 rc = 0; 624 out: 625 return rc; 626 } 627 628 static int 629 dcssblk_make_request(struct request_queue *q, struct bio *bio) 630 { 631 struct dcssblk_dev_info *dev_info; 632 struct bio_vec *bvec; 633 unsigned long index; 634 unsigned long page_addr; 635 unsigned long source_addr; 636 unsigned long bytes_done; 637 int i; 638 639 bytes_done = 0; 640 dev_info = bio->bi_bdev->bd_disk->private_data; 641 if (dev_info == NULL) 642 goto fail; 643 if ((bio->bi_sector & 7) != 0 || (bio->bi_size & 4095) != 0) 644 /* Request is not page-aligned. */ 645 goto fail; 646 if (((bio->bi_size >> 9) + bio->bi_sector) 647 > get_capacity(bio->bi_bdev->bd_disk)) { 648 /* Request beyond end of DCSS segment. */ 649 goto fail; 650 } 651 /* verify data transfer direction */ 652 if (dev_info->is_shared) { 653 switch (dev_info->segment_type) { 654 case SEG_TYPE_SR: 655 case SEG_TYPE_ER: 656 case SEG_TYPE_SC: 657 /* cannot write to these segments */ 658 if (bio_data_dir(bio) == WRITE) { 659 PRINT_WARN("rejecting write to ro segment %s\n", dev_info->dev.bus_id); 660 goto fail; 661 } 662 } 663 } 664 665 index = (bio->bi_sector >> 3); 666 bio_for_each_segment(bvec, bio, i) { 667 page_addr = (unsigned long) 668 page_address(bvec->bv_page) + bvec->bv_offset; 669 source_addr = dev_info->start + (index<<12) + bytes_done; 670 if (unlikely(page_addr & 4095) != 0 || (bvec->bv_len & 4095) != 0) 671 // More paranoia. 672 goto fail; 673 if (bio_data_dir(bio) == READ) { 674 memcpy((void*)page_addr, (void*)source_addr, 675 bvec->bv_len); 676 } else { 677 memcpy((void*)source_addr, (void*)page_addr, 678 bvec->bv_len); 679 } 680 bytes_done += bvec->bv_len; 681 } 682 bio_endio(bio, 0); 683 return 0; 684 fail: 685 bio_io_error(bio); 686 return 0; 687 } 688 689 static int 690 dcssblk_direct_access (struct block_device *bdev, sector_t secnum, 691 unsigned long *data) 692 { 693 struct dcssblk_dev_info *dev_info; 694 unsigned long pgoff; 695 696 dev_info = bdev->bd_disk->private_data; 697 if (!dev_info) 698 return -ENODEV; 699 if (secnum % (PAGE_SIZE/512)) 700 return -EINVAL; 701 pgoff = secnum / (PAGE_SIZE / 512); 702 if ((pgoff+1)*PAGE_SIZE-1 > dev_info->end - dev_info->start) 703 return -ERANGE; 704 *data = (unsigned long) (dev_info->start+pgoff*PAGE_SIZE); 705 return 0; 706 } 707 708 static void 709 dcssblk_check_params(void) 710 { 711 int rc, i, j, k; 712 char buf[9]; 713 struct dcssblk_dev_info *dev_info; 714 715 for (i = 0; (i < DCSSBLK_PARM_LEN) && (dcssblk_segments[i] != '\0'); 716 i++) { 717 for (j = i; (dcssblk_segments[j] != ',') && 718 (dcssblk_segments[j] != '\0') && 719 (dcssblk_segments[j] != '(') && 720 (j - i) < 8; j++) 721 { 722 buf[j-i] = dcssblk_segments[j]; 723 } 724 buf[j-i] = '\0'; 725 rc = dcssblk_add_store(dcssblk_root_dev, NULL, buf, j-i); 726 if ((rc >= 0) && (dcssblk_segments[j] == '(')) { 727 for (k = 0; buf[k] != '\0'; k++) 728 buf[k] = toupper(buf[k]); 729 if (!strncmp(&dcssblk_segments[j], "(local)", 7)) { 730 down_read(&dcssblk_devices_sem); 731 dev_info = dcssblk_get_device_by_name(buf); 732 up_read(&dcssblk_devices_sem); 733 if (dev_info) 734 dcssblk_shared_store(&dev_info->dev, 735 NULL, "0\n", 2); 736 } 737 } 738 while ((dcssblk_segments[j] != ',') && 739 (dcssblk_segments[j] != '\0')) 740 { 741 j++; 742 } 743 if (dcssblk_segments[j] == '\0') 744 break; 745 i = j; 746 } 747 } 748 749 /* 750 * The init/exit functions. 751 */ 752 static void __exit 753 dcssblk_exit(void) 754 { 755 PRINT_DEBUG("DCSSBLOCK EXIT...\n"); 756 s390_root_dev_unregister(dcssblk_root_dev); 757 unregister_blkdev(dcssblk_major, DCSSBLK_NAME); 758 PRINT_DEBUG("...finished!\n"); 759 } 760 761 static int __init 762 dcssblk_init(void) 763 { 764 int rc; 765 766 PRINT_DEBUG("DCSSBLOCK INIT...\n"); 767 dcssblk_root_dev = s390_root_dev_register("dcssblk"); 768 if (IS_ERR(dcssblk_root_dev)) { 769 PRINT_ERR("device_register() failed!\n"); 770 return PTR_ERR(dcssblk_root_dev); 771 } 772 rc = device_create_file(dcssblk_root_dev, &dev_attr_add); 773 if (rc) { 774 PRINT_ERR("device_create_file(add) failed!\n"); 775 s390_root_dev_unregister(dcssblk_root_dev); 776 return rc; 777 } 778 rc = device_create_file(dcssblk_root_dev, &dev_attr_remove); 779 if (rc) { 780 PRINT_ERR("device_create_file(remove) failed!\n"); 781 s390_root_dev_unregister(dcssblk_root_dev); 782 return rc; 783 } 784 rc = register_blkdev(0, DCSSBLK_NAME); 785 if (rc < 0) { 786 PRINT_ERR("Can't get dynamic major!\n"); 787 s390_root_dev_unregister(dcssblk_root_dev); 788 return rc; 789 } 790 dcssblk_major = rc; 791 init_rwsem(&dcssblk_devices_sem); 792 793 dcssblk_check_params(); 794 795 PRINT_DEBUG("...finished!\n"); 796 return 0; 797 } 798 799 module_init(dcssblk_init); 800 module_exit(dcssblk_exit); 801 802 module_param_string(segments, dcssblk_segments, DCSSBLK_PARM_LEN, 0444); 803 MODULE_PARM_DESC(segments, "Name of DCSS segment(s) to be loaded, " 804 "comma-separated list, each name max. 8 chars.\n" 805 "Adding \"(local)\" to segment name equals echoing 0 to " 806 "/sys/devices/dcssblk/<segment name>/shared after loading " 807 "the segment - \n" 808 "e.g. segments=\"mydcss1,mydcss2,mydcss3(local)\""); 809 810 MODULE_LICENSE("GPL"); 811