blob: 48b71236192d8d36f54335e58523afcdad094a71 [file] [log] [blame]
Stefano Stabellini72e59c32017-07-05 13:08:39 -07001/*
2 * (c) 2017 Stefano Stabellini <stefano@aporeto.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14
15#include <linux/kthread.h>
16#include <linux/list.h>
17#include <linux/radix-tree.h>
18#include <linux/module.h>
19#include <linux/semaphore.h>
20#include <linux/wait.h>
21
22#include <xen/events.h>
23#include <xen/grant_table.h>
24#include <xen/xen.h>
25#include <xen/xenbus.h>
26#include <xen/interface/io/pvcalls.h>
27
Stefano Stabellini0a9c75c2017-07-06 10:59:17 -070028#define PVCALLS_VERSIONS "1"
29#define MAX_RING_ORDER XENBUS_MAX_RING_GRANT_ORDER
30
Stefano Stabellini9be07332017-07-05 13:08:48 -070031struct pvcalls_back_global {
32 struct list_head frontends;
33 struct semaphore frontends_lock;
34} pvcalls_back_global;
35
Stefano Stabellinid0e4d562017-07-06 10:59:29 -070036/*
37 * Per-frontend data structure. It contains pointers to the command
38 * ring, its event channel, a list of active sockets and a tree of
39 * passive sockets.
40 */
41struct pvcalls_fedata {
42 struct list_head list;
43 struct xenbus_device *dev;
44 struct xen_pvcalls_sring *sring;
45 struct xen_pvcalls_back_ring ring;
46 int irq;
47 struct list_head socket_mappings;
48 struct radix_tree_root socketpass_mappings;
49 struct semaphore socket_lock;
50};
51
52static irqreturn_t pvcalls_back_event(int irq, void *dev_id)
53{
54 return IRQ_HANDLED;
55}
56
Stefano Stabellini0a9c75c2017-07-06 10:59:17 -070057static int backend_connect(struct xenbus_device *dev)
58{
Stefano Stabellinid0e4d562017-07-06 10:59:29 -070059 int err, evtchn;
60 grant_ref_t ring_ref;
61 struct pvcalls_fedata *fedata = NULL;
62
63 fedata = kzalloc(sizeof(struct pvcalls_fedata), GFP_KERNEL);
64 if (!fedata)
65 return -ENOMEM;
66
67 fedata->irq = -1;
68 err = xenbus_scanf(XBT_NIL, dev->otherend, "port", "%u",
69 &evtchn);
70 if (err != 1) {
71 err = -EINVAL;
72 xenbus_dev_fatal(dev, err, "reading %s/event-channel",
73 dev->otherend);
74 goto error;
75 }
76
77 err = xenbus_scanf(XBT_NIL, dev->otherend, "ring-ref", "%u", &ring_ref);
78 if (err != 1) {
79 err = -EINVAL;
80 xenbus_dev_fatal(dev, err, "reading %s/ring-ref",
81 dev->otherend);
82 goto error;
83 }
84
85 err = bind_interdomain_evtchn_to_irq(dev->otherend_id, evtchn);
86 if (err < 0)
87 goto error;
88 fedata->irq = err;
89
90 err = request_threaded_irq(fedata->irq, NULL, pvcalls_back_event,
91 IRQF_ONESHOT, "pvcalls-back", dev);
92 if (err < 0)
93 goto error;
94
95 err = xenbus_map_ring_valloc(dev, &ring_ref, 1,
96 (void **)&fedata->sring);
97 if (err < 0)
98 goto error;
99
100 BACK_RING_INIT(&fedata->ring, fedata->sring, XEN_PAGE_SIZE * 1);
101 fedata->dev = dev;
102
103 INIT_LIST_HEAD(&fedata->socket_mappings);
104 INIT_RADIX_TREE(&fedata->socketpass_mappings, GFP_KERNEL);
105 sema_init(&fedata->socket_lock, 1);
106 dev_set_drvdata(&dev->dev, fedata);
107
108 down(&pvcalls_back_global.frontends_lock);
109 list_add_tail(&fedata->list, &pvcalls_back_global.frontends);
110 up(&pvcalls_back_global.frontends_lock);
111
Stefano Stabellini0a9c75c2017-07-06 10:59:17 -0700112 return 0;
Stefano Stabellinid0e4d562017-07-06 10:59:29 -0700113
114 error:
115 if (fedata->irq >= 0)
116 unbind_from_irqhandler(fedata->irq, dev);
117 if (fedata->sring != NULL)
118 xenbus_unmap_ring_vfree(dev, fedata->sring);
119 kfree(fedata);
120 return err;
Stefano Stabellini0a9c75c2017-07-06 10:59:17 -0700121}
122
123static int backend_disconnect(struct xenbus_device *dev)
124{
125 return 0;
126}
127
Stefano Stabellini72e59c32017-07-05 13:08:39 -0700128static int pvcalls_back_probe(struct xenbus_device *dev,
129 const struct xenbus_device_id *id)
130{
Stefano Stabellini0a9c75c2017-07-06 10:59:17 -0700131 int err, abort;
132 struct xenbus_transaction xbt;
133
134again:
135 abort = 1;
136
137 err = xenbus_transaction_start(&xbt);
138 if (err) {
139 pr_warn("%s cannot create xenstore transaction\n", __func__);
140 return err;
141 }
142
143 err = xenbus_printf(xbt, dev->nodename, "versions", "%s",
144 PVCALLS_VERSIONS);
145 if (err) {
146 pr_warn("%s write out 'versions' failed\n", __func__);
147 goto abort;
148 }
149
150 err = xenbus_printf(xbt, dev->nodename, "max-page-order", "%u",
151 MAX_RING_ORDER);
152 if (err) {
153 pr_warn("%s write out 'max-page-order' failed\n", __func__);
154 goto abort;
155 }
156
157 err = xenbus_printf(xbt, dev->nodename, "function-calls",
158 XENBUS_FUNCTIONS_CALLS);
159 if (err) {
160 pr_warn("%s write out 'function-calls' failed\n", __func__);
161 goto abort;
162 }
163
164 abort = 0;
165abort:
166 err = xenbus_transaction_end(xbt, abort);
167 if (err) {
168 if (err == -EAGAIN && !abort)
169 goto again;
170 pr_warn("%s cannot complete xenstore transaction\n", __func__);
171 return err;
172 }
173
174 if (abort)
175 return -EFAULT;
176
177 xenbus_switch_state(dev, XenbusStateInitWait);
178
Stefano Stabellini72e59c32017-07-05 13:08:39 -0700179 return 0;
180}
181
Stefano Stabellini0a9c75c2017-07-06 10:59:17 -0700182static void set_backend_state(struct xenbus_device *dev,
183 enum xenbus_state state)
184{
185 while (dev->state != state) {
186 switch (dev->state) {
187 case XenbusStateClosed:
188 switch (state) {
189 case XenbusStateInitWait:
190 case XenbusStateConnected:
191 xenbus_switch_state(dev, XenbusStateInitWait);
192 break;
193 case XenbusStateClosing:
194 xenbus_switch_state(dev, XenbusStateClosing);
195 break;
196 default:
197 __WARN();
198 }
199 break;
200 case XenbusStateInitWait:
201 case XenbusStateInitialised:
202 switch (state) {
203 case XenbusStateConnected:
204 backend_connect(dev);
205 xenbus_switch_state(dev, XenbusStateConnected);
206 break;
207 case XenbusStateClosing:
208 case XenbusStateClosed:
209 xenbus_switch_state(dev, XenbusStateClosing);
210 break;
211 default:
212 __WARN();
213 }
214 break;
215 case XenbusStateConnected:
216 switch (state) {
217 case XenbusStateInitWait:
218 case XenbusStateClosing:
219 case XenbusStateClosed:
220 down(&pvcalls_back_global.frontends_lock);
221 backend_disconnect(dev);
222 up(&pvcalls_back_global.frontends_lock);
223 xenbus_switch_state(dev, XenbusStateClosing);
224 break;
225 default:
226 __WARN();
227 }
228 break;
229 case XenbusStateClosing:
230 switch (state) {
231 case XenbusStateInitWait:
232 case XenbusStateConnected:
233 case XenbusStateClosed:
234 xenbus_switch_state(dev, XenbusStateClosed);
235 break;
236 default:
237 __WARN();
238 }
239 break;
240 default:
241 __WARN();
242 }
243 }
244}
245
Stefano Stabellini72e59c32017-07-05 13:08:39 -0700246static void pvcalls_back_changed(struct xenbus_device *dev,
247 enum xenbus_state frontend_state)
248{
Stefano Stabellini0a9c75c2017-07-06 10:59:17 -0700249 switch (frontend_state) {
250 case XenbusStateInitialising:
251 set_backend_state(dev, XenbusStateInitWait);
252 break;
253
254 case XenbusStateInitialised:
255 case XenbusStateConnected:
256 set_backend_state(dev, XenbusStateConnected);
257 break;
258
259 case XenbusStateClosing:
260 set_backend_state(dev, XenbusStateClosing);
261 break;
262
263 case XenbusStateClosed:
264 set_backend_state(dev, XenbusStateClosed);
265 if (xenbus_dev_is_online(dev))
266 break;
267 device_unregister(&dev->dev);
268 break;
269 case XenbusStateUnknown:
270 set_backend_state(dev, XenbusStateClosed);
271 device_unregister(&dev->dev);
272 break;
273
274 default:
275 xenbus_dev_fatal(dev, -EINVAL, "saw state %d at frontend",
276 frontend_state);
277 break;
278 }
Stefano Stabellini72e59c32017-07-05 13:08:39 -0700279}
280
281static int pvcalls_back_remove(struct xenbus_device *dev)
282{
283 return 0;
284}
285
286static int pvcalls_back_uevent(struct xenbus_device *xdev,
287 struct kobj_uevent_env *env)
288{
289 return 0;
290}
291
292static const struct xenbus_device_id pvcalls_back_ids[] = {
293 { "pvcalls" },
294 { "" }
295};
296
297static struct xenbus_driver pvcalls_back_driver = {
298 .ids = pvcalls_back_ids,
299 .probe = pvcalls_back_probe,
300 .remove = pvcalls_back_remove,
301 .uevent = pvcalls_back_uevent,
302 .otherend_changed = pvcalls_back_changed,
303};
Stefano Stabellini9be07332017-07-05 13:08:48 -0700304
305static int __init pvcalls_back_init(void)
306{
307 int ret;
308
309 if (!xen_domain())
310 return -ENODEV;
311
312 ret = xenbus_register_backend(&pvcalls_back_driver);
313 if (ret < 0)
314 return ret;
315
316 sema_init(&pvcalls_back_global.frontends_lock, 1);
317 INIT_LIST_HEAD(&pvcalls_back_global.frontends);
318 return 0;
319}
320module_init(pvcalls_back_init);