diff options
author | rwatson <rwatson@FreeBSD.org> | 2004-07-12 18:39:59 +0000 |
---|---|---|
committer | rwatson <rwatson@FreeBSD.org> | 2004-07-12 18:39:59 +0000 |
commit | 9183ed533a6a59e2febbded3d383cd619c86b214 (patch) | |
tree | 9b431220526cb46ffec785cb7faee8827c5c49fc /sys/netatalk | |
parent | d0f3949724322ee8bfcd3f3026b6911ef764e498 (diff) | |
download | FreeBSD-src-9183ed533a6a59e2febbded3d383cd619c86b214.zip FreeBSD-src-9183ed533a6a59e2febbded3d383cd619c86b214.tar.gz |
Procotol control block locking for netatalk DDP.
Diffstat (limited to 'sys/netatalk')
-rw-r--r-- | sys/netatalk/ddp_input.c | 18 | ||||
-rw-r--r-- | sys/netatalk/ddp_pcb.c | 50 | ||||
-rw-r--r-- | sys/netatalk/ddp_pcb.h | 20 | ||||
-rw-r--r-- | sys/netatalk/ddp_usrreq.c | 79 | ||||
-rw-r--r-- | sys/netatalk/ddp_var.h | 2 |
5 files changed, 134 insertions, 35 deletions
diff --git a/sys/netatalk/ddp_input.c b/sys/netatalk/ddp_input.c index 679f943..58d4ad8 100644 --- a/sys/netatalk/ddp_input.c +++ b/sys/netatalk/ddp_input.c @@ -1,4 +1,5 @@ /* + * Copyright (c) 2004 Robert N. M. Watson * Copyright (c) 1990,1994 Regents of The University of Michigan. * All Rights Reserved. See COPYRIGHT. * @@ -24,11 +25,13 @@ #include <netatalk/at_var.h> #include <netatalk/ddp.h> #include <netatalk/ddp_var.h> +#include <netatalk/ddp_pcb.h> #include <netatalk/at_extern.h> static volatile int ddp_forward = 1; static volatile int ddp_firewall = 0; static struct ddpstat ddpstat; + static struct route forwro; static void ddp_input(struct mbuf *, struct ifnet *, struct elaphdr *, int); @@ -360,17 +363,16 @@ ddp_input(m, ifp, elh, phase) * Search for ddp protocol control blocks that match these * addresses. */ + DDP_LIST_SLOCK(); if ((ddp = ddp_search(&from, &to, aa)) == NULL) { - m_freem(m); - return; + goto out; } #ifdef MAC SOCK_LOCK(ddp->ddp_socket); if (mac_check_socket_deliver(ddp->ddp_socket, m) != 0) { SOCK_UNLOCK(ddp->ddp_socket); - m_freem(m); - return; + goto out; } SOCK_UNLOCK(ddp->ddp_socket); #endif @@ -384,13 +386,17 @@ ddp_input(m, ifp, elh, phase) * If the socket is full (or similar error) dump the packet. */ ddpstat.ddps_nosockspace++; - m_freem(m); - return; + goto out; } /* * And wake up whatever might be waiting for it */ sorwakeup(ddp->ddp_socket); + m = NULL; +out: + DDP_LIST_SUNLOCK(); + if (m != NULL) + m_freem(m); } #if 0 diff --git a/sys/netatalk/ddp_pcb.c b/sys/netatalk/ddp_pcb.c index e6dc5fb..8073d55 100644 --- a/sys/netatalk/ddp_pcb.c +++ b/sys/netatalk/ddp_pcb.c @@ -1,4 +1,5 @@ /* + * Copyright (c) 2004 Robert N. M. Watson * Copyright (c) 1990,1994 Regents of The University of Michigan. * All Rights Reserved. See COPYRIGHT. * @@ -22,12 +23,18 @@ #include <netatalk/ddp_pcb.h> #include <netatalk/at_extern.h> +struct mtx ddp_list_mtx; static struct ddpcb *ddp_ports[ ATPORT_LAST ]; -struct ddpcb *ddpcb_list = NULL; +struct ddpcb *ddpcb_list = NULL; void at_sockaddr(struct ddpcb *ddp, struct sockaddr **addr) { + + /* + * Prevent modification of ddp during copy of addr. + */ + DDP_LOCK_ASSERT(ddp); *addr = sodupsockaddr((struct sockaddr *)&ddp->ddp_lsat, M_NOWAIT); } @@ -38,6 +45,12 @@ at_pcbsetaddr(struct ddpcb *ddp, struct sockaddr *addr, struct thread *td) struct at_ifaddr *aa; struct ddpcb *ddpp; + /* + * We read and write both the ddp passed in, and also ddp_ports. + */ + DDP_LIST_XLOCK_ASSERT(); + DDP_LOCK_ASSERT(ddp); + if (ddp->ddp_lsat.sat_port != ATADDR_ANYPORT) { /* shouldn't be bound */ return (EINVAL); } @@ -134,6 +147,9 @@ at_pcbconnect(struct ddpcb *ddp, struct sockaddr *addr, struct thread *td) struct ifnet *ifp; u_short hintnet = 0, net; + DDP_LIST_XLOCK_ASSERT(); + DDP_LOCK_ASSERT(ddp); + if (sat->sat_family != AF_APPLETALK) { return (EAFNOSUPPORT); } @@ -222,6 +238,9 @@ at_pcbconnect(struct ddpcb *ddp, struct sockaddr *addr, struct thread *td) void at_pcbdisconnect(struct ddpcb *ddp) { + + DDP_LOCK_ASSERT(ddp); + ddp->ddp_fsat.sat_addr.s_net = ATADDR_ANYNET; ddp->ddp_fsat.sat_addr.s_node = ATADDR_ANYNODE; ddp->ddp_fsat.sat_port = ATADDR_ANYPORT; @@ -232,9 +251,15 @@ at_pcballoc(struct socket *so) { struct ddpcb *ddp; - MALLOC(ddp, struct ddpcb *, sizeof *ddp, M_PCB, M_WAITOK | M_ZERO); + DDP_LIST_XLOCK_ASSERT(); + + MALLOC(ddp, struct ddpcb *, sizeof *ddp, M_PCB, M_NOWAIT | M_ZERO); + DDP_LOCK_INIT(ddp); ddp->ddp_lsat.sat_port = ATADDR_ANYPORT; + ddp->ddp_socket = so; + so->so_pcb = (caddr_t)ddp; + ddp->ddp_next = ddpcb_list; ddp->ddp_prev = NULL; ddp->ddp_pprev = NULL; @@ -243,15 +268,19 @@ at_pcballoc(struct socket *so) ddpcb_list->ddp_prev = ddp; } ddpcb_list = ddp; - - ddp->ddp_socket = so; - so->so_pcb = (caddr_t)ddp; - return (0); + return(0); } void at_pcbdetach(struct socket *so, struct ddpcb *ddp) { + + /* + * We modify ddp, ddp_ports, and the global list. + */ + DDP_LIST_XLOCK_ASSERT(); + DDP_LOCK_ASSERT(ddp); + soisdisconnected(so); SOCK_LOCK(so); so->so_pcb = NULL; @@ -282,6 +311,8 @@ at_pcbdetach(struct socket *so, struct ddpcb *ddp) if (ddp->ddp_next) { ddp->ddp_next->ddp_prev = ddp->ddp_prev; } + DDP_UNLOCK(ddp); + DDP_LOCK_DESTROY(ddp); FREE(ddp, M_PCB); } @@ -297,6 +328,8 @@ ddp_search(struct sockaddr_at *from, struct sockaddr_at *to, { struct ddpcb *ddp; + DDP_LIST_SLOCK_ASSERT(); + /* * Check for bad ports. */ @@ -309,11 +342,13 @@ ddp_search(struct sockaddr_at *from, struct sockaddr_at *to, * the interface? */ for (ddp = ddp_ports[ to->sat_port - 1 ]; ddp; ddp = ddp->ddp_pnext) { + DDP_LOCK(ddp); /* XXX should we handle 0.YY? */ /* XXXX.YY to socket on destination interface */ if (to->sat_addr.s_net == ddp->ddp_lsat.sat_addr.s_net && to->sat_addr.s_node == ddp->ddp_lsat.sat_addr.s_node) { + DDP_UNLOCK(ddp); break; } @@ -321,6 +356,7 @@ ddp_search(struct sockaddr_at *from, struct sockaddr_at *to, if (to->sat_addr.s_node == ATADDR_BCAST && (to->sat_addr.s_net == 0 || to->sat_addr.s_net == ddp->ddp_lsat.sat_addr.s_net) && ddp->ddp_lsat.sat_addr.s_net == AA_SAT(aa)->sat_addr.s_net) { + DDP_UNLOCK(ddp); break; } @@ -331,8 +367,10 @@ ddp_search(struct sockaddr_at *from, struct sockaddr_at *to, ntohs(aa->aa_firstnet) && ntohs(ddp->ddp_lsat.sat_addr.s_net) <= ntohs(aa->aa_lastnet)) { + DDP_UNLOCK(ddp); break; } + DDP_UNLOCK(ddp); } return (ddp); } diff --git a/sys/netatalk/ddp_pcb.h b/sys/netatalk/ddp_pcb.h index 819d674..85ec0cc 100644 --- a/sys/netatalk/ddp_pcb.h +++ b/sys/netatalk/ddp_pcb.h @@ -1,4 +1,5 @@ /* + * Copyright (c) 2004 Robert N. M. Watson * Copyright (c) 1990,1994 Regents of The University of Michigan. * All Rights Reserved. See COPYRIGHT. * @@ -17,4 +18,23 @@ int at_pcbsetaddr(struct ddpcb *ddp, struct sockaddr *addr, struct thread *td); void at_sockaddr(struct ddpcb *ddp, struct sockaddr **addr); +/* Lock macros for per-pcb locks. */ +#define DDP_LOCK_INIT(ddp) mtx_init(&(ddp)->ddp_mtx, "ddp_mtx", \ + NULL, MTX_DEF) +#define DDP_LOCK_DESTROY(ddp) mtx_destroy(&(ddp)->ddp_mtx) +#define DDP_LOCK(ddp) mtx_lock(&(ddp)->ddp_mtx) +#define DDP_UNLOCK(ddp) mtx_unlock(&(ddp)->ddp_mtx) +#define DDP_LOCK_ASSERT(ddp) mtx_assert(&(ddp)->ddp_mtx, MA_OWNED) + +/* Lock macros for global pcb list lock. */ +#define DDP_LIST_LOCK_INIT() mtx_init(&ddp_list_mtx, "ddp_list_mtx", \ + NULL, MTX_DEF) +#define DDP_LIST_LOCK_DESTROY() mtx_destroy(&ddp_list_mtx) +#define DDP_LIST_XLOCK() mtx_lock(&ddp_list_mtx) +#define DDP_LIST_XUNLOCK() mtx_unlock(&ddp_list_mtx) +#define DDP_LIST_XLOCK_ASSERT() mtx_assert(&ddp_list_mtx, MA_OWNED) +#define DDP_LIST_SLOCK() mtx_lock(&ddp_list_mtx) +#define DDP_LIST_SUNLOCK() mtx_unlock(&ddp_list_mtx) +#define DDP_LIST_SLOCK_ASSERT() mtx_assert(&ddp_list_mtx, MA_OWNED) + #endif diff --git a/sys/netatalk/ddp_usrreq.c b/sys/netatalk/ddp_usrreq.c index e33da4a..1621f1f 100644 --- a/sys/netatalk/ddp_usrreq.c +++ b/sys/netatalk/ddp_usrreq.c @@ -1,4 +1,5 @@ /* + * Copyright (c) 2004 Robert N. M. Watson * Copyright (c) 1990,1994 Regents of The University of Michigan. * All Rights Reserved. See COPYRIGHT. * @@ -33,17 +34,22 @@ ddp_attach(struct socket *so, int proto, struct thread *td) struct ddpcb *ddp; int error = 0; - ddp = sotoddpcb(so); - if (ddp != NULL) { - return (EINVAL); - } + if (ddp != NULL) + return (EINVAL); + /* + * Allocate socket buffer space first so that it's present + * before first use. + */ + error = soreserve(so, ddp_sendspace, ddp_recvspace); + if (error) + return (error); + + DDP_LIST_XLOCK(); error = at_pcballoc(so); - if (error) { - return (error); - } - return (soreserve(so, ddp_sendspace, ddp_recvspace)); + DDP_LIST_XUNLOCK(); + return (error); } static int @@ -52,10 +58,13 @@ ddp_detach(struct socket *so) struct ddpcb *ddp; ddp = sotoddpcb(so); - if (ddp == NULL) { + if (ddp == NULL) return (EINVAL); - } + + DDP_LIST_XLOCK(); + DDP_LOCK(ddp); at_pcbdetach(so, ddp); + DDP_LIST_XUNLOCK(); return (0); } @@ -69,7 +78,11 @@ ddp_bind(struct socket *so, struct sockaddr *nam, struct thread *td) if (ddp == NULL) { return (EINVAL); } + DDP_LIST_XLOCK(); + DDP_LOCK(ddp); error = at_pcbsetaddr(ddp, nam, td); + DDP_UNLOCK(ddp); + DDP_LIST_XUNLOCK(); return (error); } @@ -84,11 +97,17 @@ ddp_connect(struct socket *so, struct sockaddr *nam, struct thread *td) return (EINVAL); } + DDP_LIST_XLOCK(); + DDP_LOCK(ddp); if (ddp->ddp_fsat.sat_port != ATADDR_ANYPORT) { + DDP_UNLOCK(ddp); + DDP_LIST_XUNLOCK(); return (EISCONN); } - error = at_pcbconnect(ddp, nam, td); + error = at_pcbconnect( ddp, nam, td ); + DDP_UNLOCK(ddp); + DDP_LIST_XUNLOCK(); if (error == 0) soisconnected(so); return (error); @@ -104,12 +123,15 @@ ddp_disconnect(struct socket *so) if (ddp == NULL) { return (EINVAL); } + DDP_LOCK(ddp); if (ddp->ddp_fsat.sat_addr.s_node == ATADDR_ANYNODE) { + DDP_UNLOCK(ddp); return (ENOTCONN); } at_pcbdisconnect(ddp); ddp->ddp_fsat.sat_addr.s_node = ATADDR_ANYNODE; + DDP_UNLOCK(ddp); soisdisconnected(so); return (0); } @@ -144,23 +166,28 @@ ddp_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr, } if (addr != NULL) { + DDP_LIST_XLOCK(); + DDP_LOCK(ddp); if (ddp->ddp_fsat.sat_port != ATADDR_ANYPORT) { - return (EISCONN); + error = EISCONN; + goto out; } error = at_pcbconnect(ddp, addr, td); - if (error) { - return (error); + if (error == 0) { + error = ddp_output(m, so); + at_pcbdisconnect(ddp); } +out: + DDP_UNLOCK(ddp); + DDP_LIST_XUNLOCK(); } else { - if (ddp->ddp_fsat.sat_port == ATADDR_ANYPORT) { - return (ENOTCONN); - } - } - - error = ddp_output(m, so); - if (addr != NULL) { - at_pcbdisconnect(ddp); + DDP_LOCK(ddp); + if (ddp->ddp_fsat.sat_port == ATADDR_ANYPORT) + error = ENOTCONN; + else + error = ddp_output(m, so); + DDP_UNLOCK(ddp); } return (error); } @@ -174,20 +201,23 @@ ddp_abort(struct socket *so) if (ddp == NULL) { return (EINVAL); } + DDP_LIST_XLOCK(); + DDP_LOCK(ddp); at_pcbdetach(so, ddp); + DDP_LIST_XUNLOCK(); return (0); } void ddp_init(void) { - atintrq1.ifq_maxlen = IFQ_MAXLEN; atintrq2.ifq_maxlen = IFQ_MAXLEN; aarpintrq.ifq_maxlen = IFQ_MAXLEN; mtx_init(&atintrq1.ifq_mtx, "at1_inq", NULL, MTX_DEF); mtx_init(&atintrq2.ifq_mtx, "at2_inq", NULL, MTX_DEF); mtx_init(&aarpintrq.ifq_mtx, "aarp_inq", NULL, MTX_DEF); + DDP_LIST_LOCK_INIT(); netisr_register(NETISR_ATALK1, at1intr, &atintrq1, 0); netisr_register(NETISR_ATALK2, at2intr, &atintrq2, 0); netisr_register(NETISR_AARP, aarpintr, &aarpintrq, 0); @@ -202,6 +232,7 @@ ddp_clean(void) for (ddp = ddpcb_list; ddp != NULL; ddp = ddp->ddp_next) { at_pcbdetach(ddp->ddp_socket, ddp); } + DDP_LIST_LOCK_DESTROY(); } #endif @@ -220,7 +251,9 @@ at_setsockaddr(struct socket *so, struct sockaddr **nam) if (ddp == NULL) { return (EINVAL); } + DDP_LOCK(ddp); at_sockaddr(ddp, nam); + DDP_UNLOCK(ddp); return (0); } diff --git a/sys/netatalk/ddp_var.h b/sys/netatalk/ddp_var.h index 1e74381..6fb3aed 100644 --- a/sys/netatalk/ddp_var.h +++ b/sys/netatalk/ddp_var.h @@ -13,6 +13,7 @@ struct ddpcb { struct socket *ddp_socket; struct ddpcb *ddp_prev, *ddp_next; struct ddpcb *ddp_pprev, *ddp_pnext; + struct mtx ddp_mtx; }; #define sotoddpcb(so) ((struct ddpcb *)(so)->so_pcb) @@ -34,5 +35,6 @@ struct ddpstat { extern int ddp_cksum; extern struct ddpcb *ddpcb_list; extern struct pr_usrreqs ddp_usrreqs; +extern struct mtx ddp_list_mtx; #endif #endif /* _NETATALK_DDP_VAR_H_ */ |