Merge branch 'r6040-next'
[linux/fpc-iii.git] / drivers / misc / mic / scif / scif_peer_bus.c
blob6ffa3bdbd45b2bd90b24f7fa30ab69de97873a42
1 /*
2 * Intel MIC Platform Software Stack (MPSS)
4 * Copyright(c) 2014 Intel Corporation.
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.
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.
15 * Intel SCIF driver.
17 #include "scif_main.h"
18 #include "../bus/scif_bus.h"
19 #include "scif_peer_bus.h"
21 static inline struct scif_peer_dev *
22 dev_to_scif_peer(struct device *dev)
24 return container_of(dev, struct scif_peer_dev, dev);
27 struct bus_type scif_peer_bus = {
28 .name = "scif_peer_bus",
31 static void scif_peer_release_dev(struct device *d)
33 struct scif_peer_dev *sdev = dev_to_scif_peer(d);
34 struct scif_dev *scifdev = &scif_dev[sdev->dnode];
36 scif_cleanup_scifdev(scifdev);
37 kfree(sdev);
40 static int scif_peer_initialize_device(struct scif_dev *scifdev)
42 struct scif_peer_dev *spdev;
43 int ret;
45 spdev = kzalloc(sizeof(*spdev), GFP_KERNEL);
46 if (!spdev) {
47 ret = -ENOMEM;
48 goto err;
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);
57 device_initialize(&spdev->dev);
58 get_device(&spdev->dev);
59 rcu_assign_pointer(scifdev->spdev, spdev);
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;
66 err:
67 dev_err(&scifdev->sdev->dev,
68 "dnode %d: initialize_device rc %d\n", scifdev->node, ret);
69 return ret;
72 static int scif_peer_add_device(struct scif_dev *scifdev)
74 struct scif_peer_dev *spdev = rcu_dereference(scifdev->spdev);
75 char pool_name[16];
76 int ret;
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;
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;
96 dev_dbg(&spdev->dev, "Added peer dnode %d\n", spdev->dnode);
97 return 0;
98 del_spdev:
99 device_del(&spdev->dev);
100 put_spdev:
101 RCU_INIT_POINTER(scifdev->spdev, NULL);
102 synchronize_rcu();
103 put_device(&spdev->dev);
105 mutex_lock(&scif_info.conflock);
106 scif_info.total--;
107 mutex_unlock(&scif_info.conflock);
108 return ret;
111 void scif_add_peer_device(struct work_struct *work)
113 struct scif_dev *scifdev = container_of(work, struct scif_dev,
114 peer_add_work);
116 scif_peer_add_device(scifdev);
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.
131 void scif_peer_register_device(struct scif_dev *scifdev)
133 int ret;
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);
140 exit:
141 mutex_unlock(&scifdev->lock);
144 int scif_peer_unregister_device(struct scif_dev *scifdev)
146 struct scif_peer_dev *spdev;
148 mutex_lock(&scifdev->lock);
149 /* Flush work to ensure device register is complete */
150 flush_work(&scifdev->peer_add_work);
153 * Continue holding scifdev->lock since theoretically unregister_device
154 * can be called simultaneously from multiple threads
156 spdev = rcu_dereference(scifdev->spdev);
157 if (!spdev) {
158 mutex_unlock(&scifdev->lock);
159 return -ENODEV;
162 RCU_INIT_POINTER(scifdev->spdev, NULL);
163 synchronize_rcu();
164 mutex_unlock(&scifdev->lock);
166 dev_dbg(&spdev->dev, "Removing peer dnode %d\n", spdev->dnode);
167 device_unregister(&spdev->dev);
169 mutex_lock(&scif_info.conflock);
170 scif_info.total--;
171 mutex_unlock(&scif_info.conflock);
172 return 0;
175 int scif_peer_bus_init(void)
177 return bus_register(&scif_peer_bus);
180 void scif_peer_bus_exit(void)
182 bus_unregister(&scif_peer_bus);