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 blk_queue_make_request(dev_info->dcssblk_queue, dcssblk_make_request); 419 blk_queue_hardsect_size(dev_info->dcssblk_queue, 4096); 420 /* 421 * load the segment 422 */ 423 rc = segment_load(local_buf, SEGMENT_SHARED, 424 &dev_info->start, &dev_info->end); 425 if (rc < 0) { 426 dcssblk_segment_warn(rc, dev_info->segment_name); 427 goto dealloc_gendisk; 428 } 429 seg_byte_size = (dev_info->end - dev_info->start + 1); 430 set_capacity(dev_info->gd, seg_byte_size >> 9); // size in sectors 431 PRINT_INFO("Loaded segment %s, size = %lu Byte, " 432 "capacity = %lu (512 Byte) sectors\n", local_buf, 433 seg_byte_size, seg_byte_size >> 9); 434 435 dev_info->segment_type = rc; 436 dev_info->save_pending = 0; 437 dev_info->is_shared = 1; 438 dev_info->dev.parent = dcssblk_root_dev; 439 440 /* 441 * get minor, add to list 442 */ 443 down_write(&dcssblk_devices_sem); 444 rc = dcssblk_assign_free_minor(dev_info); 445 if (rc) { 446 up_write(&dcssblk_devices_sem); 447 PRINT_ERR("No free minor number available! " 448 "Unloading segment...\n"); 449 goto unload_seg; 450 } 451 sprintf(dev_info->gd->disk_name, "dcssblk%d", 452 dev_info->gd->first_minor); 453 list_add_tail(&dev_info->lh, &dcssblk_devices); 454 455 if (!try_module_get(THIS_MODULE)) { 456 rc = -ENODEV; 457 goto list_del; 458 } 459 /* 460 * register the device 461 */ 462 rc = device_register(&dev_info->dev); 463 if (rc) { 464 PRINT_ERR("Segment %s could not be registered RC=%d\n", 465 local_buf, rc); 466 module_put(THIS_MODULE); 467 goto list_del; 468 } 469 get_device(&dev_info->dev); 470 rc = device_create_file(&dev_info->dev, &dev_attr_shared); 471 if (rc) 472 goto unregister_dev; 473 rc = device_create_file(&dev_info->dev, &dev_attr_save); 474 if (rc) 475 goto unregister_dev; 476 477 add_disk(dev_info->gd); 478 479 switch (dev_info->segment_type) { 480 case SEG_TYPE_SR: 481 case SEG_TYPE_ER: 482 case SEG_TYPE_SC: 483 set_disk_ro(dev_info->gd,1); 484 break; 485 default: 486 set_disk_ro(dev_info->gd,0); 487 break; 488 } 489 PRINT_DEBUG("Segment %s loaded successfully\n", local_buf); 490 up_write(&dcssblk_devices_sem); 491 rc = count; 492 goto out; 493 494 unregister_dev: 495 PRINT_ERR("device_create_file() failed!\n"); 496 list_del(&dev_info->lh); 497 blk_cleanup_queue(dev_info->dcssblk_queue); 498 dev_info->gd->queue = NULL; 499 put_disk(dev_info->gd); 500 device_unregister(&dev_info->dev); 501 segment_unload(dev_info->segment_name); 502 put_device(&dev_info->dev); 503 up_write(&dcssblk_devices_sem); 504 goto out; 505 list_del: 506 list_del(&dev_info->lh); 507 up_write(&dcssblk_devices_sem); 508 unload_seg: 509 segment_unload(local_buf); 510 dealloc_gendisk: 511 blk_cleanup_queue(dev_info->dcssblk_queue); 512 dev_info->gd->queue = NULL; 513 put_disk(dev_info->gd); 514 free_dev_info: 515 kfree(dev_info); 516 out: 517 kfree(local_buf); 518 out_nobuf: 519 return rc; 520 } 521 522 /* 523 * device attribute for removing devices 524 */ 525 static ssize_t 526 dcssblk_remove_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 527 { 528 struct dcssblk_dev_info *dev_info; 529 int rc, i; 530 char *local_buf; 531 532 if (dev != dcssblk_root_dev) { 533 return -EINVAL; 534 } 535 local_buf = kmalloc(count + 1, GFP_KERNEL); 536 if (local_buf == NULL) { 537 return -ENOMEM; 538 } 539 /* 540 * parse input 541 */ 542 for (i = 0; ((*(buf+i)!='\0') && (*(buf+i)!='\n') && i < count); i++) { 543 local_buf[i] = toupper(buf[i]); 544 } 545 local_buf[i] = '\0'; 546 if ((i == 0) || (i > 8)) { 547 rc = -ENAMETOOLONG; 548 goto out_buf; 549 } 550 551 down_write(&dcssblk_devices_sem); 552 dev_info = dcssblk_get_device_by_name(local_buf); 553 if (dev_info == NULL) { 554 up_write(&dcssblk_devices_sem); 555 PRINT_WARN("Segment %s is not loaded!\n", local_buf); 556 rc = -ENODEV; 557 goto out_buf; 558 } 559 if (atomic_read(&dev_info->use_count) != 0) { 560 up_write(&dcssblk_devices_sem); 561 PRINT_WARN("Segment %s is in use!\n", local_buf); 562 rc = -EBUSY; 563 goto out_buf; 564 } 565 list_del(&dev_info->lh); 566 567 del_gendisk(dev_info->gd); 568 blk_cleanup_queue(dev_info->dcssblk_queue); 569 dev_info->gd->queue = NULL; 570 put_disk(dev_info->gd); 571 device_unregister(&dev_info->dev); 572 segment_unload(dev_info->segment_name); 573 PRINT_DEBUG("Segment %s unloaded successfully\n", 574 dev_info->segment_name); 575 put_device(&dev_info->dev); 576 up_write(&dcssblk_devices_sem); 577 578 rc = count; 579 out_buf: 580 kfree(local_buf); 581 return rc; 582 } 583 584 static int 585 dcssblk_open(struct inode *inode, struct file *filp) 586 { 587 struct dcssblk_dev_info *dev_info; 588 int rc; 589 590 dev_info = inode->i_bdev->bd_disk->private_data; 591 if (NULL == dev_info) { 592 rc = -ENODEV; 593 goto out; 594 } 595 atomic_inc(&dev_info->use_count); 596 inode->i_bdev->bd_block_size = 4096; 597 rc = 0; 598 out: 599 return rc; 600 } 601 602 static int 603 dcssblk_release(struct inode *inode, struct file *filp) 604 { 605 struct dcssblk_dev_info *dev_info; 606 int rc; 607 608 dev_info = inode->i_bdev->bd_disk->private_data; 609 if (NULL == dev_info) { 610 rc = -ENODEV; 611 goto out; 612 } 613 down_write(&dcssblk_devices_sem); 614 if (atomic_dec_and_test(&dev_info->use_count) 615 && (dev_info->save_pending)) { 616 PRINT_INFO("Segment %s became idle and is being saved now\n", 617 dev_info->segment_name); 618 segment_save(dev_info->segment_name); 619 dev_info->save_pending = 0; 620 } 621 up_write(&dcssblk_devices_sem); 622 rc = 0; 623 out: 624 return rc; 625 } 626 627 static int 628 dcssblk_make_request(struct request_queue *q, struct bio *bio) 629 { 630 struct dcssblk_dev_info *dev_info; 631 struct bio_vec *bvec; 632 unsigned long index; 633 unsigned long page_addr; 634 unsigned long source_addr; 635 unsigned long bytes_done; 636 int i; 637 638 bytes_done = 0; 639 dev_info = bio->bi_bdev->bd_disk->private_data; 640 if (dev_info == NULL) 641 goto fail; 642 if ((bio->bi_sector & 7) != 0 || (bio->bi_size & 4095) != 0) 643 /* Request is not page-aligned. */ 644 goto fail; 645 if (((bio->bi_size >> 9) + bio->bi_sector) 646 > get_capacity(bio->bi_bdev->bd_disk)) { 647 /* Request beyond end of DCSS segment. */ 648 goto fail; 649 } 650 /* verify data transfer direction */ 651 if (dev_info->is_shared) { 652 switch (dev_info->segment_type) { 653 case SEG_TYPE_SR: 654 case SEG_TYPE_ER: 655 case SEG_TYPE_SC: 656 /* cannot write to these segments */ 657 if (bio_data_dir(bio) == WRITE) { 658 PRINT_WARN("rejecting write to ro segment %s\n", dev_info->dev.bus_id); 659 goto fail; 660 } 661 } 662 } 663 664 index = (bio->bi_sector >> 3); 665 bio_for_each_segment(bvec, bio, i) { 666 page_addr = (unsigned long) 667 page_address(bvec->bv_page) + bvec->bv_offset; 668 source_addr = dev_info->start + (index<<12) + bytes_done; 669 if (unlikely((page_addr & 4095) != 0) || (bvec->bv_len & 4095) != 0) 670 // More paranoia. 671 goto fail; 672 if (bio_data_dir(bio) == READ) { 673 memcpy((void*)page_addr, (void*)source_addr, 674 bvec->bv_len); 675 } else { 676 memcpy((void*)source_addr, (void*)page_addr, 677 bvec->bv_len); 678 } 679 bytes_done += bvec->bv_len; 680 } 681 bio_endio(bio, 0); 682 return 0; 683 fail: 684 bio_io_error(bio); 685 return 0; 686 } 687 688 static int 689 dcssblk_direct_access (struct block_device *bdev, sector_t secnum, 690 unsigned long *data) 691 { 692 struct dcssblk_dev_info *dev_info; 693 unsigned long pgoff; 694 695 dev_info = bdev->bd_disk->private_data; 696 if (!dev_info) 697 return -ENODEV; 698 if (secnum % (PAGE_SIZE/512)) 699 return -EINVAL; 700 pgoff = secnum / (PAGE_SIZE / 512); 701 if ((pgoff+1)*PAGE_SIZE-1 > dev_info->end - dev_info->start) 702 return -ERANGE; 703 *data = (unsigned long) (dev_info->start+pgoff*PAGE_SIZE); 704 return 0; 705 } 706 707 static void 708 dcssblk_check_params(void) 709 { 710 int rc, i, j, k; 711 char buf[9]; 712 struct dcssblk_dev_info *dev_info; 713 714 for (i = 0; (i < DCSSBLK_PARM_LEN) && (dcssblk_segments[i] != '\0'); 715 i++) { 716 for (j = i; (dcssblk_segments[j] != ',') && 717 (dcssblk_segments[j] != '\0') && 718 (dcssblk_segments[j] != '(') && 719 (j - i) < 8; j++) 720 { 721 buf[j-i] = dcssblk_segments[j]; 722 } 723 buf[j-i] = '\0'; 724 rc = dcssblk_add_store(dcssblk_root_dev, NULL, buf, j-i); 725 if ((rc >= 0) && (dcssblk_segments[j] == '(')) { 726 for (k = 0; buf[k] != '\0'; k++) 727 buf[k] = toupper(buf[k]); 728 if (!strncmp(&dcssblk_segments[j], "(local)", 7)) { 729 down_read(&dcssblk_devices_sem); 730 dev_info = dcssblk_get_device_by_name(buf); 731 up_read(&dcssblk_devices_sem); 732 if (dev_info) 733 dcssblk_shared_store(&dev_info->dev, 734 NULL, "0\n", 2); 735 } 736 } 737 while ((dcssblk_segments[j] != ',') && 738 (dcssblk_segments[j] != '\0')) 739 { 740 j++; 741 } 742 if (dcssblk_segments[j] == '\0') 743 break; 744 i = j; 745 } 746 } 747 748 /* 749 * The init/exit functions. 750 */ 751 static void __exit 752 dcssblk_exit(void) 753 { 754 PRINT_DEBUG("DCSSBLOCK EXIT...\n"); 755 s390_root_dev_unregister(dcssblk_root_dev); 756 unregister_blkdev(dcssblk_major, DCSSBLK_NAME); 757 PRINT_DEBUG("...finished!\n"); 758 } 759 760 static int __init 761 dcssblk_init(void) 762 { 763 int rc; 764 765 PRINT_DEBUG("DCSSBLOCK INIT...\n"); 766 dcssblk_root_dev = s390_root_dev_register("dcssblk"); 767 if (IS_ERR(dcssblk_root_dev)) { 768 PRINT_ERR("device_register() failed!\n"); 769 return PTR_ERR(dcssblk_root_dev); 770 } 771 rc = device_create_file(dcssblk_root_dev, &dev_attr_add); 772 if (rc) { 773 PRINT_ERR("device_create_file(add) failed!\n"); 774 s390_root_dev_unregister(dcssblk_root_dev); 775 return rc; 776 } 777 rc = device_create_file(dcssblk_root_dev, &dev_attr_remove); 778 if (rc) { 779 PRINT_ERR("device_create_file(remove) failed!\n"); 780 s390_root_dev_unregister(dcssblk_root_dev); 781 return rc; 782 } 783 rc = register_blkdev(0, DCSSBLK_NAME); 784 if (rc < 0) { 785 PRINT_ERR("Can't get dynamic major!\n"); 786 s390_root_dev_unregister(dcssblk_root_dev); 787 return rc; 788 } 789 dcssblk_major = rc; 790 init_rwsem(&dcssblk_devices_sem); 791 792 dcssblk_check_params(); 793 794 PRINT_DEBUG("...finished!\n"); 795 return 0; 796 } 797 798 module_init(dcssblk_init); 799 module_exit(dcssblk_exit); 800 801 module_param_string(segments, dcssblk_segments, DCSSBLK_PARM_LEN, 0444); 802 MODULE_PARM_DESC(segments, "Name of DCSS segment(s) to be loaded, " 803 "comma-separated list, each name max. 8 chars.\n" 804 "Adding \"(local)\" to segment name equals echoing 0 to " 805 "/sys/devices/dcssblk/<segment name>/shared after loading " 806 "the segment - \n" 807 "e.g. segments=\"mydcss1,mydcss2,mydcss3(local)\""); 808 809 MODULE_LICENSE("GPL"); 810