misc: mic: Add support for kernel mode SCIF clients
authorAshutosh Dixit <ashutosh.dixit@intel.com>
Wed, 30 Sep 2015 01:11:15 +0000 (18:11 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 4 Oct 2015 11:46:06 +0000 (12:46 +0100)
Add support for registration/de-registration of kernel mode SCIF
clients. SCIF clients are probed with new and existing SCIF peer
devices. Similarly the client remove method is called when SCIF
peer devices are removed.

Changes to SCIF peer device framework necessitated by supporting
kernel mode SCIF clients are also included in this patch.

Reviewed-by: Nikhil Rao <nikhil.rao@intel.com>
Reviewed-by: Sudeep Dutt <sudeep.dutt@intel.com>
Signed-off-by: Ashutosh Dixit <ashutosh.dixit@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/misc/mic/scif/scif_api.c
drivers/misc/mic/scif/scif_main.c
drivers/misc/mic/scif/scif_main.h
drivers/misc/mic/scif/scif_nm.c
drivers/misc/mic/scif/scif_nodeqp.c
drivers/misc/mic/scif/scif_peer_bus.c
drivers/misc/mic/scif/scif_peer_bus.h
include/linux/scif.h

index bf2d70fcd05574172bc7bbf237bedd72647e7ccc..b47d56d5d93c2e20c75876ed5f9f7666209e8a16 100644 (file)
@@ -1430,3 +1430,46 @@ int scif_get_node_ids(u16 *nodes, int len, u16 *self)
        return online;
 }
 EXPORT_SYMBOL_GPL(scif_get_node_ids);
+
+static int scif_add_client_dev(struct device *dev, struct subsys_interface *si)
+{
+       struct scif_client *client =
+               container_of(si, struct scif_client, si);
+       struct scif_peer_dev *spdev =
+               container_of(dev, struct scif_peer_dev, dev);
+
+       if (client->probe)
+               client->probe(spdev);
+       return 0;
+}
+
+static void scif_remove_client_dev(struct device *dev,
+                                  struct subsys_interface *si)
+{
+       struct scif_client *client =
+               container_of(si, struct scif_client, si);
+       struct scif_peer_dev *spdev =
+               container_of(dev, struct scif_peer_dev, dev);
+
+       if (client->remove)
+               client->remove(spdev);
+}
+
+void scif_client_unregister(struct scif_client *client)
+{
+       subsys_interface_unregister(&client->si);
+}
+EXPORT_SYMBOL_GPL(scif_client_unregister);
+
+int scif_client_register(struct scif_client *client)
+{
+       struct subsys_interface *si = &client->si;
+
+       si->name = client->name;
+       si->subsys = &scif_peer_bus;
+       si->add_dev = scif_add_client_dev;
+       si->remove_dev = scif_remove_client_dev;
+
+       return subsys_interface_register(&client->si);
+}
+EXPORT_SYMBOL_GPL(scif_client_register);
index 6ce851f5c7e65fdb8a0d9f8cca82c2dc0a8de056..f90bd06900cb4632786803faa7ea7a09b4716917 100644 (file)
@@ -80,35 +80,6 @@ irqreturn_t scif_intr_handler(int irq, void *data)
        return IRQ_HANDLED;
 }
 
-static int scif_peer_probe(struct scif_peer_dev *spdev)
-{
-       struct scif_dev *scifdev = &scif_dev[spdev->dnode];
-
-       mutex_lock(&scif_info.conflock);
-       scif_info.total++;
-       scif_info.maxid = max_t(u32, spdev->dnode, scif_info.maxid);
-       mutex_unlock(&scif_info.conflock);
-       rcu_assign_pointer(scifdev->spdev, spdev);
-
-       /* In the future SCIF kernel client devices will be added here */
-       return 0;
-}
-
-static void scif_peer_remove(struct scif_peer_dev *spdev)
-{
-       struct scif_dev *scifdev = &scif_dev[spdev->dnode];
-
-       /* In the future SCIF kernel client devices will be removed here */
-       spdev = rcu_dereference(scifdev->spdev);
-       if (spdev)
-               RCU_INIT_POINTER(scifdev->spdev, NULL);
-       synchronize_rcu();
-
-       mutex_lock(&scif_info.conflock);
-       scif_info.total--;
-       mutex_unlock(&scif_info.conflock);
-}
-
 static void scif_qp_setup_handler(struct work_struct *work)
 {
        struct scif_dev *scifdev = container_of(work, struct scif_dev,
@@ -139,20 +110,13 @@ static void scif_qp_setup_handler(struct work_struct *work)
        }
 }
 
