Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.1 710 lines 17 kB view raw
1/* 2 * bus driver for ccwgroup 3 * 4 * Copyright IBM Corp. 2002, 2009 5 * 6 * Author(s): Arnd Bergmann (arndb@de.ibm.com) 7 * Cornelia Huck (cornelia.huck@de.ibm.com) 8 */ 9#include <linux/module.h> 10#include <linux/errno.h> 11#include <linux/slab.h> 12#include <linux/list.h> 13#include <linux/device.h> 14#include <linux/init.h> 15#include <linux/ctype.h> 16#include <linux/dcache.h> 17 18#include <asm/ccwdev.h> 19#include <asm/ccwgroup.h> 20 21#define CCW_BUS_ID_SIZE 20 22 23/* In Linux 2.4, we had a channel device layer called "chandev" 24 * that did all sorts of obscure stuff for networking devices. 25 * This is another driver that serves as a replacement for just 26 * one of its functions, namely the translation of single subchannels 27 * to devices that use multiple subchannels. 28 */ 29 30/* a device matches a driver if all its slave devices match the same 31 * entry of the driver */ 32static int 33ccwgroup_bus_match (struct device * dev, struct device_driver * drv) 34{ 35 struct ccwgroup_device *gdev; 36 struct ccwgroup_driver *gdrv; 37 38 gdev = to_ccwgroupdev(dev); 39 gdrv = to_ccwgroupdrv(drv); 40 41 if (gdev->creator_id == gdrv->driver_id) 42 return 1; 43 44 return 0; 45} 46static int 47ccwgroup_uevent (struct device *dev, struct kobj_uevent_env *env) 48{ 49 /* TODO */ 50 return 0; 51} 52 53static struct bus_type ccwgroup_bus_type; 54 55static void 56__ccwgroup_remove_symlinks(struct ccwgroup_device *gdev) 57{ 58 int i; 59 char str[8]; 60 61 for (i = 0; i < gdev->count; i++) { 62 sprintf(str, "cdev%d", i); 63 sysfs_remove_link(&gdev->dev.kobj, str); 64 sysfs_remove_link(&gdev->cdev[i]->dev.kobj, "group_device"); 65 } 66 67} 68 69/* 70 * Remove references from ccw devices to ccw group device and from 71 * ccw group device to ccw devices. 72 */ 73static void __ccwgroup_remove_cdev_refs(struct ccwgroup_device *gdev) 74{ 75 struct ccw_device *cdev; 76 int i; 77 78 for (i = 0; i < gdev->count; i++) { 79 cdev = gdev->cdev[i]; 80 if (!cdev) 81 continue; 82 spin_lock_irq(cdev->ccwlock); 83 dev_set_drvdata(&cdev->dev, NULL); 84 spin_unlock_irq(cdev->ccwlock); 85 gdev->cdev[i] = NULL; 86 put_device(&cdev->dev); 87 } 88} 89 90/* 91 * Provide an 'ungroup' attribute so the user can remove group devices no 92 * longer needed or accidentially created. Saves memory :) 93 */ 94static void ccwgroup_ungroup_callback(struct device *dev) 95{ 96 struct ccwgroup_device *gdev = to_ccwgroupdev(dev); 97 98 mutex_lock(&gdev->reg_mutex); 99 if (device_is_registered(&gdev->dev)) { 100 __ccwgroup_remove_symlinks(gdev); 101 device_unregister(dev); 102 __ccwgroup_remove_cdev_refs(gdev); 103 } 104 mutex_unlock(&gdev->reg_mutex); 105} 106 107static ssize_t 108ccwgroup_ungroup_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 109{ 110 struct ccwgroup_device *gdev; 111 int rc; 112 113 gdev = to_ccwgroupdev(dev); 114 115 /* Prevent concurrent online/offline processing and ungrouping. */ 116 if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0) 117 return -EAGAIN; 118 if (gdev->state != CCWGROUP_OFFLINE) { 119 rc = -EINVAL; 120 goto out; 121 } 122 /* Note that we cannot unregister the device from one of its 123 * attribute methods, so we have to use this roundabout approach. 124 */ 125 rc = device_schedule_callback(dev, ccwgroup_ungroup_callback); 126out: 127 if (rc) { 128 if (rc != -EAGAIN) 129 /* Release onoff "lock" when ungrouping failed. */ 130 atomic_set(&gdev->onoff, 0); 131 return rc; 132 } 133 return count; 134} 135 136static DEVICE_ATTR(ungroup, 0200, NULL, ccwgroup_ungroup_store); 137 138static void 139ccwgroup_release (struct device *dev) 140{ 141 kfree(to_ccwgroupdev(dev)); 142} 143 144static int 145__ccwgroup_create_symlinks(struct ccwgroup_device *gdev) 146{ 147 char str[8]; 148 int i, rc; 149 150 for (i = 0; i < gdev->count; i++) { 151 rc = sysfs_create_link(&gdev->cdev[i]->dev.kobj, &gdev->dev.kobj, 152 "group_device"); 153 if (rc) { 154 for (--i; i >= 0; i--) 155 sysfs_remove_link(&gdev->cdev[i]->dev.kobj, 156 "group_device"); 157 return rc; 158 } 159 } 160 for (i = 0; i < gdev->count; i++) { 161 sprintf(str, "cdev%d", i); 162 rc = sysfs_create_link(&gdev->dev.kobj, &gdev->cdev[i]->dev.kobj, 163 str); 164 if (rc) { 165 for (--i; i >= 0; i--) { 166 sprintf(str, "cdev%d", i); 167 sysfs_remove_link(&gdev->dev.kobj, str); 168 } 169 for (i = 0; i < gdev->count; i++) 170 sysfs_remove_link(&gdev->cdev[i]->dev.kobj, 171 "group_device"); 172 return rc; 173 } 174 } 175 return 0; 176} 177 178static int __get_next_bus_id(const char **buf, char *bus_id) 179{ 180 int rc, len; 181 char *start, *end; 182 183 start = (char *)*buf; 184 end = strchr(start, ','); 185 if (!end) { 186 /* Last entry. Strip trailing newline, if applicable. */ 187 end = strchr(start, '\n'); 188 if (end) 189 *end = '\0'; 190 len = strlen(start) + 1; 191 } else { 192 len = end - start + 1; 193 end++; 194 } 195 if (len < CCW_BUS_ID_SIZE) { 196 strlcpy(bus_id, start, len); 197 rc = 0; 198 } else 199 rc = -EINVAL; 200 *buf = end; 201 return rc; 202} 203 204static int __is_valid_bus_id(char bus_id[CCW_BUS_ID_SIZE]) 205{ 206 int cssid, ssid, devno; 207 208 /* Must be of form %x.%x.%04x */ 209 if (sscanf(bus_id, "%x.%1x.%04x", &cssid, &ssid, &devno) != 3) 210 return 0; 211 return 1; 212} 213 214/** 215 * ccwgroup_create_from_string() - create and register a ccw group device 216 * @root: parent device for the new device 217 * @creator_id: identifier of creating driver 218 * @cdrv: ccw driver of slave devices 219 * @num_devices: number of slave devices 220 * @buf: buffer containing comma separated bus ids of slave devices 221 * 222 * Create and register a new ccw group device as a child of @root. Slave 223 * devices are obtained from the list of bus ids given in @buf and must all 224 * belong to @cdrv. 225 * Returns: 226 * %0 on success and an error code on failure. 227 * Context: 228 * non-atomic 229 */ 230int ccwgroup_create_from_string(struct device *root, unsigned int creator_id, 231 struct ccw_driver *cdrv, int num_devices, 232 const char *buf) 233{ 234 struct ccwgroup_device *gdev; 235 int rc, i; 236 char tmp_bus_id[CCW_BUS_ID_SIZE]; 237 const char *curr_buf; 238 239 gdev = kzalloc(sizeof(*gdev) + num_devices * sizeof(gdev->cdev[0]), 240 GFP_KERNEL); 241 if (!gdev) 242 return -ENOMEM; 243 244 atomic_set(&gdev->onoff, 0); 245 mutex_init(&gdev->reg_mutex); 246 mutex_lock(&gdev->reg_mutex); 247 gdev->creator_id = creator_id; 248 gdev->count = num_devices; 249 gdev->dev.bus = &ccwgroup_bus_type; 250 gdev->dev.parent = root; 251 gdev->dev.release = ccwgroup_release; 252 device_initialize(&gdev->dev); 253 254 curr_buf = buf; 255 for (i = 0; i < num_devices && curr_buf; i++) { 256 rc = __get_next_bus_id(&curr_buf, tmp_bus_id); 257 if (rc != 0) 258 goto error; 259 if (!__is_valid_bus_id(tmp_bus_id)) { 260 rc = -EINVAL; 261 goto error; 262 } 263 gdev->cdev[i] = get_ccwdev_by_busid(cdrv, tmp_bus_id); 264 /* 265 * All devices have to be of the same type in 266 * order to be grouped. 267 */ 268 if (!gdev->cdev[i] 269 || gdev->cdev[i]->id.driver_info != 270 gdev->cdev[0]->id.driver_info) { 271 rc = -EINVAL; 272 goto error; 273 } 274 /* Don't allow a device to belong to more than one group. */ 275 spin_lock_irq(gdev->cdev[i]->ccwlock); 276 if (dev_get_drvdata(&gdev->cdev[i]->dev)) { 277 spin_unlock_irq(gdev->cdev[i]->ccwlock); 278 rc = -EINVAL; 279 goto error; 280 } 281 dev_set_drvdata(&gdev->cdev[i]->dev, gdev); 282 spin_unlock_irq(gdev->cdev[i]->ccwlock); 283 } 284 /* Check for sufficient number of bus ids. */ 285 if (i < num_devices && !curr_buf) { 286 rc = -EINVAL; 287 goto error; 288 } 289 /* Check for trailing stuff. */ 290 if (i == num_devices && strlen(curr_buf) > 0) { 291 rc = -EINVAL; 292 goto error; 293 } 294 295 dev_set_name(&gdev->dev, "%s", dev_name(&gdev->cdev[0]->dev)); 296 297 rc = device_add(&gdev->dev); 298 if (rc) 299 goto error; 300 get_device(&gdev->dev); 301 rc = device_create_file(&gdev->dev, &dev_attr_ungroup); 302 303 if (rc) { 304 device_unregister(&gdev->dev); 305 goto error; 306 } 307 308 rc = __ccwgroup_create_symlinks(gdev); 309 if (!rc) { 310 mutex_unlock(&gdev->reg_mutex); 311 put_device(&gdev->dev); 312 return 0; 313 } 314 device_remove_file(&gdev->dev, &dev_attr_ungroup); 315 device_unregister(&gdev->dev); 316error: 317 for (i = 0; i < num_devices; i++) 318 if (gdev->cdev[i]) { 319 spin_lock_irq(gdev->cdev[i]->ccwlock); 320 if (dev_get_drvdata(&gdev->cdev[i]->dev) == gdev) 321 dev_set_drvdata(&gdev->cdev[i]->dev, NULL); 322 spin_unlock_irq(gdev->cdev[i]->ccwlock); 323 put_device(&gdev->cdev[i]->dev); 324 gdev->cdev[i] = NULL; 325 } 326 mutex_unlock(&gdev->reg_mutex); 327 put_device(&gdev->dev); 328 return rc; 329} 330EXPORT_SYMBOL(ccwgroup_create_from_string); 331 332static int ccwgroup_notifier(struct notifier_block *nb, unsigned long action, 333 void *data); 334 335static struct notifier_block ccwgroup_nb = { 336 .notifier_call = ccwgroup_notifier 337}; 338 339static int __init init_ccwgroup(void) 340{ 341 int ret; 342 343 ret = bus_register(&ccwgroup_bus_type); 344 if (ret) 345 return ret; 346 347 ret = bus_register_notifier(&ccwgroup_bus_type, &ccwgroup_nb); 348 if (ret) 349 bus_unregister(&ccwgroup_bus_type); 350 351 return ret; 352} 353 354static void __exit cleanup_ccwgroup(void) 355{ 356 bus_unregister_notifier(&ccwgroup_bus_type, &ccwgroup_nb); 357 bus_unregister(&ccwgroup_bus_type); 358} 359 360module_init(init_ccwgroup); 361module_exit(cleanup_ccwgroup); 362 363/************************** driver stuff ******************************/ 364 365static int 366ccwgroup_set_online(struct ccwgroup_device *gdev) 367{ 368 struct ccwgroup_driver *gdrv; 369 int ret; 370 371 if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0) 372 return -EAGAIN; 373 if (gdev->state == CCWGROUP_ONLINE) { 374 ret = 0; 375 goto out; 376 } 377 if (!gdev->dev.driver) { 378 ret = -EINVAL; 379 goto out; 380 } 381 gdrv = to_ccwgroupdrv (gdev->dev.driver); 382 if ((ret = gdrv->set_online ? gdrv->set_online(gdev) : 0)) 383 goto out; 384 385 gdev->state = CCWGROUP_ONLINE; 386 out: 387 atomic_set(&gdev->onoff, 0); 388 return ret; 389} 390 391static int 392ccwgroup_set_offline(struct ccwgroup_device *gdev) 393{ 394 struct ccwgroup_driver *gdrv; 395 int ret; 396 397 if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0) 398 return -EAGAIN; 399 if (gdev->state == CCWGROUP_OFFLINE) { 400 ret = 0; 401 goto out; 402 } 403 if (!gdev->dev.driver) { 404 ret = -EINVAL; 405 goto out; 406 } 407 gdrv = to_ccwgroupdrv (gdev->dev.driver); 408 if ((ret = gdrv->set_offline ? gdrv->set_offline(gdev) : 0)) 409 goto out; 410 411 gdev->state = CCWGROUP_OFFLINE; 412 out: 413 atomic_set(&gdev->onoff, 0); 414 return ret; 415} 416 417static ssize_t 418ccwgroup_online_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 419{ 420 struct ccwgroup_device *gdev; 421 struct ccwgroup_driver *gdrv; 422 unsigned long value; 423 int ret; 424 425 if (!dev->driver) 426 return -ENODEV; 427 428 gdev = to_ccwgroupdev(dev); 429 gdrv = to_ccwgroupdrv(dev->driver); 430 431 if (!try_module_get(gdrv->driver.owner)) 432 return -EINVAL; 433 434 ret = strict_strtoul(buf, 0, &value); 435 if (ret) 436 goto out; 437 438 if (value == 1) 439 ret = ccwgroup_set_online(gdev); 440 else if (value == 0) 441 ret = ccwgroup_set_offline(gdev); 442 else 443 ret = -EINVAL; 444out: 445 module_put(gdrv->driver.owner); 446 return (ret == 0) ? count : ret; 447} 448 449static ssize_t 450ccwgroup_online_show (struct device *dev, struct device_attribute *attr, char *buf) 451{ 452 int online; 453 454 online = (to_ccwgroupdev(dev)->state == CCWGROUP_ONLINE); 455 456 return sprintf(buf, online ? "1\n" : "0\n"); 457} 458 459static DEVICE_ATTR(online, 0644, ccwgroup_online_show, ccwgroup_online_store); 460 461static int 462ccwgroup_probe (struct device *dev) 463{ 464 struct ccwgroup_device *gdev; 465 struct ccwgroup_driver *gdrv; 466 467 int ret; 468 469 gdev = to_ccwgroupdev(dev); 470 gdrv = to_ccwgroupdrv(dev->driver); 471 472 if ((ret = device_create_file(dev, &dev_attr_online))) 473 return ret; 474 475 ret = gdrv->probe ? gdrv->probe(gdev) : -ENODEV; 476 if (ret) 477 device_remove_file(dev, &dev_attr_online); 478 479 return ret; 480} 481 482static int 483ccwgroup_remove (struct device *dev) 484{ 485 struct ccwgroup_device *gdev; 486 struct ccwgroup_driver *gdrv; 487 488 device_remove_file(dev, &dev_attr_online); 489 device_remove_file(dev, &dev_attr_ungroup); 490 491 if (!dev->driver) 492 return 0; 493 494 gdev = to_ccwgroupdev(dev); 495 gdrv = to_ccwgroupdrv(dev->driver); 496 497 if (gdrv->remove) 498 gdrv->remove(gdev); 499 500 return 0; 501} 502 503static void ccwgroup_shutdown(struct device *dev) 504{ 505 struct ccwgroup_device *gdev; 506 struct ccwgroup_driver *gdrv; 507 508 if (!dev->driver) 509 return; 510 511 gdev = to_ccwgroupdev(dev); 512 gdrv = to_ccwgroupdrv(dev->driver); 513 514 if (gdrv->shutdown) 515 gdrv->shutdown(gdev); 516} 517 518static int ccwgroup_pm_prepare(struct device *dev) 519{ 520 struct ccwgroup_device *gdev = to_ccwgroupdev(dev); 521 struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver); 522 523 /* Fail while device is being set online/offline. */ 524 if (atomic_read(&gdev->onoff)) 525 return -EAGAIN; 526 527 if (!gdev->dev.driver || gdev->state != CCWGROUP_ONLINE) 528 return 0; 529 530 return gdrv->prepare ? gdrv->prepare(gdev) : 0; 531} 532 533static void ccwgroup_pm_complete(struct device *dev) 534{ 535 struct ccwgroup_device *gdev = to_ccwgroupdev(dev); 536 struct ccwgroup_driver *gdrv = to_ccwgroupdrv(dev->driver); 537 538 if (!gdev->dev.driver || gdev->state != CCWGROUP_ONLINE) 539 return; 540 541 if (gdrv->complete) 542 gdrv->complete(gdev); 543} 544 545static int ccwgroup_pm_freeze(struct device *dev) 546{ 547 struct ccwgroup_device *gdev = to_ccwgroupdev(dev); 548 struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver); 549 550 if (!gdev->dev.driver || gdev->state != CCWGROUP_ONLINE) 551 return 0; 552 553 return gdrv->freeze ? gdrv->freeze(gdev) : 0; 554} 555 556static int ccwgroup_pm_thaw(struct device *dev) 557{ 558 struct ccwgroup_device *gdev = to_ccwgroupdev(dev); 559 struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver); 560 561 if (!gdev->dev.driver || gdev->state != CCWGROUP_ONLINE) 562 return 0; 563 564 return gdrv->thaw ? gdrv->thaw(gdev) : 0; 565} 566 567static int ccwgroup_pm_restore(struct device *dev) 568{ 569 struct ccwgroup_device *gdev = to_ccwgroupdev(dev); 570 struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver); 571 572 if (!gdev->dev.driver || gdev->state != CCWGROUP_ONLINE) 573 return 0; 574 575 return gdrv->restore ? gdrv->restore(gdev) : 0; 576} 577 578static const struct dev_pm_ops ccwgroup_pm_ops = { 579 .prepare = ccwgroup_pm_prepare, 580 .complete = ccwgroup_pm_complete, 581 .freeze = ccwgroup_pm_freeze, 582 .thaw = ccwgroup_pm_thaw, 583 .restore = ccwgroup_pm_restore, 584}; 585 586static struct bus_type ccwgroup_bus_type = { 587 .name = "ccwgroup", 588 .match = ccwgroup_bus_match, 589 .uevent = ccwgroup_uevent, 590 .probe = ccwgroup_probe, 591 .remove = ccwgroup_remove, 592 .shutdown = ccwgroup_shutdown, 593 .pm = &ccwgroup_pm_ops, 594}; 595 596 597static int ccwgroup_notifier(struct notifier_block *nb, unsigned long action, 598 void *data) 599{ 600 struct device *dev = data; 601 602 if (action == BUS_NOTIFY_UNBIND_DRIVER) 603 device_schedule_callback(dev, ccwgroup_ungroup_callback); 604 605 return NOTIFY_OK; 606} 607 608 609/** 610 * ccwgroup_driver_register() - register a ccw group driver 611 * @cdriver: driver to be registered 612 * 613 * This function is mainly a wrapper around driver_register(). 614 */ 615int ccwgroup_driver_register(struct ccwgroup_driver *cdriver) 616{ 617 /* register our new driver with the core */ 618 cdriver->driver.bus = &ccwgroup_bus_type; 619 620 return driver_register(&cdriver->driver); 621} 622 623static int 624__ccwgroup_match_all(struct device *dev, void *data) 625{ 626 return 1; 627} 628 629/** 630 * ccwgroup_driver_unregister() - deregister a ccw group driver 631 * @cdriver: driver to be deregistered 632 * 633 * This function is mainly a wrapper around driver_unregister(). 634 */ 635void ccwgroup_driver_unregister(struct ccwgroup_driver *cdriver) 636{ 637 struct device *dev; 638 639 /* We don't want ccwgroup devices to live longer than their driver. */ 640 get_driver(&cdriver->driver); 641 while ((dev = driver_find_device(&cdriver->driver, NULL, NULL, 642 __ccwgroup_match_all))) { 643 struct ccwgroup_device *gdev = to_ccwgroupdev(dev); 644 645 mutex_lock(&gdev->reg_mutex); 646 __ccwgroup_remove_symlinks(gdev); 647 device_unregister(dev); 648 __ccwgroup_remove_cdev_refs(gdev); 649 mutex_unlock(&gdev->reg_mutex); 650 put_device(dev); 651 } 652 put_driver(&cdriver->driver); 653 driver_unregister(&cdriver->driver); 654} 655 656/** 657 * ccwgroup_probe_ccwdev() - probe function for slave devices 658 * @cdev: ccw device to be probed 659 * 660 * This is a dummy probe function for ccw devices that are slave devices in 661 * a ccw group device. 662 * Returns: 663 * always %0 664 */ 665int ccwgroup_probe_ccwdev(struct ccw_device *cdev) 666{ 667 return 0; 668} 669 670/** 671 * ccwgroup_remove_ccwdev() - remove function for slave devices 672 * @cdev: ccw device to be removed 673 * 674 * This is a remove function for ccw devices that are slave devices in a ccw 675 * group device. It sets the ccw device offline and also deregisters the 676 * embedding ccw group device. 677 */ 678void ccwgroup_remove_ccwdev(struct ccw_device *cdev) 679{ 680 struct ccwgroup_device *gdev; 681 682 /* Ignore offlining errors, device is gone anyway. */ 683 ccw_device_set_offline(cdev); 684 /* If one of its devices is gone, the whole group is done for. */ 685 spin_lock_irq(cdev->ccwlock); 686 gdev = dev_get_drvdata(&cdev->dev); 687 if (!gdev) { 688 spin_unlock_irq(cdev->ccwlock); 689 return; 690 } 691 /* Get ccwgroup device reference for local processing. */ 692 get_device(&gdev->dev); 693 spin_unlock_irq(cdev->ccwlock); 694 /* Unregister group device. */ 695 mutex_lock(&gdev->reg_mutex); 696 if (device_is_registered(&gdev->dev)) { 697 __ccwgroup_remove_symlinks(gdev); 698 device_unregister(&gdev->dev); 699 __ccwgroup_remove_cdev_refs(gdev); 700 } 701 mutex_unlock(&gdev->reg_mutex); 702 /* Release ccwgroup device reference for local processing. */ 703 put_device(&gdev->dev); 704} 705 706MODULE_LICENSE("GPL"); 707EXPORT_SYMBOL(ccwgroup_driver_register); 708EXPORT_SYMBOL(ccwgroup_driver_unregister); 709EXPORT_SYMBOL(ccwgroup_probe_ccwdev); 710EXPORT_SYMBOL(ccwgroup_remove_ccwdev);