linux/drivers/s390/cio/ccwgroup.c
<<
>>
Prefs
   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
  34static struct bus_type ccwgroup_bus_type;
  35
  36static 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 */
  56int 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;
  71out:
  72        atomic_set(&gdev->onoff, 0);
  73        return ret;
  74}
  75EXPORT_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 */
  85int 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;
 100out:
 101        atomic_set(&gdev->onoff, 0);
 102        return ret;
 103}
 104EXPORT_SYMBOL(ccwgroup_set_offline);
 105
 106static 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;
 130out:
 131        device_unlock(dev);
 132        return (ret == 0) ? count : ret;
 133}
 134
 135static 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 */
 151static 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
 161static 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;
 180out:
 181        if (rc) {
 182                /* Release onoff "lock" when ungrouping failed. */
 183                atomic_set(&gdev->onoff, 0);
 184                return rc;
 185        }
 186        return count;
 187}
 188static DEVICE_ATTR(ungroup, 0200, NULL, ccwgroup_ungroup_store);
 189static DEVICE_ATTR(online, 0644, ccwgroup_online_show, ccwgroup_online_store);
 190
 191static struct attribute *ccwgroup_dev_attrs[] = {
 192        &dev_attr_online.attr,
 193        &dev_attr_ungroup.attr,
 194        NULL,
 195};
 196ATTRIBUTE_GROUPS(ccwgroup_dev);
 197
 198static 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
 207static 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
 228static 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
 261static 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 */
 307int 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;
 392error:
 393        mutex_unlock(&gdev->reg_mutex);
 394        put_device(&gdev->dev);
 395        return rc;
 396}
 397EXPORT_SYMBOL(ccwgroup_create_dev);
 398
 399static 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
 412static struct notifier_block ccwgroup_nb = {
 413        .notifier_call = ccwgroup_notifier
 414};
 415
 416static 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
 431static void __exit cleanup_ccwgroup(void)
 432{
 433        bus_unregister_notifier(&ccwgroup_bus_type, &ccwgroup_nb);
 434        bus_unregister(&ccwgroup_bus_type);
 435}
 436
 437module_init(init_ccwgroup);
 438module_exit(cleanup_ccwgroup);
 439
 440/************************** driver stuff ******************************/
 441
 442static int 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 (!dev->driver)
 448                return 0;
 449        if (gdrv->remove)
 450                gdrv->remove(gdev);
 451
 452        return 0;
 453}
 454
 455static void ccwgroup_shutdown(struct device *dev)
 456{
 457        struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
 458        struct ccwgroup_driver *gdrv = to_ccwgroupdrv(dev->driver);
 459
 460        if (!dev->driver)
 461                return;
 462        if (gdrv->shutdown)
 463                gdrv->shutdown(gdev);
 464}
 465
 466static struct bus_type ccwgroup_bus_type = {
 467        .name   = "ccwgroup",
 468        .dev_groups = ccwgroup_dev_groups,
 469        .remove = ccwgroup_remove,
 470        .shutdown = ccwgroup_shutdown,
 471};
 472
 473bool dev_is_ccwgroup(struct device *dev)
 474{
 475        return dev->bus == &ccwgroup_bus_type;
 476}
 477EXPORT_SYMBOL(dev_is_ccwgroup);
 478
 479/**
 480 * ccwgroup_driver_register() - register a ccw group driver
 481 * @cdriver: driver to be registered
 482 *
 483 * This function is mainly a wrapper around driver_register().
 484 */
 485int ccwgroup_driver_register(struct ccwgroup_driver *cdriver)
 486{
 487        /* register our new driver with the core */
 488        cdriver->driver.bus = &ccwgroup_bus_type;
 489
 490        return driver_register(&cdriver->driver);
 491}
 492EXPORT_SYMBOL(ccwgroup_driver_register);
 493
 494/**
 495 * ccwgroup_driver_unregister() - deregister a ccw group driver
 496 * @cdriver: driver to be deregistered
 497 *
 498 * This function is mainly a wrapper around driver_unregister().
 499 */
 500void ccwgroup_driver_unregister(struct ccwgroup_driver *cdriver)
 501{
 502        driver_unregister(&cdriver->driver);
 503}
 504EXPORT_SYMBOL(ccwgroup_driver_unregister);
 505
 506/**
 507 * get_ccwgroupdev_by_busid() - obtain device from a bus id
 508 * @gdrv: driver the device is owned by
 509 * @bus_id: bus id of the device to be searched
 510 *
 511 * This function searches all devices owned by @gdrv for a device with a bus
 512 * id matching @bus_id.
 513 * Returns:
 514 *  If a match is found, its reference count of the found device is increased
 515 *  and it is returned; else %NULL is returned.
 516 */
 517struct ccwgroup_device *get_ccwgroupdev_by_busid(struct ccwgroup_driver *gdrv,
 518                                                 char *bus_id)
 519{
 520        struct device *dev;
 521
 522        dev = driver_find_device_by_name(&gdrv->driver, bus_id);
 523
 524        return dev ? to_ccwgroupdev(dev) : NULL;
 525}
 526EXPORT_SYMBOL_GPL(get_ccwgroupdev_by_busid);
 527
 528/**
 529 * ccwgroup_probe_ccwdev() - probe function for slave devices
 530 * @cdev: ccw device to be probed
 531 *
 532 * This is a dummy probe function for ccw devices that are slave devices in
 533 * a ccw group device.
 534 * Returns:
 535 *  always %0
 536 */
 537int ccwgroup_probe_ccwdev(struct ccw_device *cdev)
 538{
 539        return 0;
 540}
 541EXPORT_SYMBOL(ccwgroup_probe_ccwdev);
 542
 543/**
 544 * ccwgroup_remove_ccwdev() - remove function for slave devices
 545 * @cdev: ccw device to be removed
 546 *
 547 * This is a remove function for ccw devices that are slave devices in a ccw
 548 * group device. It sets the ccw device offline and also deregisters the
 549 * embedding ccw group device.
 550 */
 551void ccwgroup_remove_ccwdev(struct ccw_device *cdev)
 552{
 553        struct ccwgroup_device *gdev;
 554
 555        /* Ignore offlining errors, device is gone anyway. */
 556        ccw_device_set_offline(cdev);
 557        /* If one of its devices is gone, the whole group is done for. */
 558        spin_lock_irq(cdev->ccwlock);
 559        gdev = dev_get_drvdata(&cdev->dev);
 560        if (!gdev) {
 561                spin_unlock_irq(cdev->ccwlock);
 562                return;
 563        }
 564        /* Get ccwgroup device reference for local processing. */
 565        get_device(&gdev->dev);
 566        spin_unlock_irq(cdev->ccwlock);
 567        /* Unregister group device. */
 568        ccwgroup_ungroup(gdev);
 569        /* Release ccwgroup device reference for local processing. */
 570        put_device(&gdev->dev);
 571}
 572EXPORT_SYMBOL(ccwgroup_remove_ccwdev);
 573MODULE_LICENSE("GPL");
 574