1139823Simp/*-
275374Sbp * Copyright (c) 2000-2001 Boris Popov
375374Sbp * All rights reserved.
475374Sbp *
575374Sbp * Redistribution and use in source and binary forms, with or without
675374Sbp * modification, are permitted provided that the following conditions
775374Sbp * are met:
875374Sbp * 1. Redistributions of source code must retain the above copyright
975374Sbp *    notice, this list of conditions and the following disclaimer.
1075374Sbp * 2. Redistributions in binary form must reproduce the above copyright
1175374Sbp *    notice, this list of conditions and the following disclaimer in the
1275374Sbp *    documentation and/or other materials provided with the distribution.
1375374Sbp *
1475374Sbp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1575374Sbp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1675374Sbp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1775374Sbp * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1875374Sbp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1975374Sbp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2075374Sbp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2175374Sbp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2275374Sbp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2375374Sbp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2475374Sbp * SUCH DAMAGE.
2575374Sbp */
26116189Sobrien
27116189Sobrien#include <sys/cdefs.h>
28116189Sobrien__FBSDID("$FreeBSD$");
2975374Sbp
3075374Sbp#include <sys/param.h>
3175374Sbp#include <sys/systm.h>
3295533Smike#include <sys/endian.h>
3375374Sbp#include <sys/proc.h>
3475374Sbp#include <sys/kernel.h>
3575374Sbp#include <sys/kthread.h>
3675374Sbp#include <sys/malloc.h>
3775374Sbp#include <sys/mbuf.h>
3875374Sbp#include <sys/unistd.h>
3975374Sbp
4075374Sbp#include <netsmb/smb.h>
4175374Sbp#include <netsmb/smb_conn.h>
4275374Sbp#include <netsmb/smb_rq.h>
4375374Sbp#include <netsmb/smb_tran.h>
4475374Sbp#include <netsmb/smb_trantcp.h>
4575374Sbp
4675374Sbp
4775374Sbp#define SMBIOD_SLEEP_TIMO	2
4875374Sbp#define	SMBIOD_PING_TIMO	60	/* seconds */
4975374Sbp
5075374Sbp#define	SMB_IOD_EVLOCKPTR(iod)	(&((iod)->iod_evlock))
5175374Sbp#define	SMB_IOD_EVLOCK(iod)	smb_sl_lock(&((iod)->iod_evlock))
5275374Sbp#define	SMB_IOD_EVUNLOCK(iod)	smb_sl_unlock(&((iod)->iod_evlock))
5375374Sbp
5475374Sbp#define	SMB_IOD_RQLOCKPTR(iod)	(&((iod)->iod_rqlock))
5575374Sbp#define	SMB_IOD_RQLOCK(iod)	smb_sl_lock(&((iod)->iod_rqlock))
5675374Sbp#define	SMB_IOD_RQUNLOCK(iod)	smb_sl_unlock(&((iod)->iod_rqlock))
5775374Sbp
5875374Sbp#define	smb_iod_wakeup(iod)	wakeup(&(iod)->iod_flags)
5975374Sbp
6075374Sbp
6175374Sbpstatic MALLOC_DEFINE(M_SMBIOD, "SMBIOD", "SMB network io daemon");
6275374Sbp
6375374Sbpstatic int smb_iod_next;
6475374Sbp
6575374Sbpstatic int  smb_iod_sendall(struct smbiod *iod);
6675374Sbpstatic int  smb_iod_disconnect(struct smbiod *iod);
6775374Sbpstatic void smb_iod_thread(void *);
6875374Sbp
6975374Sbpstatic __inline void
7075374Sbpsmb_iod_rqprocessed(struct smb_rq *rqp, int error)
7175374Sbp{
7275374Sbp	SMBRQ_SLOCK(rqp);
7375374Sbp	rqp->sr_lerror = error;
7475374Sbp	rqp->sr_rpgen++;
7575374Sbp	rqp->sr_state = SMBRQ_NOTIFIED;
7675374Sbp	wakeup(&rqp->sr_state);
7775374Sbp	SMBRQ_SUNLOCK(rqp);
7875374Sbp}
7975374Sbp
8075374Sbpstatic void
8175374Sbpsmb_iod_invrq(struct smbiod *iod)
8275374Sbp{
8375374Sbp	struct smb_rq *rqp;
8475374Sbp
8575374Sbp	/*
8682045Sbp	 * Invalidate all outstanding requests for this connection
8775374Sbp	 */
8875374Sbp	SMB_IOD_RQLOCK(iod);
8975374Sbp	TAILQ_FOREACH(rqp, &iod->iod_rqlist, sr_link) {
9075374Sbp		if (rqp->sr_flags & SMBR_INTERNAL)
9175374Sbp			SMBRQ_SUNLOCK(rqp);
9275374Sbp		rqp->sr_flags |= SMBR_RESTART;
9375374Sbp		smb_iod_rqprocessed(rqp, ENOTCONN);
9475374Sbp	}
9575374Sbp	SMB_IOD_RQUNLOCK(iod);
9675374Sbp}
9775374Sbp
9875374Sbpstatic void
9975374Sbpsmb_iod_closetran(struct smbiod *iod)
10075374Sbp{
10175374Sbp	struct smb_vc *vcp = iod->iod_vc;
10287192Sbp	struct thread *td = iod->iod_td;
10375374Sbp
10475374Sbp	if (vcp->vc_tdata == NULL)
10575374Sbp		return;
10687192Sbp	SMB_TRAN_DISCONNECT(vcp, td);
10787192Sbp	SMB_TRAN_DONE(vcp, td);
10875374Sbp	vcp->vc_tdata = NULL;
10975374Sbp}
11075374Sbp
11175374Sbpstatic void
11275374Sbpsmb_iod_dead(struct smbiod *iod)
11375374Sbp{
11475374Sbp	iod->iod_state = SMBIOD_ST_DEAD;
11575374Sbp	smb_iod_closetran(iod);
11675374Sbp	smb_iod_invrq(iod);
11775374Sbp}
11875374Sbp
11975374Sbpstatic int
12075374Sbpsmb_iod_connect(struct smbiod *iod)
12175374Sbp{
12275374Sbp	struct smb_vc *vcp = iod->iod_vc;
12387192Sbp	struct thread *td = iod->iod_td;
12475374Sbp	int error;
12575374Sbp
12675374Sbp	SMBIODEBUG("%d\n", iod->iod_state);
12775374Sbp	switch(iod->iod_state) {
12875374Sbp	    case SMBIOD_ST_VCACTIVE:
12975374Sbp		SMBERROR("called for already opened connection\n");
13075374Sbp		return EISCONN;
13175374Sbp	    case SMBIOD_ST_DEAD:
13275374Sbp		return ENOTCONN;	/* XXX: last error code ? */
13375374Sbp	    default:
13475374Sbp		break;
13575374Sbp	}
13675374Sbp	vcp->vc_genid++;
13775374Sbp	error = 0;
138119376Smarcel
139119376Smarcel	error = (int)SMB_TRAN_CREATE(vcp, td);
140119376Smarcel	if (error)
141119376Smarcel		goto fail;
142119376Smarcel	SMBIODEBUG("tcreate\n");
143119376Smarcel	if (vcp->vc_laddr) {
144119376Smarcel		error = (int)SMB_TRAN_BIND(vcp, vcp->vc_laddr, td);
145119376Smarcel		if (error)
146119376Smarcel			goto fail;
147119376Smarcel	}
148119376Smarcel	SMBIODEBUG("tbind\n");
149119376Smarcel	error = (int)SMB_TRAN_CONNECT(vcp, vcp->vc_paddr, td);
150119376Smarcel	if (error)
151119376Smarcel		goto fail;
152119376Smarcel	SMB_TRAN_SETPARAM(vcp, SMBTP_SELECTID, &iod->iod_flags);
153119376Smarcel	iod->iod_state = SMBIOD_ST_TRANACTIVE;
154119376Smarcel	SMBIODEBUG("tconnect\n");
155119376Smarcel	/* vcp->vc_mid = 0;*/
156119376Smarcel	error = (int)smb_smb_negotiate(vcp, &iod->iod_scred);
157119376Smarcel	if (error)
158119376Smarcel		goto fail;
159119376Smarcel	SMBIODEBUG("snegotiate\n");
160119376Smarcel	error = (int)smb_smb_ssnsetup(vcp, &iod->iod_scred);
161119376Smarcel	if (error)
162119376Smarcel		goto fail;
163119376Smarcel	iod->iod_state = SMBIOD_ST_VCACTIVE;
164119376Smarcel	SMBIODEBUG("completed\n");
165119376Smarcel	smb_iod_invrq(iod);
166119376Smarcel	return (0);
167119376Smarcel
168119376Smarcel fail:
169119376Smarcel	smb_iod_dead(iod);
170119376Smarcel	return (error);
17175374Sbp}
17275374Sbp
17375374Sbpstatic int
17475374Sbpsmb_iod_disconnect(struct smbiod *iod)
17575374Sbp{
17675374Sbp	struct smb_vc *vcp = iod->iod_vc;
17775374Sbp
17875374Sbp	SMBIODEBUG("\n");
17975374Sbp	if (iod->iod_state == SMBIOD_ST_VCACTIVE) {
18075374Sbp		smb_smb_ssnclose(vcp, &iod->iod_scred);
18175374Sbp		iod->iod_state = SMBIOD_ST_TRANACTIVE;
18275374Sbp	}
18375374Sbp	vcp->vc_smbuid = SMB_UID_UNKNOWN;
18475374Sbp	smb_iod_closetran(iod);
18575374Sbp	iod->iod_state = SMBIOD_ST_NOTCONN;
18675374Sbp	return 0;
18775374Sbp}
18875374Sbp
18975374Sbpstatic int
19075374Sbpsmb_iod_treeconnect(struct smbiod *iod, struct smb_share *ssp)
19175374Sbp{
19275374Sbp	int error;
19375374Sbp
19475374Sbp	if (iod->iod_state != SMBIOD_ST_VCACTIVE) {
19575374Sbp		if (iod->iod_state != SMBIOD_ST_DEAD)
19675374Sbp			return ENOTCONN;
19775374Sbp		iod->iod_state = SMBIOD_ST_RECONNECT;
19875374Sbp		error = smb_iod_connect(iod);
19975374Sbp		if (error)
20075374Sbp			return error;
20175374Sbp	}
20275374Sbp	SMBIODEBUG("tree reconnect\n");
20375374Sbp	SMBS_ST_LOCK(ssp);
20475374Sbp	ssp->ss_flags |= SMBS_RECONNECTING;
20575374Sbp	SMBS_ST_UNLOCK(ssp);
20675374Sbp	error = smb_smb_treeconnect(ssp, &iod->iod_scred);
20775374Sbp	SMBS_ST_LOCK(ssp);
20875374Sbp	ssp->ss_flags &= ~SMBS_RECONNECTING;
20975374Sbp	SMBS_ST_UNLOCK(ssp);
21075374Sbp	wakeup(&ssp->ss_vcgenid);
21175374Sbp	return error;
21275374Sbp}
21375374Sbp
21475374Sbpstatic int
21575374Sbpsmb_iod_sendrq(struct smbiod *iod, struct smb_rq *rqp)
21675374Sbp{
21787192Sbp	struct thread *td = iod->iod_td;
21875374Sbp	struct smb_vc *vcp = iod->iod_vc;
21975374Sbp	struct smb_share *ssp = rqp->sr_share;
22075374Sbp	struct mbuf *m;
22175374Sbp	int error;
22275374Sbp
22375374Sbp	SMBIODEBUG("iod_state = %d\n", iod->iod_state);
22475374Sbp	switch (iod->iod_state) {
22575374Sbp	    case SMBIOD_ST_NOTCONN:
22675374Sbp		smb_iod_rqprocessed(rqp, ENOTCONN);
22775374Sbp		return 0;
22875374Sbp	    case SMBIOD_ST_DEAD:
22975374Sbp		iod->iod_state = SMBIOD_ST_RECONNECT;
23075374Sbp		return 0;
23175374Sbp	    case SMBIOD_ST_RECONNECT:
23275374Sbp		return 0;
23375374Sbp	    default:
23475374Sbp		break;
23575374Sbp	}
23675374Sbp	if (rqp->sr_sendcnt == 0) {
23775374Sbp#ifdef movedtoanotherplace
23875374Sbp		if (vcp->vc_maxmux != 0 && iod->iod_muxcnt >= vcp->vc_maxmux)
23975374Sbp			return 0;
24075374Sbp#endif
241161523Smarcel		le16enc(rqp->sr_rqtid, ssp ? ssp->ss_tid : SMB_TID_UNKNOWN);
242161523Smarcel		le16enc(rqp->sr_rquid, vcp ? vcp->vc_smbuid : 0);
24375374Sbp		mb_fixhdr(&rqp->sr_rq);
244124087Stjr		if (vcp->vc_hflags2 & SMB_FLAGS2_SECURITY_SIGNATURE)
245124087Stjr			smb_rq_sign(rqp);
24675374Sbp	}
24775374Sbp	if (rqp->sr_sendcnt++ > 5) {
24875374Sbp		rqp->sr_flags |= SMBR_RESTART;
24975374Sbp		smb_iod_rqprocessed(rqp, rqp->sr_lerror);
25075374Sbp		/*
25175374Sbp		 * If all attempts to send a request failed, then
25275374Sbp		 * something is seriously hosed.
25375374Sbp		 */
25475374Sbp		return ENOTCONN;
25575374Sbp	}
25675374Sbp	SMBSDEBUG("M:%04x, P:%04x, U:%04x, T:%04x\n", rqp->sr_mid, 0, 0, 0);
25775374Sbp	m_dumpm(rqp->sr_rq.mb_top);
258243882Sglebius	m = m_copym(rqp->sr_rq.mb_top, 0, M_COPYALL, M_WAITOK);
259177599Sru	error = rqp->sr_lerror = SMB_TRAN_SEND(vcp, m, td);
26075374Sbp	if (error == 0) {
26175374Sbp		getnanotime(&rqp->sr_timesent);
26275374Sbp		iod->iod_lastrqsent = rqp->sr_timesent;
26375374Sbp		rqp->sr_flags |= SMBR_SENT;
26475374Sbp		rqp->sr_state = SMBRQ_SENT;
26575374Sbp		return 0;
26675374Sbp	}
26775374Sbp	/*
26875374Sbp	 * Check for fatal errors
26975374Sbp	 */
27075374Sbp	if (SMB_TRAN_FATAL(vcp, error)) {
27175374Sbp		/*
27275374Sbp		 * No further attempts should be made
27375374Sbp		 */
27475374Sbp		return ENOTCONN;
27575374Sbp	}
27675374Sbp	if (smb_rq_intr(rqp))
27775374Sbp		smb_iod_rqprocessed(rqp, EINTR);
27875374Sbp	return 0;
27975374Sbp}
28075374Sbp
28175374Sbp/*
28275374Sbp * Process incoming packets
28375374Sbp */
28475374Sbpstatic int
28575374Sbpsmb_iod_recvall(struct smbiod *iod)
28675374Sbp{
28775374Sbp	struct smb_vc *vcp = iod->iod_vc;
28887192Sbp	struct thread *td = iod->iod_td;
28975374Sbp	struct smb_rq *rqp;
29075374Sbp	struct mbuf *m;
29175374Sbp	u_char *hp;
29275374Sbp	u_short mid;
29375374Sbp	int error;
29475374Sbp
29575374Sbp	switch (iod->iod_state) {
29675374Sbp	    case SMBIOD_ST_NOTCONN:
29775374Sbp	    case SMBIOD_ST_DEAD:
29875374Sbp	    case SMBIOD_ST_RECONNECT:
29975374Sbp		return 0;
30075374Sbp	    default:
30175374Sbp		break;
30275374Sbp	}
30375374Sbp	for (;;) {
30475374Sbp		m = NULL;
30587192Sbp		error = SMB_TRAN_RECV(vcp, &m, td);
30675374Sbp		if (error == EWOULDBLOCK)
30775374Sbp			break;
30875374Sbp		if (SMB_TRAN_FATAL(vcp, error)) {
30975374Sbp			smb_iod_dead(iod);
31075374Sbp			break;
31175374Sbp		}
31275374Sbp		if (error)
31375374Sbp			break;
31475374Sbp		if (m == NULL) {
31575374Sbp			SMBERROR("tran return NULL without error\n");
31675374Sbp			error = EPIPE;
31775374Sbp			continue;
31875374Sbp		}
31975374Sbp		m = m_pullup(m, SMB_HDRLEN);
32075374Sbp		if (m == NULL)
32175374Sbp			continue;	/* wait for a good packet */
32275374Sbp		/*
32375374Sbp		 * Now we got an entire and possibly invalid SMB packet.
32475374Sbp		 * Be careful while parsing it.
32575374Sbp		 */
32675374Sbp		m_dumpm(m);
32775374Sbp		hp = mtod(m, u_char*);
32875374Sbp		if (bcmp(hp, SMB_SIGNATURE, SMB_SIGLEN) != 0) {
32975374Sbp			m_freem(m);
33075374Sbp			continue;
33175374Sbp		}
33275374Sbp		mid = SMB_HDRMID(hp);
33375374Sbp		SMBSDEBUG("mid %04x\n", (u_int)mid);
33475374Sbp		SMB_IOD_RQLOCK(iod);
33575374Sbp		TAILQ_FOREACH(rqp, &iod->iod_rqlist, sr_link) {
33675374Sbp			if (rqp->sr_mid != mid)
33775374Sbp				continue;
33875374Sbp			SMBRQ_SLOCK(rqp);
33975374Sbp			if (rqp->sr_rp.md_top == NULL) {
34075374Sbp				md_initm(&rqp->sr_rp, m);
34175374Sbp			} else {
34275374Sbp				if (rqp->sr_flags & SMBR_MULTIPACKET) {
34375374Sbp					md_append_record(&rqp->sr_rp, m);
34475374Sbp				} else {
34575374Sbp					SMBRQ_SUNLOCK(rqp);
34675374Sbp					SMBERROR("duplicate response %d (ignored)\n", mid);
34775374Sbp					break;
34875374Sbp				}
34975374Sbp			}
35075374Sbp			SMBRQ_SUNLOCK(rqp);
35175374Sbp			smb_iod_rqprocessed(rqp, 0);
35275374Sbp			break;
35375374Sbp		}
35475374Sbp		SMB_IOD_RQUNLOCK(iod);
35575374Sbp		if (rqp == NULL) {
35675374Sbp			SMBERROR("drop resp with mid %d\n", (u_int)mid);
35775374Sbp/*			smb_printrqlist(vcp);*/
35875374Sbp			m_freem(m);
35975374Sbp		}
36075374Sbp	}
36175374Sbp	/*
36275374Sbp	 * check for interrupts
36375374Sbp	 */
36475374Sbp	SMB_IOD_RQLOCK(iod);
36575374Sbp	TAILQ_FOREACH(rqp, &iod->iod_rqlist, sr_link) {
366112888Sjeff		if (smb_td_intr(rqp->sr_cred->scr_td)) {
36775374Sbp			smb_iod_rqprocessed(rqp, EINTR);
36875374Sbp		}
36975374Sbp	}
37075374Sbp	SMB_IOD_RQUNLOCK(iod);
37175374Sbp	return 0;
37275374Sbp}
37375374Sbp
37475374Sbpint
37575374Sbpsmb_iod_request(struct smbiod *iod, int event, void *ident)
37675374Sbp{
37775374Sbp	struct smbiod_event *evp;
37875374Sbp	int error;
37975374Sbp
38075374Sbp	SMBIODEBUG("\n");
381111119Simp	evp = smb_zmalloc(sizeof(*evp), M_SMBIOD, M_WAITOK);
38275374Sbp	evp->ev_type = event;
38375374Sbp	evp->ev_ident = ident;
38475374Sbp	SMB_IOD_EVLOCK(iod);
38575374Sbp	STAILQ_INSERT_TAIL(&iod->iod_evlist, evp, ev_link);
38675374Sbp	if ((event & SMBIOD_EV_SYNC) == 0) {
38775374Sbp		SMB_IOD_EVUNLOCK(iod);
38875374Sbp		smb_iod_wakeup(iod);
38975374Sbp		return 0;
39075374Sbp	}
39175374Sbp	smb_iod_wakeup(iod);
39275374Sbp	msleep(evp, SMB_IOD_EVLOCKPTR(iod), PWAIT | PDROP, "90evw", 0);
39375374Sbp	error = evp->ev_error;
39475374Sbp	free(evp, M_SMBIOD);
39575374Sbp	return error;
39675374Sbp}
39775374Sbp
39875374Sbp/*
39975374Sbp * Place request in the queue.
40075374Sbp * Request from smbiod have a high priority.
40175374Sbp */
40275374Sbpint
40375374Sbpsmb_iod_addrq(struct smb_rq *rqp)
40475374Sbp{
40575374Sbp	struct smb_vc *vcp = rqp->sr_vc;
40675374Sbp	struct smbiod *iod = vcp->vc_iod;
40775374Sbp	int error;
40875374Sbp
40975374Sbp	SMBIODEBUG("\n");
410116339Stjr	if (rqp->sr_cred->scr_td != NULL &&
411116339Stjr	    rqp->sr_cred->scr_td->td_proc == iod->iod_p) {
41275374Sbp		rqp->sr_flags |= SMBR_INTERNAL;
41375374Sbp		SMB_IOD_RQLOCK(iod);
41475374Sbp		TAILQ_INSERT_HEAD(&iod->iod_rqlist, rqp, sr_link);
41575374Sbp		SMB_IOD_RQUNLOCK(iod);
41675374Sbp		for (;;) {
41775374Sbp			if (smb_iod_sendrq(iod, rqp) != 0) {
41875374Sbp				smb_iod_dead(iod);
41975374Sbp				break;
42075374Sbp			}
42175374Sbp			/*
42275374Sbp			 * we don't need to lock state field here
42375374Sbp			 */
42475374Sbp			if (rqp->sr_state != SMBRQ_NOTSENT)
42575374Sbp				break;
42675374Sbp			tsleep(&iod->iod_flags, PWAIT, "90sndw", hz);
42775374Sbp		}
42875374Sbp		if (rqp->sr_lerror)
42975374Sbp			smb_iod_removerq(rqp);
43075374Sbp		return rqp->sr_lerror;
43175374Sbp	}
43275374Sbp
43375374Sbp	switch (iod->iod_state) {
43475374Sbp	    case SMBIOD_ST_NOTCONN:
43575374Sbp		return ENOTCONN;
43675374Sbp	    case SMBIOD_ST_DEAD:
43775374Sbp		error = smb_iod_request(vcp->vc_iod, SMBIOD_EV_CONNECT | SMBIOD_EV_SYNC, NULL);
43875374Sbp		if (error)
43975374Sbp			return error;
44075374Sbp		return EXDEV;
44175374Sbp	    default:
44275374Sbp		break;
44375374Sbp	}
44475374Sbp
44575374Sbp	SMB_IOD_RQLOCK(iod);
44675374Sbp	for (;;) {
44775374Sbp		if (vcp->vc_maxmux == 0) {
44875374Sbp			SMBERROR("maxmux == 0\n");
44975374Sbp			break;
45075374Sbp		}
45175374Sbp		if (iod->iod_muxcnt < vcp->vc_maxmux)
45275374Sbp			break;
45375374Sbp		iod->iod_muxwant++;
45475374Sbp		msleep(&iod->iod_muxwant, SMB_IOD_RQLOCKPTR(iod),
45575374Sbp		    PWAIT, "90mux", 0);
45675374Sbp	}
45775374Sbp	iod->iod_muxcnt++;
45875374Sbp	TAILQ_INSERT_TAIL(&iod->iod_rqlist, rqp, sr_link);
45975374Sbp	SMB_IOD_RQUNLOCK(iod);
46075374Sbp	smb_iod_wakeup(iod);
46175374Sbp	return 0;
46275374Sbp}
46375374Sbp
46475374Sbpint
46575374Sbpsmb_iod_removerq(struct smb_rq *rqp)
46675374Sbp{
46775374Sbp	struct smb_vc *vcp = rqp->sr_vc;
46875374Sbp	struct smbiod *iod = vcp->vc_iod;
46975374Sbp
47075374Sbp	SMBIODEBUG("\n");
47175374Sbp	if (rqp->sr_flags & SMBR_INTERNAL) {
47275374Sbp		SMB_IOD_RQLOCK(iod);
47375374Sbp		TAILQ_REMOVE(&iod->iod_rqlist, rqp, sr_link);
47475374Sbp		SMB_IOD_RQUNLOCK(iod);
47575374Sbp		return 0;
47675374Sbp	}
47775374Sbp	SMB_IOD_RQLOCK(iod);
47875374Sbp	while (rqp->sr_flags & SMBR_XLOCK) {
47975374Sbp		rqp->sr_flags |= SMBR_XLOCKWANT;
48075374Sbp		msleep(rqp, SMB_IOD_RQLOCKPTR(iod), PWAIT, "90xrm", 0);
48175374Sbp	}
48275374Sbp	TAILQ_REMOVE(&iod->iod_rqlist, rqp, sr_link);
48375374Sbp	iod->iod_muxcnt--;
48475374Sbp	if (iod->iod_muxwant) {
48575374Sbp		iod->iod_muxwant--;
48675374Sbp		wakeup(&iod->iod_muxwant);
48775374Sbp	}
48875374Sbp	SMB_IOD_RQUNLOCK(iod);
48975374Sbp	return 0;
49075374Sbp}
49175374Sbp
49275374Sbpint
49375374Sbpsmb_iod_waitrq(struct smb_rq *rqp)
49475374Sbp{
49575374Sbp	struct smbiod *iod = rqp->sr_vc->vc_iod;
49675374Sbp	int error;
49775374Sbp
49875374Sbp	SMBIODEBUG("\n");
49975374Sbp	if (rqp->sr_flags & SMBR_INTERNAL) {
50075374Sbp		for (;;) {
50175374Sbp			smb_iod_sendall(iod);
50275374Sbp			smb_iod_recvall(iod);
50375374Sbp			if (rqp->sr_rpgen != rqp->sr_rplast)
50475374Sbp				break;
50575374Sbp			tsleep(&iod->iod_flags, PWAIT, "90irq", hz);
50675374Sbp		}
50775374Sbp		smb_iod_removerq(rqp);
50875374Sbp		return rqp->sr_lerror;
50975374Sbp
51075374Sbp	}
51175374Sbp	SMBRQ_SLOCK(rqp);
51275374Sbp	if (rqp->sr_rpgen == rqp->sr_rplast)
51375374Sbp		msleep(&rqp->sr_state, SMBRQ_SLOCKPTR(rqp), PWAIT, "90wrq", 0);
51475374Sbp	rqp->sr_rplast++;
51575374Sbp	SMBRQ_SUNLOCK(rqp);
51675374Sbp	error = rqp->sr_lerror;
51775374Sbp	if (rqp->sr_flags & SMBR_MULTIPACKET) {
51875374Sbp		/*
51975374Sbp		 * If request should stay in the list, then reinsert it
52075374Sbp		 * at the end of queue so other waiters have chance to concur
52175374Sbp		 */
52275374Sbp		SMB_IOD_RQLOCK(iod);
52375374Sbp		TAILQ_REMOVE(&iod->iod_rqlist, rqp, sr_link);
52475374Sbp		TAILQ_INSERT_TAIL(&iod->iod_rqlist, rqp, sr_link);
52575374Sbp		SMB_IOD_RQUNLOCK(iod);
52675374Sbp	} else
52775374Sbp		smb_iod_removerq(rqp);
52875374Sbp	return error;
52975374Sbp}
53075374Sbp
53175374Sbp
53275374Sbpstatic int
53375374Sbpsmb_iod_sendall(struct smbiod *iod)
53475374Sbp{
53575374Sbp	struct smb_vc *vcp = iod->iod_vc;
53675374Sbp	struct smb_rq *rqp;
53775374Sbp	struct timespec ts, tstimeout;
53875374Sbp	int herror;
53975374Sbp
54075374Sbp	herror = 0;
54175374Sbp	/*
54275374Sbp	 * Loop through the list of requests and send them if possible
54375374Sbp	 */
54475374Sbp	SMB_IOD_RQLOCK(iod);
54575374Sbp	TAILQ_FOREACH(rqp, &iod->iod_rqlist, sr_link) {
54675374Sbp		switch (rqp->sr_state) {
54775374Sbp		    case SMBRQ_NOTSENT:
54875374Sbp			rqp->sr_flags |= SMBR_XLOCK;
54975374Sbp			SMB_IOD_RQUNLOCK(iod);
55075374Sbp			herror = smb_iod_sendrq(iod, rqp);
55175374Sbp			SMB_IOD_RQLOCK(iod);
55275374Sbp			rqp->sr_flags &= ~SMBR_XLOCK;
55375374Sbp			if (rqp->sr_flags & SMBR_XLOCKWANT) {
55475374Sbp				rqp->sr_flags &= ~SMBR_XLOCKWANT;
55575374Sbp				wakeup(rqp);
55675374Sbp			}
55775374Sbp			break;
55875374Sbp		    case SMBRQ_SENT:
55975374Sbp			SMB_TRAN_GETPARAM(vcp, SMBTP_TIMEOUT, &tstimeout);
56075374Sbp			timespecadd(&tstimeout, &tstimeout);
56175374Sbp			getnanotime(&ts);
56275374Sbp			timespecsub(&ts, &tstimeout);
56375374Sbp			if (timespeccmp(&ts, &rqp->sr_timesent, >)) {
56475374Sbp				smb_iod_rqprocessed(rqp, ETIMEDOUT);
56575374Sbp			}
56675374Sbp			break;
56775374Sbp		    default:
56897209Speter			break;
56975374Sbp		}
57075374Sbp		if (herror)
57175374Sbp			break;
57275374Sbp	}
57375374Sbp	SMB_IOD_RQUNLOCK(iod);
57475374Sbp	if (herror == ENOTCONN)
57575374Sbp		smb_iod_dead(iod);
57675374Sbp	return 0;
57775374Sbp}
57875374Sbp
57975374Sbp/*
58075374Sbp * "main" function for smbiod daemon
58175374Sbp */
58275374Sbpstatic __inline void
58375374Sbpsmb_iod_main(struct smbiod *iod)
58475374Sbp{
58575374Sbp/*	struct smb_vc *vcp = iod->iod_vc;*/
58675374Sbp	struct smbiod_event *evp;
58775374Sbp/*	struct timespec tsnow;*/
58875374Sbp	int error;
58975374Sbp
59075374Sbp	SMBIODEBUG("\n");
59175374Sbp	error = 0;
59275374Sbp
59375374Sbp	/*
59475374Sbp	 * Check all interesting events
59575374Sbp	 */
59675374Sbp	for (;;) {
59775374Sbp		SMB_IOD_EVLOCK(iod);
59875374Sbp		evp = STAILQ_FIRST(&iod->iod_evlist);
59975374Sbp		if (evp == NULL) {
60075374Sbp			SMB_IOD_EVUNLOCK(iod);
60175374Sbp			break;
60275374Sbp		}
60375374Sbp		STAILQ_REMOVE_HEAD(&iod->iod_evlist, ev_link);
60475374Sbp		evp->ev_type |= SMBIOD_EV_PROCESSING;
60575374Sbp		SMB_IOD_EVUNLOCK(iod);
60675374Sbp		switch (evp->ev_type & SMBIOD_EV_MASK) {
60775374Sbp		    case SMBIOD_EV_CONNECT:
60875374Sbp			iod->iod_state = SMBIOD_ST_RECONNECT;
60975374Sbp			evp->ev_error = smb_iod_connect(iod);
61075374Sbp			break;
61175374Sbp		    case SMBIOD_EV_DISCONNECT:
61275374Sbp			evp->ev_error = smb_iod_disconnect(iod);
61375374Sbp			break;
61475374Sbp		    case SMBIOD_EV_TREECONNECT:
61575374Sbp			evp->ev_error = smb_iod_treeconnect(iod, evp->ev_ident);
61675374Sbp			break;
61775374Sbp		    case SMBIOD_EV_SHUTDOWN:
61875374Sbp			iod->iod_flags |= SMBIOD_SHUTDOWN;
61975374Sbp			break;
62075374Sbp		    case SMBIOD_EV_NEWRQ:
62175374Sbp			break;
62275374Sbp		}
62375374Sbp		if (evp->ev_type & SMBIOD_EV_SYNC) {
62475374Sbp			SMB_IOD_EVLOCK(iod);
62575374Sbp			wakeup(evp);
62675374Sbp			SMB_IOD_EVUNLOCK(iod);
62775374Sbp		} else
62875374Sbp			free(evp, M_SMBIOD);
62975374Sbp	}
63075374Sbp#if 0
63175374Sbp	if (iod->iod_state == SMBIOD_ST_VCACTIVE) {
63275374Sbp		getnanotime(&tsnow);
63375374Sbp		timespecsub(&tsnow, &iod->iod_pingtimo);
63475374Sbp		if (timespeccmp(&tsnow, &iod->iod_lastrqsent, >)) {
63575374Sbp			smb_smb_echo(vcp, &iod->iod_scred);
63675374Sbp		}
63775374Sbp	}
63875374Sbp#endif
63975374Sbp	smb_iod_sendall(iod);
64075374Sbp	smb_iod_recvall(iod);
64175374Sbp	return;
64275374Sbp}
64375374Sbp
64475374Sbpvoid
64575374Sbpsmb_iod_thread(void *arg)
64675374Sbp{
64775374Sbp	struct smbiod *iod = arg;
64875374Sbp
649177654Sattilio	mtx_lock(&Giant);
650177654Sattilio
65187192Sbp	/*
65287192Sbp	 * Here we assume that the thread structure will be the same
65387192Sbp	 * for an entire kthread (kproc, to be more precise) life.
65487192Sbp	 */
65587192Sbp	iod->iod_td = curthread;
65687192Sbp	smb_makescred(&iod->iod_scred, iod->iod_td, NULL);
65775374Sbp	while ((iod->iod_flags & SMBIOD_SHUTDOWN) == 0) {
65875374Sbp		smb_iod_main(iod);
65975374Sbp		SMBIODEBUG("going to sleep for %d ticks\n", iod->iod_sleeptimo);
66075374Sbp		if (iod->iod_flags & SMBIOD_SHUTDOWN)
66175374Sbp			break;
66275374Sbp		tsleep(&iod->iod_flags, PWAIT, "90idle", iod->iod_sleeptimo);
66375374Sbp	}
664177654Sattilio	mtx_unlock(&Giant);
665172836Sjulian	kproc_exit(0);
66675374Sbp}
66775374Sbp
66875374Sbpint
66975374Sbpsmb_iod_create(struct smb_vc *vcp)
67075374Sbp{
67175374Sbp	struct smbiod *iod;
67275374Sbp	int error;
67375374Sbp
674111119Simp	iod = smb_zmalloc(sizeof(*iod), M_SMBIOD, M_WAITOK);
67575374Sbp	iod->iod_id = smb_iod_next++;
67675374Sbp	iod->iod_state = SMBIOD_ST_NOTCONN;
67775374Sbp	iod->iod_vc = vcp;
67875374Sbp	iod->iod_sleeptimo = hz * SMBIOD_SLEEP_TIMO;
67975374Sbp	iod->iod_pingtimo.tv_sec = SMBIOD_PING_TIMO;
68075374Sbp	getnanotime(&iod->iod_lastrqsent);
68175374Sbp	vcp->vc_iod = iod;
68275374Sbp	smb_sl_init(&iod->iod_rqlock, "90rql");
68375374Sbp	TAILQ_INIT(&iod->iod_rqlist);
68475374Sbp	smb_sl_init(&iod->iod_evlock, "90evl");
68575374Sbp	STAILQ_INIT(&iod->iod_evlist);
686172836Sjulian	error = kproc_create(smb_iod_thread, iod, &iod->iod_p,
687104354Sscottl	    RFNOWAIT, 0, "smbiod%d", iod->iod_id);
68875374Sbp	if (error) {
68975374Sbp		SMBERROR("can't start smbiod: %d", error);
69075374Sbp		free(iod, M_SMBIOD);
69175374Sbp		return error;
69275374Sbp	}
69375374Sbp	return 0;
69475374Sbp}
69575374Sbp
69675374Sbpint
69775374Sbpsmb_iod_destroy(struct smbiod *iod)
69875374Sbp{
69975374Sbp	smb_iod_request(iod, SMBIOD_EV_SHUTDOWN | SMBIOD_EV_SYNC, NULL);
70082045Sbp	smb_sl_destroy(&iod->iod_rqlock);
70182045Sbp	smb_sl_destroy(&iod->iod_evlock);
70275374Sbp	free(iod, M_SMBIOD);
70375374Sbp	return 0;
70475374Sbp}
70575374Sbp
70675374Sbpint
70775374Sbpsmb_iod_init(void)
70875374Sbp{
70975374Sbp	return 0;
71075374Sbp}
71175374Sbp
71275374Sbpint
71375374Sbpsmb_iod_done(void)
71475374Sbp{
71575374Sbp	return 0;
71675374Sbp}
71775374Sbp
718