• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
13 static inline struct scif_peer_dev *
dev_to_scif_peer(struct device * dev)14 dev_to_scif_peer(struct device *dev)
15 {
16 	return container_of(dev, struct scif_peer_dev, dev);
17 }
18 
19 struct bus_type scif_peer_bus = {
20 	.name  = "scif_peer_bus",
21 };
22 
scif_peer_release_dev(struct device * d)23 static 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 
scif_peer_initialize_device(struct scif_dev * scifdev)32 static 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;
58 err:
59 	dev_err(&scifdev->sdev->dev,
60 		"dnode %d: initialize_device rc %d\n", scifdev->node, ret);
61 	return ret;
62 }
63 
scif_peer_add_device(struct scif_dev * scifdev)64 static 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;
90 del_spdev:
91 	device_del(&spdev->dev);
92 put_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 
scif_add_peer_device(struct work_struct * work)103 void 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  */
scif_peer_register_device(struct scif_dev * scifdev)123 void 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);
132 exit:
133 	mutex_unlock(&scifdev->lock);
134 }
135 
scif_peer_unregister_device(struct scif_dev * scifdev)136 int 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 
scif_peer_bus_init(void)167 int scif_peer_bus_init(void)
168 {
169 	return bus_register(&scif_peer_bus);
170 }
171 
scif_peer_bus_exit(void)172 void scif_peer_bus_exit(void)
173 {
174 	bus_unregister(&scif_peer_bus);
175 }
176