xref: /openbmc/linux/drivers/xen/pvcalls-front.c (revision 2195046bfd69e487d9a76dc47840f15c8412840c)
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>
16*2195046bSStefano Stabellini #include <linux/net.h>
17*2195046bSStefano Stabellini #include <linux/socket.h>
18*2195046bSStefano Stabellini 
19*2195046bSStefano Stabellini #include <net/sock.h>
20416efba0SStefano Stabellini 
21416efba0SStefano Stabellini #include <xen/events.h>
22416efba0SStefano Stabellini #include <xen/grant_table.h>
23416efba0SStefano Stabellini #include <xen/xen.h>
24416efba0SStefano Stabellini #include <xen/xenbus.h>
25416efba0SStefano Stabellini #include <xen/interface/io/pvcalls.h>
26416efba0SStefano Stabellini 
27*2195046bSStefano Stabellini #include "pvcalls-front.h"
28*2195046bSStefano Stabellini 
29aa7ba376SStefano Stabellini #define PVCALLS_INVALID_ID UINT_MAX
30aa7ba376SStefano Stabellini #define PVCALLS_RING_ORDER XENBUS_MAX_RING_GRANT_ORDER
31aa7ba376SStefano Stabellini #define PVCALLS_NR_RSP_PER_RING __CONST_RING_SIZE(xen_pvcalls, XEN_PAGE_SIZE)
32aa7ba376SStefano Stabellini 
33aa7ba376SStefano Stabellini struct pvcalls_bedata {
34aa7ba376SStefano Stabellini 	struct xen_pvcalls_front_ring ring;
35aa7ba376SStefano Stabellini 	grant_ref_t ref;
36aa7ba376SStefano Stabellini 	int irq;
37aa7ba376SStefano Stabellini 
38aa7ba376SStefano Stabellini 	struct list_head socket_mappings;
39aa7ba376SStefano Stabellini 	spinlock_t socket_lock;
40aa7ba376SStefano Stabellini 
41aa7ba376SStefano Stabellini 	wait_queue_head_t inflight_req;
42aa7ba376SStefano Stabellini 	struct xen_pvcalls_response rsp[PVCALLS_NR_RSP_PER_RING];
43aa7ba376SStefano Stabellini };
44aa7ba376SStefano Stabellini /* Only one front/back connection supported. */
45aa7ba376SStefano Stabellini static struct xenbus_device *pvcalls_front_dev;
46aa7ba376SStefano Stabellini static atomic_t pvcalls_refcount;
47aa7ba376SStefano Stabellini 
48aa7ba376SStefano Stabellini /* first increment refcount, then proceed */
49aa7ba376SStefano Stabellini #define pvcalls_enter() {               \
50aa7ba376SStefano Stabellini 	atomic_inc(&pvcalls_refcount);      \
51aa7ba376SStefano Stabellini }
52aa7ba376SStefano Stabellini 
53aa7ba376SStefano Stabellini /* first complete other operations, then decrement refcount */
54aa7ba376SStefano Stabellini #define pvcalls_exit() {                \
55aa7ba376SStefano Stabellini 	atomic_dec(&pvcalls_refcount);      \
56aa7ba376SStefano Stabellini }
57aa7ba376SStefano Stabellini 
58aa7ba376SStefano Stabellini struct sock_mapping {
59aa7ba376SStefano Stabellini 	bool active_socket;
60aa7ba376SStefano Stabellini 	struct list_head list;
61aa7ba376SStefano Stabellini 	struct socket *sock;
62aa7ba376SStefano Stabellini };
63aa7ba376SStefano Stabellini 
64*2195046bSStefano Stabellini static inline int get_request(struct pvcalls_bedata *bedata, int *req_id)
65*2195046bSStefano Stabellini {
66*2195046bSStefano Stabellini 	*req_id = bedata->ring.req_prod_pvt & (RING_SIZE(&bedata->ring) - 1);
67*2195046bSStefano Stabellini 	if (RING_FULL(&bedata->ring) ||
68*2195046bSStefano Stabellini 	    bedata->rsp[*req_id].req_id != PVCALLS_INVALID_ID)
69*2195046bSStefano Stabellini 		return -EAGAIN;
70*2195046bSStefano Stabellini 	return 0;
71*2195046bSStefano Stabellini }
72*2195046bSStefano Stabellini 
73aa7ba376SStefano Stabellini static irqreturn_t pvcalls_front_event_handler(int irq, void *dev_id)
74aa7ba376SStefano Stabellini {
75*2195046bSStefano Stabellini 	struct xenbus_device *dev = dev_id;
76*2195046bSStefano Stabellini 	struct pvcalls_bedata *bedata;
77*2195046bSStefano Stabellini 	struct xen_pvcalls_response *rsp;
78*2195046bSStefano Stabellini 	uint8_t *src, *dst;
79*2195046bSStefano Stabellini 	int req_id = 0, more = 0, done = 0;
80*2195046bSStefano Stabellini 
81*2195046bSStefano Stabellini 	if (dev == NULL)
82*2195046bSStefano Stabellini 		return IRQ_HANDLED;
83*2195046bSStefano Stabellini 
84*2195046bSStefano Stabellini 	pvcalls_enter();
85*2195046bSStefano Stabellini 	bedata = dev_get_drvdata(&dev->dev);
86*2195046bSStefano Stabellini 	if (bedata == NULL) {
87*2195046bSStefano Stabellini 		pvcalls_exit();
88*2195046bSStefano Stabellini 		return IRQ_HANDLED;
89*2195046bSStefano Stabellini 	}
90*2195046bSStefano Stabellini 
91*2195046bSStefano Stabellini again:
92*2195046bSStefano Stabellini 	while (RING_HAS_UNCONSUMED_RESPONSES(&bedata->ring)) {
93*2195046bSStefano Stabellini 		rsp = RING_GET_RESPONSE(&bedata->ring, bedata->ring.rsp_cons);
94*2195046bSStefano Stabellini 
95*2195046bSStefano Stabellini 		req_id = rsp->req_id;
96*2195046bSStefano Stabellini 		dst = (uint8_t *)&bedata->rsp[req_id] + sizeof(rsp->req_id);
97*2195046bSStefano Stabellini 		src = (uint8_t *)rsp + sizeof(rsp->req_id);
98*2195046bSStefano Stabellini 		memcpy(dst, src, sizeof(*rsp) - sizeof(rsp->req_id));
99*2195046bSStefano Stabellini 		/*
100*2195046bSStefano Stabellini 		 * First copy the rest of the data, then req_id. It is
101*2195046bSStefano Stabellini 		 * paired with the barrier when accessing bedata->rsp.
102*2195046bSStefano Stabellini 		 */
103*2195046bSStefano Stabellini 		smp_wmb();
104*2195046bSStefano Stabellini 		bedata->rsp[req_id].req_id = rsp->req_id;
105*2195046bSStefano Stabellini 
106*2195046bSStefano Stabellini 		done = 1;
107*2195046bSStefano Stabellini 		bedata->ring.rsp_cons++;
108*2195046bSStefano Stabellini 	}
109*2195046bSStefano Stabellini 
110*2195046bSStefano Stabellini 	RING_FINAL_CHECK_FOR_RESPONSES(&bedata->ring, more);
111*2195046bSStefano Stabellini 	if (more)
112*2195046bSStefano Stabellini 		goto again;
113*2195046bSStefano Stabellini 	if (done)
114*2195046bSStefano Stabellini 		wake_up(&bedata->inflight_req);
115*2195046bSStefano Stabellini 	pvcalls_exit();
116aa7ba376SStefano Stabellini 	return IRQ_HANDLED;
117aa7ba376SStefano Stabellini }
118aa7ba376SStefano Stabellini 
119aa7ba376SStefano Stabellini static void pvcalls_front_free_map(struct pvcalls_bedata *bedata,
120aa7ba376SStefano Stabellini 				   struct sock_mapping *map)
121aa7ba376SStefano Stabellini {
122aa7ba376SStefano Stabellini }
123aa7ba376SStefano Stabellini 
124*2195046bSStefano Stabellini int pvcalls_front_socket(struct socket *sock)
125*2195046bSStefano Stabellini {
126*2195046bSStefano Stabellini 	struct pvcalls_bedata *bedata;
127*2195046bSStefano Stabellini 	struct sock_mapping *map = NULL;
128*2195046bSStefano Stabellini 	struct xen_pvcalls_request *req;
129*2195046bSStefano Stabellini 	int notify, req_id, ret;
130*2195046bSStefano Stabellini 
131*2195046bSStefano Stabellini 	/*
132*2195046bSStefano Stabellini 	 * PVCalls only supports domain AF_INET,
133*2195046bSStefano Stabellini 	 * type SOCK_STREAM and protocol 0 sockets for now.
134*2195046bSStefano Stabellini 	 *
135*2195046bSStefano Stabellini 	 * Check socket type here, AF_INET and protocol checks are done
136*2195046bSStefano Stabellini 	 * by the caller.
137*2195046bSStefano Stabellini 	 */
138*2195046bSStefano Stabellini 	if (sock->type != SOCK_STREAM)
139*2195046bSStefano Stabellini 		return -EOPNOTSUPP;
140*2195046bSStefano Stabellini 
141*2195046bSStefano Stabellini 	pvcalls_enter();
142*2195046bSStefano Stabellini 	if (!pvcalls_front_dev) {
143*2195046bSStefano Stabellini 		pvcalls_exit();
144*2195046bSStefano Stabellini 		return -EACCES;
145*2195046bSStefano Stabellini 	}
146*2195046bSStefano Stabellini 	bedata = dev_get_drvdata(&pvcalls_front_dev->dev);
147*2195046bSStefano Stabellini 
148*2195046bSStefano Stabellini 	map = kzalloc(sizeof(*map), GFP_KERNEL);
149*2195046bSStefano Stabellini 	if (map == NULL) {
150*2195046bSStefano Stabellini 		pvcalls_exit();
151*2195046bSStefano Stabellini 		return -ENOMEM;
152*2195046bSStefano Stabellini 	}
153*2195046bSStefano Stabellini 
154*2195046bSStefano Stabellini 	spin_lock(&bedata->socket_lock);
155*2195046bSStefano Stabellini 
156*2195046bSStefano Stabellini 	ret = get_request(bedata, &req_id);
157*2195046bSStefano Stabellini 	if (ret < 0) {
158*2195046bSStefano Stabellini 		kfree(map);
159*2195046bSStefano Stabellini 		spin_unlock(&bedata->socket_lock);
160*2195046bSStefano Stabellini 		pvcalls_exit();
161*2195046bSStefano Stabellini 		return ret;
162*2195046bSStefano Stabellini 	}
163*2195046bSStefano Stabellini 
164*2195046bSStefano Stabellini 	/*
165*2195046bSStefano Stabellini 	 * sock->sk->sk_send_head is not used for ip sockets: reuse the
166*2195046bSStefano Stabellini 	 * field to store a pointer to the struct sock_mapping
167*2195046bSStefano Stabellini 	 * corresponding to the socket. This way, we can easily get the
168*2195046bSStefano Stabellini 	 * struct sock_mapping from the struct socket.
169*2195046bSStefano Stabellini 	 */
170*2195046bSStefano Stabellini 	sock->sk->sk_send_head = (void *)map;
171*2195046bSStefano Stabellini 	list_add_tail(&map->list, &bedata->socket_mappings);
172*2195046bSStefano Stabellini 
173*2195046bSStefano Stabellini 	req = RING_GET_REQUEST(&bedata->ring, req_id);
174*2195046bSStefano Stabellini 	req->req_id = req_id;
175*2195046bSStefano Stabellini 	req->cmd = PVCALLS_SOCKET;
176*2195046bSStefano Stabellini 	req->u.socket.id = (uintptr_t) map;
177*2195046bSStefano Stabellini 	req->u.socket.domain = AF_INET;
178*2195046bSStefano Stabellini 	req->u.socket.type = SOCK_STREAM;
179*2195046bSStefano Stabellini 	req->u.socket.protocol = IPPROTO_IP;
180*2195046bSStefano Stabellini 
181*2195046bSStefano Stabellini 	bedata->ring.req_prod_pvt++;
182*2195046bSStefano Stabellini 	RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&bedata->ring, notify);
183*2195046bSStefano Stabellini 	spin_unlock(&bedata->socket_lock);
184*2195046bSStefano Stabellini 	if (notify)
185*2195046bSStefano Stabellini 		notify_remote_via_irq(bedata->irq);
186*2195046bSStefano Stabellini 
187*2195046bSStefano Stabellini 	wait_event(bedata->inflight_req,
188*2195046bSStefano Stabellini 		   READ_ONCE(bedata->rsp[req_id].req_id) == req_id);
189*2195046bSStefano Stabellini 
190*2195046bSStefano Stabellini 	/* read req_id, then the content */
191*2195046bSStefano Stabellini 	smp_rmb();
192*2195046bSStefano Stabellini 	ret = bedata->rsp[req_id].ret;
193*2195046bSStefano Stabellini 	bedata->rsp[req_id].req_id = PVCALLS_INVALID_ID;
194*2195046bSStefano Stabellini 
195*2195046bSStefano Stabellini 	pvcalls_exit();
196*2195046bSStefano Stabellini 	return ret;
197*2195046bSStefano Stabellini }
198*2195046bSStefano Stabellini 
199416efba0SStefano Stabellini static const struct xenbus_device_id pvcalls_front_ids[] = {
200416efba0SStefano Stabellini 	{ "pvcalls" },
201416efba0SStefano Stabellini 	{ "" }
202416efba0SStefano Stabellini };
203416efba0SStefano Stabellini 
204416efba0SStefano Stabellini static int pvcalls_front_remove(struct xenbus_device *dev)
205416efba0SStefano Stabellini {
206aa7ba376SStefano Stabellini 	struct pvcalls_bedata *bedata;
207aa7ba376SStefano Stabellini 	struct sock_mapping *map = NULL, *n;
208aa7ba376SStefano Stabellini 
209aa7ba376SStefano Stabellini 	bedata = dev_get_drvdata(&pvcalls_front_dev->dev);
210aa7ba376SStefano Stabellini 	dev_set_drvdata(&dev->dev, NULL);
211aa7ba376SStefano Stabellini 	pvcalls_front_dev = NULL;
212aa7ba376SStefano Stabellini 	if (bedata->irq >= 0)
213aa7ba376SStefano Stabellini 		unbind_from_irqhandler(bedata->irq, dev);
214aa7ba376SStefano Stabellini 
215aa7ba376SStefano Stabellini 	smp_mb();
216aa7ba376SStefano Stabellini 	while (atomic_read(&pvcalls_refcount) > 0)
217aa7ba376SStefano Stabellini 		cpu_relax();
218aa7ba376SStefano Stabellini 	list_for_each_entry_safe(map, n, &bedata->socket_mappings, list) {
219aa7ba376SStefano Stabellini 		if (map->active_socket) {
220aa7ba376SStefano Stabellini 			/* No need to lock, refcount is 0 */
221aa7ba376SStefano Stabellini 			pvcalls_front_free_map(bedata, map);
222aa7ba376SStefano Stabellini 		} else {
223aa7ba376SStefano Stabellini 			list_del(&map->list);
224aa7ba376SStefano Stabellini 			kfree(map);
225aa7ba376SStefano Stabellini 		}
226aa7ba376SStefano Stabellini 	}
227aa7ba376SStefano Stabellini 	if (bedata->ref >= 0)
228aa7ba376SStefano Stabellini 		gnttab_end_foreign_access(bedata->ref, 0, 0);
229aa7ba376SStefano Stabellini 	kfree(bedata->ring.sring);
230aa7ba376SStefano Stabellini 	kfree(bedata);
231aa7ba376SStefano Stabellini 	xenbus_switch_state(dev, XenbusStateClosed);
232416efba0SStefano Stabellini 	return 0;
233416efba0SStefano Stabellini }
234416efba0SStefano Stabellini 
235416efba0SStefano Stabellini static int pvcalls_front_probe(struct xenbus_device *dev,
236416efba0SStefano Stabellini 			  const struct xenbus_device_id *id)
237416efba0SStefano Stabellini {
23821968190SStefano Stabellini 	int ret = -ENOMEM, evtchn, i;
23921968190SStefano Stabellini 	unsigned int max_page_order, function_calls, len;
24021968190SStefano Stabellini 	char *versions;
24121968190SStefano Stabellini 	grant_ref_t gref_head = 0;
24221968190SStefano Stabellini 	struct xenbus_transaction xbt;
24321968190SStefano Stabellini 	struct pvcalls_bedata *bedata = NULL;
24421968190SStefano Stabellini 	struct xen_pvcalls_sring *sring;
24521968190SStefano Stabellini 
24621968190SStefano Stabellini 	if (pvcalls_front_dev != NULL) {
24721968190SStefano Stabellini 		dev_err(&dev->dev, "only one PV Calls connection supported\n");
24821968190SStefano Stabellini 		return -EINVAL;
24921968190SStefano Stabellini 	}
25021968190SStefano Stabellini 
25121968190SStefano Stabellini 	versions = xenbus_read(XBT_NIL, dev->otherend, "versions", &len);
25221968190SStefano Stabellini 	if (!len)
25321968190SStefano Stabellini 		return -EINVAL;
25421968190SStefano Stabellini 	if (strcmp(versions, "1")) {
25521968190SStefano Stabellini 		kfree(versions);
25621968190SStefano Stabellini 		return -EINVAL;
25721968190SStefano Stabellini 	}
25821968190SStefano Stabellini 	kfree(versions);
25921968190SStefano Stabellini 	max_page_order = xenbus_read_unsigned(dev->otherend,
26021968190SStefano Stabellini 					      "max-page-order", 0);
26121968190SStefano Stabellini 	if (max_page_order < PVCALLS_RING_ORDER)
26221968190SStefano Stabellini 		return -ENODEV;
26321968190SStefano Stabellini 	function_calls = xenbus_read_unsigned(dev->otherend,
26421968190SStefano Stabellini 					      "function-calls", 0);
26521968190SStefano Stabellini 	/* See XENBUS_FUNCTIONS_CALLS in pvcalls.h */
26621968190SStefano Stabellini 	if (function_calls != 1)
26721968190SStefano Stabellini 		return -ENODEV;
26821968190SStefano Stabellini 	pr_info("%s max-page-order is %u\n", __func__, max_page_order);
26921968190SStefano Stabellini 
27021968190SStefano Stabellini 	bedata = kzalloc(sizeof(struct pvcalls_bedata), GFP_KERNEL);
27121968190SStefano Stabellini 	if (!bedata)
27221968190SStefano Stabellini 		return -ENOMEM;
27321968190SStefano Stabellini 
27421968190SStefano Stabellini 	dev_set_drvdata(&dev->dev, bedata);
27521968190SStefano Stabellini 	pvcalls_front_dev = dev;
27621968190SStefano Stabellini 	init_waitqueue_head(&bedata->inflight_req);
27721968190SStefano Stabellini 	INIT_LIST_HEAD(&bedata->socket_mappings);
27821968190SStefano Stabellini 	spin_lock_init(&bedata->socket_lock);
27921968190SStefano Stabellini 	bedata->irq = -1;
28021968190SStefano Stabellini 	bedata->ref = -1;
28121968190SStefano Stabellini 
28221968190SStefano Stabellini 	for (i = 0; i < PVCALLS_NR_RSP_PER_RING; i++)
28321968190SStefano Stabellini 		bedata->rsp[i].req_id = PVCALLS_INVALID_ID;
28421968190SStefano Stabellini 
28521968190SStefano Stabellini 	sring = (struct xen_pvcalls_sring *) __get_free_page(GFP_KERNEL |
28621968190SStefano Stabellini 							     __GFP_ZERO);
28721968190SStefano Stabellini 	if (!sring)
28821968190SStefano Stabellini 		goto error;
28921968190SStefano Stabellini 	SHARED_RING_INIT(sring);
29021968190SStefano Stabellini 	FRONT_RING_INIT(&bedata->ring, sring, XEN_PAGE_SIZE);
29121968190SStefano Stabellini 
29221968190SStefano Stabellini 	ret = xenbus_alloc_evtchn(dev, &evtchn);
29321968190SStefano Stabellini 	if (ret)
29421968190SStefano Stabellini 		goto error;
29521968190SStefano Stabellini 
29621968190SStefano Stabellini 	bedata->irq = bind_evtchn_to_irqhandler(evtchn,
29721968190SStefano Stabellini 						pvcalls_front_event_handler,
29821968190SStefano Stabellini 						0, "pvcalls-frontend", dev);
29921968190SStefano Stabellini 	if (bedata->irq < 0) {
30021968190SStefano Stabellini 		ret = bedata->irq;
30121968190SStefano Stabellini 		goto error;
30221968190SStefano Stabellini 	}
30321968190SStefano Stabellini 
30421968190SStefano Stabellini 	ret = gnttab_alloc_grant_references(1, &gref_head);
30521968190SStefano Stabellini 	if (ret < 0)
30621968190SStefano Stabellini 		goto error;
30721968190SStefano Stabellini 	bedata->ref = gnttab_claim_grant_reference(&gref_head);
30821968190SStefano Stabellini 	if (bedata->ref < 0) {
30921968190SStefano Stabellini 		ret = bedata->ref;
31021968190SStefano Stabellini 		goto error;
31121968190SStefano Stabellini 	}
31221968190SStefano Stabellini 	gnttab_grant_foreign_access_ref(bedata->ref, dev->otherend_id,
31321968190SStefano Stabellini 					virt_to_gfn((void *)sring), 0);
31421968190SStefano Stabellini 
31521968190SStefano Stabellini  again:
31621968190SStefano Stabellini 	ret = xenbus_transaction_start(&xbt);
31721968190SStefano Stabellini 	if (ret) {
31821968190SStefano Stabellini 		xenbus_dev_fatal(dev, ret, "starting transaction");
31921968190SStefano Stabellini 		goto error;
32021968190SStefano Stabellini 	}
32121968190SStefano Stabellini 	ret = xenbus_printf(xbt, dev->nodename, "version", "%u", 1);
32221968190SStefano Stabellini 	if (ret)
32321968190SStefano Stabellini 		goto error_xenbus;
32421968190SStefano Stabellini 	ret = xenbus_printf(xbt, dev->nodename, "ring-ref", "%d", bedata->ref);
32521968190SStefano Stabellini 	if (ret)
32621968190SStefano Stabellini 		goto error_xenbus;
32721968190SStefano Stabellini 	ret = xenbus_printf(xbt, dev->nodename, "port", "%u",
32821968190SStefano Stabellini 			    evtchn);
32921968190SStefano Stabellini 	if (ret)
33021968190SStefano Stabellini 		goto error_xenbus;
33121968190SStefano Stabellini 	ret = xenbus_transaction_end(xbt, 0);
33221968190SStefano Stabellini 	if (ret) {
33321968190SStefano Stabellini 		if (ret == -EAGAIN)
33421968190SStefano Stabellini 			goto again;
33521968190SStefano Stabellini 		xenbus_dev_fatal(dev, ret, "completing transaction");
33621968190SStefano Stabellini 		goto error;
33721968190SStefano Stabellini 	}
33821968190SStefano Stabellini 	xenbus_switch_state(dev, XenbusStateInitialised);
33921968190SStefano Stabellini 
340416efba0SStefano Stabellini 	return 0;
34121968190SStefano Stabellini 
34221968190SStefano Stabellini  error_xenbus:
34321968190SStefano Stabellini 	xenbus_transaction_end(xbt, 1);
34421968190SStefano Stabellini 	xenbus_dev_fatal(dev, ret, "writing xenstore");
34521968190SStefano Stabellini  error:
34621968190SStefano Stabellini 	pvcalls_front_remove(dev);
34721968190SStefano Stabellini 	return ret;
348416efba0SStefano Stabellini }
349416efba0SStefano Stabellini 
350416efba0SStefano Stabellini static void pvcalls_front_changed(struct xenbus_device *dev,
351416efba0SStefano Stabellini 			    enum xenbus_state backend_state)
352416efba0SStefano Stabellini {
35321968190SStefano Stabellini 	switch (backend_state) {
35421968190SStefano Stabellini 	case XenbusStateReconfiguring:
35521968190SStefano Stabellini 	case XenbusStateReconfigured:
35621968190SStefano Stabellini 	case XenbusStateInitialising:
35721968190SStefano Stabellini 	case XenbusStateInitialised:
35821968190SStefano Stabellini 	case XenbusStateUnknown:
35921968190SStefano Stabellini 		break;
36021968190SStefano Stabellini 
36121968190SStefano Stabellini 	case XenbusStateInitWait:
36221968190SStefano Stabellini 		break;
36321968190SStefano Stabellini 
36421968190SStefano Stabellini 	case XenbusStateConnected:
36521968190SStefano Stabellini 		xenbus_switch_state(dev, XenbusStateConnected);
36621968190SStefano Stabellini 		break;
36721968190SStefano Stabellini 
36821968190SStefano Stabellini 	case XenbusStateClosed:
36921968190SStefano Stabellini 		if (dev->state == XenbusStateClosed)
37021968190SStefano Stabellini 			break;
37121968190SStefano Stabellini 		/* Missed the backend's CLOSING state -- fallthrough */
37221968190SStefano Stabellini 	case XenbusStateClosing:
37321968190SStefano Stabellini 		xenbus_frontend_closed(dev);
37421968190SStefano Stabellini 		break;
37521968190SStefano Stabellini 	}
376416efba0SStefano Stabellini }
377416efba0SStefano Stabellini 
378416efba0SStefano Stabellini static struct xenbus_driver pvcalls_front_driver = {
379416efba0SStefano Stabellini 	.ids = pvcalls_front_ids,
380416efba0SStefano Stabellini 	.probe = pvcalls_front_probe,
381416efba0SStefano Stabellini 	.remove = pvcalls_front_remove,
382416efba0SStefano Stabellini 	.otherend_changed = pvcalls_front_changed,
383416efba0SStefano Stabellini };
384416efba0SStefano Stabellini 
385416efba0SStefano Stabellini static int __init pvcalls_frontend_init(void)
386416efba0SStefano Stabellini {
387416efba0SStefano Stabellini 	if (!xen_domain())
388416efba0SStefano Stabellini 		return -ENODEV;
389416efba0SStefano Stabellini 
390416efba0SStefano Stabellini 	pr_info("Initialising Xen pvcalls frontend driver\n");
391416efba0SStefano Stabellini 
392416efba0SStefano Stabellini 	return xenbus_register_frontend(&pvcalls_front_driver);
393416efba0SStefano Stabellini }
394416efba0SStefano Stabellini 
395416efba0SStefano Stabellini module_init(pvcalls_frontend_init);
396