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