2 * Copyright (c) 2000-2001 Boris Popov
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
32 * $FreeBSD: src/sys/netsmb/smb_iod.c,v 1.1.2.2 2002/04/23 03:45:01 bp Exp $
33 * $DragonFly: src/sys/netproto/smb/smb_iod.c,v 1.15 2007/02/03 17:05:58 corecode Exp $
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/endian.h>
40 #include <sys/kernel.h>
41 #include <sys/kthread.h>
42 #include <sys/malloc.h>
44 #include <sys/unistd.h>
46 #include <sys/mplock2.h>
52 #include "smb_trantcp.h"
55 #define SMBIOD_SLEEP_TIMO 2
56 #define SMBIOD_PING_TIMO 60 /* seconds */
58 #define SMB_IOD_EVLOCKPTR(iod) (&(iod)->iod_evlock)
59 #define SMB_IOD_EVLOCK(iod) smb_sl_lock(&(iod)->iod_evlock)
60 #define SMB_IOD_EVUNLOCK(iod) smb_sl_unlock(&(iod)->iod_evlock)
61 #define SMB_IOD_EVINTERLOCK(iod) (&(iod)->iod_evlock)
63 #define SMB_IOD_RQLOCKPTR(iod) (&(iod)->iod_rqlock)
64 #define SMB_IOD_RQLOCK(iod) smb_sl_lock(&((iod)->iod_rqlock))
65 #define SMB_IOD_RQUNLOCK(iod) smb_sl_unlock(&(iod)->iod_rqlock)
66 #define SMB_IOD_RQINTERLOCK(iod) (&(iod)->iod_rqlock)
68 #define smb_iod_wakeup(iod) wakeup(&(iod)->iod_flags)
71 static MALLOC_DEFINE(M_SMBIOD, "SMBIOD", "SMB network io daemon");
73 static int smb_iod_next;
75 static int smb_iod_sendall(struct smbiod *iod);
76 static int smb_iod_disconnect(struct smbiod *iod);
77 static void smb_iod_thread(void *);
80 smb_iod_rqprocessed(struct smb_rq *rqp, int error)
83 rqp->sr_lerror = error;
85 rqp->sr_state = SMBRQ_NOTIFIED;
86 wakeup(&rqp->sr_state);
91 smb_iod_invrq(struct smbiod *iod)
96 * Invalidate all outstanding requests for this connection
99 TAILQ_FOREACH(rqp, &iod->iod_rqlist, sr_link) {
101 /* this makes no sense whatsoever XXX */
102 if (rqp->sr_flags & SMBR_INTERNAL)
105 rqp->sr_flags |= SMBR_RESTART;
106 smb_iod_rqprocessed(rqp, ENOTCONN);
108 SMB_IOD_RQUNLOCK(iod);
112 smb_iod_closetran(struct smbiod *iod)
114 struct smb_vc *vcp = iod->iod_vc;
115 struct thread *td = iod->iod_td;
117 if (vcp->vc_tdata == NULL)
119 SMB_TRAN_DISCONNECT(vcp, td);
120 SMB_TRAN_DONE(vcp, td);
121 vcp->vc_tdata = NULL;
125 smb_iod_dead(struct smbiod *iod)
127 iod->iod_state = SMBIOD_ST_DEAD;
128 smb_iod_closetran(iod);
133 smb_iod_connect(struct smbiod *iod)
135 struct smb_vc *vcp = iod->iod_vc;
136 struct thread *td = iod->iod_td;
139 SMBIODEBUG("%d\n", iod->iod_state);
140 switch(iod->iod_state) {
141 case SMBIOD_ST_VCACTIVE:
142 SMBERROR("called for already opened connection\n");
145 return ENOTCONN; /* XXX: last error code ? */
152 error = SMB_TRAN_CREATE(vcp, td);
155 SMBIODEBUG("tcreate\n");
158 error = SMB_TRAN_BIND(vcp, vcp->vc_laddr, td);
162 SMBIODEBUG("tbind\n");
164 error = SMB_TRAN_CONNECT(vcp, vcp->vc_paddr, td);
167 SMB_TRAN_SETPARAM(vcp, SMBTP_SELECTID, &iod->iod_flags);
168 iod->iod_state = SMBIOD_ST_TRANACTIVE;
169 SMBIODEBUG("tconnect\n");
171 /* vcp->vc_mid = 0;*/
173 error = smb_smb_negotiate(vcp, &iod->iod_scred);
176 SMBIODEBUG("snegotiate\n");
178 error = smb_smb_ssnsetup(vcp, &iod->iod_scred);
181 iod->iod_state = SMBIOD_ST_VCACTIVE;
182 SMBIODEBUG("completed\n");
194 smb_iod_disconnect(struct smbiod *iod)
196 struct smb_vc *vcp = iod->iod_vc;
199 if (iod->iod_state == SMBIOD_ST_VCACTIVE) {
200 smb_smb_ssnclose(vcp, &iod->iod_scred);
201 iod->iod_state = SMBIOD_ST_TRANACTIVE;
203 vcp->vc_smbuid = SMB_UID_UNKNOWN;
204 smb_iod_closetran(iod);
205 iod->iod_state = SMBIOD_ST_NOTCONN;
210 smb_iod_treeconnect(struct smbiod *iod, struct smb_share *ssp)
214 if (iod->iod_state != SMBIOD_ST_VCACTIVE) {
215 if (iod->iod_state != SMBIOD_ST_DEAD)
217 iod->iod_state = SMBIOD_ST_RECONNECT;
218 error = smb_iod_connect(iod);
222 SMBIODEBUG("tree reconnect\n");
224 ssp->ss_flags |= SMBS_RECONNECTING;
226 error = smb_smb_treeconnect(ssp, &iod->iod_scred);
228 ssp->ss_flags &= ~SMBS_RECONNECTING;
230 wakeup(&ssp->ss_vcgenid);
235 smb_iod_sendrq(struct smbiod *iod, struct smb_rq *rqp)
237 struct thread *td = iod->iod_td;
238 struct smb_vc *vcp = iod->iod_vc;
239 struct smb_share *ssp = rqp->sr_share;
243 SMBIODEBUG("iod_state = %d\n", iod->iod_state);
244 switch (iod->iod_state) {
245 case SMBIOD_ST_NOTCONN:
246 smb_iod_rqprocessed(rqp, ENOTCONN);
249 iod->iod_state = SMBIOD_ST_RECONNECT;
251 case SMBIOD_ST_RECONNECT:
256 if (rqp->sr_sendcnt == 0) {
257 #ifdef movedtoanotherplace
258 if (vcp->vc_maxmux != 0 && iod->iod_muxcnt >= vcp->vc_maxmux)
261 *rqp->sr_rqtid = htole16(ssp ? ssp->ss_tid : SMB_TID_UNKNOWN);
262 *rqp->sr_rquid = htole16(vcp ? vcp->vc_smbuid : 0);
263 mb_fixhdr(&rqp->sr_rq);
265 if (rqp->sr_sendcnt++ > 5) {
266 rqp->sr_flags |= SMBR_RESTART;
267 smb_iod_rqprocessed(rqp, rqp->sr_lerror);
269 * If all attempts to send a request failed, then
270 * something is seriously hosed.
274 SMBSDEBUG("M:%04x, P:%04x, U:%04x, T:%04x\n", rqp->sr_mid, 0, 0, 0);
275 m_dumpm(rqp->sr_rq.mb_top);
276 m = m_copym(rqp->sr_rq.mb_top, 0, M_COPYALL, MB_WAIT);
277 error = rqp->sr_lerror = m ? SMB_TRAN_SEND(vcp, m, td) : ENOBUFS;
279 getnanotime(&rqp->sr_timesent);
280 iod->iod_lastrqsent = rqp->sr_timesent;
281 rqp->sr_flags |= SMBR_SENT;
282 rqp->sr_state = SMBRQ_SENT;
286 * Check for fatal errors
288 if (SMB_TRAN_FATAL(vcp, error)) {
290 * No further attempts should be made
294 if (smb_rq_intr(rqp))
295 smb_iod_rqprocessed(rqp, EINTR);
300 * Process incoming packets
303 smb_iod_recvall(struct smbiod *iod)
305 struct smb_vc *vcp = iod->iod_vc;
306 struct thread *td = iod->iod_td;
313 switch (iod->iod_state) {
314 case SMBIOD_ST_NOTCONN:
316 case SMBIOD_ST_RECONNECT:
323 error = SMB_TRAN_RECV(vcp, &m, td);
324 if (error == EWOULDBLOCK)
326 if (SMB_TRAN_FATAL(vcp, error)) {
333 SMBERROR("tran return NULL without error\n");
337 m = m_pullup(m, SMB_HDRLEN);
339 continue; /* wait for a good packet */
341 * Now we got an entire and possibly invalid SMB packet.
342 * Be careful while parsing it.
345 hp = mtod(m, u_char*);
346 if (bcmp(hp, SMB_SIGNATURE, SMB_SIGLEN) != 0) {
350 mid = SMB_HDRMID(hp);
351 SMBSDEBUG("mid %04x\n", (u_int)mid);
353 TAILQ_FOREACH(rqp, &iod->iod_rqlist, sr_link) {
354 if (rqp->sr_mid != mid)
357 if (rqp->sr_rp.md_top == NULL) {
358 md_initm(&rqp->sr_rp, m);
360 if (rqp->sr_flags & SMBR_MULTIPACKET) {
361 md_append_record(&rqp->sr_rp, m);
364 SMBERROR("duplicate response %d (ignored)\n", mid);
369 smb_iod_rqprocessed(rqp, 0);
372 SMB_IOD_RQUNLOCK(iod);
374 SMBERROR("drop resp with mid %d\n", (u_int)mid);
375 /* smb_printrqlist(vcp);*/
380 * check for interrupts
383 TAILQ_FOREACH(rqp, &iod->iod_rqlist, sr_link) {
384 if (smb_proc_intr(rqp->sr_cred->scr_td)) {
385 smb_iod_rqprocessed(rqp, EINTR);
388 SMB_IOD_RQUNLOCK(iod);
393 smb_iod_request(struct smbiod *iod, int event, void *ident)
395 struct smbiod_event *evp;
399 evp = smb_zmalloc(sizeof(*evp), M_SMBIOD, M_WAITOK);
400 evp->ev_type = event;
401 evp->ev_ident = ident;
403 STAILQ_INSERT_TAIL(&iod->iod_evlist, evp, ev_link);
404 if ((event & SMBIOD_EV_SYNC) == 0) {
405 SMB_IOD_EVUNLOCK(iod);
410 smb_sleep(evp, SMB_IOD_EVINTERLOCK(iod), PDROP, "90evw", 0);
411 error = evp->ev_error;
412 kfree(evp, M_SMBIOD);
417 * Place request in the queue.
418 * Request from smbiod have a high priority.
421 smb_iod_addrq(struct smb_rq *rqp)
423 struct smb_vc *vcp = rqp->sr_vc;
424 struct smbiod *iod = vcp->vc_iod;
428 if (rqp->sr_cred->scr_td == iod->iod_td) {
429 rqp->sr_flags |= SMBR_INTERNAL;
431 TAILQ_INSERT_HEAD(&iod->iod_rqlist, rqp, sr_link);
432 SMB_IOD_RQUNLOCK(iod);
434 if (smb_iod_sendrq(iod, rqp) != 0) {
439 * we don't need to lock state field here
441 if (rqp->sr_state != SMBRQ_NOTSENT)
443 tsleep(&iod->iod_flags, 0, "90sndw", hz);
446 smb_iod_removerq(rqp);
447 return rqp->sr_lerror;
450 switch (iod->iod_state) {
451 case SMBIOD_ST_NOTCONN:
454 error = smb_iod_request(vcp->vc_iod, SMBIOD_EV_CONNECT | SMBIOD_EV_SYNC, NULL);
464 if (vcp->vc_maxmux == 0) {
465 SMBERROR("maxmux == 0\n");
468 if (iod->iod_muxcnt < vcp->vc_maxmux)
471 smb_sleep(&iod->iod_muxwant, SMB_IOD_RQINTERLOCK(iod), 0, "90mux", 0);
474 TAILQ_INSERT_TAIL(&iod->iod_rqlist, rqp, sr_link);
475 SMB_IOD_RQUNLOCK(iod);
481 smb_iod_removerq(struct smb_rq *rqp)
483 struct smb_vc *vcp = rqp->sr_vc;
484 struct smbiod *iod = vcp->vc_iod;
487 if (rqp->sr_flags & SMBR_INTERNAL) {
489 TAILQ_REMOVE(&iod->iod_rqlist, rqp, sr_link);
490 SMB_IOD_RQUNLOCK(iod);
494 while (rqp->sr_flags & SMBR_XLOCK) {
495 rqp->sr_flags |= SMBR_XLOCKWANT;
496 smb_sleep(rqp, SMB_IOD_RQINTERLOCK(iod), 0, "90xrm", 0);
498 TAILQ_REMOVE(&iod->iod_rqlist, rqp, sr_link);
500 if (iod->iod_muxwant) {
502 wakeup(&iod->iod_muxwant);
504 SMB_IOD_RQUNLOCK(iod);
509 smb_iod_waitrq(struct smb_rq *rqp)
511 struct smbiod *iod = rqp->sr_vc->vc_iod;
515 if (rqp->sr_flags & SMBR_INTERNAL) {
517 smb_iod_sendall(iod);
518 smb_iod_recvall(iod);
519 if (rqp->sr_rpgen != rqp->sr_rplast)
521 tsleep(&iod->iod_flags, 0, "90irq", hz);
523 smb_iod_removerq(rqp);
524 return rqp->sr_lerror;
528 if (rqp->sr_rpgen == rqp->sr_rplast)
529 smb_sleep(&rqp->sr_state, SMBRQ_INTERLOCK(rqp), 0, "90wrq", 0);
532 error = rqp->sr_lerror;
533 if (rqp->sr_flags & SMBR_MULTIPACKET) {
535 * If request should stay in the list, then reinsert it
536 * at the end of queue so other waiters have chance to concur
539 TAILQ_REMOVE(&iod->iod_rqlist, rqp, sr_link);
540 TAILQ_INSERT_TAIL(&iod->iod_rqlist, rqp, sr_link);
541 SMB_IOD_RQUNLOCK(iod);
543 smb_iod_removerq(rqp);
549 smb_iod_sendall(struct smbiod *iod)
551 struct smb_vc *vcp = iod->iod_vc;
553 struct timespec ts, tstimeout;
558 * Loop through the list of requests and send them if possible
561 TAILQ_FOREACH(rqp, &iod->iod_rqlist, sr_link) {
562 switch (rqp->sr_state) {
564 rqp->sr_flags |= SMBR_XLOCK;
565 SMB_IOD_RQUNLOCK(iod);
566 herror = smb_iod_sendrq(iod, rqp);
568 rqp->sr_flags &= ~SMBR_XLOCK;
569 if (rqp->sr_flags & SMBR_XLOCKWANT) {
570 rqp->sr_flags &= ~SMBR_XLOCKWANT;
575 SMB_TRAN_GETPARAM(vcp, SMBTP_TIMEOUT, &tstimeout);
576 timespecadd(&tstimeout, &tstimeout);
578 timespecsub(&ts, &tstimeout);
579 if (timespeccmp(&ts, &rqp->sr_timesent, >)) {
580 smb_iod_rqprocessed(rqp, ETIMEDOUT);
589 SMB_IOD_RQUNLOCK(iod);
590 if (herror == ENOTCONN)
596 * "main" function for smbiod daemon
599 smb_iod_main(struct smbiod *iod)
601 /* struct smb_vc *vcp = iod->iod_vc;*/
602 struct smbiod_event *evp;
603 /* struct timespec tsnow;*/
610 * Check all interesting events
614 evp = STAILQ_FIRST(&iod->iod_evlist);
616 SMB_IOD_EVUNLOCK(iod);
619 STAILQ_REMOVE_HEAD(&iod->iod_evlist, ev_link);
620 evp->ev_type |= SMBIOD_EV_PROCESSING;
621 SMB_IOD_EVUNLOCK(iod);
622 switch (evp->ev_type & SMBIOD_EV_MASK) {
623 case SMBIOD_EV_CONNECT:
624 iod->iod_state = SMBIOD_ST_RECONNECT;
625 evp->ev_error = smb_iod_connect(iod);
627 case SMBIOD_EV_DISCONNECT:
628 evp->ev_error = smb_iod_disconnect(iod);
630 case SMBIOD_EV_TREECONNECT:
631 evp->ev_error = smb_iod_treeconnect(iod, evp->ev_ident);
633 case SMBIOD_EV_SHUTDOWN:
634 iod->iod_flags |= SMBIOD_SHUTDOWN;
636 case SMBIOD_EV_NEWRQ:
639 if (evp->ev_type & SMBIOD_EV_SYNC) {
642 SMB_IOD_EVUNLOCK(iod);
644 kfree(evp, M_SMBIOD);
647 if (iod->iod_state == SMBIOD_ST_VCACTIVE) {
649 timespecsub(&tsnow, &iod->iod_pingtimo);
650 if (timespeccmp(&tsnow, &iod->iod_lastrqsent, >)) {
651 smb_smb_echo(vcp, &iod->iod_scred);
655 smb_iod_sendall(iod);
656 smb_iod_recvall(iod);
660 #define kthread_create_compat smb_kthread_create
661 #define kthread_exit_compat smb_kthread_exit
664 smb_iod_thread(void *arg)
666 struct smbiod *iod = arg;
669 * mplock not held on entry but we aren't mpsafe yet.
673 smb_makescred(&iod->iod_scred, iod->iod_td, NULL);
674 while ((iod->iod_flags & SMBIOD_SHUTDOWN) == 0) {
676 SMBIODEBUG("going to sleep for %d ticks\n", iod->iod_sleeptimo);
677 if (iod->iod_flags & SMBIOD_SHUTDOWN)
679 tsleep(&iod->iod_flags, 0, "90idle", iod->iod_sleeptimo);
681 kthread_exit_compat();
685 smb_iod_create(struct smb_vc *vcp)
688 struct proc *newp = NULL;
691 iod = smb_zmalloc(sizeof(*iod), M_SMBIOD, M_WAITOK);
692 iod->iod_id = smb_iod_next++;
693 iod->iod_state = SMBIOD_ST_NOTCONN;
695 iod->iod_sleeptimo = hz * SMBIOD_SLEEP_TIMO;
696 iod->iod_pingtimo.tv_sec = SMBIOD_PING_TIMO;
697 getnanotime(&iod->iod_lastrqsent);
699 smb_sl_init(&iod->iod_rqlock, "90rql");
700 TAILQ_INIT(&iod->iod_rqlist);
701 smb_sl_init(&iod->iod_evlock, "90evl");
702 STAILQ_INIT(&iod->iod_evlist);
703 error = kthread_create_compat(smb_iod_thread, iod, &newp,
704 RFNOWAIT, "smbiod%d", iod->iod_id);
706 SMBERROR("can't start smbiod: %d", error);
707 kfree(iod, M_SMBIOD);
711 iod->iod_td = ONLY_LWP_IN_PROC(newp)->lwp_thread;
716 smb_iod_destroy(struct smbiod *iod)
718 smb_iod_request(iod, SMBIOD_EV_SHUTDOWN | SMBIOD_EV_SYNC, NULL);
719 smb_sl_destroy(&iod->iod_rqlock);
720 smb_sl_destroy(&iod->iod_evlock);
721 kfree(iod, M_SMBIOD);