-static int scif_setup_scifdev(struct scif_hw_dev *sdev)
+static int scif_setup_scifdev(void)
 {
+       /* We support a maximum of 129 SCIF nodes including the mgmt node */
+#define MAX_SCIF_NODES 129
        int i;
-       u8 num_nodes;
+       u8 num_nodes = MAX_SCIF_NODES;
 
-       if (sdev->snode) {
-               struct mic_bootparam __iomem *bp = sdev->rdp;
-
-               num_nodes = ioread8(&bp->tot_nodes);
-       } else {
-               struct mic_bootparam *bp = sdev->dp;
-
-               num_nodes = bp->tot_nodes;
-       }
        scif_dev = kcalloc(num_nodes, sizeof(*scif_dev), GFP_KERNEL);
        if (!scif_dev)
                return -ENOMEM;
@@ -163,7 +127,7 @@ static int scif_setup_scifdev(struct scif_hw_dev *sdev)
                scifdev->exit = OP_IDLE;
                init_waitqueue_head(&scifdev->disconn_wq);
                mutex_init(&scifdev->lock);
-               INIT_WORK(&scifdev->init_msg_work, scif_qp_response_ack);
+               INIT_WORK(&scifdev->peer_add_work, scif_add_peer_device);
                INIT_DELAYED_WORK(&scifdev->p2p_dwork,
                                  scif_poll_qp_state);
                INIT_DELAYED_WORK(&scifdev->qp_dwork,
@@ -181,27 +145,21 @@ static void scif_destroy_scifdev(void)
 
 static int scif_probe(struct scif_hw_dev *sdev)
 {
-       struct scif_dev *scifdev;
+       struct scif_dev *scifdev = &scif_dev[sdev->dnode];
        int rc;
 
        dev_set_drvdata(&sdev->dev, sdev);
+       scifdev->sdev = sdev;
+
        if (1 == atomic_add_return(1, &g_loopb_cnt)) {
-               struct scif_dev *loopb_dev;
+               struct scif_dev *loopb_dev = &scif_dev[sdev->snode];
 
-               rc = scif_setup_scifdev(sdev);
-               if (rc)
-                       goto exit;
-               scifdev = &scif_dev[sdev->dnode];
-               scifdev->sdev = sdev;
-               loopb_dev = &scif_dev[sdev->snode];
                loopb_dev->sdev = sdev;
                rc = scif_setup_loopback_qp(loopb_dev);
                if (rc)
-                       goto free_sdev;
-       } else {
-               scifdev = &scif_dev[sdev->dnode];
-               scifdev->sdev = sdev;
+                       goto exit;
        }
+
        rc = scif_setup_intr_wq(scifdev);
        if (rc)
                goto destroy_loopb;
@@ -237,8 +195,6 @@ destroy_intr:
 destroy_loopb:
        if (atomic_dec_and_test(&g_loopb_cnt))
                scif_destroy_loopback_qp(&scif_dev[sdev->snode]);
-free_sdev:
-       scif_destroy_scifdev();
 exit:
        return rc;
 }
@@ -290,13 +246,6 @@ static void scif_remove(struct scif_hw_dev *sdev)
        scifdev->sdev = NULL;
 }
 
-static struct scif_peer_driver scif_peer_driver = {
-       .driver.name =  KBUILD_MODNAME,
-       .driver.owner = THIS_MODULE,
-       .probe = scif_peer_probe,
-       .remove = scif_peer_remove,
-};
-
 static struct scif_hw_dev_id id_table[] = {
        { MIC_SCIF_DEV, SCIF_DEV_ANY_ID },
        { 0 },
@@ -312,6 +261,8 @@ static struct scif_driver scif_driver = {
 
 static int _scif_init(void)
 {
+       int rc;
+
        spin_lock_init(&scif_info.eplock);
        spin_lock_init(&scif_info.nb_connect_lock);
        spin_lock_init(&scif_info.port_lock);
@@ -326,10 +277,15 @@ static int _scif_init(void)
        init_waitqueue_head(&scif_info.exitwq);
        scif_info.en_msg_log = 0;
        scif_info.p2p_enable = 1;
+       rc = scif_setup_scifdev();
+       if (rc)
+               goto error;
        INIT_WORK(&scif_info.misc_work, scif_misc_handler);
        INIT_WORK(&scif_info.conn_work, scif_conn_handler);
        idr_init(&scif_ports);
        return 0;
+error:
+       return rc;
 }
 
 static void _scif_exit(void)
@@ -347,12 +303,9 @@ static int __init scif_init(void)
        rc = scif_peer_bus_init();
        if (rc)
                goto exit;
-       rc = scif_peer_register_driver(&scif_peer_driver);
-       if (rc)
-               goto peer_bus_exit;
        rc = scif_register_driver(&scif_driver);
        if (rc)
-               goto unreg_scif_peer;
+               goto peer_bus_exit;
        rc = misc_register(mdev);
        if (rc)
                goto unreg_scif;
@@ -360,8 +313,6 @@ static int __init scif_init(void)
        return 0;
 unreg_scif:
        scif_unregister_driver(&scif_driver);
-unreg_scif_peer:
-       scif_peer_unregister_driver(&scif_peer_driver);
 peer_bus_exit:
        scif_peer_bus_exit();
 exit:
@@ -374,7 +325,6 @@ static void __exit scif_exit(void)
        scif_exit_debugfs();
        misc_deregister(&scif_info.mdev);
        scif_unregister_driver(&scif_driver);
-       scif_peer_unregister_driver(&scif_peer_driver);
        scif_peer_bus_exit();
        _scif_exit();
 }
index 87c13279a8f0a0ae590c7d7a719be308100a3951..b0795b05980380655bc93763a701cb994d6498f8 100644 (file)
@@ -140,7 +140,7 @@ struct scif_p2p_info {
  * @db: doorbell the peer will trigger to generate an interrupt on self
  * @rdb: Doorbell to trigger on the peer to generate an interrupt on the peer
  * @cookie: Cookie received while registering the interrupt handler
- * init_msg_work: work scheduled for SCIF_INIT message processing
+ * @peer_add_work: Work for handling device_add for peer devices
  * @p2p_dwork: Delayed work to enable polling for P2P state
  * @qp_dwork: Delayed work for enabling polling for remote QP information
  * @p2p_retry: Number of times to retry polling of P2P state
@@ -166,7 +166,7 @@ struct scif_dev {
        int db;
        int rdb;
        struct mic_irq *cookie;
-       struct work_struct init_msg_work;
+       struct work_struct peer_add_work;
        struct delayed_work p2p_dwork;
        struct delayed_work qp_dwork;
        int p2p_retry;
@@ -183,6 +183,7 @@ struct scif_dev {
 
 extern struct scif_info scif_info;
 extern struct idr scif_ports;
+extern struct bus_type scif_peer_bus;
 extern struct scif_dev *scif_dev;
 extern const struct file_operations scif_fops;
 extern const struct file_operations scif_anon_fops;
index 9b4c5382d6a777239240a28fa63c4f85b1590e59..f1b1b97b36366ab7a09ea3090286b69052c6b347 100644 (file)
@@ -147,14 +147,8 @@ void scif_cleanup_scifdev(struct scif_dev *dev)
 void scif_handle_remove_node(int node)
 {
        struct scif_dev *scifdev = &scif_dev[node];
-       struct scif_peer_dev *spdev;
-
-       rcu_read_lock();
-       spdev = rcu_dereference(scifdev->spdev);
-       rcu_read_unlock();
-       if (spdev)
-               scif_peer_unregister_device(spdev);
-       else
+
+       if (scif_peer_unregister_device(scifdev))
                scif_send_acks(scifdev);
 }
 
index 6dfdae3452d609fd07c82cce24f933771a093a70..5cdb9f08835030065aa52b652e975047b53996a2 100644 (file)
@@ -259,6 +259,11 @@ int scif_setup_qp_connect_response(struct scif_dev *scifdev,
                     &qp->remote_qp->local_write,
                     r_buf,
                     get_count_order(remote_size));
+       /*
+        * Because the node QP may already be processing an INIT message, set
+        * the read pointer so the cached read offset isn't lost
+        */
+       qp->remote_qp->local_read = qp->inbound_q.current_read_offset;
        /*
         * resetup the inbound_q now that we know where the
         * inbound_read really is.
@@ -529,27 +534,6 @@ static void scif_p2p_setup(void)
        }
 }
 
-void scif_qp_response_ack(struct work_struct *work)
-{
-       struct scif_dev *scifdev = container_of(work, struct scif_dev,
-                                               init_msg_work);
-       struct scif_peer_dev *spdev;
-
-       /* Drop the INIT message if it has already been received */
-       if (_scifdev_alive(scifdev))
-               return;
-
-       spdev = scif_peer_register_device(scifdev);
-       if (IS_ERR(spdev))
-               return;
-
-       if (scif_is_mgmt_node()) {
-               mutex_lock(&scif_info.conflock);
-               scif_p2p_setup();
-               mutex_unlock(&scif_info.conflock);
-       }
-}
-
 static char *message_types[] = {"BAD",
                                "INIT",
                                "EXIT",
@@ -682,13 +666,14 @@ scif_init(struct scif_dev *scifdev, struct scifmsg *msg)
         * address to complete initializing the inbound_q.
         */
        flush_delayed_work(&scifdev->qp_dwork);
-       /*
-        * Delegate the peer device registration to a workqueue, otherwise if
-        * SCIF client probe (called during peer device registration) calls
-        * scif_connect(..), it will block the message processing thread causing
-        * a deadlock.
-        */
-       schedule_work(&scifdev->init_msg_work);
+
+       scif_peer_register_device(scifdev);
+
+       if (scif_is_mgmt_node()) {
+               mutex_lock(&scif_info.conflock);
+               scif_p2p_setup();
+               mutex_unlock(&scif_info.conflock);
+       }
 }
 
 /**
@@ -838,13 +823,13 @@ void scif_poll_qp_state(struct work_struct *work)
                                      msecs_to_jiffies(SCIF_NODE_QP_TIMEOUT));
                return;
        }
-       scif_peer_register_device(peerdev);
        return;
 timeout:
        dev_err(&peerdev->sdev->dev,
                "%s %d remote node %d offline,  state = 0x%x\n",
                __func__, __LINE__, peerdev->node, qp->qp_state);
        qp->remote_qp->qp_state = SCIF_QP_OFFLINE;
+       scif_peer_unregister_device(peerdev);
        scif_cleanup_scifdev(peerdev);
 }
 
@@ -894,6 +879,9 @@ scif_node_add_ack(struct scif_dev *scifdev, struct scifmsg *msg)
                goto local_error;
        peerdev->rdb = msg->payload[2];
        qp->remote_qp->qp_state = SCIF_QP_ONLINE;
+
+       scif_peer_register_device(peerdev);
+
        schedule_delayed_work(&peerdev->p2p_dwork, 0);
        return;
 local_error:
@@ -1169,7 +1157,6 @@ int scif_setup_loopback_qp(struct scif_dev *scifdev)
        int err = 0;
        void *local_q;
        struct scif_qp *qp;
-       struct scif_peer_dev *spdev;
 
        err = scif_setup_intr_wq(scifdev);
        if (err)
@@ -1216,15 +1203,11 @@ int scif_setup_loopback_qp(struct scif_dev *scifdev)
                     &qp->local_write,
                     local_q, get_count_order(SCIF_NODE_QP_SIZE));
        scif_info.nodeid = scifdev->node;
-       spdev = scif_peer_register_device(scifdev);
-       if (IS_ERR(spdev)) {
-               err = PTR_ERR(spdev);
-               goto free_local_q;
-       }
+
+       scif_peer_register_device(scifdev);
+
        scif_info.loopb_dev = scifdev;
        return err;
-free_local_q:
-       kfree(local_q);
 free_qpairs:
        kfree(scifdev->qpairs);
 destroy_loopb_wq:
@@ -1243,13 +1226,7 @@ exit:
  */
 int scif_destroy_loopback_qp(struct scif_dev *scifdev)
 {
-       struct scif_peer_dev *spdev;
-
-       rcu_read_lock();
-       spdev = rcu_dereference(scifdev->spdev);
-       rcu_read_unlock();
-       if (spdev)
-               scif_peer_unregister_device(spdev);
+       scif_peer_unregister_device(scifdev);
        destroy_workqueue(scif_info.loopb_wq);
        scif_destroy_intr_wq(scifdev);
        kfree(scifdev->qpairs->outbound_q.rb_base);
index 589ae9ad2501c0b233686dccbd7d7ef005715320..547bf7bdd5c2d0065166bf0a5e9b2b6e8db78889 100644 (file)
@@ -24,93 +24,138 @@ dev_to_scif_peer(struct device *dev)
        return container_of(dev, struct scif_peer_dev, dev);
 }
 
-static inline struct scif_peer_driver *
-drv_to_scif_peer(struct device_driver *drv)
-{
-       return container_of(drv, struct scif_peer_driver, driver);
-}
+struct bus_type scif_peer_bus = {
+       .name  = "scif_peer_bus",
+};
 
-static int scif_peer_dev_match(struct device *dv, struct device_driver *dr)
+static void scif_peer_release_dev(struct device *d)
 {
-       return !strncmp(dev_name(dv), dr->name, 4);
+       struct scif_peer_dev *sdev = dev_to_scif_peer(d);
+       struct scif_dev *scifdev = &scif_dev[sdev->dnode];
+
+       scif_cleanup_scifdev(scifdev);
+       kfree(sdev);
 }
 
-static int scif_peer_dev_probe(struct device *d)
+static int scif_peer_initialize_device(struct scif_dev *scifdev)
 {
-       struct scif_peer_dev *dev = dev_to_scif_peer(d);
-       struct scif_peer_driver *drv = drv_to_scif_peer(dev->dev.driver);
+       struct scif_peer_dev *spdev;
+       int ret;
 
-       return drv->probe(dev);
-}
+       spdev = kzalloc(sizeof(*spdev), GFP_KERNEL);
+       if (!spdev) {
+               ret = -ENOMEM;
+               goto err;
+       }
 
-static int scif_peer_dev_remove(struct device *d)
-{
-       struct scif_peer_dev *dev = dev_to_scif_peer(d);
-       struct scif_peer_driver *drv = drv_to_scif_peer(dev->dev.driver);
+       spdev->dev.parent = scifdev->sdev->dev.parent;
+       spdev->dev.release = scif_peer_release_dev;
+       spdev->dnode = scifdev->node;
+       spdev->dev.bus = &scif_peer_bus;
+       dev_set_name(&spdev->dev, "scif_peer-dev%u", spdev->dnode);
+
+       device_initialize(&spdev->dev);
+       get_device(&spdev->dev);
+       rcu_assign_pointer(scifdev->spdev, spdev);
 
-       drv->remove(dev);
+       mutex_lock(&scif_info.conflock);
+       scif_info.total++;
+       scif_info.maxid = max_t(u32, spdev->dnode, scif_info.maxid);
+       mutex_unlock(&scif_info.conflock);
        return 0;
+err:
+       dev_err(&scifdev->sdev->dev,
+               "dnode %d: initialize_device rc %d\n", scifdev->node, ret);
+       return ret;
 }
 
-static struct bus_type scif_peer_bus = {
-       .name  = "scif_peer_bus",
-       .match = scif_peer_dev_match,
-       .probe = scif_peer_dev_probe,
-       .remove = scif_peer_dev_remove,
-};
-
-int scif_peer_register_driver(struct scif_peer_driver *driver)
+static int scif_peer_add_device(struct scif_dev *scifdev)
 {
-       driver->driver.bus = &scif_peer_bus;
-       return driver_register(&driver->driver);
+       struct scif_peer_dev *spdev = rcu_dereference(scifdev->spdev);
+       int ret;
+
+       ret = device_add(&spdev->dev);
+       put_device(&spdev->dev);
+       if (ret) {
+               dev_err(&scifdev->sdev->dev,
+                       "dnode %d: peer device_add failed\n", scifdev->node);
+               goto put_spdev;
+       }
+       dev_dbg(&spdev->dev, "Added peer dnode %d\n", spdev->dnode);
+       return 0;
+put_spdev:
+       RCU_INIT_POINTER(scifdev->spdev, NULL);
+       synchronize_rcu();
+       put_device(&spdev->dev);
+
+       mutex_lock(&scif_info.conflock);
+       scif_info.total--;
+       mutex_unlock(&scif_info.conflock);
+       return ret;
 }
 
-void scif_peer_unregister_driver(struct scif_peer_driver *driver)
+void scif_add_peer_device(struct work_struct *work)
 {
-       driver_unregister(&driver->driver);
+       struct scif_dev *scifdev = container_of(work, struct scif_dev,
+                                               peer_add_work);
+
+       scif_peer_add_device(scifdev);
 }
 
-static void scif_peer_release_dev(struct device *d)
+/*
+ * Peer device registration is split into a device_initialize and a device_add.
+ * The reason for doing this is as follows: First, peer device registration
+ * itself cannot be done in the message processing thread and must be delegated
+ * to another workqueue, otherwise if SCIF client probe, called during peer
+ * device registration, calls scif_connect(..), it will block the message
+ * processing thread causing a deadlock. Next, device_initialize is done in the
+ * "top-half" message processing thread and device_add in the "bottom-half"
+ * workqueue. If this is not done, SCIF_CNCT_REQ message processing executing
+ * concurrently with SCIF_INIT message processing is unable to get a reference
+ * on the peer device, thereby failing the connect request.
+ */
+void scif_peer_register_device(struct scif_dev *scifdev)
 {
-       struct scif_peer_dev *sdev = dev_to_scif_peer(d);
-       struct scif_dev *scifdev = &scif_dev[sdev->dnode];
+       int ret;
 
-       scif_cleanup_scifdev(scifdev);
-       kfree(sdev);
+       mutex_lock(&scifdev->lock);
+       ret = scif_peer_initialize_device(scifdev);
+       if (ret)
+               goto exit;
+       schedule_work(&scifdev->peer_add_work);
+exit:
+       mutex_unlock(&scifdev->lock);
 }
 
-struct scif_peer_dev *
-scif_peer_register_device(struct scif_dev *scifdev)
+int scif_peer_unregister_device(struct scif_dev *scifdev)
 {
-       int ret;
        struct scif_peer_dev *spdev;
 
-       spdev = kzalloc(sizeof(*spdev), GFP_KERNEL);
-       if (!spdev)
-               return ERR_PTR(-ENOMEM);
-
-       spdev->dev.parent = scifdev->sdev->dev.parent;
-       spdev->dev.release = scif_peer_release_dev;
-       spdev->dnode = scifdev->node;
-       spdev->dev.bus = &scif_peer_bus;
+       mutex_lock(&scifdev->lock);
+       /* Flush work to ensure device register is complete */
+       flush_work(&scifdev->peer_add_work);
 
-       dev_set_name(&spdev->dev, "scif_peer-dev%u", spdev->dnode);
        /*
-        * device_register() causes the bus infrastructure to look for a
-        * matching driver.
+        * Continue holding scifdev->lock since theoretically unregister_device
+        * can be called simultaneously from multiple threads
         */
-       ret = device_register(&spdev->dev);
-       if (ret)
-               goto free_spdev;
-       return spdev;
-free_spdev:
-       kfree(spdev);
-       return ERR_PTR(ret);
-}
-
-void scif_peer_unregister_device(struct scif_peer_dev *sdev)
-{
-       device_unregister(&sdev->dev);
+       spdev = rcu_dereference(scifdev->spdev);
+       if (!spdev) {
+               mutex_unlock(&scifdev->lock);
+               return -ENODEV;
+       }
+
+       RCU_INIT_POINTER(scifdev->spdev, NULL);
+       synchronize_rcu();
+       mutex_unlock(&scifdev->lock);
+
+       dev_dbg(&spdev->dev, "Removing peer dnode %d\n", spdev->dnode);
+       device_unregister(&spdev->dev);
+
+       mutex_lock(&scif_info.conflock);
+       scif_info.total--;
+       mutex_unlock(&scif_info.conflock);
+       return 0;
 }
 
 int scif_peer_bus_init(void)
index 33f0dbb3015279603a412f49f9bc86f20a3196bf..a3b8dd2edaa52d4bf1ace09f75b17bc7292b5329 100644 (file)
 
 #include <linux/device.h>
 #include <linux/mic_common.h>
-
-/*
- * Peer devices show up as PCIe devices for the mgmt node but not the cards.
- * The mgmt node discovers all the cards on the PCIe bus and informs the other
- * cards about their peers. Upon notification of a peer a node adds a peer
- * device to the peer bus to maintain symmetry in the way devices are
- * discovered across all nodes in the SCIF network.
- */
-/**
- * scif_peer_dev - representation of a peer SCIF device
- * @dev: underlying device
- * @dnode - The destination node which this device will communicate with.
- */
-struct scif_peer_dev {
-       struct device dev;
-       u8 dnode;
-};
-
-/**
- * scif_peer_driver - operations for a scif_peer I/O driver
- * @driver: underlying device driver (populate name and owner).
- * @id_table: the ids serviced by this driver.
- * @probe: the function to call when a device is found.  Returns 0 or -errno.
- * @remove: the function to call when a device is removed.
- */
-struct scif_peer_driver {
-       struct device_driver driver;
-       const struct scif_peer_dev_id *id_table;
-
-       int (*probe)(struct scif_peer_dev *dev);
-       void (*remove)(struct scif_peer_dev *dev);
-};
+#include <linux/scif.h>
 
 struct scif_dev;
 
-int scif_peer_register_driver(struct scif_peer_driver *driver);
-void scif_peer_unregister_driver(struct scif_peer_driver *driver);
-
-struct scif_peer_dev *scif_peer_register_device(struct scif_dev *sdev);
-void scif_peer_unregister_device(struct scif_peer_dev *sdev);
-
+void scif_add_peer_device(struct work_struct *work);
+void scif_peer_register_device(struct scif_dev *sdev);
+int scif_peer_unregister_device(struct scif_dev *scifdev);
 int scif_peer_bus_init(void);
 void scif_peer_bus_exit(void);
 #endif /* _SCIF_PEER_BUS_H */
index b1923ad2ddd3899483741f3806ede0ff8245baf9..fd62c051b1669ba5d6947caa1fa0efc3f221dc99 100644 (file)
@@ -55,6 +55,7 @@
 
 #include <linux/types.h>
 #include <linux/poll.h>
+#include <linux/device.h>
 #include <linux/scif_ioctl.h>
 
 #define SCIF_ACCEPT_SYNC       1
@@ -105,6 +106,37 @@ struct scif_pollepd {
        short revents;
 };
 
+/**
+ * scif_peer_dev - representation of a peer SCIF device
+ *
+ * Peer devices show up as PCIe devices for the mgmt node but not the cards.
+ * The mgmt node discovers all the cards on the PCIe bus and informs the other
+ * cards about their peers. Upon notification of a peer a node adds a peer
+ * device to the peer bus to maintain symmetry in the way devices are
+ * discovered across all nodes in the SCIF network.
+ *
+ * @dev: underlying device
+ * @dnode - The destination node which this device will communicate with.
+ */
+struct scif_peer_dev {
+       struct device dev;
+       u8 dnode;
+};
+
+/**
+ * scif_client - representation of a SCIF client
+ * @name: client name
+ * @probe - client method called when a peer device is registered
+ * @remove - client method called when a peer device is unregistered
+ * @si - subsys_interface used internally for implementing SCIF clients
+ */
+struct scif_client {
+       const char *name;
+       void (*probe)(struct scif_peer_dev *spdev);
+       void (*remove)(struct scif_peer_dev *spdev);
+       struct subsys_interface si;
+};
+
 #define SCIF_OPEN_FAILED ((scif_epd_t)-1)
 #define SCIF_REGISTER_FAILED ((off_t)-1)
 #define SCIF_MMAP_FAILED ((void *)-1)
@@ -1064,4 +1096,30 @@ int scif_get_node_ids(u16 *nodes, int len, u16 *self);
  */
 int scif_poll(struct scif_pollepd *epds, unsigned int nepds, long timeout);
 
+/**
+ * scif_client_register() - Register a SCIF client
+ * @client:    client to be registered
+ *
+ * scif_client_register() registers a SCIF client. The probe() method
+ * of the client is called when SCIF peer devices come online and the
+ * remove() method is called when the peer devices disappear.
+ *
+ * Return:
+ * Upon successful completion, scif_client_register() returns a non-negative
+ * value. Otherwise the return value is the same as subsys_interface_register()
+ * in the kernel.
+ */
+int scif_client_register(struct scif_client *client);
+
+/**
+ * scif_client_unregister() - Unregister a SCIF client
+ * @client:    client to be unregistered
+ *
+ * scif_client_unregister() unregisters a SCIF client.
+ *
+ * Return:
+ * None
+ */
+void scif_client_unregister(struct scif_client *client);
+
 #endif /* __SCIF_H__ */