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