linux/drivers/misc/mic/scif/scif_peer_bus.c
<<
>>
Prefs
   1/*
   2 * Intel MIC Platform Software Stack (MPSS)
   3 *
   4 * Copyright(c) 2014 Intel Corporation.
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License, version 2, as
   8 * published by the Free Software Foundation.
   9 *
  10 * This program is distributed in the hope that it will be useful, but
  11 * WITHOUT ANY WARRANTY; without even the implied warranty of
  12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13 * General Public License for more details.
  14 *
  15 * Intel SCIF driver.
  16 */
  17#include "scif_main.h"
  18#include "../bus/scif_bus.h"
  19#include "scif_peer_bus.h"
  20
  21static inline struct scif_peer_dev *
  22dev_to_scif_peer(struct device *dev)
  23{
  24        return container_of(dev, struct scif_peer_dev, dev);
  25}
  26
  27struct bus_type scif_peer_bus = {
  28        .name  = "scif_peer_bus",
  29};
  30
  31static void scif_peer_release_dev(struct device *d)
  32{
  33        struct scif_peer_dev *sdev = dev_to_scif_peer(d);
  34        struct scif_dev *scifdev = &scif_dev[sdev->dnode];
  35
  36        scif_cleanup_scifdev(scifdev);
  37        kfree(sdev);
  38}
  39
  40static int scif_peer_initialize_device(struct scif_dev *scifdev)
  41{
  42        struct scif_peer_dev *spdev;
  43        int ret;
  44
  45        spdev = kzalloc(sizeof(*spdev), GFP_KERNEL);
  46        if (!spdev) {
  47                ret = -ENOMEM;
  48                goto err;
  49        }
  50
  51        spdev->dev.parent = scifdev->sdev->dev.parent;
  52        spdev->dev.release = scif_peer_release_dev;
  53        spdev->dnode = scifdev->node;
  54        spdev->dev.bus = &scif_peer_bus;
  55        dev_set_name(&spdev->dev, "scif_peer-dev%u", spdev->dnode);
  56
  57        device_initialize(&spdev->dev);
  58        get_device(&spdev->dev);
  59        rcu_assign_pointer(scifdev->spdev, spdev);
  60
  61        mutex_lock(&scif_info.conflock);
  62        scif_info.total++;
  63        scif_info.maxid = max_t(u32, spdev->dnode, scif_info.maxid);
  64        mutex_unlock(&scif_info.conflock);
  65        return 0;
  66err:
  67        dev_err(&scifdev->sdev->dev,
  68                "dnode %d: initialize_device rc %d\n", scifdev->node, ret);
  69        return ret;
  70}
  71
  72static int scif_peer_add_device(struct scif_dev *scifdev)
  73{
  74        struct scif_peer_dev *spdev = rcu_dereference(scifdev->spdev);
  75        char pool_name[16];
  76        int ret;
  77
  78        ret = device_add(&spdev->dev);
  79        put_device(&spdev->dev);
  80        if (ret) {
  81                dev_err(&scifdev->sdev->dev,
  82                        "dnode %d: peer device_add failed\n", scifdev->node);
  83                goto put_spdev;
  84        }
  85
  86        scnprintf(pool_name, sizeof(pool_name), "scif-%d", spdev->dnode);
  87        scifdev->signal_pool = dmam_pool_create(pool_name, &scifdev->sdev->dev,
  88                                                sizeof(struct scif_status), 1,
  89                                                0);
  90        if (!scifdev->signal_pool) {
  91                dev_err(&scifdev->sdev->dev,
  92                        "dnode %d: dmam_pool_create failed\n", scifdev->node);
  93                ret = -ENOMEM;
  94                goto del_spdev;
  95        }
  96        dev_dbg(&spdev->dev, "Added peer dnode %d\n", spdev->dnode);
  97        return 0;
  98del_spdev:
  99        device_del(&spdev->dev);
 100put_spdev:
 101        RCU_INIT_POINTER(scifdev->spdev, NULL);
 102        synchronize_rcu();
 103        put_device(&spdev->dev);
 104
 105        mutex_lock(&scif_info.conflock);
 106        scif_info.total--;
 107        mutex_unlock(&scif_info.conflock);
 108        return ret;
 109}
 110
 111void scif_add_peer_device(struct work_struct *work)
 112{
 113        struct scif_dev *scifdev = container_of(work, struct scif_dev,
 114                                                peer_add_work);
 115
 116        scif_peer_add_device(scifdev);
 117}
 118
 119/*
 120 * Peer device registration is split into a device_initialize and a device_add.
 121 * The reason for doing this is as follows: First, peer device registration
 122 * itself cannot be done in the message processing thread and must be delegated
 123 * to another workqueue, otherwise if SCIF client probe, called during peer
 124 * device registration, calls scif_connect(..), it will block the message
 125 * processing thread causing a deadlock. Next, device_initialize is done in the
 126 * "top-half" message processing thread and device_add in the "bottom-half"
 127 * workqueue. If this is not done, SCIF_CNCT_REQ message processing executing
 128 * concurrently with SCIF_INIT message processing is unable to get a reference
 129 * on the peer device, thereby failing the connect request.
 130 */
 131void scif_peer_register_device(struct scif_dev *scifdev)
 132{
 133        int ret;
 134
 135        mutex_lock(&scifdev->lock);
 136        ret = scif_peer_initialize_device(scifdev);
 137        if (ret)
 138                goto exit;
 139        schedule_work(&scifdev->peer_add_work);
 140exit:
 141        mutex_unlock(&scifdev->lock);
 142}
 143
 144int scif_peer_unregister_device(struct scif_dev *scifdev)
 145{
 146        struct scif_peer_dev *spdev;
 147
 148        mutex_lock(&scifdev->lock);
 149        /* Flush work to ensure device register is complete */
 150        flush_work(&scifdev->peer_add_work);
 151
 152        /*
 153         * Continue holding scifdev->lock since theoretically unregister_device
 154         * can be called simultaneously from multiple threads
 155         */
 156        spdev = rcu_dereference(scifdev->spdev);
 157        if (!spdev) {
 158                mutex_unlock(&scifdev->lock);
 159                return -ENODEV;
 160        }
 161
 162        RCU_INIT_POINTER(scifdev->spdev, NULL);
 163        synchronize_rcu();
 164        mutex_unlock(&scifdev->lock);
 165
 166        dev_dbg(&spdev->dev, "Removing peer dnode %d\n", spdev->dnode);
 167        device_unregister(&spdev->dev);
 168
 169        mutex_lock(&scif_info.conflock);
 170        scif_info.total--;
 171        mutex_unlock(&scif_info.conflock);
 172        return 0;
 173}
 174
 175int scif_peer_bus_init(void)
 176{
 177        return bus_register(&scif_peer_bus);
 178}
 179
 180void scif_peer_bus_exit(void)
 181{
 182        bus_unregister(&scif_peer_bus);
 183}
 184