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