xref: /freebsd/sys/netsmb/smb_iod.c (revision 315ee00f)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2000-2001 Boris Popov
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/endian.h>
33 #include <sys/proc.h>
34 #include <sys/kernel.h>
35 #include <sys/kthread.h>
36 #include <sys/malloc.h>
37 #include <sys/mbuf.h>
38 #include <sys/unistd.h>
39 
40 #include <netsmb/smb.h>
41 #include <netsmb/smb_conn.h>
42 #include <netsmb/smb_rq.h>
43 #include <netsmb/smb_tran.h>
44 #include <netsmb/smb_trantcp.h>
45 
46 #define SMBIOD_SLEEP_TIMO	2
47 #define	SMBIOD_PING_TIMO	60	/* seconds */
48 
49 #define	SMB_IOD_EVLOCKPTR(iod)	(&((iod)->iod_evlock))
50 #define	SMB_IOD_EVLOCK(iod)	smb_sl_lock(&((iod)->iod_evlock))
51 #define	SMB_IOD_EVUNLOCK(iod)	smb_sl_unlock(&((iod)->iod_evlock))
52 
53 #define	SMB_IOD_RQLOCKPTR(iod)	(&((iod)->iod_rqlock))
54 #define	SMB_IOD_RQLOCK(iod)	smb_sl_lock(&((iod)->iod_rqlock))
55 #define	SMB_IOD_RQUNLOCK(iod)	smb_sl_unlock(&((iod)->iod_rqlock))
56 
57 #define	smb_iod_wakeup(iod)	wakeup(&(iod)->iod_flags)
58 
59 static MALLOC_DEFINE(M_SMBIOD, "SMBIOD", "SMB network io daemon");
60 
61 static int smb_iod_next;
62 
63 static int  smb_iod_sendall(struct smbiod *iod);
64 static int  smb_iod_disconnect(struct smbiod *iod);
65 static void smb_iod_thread(void *);
66 
67 static __inline void
68 smb_iod_rqprocessed(struct smb_rq *rqp, int error)
69 {
70 	SMBRQ_SLOCK(rqp);
71 	rqp->sr_lerror = error;
72 	rqp->sr_rpgen++;
73 	rqp->sr_state = SMBRQ_NOTIFIED;
74 	wakeup(&rqp->sr_state);
75 	SMBRQ_SUNLOCK(rqp);
76 }
77 
78 static void
79 smb_iod_invrq(struct smbiod *iod)
80 {
81 	struct smb_rq *rqp;
82 
83 	/*
84 	 * Invalidate all outstanding requests for this connection
85 	 */
86 	SMB_IOD_RQLOCK(iod);
87 	TAILQ_FOREACH(rqp, &iod->iod_rqlist, sr_link) {
88 		rqp->sr_flags |= SMBR_RESTART;
89 		smb_iod_rqprocessed(rqp, ENOTCONN);
90 	}
91 	SMB_IOD_RQUNLOCK(iod);
92 }
93 
94 static void
95 smb_iod_closetran(struct smbiod *iod)
96 {
97 	struct smb_vc *vcp = iod->iod_vc;
98 	struct thread *td = iod->iod_td;
99 
100 	if (vcp->vc_tdata == NULL)
101 		return;
102 	SMB_TRAN_DISCONNECT(vcp, td);
103 	SMB_TRAN_DONE(vcp, td);
104 	vcp->vc_tdata = NULL;
105 }
106 
107 static void
108 smb_iod_dead(struct smbiod *iod)
109 {
110 	iod->iod_state = SMBIOD_ST_DEAD;
111 	smb_iod_closetran(iod);
112 	smb_iod_invrq(iod);
113 }
114 
115 static int
116 smb_iod_connect(struct smbiod *iod)
117 {
118 	struct smb_vc *vcp = iod->iod_vc;
119 	struct thread *td = iod->iod_td;
120 	int error;
121 
122 	SMBIODEBUG("%d\n", iod->iod_state);
123 	switch(iod->iod_state) {
124 	    case SMBIOD_ST_VCACTIVE:
125 		SMBERROR("called for already opened connection\n");
126 		return EISCONN;
127 	    case SMBIOD_ST_DEAD:
128 		return ENOTCONN;	/* XXX: last error code ? */
129 	    default:
130 		break;
131 	}
132 	vcp->vc_genid++;
133 	error = 0;
134 
135 	error = (int)SMB_TRAN_CREATE(vcp, td);
136 	if (error)
137 		goto fail;
138 	SMBIODEBUG("tcreate\n");
139 	if (vcp->vc_laddr) {
140 		error = (int)SMB_TRAN_BIND(vcp, vcp->vc_laddr, td);
141 		if (error)
142 			goto fail;
143 	}
144 	SMBIODEBUG("tbind\n");
145 	error = (int)SMB_TRAN_CONNECT(vcp, vcp->vc_paddr, td);
146 	if (error)
147 		goto fail;
148 	SMB_TRAN_SETPARAM(vcp, SMBTP_SELECTID, &iod->iod_flags);
149 	iod->iod_state = SMBIOD_ST_TRANACTIVE;
150 	SMBIODEBUG("tconnect\n");
151 	/* vcp->vc_mid = 0;*/
152 	error = (int)smb_smb_negotiate(vcp, &iod->iod_scred);
153 	if (error)
154 		goto fail;
155 	SMBIODEBUG("snegotiate\n");
156 	error = (int)smb_smb_ssnsetup(vcp, &iod->iod_scred);
157 	if (error)
158 		goto fail;
159 	iod->iod_state = SMBIOD_ST_VCACTIVE;
160 	SMBIODEBUG("completed\n");
161 	smb_iod_invrq(iod);
162 	return (0);
163 
164  fail:
165 	smb_iod_dead(iod);
166 	return (error);
167 }
168 
169 static int
170 smb_iod_disconnect(struct smbiod *iod)
171 {
172 	struct smb_vc *vcp = iod->iod_vc;
173 
174 	SMBIODEBUG("\n");
175 	if (iod->iod_state == SMBIOD_ST_VCACTIVE) {
176 		smb_smb_ssnclose(vcp, &iod->iod_scred);
177 		iod->iod_state = SMBIOD_ST_TRANACTIVE;
178 	}
179 	vcp->vc_smbuid = SMB_UID_UNKNOWN;
180 	smb_iod_closetran(iod);
181 	iod->iod_state = SMBIOD_ST_NOTCONN;
182 	return 0;
183 }
184 
185 static int
186 smb_iod_treeconnect(struct smbiod *iod, struct smb_share *ssp)
187 {
188 	int error;
189 
190 	if (iod->iod_state != SMBIOD_ST_VCACTIVE) {
191 		if (iod->iod_state != SMBIOD_ST_DEAD)
192 			return ENOTCONN;
193 		iod->iod_state = SMBIOD_ST_RECONNECT;
194 		error = smb_iod_connect(iod);
195 		if (error)
196 			return error;
197 	}
198 	SMBIODEBUG("tree reconnect\n");
199 	SMBS_ST_LOCK(ssp);
200 	ssp->ss_flags |= SMBS_RECONNECTING;
201 	SMBS_ST_UNLOCK(ssp);
202 	error = smb_smb_treeconnect(ssp, &iod->iod_scred);
203 	SMBS_ST_LOCK(ssp);
204 	ssp->ss_flags &= ~SMBS_RECONNECTING;
205 	SMBS_ST_UNLOCK(ssp);
206 	wakeup(&ssp->ss_vcgenid);
207 	return error;
208 }
209 
210 static int
211 smb_iod_sendrq(struct smbiod *iod, struct smb_rq *rqp)
212 {
213 	struct thread *td = iod->iod_td;
214 	struct smb_vc *vcp = iod->iod_vc;
215 	struct smb_share *ssp = rqp->sr_share;
216 	struct mbuf *m;
217 	int error;
218 
219 	SMBIODEBUG("iod_state = %d\n", iod->iod_state);
220 	switch (iod->iod_state) {
221 	    case SMBIOD_ST_NOTCONN:
222 		smb_iod_rqprocessed(rqp, ENOTCONN);
223 		return 0;
224 	    case SMBIOD_ST_DEAD:
225 		iod->iod_state = SMBIOD_ST_RECONNECT;
226 		return 0;
227 	    case SMBIOD_ST_RECONNECT:
228 		return 0;
229 	    default:
230 		break;
231 	}
232 	if (rqp->sr_sendcnt == 0) {
233 #ifdef movedtoanotherplace
234 		if (vcp->vc_maxmux != 0 && iod->iod_muxcnt >= vcp->vc_maxmux)
235 			return 0;
236 #endif
237 		le16enc(rqp->sr_rqtid, ssp ? ssp->ss_tid : SMB_TID_UNKNOWN);
238 		le16enc(rqp->sr_rquid, vcp ? vcp->vc_smbuid : 0);
239 		mb_fixhdr(&rqp->sr_rq);
240 		if (vcp->vc_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE)
241 			smb_rq_sign(rqp);
242 	}
243 	if (rqp->sr_sendcnt++ > 5) {
244 		rqp->sr_flags |= SMBR_RESTART;
245 		smb_iod_rqprocessed(rqp, rqp->sr_lerror);
246 		/*
247 		 * If all attempts to send a request failed, then
248 		 * something is seriously hosed.
249 		 */
250 		return ENOTCONN;
251 	}
252 	SMBSDEBUG("M:%04x, P:%04x, U:%04x, T:%04x\n", rqp->sr_mid, 0, 0, 0);
253 	m_dumpm(rqp->sr_rq.mb_top);
254 	m = m_copym(rqp->sr_rq.mb_top, 0, M_COPYALL, M_WAITOK);
255 	error = rqp->sr_lerror = SMB_TRAN_SEND(vcp, m, td);
256 	if (error == 0) {
257 		getnanotime(&rqp->sr_timesent);
258 		iod->iod_lastrqsent = rqp->sr_timesent;
259 		rqp->sr_flags |= SMBR_SENT;
260 		rqp->sr_state = SMBRQ_SENT;
261 		return 0;
262 	}
263 	/*
264 	 * Check for fatal errors
265 	 */
266 	if (SMB_TRAN_FATAL(vcp, error)) {
267 		/*
268 		 * No further attempts should be made
269 		 */
270 		return ENOTCONN;
271 	}
272 	if (smb_rq_intr(rqp))
273 		smb_iod_rqprocessed(rqp, EINTR);
274 	return 0;
275 }
276 
277 /*
278  * Process incoming packets
279  */
280 static int
281 smb_iod_recvall(struct smbiod *iod)
282 {
283 	struct smb_vc *vcp = iod->iod_vc;
284 	struct thread *td = iod->iod_td;
285 	struct smb_rq *rqp;
286 	struct mbuf *m;
287 	u_char *hp;
288 	u_short mid;
289 	int error;
290 
291 	switch (iod->iod_state) {
292 	    case SMBIOD_ST_NOTCONN:
293 	    case SMBIOD_ST_DEAD:
294 	    case SMBIOD_ST_RECONNECT:
295 		return 0;
296 	    default:
297 		break;
298 	}
299 	for (;;) {
300 		m = NULL;
301 		error = SMB_TRAN_RECV(vcp, &m, td);
302 		if (error == EWOULDBLOCK)
303 			break;
304 		if (SMB_TRAN_FATAL(vcp, error)) {
305 			smb_iod_dead(iod);
306 			break;
307 		}
308 		if (error)
309 			break;
310 		if (m == NULL) {
311 			SMBERROR("tran return NULL without error\n");
312 			error = EPIPE;
313 			continue;
314 		}
315 		m = m_pullup(m, SMB_HDRLEN);
316 		if (m == NULL)
317 			continue;	/* wait for a good packet */
318 		/*
319 		 * Now we got an entire and possibly invalid SMB packet.
320 		 * Be careful while parsing it.
321 		 */
322 		m_dumpm(m);
323 		hp = mtod(m, u_char*);
324 		if (bcmp(hp, SMB_SIGNATURE, SMB_SIGLEN) != 0) {
325 			m_freem(m);
326 			continue;
327 		}
328 		mid = SMB_HDRMID(hp);
329 		SMBSDEBUG("mid %04x\n", (u_int)mid);
330 		SMB_IOD_RQLOCK(iod);
331 		TAILQ_FOREACH(rqp, &iod->iod_rqlist, sr_link) {
332 			if (rqp->sr_mid != mid)
333 				continue;
334 			SMBRQ_SLOCK(rqp);
335 			if (rqp->sr_rp.md_top == NULL) {
336 				md_initm(&rqp->sr_rp, m);
337 			} else {
338 				if (rqp->sr_flags & SMBR_MULTIPACKET) {
339 					md_append_record(&rqp->sr_rp, m);
340 				} else {
341 					SMBRQ_SUNLOCK(rqp);
342 					SMBERROR("duplicate response %d (ignored)\n", mid);
343 					break;
344 				}
345 			}
346 			SMBRQ_SUNLOCK(rqp);
347 			smb_iod_rqprocessed(rqp, 0);
348 			break;
349 		}
350 		SMB_IOD_RQUNLOCK(iod);
351 		if (rqp == NULL) {
352 			SMBERROR("drop resp with mid %d\n", (u_int)mid);
353 /*			smb_printrqlist(vcp);*/
354 			m_freem(m);
355 		}
356 	}
357 	/*
358 	 * check for interrupts
359 	 */
360 	SMB_IOD_RQLOCK(iod);
361 	TAILQ_FOREACH(rqp, &iod->iod_rqlist, sr_link) {
362 		if (smb_td_intr(rqp->sr_cred->scr_td)) {
363 			smb_iod_rqprocessed(rqp, EINTR);
364 		}
365 	}
366 	SMB_IOD_RQUNLOCK(iod);
367 	return 0;
368 }
369 
370 int
371 smb_iod_request(struct smbiod *iod, int event, void *ident)
372 {
373 	struct smbiod_event *evp;
374 	int error;
375 
376 	SMBIODEBUG("\n");
377 	evp = smb_zmalloc(sizeof(*evp), M_SMBIOD, M_WAITOK);
378 	evp->ev_type = event;
379 	evp->ev_ident = ident;
380 	SMB_IOD_EVLOCK(iod);
381 	STAILQ_INSERT_TAIL(&iod->iod_evlist, evp, ev_link);
382 	if ((event & SMBIOD_EV_SYNC) == 0) {
383 		SMB_IOD_EVUNLOCK(iod);
384 		smb_iod_wakeup(iod);
385 		return 0;
386 	}
387 	smb_iod_wakeup(iod);
388 	msleep(evp, SMB_IOD_EVLOCKPTR(iod), PWAIT | PDROP, "90evw", 0);
389 	error = evp->ev_error;
390 	free(evp, M_SMBIOD);
391 	return error;
392 }
393 
394 /*
395  * Place request in the queue.
396  * Request from smbiod have a high priority.
397  */
398 int
399 smb_iod_addrq(struct smb_rq *rqp)
400 {
401 	struct smb_vc *vcp = rqp->sr_vc;
402 	struct smbiod *iod = vcp->vc_iod;
403 	int error;
404 
405 	SMBIODEBUG("\n");
406 	if (rqp->sr_cred->scr_td != NULL &&
407 	    rqp->sr_cred->scr_td->td_proc == iod->iod_p) {
408 		rqp->sr_flags |= SMBR_INTERNAL;
409 		SMB_IOD_RQLOCK(iod);
410 		TAILQ_INSERT_HEAD(&iod->iod_rqlist, rqp, sr_link);
411 		SMB_IOD_RQUNLOCK(iod);
412 		for (;;) {
413 			if (smb_iod_sendrq(iod, rqp) != 0) {
414 				smb_iod_dead(iod);
415 				break;
416 			}
417 			/*
418 			 * we don't need to lock state field here
419 			 */
420 			if (rqp->sr_state != SMBRQ_NOTSENT)
421 				break;
422 			tsleep(&iod->iod_flags, PWAIT, "90sndw", hz);
423 		}
424 		if (rqp->sr_lerror)
425 			smb_iod_removerq(rqp);
426 		return rqp->sr_lerror;
427 	}
428 
429 	switch (iod->iod_state) {
430 	    case SMBIOD_ST_NOTCONN:
431 		return ENOTCONN;
432 	    case SMBIOD_ST_DEAD:
433 		error = smb_iod_request(vcp->vc_iod, SMBIOD_EV_CONNECT | SMBIOD_EV_SYNC, NULL);
434 		if (error)
435 			return error;
436 		return EXDEV;
437 	    default:
438 		break;
439 	}
440 
441 	SMB_IOD_RQLOCK(iod);
442 	for (;;) {
443 		if (vcp->vc_maxmux == 0) {
444 			SMBERROR("maxmux == 0\n");
445 			break;
446 		}
447 		if (iod->iod_muxcnt < vcp->vc_maxmux)
448 			break;
449 		iod->iod_muxwant++;
450 		msleep(&iod->iod_muxwant, SMB_IOD_RQLOCKPTR(iod),
451 		    PWAIT, "90mux", 0);
452 	}
453 	iod->iod_muxcnt++;
454 	TAILQ_INSERT_TAIL(&iod->iod_rqlist, rqp, sr_link);
455 	SMB_IOD_RQUNLOCK(iod);
456 	smb_iod_wakeup(iod);
457 	return 0;
458 }
459 
460 int
461 smb_iod_removerq(struct smb_rq *rqp)
462 {
463 	struct smb_vc *vcp = rqp->sr_vc;
464 	struct smbiod *iod = vcp->vc_iod;
465 
466 	SMBIODEBUG("\n");
467 	if (rqp->sr_flags & SMBR_INTERNAL) {
468 		SMB_IOD_RQLOCK(iod);
469 		TAILQ_REMOVE(&iod->iod_rqlist, rqp, sr_link);
470 		SMB_IOD_RQUNLOCK(iod);
471 		return 0;
472 	}
473 	SMB_IOD_RQLOCK(iod);
474 	while (rqp->sr_flags & SMBR_XLOCK) {
475 		rqp->sr_flags |= SMBR_XLOCKWANT;
476 		msleep(rqp, SMB_IOD_RQLOCKPTR(iod), PWAIT, "90xrm", 0);
477 	}
478 	TAILQ_REMOVE(&iod->iod_rqlist, rqp, sr_link);
479 	iod->iod_muxcnt--;
480 	if (iod->iod_muxwant) {
481 		iod->iod_muxwant--;
482 		wakeup(&iod->iod_muxwant);
483 	}
484 	SMB_IOD_RQUNLOCK(iod);
485 	return 0;
486 }
487 
488 int
489 smb_iod_waitrq(struct smb_rq *rqp)
490 {
491 	struct smbiod *iod = rqp->sr_vc->vc_iod;
492 	int error;
493 
494 	SMBIODEBUG("\n");
495 	if (rqp->sr_flags & SMBR_INTERNAL) {
496 		for (;;) {
497 			smb_iod_sendall(iod);
498 			smb_iod_recvall(iod);
499 			if (rqp->sr_rpgen != rqp->sr_rplast)
500 				break;
501 			tsleep(&iod->iod_flags, PWAIT, "90irq", hz);
502 		}
503 		smb_iod_removerq(rqp);
504 		return rqp->sr_lerror;
505 	}
506 	SMBRQ_SLOCK(rqp);
507 	if (rqp->sr_rpgen == rqp->sr_rplast)
508 		msleep(&rqp->sr_state, SMBRQ_SLOCKPTR(rqp), PWAIT, "90wrq", 0);
509 	rqp->sr_rplast++;
510 	SMBRQ_SUNLOCK(rqp);
511 	error = rqp->sr_lerror;
512 	if (rqp->sr_flags & SMBR_MULTIPACKET) {
513 		/*
514 		 * If request should stay in the list, then reinsert it
515 		 * at the end of queue so other waiters have chance to concur
516 		 */
517 		SMB_IOD_RQLOCK(iod);
518 		TAILQ_REMOVE(&iod->iod_rqlist, rqp, sr_link);
519 		TAILQ_INSERT_TAIL(&iod->iod_rqlist, rqp, sr_link);
520 		SMB_IOD_RQUNLOCK(iod);
521 	} else
522 		smb_iod_removerq(rqp);
523 	return error;
524 }
525 
526 static int
527 smb_iod_sendall(struct smbiod *iod)
528 {
529 	struct smb_vc *vcp = iod->iod_vc;
530 	struct smb_rq *rqp;
531 	struct timespec ts, tstimeout;
532 	int herror;
533 
534 	herror = 0;
535 	/*
536 	 * Loop through the list of requests and send them if possible
537 	 */
538 	SMB_IOD_RQLOCK(iod);
539 	TAILQ_FOREACH(rqp, &iod->iod_rqlist, sr_link) {
540 		switch (rqp->sr_state) {
541 		    case SMBRQ_NOTSENT:
542 			rqp->sr_flags |= SMBR_XLOCK;
543 			SMB_IOD_RQUNLOCK(iod);
544 			herror = smb_iod_sendrq(iod, rqp);
545 			SMB_IOD_RQLOCK(iod);
546 			rqp->sr_flags &= ~SMBR_XLOCK;
547 			if (rqp->sr_flags & SMBR_XLOCKWANT) {
548 				rqp->sr_flags &= ~SMBR_XLOCKWANT;
549 				wakeup(rqp);
550 			}
551 			break;
552 		    case SMBRQ_SENT:
553 			SMB_TRAN_GETPARAM(vcp, SMBTP_TIMEOUT, &tstimeout);
554 			timespecadd(&tstimeout, &tstimeout, &tstimeout);
555 			getnanotime(&ts);
556 			timespecsub(&ts, &tstimeout, &ts);
557 			if (timespeccmp(&ts, &rqp->sr_timesent, >)) {
558 				smb_iod_rqprocessed(rqp, ETIMEDOUT);
559 			}
560 			break;
561 		    default:
562 			break;
563 		}
564 		if (herror)
565 			break;
566 	}
567 	SMB_IOD_RQUNLOCK(iod);
568 	if (herror == ENOTCONN)
569 		smb_iod_dead(iod);
570 	return 0;
571 }
572 
573 /*
574  * "main" function for smbiod daemon
575  */
576 static __inline void
577 smb_iod_main(struct smbiod *iod)
578 {
579 /*	struct smb_vc *vcp = iod->iod_vc;*/
580 	struct smbiod_event *evp;
581 /*	struct timespec tsnow;*/
582 
583 	SMBIODEBUG("\n");
584 
585 	/*
586 	 * Check all interesting events
587 	 */
588 	for (;;) {
589 		SMB_IOD_EVLOCK(iod);
590 		evp = STAILQ_FIRST(&iod->iod_evlist);
591 		if (evp == NULL) {
592 			SMB_IOD_EVUNLOCK(iod);
593 			break;
594 		}
595 		STAILQ_REMOVE_HEAD(&iod->iod_evlist, ev_link);
596 		evp->ev_type |= SMBIOD_EV_PROCESSING;
597 		SMB_IOD_EVUNLOCK(iod);
598 		switch (evp->ev_type & SMBIOD_EV_MASK) {
599 		    case SMBIOD_EV_CONNECT:
600 			iod->iod_state = SMBIOD_ST_RECONNECT;
601 			evp->ev_error = smb_iod_connect(iod);
602 			break;
603 		    case SMBIOD_EV_DISCONNECT:
604 			evp->ev_error = smb_iod_disconnect(iod);
605 			break;
606 		    case SMBIOD_EV_TREECONNECT:
607 			evp->ev_error = smb_iod_treeconnect(iod, evp->ev_ident);
608 			break;
609 		    case SMBIOD_EV_SHUTDOWN:
610 			iod->iod_flags |= SMBIOD_SHUTDOWN;
611 			break;
612 		    case SMBIOD_EV_NEWRQ:
613 			break;
614 		}
615 		if (evp->ev_type & SMBIOD_EV_SYNC) {
616 			SMB_IOD_EVLOCK(iod);
617 			wakeup(evp);
618 			SMB_IOD_EVUNLOCK(iod);
619 		} else
620 			free(evp, M_SMBIOD);
621 	}
622 #if 0
623 	if (iod->iod_state == SMBIOD_ST_VCACTIVE) {
624 		getnanotime(&tsnow);
625 		timespecsub(&tsnow, &iod->iod_pingtimo, &tsnow);
626 		if (timespeccmp(&tsnow, &iod->iod_lastrqsent, >)) {
627 			smb_smb_echo(vcp, &iod->iod_scred);
628 		}
629 	}
630 #endif
631 	smb_iod_sendall(iod);
632 	smb_iod_recvall(iod);
633 	return;
634 }
635 
636 void
637 smb_iod_thread(void *arg)
638 {
639 	struct smbiod *iod = arg;
640 
641 	mtx_lock(&Giant);
642 
643 	/*
644 	 * Here we assume that the thread structure will be the same
645 	 * for an entire kthread (kproc, to be more precise) life.
646 	 */
647 	iod->iod_td = curthread;
648 	smb_makescred(&iod->iod_scred, iod->iod_td, NULL);
649 	while ((iod->iod_flags & SMBIOD_SHUTDOWN) == 0) {
650 		smb_iod_main(iod);
651 		SMBIODEBUG("going to sleep for %d ticks\n", iod->iod_sleeptimo);
652 		if (iod->iod_flags & SMBIOD_SHUTDOWN)
653 			break;
654 		tsleep(&iod->iod_flags, PWAIT, "90idle", iod->iod_sleeptimo);
655 	}
656 
657 	/* We can now safely destroy the mutexes and free the iod structure. */
658 	smb_sl_destroy(&iod->iod_rqlock);
659 	smb_sl_destroy(&iod->iod_evlock);
660 	free(iod, M_SMBIOD);
661 	mtx_unlock(&Giant);
662 	kproc_exit(0);
663 }
664 
665 int
666 smb_iod_create(struct smb_vc *vcp)
667 {
668 	struct smbiod *iod;
669 	int error;
670 
671 	iod = smb_zmalloc(sizeof(*iod), M_SMBIOD, M_WAITOK);
672 	iod->iod_id = smb_iod_next++;
673 	iod->iod_state = SMBIOD_ST_NOTCONN;
674 	iod->iod_vc = vcp;
675 	iod->iod_sleeptimo = hz * SMBIOD_SLEEP_TIMO;
676 	iod->iod_pingtimo.tv_sec = SMBIOD_PING_TIMO;
677 	getnanotime(&iod->iod_lastrqsent);
678 	vcp->vc_iod = iod;
679 	smb_sl_init(&iod->iod_rqlock, "90rql");
680 	TAILQ_INIT(&iod->iod_rqlist);
681 	smb_sl_init(&iod->iod_evlock, "90evl");
682 	STAILQ_INIT(&iod->iod_evlist);
683 	error = kproc_create(smb_iod_thread, iod, &iod->iod_p,
684 	    RFNOWAIT, 0, "smbiod%d", iod->iod_id);
685 	if (error) {
686 		SMBERROR("can't start smbiod: %d", error);
687 		vcp->vc_iod = NULL;
688 		smb_sl_destroy(&iod->iod_rqlock);
689 		smb_sl_destroy(&iod->iod_evlock);
690 		free(iod, M_SMBIOD);
691 		return error;
692 	}
693 	return 0;
694 }
695 
696 int
697 smb_iod_destroy(struct smbiod *iod)
698 {
699 	smb_iod_request(iod, SMBIOD_EV_SHUTDOWN | SMBIOD_EV_SYNC, NULL);
700 	return 0;
701 }
702 
703 int
704 smb_iod_init(void)
705 {
706 	return 0;
707 }
708 
709 int
710 smb_iod_done(void)
711 {
712 	return 0;
713 }
714