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