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