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 __init 319 init_ccwgroup (void) 320 { 321 return bus_register (&ccwgroup_bus_type); 322 } 323 324 static void __exit 325 cleanup_ccwgroup (void) 326 { 327 bus_unregister (&ccwgroup_bus_type); 328 } 329 330 module_init(init_ccwgroup); 331 module_exit(cleanup_ccwgroup); 332 333 /************************** driver stuff ******************************/ 334 335 static int 336 ccwgroup_set_online(struct ccwgroup_device *gdev) 337 { 338 struct ccwgroup_driver *gdrv; 339 int ret; 340 341 if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0) 342 return -EAGAIN; 343 if (gdev->state == CCWGROUP_ONLINE) { 344 ret = 0; 345 goto out; 346 } 347 if (!gdev->dev.driver) { 348 ret = -EINVAL; 349 goto out; 350 } 351 gdrv = to_ccwgroupdrv (gdev->dev.driver); 352 if ((ret = gdrv->set_online ? gdrv->set_online(gdev) : 0)) 353 goto out; 354 355 gdev->state = CCWGROUP_ONLINE; 356 out: 357 atomic_set(&gdev->onoff, 0); 358 return ret; 359 } 360 361 static int 362 ccwgroup_set_offline(struct ccwgroup_device *gdev) 363 { 364 struct ccwgroup_driver *gdrv; 365 int ret; 366 367 if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0) 368 return -EAGAIN; 369 if (gdev->state == CCWGROUP_OFFLINE) { 370 ret = 0; 371 goto out; 372 } 373 if (!gdev->dev.driver) { 374 ret = -EINVAL; 375 goto out; 376 } 377 gdrv = to_ccwgroupdrv (gdev->dev.driver); 378 if ((ret = gdrv->set_offline ? gdrv->set_offline(gdev) : 0)) 379 goto out; 380 381 gdev->state = CCWGROUP_OFFLINE; 382 out: 383 atomic_set(&gdev->onoff, 0); 384 return ret; 385 } 386 387 static ssize_t 388 ccwgroup_online_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 389 { 390 struct ccwgroup_device *gdev; 391 struct ccwgroup_driver *gdrv; 392 unsigned long value; 393 int ret; 394 395 gdev = to_ccwgroupdev(dev); 396 if (!dev->driver) 397 return count; 398 399 gdrv = to_ccwgroupdrv (gdev->dev.driver); 400 if (!try_module_get(gdrv->owner)) 401 return -EINVAL; 402 403 ret = strict_strtoul(buf, 0, &value); 404 if (ret) 405 goto out; 406 ret = count; 407 if (value == 1) 408 ccwgroup_set_online(gdev); 409 else if (value == 0) 410 ccwgroup_set_offline(gdev); 411 else 412 ret = -EINVAL; 413 out: 414 module_put(gdrv->owner); 415 return ret; 416 } 417 418 static ssize_t 419 ccwgroup_online_show (struct device *dev, struct device_attribute *attr, char *buf) 420 { 421 int online; 422 423 online = (to_ccwgroupdev(dev)->state == CCWGROUP_ONLINE); 424 425 return sprintf(buf, online ? "1\n" : "0\n"); 426 } 427 428 static DEVICE_ATTR(online, 0644, ccwgroup_online_show, ccwgroup_online_store); 429 430 static int 431 ccwgroup_probe (struct device *dev) 432 { 433 struct ccwgroup_device *gdev; 434 struct ccwgroup_driver *gdrv; 435 436 int ret; 437 438 gdev = to_ccwgroupdev(dev); 439 gdrv = to_ccwgroupdrv(dev->driver); 440 441 if ((ret = device_create_file(dev, &dev_attr_online))) 442 return ret; 443 444 ret = gdrv->probe ? gdrv->probe(gdev) : -ENODEV; 445 if (ret) 446 device_remove_file(dev, &dev_attr_online); 447 448 return ret; 449 } 450 451 static int 452 ccwgroup_remove (struct device *dev) 453 { 454 struct ccwgroup_device *gdev; 455 struct ccwgroup_driver *gdrv; 456 457 gdev = to_ccwgroupdev(dev); 458 gdrv = to_ccwgroupdrv(dev->driver); 459 460 device_remove_file(dev, &dev_attr_online); 461 462 if (gdrv && gdrv->remove) 463 gdrv->remove(gdev); 464 return 0; 465 } 466 467 static void ccwgroup_shutdown(struct device *dev) 468 { 469 struct ccwgroup_device *gdev; 470 struct ccwgroup_driver *gdrv; 471 472 gdev = to_ccwgroupdev(dev); 473 gdrv = to_ccwgroupdrv(dev->driver); 474 if (gdrv && gdrv->shutdown) 475 gdrv->shutdown(gdev); 476 } 477 478 static struct bus_type ccwgroup_bus_type = { 479 .name = "ccwgroup", 480 .match = ccwgroup_bus_match, 481 .uevent = ccwgroup_uevent, 482 .probe = ccwgroup_probe, 483 .remove = ccwgroup_remove, 484 .shutdown = ccwgroup_shutdown, 485 }; 486 487 /** 488 * ccwgroup_driver_register() - register a ccw group driver 489 * @cdriver: driver to be registered 490 * 491 * This function is mainly a wrapper around driver_register(). 492 */ 493 int ccwgroup_driver_register(struct ccwgroup_driver *cdriver) 494 { 495 /* register our new driver with the core */ 496 cdriver->driver.bus = &ccwgroup_bus_type; 497 cdriver->driver.name = cdriver->name; 498 cdriver->driver.owner = cdriver->owner; 499 500 return driver_register(&cdriver->driver); 501 } 502 503 static int 504 __ccwgroup_match_all(struct device *dev, void *data) 505 { 506 return 1; 507 } 508 509 /** 510 * ccwgroup_driver_unregister() - deregister a ccw group driver 511 * @cdriver: driver to be deregistered 512 * 513 * This function is mainly a wrapper around driver_unregister(). 514 */ 515 void ccwgroup_driver_unregister(struct ccwgroup_driver *cdriver) 516 { 517 struct device *dev; 518 519 /* We don't want ccwgroup devices to live longer than their driver. */ 520 get_driver(&cdriver->driver); 521 while ((dev = driver_find_device(&cdriver->driver, NULL, NULL, 522 __ccwgroup_match_all))) { 523 struct ccwgroup_device *gdev = to_ccwgroupdev(dev); 524 525 mutex_lock(&gdev->reg_mutex); 526 __ccwgroup_remove_symlinks(gdev); 527 device_unregister(dev); 528 mutex_unlock(&gdev->reg_mutex); 529 put_device(dev); 530 } 531 put_driver(&cdriver->driver); 532 driver_unregister(&cdriver->driver); 533 } 534 535 /** 536 * ccwgroup_probe_ccwdev() - probe function for slave devices 537 * @cdev: ccw device to be probed 538 * 539 * This is a dummy probe function for ccw devices that are slave devices in 540 * a ccw group device. 541 * Returns: 542 * always %0 543 */ 544 int ccwgroup_probe_ccwdev(struct ccw_device *cdev) 545 { 546 return 0; 547 } 548 549 static struct ccwgroup_device * 550 __ccwgroup_get_gdev_by_cdev(struct ccw_device *cdev) 551 { 552 struct ccwgroup_device *gdev; 553 554 gdev = dev_get_drvdata(&cdev->dev); 555 if (gdev) { 556 if (get_device(&gdev->dev)) { 557 mutex_lock(&gdev->reg_mutex); 558 if (device_is_registered(&gdev->dev)) 559 return gdev; 560 mutex_unlock(&gdev->reg_mutex); 561 put_device(&gdev->dev); 562 } 563 return NULL; 564 } 565 return NULL; 566 } 567 568 /** 569 * ccwgroup_remove_ccwdev() - remove function for slave devices 570 * @cdev: ccw device to be removed 571 * 572 * This is a remove function for ccw devices that are slave devices in a ccw 573 * group device. It sets the ccw device offline and also deregisters the 574 * embedding ccw group device. 575 */ 576 void ccwgroup_remove_ccwdev(struct ccw_device *cdev) 577 { 578 struct ccwgroup_device *gdev; 579 580 /* Ignore offlining errors, device is gone anyway. */ 581 ccw_device_set_offline(cdev); 582 /* If one of its devices is gone, the whole group is done for. */ 583 gdev = __ccwgroup_get_gdev_by_cdev(cdev); 584 if (gdev) { 585 __ccwgroup_remove_symlinks(gdev); 586 device_unregister(&gdev->dev); 587 mutex_unlock(&gdev->reg_mutex); 588 put_device(&gdev->dev); 589 } 590 } 591 592 MODULE_LICENSE("GPL"); 593 EXPORT_SYMBOL(ccwgroup_driver_register); 594 EXPORT_SYMBOL(ccwgroup_driver_unregister); 595 EXPORT_SYMBOL(ccwgroup_probe_ccwdev); 596 EXPORT_SYMBOL(ccwgroup_remove_ccwdev); 597