xref: /openbsd/sys/arch/sparc64/dev/vldcp.c (revision 3b372c34)
1 /*	$OpenBSD: vldcp.c,v 1.26 2024/05/14 08:26:13 jsg Exp $	*/
2 /*
3  * Copyright (c) 2009, 2012 Mark Kettenis
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/conf.h>
20 #include <sys/device.h>
21 #include <sys/malloc.h>
22 #include <sys/proc.h>
23 #include <sys/systm.h>
24 
25 #include <machine/autoconf.h>
26 #include <machine/hypervisor.h>
27 #include <machine/mdesc.h>
28 
29 #include <uvm/uvm_extern.h>
30 
31 #include <sparc64/dev/cbusvar.h>
32 #include <sparc64/dev/ldcvar.h>
33 
34 #ifdef VLDCP_DEBUG
35 #define DPRINTF(x)	printf x
36 #else
37 #define DPRINTF(x)
38 #endif
39 
40 #include <sys/ioccom.h>
41 
42 struct hv_io {
43 	uint64_t	hi_cookie;
44 	void		*hi_addr;
45 	size_t		hi_len;
46 };
47 
48 #define HVIOCREAD	_IOW('h', 0, struct hv_io)
49 #define HVIOCWRITE	_IOW('h', 1, struct hv_io)
50 
51 #define VLDCP_TX_ENTRIES	128
52 #define VLDCP_RX_ENTRIES	128
53 
54 struct vldcp_softc {
55 	struct device	sc_dv;
56 	bus_space_tag_t	sc_bustag;
57 	bus_dma_tag_t	sc_dmatag;
58 
59 	uint64_t	sc_tx_ino;
60 	uint64_t	sc_rx_ino;
61 	void		*sc_tx_ih;
62 	void		*sc_rx_ih;
63 
64 	struct ldc_conn	sc_lc;
65 
66 	struct selinfo	sc_rsel;
67 	struct selinfo	sc_wsel;
68 };
69 
70 int	vldcp_match(struct device *, void *, void *);
71 void	vldcp_attach(struct device *, struct device *, void *);
72 void	filt_vldcprdetach(struct knote *);
73 void	filt_vldcpwdetach(struct knote *);
74 int	filt_vldcpread(struct knote *, long);
75 int	vldcpkqfilter(dev_t, struct knote *);
76 
77 const struct cfattach vldcp_ca = {
78 	sizeof(struct vldcp_softc), vldcp_match, vldcp_attach
79 };
80 
81 struct cfdriver vldcp_cd = {
82 	NULL, "vldcp", DV_DULL
83 };
84 
85 int	vldcp_tx_intr(void *);
86 int	vldcp_rx_intr(void *);
87 
88 /*
89  * We attach to certain well-known channels.  These are assigned fixed
90  * device minor device numbers through their index in the table below.
91  * So "hvctl" gets minor 0, "spds" gets minor 1, etc. etc.
92  *
93  * We also attach to the domain services channels.  These are named
94  * "ldom-<guestname>" and get assigned a device minor starting at
95  * VLDC_LDOM_OFFSET.
96  */
97 #define VLDC_NUM_SERVICES	64
98 #define VLDC_LDOM_OFFSET	32
99 int vldc_num_ldoms;
100 
101 struct vldc_svc {
102 	const char *vs_name;
103 	struct vldcp_softc *vs_sc;
104 };
105 
106 struct vldc_svc vldc_svc[VLDC_NUM_SERVICES] = {
107 	{ "hvctl" },
108 	{ "spds" },
109 	{ NULL }
110 };
111 
112 int
vldcp_match(struct device * parent,void * match,void * aux)113 vldcp_match(struct device *parent, void *match, void *aux)
114 {
115 	struct cbus_attach_args *ca = aux;
116 	struct vldc_svc *svc;
117 
118 	for (svc = vldc_svc; svc->vs_name != NULL; svc++)
119 		if (strcmp(ca->ca_name, svc->vs_name) == 0)
120 			return (1);
121 
122 	if (strncmp(ca->ca_name, "ldom-", 5) == 0 &&
123 	    strcmp(ca->ca_name, "ldom-primary") != 0)
124 		return (1);
125 
126 	return (0);
127 }
128 
129 void
vldcp_attach(struct device * parent,struct device * self,void * aux)130 vldcp_attach(struct device *parent, struct device *self, void *aux)
131 {
132 	struct vldcp_softc *sc = (struct vldcp_softc *)self;
133 	struct cbus_attach_args *ca = aux;
134 	struct vldc_svc *svc;
135 	struct ldc_conn *lc;
136 
137 	sc->sc_bustag = ca->ca_bustag;
138 	sc->sc_dmatag = ca->ca_dmatag;
139 	sc->sc_tx_ino = ca->ca_tx_ino;
140 	sc->sc_rx_ino = ca->ca_rx_ino;
141 
142 	printf(": ivec 0x%llx, 0x%llx", sc->sc_tx_ino, sc->sc_rx_ino);
143 
144 	/*
145 	 * Un-configure queues before registering interrupt handlers,
146 	 * such that we dont get any stale LDC packets or events.
147 	 */
148 	hv_ldc_tx_qconf(ca->ca_id, 0, 0);
149 	hv_ldc_rx_qconf(ca->ca_id, 0, 0);
150 
151 	sc->sc_tx_ih = bus_intr_establish(ca->ca_bustag, sc->sc_tx_ino,
152 	    IPL_TTY, 0, vldcp_tx_intr, sc, sc->sc_dv.dv_xname);
153 	sc->sc_rx_ih = bus_intr_establish(ca->ca_bustag, sc->sc_rx_ino,
154 	    IPL_TTY, 0, vldcp_rx_intr, sc, sc->sc_dv.dv_xname);
155 	if (sc->sc_tx_ih == NULL || sc->sc_rx_ih == NULL) {
156 		printf(", can't establish interrupt\n");
157 		return;
158 	}
159 
160 	lc = &sc->sc_lc;
161 	lc->lc_id = ca->ca_id;
162 	lc->lc_sc = sc;
163 
164 	lc->lc_txq = ldc_queue_alloc(sc->sc_dmatag, VLDCP_TX_ENTRIES);
165 	if (lc->lc_txq == NULL) {
166 		printf(", can't allocate tx queue\n");
167 		return;
168 	}
169 
170 	lc->lc_rxq = ldc_queue_alloc(sc->sc_dmatag, VLDCP_RX_ENTRIES);
171 	if (lc->lc_rxq == NULL) {
172 		printf(", can't allocate rx queue\n");
173 		goto free_txqueue;
174 	}
175 	lc->lc_rx_state = LDC_CHANNEL_INIT;
176 
177 	for (svc = vldc_svc; svc->vs_name != NULL; svc++) {
178 		if (strcmp(ca->ca_name, svc->vs_name) == 0) {
179 			svc->vs_sc = sc;
180 			break;
181 		}
182 	}
183 
184 	if (strncmp(ca->ca_name, "ldom-", 5) == 0 &&
185 	    strcmp(ca->ca_name, "ldom-primary") != 0) {
186 		int minor = VLDC_LDOM_OFFSET + vldc_num_ldoms++;
187 		if (minor < nitems(vldc_svc))
188 			vldc_svc[minor].vs_sc = sc;
189 	}
190 
191 	printf(" channel \"%s\"\n", ca->ca_name);
192 	return;
193 
194 #if 0
195 free_rxqueue:
196 	ldc_queue_free(sc->sc_dmatag, lc->lc_rxq);
197 #endif
198 free_txqueue:
199 	ldc_queue_free(sc->sc_dmatag, lc->lc_txq);
200 }
201 
202 int
vldcp_tx_intr(void * arg)203 vldcp_tx_intr(void *arg)
204 {
205 	struct vldcp_softc *sc = arg;
206 	struct ldc_conn *lc = &sc->sc_lc;
207 	uint64_t tx_head, tx_tail, tx_state;
208 	int err;
209 
210 	err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state);
211 	if (err != H_EOK) {
212 		printf("%s: hv_ldc_tx_get_state %d\n", __func__, err);
213 		return (0);
214 	}
215 
216 	if (tx_state != lc->lc_tx_state) {
217 		switch (tx_state) {
218 		case LDC_CHANNEL_DOWN:
219 			DPRINTF(("%s: Tx link down\n", __func__));
220 			break;
221 		case LDC_CHANNEL_UP:
222 			DPRINTF(("%s: Tx link up\n", __func__));
223 			break;
224 		case LDC_CHANNEL_RESET:
225 			DPRINTF(("%s: Tx link reset\n", __func__));
226 			break;
227 		}
228 		lc->lc_tx_state = tx_state;
229 	}
230 
231 	cbus_intr_setenabled(sc->sc_bustag, sc->sc_tx_ino, INTR_DISABLED);
232 	selwakeup(&sc->sc_wsel);
233 	wakeup(lc->lc_txq);
234 	return (1);
235 }
236 
237 int
vldcp_rx_intr(void * arg)238 vldcp_rx_intr(void *arg)
239 {
240 	struct vldcp_softc *sc = arg;
241 	struct ldc_conn *lc = &sc->sc_lc;
242 	uint64_t rx_head, rx_tail, rx_state;
243 	int err;
244 
245 	err = hv_ldc_rx_get_state(lc->lc_id, &rx_head, &rx_tail, &rx_state);
246 	if (err != H_EOK) {
247 		printf("%s: hv_ldc_rx_get_state %d\n", __func__, err);
248 		return (0);
249 	}
250 
251 	if (rx_state != lc->lc_rx_state) {
252 		switch (rx_state) {
253 		case LDC_CHANNEL_DOWN:
254 			DPRINTF(("%s: Rx link down\n", __func__));
255 			if (rx_head == rx_tail)
256 				break;
257 			/* Discard and ack pending I/O. */
258 			DPRINTF(("setting rx qhead to %llx\n", rx_tail));
259 			err = hv_ldc_rx_set_qhead(lc->lc_id, rx_tail);
260 			if (err == H_EOK)
261 				break;
262 			printf("%s: hv_ldc_rx_set_qhead %d\n", __func__, err);
263 			break;
264 		case LDC_CHANNEL_UP:
265 			DPRINTF(("%s: Rx link up\n", __func__));
266 			break;
267 		case LDC_CHANNEL_RESET:
268 			DPRINTF(("%s: Rx link reset\n", __func__));
269 			if (rx_head == rx_tail)
270 				break;
271 			/* Discard and ack pending I/O. */
272 			DPRINTF(("setting rx qhead to %llx\n", rx_tail));
273 			err = hv_ldc_rx_set_qhead(lc->lc_id, rx_tail);
274 			if (err == H_EOK)
275 				break;
276 			printf("%s: hv_ldc_rx_set_qhead %d\n", __func__, err);
277 			break;
278 		}
279 		lc->lc_rx_state = rx_state;
280 		cbus_intr_setenabled(sc->sc_bustag, sc->sc_rx_ino,
281 		    INTR_DISABLED);
282 		selwakeup(&sc->sc_rsel);
283 		wakeup(lc->lc_rxq);
284 		return (1);
285 	}
286 
287 	if (rx_head == rx_tail)
288 		return (0);
289 
290 	cbus_intr_setenabled(sc->sc_bustag, sc->sc_rx_ino, INTR_DISABLED);
291 	selwakeup(&sc->sc_rsel);
292 	wakeup(lc->lc_rxq);
293 	return (1);
294 }
295 
296 cdev_decl(vldcp);
297 struct vldcp_softc *vldcp_lookup(dev_t);
298 
299 struct vldcp_softc *
vldcp_lookup(dev_t dev)300 vldcp_lookup(dev_t dev)
301 {
302 	struct vldcp_softc *sc = NULL;
303 
304 	if (minor(dev) < nitems(vldc_svc))
305 		sc = vldc_svc[minor(dev)].vs_sc;
306 
307 	if (sc)
308 		device_ref(&sc->sc_dv);
309 
310 	return (sc);
311 }
312 
313 int
vldcpopen(dev_t dev,int flag,int mode,struct proc * p)314 vldcpopen(dev_t dev, int flag, int mode, struct proc *p)
315 {
316 	struct vldcp_softc *sc;
317 	struct ldc_conn *lc;
318 	uint64_t rx_head, rx_tail, rx_state;
319 	int err;
320 
321 	sc = vldcp_lookup(dev);
322 	if (sc == NULL)
323 		return (ENXIO);
324 	lc = &sc->sc_lc;
325 
326 	err = hv_ldc_tx_qconf(lc->lc_id,
327 	    lc->lc_txq->lq_map->dm_segs[0].ds_addr, lc->lc_txq->lq_nentries);
328 	if (err != H_EOK)
329 		printf("%s: hv_ldc_tx_qconf %d\n", __func__, err);
330 
331 	err = hv_ldc_rx_qconf(lc->lc_id,
332 	    lc->lc_rxq->lq_map->dm_segs[0].ds_addr, lc->lc_rxq->lq_nentries);
333 	if (err != H_EOK)
334 		printf("%s: hv_ldc_rx_qconf %d\n", __func__, err);
335 
336 	/* Clear a pending channel reset.  */
337 	err = hv_ldc_rx_get_state(lc->lc_id, &rx_head, &rx_tail, &rx_state);
338 	if (err != H_EOK)
339 		printf("%s: hv_ldc_rx_get_state %d\n", __func__, err);
340 
341 	device_unref(&sc->sc_dv);
342 	return (0);
343 }
344 
345 int
vldcpclose(dev_t dev,int flag,int mode,struct proc * p)346 vldcpclose(dev_t dev, int flag, int mode, struct proc *p)
347 {
348 	struct vldcp_softc *sc;
349 
350 	sc = vldcp_lookup(dev);
351 	if (sc == NULL)
352 		return (ENXIO);
353 
354 	cbus_intr_setenabled(sc->sc_bustag, sc->sc_tx_ino, INTR_DISABLED);
355 	cbus_intr_setenabled(sc->sc_bustag, sc->sc_rx_ino, INTR_DISABLED);
356 
357 	hv_ldc_tx_qconf(sc->sc_lc.lc_id, 0, 0);
358 	hv_ldc_rx_qconf(sc->sc_lc.lc_id, 0, 0);
359 
360 	device_unref(&sc->sc_dv);
361 	return (0);
362 }
363 
364 int
vldcpread(dev_t dev,struct uio * uio,int ioflag)365 vldcpread(dev_t dev, struct uio *uio, int ioflag)
366 {
367 	struct vldcp_softc *sc;
368 	struct ldc_conn *lc;
369 	uint64_t rx_head, rx_tail, rx_state;
370 	int err, ret;
371 	int s;
372 
373 	sc = vldcp_lookup(dev);
374 	if (sc == NULL)
375 		return (ENXIO);
376 	lc = &sc->sc_lc;
377 
378 	if (uio->uio_resid != 64) {
379 		device_unref(&sc->sc_dv);
380 		return (EINVAL);
381 	}
382 
383 	s = spltty();
384 retry:
385 	err = hv_ldc_rx_get_state(lc->lc_id, &rx_head, &rx_tail, &rx_state);
386 	if (err != H_EOK) {
387 		splx(s);
388 		printf("%s: hv_ldc_rx_get_state %d\n", __func__, err);
389 		device_unref(&sc->sc_dv);
390 		return (EIO);
391 	}
392 
393 	DPRINTF(("rx head %llx, rx tail %llx, state %lld\n", rx_head, rx_tail, rx_state));
394 
395 	if (rx_state != LDC_CHANNEL_UP) {
396 		splx(s);
397 		device_unref(&sc->sc_dv);
398 		return (EIO);
399 	}
400 
401 	if (rx_head == rx_tail) {
402 		cbus_intr_setenabled(sc->sc_bustag, sc->sc_rx_ino,
403 		    INTR_ENABLED);
404 		ret = tsleep_nsec(lc->lc_rxq, PWAIT | PCATCH, "hvrd", INFSLP);
405 		if (ret) {
406 			splx(s);
407 			device_unref(&sc->sc_dv);
408 			return (ret);
409 		}
410 		goto retry;
411 	}
412 
413 	ret = uiomove(lc->lc_rxq->lq_va + rx_head, 64, uio);
414 
415 	rx_head += 64;
416 	rx_head &= ((lc->lc_rxq->lq_nentries * 64) - 1);
417 	err = hv_ldc_rx_set_qhead(lc->lc_id, rx_head);
418 	if (err != H_EOK)
419 		printf("%s: hv_ldc_rx_set_qhead %d\n", __func__, err);
420 
421 	splx(s);
422 	device_unref(&sc->sc_dv);
423 	return (ret);
424 }
425 
426 int
vldcpwrite(dev_t dev,struct uio * uio,int ioflag)427 vldcpwrite(dev_t dev, struct uio *uio, int ioflag)
428 {
429 	struct vldcp_softc *sc;
430 	struct ldc_conn *lc;
431 	uint64_t tx_head, tx_tail, tx_state;
432 	uint64_t next_tx_tail;
433 	int err, ret;
434 	int s;
435 
436 	sc = vldcp_lookup(dev);
437 	if (sc == NULL)
438 		return (ENXIO);
439 	lc = &sc->sc_lc;
440 
441 	if (uio->uio_resid != 64) {
442 		device_unref(&sc->sc_dv);
443 		return (EINVAL);
444 	}
445 
446 	s = spltty();
447 retry:
448 	err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state);
449 	if (err != H_EOK) {
450 		splx(s);
451 		printf("%s: hv_ldc_tx_get_state %d\n", __func__, err);
452 		device_unref(&sc->sc_dv);
453 		return (EIO);
454 	}
455 
456 	if (tx_state != LDC_CHANNEL_UP) {
457 		splx(s);
458 		device_unref(&sc->sc_dv);
459 		return (EIO);
460 	}
461 
462 	DPRINTF(("tx head %llx, tx tail %llx\n", tx_head, tx_tail));
463 
464 	next_tx_tail = tx_tail + 64;
465 	next_tx_tail &= ((lc->lc_txq->lq_nentries * 64) - 1);
466 
467 	if (tx_head == next_tx_tail) {
468 		cbus_intr_setenabled(sc->sc_bustag, sc->sc_tx_ino,
469 		    INTR_ENABLED);
470 		ret = tsleep_nsec(lc->lc_txq, PWAIT | PCATCH, "hvwr", INFSLP);
471 		if (ret) {
472 			splx(s);
473 			device_unref(&sc->sc_dv);
474 			return (ret);
475 		}
476 		goto retry;
477 	}
478 	splx(s);
479 
480 	ret = uiomove(lc->lc_txq->lq_va + tx_tail, 64, uio);
481 
482 	err = hv_ldc_tx_set_qtail(lc->lc_id, next_tx_tail);
483 	if (err != H_EOK) {
484 		printf("%s: hv_ldc_tx_set_qtail: %d\n", __func__, err);
485 		device_unref(&sc->sc_dv);
486 		return (EIO);
487 	}
488 
489 	device_unref(&sc->sc_dv);
490 	return (ret);
491 }
492 
493 int
vldcpioctl(dev_t dev,u_long cmd,caddr_t data,int flag,struct proc * p)494 vldcpioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
495 {
496 	struct vldcp_softc *sc;
497 	struct ldc_conn *lc;
498 	struct hv_io *hi = (struct hv_io *)data;
499 	paddr_t pa, offset;
500 	psize_t nbytes;
501 	caddr_t buf;
502 	size_t size;
503 	int err;
504 
505 	sc = vldcp_lookup(dev);
506 	if (sc == NULL)
507 		return (ENXIO);
508 	lc = &sc->sc_lc;
509 
510 	switch (cmd) {
511 	case HVIOCREAD:
512 	case HVIOCWRITE:
513 		break;
514 	default:
515 		device_unref(&sc->sc_dv);
516 		return (ENOTTY);
517 	}
518 
519 	buf = malloc(PAGE_SIZE, M_DEVBUF, M_WAITOK);
520 
521 	switch(cmd) {
522 	case HVIOCREAD:
523 		size = hi->hi_len;
524 		offset = 0;
525 		while (size > 0) {
526 			pmap_extract(pmap_kernel(), (vaddr_t)buf, &pa);
527 			nbytes = min(PAGE_SIZE, size);
528 			err = hv_ldc_copy(lc->lc_id, LDC_COPY_IN,
529 			    hi->hi_cookie + offset, pa, nbytes, &nbytes);
530 			if (err != H_EOK) {
531 				printf("hv_ldc_copy %d\n", err);
532 				free(buf, M_DEVBUF, 0);
533 				device_unref(&sc->sc_dv);
534 				return (EINVAL);
535 			}
536 			err = copyout(buf, (caddr_t)hi->hi_addr + offset, nbytes);
537 			if (err) {
538 				free(buf, M_DEVBUF, 0);
539 				device_unref(&sc->sc_dv);
540 				return (err);
541 			}
542 			size -= nbytes;
543 			offset += nbytes;
544 		}
545 		break;
546 	case HVIOCWRITE:
547 		size = hi->hi_len;
548 		offset = 0;
549 		while (size > 0) {
550 			pmap_extract(pmap_kernel(), (vaddr_t)buf, &pa);
551 			nbytes = min(PAGE_SIZE, size);
552 			err = copyin((caddr_t)hi->hi_addr + offset, buf, nbytes);
553 			if (err) {
554 				free(buf, M_DEVBUF, 0);
555 				device_unref(&sc->sc_dv);
556 				return (err);
557 			}
558 			err = hv_ldc_copy(lc->lc_id, LDC_COPY_OUT,
559 			    hi->hi_cookie + offset, pa, nbytes, &nbytes);
560 			if (err != H_EOK) {
561 				printf("hv_ldc_copy %d\n", err);
562 				free(buf, M_DEVBUF, 0);
563 				device_unref(&sc->sc_dv);
564 				return (EINVAL);
565 			}
566 			size -= nbytes;
567 			offset += nbytes;
568 		}
569 		break;
570 
571 	}
572 
573 	free(buf, M_DEVBUF, 0);
574 
575 	device_unref(&sc->sc_dv);
576 	return (0);
577 }
578 
579 void
filt_vldcprdetach(struct knote * kn)580 filt_vldcprdetach(struct knote *kn)
581 {
582 	struct vldcp_softc *sc = (void *)kn->kn_hook;
583 	int s;
584 
585 	s = spltty();
586 	klist_remove_locked(&sc->sc_rsel.si_note, kn);
587 	splx(s);
588 }
589 
590 void
filt_vldcpwdetach(struct knote * kn)591 filt_vldcpwdetach(struct knote *kn)
592 {
593 	struct vldcp_softc *sc = (void *)kn->kn_hook;
594 	int s;
595 
596 	s = spltty();
597 	klist_remove_locked(&sc->sc_wsel.si_note, kn);
598 	splx(s);
599 }
600 
601 int
filt_vldcpread(struct knote * kn,long hint)602 filt_vldcpread(struct knote *kn, long hint)
603 {
604 	struct vldcp_softc *sc = (void *)kn->kn_hook;
605 	struct ldc_conn *lc = &sc->sc_lc;
606 	uint64_t head, tail, avail, state;
607 	int s, err;
608 
609 	s = spltty();
610 	err = hv_ldc_rx_get_state(lc->lc_id, &head, &tail, &state);
611 	if (err == 0 && state == LDC_CHANNEL_UP && head != tail) {
612 		avail = (tail - head) / sizeof(struct ldc_pkt) +
613 		    lc->lc_rxq->lq_nentries;
614 		avail %= lc->lc_rxq->lq_nentries;
615 		kn->kn_data = avail;
616 	} else {
617 		cbus_intr_setenabled(sc->sc_bustag, sc->sc_rx_ino,
618 		    INTR_ENABLED);
619 		kn->kn_data = 0;
620 	}
621 	splx(s);
622 
623 	return (kn->kn_data > 0);
624 }
625 
626 int
filt_vldcwrite(struct knote * kn,long hint)627 filt_vldcwrite(struct knote *kn, long hint)
628 {
629 	struct vldcp_softc *sc = (void *)kn->kn_hook;
630 	struct ldc_conn *lc = &sc->sc_lc;
631 	uint64_t head, tail, avail, state;
632 	int s, err;
633 
634 	s = spltty();
635 	err = hv_ldc_tx_get_state(lc->lc_id, &head, &tail, &state);
636 	if (err == 0 && state == LDC_CHANNEL_UP && head != tail) {
637 		avail = (head - tail) / sizeof(struct ldc_pkt) +
638 		    lc->lc_txq->lq_nentries - 1;
639 		avail %= lc->lc_txq->lq_nentries;
640 		kn->kn_data = avail;
641 	} else {
642 		cbus_intr_setenabled(sc->sc_bustag, sc->sc_tx_ino,
643 		    INTR_ENABLED);
644 		kn->kn_data = 0;
645 	}
646 	splx(s);
647 
648 	return (kn->kn_data > 0);
649 }
650 
651 const struct filterops vldcpread_filtops = {
652 	.f_flags	= FILTEROP_ISFD,
653 	.f_attach	= NULL,
654 	.f_detach	= filt_vldcprdetach,
655 	.f_event	= filt_vldcpread,
656 };
657 
658 const struct filterops vldcpwrite_filtops = {
659 	.f_flags	= FILTEROP_ISFD,
660 	.f_attach	= NULL,
661 	.f_detach	= filt_vldcpwdetach,
662 	.f_event	= filt_vldcwrite,
663 };
664 
665 int
vldcpkqfilter(dev_t dev,struct knote * kn)666 vldcpkqfilter(dev_t dev, struct knote *kn)
667 {
668 	struct vldcp_softc *sc;
669 	struct klist *klist;
670 	int s;
671 
672 	sc = vldcp_lookup(dev);
673 	if (sc == NULL)
674 		return (ENXIO);
675 
676 	switch (kn->kn_filter) {
677 	case EVFILT_READ:
678 		klist = &sc->sc_rsel.si_note;
679 		kn->kn_fop = &vldcpread_filtops;
680 		break;
681 	case EVFILT_WRITE:
682 		klist = &sc->sc_wsel.si_note;
683 		kn->kn_fop = &vldcpwrite_filtops;
684 		break;
685 
686 	default:
687 		device_unref(&sc->sc_dv);
688 		return (EINVAL);
689 	}
690 
691 	kn->kn_hook = sc;
692 
693 	s = spltty();
694 	klist_insert_locked(klist, kn);
695 	splx(s);
696 
697 	device_unref(&sc->sc_dv);
698 	return (0);
699 }
700