xref: /illumos-gate/usr/src/uts/sun4v/io/vldc.c (revision d3d50737)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 
28 #include <sys/types.h>
29 #include <sys/file.h>
30 #include <sys/errno.h>
31 #include <sys/uio.h>
32 #include <sys/open.h>
33 #include <sys/cred.h>
34 #include <sys/kmem.h>
35 #include <sys/conf.h>
36 #include <sys/cmn_err.h>
37 #include <sys/ksynch.h>
38 #include <sys/modctl.h>
39 #include <sys/stat.h>			/* needed for S_IFBLK and S_IFCHR */
40 #include <sys/debug.h>
41 #include <sys/sysmacros.h>
42 #include <sys/types.h>
43 #include <sys/cred.h>
44 #include <sys/promif.h>
45 #include <sys/ddi.h>
46 #include <sys/sunddi.h>
47 #include <sys/cyclic.h>
48 #include <sys/note.h>
49 #include <sys/mach_descrip.h>
50 #include <sys/mdeg.h>
51 #include <sys/ldc.h>
52 #include <sys/vldc_impl.h>
53 
54 /*
55  * Function prototypes.
56  */
57 
58 /* DDI entrypoints */
59 static int vldc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
60 static int vldc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
61 static int vldc_open(dev_t *devp, int flag, int otyp, cred_t *cred);
62 static int vldc_close(dev_t dev, int flag, int otyp, cred_t *cred);
63 static int vldc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
64     cred_t *credp, int *rvalp);
65 static int vldc_read(dev_t dev, struct uio *uiop, cred_t *credp);
66 static int vldc_write(dev_t dev, struct uio *uiop, cred_t *credp);
67 static int vldc_chpoll(dev_t dev, short events, int anyyet,
68     short *reventsp, struct pollhead **phpp);
69 
70 /* Internal functions */
71 static uint_t i_vldc_cb(uint64_t event, caddr_t arg);
72 static int i_vldc_mdeg_cb(void *cb_argp, mdeg_result_t *resp);
73 static int i_vldc_mdeg_register(vldc_t *vldcp);
74 static int i_vldc_mdeg_unregister(vldc_t *vldcp);
75 static int i_vldc_add_port(vldc_t *vldcp, md_t *mdp, mde_cookie_t node);
76 static int i_vldc_remove_port(vldc_t *vldcp, uint_t portno);
77 static int i_vldc_close_port(vldc_t *vldcp, uint_t portno);
78 
79 /* soft state structure */
80 static void *vldc_ssp;
81 
82 /*
83  * Matching criteria passed to the MDEG to register interest
84  * in changes to 'virtual-device-port' nodes identified by their
85  * 'id' property.
86  */
87 static md_prop_match_t vport_prop_match[] = {
88 	{ MDET_PROP_VAL,    "id"   },
89 	{ MDET_LIST_END,    NULL    }
90 };
91 
92 static mdeg_node_match_t vport_match = { "virtual-device-port",
93 					vport_prop_match };
94 
95 /*
96  * Specification of an MD node passed to the MDEG to filter any
97  * 'virtual-device-port' nodes that do not belong to the specified
98  * node. This template is copied for each vldc instance and filled
99  * in with the appropriate 'name' and 'cfg-handle' values before
100  * being passed to the MDEG.
101  */
102 static mdeg_prop_spec_t vldc_prop_template[] = {
103 	{ MDET_PROP_STR,    "name",		NULL	},
104 	{ MDET_PROP_VAL,    "cfg-handle",	NULL    },
105 	{ MDET_LIST_END,    NULL,		NULL    }
106 };
107 
108 #define	VLDC_MDEG_PROP_NAME(specp)		((specp)[0].ps_str)
109 #define	VLDC_SET_MDEG_PROP_NAME(specp, name)	((specp)[0].ps_str = (name))
110 #define	VLDC_SET_MDEG_PROP_INST(specp, inst)	((specp)[1].ps_val = (inst))
111 
112 
113 static struct cb_ops vldc_cb_ops = {
114 	vldc_open,	/* open */
115 	vldc_close,	/* close */
116 	nodev,		/* strategy */
117 	nodev,		/* print */
118 	nodev,		/* dump */
119 	vldc_read,	/* read */
120 	vldc_write,	/* write */
121 	vldc_ioctl,	/* ioctl */
122 	nodev,		/* devmap */
123 	nodev,		/* mmap */
124 	ddi_segmap,	/* segmap */
125 	vldc_chpoll,	/* chpoll */
126 	ddi_prop_op,	/* prop_op */
127 	NULL,		/* stream */
128 	D_NEW | D_MP	/* flag */
129 };
130 
131 static struct dev_ops vldc_ops = {
132 	DEVO_REV,		/* rev */
133 	0,			/* ref count */
134 	ddi_getinfo_1to1,	/* getinfo */
135 	nulldev,		/* identify */
136 	nulldev,		/* probe */
137 	vldc_attach,		/* attach */
138 	vldc_detach,		/* detach */
139 	nodev,			/* reset */
140 	&vldc_cb_ops,		/* cb_ops */
141 	(struct bus_ops *)NULL,	/* bus_ops */
142 	NULL,			/* power */
143 	ddi_quiesce_not_needed,		/* quiesce */
144 };
145 
146 extern struct mod_ops mod_driverops;
147 
148 static struct modldrv md = {
149 	&mod_driverops, 			/* Type - it is a driver */
150 	"sun4v Virtual LDC Driver",		/* Name of the module */
151 	&vldc_ops,				/* driver specific ops */
152 };
153 
154 static struct modlinkage ml = {
155 	MODREV_1,
156 	&md,
157 	NULL
158 };
159 
160 /* maximum MTU and cookie size tunables */
161 uint32_t vldc_max_mtu = VLDC_MAX_MTU;
162 uint64_t vldc_max_cookie = VLDC_MAX_COOKIE;
163 
164 /*
165  * when ldc_close() returns EAGAIN, it is retried with a wait
166  * of 'vldc_close_delay' between each retry.
167  */
168 static clock_t	vldc_close_delay = VLDC_CLOSE_DELAY;
169 
170 #ifdef DEBUG
171 
172 /*
173  * Print debug messages
174  *
175  * set vldcdbg to 0x7 to enable all messages
176  *
177  * 0x4 - Warnings
178  * 0x2 - All debug messages (most verbose)
179  * 0x1 - Minimal debug messages
180  */
181 
182 int vldcdbg = 0x0;
183 
184 static void
vldcdebug(const char * fmt,...)185 vldcdebug(const char *fmt, ...)
186 {
187 	char buf[512];
188 	va_list ap;
189 
190 	va_start(ap, fmt);
191 	(void) vsnprintf(buf, sizeof (buf), fmt, ap);
192 	va_end(ap);
193 
194 	cmn_err(CE_CONT, "?%s", buf);
195 }
196 
197 #define	D1	if (vldcdbg & 0x01) vldcdebug
198 #define	D2	if (vldcdbg & 0x02) vldcdebug
199 #define	DWARN	if (vldcdbg & 0x04) vldcdebug
200 
201 #else /* not DEBUG */
202 
203 #define	D1	if (0) printf
204 #define	D2	if (0) printf
205 #define	DWARN	if (0) printf
206 
207 #endif /* not DEBUG */
208 
209 
210 /* _init(9E): initialize the loadable module */
211 int
_init(void)212 _init(void)
213 {
214 	int error;
215 
216 	/* init the soft state structure */
217 	error = ddi_soft_state_init(&vldc_ssp, sizeof (vldc_t), 1);
218 	if (error != 0) {
219 		return (error);
220 	}
221 
222 	/* Link the driver into the system */
223 	error = mod_install(&ml);
224 
225 	return (error);
226 }
227 
228 /* _info(9E): return information about the loadable module */
229 int
_info(struct modinfo * modinfop)230 _info(struct modinfo *modinfop)
231 {
232 	/* Report status of the dynamically loadable driver module */
233 	return (mod_info(&ml, modinfop));
234 }
235 
236 /* _fini(9E): prepare the module for unloading. */
237 int
_fini(void)238 _fini(void)
239 {
240 	int error;
241 
242 	/* Unlink the driver module from the system */
243 	if ((error = mod_remove(&ml)) == 0) {
244 		/*
245 		 * We have successfully "removed" the driver.
246 		 * destroy soft state
247 		 */
248 		ddi_soft_state_fini(&vldc_ssp);
249 	}
250 
251 	return (error);
252 }
253 
254 /* ldc callback */
255 static uint_t
i_vldc_cb(uint64_t event,caddr_t arg)256 i_vldc_cb(uint64_t event, caddr_t arg)
257 {
258 	int 		rv;
259 	vldc_port_t	*vport = (vldc_port_t *)arg;
260 	ldc_status_t	old_status;
261 	short		pollevents = 0;
262 
263 	ASSERT(vport != NULL);
264 	ASSERT(vport->minorp != NULL);
265 
266 	D1("i_vldc_cb: vldc@%d:%d callback invoked, channel=0x%lx, "
267 	    "event=0x%lx\n", vport->inst, vport->number, vport->ldc_id, event);
268 
269 	/* ensure the port can't be destroyed while we are handling the cb */
270 	mutex_enter(&vport->minorp->lock);
271 
272 	if (vport->status == VLDC_PORT_CLOSED) {
273 		return (LDC_SUCCESS);
274 	}
275 
276 	old_status = vport->ldc_status;
277 	rv = ldc_status(vport->ldc_handle, &vport->ldc_status);
278 	if (rv != 0) {
279 		DWARN("i_vldc_cb: vldc@%d:%d could not get ldc status, "
280 		    "rv=%d\n", vport->inst, vport->number, rv);
281 		mutex_exit(&vport->minorp->lock);
282 		return (LDC_SUCCESS);
283 	}
284 
285 	if (event & LDC_EVT_UP) {
286 		pollevents |= POLLOUT;
287 		vport->hanged_up = B_FALSE;
288 
289 	} else if (event & LDC_EVT_RESET) {
290 		/*
291 		 * Mark the port in reset, if it is not CLOSED and
292 		 * the channel was previously in LDC_UP state. This
293 		 * implies that the port cannot be used until it has
294 		 * been closed and reopened.
295 		 */
296 		if (old_status == LDC_UP) {
297 			vport->status = VLDC_PORT_RESET;
298 			vport->hanged_up = B_TRUE;
299 			pollevents = POLLHUP;
300 		} else {
301 			rv = ldc_up(vport->ldc_handle);
302 			if (rv) {
303 				DWARN("i_vldc_cb: vldc@%d:%d cannot bring "
304 				    "channel UP rv=%d\n", vport->inst,
305 				    vport->number, rv);
306 				mutex_exit(&vport->minorp->lock);
307 				return (LDC_SUCCESS);
308 			}
309 			rv = ldc_status(vport->ldc_handle, &vport->ldc_status);
310 			if (rv != 0) {
311 				DWARN("i_vldc_cb: vldc@%d:%d could not get "
312 				    "ldc status, rv=%d\n", vport->inst,
313 				    vport->number, rv);
314 				mutex_exit(&vport->minorp->lock);
315 				return (LDC_SUCCESS);
316 			}
317 			if (vport->ldc_status == LDC_UP) {
318 				pollevents |= POLLOUT;
319 				vport->hanged_up = B_FALSE;
320 			}
321 		}
322 
323 	} else if (event & LDC_EVT_DOWN) {
324 		/*
325 		 * The other side went away - mark port in RESET state
326 		 */
327 		vport->status = VLDC_PORT_RESET;
328 		vport->hanged_up = B_TRUE;
329 		pollevents = POLLHUP;
330 	}
331 
332 	if (event & LDC_EVT_READ)
333 		pollevents |= POLLIN;
334 
335 	mutex_exit(&vport->minorp->lock);
336 
337 	if (pollevents != 0) {
338 		D1("i_vldc_cb: port@%d pollwakeup=0x%x\n",
339 		    vport->number, pollevents);
340 		pollwakeup(&vport->poll, pollevents);
341 	}
342 
343 	return (LDC_SUCCESS);
344 }
345 
346 /* mdeg callback */
347 static int
i_vldc_mdeg_cb(void * cb_argp,mdeg_result_t * resp)348 i_vldc_mdeg_cb(void *cb_argp, mdeg_result_t *resp)
349 {
350 	vldc_t		*vldcp;
351 	int		idx;
352 	uint64_t	portno;
353 	int		rv;
354 	md_t		*mdp;
355 	mde_cookie_t	node;
356 
357 	if (resp == NULL) {
358 		D1("i_vldc_mdeg_cb: no result returned\n");
359 		return (MDEG_FAILURE);
360 	}
361 
362 	vldcp = (vldc_t *)cb_argp;
363 
364 	mutex_enter(&vldcp->lock);
365 	if (vldcp->detaching == B_TRUE) {
366 		D1("i_vldc_mdeg_cb: detach in progress\n");
367 		mutex_exit(&vldcp->lock);
368 		return (MDEG_FAILURE);
369 	}
370 
371 	D1("i_vldc_mdeg_cb: added=%d, removed=%d, matched=%d\n",
372 	    resp->added.nelem, resp->removed.nelem, resp->match_prev.nelem);
373 
374 	/* process added ports */
375 	for (idx = 0; idx < resp->added.nelem; idx++) {
376 		mdp = resp->added.mdp;
377 		node = resp->added.mdep[idx];
378 
379 		D1("i_vldc_mdeg_cb: processing added node 0x%lx\n", node);
380 
381 		/* attempt to add a port */
382 		if ((rv = i_vldc_add_port(vldcp, mdp, node)) != MDEG_SUCCESS) {
383 			cmn_err(CE_NOTE, "?i_vldc_mdeg_cb: unable to add port, "
384 			    "err = %d", rv);
385 		}
386 	}
387 
388 	/* process removed ports */
389 	for (idx = 0; idx < resp->removed.nelem; idx++) {
390 		mdp = resp->removed.mdp;
391 		node = resp->removed.mdep[idx];
392 
393 		D1("i_vldc_mdeg_cb: processing removed node 0x%lx\n", node);
394 
395 		/* read in the port's id property */
396 		if (md_get_prop_val(mdp, node, "id", &portno)) {
397 			cmn_err(CE_NOTE, "?i_vldc_mdeg_cb: node 0x%lx of "
398 			    "removed list has no 'id' property", node);
399 			continue;
400 		}
401 
402 		/* attempt to remove a port */
403 		if ((rv = i_vldc_remove_port(vldcp, portno)) != 0) {
404 			cmn_err(CE_NOTE, "?i_vldc_mdeg_cb: unable to remove "
405 			    "port %lu, err %d", portno, rv);
406 		}
407 	}
408 
409 	/*
410 	 * Currently no support for updating already active ports. So, ignore
411 	 * the match_curr and match_prev arrays for now.
412 	 */
413 
414 	mutex_exit(&vldcp->lock);
415 
416 	return (MDEG_SUCCESS);
417 }
418 
419 /* register callback to mdeg */
420 static int
i_vldc_mdeg_register(vldc_t * vldcp)421 i_vldc_mdeg_register(vldc_t *vldcp)
422 {
423 	mdeg_prop_spec_t *pspecp;
424 	mdeg_node_spec_t *inst_specp;
425 	mdeg_handle_t	mdeg_hdl;
426 	size_t		templatesz;
427 	int		inst;
428 	char		*name;
429 	size_t		namesz;
430 	char		*nameprop;
431 	int		rv;
432 
433 	/* get the unique vldc instance assigned by the LDom manager */
434 	inst = ddi_prop_get_int(DDI_DEV_T_ANY, vldcp->dip,
435 	    DDI_PROP_DONTPASS, "reg", -1);
436 	if (inst == -1) {
437 		cmn_err(CE_NOTE, "?vldc%d has no 'reg' property",
438 		    ddi_get_instance(vldcp->dip));
439 		return (DDI_FAILURE);
440 	}
441 
442 	/* get the name of the vldc instance */
443 	rv = ddi_prop_lookup_string(DDI_DEV_T_ANY, vldcp->dip,
444 	    DDI_PROP_DONTPASS, "name", &nameprop);
445 	if (rv != DDI_PROP_SUCCESS) {
446 		cmn_err(CE_NOTE, "?vldc%d has no 'name' property",
447 		    ddi_get_instance(vldcp->dip));
448 		return (DDI_FAILURE);
449 	}
450 
451 	D1("i_vldc_mdeg_register: name=%s, instance=%d\n", nameprop, inst);
452 
453 	/*
454 	 * Allocate and initialize a per-instance copy
455 	 * of the global property spec array that will
456 	 * uniquely identify this vldc instance.
457 	 */
458 	templatesz = sizeof (vldc_prop_template);
459 	pspecp = kmem_alloc(templatesz, KM_SLEEP);
460 
461 	bcopy(vldc_prop_template, pspecp, templatesz);
462 
463 	/* copy in the name property */
464 	namesz = strlen(nameprop) + 1;
465 	name = kmem_alloc(namesz, KM_SLEEP);
466 
467 	bcopy(nameprop, name, namesz);
468 	VLDC_SET_MDEG_PROP_NAME(pspecp, name);
469 	ddi_prop_free(nameprop);
470 
471 	/* copy in the instance property */
472 	VLDC_SET_MDEG_PROP_INST(pspecp, inst);
473 
474 	/* initialize the complete prop spec structure */
475 	inst_specp = kmem_alloc(sizeof (mdeg_node_spec_t), KM_SLEEP);
476 	inst_specp->namep = "virtual-device";
477 	inst_specp->specp = pspecp;
478 
479 	/* perform the registration */
480 	rv = mdeg_register(inst_specp, &vport_match, i_vldc_mdeg_cb,
481 	    vldcp, &mdeg_hdl);
482 
483 	if (rv != MDEG_SUCCESS) {
484 		cmn_err(CE_NOTE, "?i_vldc_mdeg_register: mdeg_register "
485 		    "failed, err = %d", rv);
486 		kmem_free(name, namesz);
487 		kmem_free(pspecp, templatesz);
488 		kmem_free(inst_specp, sizeof (mdeg_node_spec_t));
489 		return (DDI_FAILURE);
490 	}
491 
492 	/* save off data that will be needed later */
493 	vldcp->inst_spec = inst_specp;
494 	vldcp->mdeg_hdl = mdeg_hdl;
495 
496 	return (DDI_SUCCESS);
497 }
498 
499 /* unregister callback from mdeg */
500 static int
i_vldc_mdeg_unregister(vldc_t * vldcp)501 i_vldc_mdeg_unregister(vldc_t *vldcp)
502 {
503 	char	*name;
504 	int	rv;
505 
506 	D1("i_vldc_mdeg_unregister: hdl=0x%lx\n", vldcp->mdeg_hdl);
507 
508 	rv = mdeg_unregister(vldcp->mdeg_hdl);
509 	if (rv != MDEG_SUCCESS) {
510 		return (rv);
511 	}
512 
513 	/*
514 	 * Clean up cached MDEG data
515 	 */
516 	name = VLDC_MDEG_PROP_NAME(vldcp->inst_spec->specp);
517 	if (name != NULL) {
518 		kmem_free(name, strlen(name) + 1);
519 	}
520 	kmem_free(vldcp->inst_spec->specp, sizeof (vldc_prop_template));
521 	vldcp->inst_spec->specp = NULL;
522 
523 	kmem_free(vldcp->inst_spec, sizeof (mdeg_node_spec_t));
524 	vldcp->inst_spec = NULL;
525 
526 	return (MDEG_SUCCESS);
527 }
528 
529 static int
i_vldc_get_port_channel(md_t * mdp,mde_cookie_t node,uint64_t * ldc_id)530 i_vldc_get_port_channel(md_t *mdp, mde_cookie_t node, uint64_t *ldc_id)
531 {
532 	int num_nodes, nchan;
533 	size_t listsz;
534 	mde_cookie_t *listp;
535 
536 	/*
537 	 * Find the channel-endpoint node(s) (which should be under this
538 	 * port node) which contain the channel id(s).
539 	 */
540 	if ((num_nodes = md_node_count(mdp)) <= 0) {
541 		cmn_err(CE_NOTE, "?i_vldc_get_port_channel: invalid number of "
542 		    "channel-endpoint nodes found (%d)", num_nodes);
543 		return (-1);
544 	}
545 
546 	/* allocate space for node list */
547 	listsz = num_nodes * sizeof (mde_cookie_t);
548 	listp = kmem_alloc(listsz, KM_SLEEP);
549 
550 	nchan = md_scan_dag(mdp, node, md_find_name(mdp, "channel-endpoint"),
551 	    md_find_name(mdp, "fwd"), listp);
552 
553 	if (nchan <= 0) {
554 		cmn_err(CE_NOTE, "?i_vldc_get_port_channel: no channel-endpoint"
555 		    " nodes found");
556 		kmem_free(listp, listsz);
557 		return (-1);
558 	}
559 
560 	D2("i_vldc_get_port_channel: %d channel-endpoint nodes found", nchan);
561 
562 	/* use property from first node found */
563 	if (md_get_prop_val(mdp, listp[0], "id", ldc_id)) {
564 		cmn_err(CE_NOTE, "?i_vldc_get_port_channel: channel-endpoint "
565 		    "has no 'id' property");
566 		kmem_free(listp, listsz);
567 		return (-1);
568 	}
569 
570 	kmem_free(listp, listsz);
571 
572 	return (0);
573 }
574 
575 /* add a vldc port */
576 static int
i_vldc_add_port(vldc_t * vldcp,md_t * mdp,mde_cookie_t node)577 i_vldc_add_port(vldc_t *vldcp, md_t *mdp, mde_cookie_t node)
578 {
579 	vldc_port_t	*vport;
580 	char		*sname;
581 	uint64_t	portno;
582 	int		vldc_inst;
583 	minor_t		minor;
584 	int		minor_idx;
585 	boolean_t	new_minor;
586 	int		rv;
587 
588 	ASSERT(MUTEX_HELD(&vldcp->lock));
589 
590 	/* read in the port's id property */
591 	if (md_get_prop_val(mdp, node, "id", &portno)) {
592 		cmn_err(CE_NOTE, "?i_vldc_add_port: node 0x%lx of added "
593 		    "list has no 'id' property", node);
594 		return (MDEG_FAILURE);
595 	}
596 
597 	if (portno >= VLDC_MAX_PORTS) {
598 		cmn_err(CE_NOTE, "?i_vldc_add_port: found port number (%lu) "
599 		    "larger than maximum supported number of ports", portno);
600 		return (MDEG_FAILURE);
601 	}
602 
603 	vport = &(vldcp->port[portno]);
604 
605 	if (vport->minorp != NULL) {
606 		cmn_err(CE_NOTE, "?i_vldc_add_port: trying to add a port (%lu)"
607 		    " which is already bound", portno);
608 		return (MDEG_FAILURE);
609 	}
610 
611 	vport->number = portno;
612 
613 	/* get all channels for this device (currently only one) */
614 	if (i_vldc_get_port_channel(mdp, node, &vport->ldc_id) == -1) {
615 		return (MDEG_FAILURE);
616 	}
617 
618 	/* set the default MTU */
619 	vport->mtu = VLDC_DEFAULT_MTU;
620 
621 	/* get the service being exported by this port */
622 	if (md_get_prop_str(mdp, node, "vldc-svc-name", &sname)) {
623 		cmn_err(CE_NOTE, "?i_vldc_add_port: vdevice has no "
624 		    "'vldc-svc-name' property");
625 		return (MDEG_FAILURE);
626 	}
627 
628 	/* minor number look up */
629 	for (minor_idx = 0; minor_idx < vldcp->minors_assigned;
630 	    minor_idx++) {
631 		if (strcmp(vldcp->minor_tbl[minor_idx].sname, sname) == 0) {
632 			/* found previously assigned minor number */
633 			break;
634 		}
635 	}
636 
637 	new_minor = B_FALSE;
638 	if (minor_idx == vldcp->minors_assigned) {
639 		/* end of lookup - assign new minor number */
640 		if (vldcp->minors_assigned == VLDC_MAX_MINORS) {
641 			cmn_err(CE_NOTE, "?i_vldc_add_port: too many minor "
642 			    "nodes (%d)", minor_idx);
643 			return (MDEG_FAILURE);
644 		}
645 
646 		(void) strlcpy(vldcp->minor_tbl[minor_idx].sname,
647 		    sname, MAXPATHLEN);
648 
649 		vldcp->minors_assigned++;
650 		new_minor = B_TRUE;
651 	}
652 
653 	if (vldcp->minor_tbl[minor_idx].portno != VLDC_INVALID_PORTNO) {
654 		cmn_err(CE_NOTE, "?i_vldc_add_port: trying to add a port (%lu)"
655 		    " which has a minor number in use by port (%u)",
656 		    portno, vldcp->minor_tbl[minor_idx].portno);
657 		return (MDEG_FAILURE);
658 	}
659 
660 	vldc_inst = ddi_get_instance(vldcp->dip);
661 
662 	vport->inst = vldc_inst;
663 	vport->minorp = &vldcp->minor_tbl[minor_idx];
664 	vldcp->minor_tbl[minor_idx].portno = portno;
665 	vldcp->minor_tbl[minor_idx].in_use = 0;
666 
667 	D1("i_vldc_add_port: vldc@%d:%d  mtu=%d, ldc=%ld, service=%s\n",
668 	    vport->inst, vport->number, vport->mtu, vport->ldc_id, sname);
669 
670 	/*
671 	 * Create a minor node. The minor number is
672 	 * (vldc_inst << VLDC_INST_SHIFT) | minor_idx
673 	 */
674 	minor = (vldc_inst << VLDC_INST_SHIFT) | (minor_idx);
675 
676 	rv = ddi_create_minor_node(vldcp->dip, sname, S_IFCHR,
677 	    minor, DDI_NT_SERIAL, 0);
678 
679 	if (rv != DDI_SUCCESS) {
680 		cmn_err(CE_NOTE, "?i_vldc_add_port: failed to create minor"
681 		    "node (%u), err = %d", minor, rv);
682 		vldcp->minor_tbl[minor_idx].portno = VLDC_INVALID_PORTNO;
683 		if (new_minor) {
684 			vldcp->minors_assigned--;
685 		}
686 		return (MDEG_FAILURE);
687 	}
688 
689 	/*
690 	 * The port is now bound to a minor node and is initially in the
691 	 * closed state.
692 	 */
693 	vport->status = VLDC_PORT_CLOSED;
694 
695 	D1("i_vldc_add_port: port %lu initialized\n", portno);
696 
697 	return (MDEG_SUCCESS);
698 }
699 
700 /* remove a vldc port */
701 static int
i_vldc_remove_port(vldc_t * vldcp,uint_t portno)702 i_vldc_remove_port(vldc_t *vldcp, uint_t portno)
703 {
704 	vldc_port_t *vport;
705 	vldc_minor_t *vminor;
706 
707 	ASSERT(vldcp != NULL);
708 	ASSERT(MUTEX_HELD(&vldcp->lock));
709 
710 	vport = &(vldcp->port[portno]);
711 	vminor = vport->minorp;
712 	if (vminor == NULL) {
713 		cmn_err(CE_NOTE, "?i_vldc_remove_port: trying to remove a "
714 		    "port (%u) which is not bound", portno);
715 		return (MDEG_FAILURE);
716 	}
717 
718 	/*
719 	 * Make sure that all new attempts to open or use the minor node
720 	 * associated with the port will fail.
721 	 */
722 	mutex_enter(&vminor->lock);
723 	vminor->portno = VLDC_INVALID_PORTNO;
724 	mutex_exit(&vminor->lock);
725 
726 	/* send hangup to anyone polling */
727 	pollwakeup(&vport->poll, POLLHUP);
728 
729 	/* Now wait for all current users of the minor node to finish. */
730 	mutex_enter(&vminor->lock);
731 	while (vminor->in_use > 0) {
732 		cv_wait(&vminor->cv, &vminor->lock);
733 	}
734 
735 	if (vport->status != VLDC_PORT_CLOSED) {
736 		/* close the port before it is torn down */
737 		(void) i_vldc_close_port(vldcp, portno);
738 	}
739 
740 	/* remove minor node */
741 	ddi_remove_minor_node(vldcp->dip, vport->minorp->sname);
742 	vport->minorp = NULL;
743 
744 	mutex_exit(&vminor->lock);
745 
746 	D1("i_vldc_remove_port: removed vldc port %u\n", portno);
747 
748 	return (MDEG_SUCCESS);
749 }
750 
751 /*
752  * Close and destroy the ldc channel associated with the port 'vport'
753  *
754  * NOTE It may not be possible close and destroy the channel if resources
755  *	are still in use so the fucntion may exit before all the teardown
756  *	operations are completed and would have to be called again by the
757  *	vldc framework.
758  *
759  *	This function needs to be able to handle the case where it is called
760  *	more than once and has to pick up from where it left off.
761  */
762 static int
i_vldc_ldc_close(vldc_port_t * vport)763 i_vldc_ldc_close(vldc_port_t *vport)
764 {
765 	int err = 0;
766 
767 	ASSERT(MUTEX_HELD(&vport->minorp->lock));
768 
769 	/*
770 	 * If ldc_close() succeeded or if the channel was already closed[*]
771 	 * (possibly by a previously unsuccessful call to this function)
772 	 * we keep going and try to teardown the rest of the LDC state,
773 	 * otherwise we bail out.
774 	 *
775 	 * [*] indicated by ldc_close() returning a value of EFAULT
776 	 */
777 	err = ldc_close(vport->ldc_handle);
778 	if ((err != 0) && (err != EFAULT))
779 		return (err);
780 
781 	err = ldc_unreg_callback(vport->ldc_handle);
782 	if (err != 0)
783 		return (err);
784 
785 	err = ldc_fini(vport->ldc_handle);
786 	if (err != 0)
787 		return (err);
788 
789 	vport->status = VLDC_PORT_OPEN;
790 
791 	return (0);
792 }
793 
794 /* close a vldc port */
795 static int
i_vldc_close_port(vldc_t * vldcp,uint_t portno)796 i_vldc_close_port(vldc_t *vldcp, uint_t portno)
797 {
798 	vldc_port_t	*vport;
799 	vldc_minor_t	*vminor;
800 	int		rv = DDI_SUCCESS;
801 
802 	vport = &(vldcp->port[portno]);
803 
804 	ASSERT(MUTEX_HELD(&vport->minorp->lock));
805 
806 	D1("i_vldc_close_port: vldc@%d:%d: closing port\n",
807 	    vport->inst, vport->minorp->portno);
808 
809 	vminor = vport->minorp;
810 
811 	switch (vport->status) {
812 	case VLDC_PORT_CLOSED:
813 		/* nothing to do */
814 		DWARN("i_vldc_close_port: port %d in an unexpected "
815 		    "state (%d)\n", portno, vport->status);
816 		return (DDI_SUCCESS);
817 
818 	case VLDC_PORT_READY:
819 	case VLDC_PORT_RESET:
820 		do {
821 			rv = i_vldc_ldc_close(vport);
822 			if (rv != EAGAIN)
823 				break;
824 
825 			/*
826 			 * EAGAIN indicates that ldc_close() failed because
827 			 * ldc callback thread is active for the channel.
828 			 * cv_timedwait() is used to release vminor->lock and
829 			 * allow ldc callback thread to complete.
830 			 * after waking up, check if the port has been closed
831 			 * by another thread in the meantime.
832 			 */
833 			(void) cv_reltimedwait(&vminor->cv, &vminor->lock,
834 			    drv_usectohz(vldc_close_delay), TR_CLOCK_TICK);
835 			rv = 0;
836 		} while (vport->status != VLDC_PORT_CLOSED);
837 
838 		if ((rv != 0) || (vport->status == VLDC_PORT_CLOSED))
839 			return (rv);
840 
841 		break;
842 
843 	case VLDC_PORT_OPEN:
844 		break;
845 
846 	default:
847 		DWARN("i_vldc_close_port: port %d in an unexpected "
848 		    "state (%d)\n", portno, vport->status);
849 		ASSERT(0);	/* fail quickly to help diagnosis */
850 		return (EINVAL);
851 	}
852 
853 	ASSERT(vport->status == VLDC_PORT_OPEN);
854 
855 	/* free memory */
856 	kmem_free(vport->send_buf, vport->mtu);
857 	kmem_free(vport->recv_buf, vport->mtu);
858 
859 	if (strcmp(vminor->sname, VLDC_HVCTL_SVCNAME) == 0)
860 		kmem_free(vport->cookie_buf, vldc_max_cookie);
861 
862 	vport->status = VLDC_PORT_CLOSED;
863 
864 	return (rv);
865 }
866 
867 /*
868  * attach(9E): attach a device to the system.
869  * called once for each instance of the device on the system.
870  */
871 static int
vldc_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)872 vldc_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
873 {
874 	int 	i, instance;
875 	vldc_t	*vldcp;
876 
877 	switch (cmd) {
878 
879 	case DDI_ATTACH:
880 
881 		instance = ddi_get_instance(dip);
882 
883 		if (ddi_soft_state_zalloc(vldc_ssp, instance) != DDI_SUCCESS) {
884 			return (DDI_FAILURE);
885 		}
886 
887 		vldcp = ddi_get_soft_state(vldc_ssp, instance);
888 		if (vldcp == NULL) {
889 			ddi_soft_state_free(vldc_ssp, instance);
890 			return (ENXIO);
891 		}
892 
893 		D1("vldc_attach: DDI_ATTACH instance=%d\n", instance);
894 
895 		mutex_init(&vldcp->lock, NULL, MUTEX_DRIVER, NULL);
896 		vldcp->dip = dip;
897 		vldcp->detaching = B_FALSE;
898 
899 		for (i = 0; i < VLDC_MAX_PORTS; i++) {
900 			/* No minor node association to start with */
901 			vldcp->port[i].minorp = NULL;
902 		}
903 
904 		for (i = 0; i < VLDC_MAX_MINORS; i++) {
905 			mutex_init(&(vldcp->minor_tbl[i].lock), NULL,
906 			    MUTEX_DRIVER, NULL);
907 			cv_init(&(vldcp->minor_tbl[i].cv), NULL,
908 			    CV_DRIVER, NULL);
909 			/* No port association to start with */
910 			vldcp->minor_tbl[i].portno = VLDC_INVALID_PORTNO;
911 		}
912 
913 		/* Register for MD update notification */
914 		if (i_vldc_mdeg_register(vldcp) != DDI_SUCCESS) {
915 			ddi_soft_state_free(vldc_ssp, instance);
916 			return (DDI_FAILURE);
917 		}
918 
919 		return (DDI_SUCCESS);
920 
921 	case DDI_RESUME:
922 
923 		return (DDI_SUCCESS);
924 
925 	default:
926 
927 		return (DDI_FAILURE);
928 	}
929 }
930 
931 /*
932  * detach(9E): detach a device from the system.
933  */
934 static int
vldc_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)935 vldc_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
936 {
937 	int 		i, instance;
938 	vldc_t		*vldcp;
939 
940 	switch (cmd) {
941 
942 	case DDI_DETACH:
943 
944 		instance = ddi_get_instance(dip);
945 
946 		vldcp = ddi_get_soft_state(vldc_ssp, instance);
947 		if (vldcp == NULL) {
948 			return (DDI_FAILURE);
949 		}
950 
951 		D1("vldc_detach: DDI_DETACH instance=%d\n", instance);
952 
953 		mutex_enter(&vldcp->lock);
954 
955 		/* Fail the detach if all ports have not been removed. */
956 		for (i = 0; i < VLDC_MAX_MINORS; i++) {
957 			if (vldcp->minor_tbl[i].portno != VLDC_INVALID_PORTNO) {
958 				D1("vldc_detach: vldc@%d:%d is bound, "
959 				    "detach failed\n",
960 				    instance, vldcp->minor_tbl[i].portno);
961 				mutex_exit(&vldcp->lock);
962 				return (DDI_FAILURE);
963 			}
964 		}
965 
966 		/*
967 		 * Prevent MDEG from adding new ports before the callback can
968 		 * be unregistered. The lock can't be held accross the
969 		 * unregistration call because a callback may be in progress
970 		 * and blocked on the lock.
971 		 */
972 		vldcp->detaching = B_TRUE;
973 
974 		mutex_exit(&vldcp->lock);
975 
976 		if (i_vldc_mdeg_unregister(vldcp) != MDEG_SUCCESS) {
977 			vldcp->detaching = B_FALSE;
978 			return (DDI_FAILURE);
979 		}
980 
981 		/* Tear down all bound ports and free resources. */
982 		for (i = 0; i < VLDC_MAX_MINORS; i++) {
983 			if (vldcp->minor_tbl[i].portno != VLDC_INVALID_PORTNO) {
984 				(void) i_vldc_remove_port(vldcp, i);
985 			}
986 			mutex_destroy(&(vldcp->minor_tbl[i].lock));
987 			cv_destroy(&(vldcp->minor_tbl[i].cv));
988 		}
989 
990 		mutex_destroy(&vldcp->lock);
991 		ddi_soft_state_free(vldc_ssp, instance);
992 
993 		return (DDI_SUCCESS);
994 
995 	case DDI_SUSPEND:
996 
997 		return (DDI_SUCCESS);
998 
999 	default:
1000 
1001 		return (DDI_FAILURE);
1002 	}
1003 }
1004 
1005 /* cb_open */
1006 static int
vldc_open(dev_t * devp,int flag,int otyp,cred_t * cred)1007 vldc_open(dev_t *devp, int flag, int otyp, cred_t *cred)
1008 {
1009 	_NOTE(ARGUNUSED(flag, otyp, cred))
1010 
1011 	int instance;
1012 	minor_t minor;
1013 	uint64_t portno;
1014 	vldc_t *vldcp;
1015 	vldc_port_t *vport;
1016 	vldc_minor_t *vminor;
1017 
1018 	minor = getminor(*devp);
1019 	instance = VLDCINST(minor);
1020 	vldcp = ddi_get_soft_state(vldc_ssp, instance);
1021 	if (vldcp == NULL)
1022 		return (ENXIO);
1023 
1024 	vminor = VLDCMINOR(vldcp, minor);
1025 	mutex_enter(&vminor->lock);
1026 	portno = vminor->portno;
1027 	if (portno == VLDC_INVALID_PORTNO) {
1028 		mutex_exit(&vminor->lock);
1029 		return (ENXIO);
1030 	}
1031 
1032 	vport = &(vldcp->port[portno]);
1033 
1034 	D1("vldc_open: opening vldc@%d:%lu\n", instance, portno);
1035 
1036 	if (vport->status != VLDC_PORT_CLOSED) {
1037 		mutex_exit(&vminor->lock);
1038 		return (EBUSY);
1039 	}
1040 
1041 	vport->recv_buf = kmem_alloc(vport->mtu, KM_SLEEP);
1042 	vport->send_buf = kmem_alloc(vport->mtu, KM_SLEEP);
1043 
1044 	if (strcmp(vport->minorp->sname, VLDC_HVCTL_SVCNAME) == 0)
1045 		vport->cookie_buf = kmem_alloc(vldc_max_cookie, KM_SLEEP);
1046 
1047 	vport->is_stream = B_FALSE;	/* assume not a stream */
1048 	vport->hanged_up = B_FALSE;
1049 
1050 	vport->status = VLDC_PORT_OPEN;
1051 
1052 	mutex_exit(&vminor->lock);
1053 
1054 	return (DDI_SUCCESS);
1055 }
1056 
1057 /* cb_close */
1058 static int
vldc_close(dev_t dev,int flag,int otyp,cred_t * cred)1059 vldc_close(dev_t dev, int flag, int otyp, cred_t *cred)
1060 {
1061 	_NOTE(ARGUNUSED(flag, otyp, cred))
1062 
1063 	int instance;
1064 	minor_t minor;
1065 	uint64_t portno;
1066 	vldc_t *vldcp;
1067 	vldc_minor_t *vminor;
1068 	int rv;
1069 
1070 	minor = getminor(dev);
1071 	instance = VLDCINST(minor);
1072 	vldcp = ddi_get_soft_state(vldc_ssp, instance);
1073 	if (vldcp == NULL) {
1074 		return (ENXIO);
1075 	}
1076 
1077 	vminor = VLDCMINOR(vldcp, minor);
1078 	mutex_enter(&vminor->lock);
1079 	portno = vminor->portno;
1080 	if (portno == VLDC_INVALID_PORTNO) {
1081 		mutex_exit(&vminor->lock);
1082 		return (ENOLINK);
1083 	}
1084 
1085 	D1("vldc_close: closing vldc@%d:%lu\n", instance, portno);
1086 
1087 	rv = i_vldc_close_port(vldcp, portno);
1088 
1089 	mutex_exit(&vminor->lock);
1090 
1091 	return (rv);
1092 }
1093 
1094 static int
vldc_set_ldc_mode(vldc_port_t * vport,vldc_t * vldcp,int channel_mode)1095 vldc_set_ldc_mode(vldc_port_t *vport, vldc_t *vldcp, int channel_mode)
1096 {
1097 	ldc_attr_t attr;
1098 	int rv;
1099 
1100 	ASSERT(MUTEX_HELD(&vport->minorp->lock));
1101 
1102 	/* validate mode */
1103 	switch (channel_mode) {
1104 	case LDC_MODE_RELIABLE:
1105 		vport->is_stream = B_TRUE;
1106 		break;
1107 	case LDC_MODE_RAW:
1108 	case LDC_MODE_UNRELIABLE:
1109 		vport->is_stream = B_FALSE;
1110 		break;
1111 	default:
1112 		return (EINVAL);
1113 	}
1114 
1115 	if (vport->status == VLDC_PORT_READY) {
1116 		rv = i_vldc_ldc_close(vport);
1117 		if (rv != 0) {
1118 			DWARN("vldc_set_ldc_mode: i_vldc_ldc_close "
1119 			    "failed, rv=%d\n", rv);
1120 			return (rv);
1121 		}
1122 	}
1123 
1124 	D1("vldc_set_ldc_mode: vport status %d, mode %d\n",
1125 	    vport->status, channel_mode);
1126 
1127 	vport->ldc_mode = channel_mode;
1128 
1129 	/* initialize the channel */
1130 	attr.devclass = LDC_DEV_SERIAL;
1131 	attr.instance = ddi_get_instance(vldcp->dip);
1132 	attr.mtu = vport->mtu;
1133 	attr.mode = vport->ldc_mode;
1134 
1135 	if ((rv = ldc_init(vport->ldc_id, &attr,
1136 	    &vport->ldc_handle)) != 0) {
1137 		DWARN("vldc_ioctl_opt_op: ldc_init failed, rv=%d\n", rv);
1138 		goto error_init;
1139 	}
1140 
1141 	/* register it */
1142 	if ((rv = ldc_reg_callback(vport->ldc_handle,
1143 	    i_vldc_cb, (caddr_t)vport)) != 0) {
1144 		DWARN("vldc_ioctl_opt_op: ldc_reg_callback failed, rv=%d\n",
1145 		    rv);
1146 		goto error_reg;
1147 	}
1148 
1149 	/* open the channel */
1150 	if ((rv = ldc_open(vport->ldc_handle)) != 0) {
1151 		DWARN("vldc_ioctl_opt_op: ldc_open failed, rv=%d\n", rv);
1152 		goto error_open;
1153 	}
1154 
1155 	vport->status = VLDC_PORT_READY;
1156 
1157 	/*
1158 	 * Attempt to bring the channel up, but do not
1159 	 * fail if the other end is not up yet.
1160 	 */
1161 	rv = ldc_up(vport->ldc_handle);
1162 	if (rv == ECONNREFUSED) {
1163 		D1("vldc_ioctl_opt_op: remote endpoint not up yet\n");
1164 	} else if (rv != 0) {
1165 		DWARN("vldc_ioctl_opt_op: ldc_up failed, rv=%d\n", rv);
1166 		goto error_up;
1167 	}
1168 
1169 	rv = ldc_status(vport->ldc_handle, &vport->ldc_status);
1170 	if (rv != 0) {
1171 		DWARN("vldc_ioctl_opt_op: vldc@%d:%d could not get ldc "
1172 		    "status, rv=%d\n", vport->inst, vport->number, rv);
1173 		goto error_up;
1174 	}
1175 
1176 	D1("vldc_ioctl_opt_op: ldc %ld initialized successfully\n",
1177 	    vport->ldc_id);
1178 
1179 	return (0);
1180 
1181 error_up:
1182 	vport->status = VLDC_PORT_OPEN;
1183 	(void) ldc_close(vport->ldc_handle);
1184 error_open:
1185 	(void) ldc_unreg_callback(vport->ldc_handle);
1186 error_reg:
1187 	(void) ldc_fini(vport->ldc_handle);
1188 error_init:
1189 	return (rv);
1190 }
1191 
1192 /* ioctl to read cookie */
1193 static int
i_vldc_ioctl_read_cookie(vldc_port_t * vport,int vldc_instance,void * arg,int mode)1194 i_vldc_ioctl_read_cookie(vldc_port_t *vport, int vldc_instance, void *arg,
1195     int mode)
1196 {
1197 	vldc_data_t copy_info;
1198 	uint64_t len, balance, copy_size;
1199 	caddr_t src_addr, dst_addr;
1200 	int rv;
1201 
1202 	if (ddi_copyin(arg, &copy_info, sizeof (copy_info), mode) == -1) {
1203 		return (EFAULT);
1204 	}
1205 
1206 	len = balance = copy_info.length;
1207 	src_addr = (caddr_t)copy_info.src_addr;
1208 	dst_addr = (caddr_t)copy_info.dst_addr;
1209 	while (balance > 0) {
1210 
1211 		/* get the max amount to the copied */
1212 		copy_size = MIN(balance, vldc_max_cookie);
1213 
1214 		mutex_enter(&vport->minorp->lock);
1215 
1216 		D2("i_vldc_ioctl_read_cookie: vldc@%d:%d reading from 0x%p "
1217 		    "size 0x%lx to 0x%p\n", vldc_instance, vport->number,
1218 		    dst_addr, copy_size, src_addr);
1219 
1220 		/* read from the HV into the temporary buffer */
1221 		rv = ldc_mem_rdwr_cookie(vport->ldc_handle, vport->cookie_buf,
1222 		    &copy_size, dst_addr, LDC_COPY_IN);
1223 		if (rv != 0) {
1224 			DWARN("i_vldc_ioctl_read_cookie: vldc@%d:%d cannot "
1225 			    "read address 0x%p, rv=%d\n",
1226 			    vldc_instance, vport->number, dst_addr, rv);
1227 			mutex_exit(&vport->minorp->lock);
1228 			return (EFAULT);
1229 		}
1230 
1231 		D2("i_vldc_ioctl_read_cookie: vldc@%d:%d read succeeded\n",
1232 		    vldc_instance, vport->number);
1233 
1234 		mutex_exit(&vport->minorp->lock);
1235 
1236 		/*
1237 		 * copy data from temporary buffer out to the
1238 		 * caller and free buffer
1239 		 */
1240 		rv = ddi_copyout(vport->cookie_buf, src_addr, copy_size, mode);
1241 		if (rv != 0) {
1242 			return (EFAULT);
1243 		}
1244 
1245 		/* adjust len, source and dest */
1246 		balance -= copy_size;
1247 		src_addr += copy_size;
1248 		dst_addr += copy_size;
1249 	}
1250 
1251 	/* set the structure to reflect outcome */
1252 	copy_info.length = len;
1253 	if (ddi_copyout(&copy_info, arg, sizeof (copy_info), mode) != 0) {
1254 		return (EFAULT);
1255 	}
1256 
1257 	return (0);
1258 }
1259 
1260 /* ioctl to write cookie */
1261 static int
i_vldc_ioctl_write_cookie(vldc_port_t * vport,int vldc_instance,void * arg,int mode)1262 i_vldc_ioctl_write_cookie(vldc_port_t *vport, int vldc_instance, void *arg,
1263     int mode)
1264 {
1265 	vldc_data_t copy_info;
1266 	uint64_t len, balance, copy_size;
1267 	caddr_t src_addr, dst_addr;
1268 	int rv;
1269 
1270 	if (ddi_copyin(arg, &copy_info, sizeof (copy_info), mode) != 0) {
1271 		return (EFAULT);
1272 	}
1273 
1274 	D2("i_vldc_ioctl_write_cookie: vldc@%d:%d writing 0x%lx size 0x%lx "
1275 	    "to 0x%lx\n", vldc_instance, vport->number, copy_info.src_addr,
1276 	    copy_info.length, copy_info.dst_addr);
1277 
1278 	len = balance = copy_info.length;
1279 	src_addr = (caddr_t)copy_info.src_addr;
1280 	dst_addr = (caddr_t)copy_info.dst_addr;
1281 	while (balance > 0) {
1282 
1283 		/* get the max amount to the copied */
1284 		copy_size = MIN(balance, vldc_max_cookie);
1285 
1286 		/*
1287 		 * copy into the temporary buffer the data
1288 		 * to be written to the HV
1289 		 */
1290 		if (ddi_copyin((caddr_t)src_addr, vport->cookie_buf,
1291 		    copy_size, mode) != 0) {
1292 			return (EFAULT);
1293 		}
1294 
1295 		mutex_enter(&vport->minorp->lock);
1296 
1297 		/* write the data from the temporary buffer to the HV */
1298 		rv = ldc_mem_rdwr_cookie(vport->ldc_handle, vport->cookie_buf,
1299 		    &copy_size, dst_addr, LDC_COPY_OUT);
1300 		if (rv != 0) {
1301 			DWARN("i_vldc_ioctl_write_cookie: vldc@%d:%d "
1302 			    "failed to write at address 0x%p\n, rv=%d",
1303 			    vldc_instance, vport->number, dst_addr, rv);
1304 			mutex_exit(&vport->minorp->lock);
1305 			return (EFAULT);
1306 		}
1307 
1308 		D2("i_vldc_ioctl_write_cookie: vldc@%d:%d write succeeded\n",
1309 		    vldc_instance, vport->number);
1310 
1311 		mutex_exit(&vport->minorp->lock);
1312 
1313 		/* adjust len, source and dest */
1314 		balance -= copy_size;
1315 		src_addr += copy_size;
1316 		dst_addr += copy_size;
1317 	}
1318 
1319 	/* set the structure to reflect outcome */
1320 	copy_info.length = len;
1321 	if (ddi_copyout(&copy_info, (caddr_t)arg,
1322 	    sizeof (copy_info), mode) != 0) {
1323 		return (EFAULT);
1324 	}
1325 
1326 	return (0);
1327 }
1328 
1329 /* vldc specific ioctl option commands */
1330 static int
i_vldc_ioctl_opt_op(vldc_port_t * vport,vldc_t * vldcp,void * arg,int mode)1331 i_vldc_ioctl_opt_op(vldc_port_t *vport, vldc_t *vldcp, void *arg, int mode)
1332 {
1333 	vldc_opt_op_t 	vldc_cmd;
1334 	uint32_t	new_mtu;
1335 	int		rv = 0;
1336 
1337 	if (ddi_copyin(arg, &vldc_cmd, sizeof (vldc_cmd), mode) != 0) {
1338 		return (EFAULT);
1339 	}
1340 
1341 	D1("vldc_ioctl_opt_op: op %d\n", vldc_cmd.opt_sel);
1342 
1343 	switch (vldc_cmd.opt_sel) {
1344 
1345 	case VLDC_OPT_MTU_SZ:
1346 
1347 		if (vldc_cmd.op_sel == VLDC_OP_GET) {
1348 			vldc_cmd.opt_val = vport->mtu;
1349 			if (ddi_copyout(&vldc_cmd, arg,
1350 			    sizeof (vldc_cmd), mode) == -1) {
1351 				return (EFAULT);
1352 			}
1353 		} else {
1354 			new_mtu = vldc_cmd.opt_val;
1355 
1356 			if ((new_mtu < LDC_PACKET_SIZE) ||
1357 			    (new_mtu > vldc_max_mtu)) {
1358 				return (EINVAL);
1359 			}
1360 
1361 			mutex_enter(&vport->minorp->lock);
1362 
1363 			if ((vport->status != VLDC_PORT_CLOSED) &&
1364 			    (new_mtu != vport->mtu)) {
1365 				/*
1366 				 * The port has buffers allocated since it is
1367 				 * not closed plus the MTU size has changed.
1368 				 * Reallocate the buffers to the new MTU size.
1369 				 */
1370 				kmem_free(vport->recv_buf, vport->mtu);
1371 				vport->recv_buf = kmem_alloc(new_mtu, KM_SLEEP);
1372 
1373 				kmem_free(vport->send_buf, vport->mtu);
1374 				vport->send_buf = kmem_alloc(new_mtu, KM_SLEEP);
1375 
1376 				vport->mtu = new_mtu;
1377 			}
1378 
1379 			mutex_exit(&vport->minorp->lock);
1380 		}
1381 
1382 		break;
1383 
1384 	case VLDC_OPT_STATUS:
1385 
1386 		if (vldc_cmd.op_sel == VLDC_OP_GET) {
1387 			vldc_cmd.opt_val = vport->status;
1388 			if (ddi_copyout(&vldc_cmd, arg,
1389 			    sizeof (vldc_cmd), mode) == -1) {
1390 				return (EFAULT);
1391 			}
1392 		} else {
1393 			return (ENOTSUP);
1394 		}
1395 
1396 		break;
1397 
1398 	case VLDC_OPT_MODE:
1399 
1400 		if (vldc_cmd.op_sel == VLDC_OP_GET) {
1401 			vldc_cmd.opt_val = vport->ldc_mode;
1402 			if (ddi_copyout(&vldc_cmd, arg,
1403 			    sizeof (vldc_cmd), mode) == -1) {
1404 				return (EFAULT);
1405 			}
1406 		} else {
1407 			mutex_enter(&vport->minorp->lock);
1408 			rv = vldc_set_ldc_mode(vport, vldcp, vldc_cmd.opt_val);
1409 			mutex_exit(&vport->minorp->lock);
1410 		}
1411 
1412 		break;
1413 
1414 	default:
1415 
1416 		D1("vldc_ioctl_opt_op: unsupported op %d\n", vldc_cmd.opt_sel);
1417 		return (ENOTSUP);
1418 	}
1419 
1420 	return (rv);
1421 }
1422 
1423 /* cb_ioctl */
1424 static int
vldc_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)1425 vldc_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
1426     int *rvalp)
1427 {
1428 	_NOTE(ARGUNUSED(credp, rvalp))
1429 
1430 	int rv = EINVAL;
1431 	int instance;
1432 	minor_t minor;
1433 	uint64_t portno;
1434 	vldc_t *vldcp;
1435 	vldc_port_t *vport;
1436 	vldc_minor_t *vminor;
1437 
1438 	minor = getminor(dev);
1439 	instance = VLDCINST(minor);
1440 	vldcp = ddi_get_soft_state(vldc_ssp, instance);
1441 	if (vldcp == NULL) {
1442 		return (ENXIO);
1443 	}
1444 
1445 	vminor = VLDCMINOR(vldcp, minor);
1446 	mutex_enter(&vminor->lock);
1447 	portno = vminor->portno;
1448 	if (portno == VLDC_INVALID_PORTNO) {
1449 		mutex_exit(&vminor->lock);
1450 		return (ENOLINK);
1451 	}
1452 	vminor->in_use += 1;
1453 	mutex_exit(&vminor->lock);
1454 
1455 	vport = &(vldcp->port[portno]);
1456 
1457 	D1("vldc_ioctl: vldc@%d:%lu cmd=0x%x\n", instance, portno, cmd);
1458 
1459 	switch (cmd) {
1460 
1461 	case VLDC_IOCTL_OPT_OP:
1462 		rv = i_vldc_ioctl_opt_op(vport, vldcp, (void *)arg,  mode);
1463 		break;
1464 
1465 	case VLDC_IOCTL_READ_COOKIE:
1466 		if (strcmp(vport->minorp->sname, VLDC_HVCTL_SVCNAME)) {
1467 			rv = EINVAL;
1468 			break;
1469 		}
1470 		rv = i_vldc_ioctl_read_cookie(vport, instance,
1471 		    (void *)arg, mode);
1472 		break;
1473 
1474 	case VLDC_IOCTL_WRITE_COOKIE:
1475 		if (strcmp(vport->minorp->sname, VLDC_HVCTL_SVCNAME)) {
1476 			rv = EINVAL;
1477 			break;
1478 		}
1479 		rv = i_vldc_ioctl_write_cookie(vport, instance,
1480 		    (void *)arg, mode);
1481 		break;
1482 
1483 	default:
1484 		DWARN("vldc_ioctl: vldc@%d:%lu unknown cmd=0x%x\n",
1485 		    instance, portno, cmd);
1486 		rv = EINVAL;
1487 		break;
1488 	}
1489 
1490 	mutex_enter(&vminor->lock);
1491 	vminor->in_use -= 1;
1492 	if (vminor->in_use == 0) {
1493 		cv_signal(&vminor->cv);
1494 	}
1495 	mutex_exit(&vminor->lock);
1496 
1497 	D1("vldc_ioctl: rv=%d\n", rv);
1498 
1499 	return (rv);
1500 }
1501 
1502 /* cb_read */
1503 static int
vldc_read(dev_t dev,struct uio * uiop,cred_t * credp)1504 vldc_read(dev_t dev, struct uio *uiop, cred_t *credp)
1505 {
1506 	_NOTE(ARGUNUSED(credp))
1507 
1508 	int instance;
1509 	minor_t minor;
1510 	size_t size = 0;
1511 	uint64_t portno;
1512 	vldc_t *vldcp;
1513 	vldc_port_t *vport;
1514 	vldc_minor_t *vminor;
1515 	int rv = 0;
1516 
1517 	minor = getminor(dev);
1518 	instance = VLDCINST(minor);
1519 	vldcp = ddi_get_soft_state(vldc_ssp, instance);
1520 	if (vldcp == NULL) {
1521 		return (ENXIO);
1522 	}
1523 
1524 	vminor = VLDCMINOR(vldcp, minor);
1525 	mutex_enter(&vminor->lock);
1526 	portno = vminor->portno;
1527 	if (portno == VLDC_INVALID_PORTNO) {
1528 		mutex_exit(&vminor->lock);
1529 		return (ENOLINK);
1530 	}
1531 
1532 	D2("vldc_read: vldc@%d:%lu reading data\n", instance, portno);
1533 
1534 	vport = &(vldcp->port[portno]);
1535 
1536 	/* check the port status */
1537 	if (vport->status != VLDC_PORT_READY) {
1538 		DWARN("vldc_read: vldc@%d:%lu not in the ready state\n",
1539 		    instance, portno);
1540 		mutex_exit(&vminor->lock);
1541 		return (ENOTACTIVE);
1542 	}
1543 
1544 	/* read data */
1545 	size = MIN(vport->mtu, uiop->uio_resid);
1546 	rv = ldc_read(vport->ldc_handle, vport->recv_buf, &size);
1547 
1548 	D2("vldc_read: vldc@%d:%lu ldc_read size=%ld, rv=%d\n",
1549 	    instance, portno, size, rv);
1550 
1551 	if (rv == 0) {
1552 		if (size != 0) {
1553 			rv = uiomove(vport->recv_buf, size, UIO_READ, uiop);
1554 		} else {
1555 			rv = EWOULDBLOCK;
1556 		}
1557 	} else {
1558 		switch (rv) {
1559 		case ENOBUFS:
1560 			break;
1561 		case ETIMEDOUT:
1562 		case EWOULDBLOCK:
1563 			rv = EWOULDBLOCK;
1564 			break;
1565 		default:
1566 			rv = ECONNRESET;
1567 			break;
1568 		}
1569 	}
1570 
1571 	mutex_exit(&vminor->lock);
1572 
1573 	return (rv);
1574 }
1575 
1576 /* cb_write */
1577 static int
vldc_write(dev_t dev,struct uio * uiop,cred_t * credp)1578 vldc_write(dev_t dev, struct uio *uiop, cred_t *credp)
1579 {
1580 	_NOTE(ARGUNUSED(credp))
1581 
1582 	int instance;
1583 	minor_t minor;
1584 	size_t size;
1585 	size_t orig_size;
1586 	uint64_t portno;
1587 	vldc_t *vldcp;
1588 	vldc_port_t *vport;
1589 	vldc_minor_t *vminor;
1590 	int rv = EINVAL;
1591 
1592 	minor = getminor(dev);
1593 	instance = VLDCINST(minor);
1594 	vldcp = ddi_get_soft_state(vldc_ssp, instance);
1595 	if (vldcp == NULL) {
1596 		return (ENXIO);
1597 	}
1598 
1599 	vminor = VLDCMINOR(vldcp, minor);
1600 	mutex_enter(&vminor->lock);
1601 	portno = vminor->portno;
1602 	if (portno == VLDC_INVALID_PORTNO) {
1603 		mutex_exit(&vminor->lock);
1604 		return (ENOLINK);
1605 	}
1606 
1607 	vport = &(vldcp->port[portno]);
1608 
1609 	/* check the port status */
1610 	if (vport->status != VLDC_PORT_READY) {
1611 		DWARN("vldc_write: vldc@%d:%lu not in the ready state\n",
1612 		    instance, portno);
1613 		mutex_exit(&vminor->lock);
1614 		return (ENOTACTIVE);
1615 	}
1616 
1617 	orig_size = uiop->uio_resid;
1618 	size = orig_size;
1619 
1620 	if (size > vport->mtu) {
1621 		if (vport->is_stream) {
1622 			/* can only send MTU size at a time */
1623 			size = vport->mtu;
1624 		} else {
1625 			mutex_exit(&vminor->lock);
1626 			return (EMSGSIZE);
1627 		}
1628 	}
1629 
1630 	D2("vldc_write: vldc@%d:%lu writing %lu bytes\n", instance, portno,
1631 	    size);
1632 
1633 	rv = uiomove(vport->send_buf, size, UIO_WRITE, uiop);
1634 	if (rv == 0) {
1635 		rv = ldc_write(vport->ldc_handle, (caddr_t)vport->send_buf,
1636 		    &size);
1637 		if (rv != 0) {
1638 			DWARN("vldc_write: vldc@%d:%lu failed writing %lu "
1639 			    "bytes rv=%d\n", instance, portno, size, rv);
1640 		}
1641 	} else {
1642 		size = 0;
1643 	}
1644 
1645 	mutex_exit(&vminor->lock);
1646 
1647 	/* resid is total number of bytes *not* sent */
1648 	uiop->uio_resid = orig_size - size;
1649 
1650 	return (rv);
1651 }
1652 
1653 /* cb_chpoll */
1654 static int
vldc_chpoll(dev_t dev,short events,int anyyet,short * reventsp,struct pollhead ** phpp)1655 vldc_chpoll(dev_t dev, short events, int anyyet,  short *reventsp,
1656     struct pollhead **phpp)
1657 {
1658 	int instance;
1659 	minor_t minor;
1660 	uint64_t portno;
1661 	vldc_t *vldcp;
1662 	vldc_port_t *vport;
1663 	vldc_minor_t *vminor;
1664 	boolean_t haspkts;
1665 
1666 	minor = getminor(dev);
1667 	instance = VLDCINST(minor);
1668 	vldcp = ddi_get_soft_state(vldc_ssp, instance);
1669 	if (vldcp == NULL) {
1670 		return (ENXIO);
1671 	}
1672 
1673 	vminor = VLDCMINOR(vldcp, minor);
1674 	mutex_enter(&vminor->lock);
1675 	portno = vminor->portno;
1676 	if (portno == VLDC_INVALID_PORTNO) {
1677 		mutex_exit(&vminor->lock);
1678 		return (ENOLINK);
1679 	}
1680 
1681 	vport = &(vldcp->port[portno]);
1682 
1683 	/* check the port status */
1684 	if (vport->status != VLDC_PORT_READY) {
1685 		mutex_exit(&vminor->lock);
1686 		return (ENOTACTIVE);
1687 	}
1688 
1689 	D2("vldc_chpoll: vldc@%d:%lu polling events 0x%x\n",
1690 	    instance, portno, events);
1691 
1692 	*reventsp = 0;
1693 
1694 	if (vport->ldc_status == LDC_UP) {
1695 		/*
1696 		 * Check if the receive queue is empty and if not, signal that
1697 		 * there is data ready to read.
1698 		 */
1699 		if (events & POLLIN) {
1700 			if ((ldc_chkq(vport->ldc_handle, &haspkts) == 0) &&
1701 			    haspkts) {
1702 				*reventsp |= POLLIN;
1703 			}
1704 		}
1705 
1706 		if (events & POLLOUT)
1707 			*reventsp |= POLLOUT;
1708 
1709 	} else if (vport->hanged_up) {
1710 		*reventsp |= POLLHUP;
1711 		vport->hanged_up = B_FALSE;
1712 	}
1713 
1714 	mutex_exit(&vminor->lock);
1715 
1716 	if (((*reventsp) == 0) && (!anyyet)) {
1717 		*phpp = &vport->poll;
1718 	}
1719 
1720 	D2("vldc_chpoll: vldc@%d:%lu ev=0x%x, rev=0x%x\n",
1721 	    instance, portno, events, *reventsp);
1722 
1723 	return (0);
1724 }
1725