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.
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.
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]
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
25 #pragma ident "%Z%%M% %I% %E% SMI"
28 * Configuration and setup interface to vcc driver.
29 * At intialization time, vntsd opens vcc ctrl port and read initial
30 * configuratioa. It manages console groups, creates the listen thread,
31 * dynamically adds and removes virtual console within a group.
37 #include <sys/types.h>
42 #include <sys/socket.h>
49 #include <netinet/in.h>
54 /* signal all clients that console has been deleted */
56 vntsd_notify_client_cons_del(vntsd_client_t
*clientp
)
58 (void) mutex_lock(&clientp
->lock
);
59 clientp
->status
|= VNTSD_CLIENT_CONS_DELETED
;
60 (void) thr_kill(clientp
->cons_tid
, SIGUSR1
);
61 (void) mutex_unlock(&clientp
->lock
);
65 /* free console structure */
67 free_cons(vntsd_cons_t
*consp
)
70 (void) mutex_destroy(&consp
->lock
);
71 (void) cond_destroy(&consp
->cvp
);
72 if (consp
->vcc_fd
!= -1)
73 (void) close(consp
->vcc_fd
);
77 /* free group structure */
79 free_group(vntsd_group_t
*groupp
)
82 (void) mutex_destroy(&groupp
->lock
);
83 (void) cond_destroy(&groupp
->cvp
);
84 if (groupp
->sockfd
!= -1)
85 (void) close(groupp
->sockfd
);
90 * all clients connected to a console must disconnect before
94 cleanup_cons(vntsd_cons_t
*consp
)
96 vntsd_group_t
*groupp
;
100 D1(stderr
, "t@%d vntsd_disconn_clients@%d\n", thr_self(),
103 groupp
= consp
->group
;
107 (void) mutex_lock(&consp
->lock
);
109 /* wait for all clients disconnect from the console */
110 while (consp
->clientpq
!= NULL
) {
111 consp
->status
|= VNTSD_CONS_SIG_WAIT
;
113 /* signal client to disconnect the console */
114 (void) vntsd_que_walk(consp
->clientpq
,
115 (el_func_t
)vntsd_notify_client_cons_del
);
117 (void) thr_kill(consp
->wr_tid
, SIGUSR1
);
118 to
.tv_sec
= VNTSD_CV_WAIT_DELTIME
;
121 /* wait for clients to disconnect */
122 (void) cond_reltimedwait(&consp
->cvp
, &consp
->lock
, &to
);
125 /* reduce console count in the group */
126 (void) mutex_lock(&groupp
->lock
);
127 assert(groupp
->num_cons
> 0);
129 (void) mutex_unlock(&groupp
->lock
);
131 (void) mutex_unlock(&consp
->lock
);
136 /* search for a group whose console is being deleted */
138 find_clean_cons_group(vntsd_group_t
*groupp
)
140 if (groupp
->status
& VNTSD_GROUP_CLEAN_CONS
) {
147 /* search for a console that is being deleted */
149 find_clean_cons(vntsd_cons_t
*consp
)
151 if (consp
->status
& VNTSD_CONS_DELETED
) {
158 /* delete a console */
160 vntsd_delete_cons(vntsd_t
*vntsdp
)
162 vntsd_group_t
*groupp
;
166 /* get the group contains deleted console */
167 (void) mutex_lock(&vntsdp
->lock
);
168 groupp
= vntsd_que_walk(vntsdp
->grouppq
,
169 (el_func_t
)find_clean_cons_group
);
170 if (groupp
== NULL
) {
171 /* no more group has console deleted */
172 (void) mutex_unlock(&vntsdp
->lock
);
175 (void) mutex_lock(&groupp
->lock
);
176 groupp
->status
&= ~VNTSD_GROUP_CLEAN_CONS
;
177 (void) mutex_unlock(&groupp
->lock
);
178 (void) mutex_unlock(&vntsdp
->lock
);
181 /* get the console to be deleted */
182 (void) mutex_lock(&groupp
->lock
);
184 /* clean up any deleted console in the group */
185 if (groupp
->conspq
!= NULL
) {
186 consp
= vntsd_que_walk(groupp
->conspq
,
187 (el_func_t
)find_clean_cons
);
189 /* no more cons to delete */
190 (void) mutex_unlock(&groupp
->lock
);
194 /* remove console from the group */
195 (void) vntsd_que_rm(&groupp
->conspq
, consp
);
196 (void) mutex_unlock(&groupp
->lock
);
198 /* clean up the console */
203 if (groupp
->conspq
== NULL
) {
204 /* no more console in the group delete group */
205 assert(groupp
->vntsd
);
207 (void) mutex_lock(&groupp
->vntsd
->lock
);
208 (void) vntsd_que_rm(&groupp
->vntsd
->grouppq
,
210 (void) mutex_unlock(&groupp
->vntsd
->lock
);
212 /* clean up the group */
213 vntsd_clean_group(groupp
);
220 /* clean up a group */
222 vntsd_clean_group(vntsd_group_t
*groupp
)
227 D1(stderr
, "t@%d clean_group() group=%s tcp=%lld\n", thr_self(),
228 groupp
->group_name
, groupp
->tcp_port
);
230 (void) mutex_lock(&groupp
->lock
);
232 /* prevent from reentry */
233 if (groupp
->status
& VNTSD_GROUP_IN_CLEANUP
) {
234 (void) mutex_unlock(&groupp
->lock
);
237 groupp
->status
|= VNTSD_GROUP_IN_CLEANUP
;
239 /* mark group waiting for listen thread to exits */
240 groupp
->status
|= VNTSD_GROUP_SIG_WAIT
;
241 (void) mutex_unlock(&groupp
->lock
);
243 vntsd_free_que(&groupp
->conspq
, (clean_func_t
)cleanup_cons
);
245 (void) mutex_lock(&groupp
->lock
);
246 /* walk through no cons client queue */
247 while (groupp
->no_cons_clientpq
!= NULL
) {
248 (void) vntsd_que_walk(groupp
->no_cons_clientpq
,
249 (el_func_t
)vntsd_notify_client_cons_del
);
250 to
.tv_sec
= VNTSD_CV_WAIT_DELTIME
;
252 (void) cond_reltimedwait(&groupp
->cvp
, &groupp
->lock
, &to
);
255 /* waiting for listen thread to exit */
256 while (groupp
->status
& VNTSD_GROUP_SIG_WAIT
) {
257 /* signal listen thread to exit */
258 (void) thr_kill(groupp
->listen_tid
, SIGUSR1
);
259 to
.tv_sec
= VNTSD_CV_WAIT_DELTIME
;
261 /* wait listen thread to exit */
262 (void) cond_reltimedwait(&groupp
->cvp
, &groupp
->lock
, &to
);
265 (void) mutex_unlock(&groupp
->lock
);
266 (void) thr_join(groupp
->listen_tid
, NULL
, NULL
);
271 /* allocate and initialize console structure */
272 static vntsd_cons_t
*
273 alloc_cons(vntsd_group_t
*groupp
, vcc_console_t
*consolep
)
278 /* allocate console */
279 consp
= (vntsd_cons_t
*)malloc(sizeof (vntsd_cons_t
));
281 vntsd_log(VNTSD_ERR_NO_MEM
, "alloc_cons");
285 /* intialize console */
286 bzero(consp
, sizeof (vntsd_cons_t
));
288 (void) mutex_init(&consp
->lock
, USYNC_THREAD
|LOCK_ERRORCHECK
, NULL
);
289 (void) cond_init(&consp
->cvp
, USYNC_THREAD
, NULL
);
291 consp
->cons_no
= consolep
->cons_no
;
292 (void) strlcpy(consp
->domain_name
, consolep
->domain_name
, MAXPATHLEN
);
293 (void) strlcpy(consp
->dev_name
, consolep
->dev_name
, MAXPATHLEN
);
294 consp
->wr_tid
= (thread_t
)-1;
298 (void) mutex_lock(&groupp
->lock
);
300 if ((rv
= vntsd_que_append(&groupp
->conspq
, consp
)) !=
302 (void) mutex_unlock(&groupp
->lock
);
303 vntsd_log(rv
, "alloc_cons");
308 consp
->group
= groupp
;
310 (void) mutex_unlock(&groupp
->lock
);
312 D1(stderr
, "t@%d alloc_cons@%d %s %s\n", thr_self(),
313 consp
->cons_no
, consp
->domain_name
, consp
->dev_name
);
318 /* compare tcp with group->tcp */
320 grp_by_tcp(vntsd_group_t
*groupp
, uint64_t *tcp_port
)
324 return (groupp
->tcp_port
== *tcp_port
);
327 /* allocate and initialize group */
328 static vntsd_group_t
*
329 alloc_group(vntsd_t
*vntsdp
, char *group_name
, uint64_t tcp_port
)
331 vntsd_group_t
*groupp
;
334 groupp
= (vntsd_group_t
*)malloc(sizeof (vntsd_group_t
));
335 if (groupp
== NULL
) {
336 vntsd_log(VNTSD_ERR_NO_MEM
, "alloc_group");
340 /* initialize group */
341 bzero(groupp
, sizeof (vntsd_group_t
));
343 (void) mutex_init(&groupp
->lock
, USYNC_THREAD
|LOCK_ERRORCHECK
, NULL
);
344 (void) cond_init(&groupp
->cvp
, USYNC_THREAD
, NULL
);
346 if (group_name
!= NULL
) {
347 (void) memcpy(groupp
->group_name
, group_name
, MAXPATHLEN
);
350 groupp
->tcp_port
= tcp_port
;
351 groupp
->listen_tid
= (thread_t
)-1;
353 groupp
->vntsd
= vntsdp
;
355 D1(stderr
, "t@%d alloc_group@%lld:%s\n", thr_self(), groupp
->tcp_port
,
361 /* mark a deleted console */
363 vntsd_mark_deleted_cons(vntsd_cons_t
*consp
)
365 (void) mutex_lock(&consp
->lock
);
366 consp
->status
|= VNTSD_CONS_DELETED
;
367 (void) mutex_unlock(&consp
->lock
);
372 * Initialize a console, if console is associated with with a
373 * new group, intialize the group.
376 alloc_cons_with_group(vntsd_t
*vntsdp
, vcc_console_t
*consp
,
377 vntsd_group_t
**new_groupp
)
379 vntsd_group_t
*groupp
= NULL
;
384 /* match group by tcp port */
387 (void) mutex_lock(&vntsdp
->lock
);
388 groupp
= vntsd_que_find(vntsdp
->grouppq
,
389 (compare_func_t
)grp_by_tcp
, (void *)&(consp
->tcp_port
));
391 (void) mutex_lock(&groupp
->lock
);
393 (void) mutex_unlock(&vntsdp
->lock
);
395 if (groupp
!= NULL
) {
397 * group with same tcp port found.
398 * if there is no console in the group, the
399 * group should be removed and the tcp port can
400 * be used for tne new group.
401 * This is possible, when there is tight loop of
402 * creating/deleting domains. When a vcc port is
403 * removed, a read thread will have an I/O error because
404 * vcc has closed the port. The read thread then marks
405 * the console is removed and notify main thread to
406 * remove the console.
407 * Meanwhile, the same port and its group (with same
408 * tcp port and group name) is created. Vcc notify
409 * vntsd that new console is added.
410 * Main thread now have two events. If main thread polls
411 * out vcc notification first, it will find that there is
412 * a group has no console.
415 if (vntsd_chk_group_total_cons(groupp
) == 0) {
417 /* all consoles in the group have been removed */
418 (void) vntsd_que_walk(groupp
->conspq
,
419 (el_func_t
)vntsd_mark_deleted_cons
);
420 groupp
->status
|= VNTSD_GROUP_CLEAN_CONS
;
421 (void) mutex_unlock(&groupp
->lock
);
424 } else if (strcmp(groupp
->group_name
, consp
->group_name
)) {
425 /* conflict group name */
426 vntsd_log(VNTSD_ERR_VCC_GRP_NAME
,
427 "group name is different from existing group");
428 (void) mutex_unlock(&groupp
->lock
);
429 return (VNTSD_ERR_VCC_CTRL_DATA
);
432 /* group already existed */
433 (void) mutex_unlock(&groupp
->lock
);
438 if (groupp
== NULL
) {
440 groupp
= alloc_group(vntsdp
, consp
->group_name
,
442 if (groupp
== NULL
) {
443 return (VNTSD_ERR_NO_MEM
);
446 assert(groupp
->conspq
== NULL
);
447 /* queue group to vntsdp */
448 (void) mutex_lock(&vntsdp
->lock
);
449 rv
= vntsd_que_append(&vntsdp
->grouppq
, groupp
);
450 (void) mutex_unlock(&vntsdp
->lock
);
452 if (rv
!= VNTSD_SUCCESS
) {
456 *new_groupp
= groupp
;
459 /* intialize console */
460 if (alloc_cons(groupp
, consp
) == NULL
) {
462 if (new_groupp
!= NULL
) {
463 /* clean up new group */
467 return (VNTSD_ERR_NO_MEM
);
470 return (VNTSD_SUCCESS
);
475 /* create listen thread */
477 create_listen_thread(vntsd_group_t
*groupp
)
480 char err_msg
[VNTSD_LINE_LEN
];
485 (void) mutex_lock(&groupp
->lock
);
486 assert(groupp
->num_cons
);
488 D1(stderr
, "t@%d create_listen:%lld\n", thr_self(), groupp
->tcp_port
);
490 if ((rv
= thr_create(NULL
, 0, (thr_func_t
)vntsd_listen_thread
,
491 (void *)groupp
, THR_DETACHED
, &groupp
->listen_tid
))
493 (void) (void) snprintf(err_msg
, sizeof (err_msg
),
494 "Can not create listen thread for"
495 "group %s tcp %llx\n", groupp
->group_name
,
497 vntsd_log(VNTSD_ERR_CREATE_LISTEN_THR
, err_msg
);
499 /* clean up group queue */
500 vntsd_free_que(&groupp
->conspq
, (clean_func_t
)free_cons
);
501 groupp
->listen_tid
= (thread_t
)-1;
504 (void) mutex_unlock(&groupp
->lock
);
509 /* find deleted console by console no */
511 deleted_cons_by_consno(vntsd_cons_t
*consp
, int *cons_no
)
513 vntsd_client_t
*clientp
;
517 if (consp
->cons_no
!= *cons_no
)
520 /* has console marked as deleted? */
521 if ((consp
->status
& VNTSD_CONS_DELETED
) == 0)
524 if (consp
->clientpq
== NULL
)
525 /* there is no client for this console */
528 /* need to notify clients of console ? */
529 clientp
= (vntsd_client_t
*)consp
->clientpq
->handle
;
531 if (clientp
->status
& VNTSD_CLIENT_CONS_DELETED
)
532 /* clients of console have notified */
538 /* find group structure from console no */
540 find_cons_group_by_cons_no(vntsd_group_t
*groupp
, uint_t
*cons_no
)
544 consp
= vntsd_que_find(groupp
->conspq
,
545 (compare_func_t
)deleted_cons_by_consno
, cons_no
);
546 return (consp
!= NULL
);
550 /* delete a console if the console exists in the vntsd */
552 delete_cons_before_add(vntsd_t
*vntsdp
, uint_t cons_no
)
554 vntsd_group_t
*groupp
;
558 (void) mutex_lock(&vntsdp
->lock
);
559 groupp
= vntsd_que_find(vntsdp
->grouppq
,
560 (compare_func_t
)find_cons_group_by_cons_no
,
562 (void) mutex_unlock(&vntsdp
->lock
);
564 if (groupp
== NULL
) {
569 /* group exists, if console exists? */
570 (void) mutex_lock(&groupp
->lock
);
571 consp
= vntsd_que_find(groupp
->conspq
,
572 (compare_func_t
)deleted_cons_by_consno
, &cons_no
);
575 /* no such console */
576 (void) mutex_unlock(&groupp
->lock
);
580 /* console exists - mark console for main thread to delete it */
581 (void) mutex_lock(&consp
->lock
);
583 if (consp
->status
& VNTSD_CONS_DELETED
) {
585 (void) mutex_unlock(&consp
->lock
);
586 (void) mutex_unlock(&groupp
->lock
);
590 consp
->status
|= VNTSD_CONS_DELETED
;
591 groupp
->status
|= VNTSD_GROUP_CLEAN_CONS
;
593 (void) mutex_unlock(&consp
->lock
);
594 (void) mutex_unlock(&groupp
->lock
);
600 do_add_cons(vntsd_t
*vntsdp
, int cons_no
)
602 vcc_console_t console
;
603 vntsd_group_t
*groupp
;
605 char err_msg
[VNTSD_LINE_LEN
];
608 (void) snprintf(err_msg
, sizeof (err_msg
),
609 "do_add_cons():Can not add console=%d", cons_no
);
611 /* get console configuration from vcc */
613 if ((rv
= vntsd_vcc_ioctl(VCC_CONS_INFO
, cons_no
, (void *)&console
))
615 vntsd_log(rv
, err_msg
);
619 /* clean up the console if console was deleted and added again */
620 delete_cons_before_add(vntsdp
, console
.cons_no
);
622 /* initialize console */
624 if ((rv
= alloc_cons_with_group(vntsdp
, &console
, &groupp
)) !=
626 /* no memory to add this new console */
627 vntsd_log(rv
, err_msg
);
631 if (groupp
!= NULL
) {
633 /* create listen thread for this console */
634 if (create_listen_thread(groupp
)) {
635 vntsd_log(VNTSD_ERR_CREATE_LISTEN_THR
, err_msg
);
644 vntsd_daemon_wakeup(vntsd_t
*vntsdp
)
647 vcc_response_t inq_data
;
649 /* reason to wake up */
650 if (vntsd_vcc_ioctl(VCC_INQUIRY
, 0, (void *)&inq_data
) !=
652 vntsd_log(VNTSD_ERR_VCC_IOCTL
, "vntsd_daemon_wakeup()");
656 D1(stderr
, "t@%d vntsd_daemon_wakup:msg %d port %x\n", thr_self(),
657 inq_data
.reason
, inq_data
.cons_no
);
659 switch (inq_data
.reason
) {
662 do_add_cons(vntsdp
, inq_data
.cons_no
);
665 case VCC_CONS_MISS_ADDED
:
666 /* an added port was deleted before vntsd can process it */
670 DERR(stderr
, "t@%d daemon_wakeup:ioctl_unknown %d\n",
671 thr_self(), inq_data
.reason
);
672 vntsd_log(VNTSD_ERR_UNKNOWN_CMD
, "from vcc\n");
677 /* initial console configuration */
679 vntsd_get_config(vntsd_t
*vntsdp
)
684 vcc_console_t
*consp
;
685 vntsd_group_t
*groupp
;
687 /* num of consoles */
690 if (vntsd_vcc_ioctl(VCC_NUM_CONSOLE
, 0, (void *)&num_cons
) !=
692 vntsd_log(VNTSD_ERR_VCC_IOCTL
, "VCC_NUM_CONSOLE failed\n");
696 D3(stderr
, "get_config:num_cons=%d", num_cons
);
702 /* allocate memory for all consoles */
703 consp
= malloc(num_cons
*sizeof (vcc_console_t
));
706 vntsd_log(VNTSD_ERR_NO_MEM
, "for console table.");
710 /* get console table */
711 if (vntsd_vcc_ioctl(VCC_CONS_TBL
, 0, (void *)consp
) != VNTSD_SUCCESS
) {
712 vntsd_log(VNTSD_ERR_VCC_IOCTL
, " VCC_CONS_TBL "
713 "for console table\n");
717 /* intialize groups and consoles */
718 for (i
= 0; i
< num_cons
; i
++) {
719 if (alloc_cons_with_group(vntsdp
, &consp
[i
], &groupp
)
721 vntsd_log(VNTSD_ERR_ADD_CONS_FAILED
, "get_config");
725 /* create listen thread for each group */
726 (void) mutex_lock(&vntsdp
->lock
);
729 groupp
= vntsd_que_walk(vntsdp
->grouppq
,
730 (el_func_t
)create_listen_thread
);
731 if (groupp
== NULL
) {
734 vntsd_log(VNTSD_ERR_CREATE_LISTEN_THR
, "get config()");
737 (void) mutex_unlock(&vntsdp
->lock
);