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 mutex_lock(&gdev->reg_mutex); 79 __ccwgroup_remove_symlinks(gdev); 80 device_unregister(dev); 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 gdev->cdev[i]->dev.driver_data = 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 /* 156 * try to add a new ccwgroup device for one driver 157 * argc and argv[] are a list of bus_id's of devices 158 * belonging to the driver. 159 */ 160 int 161 ccwgroup_create(struct device *root, 162 unsigned int creator_id, 163 struct ccw_driver *cdrv, 164 int argc, char *argv[]) 165 { 166 struct ccwgroup_device *gdev; 167 int i; 168 int rc; 169 170 if (argc > 256) /* disallow dumb users */ 171 return -EINVAL; 172 173 gdev = kzalloc(sizeof(*gdev) + argc*sizeof(gdev->cdev[0]), GFP_KERNEL); 174 if (!gdev) 175 return -ENOMEM; 176 177 atomic_set(&gdev->onoff, 0); 178 mutex_init(&gdev->reg_mutex); 179 mutex_lock(&gdev->reg_mutex); 180 for (i = 0; i < argc; i++) { 181 gdev->cdev[i] = get_ccwdev_by_busid(cdrv, argv[i]); 182 183 /* all devices have to be of the same type in 184 * order to be grouped */ 185 if (!gdev->cdev[i] 186 || gdev->cdev[i]->id.driver_info != 187 gdev->cdev[0]->id.driver_info) { 188 rc = -EINVAL; 189 goto error; 190 } 191 /* Don't allow a device to belong to more than one group. */ 192 if (gdev->cdev[i]->dev.driver_data) { 193 rc = -EINVAL; 194 goto error; 195 } 196 gdev->cdev[i]->dev.driver_data = gdev; 197 } 198 199 gdev->creator_id = creator_id; 200 gdev->count = argc; 201 gdev->dev.bus = &ccwgroup_bus_type; 202 gdev->dev.parent = root; 203 gdev->dev.release = ccwgroup_release; 204 205 snprintf (gdev->dev.bus_id, BUS_ID_SIZE, "%s", 206 gdev->cdev[0]->dev.bus_id); 207 208 rc = device_register(&gdev->dev); 209 if (rc) 210 goto error; 211 get_device(&gdev->dev); 212 rc = device_create_file(&gdev->dev, &dev_attr_ungroup); 213 214 if (rc) { 215 device_unregister(&gdev->dev); 216 goto error; 217 } 218 219 rc = __ccwgroup_create_symlinks(gdev); 220 if (!rc) { 221 mutex_unlock(&gdev->reg_mutex); 222 put_device(&gdev->dev); 223 return 0; 224 } 225 device_remove_file(&gdev->dev, &dev_attr_ungroup); 226 device_unregister(&gdev->dev); 227 error: 228 for (i = 0; i < argc; i++) 229 if (gdev->cdev[i]) { 230 if (gdev->cdev[i]->dev.driver_data == gdev) 231 gdev->cdev[i]->dev.driver_data = NULL; 232 put_device(&gdev->cdev[i]->dev); 233 } 234 mutex_unlock(&gdev->reg_mutex); 235 put_device(&gdev->dev); 236 return rc; 237 } 238 239 static int __init 240 init_ccwgroup (void) 241 { 242 return bus_register (&ccwgroup_bus_type); 243 } 244 245 static void __exit 246 cleanup_ccwgroup (void) 247 { 248 bus_unregister (&ccwgroup_bus_type); 249 } 250 251 module_init(init_ccwgroup); 252 module_exit(cleanup_ccwgroup); 253 254 /************************** driver stuff ******************************/ 255 256 static int 257 ccwgroup_set_online(struct ccwgroup_device *gdev) 258 { 259 struct ccwgroup_driver *gdrv; 260 int ret; 261 262 if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0) 263 return -EAGAIN; 264 if (gdev->state == CCWGROUP_ONLINE) { 265 ret = 0; 266 goto out; 267 } 268 if (!gdev->dev.driver) { 269 ret = -EINVAL; 270 goto out; 271 } 272 gdrv = to_ccwgroupdrv (gdev->dev.driver); 273 if ((ret = gdrv->set_online ? gdrv->set_online(gdev) : 0)) 274 goto out; 275 276 gdev->state = CCWGROUP_ONLINE; 277 out: 278 atomic_set(&gdev->onoff, 0); 279 return ret; 280 } 281 282 static int 283 ccwgroup_set_offline(struct ccwgroup_device *gdev) 284 { 285 struct ccwgroup_driver *gdrv; 286 int ret; 287 288 if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0) 289 return -EAGAIN; 290 if (gdev->state == CCWGROUP_OFFLINE) { 291 ret = 0; 292 goto out; 293 } 294 if (!gdev->dev.driver) { 295 ret = -EINVAL; 296 goto out; 297 } 298 gdrv = to_ccwgroupdrv (gdev->dev.driver); 299 if ((ret = gdrv->set_offline ? gdrv->set_offline(gdev) : 0)) 300 goto out; 301 302 gdev->state = CCWGROUP_OFFLINE; 303 out: 304 atomic_set(&gdev->onoff, 0); 305 return ret; 306 } 307 308 static ssize_t 309 ccwgroup_online_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) 310 { 311 struct ccwgroup_device *gdev; 312 struct ccwgroup_driver *gdrv; 313 unsigned int value; 314 int ret; 315 316 gdev = to_ccwgroupdev(dev); 317 if (!dev->driver) 318 return count; 319 320 gdrv = to_ccwgroupdrv (gdev->dev.driver); 321 if (!try_module_get(gdrv->owner)) 322 return -EINVAL; 323 324 value = simple_strtoul(buf, NULL, 0); 325 ret = count; 326 if (value == 1) 327 ccwgroup_set_online(gdev); 328 else if (value == 0) 329 ccwgroup_set_offline(gdev); 330 else 331 ret = -EINVAL; 332 module_put(gdrv->owner); 333 return ret; 334 } 335 336 static ssize_t 337 ccwgroup_online_show (struct device *dev, struct device_attribute *attr, char *buf) 338 { 339 int online; 340 341 online = (to_ccwgroupdev(dev)->state == CCWGROUP_ONLINE); 342 343 return sprintf(buf, online ? "1\n" : "0\n"); 344 } 345 346 static DEVICE_ATTR(online, 0644, ccwgroup_online_show, ccwgroup_online_store); 347 348 static int 349 ccwgroup_probe (struct device *dev) 350 { 351 struct ccwgroup_device *gdev; 352 struct ccwgroup_driver *gdrv; 353 354 int ret; 355 356 gdev = to_ccwgroupdev(dev); 357 gdrv = to_ccwgroupdrv(dev->driver); 358 359 if ((ret = device_create_file(dev, &dev_attr_online))) 360 return ret; 361 362 pr_debug("%s: device %s\n", __func__, gdev->dev.bus_id); 363 ret = gdrv->probe ? gdrv->probe(gdev) : -ENODEV; 364 if (ret) 365 device_remove_file(dev, &dev_attr_online); 366 367 return ret; 368 } 369 370 static int 371 ccwgroup_remove (struct device *dev) 372 { 373 struct ccwgroup_device *gdev; 374 struct ccwgroup_driver *gdrv; 375 376 gdev = to_ccwgroupdev(dev); 377 gdrv = to_ccwgroupdrv(dev->driver); 378 379 pr_debug("%s: device %s\n", __func__, gdev->dev.bus_id); 380 381 device_remove_file(dev, &dev_attr_online); 382 383 if (gdrv && gdrv->remove) 384 gdrv->remove(gdev); 385 return 0; 386 } 387 388 static struct bus_type ccwgroup_bus_type = { 389 .name = "ccwgroup", 390 .match = ccwgroup_bus_match, 391 .uevent = ccwgroup_uevent, 392 .probe = ccwgroup_probe, 393 .remove = ccwgroup_remove, 394 }; 395 396 int 397 ccwgroup_driver_register (struct ccwgroup_driver *cdriver) 398 { 399 /* register our new driver with the core */ 400 cdriver->driver.bus = &ccwgroup_bus_type; 401 cdriver->driver.name = cdriver->name; 402 403 return driver_register(&cdriver->driver); 404 } 405 406 static int 407 __ccwgroup_match_all(struct device *dev, void *data) 408 { 409 return 1; 410 } 411 412 void 413 ccwgroup_driver_unregister (struct ccwgroup_driver *cdriver) 414 { 415 struct device *dev; 416 417 /* We don't want ccwgroup devices to live longer than their driver. */ 418 get_driver(&cdriver->driver); 419 while ((dev = driver_find_device(&cdriver->driver, NULL, NULL, 420 __ccwgroup_match_all))) { 421 struct ccwgroup_device *gdev = to_ccwgroupdev(dev); 422 423 mutex_lock(&gdev->reg_mutex); 424 __ccwgroup_remove_symlinks(gdev); 425 device_unregister(dev); 426 mutex_unlock(&gdev->reg_mutex); 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 mutex_lock(&gdev->reg_mutex); 448 if (device_is_registered(&gdev->dev)) 449 return gdev; 450 mutex_unlock(&gdev->reg_mutex); 451 put_device(&gdev->dev); 452 } 453 return NULL; 454 } 455 return NULL; 456 } 457 458 void 459 ccwgroup_remove_ccwdev(struct ccw_device *cdev) 460 { 461 struct ccwgroup_device *gdev; 462 463 /* Ignore offlining errors, device is gone anyway. */ 464 ccw_device_set_offline(cdev); 465 /* If one of its devices is gone, the whole group is done for. */ 466 gdev = __ccwgroup_get_gdev_by_cdev(cdev); 467 if (gdev) { 468 __ccwgroup_remove_symlinks(gdev); 469 device_unregister(&gdev->dev); 470 mutex_unlock(&gdev->reg_mutex); 471 put_device(&gdev->dev); 472 } 473 } 474 475 MODULE_LICENSE("GPL"); 476 EXPORT_SYMBOL(ccwgroup_driver_register); 477 EXPORT_SYMBOL(ccwgroup_driver_unregister); 478 EXPORT_SYMBOL(ccwgroup_create); 479 EXPORT_SYMBOL(ccwgroup_probe_ccwdev); 480 EXPORT_SYMBOL(ccwgroup_remove_ccwdev); 481