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, ©_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 ©_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(©_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, ©_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 ©_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(©_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