smb_iod.c revision 97209
1193326Sed/*
2193326Sed * Copyright (c) 2000-2001 Boris Popov
3193326Sed * All rights reserved.
4193326Sed *
5193326Sed * Redistribution and use in source and binary forms, with or without
6193326Sed * modification, are permitted provided that the following conditions
7193326Sed * are met:
8193326Sed * 1. Redistributions of source code must retain the above copyright
9193326Sed *    notice, this list of conditions and the following disclaimer.
10193326Sed * 2. Redistributions in binary form must reproduce the above copyright
11193326Sed *    notice, this list of conditions and the following disclaimer in the
12193326Sed *    documentation and/or other materials provided with the distribution.
13193326Sed * 3. All advertising materials mentioning features or use of this software
14239462Sdim *    must display the following acknowledgement:
15249423Sdim *    This product includes software developed by Boris Popov.
16193326Sed * 4. Neither the name of the author nor the names of any co-contributors
17195341Sed *    may be used to endorse or promote products derived from this software
18193326Sed *    without specific prior written permission.
19204643Srdivacky *
20249423Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21193326Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22193326Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23207619Srdivacky * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24193326Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25193326Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26193326Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27193326Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28239462Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29239462Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30239462Sdim * SUCH DAMAGE.
31239462Sdim *
32239462Sdim * $FreeBSD: head/sys/netsmb/smb_iod.c 97209 2002-05-24 05:40:51Z peter $
33239462Sdim */
34239462Sdim
35239462Sdim#include <sys/param.h>
36239462Sdim#include <sys/systm.h>
37239462Sdim#include <sys/endian.h>
38239462Sdim#include <sys/proc.h>
39239462Sdim#include <sys/kernel.h>
40239462Sdim#include <sys/kthread.h>
41239462Sdim#include <sys/malloc.h>
42239462Sdim#include <sys/mbuf.h>
43207619Srdivacky#include <sys/unistd.h>
44207619Srdivacky
45207619Srdivacky#include <netsmb/smb.h>
46207619Srdivacky#include <netsmb/smb_conn.h>
47207619Srdivacky#include <netsmb/smb_rq.h>
48207619Srdivacky#include <netsmb/smb_tran.h>
49218893Sdim#include <netsmb/smb_trantcp.h>
50218893Sdim
51218893Sdim
52218893Sdim#define SMBIOD_SLEEP_TIMO	2
53193326Sed#define	SMBIOD_PING_TIMO	60	/* seconds */
54193326Sed
55243830Sdim#define	SMB_IOD_EVLOCKPTR(iod)	(&((iod)->iod_evlock))
56243830Sdim#define	SMB_IOD_EVLOCK(iod)	smb_sl_lock(&((iod)->iod_evlock))
57243830Sdim#define	SMB_IOD_EVUNLOCK(iod)	smb_sl_unlock(&((iod)->iod_evlock))
58243830Sdim
59243830Sdim#define	SMB_IOD_RQLOCKPTR(iod)	(&((iod)->iod_rqlock))
60243830Sdim#define	SMB_IOD_RQLOCK(iod)	smb_sl_lock(&((iod)->iod_rqlock))
61243830Sdim#define	SMB_IOD_RQUNLOCK(iod)	smb_sl_unlock(&((iod)->iod_rqlock))
62243830Sdim
63243830Sdim#define	smb_iod_wakeup(iod)	wakeup(&(iod)->iod_flags)
64243830Sdim
65243830Sdim
66243830Sdimstatic MALLOC_DEFINE(M_SMBIOD, "SMBIOD", "SMB network io daemon");
67243830Sdim
68243830Sdimstatic int smb_iod_next;
69243830Sdim
70243830Sdimstatic int  smb_iod_sendall(struct smbiod *iod);
71243830Sdimstatic int  smb_iod_disconnect(struct smbiod *iod);
72243830Sdimstatic void smb_iod_thread(void *);
73243830Sdim
74243830Sdimstatic __inline void
75210299Sedsmb_iod_rqprocessed(struct smb_rq *rqp, int error)
76249423Sdim{
77249423Sdim	SMBRQ_SLOCK(rqp);
78193326Sed	rqp->sr_lerror = error;
79193326Sed	rqp->sr_rpgen++;
80193326Sed	rqp->sr_state = SMBRQ_NOTIFIED;
81203955Srdivacky	wakeup(&rqp->sr_state);
82234353Sdim	SMBRQ_SUNLOCK(rqp);
83234353Sdim}
84243830Sdim
85234353Sdimstatic void
86234353Sdimsmb_iod_invrq(struct smbiod *iod)
87234353Sdim{
88234353Sdim	struct smb_rq *rqp;
89243830Sdim
90218893Sdim	/*
91218893Sdim	 * Invalidate all outstanding requests for this connection
92224145Sdim	 */
93218893Sdim	SMB_IOD_RQLOCK(iod);
94234353Sdim	TAILQ_FOREACH(rqp, &iod->iod_rqlist, sr_link) {
95234353Sdim		if (rqp->sr_flags & SMBR_INTERNAL)
96243830Sdim			SMBRQ_SUNLOCK(rqp);
97234353Sdim		rqp->sr_flags |= SMBR_RESTART;
98234353Sdim		smb_iod_rqprocessed(rqp, ENOTCONN);
99234353Sdim	}
100234353Sdim	SMB_IOD_RQUNLOCK(iod);
101243830Sdim}
102193326Sed
103218893Sdimstatic void
104224145Sdimsmb_iod_closetran(struct smbiod *iod)
105224145Sdim{
106224145Sdim	struct smb_vc *vcp = iod->iod_vc;
107218893Sdim	struct thread *td = iod->iod_td;
108218893Sdim
109218893Sdim	if (vcp->vc_tdata == NULL)
110193326Sed		return;
111218893Sdim	SMB_TRAN_DISCONNECT(vcp, td);
112218893Sdim	SMB_TRAN_DONE(vcp, td);
113234353Sdim	vcp->vc_tdata = NULL;
114234353Sdim}
115224145Sdim
116234353Sdimstatic void
117234353Sdimsmb_iod_dead(struct smbiod *iod)
118218893Sdim{
119218893Sdim	iod->iod_state = SMBIOD_ST_DEAD;
120234353Sdim	smb_iod_closetran(iod);
121218893Sdim	smb_iod_invrq(iod);
122218893Sdim}
123243830Sdim
124234353Sdimstatic int
125224145Sdimsmb_iod_connect(struct smbiod *iod)
126234353Sdim{
127218893Sdim	struct smb_vc *vcp = iod->iod_vc;
128218893Sdim	struct thread *td = iod->iod_td;
129234353Sdim	int error;
130218893Sdim
131243830Sdim	SMBIODEBUG("%d\n", iod->iod_state);
132243830Sdim	switch(iod->iod_state) {
133243830Sdim	    case SMBIOD_ST_VCACTIVE:
134243830Sdim		SMBERROR("called for already opened connection\n");
135243830Sdim		return EISCONN;
136243830Sdim	    case SMBIOD_ST_DEAD:
137243830Sdim		return ENOTCONN;	/* XXX: last error code ? */
138243830Sdim	    default:
139193326Sed		break;
140193326Sed	}
141208600Srdivacky	vcp->vc_genid++;
142234353Sdim	error = 0;
143208600Srdivacky	itry {
144208600Srdivacky		ithrow(SMB_TRAN_CREATE(vcp, td));
145208600Srdivacky		SMBIODEBUG("tcreate\n");
146234353Sdim		if (vcp->vc_laddr) {
147234353Sdim			ithrow(SMB_TRAN_BIND(vcp, vcp->vc_laddr, td));
148208600Srdivacky		}
149208600Srdivacky		SMBIODEBUG("tbind\n");
150208600Srdivacky		ithrow(SMB_TRAN_CONNECT(vcp, vcp->vc_paddr, td));
151221345Sdim		SMB_TRAN_SETPARAM(vcp, SMBTP_SELECTID, &iod->iod_flags);
152221345Sdim		iod->iod_state = SMBIOD_ST_TRANACTIVE;
153221345Sdim		SMBIODEBUG("tconnect\n");
154221345Sdim/*		vcp->vc_mid = 0;*/
155208600Srdivacky		ithrow(smb_smb_negotiate(vcp, &iod->iod_scred));
156193326Sed		SMBIODEBUG("snegotiate\n");
157218893Sdim		ithrow(smb_smb_ssnsetup(vcp, &iod->iod_scred));
158218893Sdim		iod->iod_state = SMBIOD_ST_VCACTIVE;
159218893Sdim		SMBIODEBUG("completed\n");
160218893Sdim		smb_iod_invrq(iod);
161193326Sed	} icatch(error) {
162218893Sdim		smb_iod_dead(iod);
163218893Sdim	} ifinally {
164218893Sdim	} iendtry;
165218893Sdim	return error;
166198092Srdivacky}
167198092Srdivacky
168218893Sdimstatic int
169204643Srdivackysmb_iod_disconnect(struct smbiod *iod)
170204643Srdivacky{
171204643Srdivacky	struct smb_vc *vcp = iod->iod_vc;
172208600Srdivacky
173204643Srdivacky	SMBIODEBUG("\n");
174204643Srdivacky	if (iod->iod_state == SMBIOD_ST_VCACTIVE) {
175218893Sdim		smb_smb_ssnclose(vcp, &iod->iod_scred);
176219077Sdim		iod->iod_state = SMBIOD_ST_TRANACTIVE;
177219077Sdim	}
178219077Sdim	vcp->vc_smbuid = SMB_UID_UNKNOWN;
179219077Sdim	smb_iod_closetran(iod);
180218893Sdim	iod->iod_state = SMBIOD_ST_NOTCONN;
181249423Sdim	return 0;
182249423Sdim}
183218893Sdim
184218893Sdimstatic int
185218893Sdimsmb_iod_treeconnect(struct smbiod *iod, struct smb_share *ssp)
186218893Sdim{
187218893Sdim	int error;
188218893Sdim
189224145Sdim	if (iod->iod_state != SMBIOD_ST_VCACTIVE) {
190224145Sdim		if (iod->iod_state != SMBIOD_ST_DEAD)
191224145Sdim			return ENOTCONN;
192224145Sdim		iod->iod_state = SMBIOD_ST_RECONNECT;
193224145Sdim		error = smb_iod_connect(iod);
194224145Sdim		if (error)
195224145Sdim			return error;
196224145Sdim	}
197218893Sdim	SMBIODEBUG("tree reconnect\n");
198218893Sdim	SMBS_ST_LOCK(ssp);
199219077Sdim	ssp->ss_flags |= SMBS_RECONNECTING;
200219077Sdim	SMBS_ST_UNLOCK(ssp);
201219077Sdim	error = smb_smb_treeconnect(ssp, &iod->iod_scred);
202218893Sdim	SMBS_ST_LOCK(ssp);
203218893Sdim	ssp->ss_flags &= ~SMBS_RECONNECTING;
204218893Sdim	SMBS_ST_UNLOCK(ssp);
205218893Sdim	wakeup(&ssp->ss_vcgenid);
206218893Sdim	return error;
207218893Sdim}
208219077Sdim
209218893Sdimstatic int
210218893Sdimsmb_iod_sendrq(struct smbiod *iod, struct smb_rq *rqp)
211218893Sdim{
212204643Srdivacky	struct thread *td = iod->iod_td;
213204643Srdivacky	struct smb_vc *vcp = iod->iod_vc;
214204643Srdivacky	struct smb_share *ssp = rqp->sr_share;
215204643Srdivacky	struct mbuf *m;
216204643Srdivacky	int error;
217204643Srdivacky
218204643Srdivacky	SMBIODEBUG("iod_state = %d\n", iod->iod_state);
219249423Sdim	switch (iod->iod_state) {
220204643Srdivacky	    case SMBIOD_ST_NOTCONN:
221204643Srdivacky		smb_iod_rqprocessed(rqp, ENOTCONN);
222208600Srdivacky		return 0;
223249423Sdim	    case SMBIOD_ST_DEAD:
224204643Srdivacky		iod->iod_state = SMBIOD_ST_RECONNECT;
225204643Srdivacky		return 0;
226199990Srdivacky	    case SMBIOD_ST_RECONNECT:
227199990Srdivacky		return 0;
228218893Sdim	    default:
229203955Srdivacky		break;
230221345Sdim	}
231234353Sdim	if (rqp->sr_sendcnt == 0) {
232212904Sdim#ifdef movedtoanotherplace
233212904Sdim		if (vcp->vc_maxmux != 0 && iod->iod_muxcnt >= vcp->vc_maxmux)
234234353Sdim			return 0;
235234353Sdim#endif
236234353Sdim		*rqp->sr_rqtid = htoles(ssp ? ssp->ss_tid : SMB_TID_UNKNOWN);
237199990Srdivacky		*rqp->sr_rquid = htoles(vcp ? vcp->vc_smbuid : 0);
238234353Sdim		mb_fixhdr(&rqp->sr_rq);
239234353Sdim	}
240234353Sdim	if (rqp->sr_sendcnt++ > 5) {
241234353Sdim		rqp->sr_flags |= SMBR_RESTART;
242234353Sdim		smb_iod_rqprocessed(rqp, rqp->sr_lerror);
243234353Sdim		/*
244234353Sdim		 * If all attempts to send a request failed, then
245243830Sdim		 * something is seriously hosed.
246199990Srdivacky		 */
247199990Srdivacky		return ENOTCONN;
248210299Sed	}
249234353Sdim	SMBSDEBUG("M:%04x, P:%04x, U:%04x, T:%04x\n", rqp->sr_mid, 0, 0, 0);
250234353Sdim	m_dumpm(rqp->sr_rq.mb_top);
251218893Sdim	m = m_copym(rqp->sr_rq.mb_top, 0, M_COPYALL, M_TRYWAIT);
252210299Sed	error = rqp->sr_lerror = m ? SMB_TRAN_SEND(vcp, m, td) : ENOBUFS;
253234353Sdim	if (error == 0) {
254234353Sdim		getnanotime(&rqp->sr_timesent);
255210299Sed		iod->iod_lastrqsent = rqp->sr_timesent;
256218893Sdim		rqp->sr_flags |= SMBR_SENT;
257210299Sed		rqp->sr_state = SMBRQ_SENT;
258234353Sdim		return 0;
259210299Sed	}
260210299Sed	/*
261210299Sed	 * Check for fatal errors
262218893Sdim	 */
263221345Sdim	if (SMB_TRAN_FATAL(vcp, error)) {
264234353Sdim		/*
265212904Sdim		 * No further attempts should be made
266218893Sdim		 */
267208600Srdivacky		return ENOTCONN;
268218893Sdim	}
269218893Sdim	if (smb_rq_intr(rqp))
270224145Sdim		smb_iod_rqprocessed(rqp, EINTR);
271218893Sdim	return 0;
272218893Sdim}
273218893Sdim
274224145Sdim/*
275224145Sdim * Process incoming packets
276224145Sdim */
277224145Sdimstatic int
278218893Sdimsmb_iod_recvall(struct smbiod *iod)
279218893Sdim{
280221345Sdim	struct smb_vc *vcp = iod->iod_vc;
281221345Sdim	struct thread *td = iod->iod_td;
282221345Sdim	struct smb_rq *rqp;
283234353Sdim	struct mbuf *m;
284234353Sdim	u_char *hp;
285234353Sdim	u_short mid;
286208600Srdivacky	int error;
287210299Sed
288208600Srdivacky	switch (iod->iod_state) {
289218893Sdim	    case SMBIOD_ST_NOTCONN:
290218893Sdim	    case SMBIOD_ST_DEAD:
291218893Sdim	    case SMBIOD_ST_RECONNECT:
292218893Sdim		return 0;
293218893Sdim	    default:
294218893Sdim		break;
295239462Sdim	}
296218893Sdim	for (;;) {
297218893Sdim		m = NULL;
298218893Sdim		error = SMB_TRAN_RECV(vcp, &m, td);
299208600Srdivacky		if (error == EWOULDBLOCK)
300208600Srdivacky			break;
301218893Sdim		if (SMB_TRAN_FATAL(vcp, error)) {
302208600Srdivacky			smb_iod_dead(iod);
303210299Sed			break;
304208600Srdivacky		}
305208600Srdivacky		if (error)
306218893Sdim			break;
307218893Sdim		if (m == NULL) {
308218893Sdim			SMBERROR("tran return NULL without error\n");
309218893Sdim			error = EPIPE;
310218893Sdim			continue;
311224145Sdim		}
312218893Sdim		m = m_pullup(m, SMB_HDRLEN);
313234353Sdim		if (m == NULL)
314234353Sdim			continue;	/* wait for a good packet */
315234353Sdim		/*
316234353Sdim		 * Now we got an entire and possibly invalid SMB packet.
317210299Sed		 * Be careful while parsing it.
318218893Sdim		 */
319224145Sdim		m_dumpm(m);
320224145Sdim		hp = mtod(m, u_char*);
321224145Sdim		if (bcmp(hp, SMB_SIGNATURE, SMB_SIGLEN) != 0) {
322224145Sdim			m_freem(m);
323224145Sdim			continue;
324218893Sdim		}
325218893Sdim		mid = SMB_HDRMID(hp);
326234353Sdim		SMBSDEBUG("mid %04x\n", (u_int)mid);
327234353Sdim		SMB_IOD_RQLOCK(iod);
328218893Sdim		TAILQ_FOREACH(rqp, &iod->iod_rqlist, sr_link) {
329199990Srdivacky			if (rqp->sr_mid != mid)
330218893Sdim				continue;
331218893Sdim			SMBRQ_SLOCK(rqp);
332218893Sdim			if (rqp->sr_rp.md_top == NULL) {
333199990Srdivacky				md_initm(&rqp->sr_rp, m);
334218893Sdim			} else {
335218893Sdim				if (rqp->sr_flags & SMBR_MULTIPACKET) {
336218893Sdim					md_append_record(&rqp->sr_rp, m);
337218893Sdim				} else {
338218893Sdim					SMBRQ_SUNLOCK(rqp);
339218893Sdim					SMBERROR("duplicate response %d (ignored)\n", mid);
340218893Sdim					break;
341218893Sdim				}
342218893Sdim			}
343218893Sdim			SMBRQ_SUNLOCK(rqp);
344218893Sdim			smb_iod_rqprocessed(rqp, 0);
345218893Sdim			break;
346218893Sdim		}
347199990Srdivacky		SMB_IOD_RQUNLOCK(iod);
348199990Srdivacky		if (rqp == NULL) {
349207619Srdivacky			SMBERROR("drop resp with mid %d\n", (u_int)mid);
350207619Srdivacky/*			smb_printrqlist(vcp);*/
351207619Srdivacky			m_freem(m);
352207619Srdivacky		}
353207619Srdivacky	}
354207619Srdivacky	/*
355207619Srdivacky	 * check for interrupts
356218893Sdim	 */
357218893Sdim	SMB_IOD_RQLOCK(iod);
358219077Sdim	TAILQ_FOREACH(rqp, &iod->iod_rqlist, sr_link) {
359234353Sdim		if (smb_proc_intr(rqp->sr_cred->scr_td->td_proc)) {
360218893Sdim			smb_iod_rqprocessed(rqp, EINTR);
361218893Sdim		}
362218893Sdim	}
363218893Sdim	SMB_IOD_RQUNLOCK(iod);
364224145Sdim	return 0;
365224145Sdim}
366224145Sdim
367218893Sdimint
368219077Sdimsmb_iod_request(struct smbiod *iod, int event, void *ident)
369219077Sdim{
370219077Sdim	struct smbiod_event *evp;
371219077Sdim	int error;
372234353Sdim
373218893Sdim	SMBIODEBUG("\n");
374218893Sdim	evp = smb_zmalloc(sizeof(*evp), M_SMBIOD, M_WAITOK);
375218893Sdim	evp->ev_type = event;
376224145Sdim	evp->ev_ident = ident;
377218893Sdim	SMB_IOD_EVLOCK(iod);
378218893Sdim	STAILQ_INSERT_TAIL(&iod->iod_evlist, evp, ev_link);
379234353Sdim	if ((event & SMBIOD_EV_SYNC) == 0) {
380234353Sdim		SMB_IOD_EVUNLOCK(iod);
381234353Sdim		smb_iod_wakeup(iod);
382234353Sdim		return 0;
383218893Sdim	}
384234353Sdim	smb_iod_wakeup(iod);
385234353Sdim	msleep(evp, SMB_IOD_EVLOCKPTR(iod), PWAIT | PDROP, "90evw", 0);
386218893Sdim	error = evp->ev_error;
387193326Sed	free(evp, M_SMBIOD);
388193326Sed	return error;
389199990Srdivacky}
390199990Srdivacky
391219077Sdim/*
392234353Sdim * Place request in the queue.
393212904Sdim * Request from smbiod have a high priority.
394199990Srdivacky */
395199990Srdivackyint
396218893Sdimsmb_iod_addrq(struct smb_rq *rqp)
397234353Sdim{
398234353Sdim	struct smb_vc *vcp = rqp->sr_vc;
399234353Sdim	struct smbiod *iod = vcp->vc_iod;
400199990Srdivacky	int error;
401234353Sdim
402234353Sdim	SMBIODEBUG("\n");
403195341Sed	if (rqp->sr_cred->scr_td->td_proc == iod->iod_p) {
404195341Sed		rqp->sr_flags |= SMBR_INTERNAL;
405210299Sed		SMB_IOD_RQLOCK(iod);
406210299Sed		TAILQ_INSERT_HEAD(&iod->iod_rqlist, rqp, sr_link);
407234353Sdim		SMB_IOD_RQUNLOCK(iod);
408210299Sed		for (;;) {
409210299Sed			if (smb_iod_sendrq(iod, rqp) != 0) {
410234353Sdim				smb_iod_dead(iod);
411234353Sdim				break;
412210299Sed			}
413234353Sdim			/*
414219077Sdim			 * we don't need to lock state field here
415234353Sdim			 */
416218893Sdim			if (rqp->sr_state != SMBRQ_NOTSENT)
417234353Sdim				break;
418218893Sdim			tsleep(&iod->iod_flags, PWAIT, "90sndw", hz);
419210299Sed		}
420210299Sed		if (rqp->sr_lerror)
421249423Sdim			smb_iod_removerq(rqp);
422219077Sdim		return rqp->sr_lerror;
423249423Sdim	}
424249423Sdim
425249423Sdim	switch (iod->iod_state) {
426219077Sdim	    case SMBIOD_ST_NOTCONN:
427249423Sdim		return ENOTCONN;
428249423Sdim	    case SMBIOD_ST_DEAD:
429249423Sdim		error = smb_iod_request(vcp->vc_iod, SMBIOD_EV_CONNECT | SMBIOD_EV_SYNC, NULL);
430249423Sdim		if (error)
431218893Sdim			return error;
432249423Sdim		return EXDEV;
433195341Sed	    default:
434218893Sdim		break;
435218893Sdim	}
436218893Sdim
437218893Sdim	SMB_IOD_RQLOCK(iod);
438218893Sdim	for (;;) {
439218893Sdim		if (vcp->vc_maxmux == 0) {
440218893Sdim			SMBERROR("maxmux == 0\n");
441218893Sdim			break;
442212904Sdim		}
443212904Sdim		if (iod->iod_muxcnt < vcp->vc_maxmux)
444193326Sed			break;
445193326Sed		iod->iod_muxwant++;
446249423Sdim		msleep(&iod->iod_muxwant, SMB_IOD_RQLOCKPTR(iod),
447201361Srdivacky		    PWAIT, "90mux", 0);
448201361Srdivacky	}
449239462Sdim	iod->iod_muxcnt++;
450193326Sed	TAILQ_INSERT_TAIL(&iod->iod_rqlist, rqp, sr_link);
451193326Sed	SMB_IOD_RQUNLOCK(iod);
452193326Sed	smb_iod_wakeup(iod);
453193326Sed	return 0;
454239462Sdim}
455193326Sed
456193326Sedint
457239462Sdimsmb_iod_removerq(struct smb_rq *rqp)
458221345Sdim{
459221345Sdim	struct smb_vc *vcp = rqp->sr_vc;
460193326Sed	struct smbiod *iod = vcp->vc_iod;
461239462Sdim
462193326Sed	SMBIODEBUG("\n");
463239462Sdim	if (rqp->sr_flags & SMBR_INTERNAL) {
464193326Sed		SMB_IOD_RQLOCK(iod);
465239462Sdim		TAILQ_REMOVE(&iod->iod_rqlist, rqp, sr_link);
466193326Sed		SMB_IOD_RQUNLOCK(iod);
467239462Sdim		return 0;
468193326Sed	}
469239462Sdim	SMB_IOD_RQLOCK(iod);
470193326Sed	while (rqp->sr_flags & SMBR_XLOCK) {
471193326Sed		rqp->sr_flags |= SMBR_XLOCKWANT;
472193326Sed		msleep(rqp, SMB_IOD_RQLOCKPTR(iod), PWAIT, "90xrm", 0);
473221345Sdim	}
474239462Sdim	TAILQ_REMOVE(&iod->iod_rqlist, rqp, sr_link);
475239462Sdim	iod->iod_muxcnt--;
476193326Sed	if (iod->iod_muxwant) {
477239462Sdim		iod->iod_muxwant--;
478239462Sdim		wakeup(&iod->iod_muxwant);
479239462Sdim	}
480193326Sed	SMB_IOD_RQUNLOCK(iod);
481193326Sed	return 0;
482193326Sed}
483193326Sed
484193326Sedint
485221345Sdimsmb_iod_waitrq(struct smb_rq *rqp)
486221345Sdim{
487221345Sdim	struct smbiod *iod = rqp->sr_vc->vc_iod;
488221345Sdim	int error;
489221345Sdim
490221345Sdim	SMBIODEBUG("\n");
491221345Sdim	if (rqp->sr_flags & SMBR_INTERNAL) {
492221345Sdim		for (;;) {
493221345Sdim			smb_iod_sendall(iod);
494221345Sdim			smb_iod_recvall(iod);
495239462Sdim			if (rqp->sr_rpgen != rqp->sr_rplast)
496218893Sdim				break;
497218893Sdim			tsleep(&iod->iod_flags, PWAIT, "90irq", hz);
498218893Sdim		}
499218893Sdim		smb_iod_removerq(rqp);
500218893Sdim		return rqp->sr_lerror;
501218893Sdim
502218893Sdim	}
503218893Sdim	SMBRQ_SLOCK(rqp);
504218893Sdim	if (rqp->sr_rpgen == rqp->sr_rplast)
505218893Sdim		msleep(&rqp->sr_state, SMBRQ_SLOCKPTR(rqp), PWAIT, "90wrq", 0);
506199482Srdivacky	rqp->sr_rplast++;
507193326Sed	SMBRQ_SUNLOCK(rqp);
508193326Sed	error = rqp->sr_lerror;
509193326Sed	if (rqp->sr_flags & SMBR_MULTIPACKET) {
510193326Sed		/*
511193326Sed		 * If request should stay in the list, then reinsert it
512193326Sed		 * at the end of queue so other waiters have chance to concur
513193326Sed		 */
514193326Sed		SMB_IOD_RQLOCK(iod);
515193326Sed		TAILQ_REMOVE(&iod->iod_rqlist, rqp, sr_link);
516193326Sed		TAILQ_INSERT_TAIL(&iod->iod_rqlist, rqp, sr_link);
517193326Sed		SMB_IOD_RQUNLOCK(iod);
518193326Sed	} else
519193326Sed		smb_iod_removerq(rqp);
520193326Sed	return error;
521193326Sed}
522193326Sed
523193326Sed
524212904Sdimstatic int
525218893Sdimsmb_iod_sendall(struct smbiod *iod)
526212904Sdim{
527212904Sdim	struct smb_vc *vcp = iod->iod_vc;
528212904Sdim	struct smb_rq *rqp;
529218893Sdim	struct timespec ts, tstimeout;
530249423Sdim	int herror;
531249423Sdim
532212904Sdim	herror = 0;
533212904Sdim	/*
534212904Sdim	 * Loop through the list of requests and send them if possible
535212904Sdim	 */
536218893Sdim	SMB_IOD_RQLOCK(iod);
537249423Sdim	TAILQ_FOREACH(rqp, &iod->iod_rqlist, sr_link) {
538212904Sdim		switch (rqp->sr_state) {
539212904Sdim		    case SMBRQ_NOTSENT:
540212904Sdim			rqp->sr_flags |= SMBR_XLOCK;
541212904Sdim			SMB_IOD_RQUNLOCK(iod);
542212904Sdim			herror = smb_iod_sendrq(iod, rqp);
543212904Sdim			SMB_IOD_RQLOCK(iod);
544212904Sdim			rqp->sr_flags &= ~SMBR_XLOCK;
545212904Sdim			if (rqp->sr_flags & SMBR_XLOCKWANT) {
546212904Sdim				rqp->sr_flags &= ~SMBR_XLOCKWANT;
547212904Sdim				wakeup(rqp);
548212904Sdim			}
549212904Sdim			break;
550218893Sdim		    case SMBRQ_SENT:
551212904Sdim			SMB_TRAN_GETPARAM(vcp, SMBTP_TIMEOUT, &tstimeout);
552212904Sdim			timespecadd(&tstimeout, &tstimeout);
553212904Sdim			getnanotime(&ts);
554218893Sdim			timespecsub(&ts, &tstimeout);
555249423Sdim			if (timespeccmp(&ts, &rqp->sr_timesent, >)) {
556249423Sdim				smb_iod_rqprocessed(rqp, ETIMEDOUT);
557212904Sdim			}
558212904Sdim			break;
559212904Sdim		    default:
560212904Sdim			break;
561218893Sdim		}
562249423Sdim		if (herror)
563212904Sdim			break;
564212904Sdim	}
565212904Sdim	SMB_IOD_RQUNLOCK(iod);
566212904Sdim	if (herror == ENOTCONN)
567212904Sdim		smb_iod_dead(iod);
568212904Sdim	return 0;
569212904Sdim}
570212904Sdim
571212904Sdim/*
572212904Sdim * "main" function for smbiod daemon
573212904Sdim */
574221345Sdimstatic __inline void
575221345Sdimsmb_iod_main(struct smbiod *iod)
576221345Sdim{
577221345Sdim/*	struct smb_vc *vcp = iod->iod_vc;*/
578221345Sdim	struct smbiod_event *evp;
579221345Sdim/*	struct timespec tsnow;*/
580221345Sdim	int error;
581221345Sdim
582221345Sdim	SMBIODEBUG("\n");
583221345Sdim	error = 0;
584221345Sdim
585221345Sdim	/*
586221345Sdim	 * Check all interesting events
587221345Sdim	 */
588221345Sdim	for (;;) {
589221345Sdim		SMB_IOD_EVLOCK(iod);
590221345Sdim		evp = STAILQ_FIRST(&iod->iod_evlist);
591221345Sdim		if (evp == NULL) {
592239462Sdim			SMB_IOD_EVUNLOCK(iod);
593239462Sdim			break;
594239462Sdim		}
595221345Sdim		STAILQ_REMOVE_HEAD(&iod->iod_evlist, ev_link);
596221345Sdim		evp->ev_type |= SMBIOD_EV_PROCESSING;
597221345Sdim		SMB_IOD_EVUNLOCK(iod);
598221345Sdim		switch (evp->ev_type & SMBIOD_EV_MASK) {
599221345Sdim		    case SMBIOD_EV_CONNECT:
600221345Sdim			iod->iod_state = SMBIOD_ST_RECONNECT;
601221345Sdim			evp->ev_error = smb_iod_connect(iod);
602221345Sdim			break;
603221345Sdim		    case SMBIOD_EV_DISCONNECT:
604221345Sdim			evp->ev_error = smb_iod_disconnect(iod);
605221345Sdim			break;
606221345Sdim		    case SMBIOD_EV_TREECONNECT:
607212904Sdim			evp->ev_error = smb_iod_treeconnect(iod, evp->ev_ident);
608218893Sdim			break;
609218893Sdim		    case SMBIOD_EV_SHUTDOWN:
610212904Sdim			iod->iod_flags |= SMBIOD_SHUTDOWN;
611218893Sdim			break;
612249423Sdim		    case SMBIOD_EV_NEWRQ:
613249423Sdim			break;
614212904Sdim		}
615212904Sdim		if (evp->ev_type & SMBIOD_EV_SYNC) {
616212904Sdim			SMB_IOD_EVLOCK(iod);
617212904Sdim			wakeup(evp);
618218893Sdim			SMB_IOD_EVUNLOCK(iod);
619249423Sdim		} else
620212904Sdim			free(evp, M_SMBIOD);
621212904Sdim	}
622212904Sdim#if 0
623212904Sdim	if (iod->iod_state == SMBIOD_ST_VCACTIVE) {
624212904Sdim		getnanotime(&tsnow);
625212904Sdim		timespecsub(&tsnow, &iod->iod_pingtimo);
626212904Sdim		if (timespeccmp(&tsnow, &iod->iod_lastrqsent, >)) {
627212904Sdim			smb_smb_echo(vcp, &iod->iod_scred);
628212904Sdim		}
629212904Sdim	}
630212904Sdim#endif
631218893Sdim	smb_iod_sendall(iod);
632218893Sdim	smb_iod_recvall(iod);
633212904Sdim	return;
634218893Sdim}
635249423Sdim
636249423Sdimvoid
637249423Sdimsmb_iod_thread(void *arg)
638212904Sdim{
639212904Sdim	struct smbiod *iod = arg;
640212904Sdim
641212904Sdim	mtx_lock(&Giant);
642212904Sdim	/*
643212904Sdim	 * Here we assume that the thread structure will be the same
644212904Sdim	 * for an entire kthread (kproc, to be more precise) life.
645218893Sdim	 */
646212904Sdim	iod->iod_td = curthread;
647212904Sdim	smb_makescred(&iod->iod_scred, iod->iod_td, NULL);
648212904Sdim	while ((iod->iod_flags & SMBIOD_SHUTDOWN) == 0) {
649212904Sdim		smb_iod_main(iod);
650212904Sdim		SMBIODEBUG("going to sleep for %d ticks\n", iod->iod_sleeptimo);
651212904Sdim/*		mtx_unlock(&Giant, MTX_DEF);*/
652212904Sdim		if (iod->iod_flags & SMBIOD_SHUTDOWN)
653218893Sdim			break;
654212904Sdim		tsleep(&iod->iod_flags, PWAIT, "90idle", iod->iod_sleeptimo);
655212904Sdim	}
656212904Sdim/*	mtx_lock(&Giant, MTX_DEF);*/
657212904Sdim	kthread_exit(0);
658212904Sdim}
659212904Sdim
660212904Sdimint
661212904Sdimsmb_iod_create(struct smb_vc *vcp)
662212904Sdim{
663212904Sdim	struct smbiod *iod;
664212904Sdim	int error;
665234353Sdim
666234353Sdim	iod = smb_zmalloc(sizeof(*iod), M_SMBIOD, M_WAITOK);
667234353Sdim	iod->iod_id = smb_iod_next++;
668234353Sdim	iod->iod_state = SMBIOD_ST_NOTCONN;
669234353Sdim	iod->iod_vc = vcp;
670234353Sdim	iod->iod_sleeptimo = hz * SMBIOD_SLEEP_TIMO;
671212904Sdim	iod->iod_pingtimo.tv_sec = SMBIOD_PING_TIMO;
672234353Sdim	getnanotime(&iod->iod_lastrqsent);
673234353Sdim	vcp->vc_iod = iod;
674234353Sdim	smb_sl_init(&iod->iod_rqlock, "90rql");
675234353Sdim	TAILQ_INIT(&iod->iod_rqlist);
676234353Sdim	smb_sl_init(&iod->iod_evlock, "90evl");
677234353Sdim	STAILQ_INIT(&iod->iod_evlist);
678234353Sdim	error = kthread_create(smb_iod_thread, iod, &iod->iod_p,
679234353Sdim	    RFNOWAIT, "smbiod%d", iod->iod_id);
680234353Sdim	if (error) {
681234353Sdim		SMBERROR("can't start smbiod: %d", error);
682234353Sdim		free(iod, M_SMBIOD);
683234353Sdim		return error;
684234353Sdim	}
685234353Sdim	return 0;
686234353Sdim}
687234353Sdim
688234353Sdimint
689234353Sdimsmb_iod_destroy(struct smbiod *iod)
690234353Sdim{
691234353Sdim	smb_iod_request(iod, SMBIOD_EV_SHUTDOWN | SMBIOD_EV_SYNC, NULL);
692234353Sdim	smb_sl_destroy(&iod->iod_rqlock);
693234353Sdim	smb_sl_destroy(&iod->iod_evlock);
694234353Sdim	free(iod, M_SMBIOD);
695234353Sdim	return 0;
696234353Sdim}
697234353Sdim
698234353Sdimint
699201361Srdivackysmb_iod_init(void)
700201361Srdivacky{
701201361Srdivacky	return 0;
702201361Srdivacky}
703201361Srdivacky
704201361Srdivackyint
705201361Srdivackysmb_iod_done(void)
706201361Srdivacky{
707198092Srdivacky	return 0;
708193326Sed}
709193326Sed
710193326Sed