xref: /freebsd/sys/netsmb/smb_iod.c (revision b52f49a9a0f22207ad5130ad8faba08de3ed23d8)
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 	itry {
145 		ithrow(SMB_TRAN_CREATE(vcp, td));
146 		SMBIODEBUG("tcreate\n");
147 		if (vcp->vc_laddr) {
148 			ithrow(SMB_TRAN_BIND(vcp, vcp->vc_laddr, td));
149 		}
150 		SMBIODEBUG("tbind\n");
151 		ithrow(SMB_TRAN_CONNECT(vcp, vcp->vc_paddr, td));
152 		SMB_TRAN_SETPARAM(vcp, SMBTP_SELECTID, &iod->iod_flags);
153 		iod->iod_state = SMBIOD_ST_TRANACTIVE;
154 		SMBIODEBUG("tconnect\n");
155 /*		vcp->vc_mid = 0;*/
156 		ithrow(smb_smb_negotiate(vcp, &iod->iod_scred));
157 		SMBIODEBUG("snegotiate\n");
158 		ithrow(smb_smb_ssnsetup(vcp, &iod->iod_scred));
159 		iod->iod_state = SMBIOD_ST_VCACTIVE;
160 		SMBIODEBUG("completed\n");
161 		smb_iod_invrq(iod);
162 	} icatch(error) {
163 		smb_iod_dead(iod);
164 	} ifinally {
165 	} iendtry;
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 		*rqp->sr_rqtid = htole16(ssp ? ssp->ss_tid : SMB_TID_UNKNOWN);
238 		*rqp->sr_rquid = htole16(vcp ? vcp->vc_smbuid : 0);
239 		mb_fixhdr(&rqp->sr_rq);
240 	}
241 	if (rqp->sr_sendcnt++ > 5) {
242 		rqp->sr_flags |= SMBR_RESTART;
243 		smb_iod_rqprocessed(rqp, rqp->sr_lerror);
244 		/*
245 		 * If all attempts to send a request failed, then
246 		 * something is seriously hosed.
247 		 */
248 		return ENOTCONN;
249 	}
250 	SMBSDEBUG("M:%04x, P:%04x, U:%04x, T:%04x\n", rqp->sr_mid, 0, 0, 0);
251 	m_dumpm(rqp->sr_rq.mb_top);
252 	m = m_copym(rqp->sr_rq.mb_top, 0, M_COPYALL, M_TRYWAIT);
253 	error = rqp->sr_lerror = m ? SMB_TRAN_SEND(vcp, m, td) : ENOBUFS;
254 	if (error == 0) {
255 		getnanotime(&rqp->sr_timesent);
256 		iod->iod_lastrqsent = rqp->sr_timesent;
257 		rqp->sr_flags |= SMBR_SENT;
258 		rqp->sr_state = SMBRQ_SENT;
259 		return 0;
260 	}
261 	/*
262 	 * Check for fatal errors
263 	 */
264 	if (SMB_TRAN_FATAL(vcp, error)) {
265 		/*
266 		 * No further attempts should be made
267 		 */
268 		return ENOTCONN;
269 	}
270 	if (smb_rq_intr(rqp))
271 		smb_iod_rqprocessed(rqp, EINTR);
272 	return 0;
273 }
274 
275 /*
276  * Process incoming packets
277  */
278 static int
279 smb_iod_recvall(struct smbiod *iod)
280 {
281 	struct smb_vc *vcp = iod->iod_vc;
282 	struct thread *td = iod->iod_td;
283 	struct smb_rq *rqp;
284 	struct mbuf *m;
285 	u_char *hp;
286 	u_short mid;
287 	int error;
288 
289 	switch (iod->iod_state) {
290 	    case SMBIOD_ST_NOTCONN:
291 	    case SMBIOD_ST_DEAD:
292 	    case SMBIOD_ST_RECONNECT:
293 		return 0;
294 	    default:
295 		break;
296 	}
297 	for (;;) {
298 		m = NULL;
299 		error = SMB_TRAN_RECV(vcp, &m, td);
300 		if (error == EWOULDBLOCK)
301 			break;
302 		if (SMB_TRAN_FATAL(vcp, error)) {
303 			smb_iod_dead(iod);
304 			break;
305 		}
306 		if (error)
307 			break;
308 		if (m == NULL) {
309 			SMBERROR("tran return NULL without error\n");
310 			error = EPIPE;
311 			continue;
312 		}
313 		m = m_pullup(m, SMB_HDRLEN);
314 		if (m == NULL)
315 			continue;	/* wait for a good packet */
316 		/*
317 		 * Now we got an entire and possibly invalid SMB packet.
318 		 * Be careful while parsing it.
319 		 */
320 		m_dumpm(m);
321 		hp = mtod(m, u_char*);
322 		if (bcmp(hp, SMB_SIGNATURE, SMB_SIGLEN) != 0) {
323 			m_freem(m);
324 			continue;
325 		}
326 		mid = SMB_HDRMID(hp);
327 		SMBSDEBUG("mid %04x\n", (u_int)mid);
328 		SMB_IOD_RQLOCK(iod);
329 		TAILQ_FOREACH(rqp, &iod->iod_rqlist, sr_link) {
330 			if (rqp->sr_mid != mid)
331 				continue;
332 			SMBRQ_SLOCK(rqp);
333 			if (rqp->sr_rp.md_top == NULL) {
334 				md_initm(&rqp->sr_rp, m);
335 			} else {
336 				if (rqp->sr_flags & SMBR_MULTIPACKET) {
337 					md_append_record(&rqp->sr_rp, m);
338 				} else {
339 					SMBRQ_SUNLOCK(rqp);
340 					SMBERROR("duplicate response %d (ignored)\n", mid);
341 					break;
342 				}
343 			}
344 			SMBRQ_SUNLOCK(rqp);
345 			smb_iod_rqprocessed(rqp, 0);
346 			break;
347 		}
348 		SMB_IOD_RQUNLOCK(iod);
349 		if (rqp == NULL) {
350 			SMBERROR("drop resp with mid %d\n", (u_int)mid);
351 /*			smb_printrqlist(vcp);*/
352 			m_freem(m);
353 		}
354 	}
355 	/*
356 	 * check for interrupts
357 	 */
358 	SMB_IOD_RQLOCK(iod);
359 	TAILQ_FOREACH(rqp, &iod->iod_rqlist, sr_link) {
360 		if (smb_td_intr(rqp->sr_cred->scr_td)) {
361 			smb_iod_rqprocessed(rqp, EINTR);
362 		}
363 	}
364 	SMB_IOD_RQUNLOCK(iod);
365 	return 0;
366 }
367 
368 int
369 smb_iod_request(struct smbiod *iod, int event, void *ident)
370 {
371 	struct smbiod_event *evp;
372 	int error;
373 
374 	SMBIODEBUG("\n");
375 	evp = smb_zmalloc(sizeof(*evp), M_SMBIOD, M_WAITOK);
376 	evp->ev_type = event;
377 	evp->ev_ident = ident;
378 	SMB_IOD_EVLOCK(iod);
379 	STAILQ_INSERT_TAIL(&iod->iod_evlist, evp, ev_link);
380 	if ((event & SMBIOD_EV_SYNC) == 0) {
381 		SMB_IOD_EVUNLOCK(iod);
382 		smb_iod_wakeup(iod);
383 		return 0;
384 	}
385 	smb_iod_wakeup(iod);
386 	msleep(evp, SMB_IOD_EVLOCKPTR(iod), PWAIT | PDROP, "90evw", 0);
387 	error = evp->ev_error;
388 	free(evp, M_SMBIOD);
389 	return error;
390 }
391 
392 /*
393  * Place request in the queue.
394  * Request from smbiod have a high priority.
395  */
396 int
397 smb_iod_addrq(struct smb_rq *rqp)
398 {
399 	struct smb_vc *vcp = rqp->sr_vc;
400 	struct smbiod *iod = vcp->vc_iod;
401 	int error;
402 
403 	SMBIODEBUG("\n");
404 	if (rqp->sr_cred->scr_td != NULL &&
405 	    rqp->sr_cred->scr_td->td_proc == iod->iod_p) {
406 		rqp->sr_flags |= SMBR_INTERNAL;
407 		SMB_IOD_RQLOCK(iod);
408 		TAILQ_INSERT_HEAD(&iod->iod_rqlist, rqp, sr_link);
409 		SMB_IOD_RQUNLOCK(iod);
410 		for (;;) {
411 			if (smb_iod_sendrq(iod, rqp) != 0) {
412 				smb_iod_dead(iod);
413 				break;
414 			}
415 			/*
416 			 * we don't need to lock state field here
417 			 */
418 			if (rqp->sr_state != SMBRQ_NOTSENT)
419 				break;
420 			tsleep(&iod->iod_flags, PWAIT, "90sndw", hz);
421 		}
422 		if (rqp->sr_lerror)
423 			smb_iod_removerq(rqp);
424 		return rqp->sr_lerror;
425 	}
426 
427 	switch (iod->iod_state) {
428 	    case SMBIOD_ST_NOTCONN:
429 		return ENOTCONN;
430 	    case SMBIOD_ST_DEAD:
431 		error = smb_iod_request(vcp->vc_iod, SMBIOD_EV_CONNECT | SMBIOD_EV_SYNC, NULL);
432 		if (error)
433 			return error;
434 		return EXDEV;
435 	    default:
436 		break;
437 	}
438 
439 	SMB_IOD_RQLOCK(iod);
440 	for (;;) {
441 		if (vcp->vc_maxmux == 0) {
442 			SMBERROR("maxmux == 0\n");
443 			break;
444 		}
445 		if (iod->iod_muxcnt < vcp->vc_maxmux)
446 			break;
447 		iod->iod_muxwant++;
448 		msleep(&iod->iod_muxwant, SMB_IOD_RQLOCKPTR(iod),
449 		    PWAIT, "90mux", 0);
450 	}
451 	iod->iod_muxcnt++;
452 	TAILQ_INSERT_TAIL(&iod->iod_rqlist, rqp, sr_link);
453 	SMB_IOD_RQUNLOCK(iod);
454 	smb_iod_wakeup(iod);
455 	return 0;
456 }
457 
458 int
459 smb_iod_removerq(struct smb_rq *rqp)
460 {
461 	struct smb_vc *vcp = rqp->sr_vc;
462 	struct smbiod *iod = vcp->vc_iod;
463 
464 	SMBIODEBUG("\n");
465 	if (rqp->sr_flags & SMBR_INTERNAL) {
466 		SMB_IOD_RQLOCK(iod);
467 		TAILQ_REMOVE(&iod->iod_rqlist, rqp, sr_link);
468 		SMB_IOD_RQUNLOCK(iod);
469 		return 0;
470 	}
471 	SMB_IOD_RQLOCK(iod);
472 	while (rqp->sr_flags & SMBR_XLOCK) {
473 		rqp->sr_flags |= SMBR_XLOCKWANT;
474 		msleep(rqp, SMB_IOD_RQLOCKPTR(iod), PWAIT, "90xrm", 0);
475 	}
476 	TAILQ_REMOVE(&iod->iod_rqlist, rqp, sr_link);
477 	iod->iod_muxcnt--;
478 	if (iod->iod_muxwant) {
479 		iod->iod_muxwant--;
480 		wakeup(&iod->iod_muxwant);
481 	}
482 	SMB_IOD_RQUNLOCK(iod);
483 	return 0;
484 }
485 
486 int
487 smb_iod_waitrq(struct smb_rq *rqp)
488 {
489 	struct smbiod *iod = rqp->sr_vc->vc_iod;
490 	int error;
491 
492 	SMBIODEBUG("\n");
493 	if (rqp->sr_flags & SMBR_INTERNAL) {
494 		for (;;) {
495 			smb_iod_sendall(iod);
496 			smb_iod_recvall(iod);
497 			if (rqp->sr_rpgen != rqp->sr_rplast)
498 				break;
499 			tsleep(&iod->iod_flags, PWAIT, "90irq", hz);
500 		}
501 		smb_iod_removerq(rqp);
502 		return rqp->sr_lerror;
503 
504 	}
505 	SMBRQ_SLOCK(rqp);
506 	if (rqp->sr_rpgen == rqp->sr_rplast)
507 		msleep(&rqp->sr_state, SMBRQ_SLOCKPTR(rqp), PWAIT, "90wrq", 0);
508 	rqp->sr_rplast++;
509 	SMBRQ_SUNLOCK(rqp);
510 	error = rqp->sr_lerror;
511 	if (rqp->sr_flags & SMBR_MULTIPACKET) {
512 		/*
513 		 * If request should stay in the list, then reinsert it
514 		 * at the end of queue so other waiters have chance to concur
515 		 */
516 		SMB_IOD_RQLOCK(iod);
517 		TAILQ_REMOVE(&iod->iod_rqlist, rqp, sr_link);
518 		TAILQ_INSERT_TAIL(&iod->iod_rqlist, rqp, sr_link);
519 		SMB_IOD_RQUNLOCK(iod);
520 	} else
521 		smb_iod_removerq(rqp);
522 	return error;
523 }
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);
555 			getnanotime(&ts);
556 			timespecsub(&ts, &tstimeout);
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 	int error;
583 
584 	SMBIODEBUG("\n");
585 	error = 0;
586 
587 	/*
588 	 * Check all interesting events
589 	 */
590 	for (;;) {
591 		SMB_IOD_EVLOCK(iod);
592 		evp = STAILQ_FIRST(&iod->iod_evlist);
593 		if (evp == NULL) {
594 			SMB_IOD_EVUNLOCK(iod);
595 			break;
596 		}
597 		STAILQ_REMOVE_HEAD(&iod->iod_evlist, ev_link);
598 		evp->ev_type |= SMBIOD_EV_PROCESSING;
599 		SMB_IOD_EVUNLOCK(iod);
600 		switch (evp->ev_type & SMBIOD_EV_MASK) {
601 		    case SMBIOD_EV_CONNECT:
602 			iod->iod_state = SMBIOD_ST_RECONNECT;
603 			evp->ev_error = smb_iod_connect(iod);
604 			break;
605 		    case SMBIOD_EV_DISCONNECT:
606 			evp->ev_error = smb_iod_disconnect(iod);
607 			break;
608 		    case SMBIOD_EV_TREECONNECT:
609 			evp->ev_error = smb_iod_treeconnect(iod, evp->ev_ident);
610 			break;
611 		    case SMBIOD_EV_SHUTDOWN:
612 			iod->iod_flags |= SMBIOD_SHUTDOWN;
613 			break;
614 		    case SMBIOD_EV_NEWRQ:
615 			break;
616 		}
617 		if (evp->ev_type & SMBIOD_EV_SYNC) {
618 			SMB_IOD_EVLOCK(iod);
619 			wakeup(evp);
620 			SMB_IOD_EVUNLOCK(iod);
621 		} else
622 			free(evp, M_SMBIOD);
623 	}
624 #if 0
625 	if (iod->iod_state == SMBIOD_ST_VCACTIVE) {
626 		getnanotime(&tsnow);
627 		timespecsub(&tsnow, &iod->iod_pingtimo);
628 		if (timespeccmp(&tsnow, &iod->iod_lastrqsent, >)) {
629 			smb_smb_echo(vcp, &iod->iod_scred);
630 		}
631 	}
632 #endif
633 	smb_iod_sendall(iod);
634 	smb_iod_recvall(iod);
635 	return;
636 }
637 
638 void
639 smb_iod_thread(void *arg)
640 {
641 	struct smbiod *iod = arg;
642 
643 	mtx_lock(&Giant);
644 	/*
645 	 * Here we assume that the thread structure will be the same
646 	 * for an entire kthread (kproc, to be more precise) life.
647 	 */
648 	iod->iod_td = curthread;
649 	smb_makescred(&iod->iod_scred, iod->iod_td, NULL);
650 	while ((iod->iod_flags & SMBIOD_SHUTDOWN) == 0) {
651 		smb_iod_main(iod);
652 		SMBIODEBUG("going to sleep for %d ticks\n", iod->iod_sleeptimo);
653 /*		mtx_unlock(&Giant, MTX_DEF);*/
654 		if (iod->iod_flags & SMBIOD_SHUTDOWN)
655 			break;
656 		tsleep(&iod->iod_flags, PWAIT, "90idle", iod->iod_sleeptimo);
657 	}
658 /*	mtx_lock(&Giant, MTX_DEF);*/
659 	kthread_exit(0);
660 }
661 
662 int
663 smb_iod_create(struct smb_vc *vcp)
664 {
665 	struct smbiod *iod;
666 	int error;
667 
668 	iod = smb_zmalloc(sizeof(*iod), M_SMBIOD, M_WAITOK);
669 	iod->iod_id = smb_iod_next++;
670 	iod->iod_state = SMBIOD_ST_NOTCONN;
671 	iod->iod_vc = vcp;
672 	iod->iod_sleeptimo = hz * SMBIOD_SLEEP_TIMO;
673 	iod->iod_pingtimo.tv_sec = SMBIOD_PING_TIMO;
674 	getnanotime(&iod->iod_lastrqsent);
675 	vcp->vc_iod = iod;
676 	smb_sl_init(&iod->iod_rqlock, "90rql");
677 	TAILQ_INIT(&iod->iod_rqlist);
678 	smb_sl_init(&iod->iod_evlock, "90evl");
679 	STAILQ_INIT(&iod->iod_evlist);
680 	error = kthread_create(smb_iod_thread, iod, &iod->iod_p,
681 	    RFNOWAIT, 0, "smbiod%d", iod->iod_id);
682 	if (error) {
683 		SMBERROR("can't start smbiod: %d", error);
684 		free(iod, M_SMBIOD);
685 		return error;
686 	}
687 	return 0;
688 }
689 
690 int
691 smb_iod_destroy(struct smbiod *iod)
692 {
693 	smb_iod_request(iod, SMBIOD_EV_SHUTDOWN | SMBIOD_EV_SYNC, NULL);
694 	smb_sl_destroy(&iod->iod_rqlock);
695 	smb_sl_destroy(&iod->iod_evlock);
696 	free(iod, M_SMBIOD);
697 	return 0;
698 }
699 
700 int
701 smb_iod_init(void)
702 {
703 	return 0;
704 }
705 
706 int
707 smb_iod_done(void)
708 {
709 	return 0;
710 }
711 
712