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