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