xref: /onnv-gate/usr/src/cmd/fs.d/smbclnt/smbiod/smbiod.c (revision 12556:b7f7250e66fe)
110023SGordon.Ross@Sun.COM /*
210023SGordon.Ross@Sun.COM  * CDDL HEADER START
310023SGordon.Ross@Sun.COM  *
410023SGordon.Ross@Sun.COM  * The contents of this file are subject to the terms of the
510023SGordon.Ross@Sun.COM  * Common Development and Distribution License (the "License").
610023SGordon.Ross@Sun.COM  * You may not use this file except in compliance with the License.
710023SGordon.Ross@Sun.COM  *
810023SGordon.Ross@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
910023SGordon.Ross@Sun.COM  * or http://www.opensolaris.org/os/licensing.
1010023SGordon.Ross@Sun.COM  * See the License for the specific language governing permissions
1110023SGordon.Ross@Sun.COM  * and limitations under the License.
1210023SGordon.Ross@Sun.COM  *
1310023SGordon.Ross@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
1410023SGordon.Ross@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1510023SGordon.Ross@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
1610023SGordon.Ross@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
1710023SGordon.Ross@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
1810023SGordon.Ross@Sun.COM  *
1910023SGordon.Ross@Sun.COM  * CDDL HEADER END
2010023SGordon.Ross@Sun.COM  */
2110023SGordon.Ross@Sun.COM 
2210023SGordon.Ross@Sun.COM /*
2312140SGordon.Ross@Sun.COM  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
2410023SGordon.Ross@Sun.COM  */
2510023SGordon.Ross@Sun.COM 
2610023SGordon.Ross@Sun.COM /*
27*12556SGordon.Ross@Sun.COM  * SMBFS I/O Daemon (Per-user IOD)
2810023SGordon.Ross@Sun.COM  */
2910023SGordon.Ross@Sun.COM 
3010023SGordon.Ross@Sun.COM #include <sys/types.h>
3110023SGordon.Ross@Sun.COM #include <sys/stat.h>
3210023SGordon.Ross@Sun.COM #include <sys/note.h>
3310023SGordon.Ross@Sun.COM 
3410023SGordon.Ross@Sun.COM #include <errno.h>
3510023SGordon.Ross@Sun.COM #include <fcntl.h>
3610023SGordon.Ross@Sun.COM #include <signal.h>
3710023SGordon.Ross@Sun.COM #include <stdarg.h>
3810023SGordon.Ross@Sun.COM #include <stdio.h>
3910023SGordon.Ross@Sun.COM #include <string.h>
4010023SGordon.Ross@Sun.COM #include <strings.h>
4110023SGordon.Ross@Sun.COM #include <stdlib.h>
4210023SGordon.Ross@Sun.COM #include <synch.h>
4310023SGordon.Ross@Sun.COM #include <time.h>
4410023SGordon.Ross@Sun.COM #include <unistd.h>
4510023SGordon.Ross@Sun.COM #include <ucred.h>
4610023SGordon.Ross@Sun.COM #include <err.h>
4710023SGordon.Ross@Sun.COM #include <door.h>
48*12556SGordon.Ross@Sun.COM #include <libscf.h>
49*12556SGordon.Ross@Sun.COM #include <locale.h>
5010023SGordon.Ross@Sun.COM #include <thread.h>
5110023SGordon.Ross@Sun.COM 
5210023SGordon.Ross@Sun.COM #include <netsmb/smb_lib.h>
5310023SGordon.Ross@Sun.COM 
5410023SGordon.Ross@Sun.COM #define	DPRINT(...)	do \
5510023SGordon.Ross@Sun.COM { \
5610023SGordon.Ross@Sun.COM 	if (smb_debug) \
5710023SGordon.Ross@Sun.COM 		fprintf(stderr, __VA_ARGS__); \
5810023SGordon.Ross@Sun.COM 	_NOTE(CONSTCOND) \
5910023SGordon.Ross@Sun.COM } while (0)
6010023SGordon.Ross@Sun.COM 
6110023SGordon.Ross@Sun.COM mutex_t	iod_mutex = DEFAULTMUTEX;
6210023SGordon.Ross@Sun.COM int iod_thr_count;	/* threads, excluding main */
6310023SGordon.Ross@Sun.COM int iod_terminating;
6412140SGordon.Ross@Sun.COM int iod_alarm_time = 30; /* sec. */
6510023SGordon.Ross@Sun.COM 
6610023SGordon.Ross@Sun.COM void iod_dispatch(void *cookie, char *argp, size_t argsz,
6710023SGordon.Ross@Sun.COM     door_desc_t *dp, uint_t n_desc);
6810023SGordon.Ross@Sun.COM int iod_newvc(smb_iod_ssn_t *clnt_ssn);
6910023SGordon.Ross@Sun.COM void * iod_work(void *arg);
7010023SGordon.Ross@Sun.COM 
7110023SGordon.Ross@Sun.COM int
main(int argc,char ** argv)7210023SGordon.Ross@Sun.COM main(int argc, char **argv)
7310023SGordon.Ross@Sun.COM {
7410023SGordon.Ross@Sun.COM 	sigset_t oldmask, tmpmask;
7510023SGordon.Ross@Sun.COM 	char *env, *door_path = NULL;
76*12556SGordon.Ross@Sun.COM 	int door_fd = -1;
77*12556SGordon.Ross@Sun.COM 	int err, sig;
78*12556SGordon.Ross@Sun.COM 	int rc = SMF_EXIT_ERR_FATAL;
79*12556SGordon.Ross@Sun.COM 	boolean_t attached = B_FALSE;
80*12556SGordon.Ross@Sun.COM 
81*12556SGordon.Ross@Sun.COM 	/* set locale and text domain for i18n */
82*12556SGordon.Ross@Sun.COM 	(void) setlocale(LC_ALL, "");
83*12556SGordon.Ross@Sun.COM 	(void) textdomain(TEXT_DOMAIN);
8410023SGordon.Ross@Sun.COM 
8510023SGordon.Ross@Sun.COM 	/* Debugging support. */
8610023SGordon.Ross@Sun.COM 	if ((env = getenv("SMBFS_DEBUG")) != NULL) {
8710023SGordon.Ross@Sun.COM 		smb_debug = atoi(env);
8810023SGordon.Ross@Sun.COM 		if (smb_debug < 1)
8910023SGordon.Ross@Sun.COM 			smb_debug = 1;
9012140SGordon.Ross@Sun.COM 		iod_alarm_time = 300;
9110023SGordon.Ross@Sun.COM 	}
9210023SGordon.Ross@Sun.COM 
9310023SGordon.Ross@Sun.COM 	/*
94*12556SGordon.Ross@Sun.COM 	 * If a user runs this command (i.e. by accident)
95*12556SGordon.Ross@Sun.COM 	 * don't interfere with any already running IOD.
9610023SGordon.Ross@Sun.COM 	 */
9710023SGordon.Ross@Sun.COM 	err = smb_iod_open_door(&door_fd);
9810023SGordon.Ross@Sun.COM 	if (err == 0) {
9910023SGordon.Ross@Sun.COM 		close(door_fd);
10010023SGordon.Ross@Sun.COM 		door_fd = -1;
101*12556SGordon.Ross@Sun.COM 		DPRINT("%s: already running\n", argv[0]);
102*12556SGordon.Ross@Sun.COM 		exit(SMF_EXIT_OK);
10310023SGordon.Ross@Sun.COM 	}
10410023SGordon.Ross@Sun.COM 
10510023SGordon.Ross@Sun.COM 	/*
106*12556SGordon.Ross@Sun.COM 	 * Want all signals blocked, as we're doing
107*12556SGordon.Ross@Sun.COM 	 * synchronous delivery via sigwait below.
10810023SGordon.Ross@Sun.COM 	 */
10910023SGordon.Ross@Sun.COM 	sigfillset(&tmpmask);
11010023SGordon.Ross@Sun.COM 	sigprocmask(SIG_BLOCK, &tmpmask, &oldmask);
11110023SGordon.Ross@Sun.COM 
11210023SGordon.Ross@Sun.COM 	/* Setup the door service. */
113*12556SGordon.Ross@Sun.COM 	door_path = smb_iod_door_path();
114*12556SGordon.Ross@Sun.COM 	door_fd = door_create(iod_dispatch, NULL,
115*12556SGordon.Ross@Sun.COM 	    DOOR_REFUSE_DESC | DOOR_NO_CANCEL);
116*12556SGordon.Ross@Sun.COM 	if (door_fd == -1) {
117*12556SGordon.Ross@Sun.COM 		perror("iod door_create");
118*12556SGordon.Ross@Sun.COM 		goto out;
11910023SGordon.Ross@Sun.COM 	}
12010023SGordon.Ross@Sun.COM 	fdetach(door_path);
12110023SGordon.Ross@Sun.COM 	if (fattach(door_fd, door_path) < 0) {
122*12556SGordon.Ross@Sun.COM 		fprintf(stderr, "%s: fattach failed, %s\n",
123*12556SGordon.Ross@Sun.COM 		    door_path, strerror(errno));
124*12556SGordon.Ross@Sun.COM 		goto out;
12510023SGordon.Ross@Sun.COM 	}
126*12556SGordon.Ross@Sun.COM 	attached = B_TRUE;
127*12556SGordon.Ross@Sun.COM 
128*12556SGordon.Ross@Sun.COM 	/* Initializations done. */
129*12556SGordon.Ross@Sun.COM 	rc = SMF_EXIT_OK;
13010023SGordon.Ross@Sun.COM 
13110023SGordon.Ross@Sun.COM 	/*
13210023SGordon.Ross@Sun.COM 	 * Post the initial alarm, and then just
13310023SGordon.Ross@Sun.COM 	 * wait for signals.
13410023SGordon.Ross@Sun.COM 	 */
13512140SGordon.Ross@Sun.COM 	alarm(iod_alarm_time);
13610023SGordon.Ross@Sun.COM again:
13710023SGordon.Ross@Sun.COM 	sig = sigwait(&tmpmask);
13810023SGordon.Ross@Sun.COM 	DPRINT("main: sig=%d\n", sig);
139*12556SGordon.Ross@Sun.COM 	switch (sig) {
140*12556SGordon.Ross@Sun.COM 	case SIGCONT:
141*12556SGordon.Ross@Sun.COM 		goto again;
142*12556SGordon.Ross@Sun.COM 
143*12556SGordon.Ross@Sun.COM 	case SIGALRM:
144*12556SGordon.Ross@Sun.COM 		/* No threads active for a while. */
145*12556SGordon.Ross@Sun.COM 		mutex_lock(&iod_mutex);
146*12556SGordon.Ross@Sun.COM 		if (iod_thr_count > 0) {
147*12556SGordon.Ross@Sun.COM 			/*
148*12556SGordon.Ross@Sun.COM 			 * Door call thread creation raced with
149*12556SGordon.Ross@Sun.COM 			 * the alarm.  Ignore this alaram.
150*12556SGordon.Ross@Sun.COM 			 */
151*12556SGordon.Ross@Sun.COM 			mutex_unlock(&iod_mutex);
152*12556SGordon.Ross@Sun.COM 			goto again;
153*12556SGordon.Ross@Sun.COM 		}
154*12556SGordon.Ross@Sun.COM 		/* Prevent a race with iod_thr_count */
155*12556SGordon.Ross@Sun.COM 		iod_terminating = 1;
156*12556SGordon.Ross@Sun.COM 		mutex_unlock(&iod_mutex);
157*12556SGordon.Ross@Sun.COM 		break;
158*12556SGordon.Ross@Sun.COM 
159*12556SGordon.Ross@Sun.COM 	case SIGINT:
160*12556SGordon.Ross@Sun.COM 	case SIGTERM:
161*12556SGordon.Ross@Sun.COM 		break;	/* normal termination */
162*12556SGordon.Ross@Sun.COM 
163*12556SGordon.Ross@Sun.COM 	default:
164*12556SGordon.Ross@Sun.COM 		/* Unexpected signal. */
165*12556SGordon.Ross@Sun.COM 		fprintf(stderr, "iod_main: unexpected sig=%d\n", sig);
166*12556SGordon.Ross@Sun.COM 		break;
167*12556SGordon.Ross@Sun.COM 	}
168*12556SGordon.Ross@Sun.COM 
169*12556SGordon.Ross@Sun.COM out:
170*12556SGordon.Ross@Sun.COM 	iod_terminating = 1;
171*12556SGordon.Ross@Sun.COM 	if (attached)
172*12556SGordon.Ross@Sun.COM 		fdetach(door_path);
173*12556SGordon.Ross@Sun.COM 	if (door_fd != -1)
174*12556SGordon.Ross@Sun.COM 		door_revoke(door_fd);
17510023SGordon.Ross@Sun.COM 
17610023SGordon.Ross@Sun.COM 	/*
177*12556SGordon.Ross@Sun.COM 	 * We need a reference in -lumem to satisfy check_rtime,
178*12556SGordon.Ross@Sun.COM 	 * else we get build hoise.  This is sufficient.
17910023SGordon.Ross@Sun.COM 	 */
180*12556SGordon.Ross@Sun.COM 	free(NULL);
18110023SGordon.Ross@Sun.COM 
18210023SGordon.Ross@Sun.COM 	return (rc);
18310023SGordon.Ross@Sun.COM }
18410023SGordon.Ross@Sun.COM 
18510023SGordon.Ross@Sun.COM /*ARGSUSED*/
18610023SGordon.Ross@Sun.COM void
iod_dispatch(void * cookie,char * argp,size_t argsz,door_desc_t * dp,uint_t n_desc)18710023SGordon.Ross@Sun.COM iod_dispatch(void *cookie, char *argp, size_t argsz,
18810023SGordon.Ross@Sun.COM     door_desc_t *dp, uint_t n_desc)
18910023SGordon.Ross@Sun.COM {
19010023SGordon.Ross@Sun.COM 	smb_iod_ssn_t *ssn;
19110023SGordon.Ross@Sun.COM 	ucred_t *ucred;
19210023SGordon.Ross@Sun.COM 	uid_t cl_uid;
19310023SGordon.Ross@Sun.COM 	int rc;
19410023SGordon.Ross@Sun.COM 
19510023SGordon.Ross@Sun.COM 	/*
19610023SGordon.Ross@Sun.COM 	 * Verify that the calling process has the same UID.
19710023SGordon.Ross@Sun.COM 	 * Paranoia:  The door we created has mode 0600, so
19810023SGordon.Ross@Sun.COM 	 * this check is probably redundant.
19910023SGordon.Ross@Sun.COM 	 */
20010023SGordon.Ross@Sun.COM 	ucred = NULL;
20110023SGordon.Ross@Sun.COM 	if (door_ucred(&ucred) != 0) {
20210023SGordon.Ross@Sun.COM 		rc = EACCES;
20310023SGordon.Ross@Sun.COM 		goto out;
20410023SGordon.Ross@Sun.COM 	}
20510023SGordon.Ross@Sun.COM 	cl_uid = ucred_getruid(ucred);
20610023SGordon.Ross@Sun.COM 	ucred_free(ucred);
20710023SGordon.Ross@Sun.COM 	ucred = NULL;
20810023SGordon.Ross@Sun.COM 	if (cl_uid != getuid()) {
20910023SGordon.Ross@Sun.COM 		DPRINT("iod_dispatch: wrong UID\n");
21010023SGordon.Ross@Sun.COM 		rc = EACCES;
21110023SGordon.Ross@Sun.COM 		goto out;
21210023SGordon.Ross@Sun.COM 	}
21310023SGordon.Ross@Sun.COM 
21410023SGordon.Ross@Sun.COM 	/*
21510023SGordon.Ross@Sun.COM 	 * The library uses a NULL arg call to check if
216*12556SGordon.Ross@Sun.COM 	 * the daemon is running.  Just return zero.
21710023SGordon.Ross@Sun.COM 	 */
21810023SGordon.Ross@Sun.COM 	if (argp == NULL) {
21910023SGordon.Ross@Sun.COM 		rc = 0;
22010023SGordon.Ross@Sun.COM 		goto out;
22110023SGordon.Ross@Sun.COM 	}
22210023SGordon.Ross@Sun.COM 
22310023SGordon.Ross@Sun.COM 	/*
22410023SGordon.Ross@Sun.COM 	 * Otherwise, the arg must be the (fixed size)
22510023SGordon.Ross@Sun.COM 	 * smb_iod_ssn_t
22610023SGordon.Ross@Sun.COM 	 */
22710023SGordon.Ross@Sun.COM 	if (argsz != sizeof (*ssn)) {
22810023SGordon.Ross@Sun.COM 		rc = EINVAL;
22910023SGordon.Ross@Sun.COM 		goto out;
23010023SGordon.Ross@Sun.COM 	}
23110023SGordon.Ross@Sun.COM 
23210023SGordon.Ross@Sun.COM 	mutex_lock(&iod_mutex);
23310023SGordon.Ross@Sun.COM 	if (iod_terminating) {
23410023SGordon.Ross@Sun.COM 		mutex_unlock(&iod_mutex);
23510023SGordon.Ross@Sun.COM 		DPRINT("iod_dispatch: terminating\n");
23610023SGordon.Ross@Sun.COM 		rc = EINTR;
23710023SGordon.Ross@Sun.COM 		goto out;
23810023SGordon.Ross@Sun.COM 	}
23910023SGordon.Ross@Sun.COM 	if (iod_thr_count++ == 0) {
24010023SGordon.Ross@Sun.COM 		alarm(0);
24110023SGordon.Ross@Sun.COM 		DPRINT("iod_dispatch: cancelled alarm\n");
24210023SGordon.Ross@Sun.COM 	}
24310023SGordon.Ross@Sun.COM 	mutex_unlock(&iod_mutex);
24410023SGordon.Ross@Sun.COM 
24510023SGordon.Ross@Sun.COM 	ssn = (void *) argp;
24610023SGordon.Ross@Sun.COM 	rc = iod_newvc(ssn);
24710023SGordon.Ross@Sun.COM 
24810023SGordon.Ross@Sun.COM 	mutex_lock(&iod_mutex);
24910023SGordon.Ross@Sun.COM 	if (--iod_thr_count == 0) {
25010023SGordon.Ross@Sun.COM 		DPRINT("iod_dispatch: schedule alarm\n");
25112140SGordon.Ross@Sun.COM 		alarm(iod_alarm_time);
25210023SGordon.Ross@Sun.COM 	}
25310023SGordon.Ross@Sun.COM 	mutex_unlock(&iod_mutex);
25410023SGordon.Ross@Sun.COM 
25510023SGordon.Ross@Sun.COM out:
25610023SGordon.Ross@Sun.COM 	door_return((void *)&rc, sizeof (rc), NULL, 0);
25710023SGordon.Ross@Sun.COM }
25810023SGordon.Ross@Sun.COM 
25910023SGordon.Ross@Sun.COM /*
26010023SGordon.Ross@Sun.COM  * Try making a connection with the server described by
26110023SGordon.Ross@Sun.COM  * the info in the smb_iod_ssn_t arg.  If successful,
26210023SGordon.Ross@Sun.COM  * start an IOD thread to service it, then return to
26310023SGordon.Ross@Sun.COM  * the client side of the door.
26410023SGordon.Ross@Sun.COM  */
26510023SGordon.Ross@Sun.COM int
iod_newvc(smb_iod_ssn_t * clnt_ssn)26610023SGordon.Ross@Sun.COM iod_newvc(smb_iod_ssn_t *clnt_ssn)
26710023SGordon.Ross@Sun.COM {
26810023SGordon.Ross@Sun.COM 	smb_ctx_t *ctx;
26910023SGordon.Ross@Sun.COM 	thread_t tid;
27010023SGordon.Ross@Sun.COM 	int err;
27110023SGordon.Ross@Sun.COM 
27210023SGordon.Ross@Sun.COM 
27310023SGordon.Ross@Sun.COM 	/*
27410023SGordon.Ross@Sun.COM 	 * This needs to essentially "clone" the smb_ctx_t
27510023SGordon.Ross@Sun.COM 	 * from the client side of the door, or at least
27610023SGordon.Ross@Sun.COM 	 * as much of it as we need while creating a VC.
27710023SGordon.Ross@Sun.COM 	 */
27810023SGordon.Ross@Sun.COM 	err = smb_ctx_alloc(&ctx);
27910023SGordon.Ross@Sun.COM 	if (err)
28010023SGordon.Ross@Sun.COM 		return (err);
28110023SGordon.Ross@Sun.COM 	bcopy(clnt_ssn, &ctx->ct_iod_ssn, sizeof (ctx->ct_iod_ssn));
28210023SGordon.Ross@Sun.COM 
28310023SGordon.Ross@Sun.COM 	/*
28410023SGordon.Ross@Sun.COM 	 * Do the initial connection setup here, so we can
28510023SGordon.Ross@Sun.COM 	 * report the outcome to the door client.
28610023SGordon.Ross@Sun.COM 	 */
28710023SGordon.Ross@Sun.COM 	err = smb_iod_connect(ctx);
28810023SGordon.Ross@Sun.COM 	if (err != 0)
28910023SGordon.Ross@Sun.COM 		goto out;
29010023SGordon.Ross@Sun.COM 
29110023SGordon.Ross@Sun.COM 	/*
29210023SGordon.Ross@Sun.COM 	 * Create the driver session now, so we don't
29310023SGordon.Ross@Sun.COM 	 * race with the door client findvc call.
29410023SGordon.Ross@Sun.COM 	 */
29510023SGordon.Ross@Sun.COM 	if ((err = smb_ctx_gethandle(ctx)) != 0)
29610023SGordon.Ross@Sun.COM 		goto out;
29710023SGordon.Ross@Sun.COM 	if (ioctl(ctx->ct_dev_fd, SMBIOC_SSN_CREATE, &ctx->ct_ssn) < 0) {
29810023SGordon.Ross@Sun.COM 		err = errno;
29910023SGordon.Ross@Sun.COM 		goto out;
30010023SGordon.Ross@Sun.COM 	}
30110023SGordon.Ross@Sun.COM 
30210023SGordon.Ross@Sun.COM 	/* The rest happens in the iod_work thread. */
30310023SGordon.Ross@Sun.COM 	err = thr_create(NULL, 0, iod_work, ctx, THR_DETACHED, &tid);
30410023SGordon.Ross@Sun.COM 	if (err == 0) {
30510023SGordon.Ross@Sun.COM 		/*
30610023SGordon.Ross@Sun.COM 		 * Given to the new thread.
30710023SGordon.Ross@Sun.COM 		 * free at end of iod_work
30810023SGordon.Ross@Sun.COM 		 */
30910023SGordon.Ross@Sun.COM 		ctx = NULL;
31010023SGordon.Ross@Sun.COM 	}
31110023SGordon.Ross@Sun.COM 
31210023SGordon.Ross@Sun.COM out:
31310023SGordon.Ross@Sun.COM 	if (ctx)
31410023SGordon.Ross@Sun.COM 		smb_ctx_free(ctx);
31510023SGordon.Ross@Sun.COM 
31610023SGordon.Ross@Sun.COM 	return (err);
31710023SGordon.Ross@Sun.COM }
31810023SGordon.Ross@Sun.COM 
31910023SGordon.Ross@Sun.COM /*
32010023SGordon.Ross@Sun.COM  * Be the reader thread for some VC.
32110023SGordon.Ross@Sun.COM  *
32210023SGordon.Ross@Sun.COM  * This is started by a door call thread, which means
32310023SGordon.Ross@Sun.COM  * this is always at least the 2nd thread, therefore
32410023SGordon.Ross@Sun.COM  * it should never see thr_count==0 or terminating.
32510023SGordon.Ross@Sun.COM  */
32610023SGordon.Ross@Sun.COM void *
iod_work(void * arg)32710023SGordon.Ross@Sun.COM iod_work(void *arg)
32810023SGordon.Ross@Sun.COM {
32910023SGordon.Ross@Sun.COM 	smb_ctx_t *ctx = arg;
33010023SGordon.Ross@Sun.COM 
33110023SGordon.Ross@Sun.COM 	mutex_lock(&iod_mutex);
33210023SGordon.Ross@Sun.COM 	if (iod_thr_count++ == 0) {
33310023SGordon.Ross@Sun.COM 		alarm(0);
33410023SGordon.Ross@Sun.COM 		DPRINT("iod_work: cancelled alarm\n");
33510023SGordon.Ross@Sun.COM 	}
33610023SGordon.Ross@Sun.COM 	mutex_unlock(&iod_mutex);
33710023SGordon.Ross@Sun.COM 
33810023SGordon.Ross@Sun.COM 	(void) smb_iod_work(ctx);
33910023SGordon.Ross@Sun.COM 
34010023SGordon.Ross@Sun.COM 	mutex_lock(&iod_mutex);
34110023SGordon.Ross@Sun.COM 	if (--iod_thr_count == 0) {
34210023SGordon.Ross@Sun.COM 		DPRINT("iod_work: schedule alarm\n");
34312140SGordon.Ross@Sun.COM 		alarm(iod_alarm_time);
34410023SGordon.Ross@Sun.COM 	}
34510023SGordon.Ross@Sun.COM 	mutex_unlock(&iod_mutex);
34610023SGordon.Ross@Sun.COM 
34710023SGordon.Ross@Sun.COM 	smb_ctx_free(ctx);
34810023SGordon.Ross@Sun.COM 	return (NULL);
34910023SGordon.Ross@Sun.COM }
350