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