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