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