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