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