1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * bus driver for ccwgroup 4 * 5 * Copyright IBM Corp. 2002, 2012 6 * 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/cio.h> 20 #include <asm/ccwdev.h> 21 #include <asm/ccwgroup.h> 22 23 #include "device.h" 24 25 #define CCW_BUS_ID_SIZE 10 26 27 /* In Linux 2.4, we had a channel device layer called "chandev" 28 * that did all sorts of obscure stuff for networking devices. 29 * This is another driver that serves as a replacement for just 30 * one of its functions, namely the translation of single subchannels 31 * to devices that use multiple subchannels. 32 */ 33 34 static struct bus_type ccwgroup_bus_type; 35 36 static void __ccwgroup_remove_symlinks(struct ccwgroup_device *gdev) 37 { 38 int i; 39 char str[16]; 40 41 for (i = 0; i < gdev->count; i++) { 42 sprintf(str, "cdev%d", i); 43 sysfs_remove_link(&gdev->dev.kobj, str); 44 sysfs_remove_link(&gdev->cdev[i]->dev.kobj, "group_device"); 45 } 46 } 47 48 /** 49 * ccwgroup_set_online() - enable a ccwgroup device 50 * @gdev: target ccwgroup device 51 * 52 * This function attempts to put the ccwgroup device into the online state. 53 * Returns: 54 * %0 on success and a negative error value on failure. 55 */ 56 int ccwgroup_set_online(struct ccwgroup_device *gdev) 57 { 58 struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver); 59 int ret = -EINVAL; 60 61 if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0) 62 return -EAGAIN; 63 if (gdev->state == CCWGROUP_ONLINE) 64 goto out; 65 if (gdrv->set_online) 66 ret = gdrv->set_online(gdev); 67 if (ret) 68 goto out; 69 70 gdev->state = CCWGROUP_ONLINE; 71 out: 72 atomic_set(&gdev->onoff, 0); 73 return ret; 74 } 75 EXPORT_SYMBOL(ccwgroup_set_online); 76 77 /** 78 * ccwgroup_set_offline() - disable a ccwgroup device 79 * @gdev: target ccwgroup device 80 * 81 * This function attempts to put the ccwgroup device into the offline state. 82 * Returns: 83 * %0 on success and a negative error value on failure. 84 */ 85 int ccwgroup_set_offline(struct ccwgroup_device *gdev) 86 { 87 struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver); 88 int ret = -EINVAL; 89 90 if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0) 91 return -EAGAIN; 92 if (gdev->state == CCWGROUP_OFFLINE) 93 goto out; 94 if (gdrv->set_offline) 95 ret = gdrv->set_offline(gdev); 96 if (ret) 97 goto out; 98 99 gdev->state = CCWGROUP_OFFLINE; 100 out: 101 atomic_set(&gdev->onoff, 0); 102 return ret; 103 } 104 EXPORT_SYMBOL(ccwgroup_set_offline); 105 106 static ssize_t ccwgroup_online_store(struct device *dev, 107 struct device_attribute *attr, 108 const char *buf, size_t count) 109 { 110 struct ccwgroup_device *gdev = to_ccwgroupdev(dev); 111 unsigned long value; 112 int ret; 113 114 device_lock(dev); 115 if (!dev->driver) { 116 ret = -EINVAL; 117 goto out; 118 } 119 120 ret = kstrtoul(buf, 0, &value); 121 if (ret) 122 goto out; 123 124 if (value == 1) 125 ret = ccwgroup_set_online(gdev); 126 else if (value == 0) 127 ret = ccwgroup_set_offline(gdev); 128 else 129 ret = -EINVAL; 130 out: 131 device_unlock(dev); 132 return (ret == 0) ? count : ret; 133 } 134 135 static ssize_t ccwgroup_online_show(struct device *dev, 136 struct device_attribute *attr, 137 char *buf) 138 { 139 struct ccwgroup_device *gdev = to_ccwgroupdev(dev); 140 int online; 141 142 online = (gdev->state == CCWGROUP_ONLINE) ? 1 : 0; 143 144 return scnprintf(buf, PAGE_SIZE, "%d\n", online); 145 } 146 147 /* 148 * Provide an 'ungroup' attribute so the user can remove group devices no 149 * longer needed or accidentially created. Saves memory :) 150 */ 151 static void ccwgroup_ungroup(struct ccwgroup_device *gdev) 152 { 153 mutex_lock(&gdev->reg_mutex); 154 if (device_is_registered(&gdev->dev)) { 155 __ccwgroup_remove_symlinks(gdev); 156 device_unregister(&gdev->dev); 157 } 158 mutex_unlock(&gdev->reg_mutex); 159 } 160 161 static ssize_t ccwgroup_ungroup_store(struct device *dev, 162 struct device_attribute *attr, 163 const char *buf, size_t count) 164 { 165 struct ccwgroup_device *gdev = to_ccwgroupdev(dev); 166 int rc = 0; 167 168 /* Prevent concurrent online/offline processing and ungrouping. */ 169 if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0) 170 return -EAGAIN; 171 if (gdev->state != CCWGROUP_OFFLINE) { 172 rc = -EINVAL; 173 goto out; 174 } 175 176 if (device_remove_file_self(dev, attr)) 177 ccwgroup_ungroup(gdev); 178 else 179 rc = -ENODEV; 180 out: 181 if (rc) { 182 /* Release onoff "lock" when ungrouping failed. */ 183 atomic_set(&gdev->onoff, 0); 184 return rc; 185 } 186 return count; 187 } 188 static DEVICE_ATTR(ungroup, 0200, NULL, ccwgroup_ungroup_store); 189 static DEVICE_ATTR(online, 0644, ccwgroup_online_show, ccwgroup_online_store); 190 191 static struct attribute *ccwgroup_dev_attrs[] = { 192 &dev_attr_online.attr, 193 &dev_attr_ungroup.attr, 194 NULL, 195 }; 196 ATTRIBUTE_GROUPS(ccwgroup_dev); 197 198 static void ccwgroup_ungroup_workfn(struct work_struct *work) 199 { 200 struct ccwgroup_device *gdev = 201 container_of(work, struct ccwgroup_device, ungroup_work); 202 203 ccwgroup_ungroup(gdev); 204 put_device(&gdev->dev); 205 } 206 207 static void ccwgroup_release(struct device *dev) 208 { 209 struct ccwgroup_device *gdev = to_ccwgroupdev(dev); 210 unsigned int i; 211 212 for (i = 0; i < gdev->count; i++) { 213 struct ccw_device *cdev = gdev->cdev[i]; 214 unsigned long flags; 215 216 if (cdev) { 217 spin_lock_irqsave(cdev->ccwlock, flags); 218 if (dev_get_drvdata(&cdev->dev) == gdev) 219 dev_set_drvdata(&cdev->dev, NULL); 220 spin_unlock_irqrestore(cdev->ccwlock, flags); 221 put_device(&cdev->dev); 222 } 223 } 224 225 kfree(gdev); 226 } 227 228 static int __ccwgroup_create_symlinks(struct ccwgroup_device *gdev) 229 { 230 char str[16]; 231 int i, rc; 232 233 for (i = 0; i < gdev->count; i++) { 234 rc = sysfs_create_link(&gdev->cdev[i]->dev.kobj, 235 &gdev->dev.kobj, "group_device"); 236 if (rc) { 237 for (--i; i >= 0; i--) 238 sysfs_remove_link(&gdev->cdev[i]->dev.kobj, 239 "group_device"); 240 return rc; 241 } 242 } 243 for (i = 0; i < gdev->count; i++) { 244 sprintf(str, "cdev%d", i); 245 rc = sysfs_create_link(&gdev->dev.kobj, 246 &gdev->cdev[i]->dev.kobj, str); 247 if (rc) { 248 for (--i; i >= 0; i--) { 249 sprintf(str, "cdev%d", i); 250 sysfs_remove_link(&gdev->dev.kobj, str); 251 } 252 for (i = 0; i < gdev->count; i++) 253 sysfs_remove_link(&gdev->cdev[i]->dev.kobj, 254 "group_device"); 255 return rc; 256 } 257 } 258 return 0; 259 } 260 261 static int __get_next_id(const char **buf, struct ccw_dev_id *id) 262 { 263 unsigned int cssid, ssid, devno; 264 int ret = 0, len; 265 char *start, *end; 266 267 start = (char *)*buf; 268 end = strchr(start, ','); 269 if (!end) { 270 /* Last entry. Strip trailing newline, if applicable. */ 271 end = strchr(start, '\n'); 272 if (end) 273 *end = '\0'; 274 len = strlen(start) + 1; 275 } else { 276 len = end - start + 1; 277 end++; 278 } 279 if (len <= CCW_BUS_ID_SIZE) { 280 if (sscanf(start, "%2x.%1x.%04x", &cssid, &ssid, &devno) != 3) 281 ret = -EINVAL; 282 } else 283 ret = -EINVAL; 284 285 if (!ret) { 286 id->ssid = ssid; 287 id->devno = devno; 288 } 289 *buf = end; 290 return ret; 291 } 292 293 /** 294 * ccwgroup_create_dev() - create and register a ccw group device 295 * @parent: parent device for the new device 296 * @gdrv: driver for the new group device 297 * @num_devices: number of slave devices 298 * @buf: buffer containing comma separated bus ids of slave devices 299 * 300 * Create and register a new ccw group device as a child of @parent. Slave 301 * devices are obtained from the list of bus ids given in @buf. 302 * Returns: 303 * %0 on success and an error code on failure. 304 * Context: 305 * non-atomic 306 */ 307 int ccwgroup_create_dev(struct device *parent, struct ccwgroup_driver *gdrv, 308 int num_devices, const char *buf) 309 { 310 struct ccwgroup_device *gdev; 311 struct ccw_dev_id dev_id; 312 int rc, i; 313 314 if (num_devices < 1) 315 return -EINVAL; 316 317 gdev = kzalloc(struct_size(gdev, cdev, num_devices), GFP_KERNEL); 318 if (!gdev) 319 return -ENOMEM; 320 321 atomic_set(&gdev->onoff, 0); 322 mutex_init(&gdev->reg_mutex); 323 mutex_lock(&gdev->reg_mutex); 324 INIT_WORK(&gdev->ungroup_work, ccwgroup_ungroup_workfn); 325 gdev->count = num_devices; 326 gdev->dev.bus = &ccwgroup_bus_type; 327 gdev->dev.parent = parent; 328 gdev->dev.release = ccwgroup_release; 329 device_initialize(&gdev->dev); 330 331 for (i = 0; i < num_devices && buf; i++) { 332 rc = __get_next_id(&buf, &dev_id); 333 if (rc != 0) 334 goto error; 335 gdev->cdev[i] = get_ccwdev_by_dev_id(&dev_id); 336 /* 337 * All devices have to be of the same type in 338 * order to be grouped. 339 */ 340 if (!gdev->cdev[i] || !gdev->cdev[i]->drv || 341 gdev->cdev[i]->drv != gdev->cdev[0]->drv || 342 gdev->cdev[i]->id.driver_info != 343 gdev->cdev[0]->id.driver_info) { 344 rc = -EINVAL; 345 goto error; 346 } 347 /* Don't allow a device to belong to more than one group. */ 348 spin_lock_irq(gdev->cdev[i]->ccwlock); 349 if (dev_get_drvdata(&gdev->cdev[i]->dev)) { 350 spin_unlock_irq(gdev->cdev[i]->ccwlock); 351 rc = -EINVAL; 352 goto error; 353 } 354 dev_set_drvdata(&gdev->cdev[i]->dev, gdev); 355 spin_unlock_irq(gdev->cdev[i]->ccwlock); 356 } 357 /* Check for sufficient number of bus ids. */ 358 if (i < num_devices) { 359 rc = -EINVAL; 360 goto error; 361 } 362 /* Check for trailing stuff. */ 363 if (i == num_devices && buf && strlen(buf) > 0) { 364 rc = -EINVAL; 365 goto error; 366 } 367 /* Check if the devices are bound to the required ccw driver. */ 368 if (gdrv && gdrv->ccw_driver && 369 gdev->cdev[0]->drv != gdrv->ccw_driver) { 370 rc = -EINVAL; 371 goto error; 372 } 373 374 dev_set_name(&gdev->dev, "%s", dev_name(&gdev->cdev[0]->dev)); 375 376 if (gdrv) { 377 gdev->dev.driver = &gdrv->driver; 378 rc = gdrv->setup ? gdrv->setup(gdev) : 0; 379 if (rc) 380 goto error; 381 } 382 rc = device_add(&gdev->dev); 383 if (rc) 384 goto error; 385 rc = __ccwgroup_create_symlinks(gdev); 386 if (rc) { 387 device_del(&gdev->dev); 388 goto error; 389 } 390 mutex_unlock(&gdev->reg_mutex); 391 return 0; 392 error: 393 mutex_unlock(&gdev->reg_mutex); 394 put_device(&gdev->dev); 395 return rc; 396 } 397 EXPORT_SYMBOL(ccwgroup_create_dev); 398 399 static int ccwgroup_notifier(struct notifier_block *nb, unsigned long action, 400 void *data) 401 { 402 struct ccwgroup_device *gdev = to_ccwgroupdev(data); 403 404 if (action == BUS_NOTIFY_UNBOUND_DRIVER) { 405 get_device(&gdev->dev); 406 schedule_work(&gdev->ungroup_work); 407 } 408 409 return NOTIFY_OK; 410 } 411 412 static struct notifier_block ccwgroup_nb = { 413 .notifier_call = ccwgroup_notifier 414 }; 415 416 static int __init init_ccwgroup(void) 417 { 418 int ret; 419 420 ret = bus_register(&ccwgroup_bus_type); 421 if (ret) 422 return ret; 423 424 ret = bus_register_notifier(&ccwgroup_bus_type, &ccwgroup_nb); 425 if (ret) 426 bus_unregister(&ccwgroup_bus_type); 427 428 return ret; 429 } 430 431 static void __exit cleanup_ccwgroup(void) 432 { 433 bus_unregister_notifier(&ccwgroup_bus_type, &ccwgroup_nb); 434 bus_unregister(&ccwgroup_bus_type); 435 } 436 437 module_init(init_ccwgroup); 438 module_exit(cleanup_ccwgroup); 439 440 /************************** driver stuff ******************************/ 441 442 static void ccwgroup_remove(struct device *dev) 443 { 444 struct ccwgroup_device *gdev = to_ccwgroupdev(dev); 445 struct ccwgroup_driver *gdrv = to_ccwgroupdrv(dev->driver); 446 447 if (gdrv->remove) 448 gdrv->remove(gdev); 449 } 450 451 static void ccwgroup_shutdown(struct device *dev) 452 { 453 struct ccwgroup_device *gdev = to_ccwgroupdev(dev); 454 struct ccwgroup_driver *gdrv = to_ccwgroupdrv(dev->driver); 455 456 if (!dev->driver) 457 return; 458 if (gdrv->shutdown) 459 gdrv->shutdown(gdev); 460 } 461 462 static struct bus_type ccwgroup_bus_type = { 463 .name = "ccwgroup", 464 .dev_groups = ccwgroup_dev_groups, 465 .remove = ccwgroup_remove, 466 .shutdown = ccwgroup_shutdown, 467 }; 468 469 bool dev_is_ccwgroup(struct device *dev) 470 { 471 return dev->bus == &ccwgroup_bus_type; 472 } 473 EXPORT_SYMBOL(dev_is_ccwgroup); 474 475 /** 476 * ccwgroup_driver_register() - register a ccw group driver 477 * @cdriver: driver to be registered 478 * 479 * This function is mainly a wrapper around driver_register(). 480 */ 481 int ccwgroup_driver_register(struct ccwgroup_driver *cdriver) 482 { 483 /* register our new driver with the core */ 484 cdriver->driver.bus = &ccwgroup_bus_type; 485 486 return driver_register(&cdriver->driver); 487 } 488 EXPORT_SYMBOL(ccwgroup_driver_register); 489 490 /** 491 * ccwgroup_driver_unregister() - deregister a ccw group driver 492 * @cdriver: driver to be deregistered 493 * 494 * This function is mainly a wrapper around driver_unregister(). 495 */ 496 void ccwgroup_driver_unregister(struct ccwgroup_driver *cdriver) 497 { 498 driver_unregister(&cdriver->driver); 499 } 500 EXPORT_SYMBOL(ccwgroup_driver_unregister); 501 502 /** 503 * ccwgroup_probe_ccwdev() - probe function for slave devices 504 * @cdev: ccw device to be probed 505 * 506 * This is a dummy probe function for ccw devices that are slave devices in 507 * a ccw group device. 508 * Returns: 509 * always %0 510 */ 511 int ccwgroup_probe_ccwdev(struct ccw_device *cdev) 512 { 513 return 0; 514 } 515 EXPORT_SYMBOL(ccwgroup_probe_ccwdev); 516 517 /** 518 * ccwgroup_remove_ccwdev() - remove function for slave devices 519 * @cdev: ccw device to be removed 520 * 521 * This is a remove function for ccw devices that are slave devices in a ccw 522 * group device. It sets the ccw device offline and also deregisters the 523 * embedding ccw group device. 524 */ 525 void ccwgroup_remove_ccwdev(struct ccw_device *cdev) 526 { 527 struct ccwgroup_device *gdev; 528 529 /* Ignore offlining errors, device is gone anyway. */ 530 ccw_device_set_offline(cdev); 531 /* If one of its devices is gone, the whole group is done for. */ 532 spin_lock_irq(cdev->ccwlock); 533 gdev = dev_get_drvdata(&cdev->dev); 534 if (!gdev) { 535 spin_unlock_irq(cdev->ccwlock); 536 return; 537 } 538 /* Get ccwgroup device reference for local processing. */ 539 get_device(&gdev->dev); 540 spin_unlock_irq(cdev->ccwlock); 541 /* Unregister group device. */ 542 ccwgroup_ungroup(gdev); 543 /* Release ccwgroup device reference for local processing. */ 544 put_device(&gdev->dev); 545 } 546 EXPORT_SYMBOL(ccwgroup_remove_ccwdev); 547 MODULE_LICENSE("GPL"); 548