123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183 |
- /*
- * Intel MIC Platform Software Stack (MPSS)
- *
- * Copyright(c) 2014 Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License, version 2, as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * Intel SCIF driver.
- */
- #include "scif_main.h"
- #include "../bus/scif_bus.h"
- #include "scif_peer_bus.h"
- static inline struct scif_peer_dev *
- dev_to_scif_peer(struct device *dev)
- {
- return container_of(dev, struct scif_peer_dev, dev);
- }
- struct bus_type scif_peer_bus = {
- .name = "scif_peer_bus",
- };
- static void scif_peer_release_dev(struct device *d)
- {
- 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_initialize_device(struct scif_dev *scifdev)
- {
- struct scif_peer_dev *spdev;
- int ret;
- spdev = kzalloc(sizeof(*spdev), GFP_KERNEL);
- if (!spdev) {
- ret = -ENOMEM;
- goto err;
- }
- 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);
- 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 int scif_peer_add_device(struct scif_dev *scifdev)
- {
- struct scif_peer_dev *spdev = rcu_dereference(scifdev->spdev);
- char pool_name[16];
- 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;
- }
- scnprintf(pool_name, sizeof(pool_name), "scif-%d", spdev->dnode);
- scifdev->signal_pool = dmam_pool_create(pool_name, &scifdev->sdev->dev,
- sizeof(struct scif_status), 1,
- 0);
- if (!scifdev->signal_pool) {
- dev_err(&scifdev->sdev->dev,
- "dnode %d: dmam_pool_create failed\n", scifdev->node);
- ret = -ENOMEM;
- goto del_spdev;
- }
- dev_dbg(&spdev->dev, "Added peer dnode %d\n", spdev->dnode);
- return 0;
- del_spdev:
- device_del(&spdev->dev);
- 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_add_peer_device(struct work_struct *work)
- {
- struct scif_dev *scifdev = container_of(work, struct scif_dev,
- peer_add_work);
- scif_peer_add_device(scifdev);
- }
- /*
- * 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)
- {
- int ret;
- 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);
- }
- int scif_peer_unregister_device(struct scif_dev *scifdev)
- {
- struct scif_peer_dev *spdev;
- mutex_lock(&scifdev->lock);
- /* Flush work to ensure device register is complete */
- flush_work(&scifdev->peer_add_work);
- /*
- * Continue holding scifdev->lock since theoretically unregister_device
- * can be called simultaneously from multiple threads
- */
- 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)
- {
- return bus_register(&scif_peer_bus);
- }
- void scif_peer_bus_exit(void)
- {
- bus_unregister(&scif_peer_bus);
- }
|