xref: /netbsd-src/sys/arch/xen/xenbus/xenbus_dev.c (revision 100a3398b8d3c64e571cff36b46c23431b410e09)
1*100a3398Sandvar /* $NetBSD: xenbus_dev.c,v 1.19 2024/02/09 22:08:34 andvar Exp $ */
2ed3fe49dSbouyer /*
3ed3fe49dSbouyer  * xenbus_dev.c
4ed3fe49dSbouyer  *
5ed3fe49dSbouyer  * Driver giving user-space access to the kernel's xenbus connection
6ed3fe49dSbouyer  * to xenstore.
7ed3fe49dSbouyer  *
8ed3fe49dSbouyer  * Copyright (c) 2005, Christian Limpach
9ed3fe49dSbouyer  * Copyright (c) 2005, Rusty Russell, IBM Corporation
10ed3fe49dSbouyer  *
11ed3fe49dSbouyer  * This file may be distributed separately from the Linux kernel, or
12ed3fe49dSbouyer  * incorporated into other software packages, subject to the following license:
13ed3fe49dSbouyer  *
14ed3fe49dSbouyer  * Permission is hereby granted, free of charge, to any person obtaining a copy
15ed3fe49dSbouyer  * of this source file (the "Software"), to deal in the Software without
16ed3fe49dSbouyer  * restriction, including without limitation the rights to use, copy, modify,
17ed3fe49dSbouyer  * merge, publish, distribute, sublicense, and/or sell copies of the Software,
18ed3fe49dSbouyer  * and to permit persons to whom the Software is furnished to do so, subject to
19ed3fe49dSbouyer  * the following conditions:
20ed3fe49dSbouyer  *
21ed3fe49dSbouyer  * The above copyright notice and this permission notice shall be included in
22ed3fe49dSbouyer  * all copies or substantial portions of the Software.
23ed3fe49dSbouyer  *
24ed3fe49dSbouyer  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25ed3fe49dSbouyer  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26ed3fe49dSbouyer  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27ed3fe49dSbouyer  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28ed3fe49dSbouyer  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29ed3fe49dSbouyer  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
30ed3fe49dSbouyer  * IN THE SOFTWARE.
31ed3fe49dSbouyer  */
32ed3fe49dSbouyer 
3316046408Sbouyer #include <sys/cdefs.h>
34*100a3398Sandvar __KERNEL_RCSID(0, "$NetBSD: xenbus_dev.c,v 1.19 2024/02/09 22:08:34 andvar Exp $");
35ed3fe49dSbouyer 
3616046408Sbouyer #include "opt_xen.h"
3716046408Sbouyer 
3816046408Sbouyer #include <sys/types.h>
3916046408Sbouyer #include <sys/null.h>
4016046408Sbouyer #include <sys/errno.h>
4116046408Sbouyer #include <sys/param.h>
4216046408Sbouyer #include <sys/proc.h>
4316046408Sbouyer #include <sys/systm.h>
4416046408Sbouyer #include <sys/dirent.h>
4516046408Sbouyer #include <sys/stat.h>
4616046408Sbouyer #include <sys/tree.h>
4716046408Sbouyer #include <sys/vnode.h>
4816046408Sbouyer #include <miscfs/specfs/specdev.h>
4916046408Sbouyer #include <miscfs/kernfs/kernfs.h>
5016046408Sbouyer 
514e541343Sbouyer #include <xen/kernfs_machdep.h>
524e541343Sbouyer 
53c24c993fSbouyer #include <xen/intr.h>
544e541343Sbouyer #include <xen/hypervisor.h>
554e541343Sbouyer #include <xen/xenbus.h>
56ed3fe49dSbouyer #include "xenbus_comms.h"
57ed3fe49dSbouyer 
5816046408Sbouyer static int xenbus_dev_read(void *);
5916046408Sbouyer static int xenbus_dev_write(void *);
6016046408Sbouyer static int xenbus_dev_open(void *);
6116046408Sbouyer static int xenbus_dev_close(void *);
622ed85162Sbouyer static int xsd_port_read(void *);
63ed3fe49dSbouyer 
6416046408Sbouyer #define DIR_MODE	 (S_IRUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
6516046408Sbouyer #define PRIVCMD_MODE    (S_IRUSR | S_IWUSR)
6616046408Sbouyer static const struct kernfs_fileop xenbus_fileops[] = {
6716046408Sbouyer   { .kf_fileop = KERNFS_FILEOP_OPEN, .kf_vop = xenbus_dev_open },
6816046408Sbouyer   { .kf_fileop = KERNFS_FILEOP_CLOSE, .kf_vop = xenbus_dev_close },
6916046408Sbouyer   { .kf_fileop = KERNFS_FILEOP_READ, .kf_vop = xenbus_dev_read },
7016046408Sbouyer   { .kf_fileop = KERNFS_FILEOP_WRITE, .kf_vop = xenbus_dev_write },
7116046408Sbouyer };
7216046408Sbouyer 
732ed85162Sbouyer #define XSD_MODE    (S_IRUSR)
742ed85162Sbouyer static const struct kernfs_fileop xsd_port_fileops[] = {
752ed85162Sbouyer     { .kf_fileop = KERNFS_FILEOP_READ, .kf_vop = xsd_port_read },
762ed85162Sbouyer };
7716046408Sbouyer 
78e78e0779Sbouyer static kmutex_t xenbus_dev_open_mtx;
79e78e0779Sbouyer 
8016046408Sbouyer void
xenbus_kernfs_init(void)8196276685Scegger xenbus_kernfs_init(void)
8216046408Sbouyer {
8316046408Sbouyer 	kernfs_entry_t *dkt;
8416046408Sbouyer 	kfstype kfst;
8516046408Sbouyer 
8616046408Sbouyer 	kfst = KERNFS_ALLOCTYPE(xenbus_fileops);
878a3ee726Sjdolecek 	KERNFS_ALLOCENTRY(dkt, KM_SLEEP);
8816046408Sbouyer 	KERNFS_INITENTRY(dkt, DT_REG, "xenbus", NULL, kfst, VREG,
8916046408Sbouyer 	    PRIVCMD_MODE);
9016046408Sbouyer 	kernfs_addentry(kernxen_pkt, dkt);
912ed85162Sbouyer 
92afeabb04Sjym 	if (xendomain_is_dom0()) {
932ed85162Sbouyer 		kfst = KERNFS_ALLOCTYPE(xsd_port_fileops);
948a3ee726Sjdolecek 		KERNFS_ALLOCENTRY(dkt, KM_SLEEP);
95afeabb04Sjym 		KERNFS_INITENTRY(dkt, DT_REG, "xsd_port", NULL,
96afeabb04Sjym 		    kfst, VREG, XSD_MODE);
972ed85162Sbouyer 		kernfs_addentry(kernxen_pkt, dkt);
9816046408Sbouyer 	}
99e78e0779Sbouyer 	mutex_init(&xenbus_dev_open_mtx, MUTEX_DEFAULT, IPL_NONE);
100afeabb04Sjym }
10116046408Sbouyer 
102e78e0779Sbouyer /*
103e78e0779Sbouyer  * several process may open /kern/xen/xenbus in parallel.
104e78e0779Sbouyer  * In a transaction one or more write is followed by one or more read.
105*100a3398Sandvar  * Unfortunately we don't get a file descriptor identifier down there,
106e78e0779Sbouyer  * which we could use to link a read() to a transaction started in a write().
107e78e0779Sbouyer  * To work around this we keep a list of lwp that opended the xenbus file.
108e78e0779Sbouyer  * This assumes that a single lwp won't open /kern/xen/xenbus more
109e78e0779Sbouyer  * than once, and that a transaction started by one lwp won't be ended
110e78e0779Sbouyer  * by another.
111e78e0779Sbouyer  * because of this, we also assume that we always got the data before
112e78e0779Sbouyer  * the read() syscall.
113e78e0779Sbouyer  */
114e78e0779Sbouyer 
115e78e0779Sbouyer struct xenbus_dev_transaction {
116e78e0779Sbouyer 	SLIST_ENTRY(xenbus_dev_transaction) trans_next;
117e78e0779Sbouyer 	struct xenbus_transaction *handle;
118e78e0779Sbouyer };
119e78e0779Sbouyer 
120e78e0779Sbouyer struct xenbus_dev_lwp {
121e78e0779Sbouyer 	SLIST_ENTRY(xenbus_dev_lwp) lwp_next;
122e78e0779Sbouyer 	SLIST_HEAD(, xenbus_dev_transaction) transactions;
123e78e0779Sbouyer 	lwp_t *lwp;
124e78e0779Sbouyer 	/* Response queue. */
12516046408Sbouyer #define BUFFER_SIZE (PAGE_SIZE)
12616046408Sbouyer #define MASK_READ_IDX(idx) ((idx)&(BUFFER_SIZE-1))
127e78e0779Sbouyer 	char read_buffer[BUFFER_SIZE];
128e78e0779Sbouyer 	unsigned int read_cons, read_prod;
129ed3fe49dSbouyer 	/* Partial request. */
130ed3fe49dSbouyer 	unsigned int len;
131ed3fe49dSbouyer 	union {
132ed3fe49dSbouyer 		struct xsd_sockmsg msg;
13316046408Sbouyer 		char buffer[BUFFER_SIZE];
134ed3fe49dSbouyer 	} u;
135e78e0779Sbouyer 	kmutex_t mtx;
136ed3fe49dSbouyer };
137ed3fe49dSbouyer 
138e78e0779Sbouyer struct xenbus_dev_data {
139e78e0779Sbouyer 	/* lwps which opended this device */
140e78e0779Sbouyer 	SLIST_HEAD(, xenbus_dev_lwp) lwps;
141e78e0779Sbouyer 	kmutex_t mtx;
142e78e0779Sbouyer };
143e78e0779Sbouyer 
144e78e0779Sbouyer 
14516046408Sbouyer static int
xenbus_dev_read(void * v)14616046408Sbouyer xenbus_dev_read(void *v)
147ed3fe49dSbouyer {
14816046408Sbouyer 	struct vop_read_args /* {
14916046408Sbouyer 		struct vnode *a_vp;
15016046408Sbouyer 		struct uio *a_uio;
15116046408Sbouyer 		int  a_ioflag;
15216046408Sbouyer 		struct ucred *a_cred;
15316046408Sbouyer 	} */ *ap = v;
15416046408Sbouyer 	struct kernfs_node *kfs = VTOKERN(ap->a_vp);
15516046408Sbouyer 	struct uio *uio = ap->a_uio;
15646a06789Sbouyer 	struct xenbus_dev_data *u;
157e78e0779Sbouyer 	struct xenbus_dev_lwp *xlwp;
15816046408Sbouyer 	int err;
15916046408Sbouyer 	off_t offset;
160ed3fe49dSbouyer 
16146a06789Sbouyer 	mutex_enter(&xenbus_dev_open_mtx);
16246a06789Sbouyer 	u = kfs->kfs_v;
16346a06789Sbouyer 	if (u == NULL) {
16446a06789Sbouyer 		mutex_exit(&xenbus_dev_open_mtx);
16546a06789Sbouyer 		return EBADF;
16646a06789Sbouyer 	}
167e78e0779Sbouyer 	mutex_enter(&u->mtx);
16846a06789Sbouyer 	mutex_exit(&xenbus_dev_open_mtx);
169e78e0779Sbouyer 	SLIST_FOREACH(xlwp, &u->lwps, lwp_next) {
170e78e0779Sbouyer 		if (xlwp->lwp == curlwp) {
171e78e0779Sbouyer 			break;
172e78e0779Sbouyer 		}
173e78e0779Sbouyer 	}
174e78e0779Sbouyer 	if (xlwp == NULL) {
175e78e0779Sbouyer 		mutex_exit(&u->mtx);
176e78e0779Sbouyer 		return EBADF;
177e78e0779Sbouyer 	}
178e78e0779Sbouyer 	mutex_enter(&xlwp->mtx);
179e78e0779Sbouyer 	mutex_exit(&u->mtx);
180e78e0779Sbouyer 
181e78e0779Sbouyer 	if (xlwp->read_prod == xlwp->read_cons) {
182e78e0779Sbouyer 		err = EWOULDBLOCK;
18316046408Sbouyer 		goto end;
18416046408Sbouyer 	}
185ed3fe49dSbouyer 
186e78e0779Sbouyer 	offset = uio->uio_offset;
187e78e0779Sbouyer 	if (xlwp->read_cons > xlwp->read_prod) {
188e78e0779Sbouyer 		err = uiomove(
189e78e0779Sbouyer 		    &xlwp->read_buffer[MASK_READ_IDX(xlwp->read_cons)],
190e78e0779Sbouyer 		    0U - xlwp->read_cons, uio);
19116046408Sbouyer 		if (err)
19216046408Sbouyer 			goto end;
193e78e0779Sbouyer 		xlwp->read_cons += (uio->uio_offset - offset);
19416046408Sbouyer 		offset = uio->uio_offset;
19516046408Sbouyer 	}
196e78e0779Sbouyer 	err = uiomove(&xlwp->read_buffer[MASK_READ_IDX(xlwp->read_cons)],
197e78e0779Sbouyer 	    xlwp->read_prod - xlwp->read_cons, uio);
19816046408Sbouyer 	if (err == 0)
199e78e0779Sbouyer 		xlwp->read_cons += (uio->uio_offset - offset);
20016046408Sbouyer 
20116046408Sbouyer end:
202e78e0779Sbouyer 	mutex_exit(&xlwp->mtx);
20316046408Sbouyer 	return err;
204ed3fe49dSbouyer }
205ed3fe49dSbouyer 
20616046408Sbouyer static void
queue_reply(struct xenbus_dev_lwp * xlwp,char * data,unsigned int len)207e78e0779Sbouyer queue_reply(struct xenbus_dev_lwp *xlwp,
208ed3fe49dSbouyer 			char *data, unsigned int len)
209ed3fe49dSbouyer {
210ed3fe49dSbouyer 	int i;
211e78e0779Sbouyer 	KASSERT(mutex_owned(&xlwp->mtx));
212e78e0779Sbouyer 	for (i = 0; i < len; i++, xlwp->read_prod++)
213e78e0779Sbouyer 		xlwp->read_buffer[MASK_READ_IDX(xlwp->read_prod)] = data[i];
214ed3fe49dSbouyer 
215e78e0779Sbouyer 	KASSERT((xlwp->read_prod - xlwp->read_cons) <= sizeof(xlwp->read_buffer));
216ed3fe49dSbouyer }
217ed3fe49dSbouyer 
21816046408Sbouyer static int
xenbus_dev_write(void * v)21916046408Sbouyer xenbus_dev_write(void *v)
220ed3fe49dSbouyer {
22116046408Sbouyer 	struct vop_write_args /* {
22216046408Sbouyer 		struct vnode *a_vp;
22316046408Sbouyer 		struct uio *a_uio;
22416046408Sbouyer 		int  a_ioflag;
22516046408Sbouyer 		struct ucred *a_cred;
22616046408Sbouyer 	} */ *ap = v;
22716046408Sbouyer 	struct kernfs_node *kfs = VTOKERN(ap->a_vp);
22816046408Sbouyer 	struct uio *uio = ap->a_uio;
22916046408Sbouyer 
23046a06789Sbouyer 	struct xenbus_dev_data *u;
231e78e0779Sbouyer 	struct xenbus_dev_lwp *xlwp;
232ed3fe49dSbouyer 	struct xenbus_dev_transaction *trans;
233ed3fe49dSbouyer 	void *reply;
23416046408Sbouyer 	int err;
23516046408Sbouyer 	size_t size;
236ed3fe49dSbouyer 
23746a06789Sbouyer 	mutex_enter(&xenbus_dev_open_mtx);
23846a06789Sbouyer 	u = kfs->kfs_v;
23946a06789Sbouyer 	if (u == NULL) {
24046a06789Sbouyer 		mutex_exit(&xenbus_dev_open_mtx);
24146a06789Sbouyer 		return EBADF;
24246a06789Sbouyer 	}
243e78e0779Sbouyer 	mutex_enter(&u->mtx);
24446a06789Sbouyer 	mutex_exit(&xenbus_dev_open_mtx);
245e78e0779Sbouyer 	SLIST_FOREACH(xlwp, &u->lwps, lwp_next) {
246e78e0779Sbouyer 		if (xlwp->lwp == curlwp) {
247e78e0779Sbouyer 			break;
248e78e0779Sbouyer 		}
249e78e0779Sbouyer 	}
250e78e0779Sbouyer 	if (xlwp == NULL) {
251e78e0779Sbouyer 		mutex_exit(&u->mtx);
252e78e0779Sbouyer 		return EBADF;
253e78e0779Sbouyer 	}
254e78e0779Sbouyer 	mutex_enter(&xlwp->mtx);
255e78e0779Sbouyer 	mutex_exit(&u->mtx);
256e78e0779Sbouyer 
257e78e0779Sbouyer 	if (uio->uio_offset < 0) {
258e78e0779Sbouyer 		err = EINVAL;
259e78e0779Sbouyer 		goto end;
260e78e0779Sbouyer 	}
26116046408Sbouyer 	size = uio->uio_resid;
262ed3fe49dSbouyer 
263e78e0779Sbouyer 	if ((size + xlwp->len) > sizeof(xlwp->u.buffer)) {
264e78e0779Sbouyer 		err = EINVAL;
265e78e0779Sbouyer 		goto end;
266e78e0779Sbouyer 	}
267ed3fe49dSbouyer 
268e78e0779Sbouyer 	err = uiomove(xlwp->u.buffer + xlwp->len,
269e78e0779Sbouyer 		      sizeof(xlwp->u.buffer) -  xlwp->len, uio);
27016046408Sbouyer 	if (err)
271e78e0779Sbouyer 		goto end;
27216046408Sbouyer 
273e78e0779Sbouyer 	xlwp->len += size;
274e78e0779Sbouyer 	if (xlwp->len < (sizeof(xlwp->u.msg) + xlwp->u.msg.len))
275e78e0779Sbouyer 		goto end;
276ed3fe49dSbouyer 
277e78e0779Sbouyer 	switch (xlwp->u.msg.type) {
278ed3fe49dSbouyer 	case XS_TRANSACTION_START:
279ed3fe49dSbouyer 	case XS_TRANSACTION_END:
280ed3fe49dSbouyer 	case XS_DIRECTORY:
281ed3fe49dSbouyer 	case XS_READ:
282ed3fe49dSbouyer 	case XS_GET_PERMS:
283ed3fe49dSbouyer 	case XS_RELEASE:
284ed3fe49dSbouyer 	case XS_GET_DOMAIN_PATH:
285ed3fe49dSbouyer 	case XS_WRITE:
286ed3fe49dSbouyer 	case XS_MKDIR:
287ed3fe49dSbouyer 	case XS_RM:
288ed3fe49dSbouyer 	case XS_SET_PERMS:
289e78e0779Sbouyer 		err = xenbus_dev_request_and_reply(&xlwp->u.msg, &reply);
29016046408Sbouyer 		if (err == 0) {
291e78e0779Sbouyer 			if (xlwp->u.msg.type == XS_TRANSACTION_START) {
2921c0b49f5Sjdolecek 				trans = kmem_alloc(sizeof(*trans), KM_SLEEP);
293ed3fe49dSbouyer 				trans->handle = (struct xenbus_transaction *)
29416046408Sbouyer 					strtoul(reply, NULL, 0);
295e78e0779Sbouyer 				SLIST_INSERT_HEAD(&xlwp->transactions,
29616046408Sbouyer 				    trans, trans_next);
297e78e0779Sbouyer 			} else if (xlwp->u.msg.type == XS_TRANSACTION_END) {
298e78e0779Sbouyer 				SLIST_FOREACH(trans, &xlwp->transactions,
29916046408Sbouyer 						    trans_next) {
300ed3fe49dSbouyer 					if ((unsigned long)trans->handle ==
301e78e0779Sbouyer 					    (unsigned long)xlwp->u.msg.tx_id)
302ed3fe49dSbouyer 						break;
30316046408Sbouyer 				}
304e78e0779Sbouyer 				if (trans == NULL) {
305e78e0779Sbouyer 					err = EINVAL;
306e78e0779Sbouyer 					goto end;
307e78e0779Sbouyer 				}
308e78e0779Sbouyer 				SLIST_REMOVE(&xlwp->transactions, trans,
30916046408Sbouyer 				    xenbus_dev_transaction, trans_next);
3101c0b49f5Sjdolecek 				kmem_free(trans, sizeof(*trans));
311ed3fe49dSbouyer 			}
312e78e0779Sbouyer 			queue_reply(xlwp, (char *)&xlwp->u.msg,
313e78e0779Sbouyer 						sizeof(xlwp->u.msg));
314e78e0779Sbouyer 			queue_reply(xlwp, (char *)reply, xlwp->u.msg.len);
31579743bc4Sjdolecek 
31679743bc4Sjdolecek 			xenbus_dev_reply_free(&xlwp->u.msg, reply);
317ed3fe49dSbouyer 		}
318ed3fe49dSbouyer 		break;
319ed3fe49dSbouyer 
320ed3fe49dSbouyer 	default:
32116046408Sbouyer 		err = EINVAL;
322ed3fe49dSbouyer 		break;
323ed3fe49dSbouyer 	}
324ed3fe49dSbouyer 
325ed3fe49dSbouyer 	if (err == 0) {
326e78e0779Sbouyer 		xlwp->len = 0;
327ed3fe49dSbouyer 	}
328e78e0779Sbouyer end:
329e78e0779Sbouyer 	mutex_exit(&xlwp->mtx);
330ed3fe49dSbouyer 	return err;
331ed3fe49dSbouyer }
332ed3fe49dSbouyer 
33316046408Sbouyer static int
xenbus_dev_open(void * v)33416046408Sbouyer xenbus_dev_open(void *v)
335ed3fe49dSbouyer {
33616046408Sbouyer 	struct vop_open_args /* {
33716046408Sbouyer 		struct vnode *a_vp;
33816046408Sbouyer 		int a_mode;
33916046408Sbouyer 		struct ucred *a_cred;
34016046408Sbouyer 	} */ *ap = v;
34116046408Sbouyer 	struct kernfs_node *kfs = VTOKERN(ap->a_vp);
342ed3fe49dSbouyer 	struct xenbus_dev_data *u;
343e78e0779Sbouyer 	struct xenbus_dev_lwp *xlwp;
344ed3fe49dSbouyer 
34516046408Sbouyer 	if (xen_start_info.store_evtchn == 0)
34616046408Sbouyer 		return ENOENT;
347ed3fe49dSbouyer 
348e78e0779Sbouyer 	mutex_enter(&xenbus_dev_open_mtx);
349e78e0779Sbouyer 	u = kfs->kfs_v;
350e78e0779Sbouyer 	if (u == NULL) {
351e78e0779Sbouyer 		mutex_exit(&xenbus_dev_open_mtx);
3521c0b49f5Sjdolecek 
3531c0b49f5Sjdolecek 		u = kmem_zalloc(sizeof(*u), KM_SLEEP);
354e78e0779Sbouyer 		SLIST_INIT(&u->lwps);
355e78e0779Sbouyer 		mutex_init(&u->mtx, MUTEX_DEFAULT, IPL_NONE);
3561c0b49f5Sjdolecek 
3571c0b49f5Sjdolecek 		mutex_enter(&xenbus_dev_open_mtx);
3581c0b49f5Sjdolecek 		/*
3591c0b49f5Sjdolecek 		 * Must re-check if filled while waiting in alloc
3601c0b49f5Sjdolecek 		 * by some other lwp.
3611c0b49f5Sjdolecek 		 */
3621c0b49f5Sjdolecek 		if (kfs->kfs_v) {
3631c0b49f5Sjdolecek 			kmem_free(u, sizeof(*u));
3641c0b49f5Sjdolecek 			u = kfs->kfs_v;
3651c0b49f5Sjdolecek 		} else {
36616046408Sbouyer 			kfs->kfs_v = u;
3671c0b49f5Sjdolecek 		}
368e78e0779Sbouyer 	};
369e78e0779Sbouyer 	mutex_exit(&xenbus_dev_open_mtx);
3701c0b49f5Sjdolecek 
3711c0b49f5Sjdolecek 	mutex_enter(&u->mtx);
372e78e0779Sbouyer 	SLIST_FOREACH(xlwp, &u->lwps, lwp_next) {
373e78e0779Sbouyer 		if (xlwp->lwp == curlwp) {
374e78e0779Sbouyer 			break;
375e78e0779Sbouyer 		}
376e78e0779Sbouyer 	}
377e78e0779Sbouyer 	if (xlwp == NULL) {
378e78e0779Sbouyer 		mutex_exit(&u->mtx);
3791c0b49f5Sjdolecek 
3801c0b49f5Sjdolecek 		xlwp = kmem_zalloc(sizeof(*xlwp), KM_SLEEP);
381e78e0779Sbouyer 		xlwp->lwp = curlwp;
382e78e0779Sbouyer 		SLIST_INIT(&xlwp->transactions);
38372413a9eSbouyer 		mutex_init(&xlwp->mtx, MUTEX_DEFAULT, IPL_NONE);
3841c0b49f5Sjdolecek 
3851c0b49f5Sjdolecek 		mutex_enter(&u->mtx);
3861c0b49f5Sjdolecek 		/*
3871c0b49f5Sjdolecek 		 * While alloc can block, this can't be re-entered with
3881c0b49f5Sjdolecek 		 * curlwp, so no need to re-check. Also the node can't
3891c0b49f5Sjdolecek 		 * be closed while we are blocked here.
3901c0b49f5Sjdolecek 		 */
3911c0b49f5Sjdolecek 		SLIST_INSERT_HEAD(&u->lwps, xlwp, lwp_next);
392e78e0779Sbouyer 	}
393e78e0779Sbouyer 	mutex_exit(&u->mtx);
3941c0b49f5Sjdolecek 
395ed3fe49dSbouyer 	return 0;
396ed3fe49dSbouyer }
397ed3fe49dSbouyer 
39816046408Sbouyer static int
xenbus_dev_close(void * v)39916046408Sbouyer xenbus_dev_close(void *v)
400ed3fe49dSbouyer {
40116046408Sbouyer 	struct vop_close_args /* {
40216046408Sbouyer 		struct vnode *a_vp;
40316046408Sbouyer 		int a_fflag;
40416046408Sbouyer 		struct ucred *a_cred;
40516046408Sbouyer 	} */ *ap = v;
40616046408Sbouyer 	struct kernfs_node *kfs = VTOKERN(ap->a_vp);
407ed3fe49dSbouyer 
408f0d7af04Sbouyer 	struct xenbus_dev_data *u;
409e78e0779Sbouyer 	struct xenbus_dev_lwp *xlwp;
41016046408Sbouyer 	struct xenbus_dev_transaction *trans;
41116046408Sbouyer 
412e78e0779Sbouyer 	mutex_enter(&xenbus_dev_open_mtx);
413e78e0779Sbouyer 	u = kfs->kfs_v;
414e78e0779Sbouyer 	KASSERT(u != NULL);
415e78e0779Sbouyer 	mutex_enter(&u->mtx);
416e78e0779Sbouyer 	SLIST_FOREACH(xlwp, &u->lwps, lwp_next) {
417e78e0779Sbouyer 		if (xlwp->lwp == curlwp) {
418e78e0779Sbouyer 			break;
419e78e0779Sbouyer 		}
420e78e0779Sbouyer 	}
421e78e0779Sbouyer 	if (xlwp == NULL) {
422e78e0779Sbouyer 		mutex_exit(&u->mtx);
423e78e0779Sbouyer 		mutex_exit(&xenbus_dev_open_mtx);
424e78e0779Sbouyer 		return EBADF;
425e78e0779Sbouyer 	}
426e78e0779Sbouyer 	mutex_enter(&xlwp->mtx);
427e78e0779Sbouyer 	while (!SLIST_EMPTY(&xlwp->transactions)) {
428e78e0779Sbouyer 		trans = SLIST_FIRST(&xlwp->transactions);
429ed3fe49dSbouyer 		xenbus_transaction_end(trans->handle, 1);
430e78e0779Sbouyer 		SLIST_REMOVE_HEAD(&xlwp->transactions, trans_next);
4311c0b49f5Sjdolecek 		kmem_free(trans, sizeof(*trans));
432ed3fe49dSbouyer 	}
433e78e0779Sbouyer 	mutex_exit(&xlwp->mtx);
434e78e0779Sbouyer 	SLIST_REMOVE(&u->lwps, xlwp, xenbus_dev_lwp, lwp_next);
435e78e0779Sbouyer 	mutex_destroy(&xlwp->mtx);
436ed3fe49dSbouyer 
437e78e0779Sbouyer 	if (!SLIST_EMPTY(&u->lwps)) {
438e78e0779Sbouyer 		mutex_exit(&u->mtx);
439e78e0779Sbouyer 		mutex_exit(&xenbus_dev_open_mtx);
440e78e0779Sbouyer 		return 0;
441e78e0779Sbouyer 	}
442e78e0779Sbouyer 	mutex_exit(&u->mtx);
443e78e0779Sbouyer 	mutex_destroy(&u->mtx);
44416046408Sbouyer 	kfs->kfs_v = NULL;
445e78e0779Sbouyer 	mutex_exit(&xenbus_dev_open_mtx);
4461c0b49f5Sjdolecek 	kmem_free(xlwp, sizeof(*xlwp));
4471c0b49f5Sjdolecek 	kmem_free(u, sizeof(*u));
448ed3fe49dSbouyer 	return 0;
449ed3fe49dSbouyer }
450ed3fe49dSbouyer 
4512ed85162Sbouyer #define LD_STRLEN 21 /* a 64bit integer needs 20 digits in base10 */
4522ed85162Sbouyer 
4532ed85162Sbouyer static int
xsd_port_read(void * v)4542ed85162Sbouyer xsd_port_read(void *v)
4552ed85162Sbouyer {
4562ed85162Sbouyer 	struct vop_read_args /* {
4572ed85162Sbouyer 		struct vnode *a_vp;
4582ed85162Sbouyer 		struct uio *a_uio;
4592ed85162Sbouyer 		int  a_ioflag;
4602ed85162Sbouyer 		struct ucred *a_cred;
4612ed85162Sbouyer 	} */ *ap = v;
4622ed85162Sbouyer 	struct uio *uio = ap->a_uio;
4632ed85162Sbouyer 	int off, error;
4642ed85162Sbouyer 	size_t len;
4652ed85162Sbouyer 	char strbuf[LD_STRLEN], *bf;
4662ed85162Sbouyer 
4672ed85162Sbouyer 	off = (int)uio->uio_offset;
4682ed85162Sbouyer 	if (off < 0)
4692ed85162Sbouyer 		return EINVAL;
4702ed85162Sbouyer 
4712ed85162Sbouyer 	len  = snprintf(strbuf, sizeof(strbuf), "%ld\n",
4722ed85162Sbouyer 	    (long)xen_start_info.store_evtchn);
4732ed85162Sbouyer 	if (off >= len) {
4742ed85162Sbouyer 		bf = strbuf;
4752ed85162Sbouyer 		len = 0;
4762ed85162Sbouyer 	} else {
4772ed85162Sbouyer 		bf = &strbuf[off];
4782ed85162Sbouyer 		len -= off;
4792ed85162Sbouyer 	}
4802ed85162Sbouyer 	error = uiomove(bf, len, uio);
4812ed85162Sbouyer 	return error;
4822ed85162Sbouyer }
4832ed85162Sbouyer 
484ed3fe49dSbouyer /*
485ed3fe49dSbouyer  * Local variables:
486ed3fe49dSbouyer  *  c-file-style: "linux"
487ed3fe49dSbouyer  *  indent-tabs-mode: t
488ed3fe49dSbouyer  *  c-indent-level: 8
489ed3fe49dSbouyer  *  c-basic-offset: 8
490ed3fe49dSbouyer  *  tab-width: 8
491ed3fe49dSbouyer  * End:
492ed3fe49dSbouyer  */
493