xref: /openbmc/linux/drivers/xen/pvcalls-front.c (revision 21968190991320c0d9b6d1280e7dfda420164db1)
1416efba0SStefano Stabellini /*
2416efba0SStefano Stabellini  * (c) 2017 Stefano Stabellini <stefano@aporeto.com>
3416efba0SStefano Stabellini  *
4416efba0SStefano Stabellini  * This program is free software; you can redistribute it and/or modify
5416efba0SStefano Stabellini  * it under the terms of the GNU General Public License as published by
6416efba0SStefano Stabellini  * the Free Software Foundation; either version 2 of the License, or
7416efba0SStefano Stabellini  * (at your option) any later version.
8416efba0SStefano Stabellini  *
9416efba0SStefano Stabellini  * This program is distributed in the hope that it will be useful,
10416efba0SStefano Stabellini  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11416efba0SStefano Stabellini  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12416efba0SStefano Stabellini  * GNU General Public License for more details.
13416efba0SStefano Stabellini  */
14416efba0SStefano Stabellini 
15416efba0SStefano Stabellini #include <linux/module.h>
16416efba0SStefano Stabellini 
17416efba0SStefano Stabellini #include <xen/events.h>
18416efba0SStefano Stabellini #include <xen/grant_table.h>
19416efba0SStefano Stabellini #include <xen/xen.h>
20416efba0SStefano Stabellini #include <xen/xenbus.h>
21416efba0SStefano Stabellini #include <xen/interface/io/pvcalls.h>
22416efba0SStefano Stabellini 
23aa7ba376SStefano Stabellini #define PVCALLS_INVALID_ID UINT_MAX
24aa7ba376SStefano Stabellini #define PVCALLS_RING_ORDER XENBUS_MAX_RING_GRANT_ORDER
25aa7ba376SStefano Stabellini #define PVCALLS_NR_RSP_PER_RING __CONST_RING_SIZE(xen_pvcalls, XEN_PAGE_SIZE)
26aa7ba376SStefano Stabellini 
27aa7ba376SStefano Stabellini struct pvcalls_bedata {
28aa7ba376SStefano Stabellini 	struct xen_pvcalls_front_ring ring;
29aa7ba376SStefano Stabellini 	grant_ref_t ref;
30aa7ba376SStefano Stabellini 	int irq;
31aa7ba376SStefano Stabellini 
32aa7ba376SStefano Stabellini 	struct list_head socket_mappings;
33aa7ba376SStefano Stabellini 	spinlock_t socket_lock;
34aa7ba376SStefano Stabellini 
35aa7ba376SStefano Stabellini 	wait_queue_head_t inflight_req;
36aa7ba376SStefano Stabellini 	struct xen_pvcalls_response rsp[PVCALLS_NR_RSP_PER_RING];
37aa7ba376SStefano Stabellini };
38aa7ba376SStefano Stabellini /* Only one front/back connection supported. */
39aa7ba376SStefano Stabellini static struct xenbus_device *pvcalls_front_dev;
40aa7ba376SStefano Stabellini static atomic_t pvcalls_refcount;
41aa7ba376SStefano Stabellini 
42aa7ba376SStefano Stabellini /* first increment refcount, then proceed */
43aa7ba376SStefano Stabellini #define pvcalls_enter() {               \
44aa7ba376SStefano Stabellini 	atomic_inc(&pvcalls_refcount);      \
45aa7ba376SStefano Stabellini }
46aa7ba376SStefano Stabellini 
47aa7ba376SStefano Stabellini /* first complete other operations, then decrement refcount */
48aa7ba376SStefano Stabellini #define pvcalls_exit() {                \
49aa7ba376SStefano Stabellini 	atomic_dec(&pvcalls_refcount);      \
50aa7ba376SStefano Stabellini }
51aa7ba376SStefano Stabellini 
52aa7ba376SStefano Stabellini struct sock_mapping {
53aa7ba376SStefano Stabellini 	bool active_socket;
54aa7ba376SStefano Stabellini 	struct list_head list;
55aa7ba376SStefano Stabellini 	struct socket *sock;
56aa7ba376SStefano Stabellini };
57aa7ba376SStefano Stabellini 
58aa7ba376SStefano Stabellini static irqreturn_t pvcalls_front_event_handler(int irq, void *dev_id)
59aa7ba376SStefano Stabellini {
60aa7ba376SStefano Stabellini 	return IRQ_HANDLED;
61aa7ba376SStefano Stabellini }
62aa7ba376SStefano Stabellini 
63aa7ba376SStefano Stabellini static void pvcalls_front_free_map(struct pvcalls_bedata *bedata,
64aa7ba376SStefano Stabellini 				   struct sock_mapping *map)
65aa7ba376SStefano Stabellini {
66aa7ba376SStefano Stabellini }
67aa7ba376SStefano Stabellini 
68416efba0SStefano Stabellini static const struct xenbus_device_id pvcalls_front_ids[] = {
69416efba0SStefano Stabellini 	{ "pvcalls" },
70416efba0SStefano Stabellini 	{ "" }
71416efba0SStefano Stabellini };
72416efba0SStefano Stabellini 
73416efba0SStefano Stabellini static int pvcalls_front_remove(struct xenbus_device *dev)
74416efba0SStefano Stabellini {
75aa7ba376SStefano Stabellini 	struct pvcalls_bedata *bedata;
76aa7ba376SStefano Stabellini 	struct sock_mapping *map = NULL, *n;
77aa7ba376SStefano Stabellini 
78aa7ba376SStefano Stabellini 	bedata = dev_get_drvdata(&pvcalls_front_dev->dev);
79aa7ba376SStefano Stabellini 	dev_set_drvdata(&dev->dev, NULL);
80aa7ba376SStefano Stabellini 	pvcalls_front_dev = NULL;
81aa7ba376SStefano Stabellini 	if (bedata->irq >= 0)
82aa7ba376SStefano Stabellini 		unbind_from_irqhandler(bedata->irq, dev);
83aa7ba376SStefano Stabellini 
84aa7ba376SStefano Stabellini 	smp_mb();
85aa7ba376SStefano Stabellini 	while (atomic_read(&pvcalls_refcount) > 0)
86aa7ba376SStefano Stabellini 		cpu_relax();
87aa7ba376SStefano Stabellini 	list_for_each_entry_safe(map, n, &bedata->socket_mappings, list) {
88aa7ba376SStefano Stabellini 		if (map->active_socket) {
89aa7ba376SStefano Stabellini 			/* No need to lock, refcount is 0 */
90aa7ba376SStefano Stabellini 			pvcalls_front_free_map(bedata, map);
91aa7ba376SStefano Stabellini 		} else {
92aa7ba376SStefano Stabellini 			list_del(&map->list);
93aa7ba376SStefano Stabellini 			kfree(map);
94aa7ba376SStefano Stabellini 		}
95aa7ba376SStefano Stabellini 	}
96aa7ba376SStefano Stabellini 	if (bedata->ref >= 0)
97aa7ba376SStefano Stabellini 		gnttab_end_foreign_access(bedata->ref, 0, 0);
98aa7ba376SStefano Stabellini 	kfree(bedata->ring.sring);
99aa7ba376SStefano Stabellini 	kfree(bedata);
100aa7ba376SStefano Stabellini 	xenbus_switch_state(dev, XenbusStateClosed);
101416efba0SStefano Stabellini 	return 0;
102416efba0SStefano Stabellini }
103416efba0SStefano Stabellini 
104416efba0SStefano Stabellini static int pvcalls_front_probe(struct xenbus_device *dev,
105416efba0SStefano Stabellini 			  const struct xenbus_device_id *id)
106416efba0SStefano Stabellini {
107*21968190SStefano Stabellini 	int ret = -ENOMEM, evtchn, i;
108*21968190SStefano Stabellini 	unsigned int max_page_order, function_calls, len;
109*21968190SStefano Stabellini 	char *versions;
110*21968190SStefano Stabellini 	grant_ref_t gref_head = 0;
111*21968190SStefano Stabellini 	struct xenbus_transaction xbt;
112*21968190SStefano Stabellini 	struct pvcalls_bedata *bedata = NULL;
113*21968190SStefano Stabellini 	struct xen_pvcalls_sring *sring;
114*21968190SStefano Stabellini 
115*21968190SStefano Stabellini 	if (pvcalls_front_dev != NULL) {
116*21968190SStefano Stabellini 		dev_err(&dev->dev, "only one PV Calls connection supported\n");
117*21968190SStefano Stabellini 		return -EINVAL;
118*21968190SStefano Stabellini 	}
119*21968190SStefano Stabellini 
120*21968190SStefano Stabellini 	versions = xenbus_read(XBT_NIL, dev->otherend, "versions", &len);
121*21968190SStefano Stabellini 	if (!len)
122*21968190SStefano Stabellini 		return -EINVAL;
123*21968190SStefano Stabellini 	if (strcmp(versions, "1")) {
124*21968190SStefano Stabellini 		kfree(versions);
125*21968190SStefano Stabellini 		return -EINVAL;
126*21968190SStefano Stabellini 	}
127*21968190SStefano Stabellini 	kfree(versions);
128*21968190SStefano Stabellini 	max_page_order = xenbus_read_unsigned(dev->otherend,
129*21968190SStefano Stabellini 					      "max-page-order", 0);
130*21968190SStefano Stabellini 	if (max_page_order < PVCALLS_RING_ORDER)
131*21968190SStefano Stabellini 		return -ENODEV;
132*21968190SStefano Stabellini 	function_calls = xenbus_read_unsigned(dev->otherend,
133*21968190SStefano Stabellini 					      "function-calls", 0);
134*21968190SStefano Stabellini 	/* See XENBUS_FUNCTIONS_CALLS in pvcalls.h */
135*21968190SStefano Stabellini 	if (function_calls != 1)
136*21968190SStefano Stabellini 		return -ENODEV;
137*21968190SStefano Stabellini 	pr_info("%s max-page-order is %u\n", __func__, max_page_order);
138*21968190SStefano Stabellini 
139*21968190SStefano Stabellini 	bedata = kzalloc(sizeof(struct pvcalls_bedata), GFP_KERNEL);
140*21968190SStefano Stabellini 	if (!bedata)
141*21968190SStefano Stabellini 		return -ENOMEM;
142*21968190SStefano Stabellini 
143*21968190SStefano Stabellini 	dev_set_drvdata(&dev->dev, bedata);
144*21968190SStefano Stabellini 	pvcalls_front_dev = dev;
145*21968190SStefano Stabellini 	init_waitqueue_head(&bedata->inflight_req);
146*21968190SStefano Stabellini 	INIT_LIST_HEAD(&bedata->socket_mappings);
147*21968190SStefano Stabellini 	spin_lock_init(&bedata->socket_lock);
148*21968190SStefano Stabellini 	bedata->irq = -1;
149*21968190SStefano Stabellini 	bedata->ref = -1;
150*21968190SStefano Stabellini 
151*21968190SStefano Stabellini 	for (i = 0; i < PVCALLS_NR_RSP_PER_RING; i++)
152*21968190SStefano Stabellini 		bedata->rsp[i].req_id = PVCALLS_INVALID_ID;
153*21968190SStefano Stabellini 
154*21968190SStefano Stabellini 	sring = (struct xen_pvcalls_sring *) __get_free_page(GFP_KERNEL |
155*21968190SStefano Stabellini 							     __GFP_ZERO);
156*21968190SStefano Stabellini 	if (!sring)
157*21968190SStefano Stabellini 		goto error;
158*21968190SStefano Stabellini 	SHARED_RING_INIT(sring);
159*21968190SStefano Stabellini 	FRONT_RING_INIT(&bedata->ring, sring, XEN_PAGE_SIZE);
160*21968190SStefano Stabellini 
161*21968190SStefano Stabellini 	ret = xenbus_alloc_evtchn(dev, &evtchn);
162*21968190SStefano Stabellini 	if (ret)
163*21968190SStefano Stabellini 		goto error;
164*21968190SStefano Stabellini 
165*21968190SStefano Stabellini 	bedata->irq = bind_evtchn_to_irqhandler(evtchn,
166*21968190SStefano Stabellini 						pvcalls_front_event_handler,
167*21968190SStefano Stabellini 						0, "pvcalls-frontend", dev);
168*21968190SStefano Stabellini 	if (bedata->irq < 0) {
169*21968190SStefano Stabellini 		ret = bedata->irq;
170*21968190SStefano Stabellini 		goto error;
171*21968190SStefano Stabellini 	}
172*21968190SStefano Stabellini 
173*21968190SStefano Stabellini 	ret = gnttab_alloc_grant_references(1, &gref_head);
174*21968190SStefano Stabellini 	if (ret < 0)
175*21968190SStefano Stabellini 		goto error;
176*21968190SStefano Stabellini 	bedata->ref = gnttab_claim_grant_reference(&gref_head);
177*21968190SStefano Stabellini 	if (bedata->ref < 0) {
178*21968190SStefano Stabellini 		ret = bedata->ref;
179*21968190SStefano Stabellini 		goto error;
180*21968190SStefano Stabellini 	}
181*21968190SStefano Stabellini 	gnttab_grant_foreign_access_ref(bedata->ref, dev->otherend_id,
182*21968190SStefano Stabellini 					virt_to_gfn((void *)sring), 0);
183*21968190SStefano Stabellini 
184*21968190SStefano Stabellini  again:
185*21968190SStefano Stabellini 	ret = xenbus_transaction_start(&xbt);
186*21968190SStefano Stabellini 	if (ret) {
187*21968190SStefano Stabellini 		xenbus_dev_fatal(dev, ret, "starting transaction");
188*21968190SStefano Stabellini 		goto error;
189*21968190SStefano Stabellini 	}
190*21968190SStefano Stabellini 	ret = xenbus_printf(xbt, dev->nodename, "version", "%u", 1);
191*21968190SStefano Stabellini 	if (ret)
192*21968190SStefano Stabellini 		goto error_xenbus;
193*21968190SStefano Stabellini 	ret = xenbus_printf(xbt, dev->nodename, "ring-ref", "%d", bedata->ref);
194*21968190SStefano Stabellini 	if (ret)
195*21968190SStefano Stabellini 		goto error_xenbus;
196*21968190SStefano Stabellini 	ret = xenbus_printf(xbt, dev->nodename, "port", "%u",
197*21968190SStefano Stabellini 			    evtchn);
198*21968190SStefano Stabellini 	if (ret)
199*21968190SStefano Stabellini 		goto error_xenbus;
200*21968190SStefano Stabellini 	ret = xenbus_transaction_end(xbt, 0);
201*21968190SStefano Stabellini 	if (ret) {
202*21968190SStefano Stabellini 		if (ret == -EAGAIN)
203*21968190SStefano Stabellini 			goto again;
204*21968190SStefano Stabellini 		xenbus_dev_fatal(dev, ret, "completing transaction");
205*21968190SStefano Stabellini 		goto error;
206*21968190SStefano Stabellini 	}
207*21968190SStefano Stabellini 	xenbus_switch_state(dev, XenbusStateInitialised);
208*21968190SStefano Stabellini 
209416efba0SStefano Stabellini 	return 0;
210*21968190SStefano Stabellini 
211*21968190SStefano Stabellini  error_xenbus:
212*21968190SStefano Stabellini 	xenbus_transaction_end(xbt, 1);
213*21968190SStefano Stabellini 	xenbus_dev_fatal(dev, ret, "writing xenstore");
214*21968190SStefano Stabellini  error:
215*21968190SStefano Stabellini 	pvcalls_front_remove(dev);
216*21968190SStefano Stabellini 	return ret;
217416efba0SStefano Stabellini }
218416efba0SStefano Stabellini 
219416efba0SStefano Stabellini static void pvcalls_front_changed(struct xenbus_device *dev,
220416efba0SStefano Stabellini 			    enum xenbus_state backend_state)
221416efba0SStefano Stabellini {
222*21968190SStefano Stabellini 	switch (backend_state) {
223*21968190SStefano Stabellini 	case XenbusStateReconfiguring:
224*21968190SStefano Stabellini 	case XenbusStateReconfigured:
225*21968190SStefano Stabellini 	case XenbusStateInitialising:
226*21968190SStefano Stabellini 	case XenbusStateInitialised:
227*21968190SStefano Stabellini 	case XenbusStateUnknown:
228*21968190SStefano Stabellini 		break;
229*21968190SStefano Stabellini 
230*21968190SStefano Stabellini 	case XenbusStateInitWait:
231*21968190SStefano Stabellini 		break;
232*21968190SStefano Stabellini 
233*21968190SStefano Stabellini 	case XenbusStateConnected:
234*21968190SStefano Stabellini 		xenbus_switch_state(dev, XenbusStateConnected);
235*21968190SStefano Stabellini 		break;
236*21968190SStefano Stabellini 
237*21968190SStefano Stabellini 	case XenbusStateClosed:
238*21968190SStefano Stabellini 		if (dev->state == XenbusStateClosed)
239*21968190SStefano Stabellini 			break;
240*21968190SStefano Stabellini 		/* Missed the backend's CLOSING state -- fallthrough */
241*21968190SStefano Stabellini 	case XenbusStateClosing:
242*21968190SStefano Stabellini 		xenbus_frontend_closed(dev);
243*21968190SStefano Stabellini 		break;
244*21968190SStefano Stabellini 	}
245416efba0SStefano Stabellini }
246416efba0SStefano Stabellini 
247416efba0SStefano Stabellini static struct xenbus_driver pvcalls_front_driver = {
248416efba0SStefano Stabellini 	.ids = pvcalls_front_ids,
249416efba0SStefano Stabellini 	.probe = pvcalls_front_probe,
250416efba0SStefano Stabellini 	.remove = pvcalls_front_remove,
251416efba0SStefano Stabellini 	.otherend_changed = pvcalls_front_changed,
252416efba0SStefano Stabellini };
253416efba0SStefano Stabellini 
254416efba0SStefano Stabellini static int __init pvcalls_frontend_init(void)
255416efba0SStefano Stabellini {
256416efba0SStefano Stabellini 	if (!xen_domain())
257416efba0SStefano Stabellini 		return -ENODEV;
258416efba0SStefano Stabellini 
259416efba0SStefano Stabellini 	pr_info("Initialising Xen pvcalls frontend driver\n");
260416efba0SStefano Stabellini 
261416efba0SStefano Stabellini 	return xenbus_register_frontend(&pvcalls_front_driver);
262416efba0SStefano Stabellini }
263416efba0SStefano Stabellini 
264416efba0SStefano Stabellini module_init(pvcalls_frontend_init);
265