summaryrefslogtreecommitdiffstats
path: root/sys/netinet6/udp6_usrreq.c
diff options
context:
space:
mode:
authorrwatson <rwatson@FreeBSD.org>2004-07-27 23:44:03 +0000
committerrwatson <rwatson@FreeBSD.org>2004-07-27 23:44:03 +0000
commit558dee61de1ee0a98a05f904cbbdd76b04daef79 (patch)
treeda198faf6490ff3d454a432a6e7e16fea898717e /sys/netinet6/udp6_usrreq.c
parent3675ec98e8fbe871084ec3871ca2f42ca8acac1a (diff)
downloadFreeBSD-src-558dee61de1ee0a98a05f904cbbdd76b04daef79.zip
FreeBSD-src-558dee61de1ee0a98a05f904cbbdd76b04daef79.tar.gz
Commit a first pass at in6pcb and pcbinfo locking for IPv6,
synchronizing IPv6 protocol control blocks and lists. These changes are modeled on the inpcb locking for IPv4, submitted by Jennifer Yang, and committed by Jeffrey Hsu. With these locking changes, IPv6 use of inpcbs is now substantially more MPSAFE, and permits IPv4 inpcb locking assertions to be run in the presence of IPv6 compiled into the kernel.
Diffstat (limited to 'sys/netinet6/udp6_usrreq.c')
-rw-r--r--sys/netinet6/udp6_usrreq.c96
1 files changed, 74 insertions, 22 deletions
diff --git a/sys/netinet6/udp6_usrreq.c b/sys/netinet6/udp6_usrreq.c
index 23842e5..c248ce2 100644
--- a/sys/netinet6/udp6_usrreq.c
+++ b/sys/netinet6/udp6_usrreq.c
@@ -514,21 +514,30 @@ udp6_attach(struct socket *so, int proto, struct thread *td)
struct inpcb *inp;
int s, error;
+ INP_INFO_WLOCK(&udbinfo);
inp = sotoinpcb(so);
- if (inp != 0)
+ if (inp != 0) {
+ INP_INFO_WUNLOCK(&udbinfo);
return EINVAL;
+ }
if (so->so_snd.sb_hiwat == 0 || so->so_rcv.sb_hiwat == 0) {
error = soreserve(so, udp_sendspace, udp_recvspace);
- if (error)
+ if (error) {
+ INP_INFO_WUNLOCK(&udbinfo);
return error;
+ }
}
s = splnet();
error = in_pcballoc(so, &udbinfo, "udp6inp");
splx(s);
- if (error)
+ if (error) {
+ INP_INFO_WUNLOCK(&udbinfo);
return error;
+ }
inp = (struct inpcb *)so->so_pcb;
+ INP_LOCK(inp);
+ INP_INFO_WUNLOCK(&udbinfo);
inp->inp_vflag |= INP_IPV6;
if (!ip6_v6only)
inp->inp_vflag |= INP_IPV4;
@@ -541,6 +550,7 @@ udp6_attach(struct socket *so, int proto, struct thread *td)
* which may match an IPv4-mapped IPv6 address.
*/
inp->inp_ip_ttl = ip_defttl;
+ INP_UNLOCK(inp);
return 0;
}
@@ -550,9 +560,13 @@ udp6_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
struct inpcb *inp;
int s, error;
+ INP_INFO_WLOCK(&udbinfo);
inp = sotoinpcb(so);
- if (inp == 0)
+ if (inp == 0) {
+ INP_INFO_WUNLOCK(&udbinfo);
return EINVAL;
+ }
+ INP_LOCK(inp);
inp->inp_vflag &= ~INP_IPV4;
inp->inp_vflag |= INP_IPV6;
@@ -572,13 +586,15 @@ udp6_bind(struct socket *so, struct sockaddr *nam, struct thread *td)
s = splnet();
error = in_pcbbind(inp, (struct sockaddr *)&sin,
td->td_ucred);
- splx(s);
- return error;
+ goto out;
}
}
s = splnet();
error = in6_pcbbind(inp, nam, td->td_ucred);
+out:
+ INP_UNLOCK(inp);
+ INP_INFO_WUNLOCK(&udbinfo);
splx(s);
return error;
}
@@ -589,9 +605,13 @@ udp6_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
struct inpcb *inp;
int s, error;
+ INP_INFO_WLOCK(&udbinfo);
inp = sotoinpcb(so);
- if (inp == 0)
+ if (inp == 0) {
+ INP_INFO_WUNLOCK(&udbinfo);
return EINVAL;
+ }
+ INP_LOCK(inp);
if ((inp->inp_flags & IN6P_IPV6_V6ONLY) == 0) {
struct sockaddr_in6 *sin6_p;
@@ -612,11 +632,13 @@ udp6_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
inp->inp_vflag &= ~INP_IPV6;
soisconnected(so);
}
- return error;
+ goto out;
}
}
- if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr))
- return EISCONN;
+ if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) {
+ error = EISCONN;
+ goto out;
+ }
s = splnet();
error = in6_pcbconnect(inp, nam, td->td_ucred);
splx(s);
@@ -627,6 +649,9 @@ udp6_connect(struct socket *so, struct sockaddr *nam, struct thread *td)
}
soisconnected(so);
}
+out:
+ INP_UNLOCK(inp);
+ INP_INFO_WUNLOCK(&udbinfo);
return error;
}
@@ -636,12 +661,17 @@ udp6_detach(struct socket *so)
struct inpcb *inp;
int s;
+ INP_INFO_WLOCK(&udbinfo);
inp = sotoinpcb(so);
- if (inp == 0)
+ if (inp == 0) {
+ INP_INFO_WUNLOCK(&udbinfo);
return EINVAL;
+ }
+ INP_LOCK(inp);
s = splnet();
in6_pcbdetach(inp);
splx(s);
+ INP_INFO_WUNLOCK(&udbinfo);
return 0;
}
@@ -649,29 +679,40 @@ static int
udp6_disconnect(struct socket *so)
{
struct inpcb *inp;
- int s;
+ int error, s;
+ INP_INFO_WLOCK(&udbinfo);
inp = sotoinpcb(so);
- if (inp == 0)
+ if (inp == 0) {
+ INP_INFO_WUNLOCK(&udbinfo);
return EINVAL;
+ }
+ INP_LOCK(inp);
#ifdef INET
if (inp->inp_vflag & INP_IPV4) {
struct pr_usrreqs *pru;
pru = inetsw[ip_protox[IPPROTO_UDP]].pr_usrreqs;
- return ((*pru->pru_disconnect)(so));
+ error = (*pru->pru_disconnect)(so);
+ goto out;
}
#endif
- if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr))
- return ENOTCONN;
+ if (IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_faddr)) {
+ error = ENOTCONN;
+ goto out;
+ }
s = splnet();
in6_pcbdisconnect(inp);
inp->in6p_laddr = in6addr_any;
splx(s);
+ /* XXXRW: so_state locking? */
so->so_state &= ~SS_ISCONNECTED; /* XXX */
+out:
+ INP_UNLOCK(inp);
+ INP_INFO_WUNLOCK(&udbinfo);
return 0;
}
@@ -682,11 +723,14 @@ udp6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
struct inpcb *inp;
int error = 0;
+ INP_INFO_WLOCK(&udbinfo);
inp = sotoinpcb(so);
if (inp == 0) {
- error = EINVAL;
- goto bad;
+ INP_INFO_WUNLOCK(&udbinfo);
+ m_freem(m);
+ return EINVAL;
}
+ INP_LOCK(inp);
if (addr) {
if (addr->sa_len != sizeof(struct sockaddr_in6)) {
@@ -720,7 +764,8 @@ udp6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
* IPV6_V6ONLY flag, we discard this
* datagram destined to a v4 addr.
*/
- return EINVAL;
+ error = EINVAL;
+ goto out;
}
if (!IN6_IS_ADDR_UNSPECIFIED(&inp->in6p_laddr) &&
!IN6_IS_ADDR_V4MAPPED(&inp->in6p_laddr)) {
@@ -731,7 +776,8 @@ udp6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
* determine how to map IPv6 source
* address to IPv4.
*/
- return EINVAL;
+ error = EINVAL;
+ goto out;
}
if (sin6)
in6_sin6_2_sin_in_sock(addr);
@@ -739,14 +785,20 @@ udp6_send(struct socket *so, int flags, struct mbuf *m, struct sockaddr *addr,
error = ((*pru->pru_send)(so, flags, m, addr, control,
td));
/* addr will just be freed in sendit(). */
- return error;
+ goto out;
}
}
#endif
- return udp6_output(inp, m, addr, control, td);
+ error = udp6_output(inp, m, addr, control, td);
+out:
+ INP_UNLOCK(inp);
+ INP_INFO_WUNLOCK(&udbinfo);
+ return error;
bad:
+ INP_UNLOCK(inp);
+ INP_INFO_WUNLOCK(&udbinfo);
m_freem(m);
return (error);
}
OpenPOWER on IntegriCloud