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