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