[SCTP]: Convert bind_addr_list locking to RCU

Since the sctp_sockaddr_entry is now RCU enabled as part of
the patch to synchronize sctp_localaddr_list, it makes sense to
change all handling of these entries to RCU.  This includes the
sctp_bind_addrs structure and it's list of bound addresses.

This list is currently protected by an external rw_lock and that
looks like an overkill.  There are only 2 writers to the list:
bind()/bindx() calls, and BH processing of ASCONF-ACK chunks.
These are already seriealized via the socket lock, so they will
not step on each other.  These are also relatively rare, so we
should be good with RCU.

The readers are varied and they are easily converted to RCU.

Signed-off-by: Vlad Yasevich <vladislav.yasevich@hp.com>
Acked-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Acked-by: Sridhar Samdurala <sri@us.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
Vlad Yasevich 2007-09-16 16:03:28 -07:00 committed by David S. Miller
parent 2930354799
commit 559cf710b0
8 changed files with 105 additions and 162 deletions

View file

@ -1155,7 +1155,9 @@ int sctp_bind_addr_copy(struct sctp_bind_addr *dest,
int flags); int flags);
int sctp_add_bind_addr(struct sctp_bind_addr *, union sctp_addr *, int sctp_add_bind_addr(struct sctp_bind_addr *, union sctp_addr *,
__u8 use_as_src, gfp_t gfp); __u8 use_as_src, gfp_t gfp);
int sctp_del_bind_addr(struct sctp_bind_addr *, union sctp_addr *); int sctp_del_bind_addr(struct sctp_bind_addr *, union sctp_addr *,
void (*rcu_call)(struct rcu_head *,
void (*func)(struct rcu_head *)));
int sctp_bind_addr_match(struct sctp_bind_addr *, const union sctp_addr *, int sctp_bind_addr_match(struct sctp_bind_addr *, const union sctp_addr *,
struct sctp_sock *); struct sctp_sock *);
union sctp_addr *sctp_find_unmatch_addr(struct sctp_bind_addr *bp, union sctp_addr *sctp_find_unmatch_addr(struct sctp_bind_addr *bp,
@ -1226,9 +1228,6 @@ struct sctp_ep_common {
* bind_addr.address_list is our set of local IP addresses. * bind_addr.address_list is our set of local IP addresses.
*/ */
struct sctp_bind_addr bind_addr; struct sctp_bind_addr bind_addr;
/* Protection during address list comparisons. */
rwlock_t addr_lock;
}; };

View file

@ -99,7 +99,6 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
/* Initialize the bind addr area. */ /* Initialize the bind addr area. */
sctp_bind_addr_init(&asoc->base.bind_addr, ep->base.bind_addr.port); sctp_bind_addr_init(&asoc->base.bind_addr, ep->base.bind_addr.port);
rwlock_init(&asoc->base.addr_lock);
asoc->state = SCTP_STATE_CLOSED; asoc->state = SCTP_STATE_CLOSED;
@ -937,8 +936,6 @@ struct sctp_transport *sctp_assoc_is_match(struct sctp_association *asoc,
{ {
struct sctp_transport *transport; struct sctp_transport *transport;
sctp_read_lock(&asoc->base.addr_lock);
if ((htons(asoc->base.bind_addr.port) == laddr->v4.sin_port) && if ((htons(asoc->base.bind_addr.port) == laddr->v4.sin_port) &&
(htons(asoc->peer.port) == paddr->v4.sin_port)) { (htons(asoc->peer.port) == paddr->v4.sin_port)) {
transport = sctp_assoc_lookup_paddr(asoc, paddr); transport = sctp_assoc_lookup_paddr(asoc, paddr);
@ -952,7 +949,6 @@ struct sctp_transport *sctp_assoc_is_match(struct sctp_association *asoc,
transport = NULL; transport = NULL;
out: out:
sctp_read_unlock(&asoc->base.addr_lock);
return transport; return transport;
} }
@ -1376,19 +1372,13 @@ int sctp_assoc_set_bind_addr_from_cookie(struct sctp_association *asoc,
int sctp_assoc_lookup_laddr(struct sctp_association *asoc, int sctp_assoc_lookup_laddr(struct sctp_association *asoc,
const union sctp_addr *laddr) const union sctp_addr *laddr)
{ {
int found; int found = 0;
sctp_read_lock(&asoc->base.addr_lock);
if ((asoc->base.bind_addr.port == ntohs(laddr->v4.sin_port)) && if ((asoc->base.bind_addr.port == ntohs(laddr->v4.sin_port)) &&
sctp_bind_addr_match(&asoc->base.bind_addr, laddr, sctp_bind_addr_match(&asoc->base.bind_addr, laddr,
sctp_sk(asoc->base.sk))) { sctp_sk(asoc->base.sk)))
found = 1; found = 1;
goto out;
}
found = 0;
out:
sctp_read_unlock(&asoc->base.addr_lock);
return found; return found;
} }

View file

@ -167,7 +167,11 @@ int sctp_add_bind_addr(struct sctp_bind_addr *bp, union sctp_addr *new,
INIT_LIST_HEAD(&addr->list); INIT_LIST_HEAD(&addr->list);
INIT_RCU_HEAD(&addr->rcu); INIT_RCU_HEAD(&addr->rcu);
list_add_tail(&addr->list, &bp->address_list);
/* We always hold a socket lock when calling this function,
* and that acts as a writer synchronizing lock.
*/
list_add_tail_rcu(&addr->list, &bp->address_list);
SCTP_DBG_OBJCNT_INC(addr); SCTP_DBG_OBJCNT_INC(addr);
return 0; return 0;
@ -176,23 +180,35 @@ int sctp_add_bind_addr(struct sctp_bind_addr *bp, union sctp_addr *new,
/* Delete an address from the bind address list in the SCTP_bind_addr /* Delete an address from the bind address list in the SCTP_bind_addr
* structure. * structure.
*/ */
int sctp_del_bind_addr(struct sctp_bind_addr *bp, union sctp_addr *del_addr) int sctp_del_bind_addr(struct sctp_bind_addr *bp, union sctp_addr *del_addr,
void (*rcu_call)(struct rcu_head *head,
void (*func)(struct rcu_head *head)))
{ {
struct list_head *pos, *temp; struct sctp_sockaddr_entry *addr, *temp;
struct sctp_sockaddr_entry *addr;
list_for_each_safe(pos, temp, &bp->address_list) { /* We hold the socket lock when calling this function,
addr = list_entry(pos, struct sctp_sockaddr_entry, list); * and that acts as a writer synchronizing lock.
*/
list_for_each_entry_safe(addr, temp, &bp->address_list, list) {
if (sctp_cmp_addr_exact(&addr->a, del_addr)) { if (sctp_cmp_addr_exact(&addr->a, del_addr)) {
/* Found the exact match. */ /* Found the exact match. */
list_del(pos); addr->valid = 0;
kfree(addr); list_del_rcu(&addr->list);
SCTP_DBG_OBJCNT_DEC(addr); break;
return 0;
} }
} }
/* Call the rcu callback provided in the args. This function is
* called by both BH packet processing and user side socket option
* processing, but it works on different lists in those 2 contexts.
* Each context provides it's own callback, whether call_rcu_bh()
* or call_rcu(), to make sure that we wait for an appropriate time.
*/
if (addr && !addr->valid) {
rcu_call(&addr->rcu, sctp_local_addr_free);
SCTP_DBG_OBJCNT_DEC(addr);
}
return -EINVAL; return -EINVAL;
} }
@ -302,15 +318,20 @@ int sctp_bind_addr_match(struct sctp_bind_addr *bp,
struct sctp_sock *opt) struct sctp_sock *opt)
{ {
struct sctp_sockaddr_entry *laddr; struct sctp_sockaddr_entry *laddr;
struct list_head *pos; int match = 0;
list_for_each(pos, &bp->address_list) { rcu_read_lock();
laddr = list_entry(pos, struct sctp_sockaddr_entry, list); list_for_each_entry_rcu(laddr, &bp->address_list, list) {
if (opt->pf->cmp_addr(&laddr->a, addr, opt)) if (!laddr->valid)
return 1; continue;
if (opt->pf->cmp_addr(&laddr->a, addr, opt)) {
match = 1;
break;
}
} }
rcu_read_unlock();
return 0; return match;
} }
/* Find the first address in the bind address list that is not present in /* Find the first address in the bind address list that is not present in
@ -325,18 +346,19 @@ union sctp_addr *sctp_find_unmatch_addr(struct sctp_bind_addr *bp,
union sctp_addr *addr; union sctp_addr *addr;
void *addr_buf; void *addr_buf;
struct sctp_af *af; struct sctp_af *af;
struct list_head *pos;
int i; int i;
list_for_each(pos, &bp->address_list) { /* This is only called sctp_send_asconf_del_ip() and we hold
laddr = list_entry(pos, struct sctp_sockaddr_entry, list); * the socket lock in that code patch, so that address list
* can't change.
*/
list_for_each_entry(laddr, &bp->address_list, list) {
addr_buf = (union sctp_addr *)addrs; addr_buf = (union sctp_addr *)addrs;
for (i = 0; i < addrcnt; i++) { for (i = 0; i < addrcnt; i++) {
addr = (union sctp_addr *)addr_buf; addr = (union sctp_addr *)addr_buf;
af = sctp_get_af_specific(addr->v4.sin_family); af = sctp_get_af_specific(addr->v4.sin_family);
if (!af) if (!af)
return NULL; break;
if (opt->pf->cmp_addr(&laddr->a, addr, opt)) if (opt->pf->cmp_addr(&laddr->a, addr, opt))
break; break;

View file

@ -92,7 +92,6 @@ static struct sctp_endpoint *sctp_endpoint_init(struct sctp_endpoint *ep,
/* Initialize the bind addr area */ /* Initialize the bind addr area */
sctp_bind_addr_init(&ep->base.bind_addr, 0); sctp_bind_addr_init(&ep->base.bind_addr, 0);
rwlock_init(&ep->base.addr_lock);
/* Remember who we are attached to. */ /* Remember who we are attached to. */
ep->base.sk = sk; ep->base.sk = sk;
@ -225,21 +224,14 @@ void sctp_endpoint_put(struct sctp_endpoint *ep)
struct sctp_endpoint *sctp_endpoint_is_match(struct sctp_endpoint *ep, struct sctp_endpoint *sctp_endpoint_is_match(struct sctp_endpoint *ep,
const union sctp_addr *laddr) const union sctp_addr *laddr)
{ {
struct sctp_endpoint *retval; struct sctp_endpoint *retval = NULL;
sctp_read_lock(&ep->base.addr_lock);
if (htons(ep->base.bind_addr.port) == laddr->v4.sin_port) { if (htons(ep->base.bind_addr.port) == laddr->v4.sin_port) {
if (sctp_bind_addr_match(&ep->base.bind_addr, laddr, if (sctp_bind_addr_match(&ep->base.bind_addr, laddr,
sctp_sk(ep->base.sk))) { sctp_sk(ep->base.sk)))
retval = ep; retval = ep;
goto out;
}
} }
retval = NULL;
out:
sctp_read_unlock(&ep->base.addr_lock);
return retval; return retval;
} }
@ -261,9 +253,7 @@ static struct sctp_association *__sctp_endpoint_lookup_assoc(
list_for_each(pos, &ep->asocs) { list_for_each(pos, &ep->asocs) {
asoc = list_entry(pos, struct sctp_association, asocs); asoc = list_entry(pos, struct sctp_association, asocs);
if (rport == asoc->peer.port) { if (rport == asoc->peer.port) {
sctp_read_lock(&asoc->base.addr_lock);
*transport = sctp_assoc_lookup_paddr(asoc, paddr); *transport = sctp_assoc_lookup_paddr(asoc, paddr);
sctp_read_unlock(&asoc->base.addr_lock);
if (*transport) if (*transport)
return asoc; return asoc;
@ -295,20 +285,17 @@ struct sctp_association *sctp_endpoint_lookup_assoc(
int sctp_endpoint_is_peeled_off(struct sctp_endpoint *ep, int sctp_endpoint_is_peeled_off(struct sctp_endpoint *ep,
const union sctp_addr *paddr) const union sctp_addr *paddr)
{ {
struct list_head *pos;
struct sctp_sockaddr_entry *addr; struct sctp_sockaddr_entry *addr;
struct sctp_bind_addr *bp; struct sctp_bind_addr *bp;
sctp_read_lock(&ep->base.addr_lock);
bp = &ep->base.bind_addr; bp = &ep->base.bind_addr;
list_for_each(pos, &bp->address_list) { /* This function is called with the socket lock held,
addr = list_entry(pos, struct sctp_sockaddr_entry, list); * so the address_list can not change.
if (sctp_has_association(&addr->a, paddr)) { */
sctp_read_unlock(&ep->base.addr_lock); list_for_each_entry(addr, &bp->address_list, list) {
if (sctp_has_association(&addr->a, paddr))
return 1; return 1;
}
} }
sctp_read_unlock(&ep->base.addr_lock);
return 0; return 0;
} }

View file

@ -302,9 +302,7 @@ static void sctp_v6_get_saddr(struct sctp_association *asoc,
union sctp_addr *saddr) union sctp_addr *saddr)
{ {
struct sctp_bind_addr *bp; struct sctp_bind_addr *bp;
rwlock_t *addr_lock;
struct sctp_sockaddr_entry *laddr; struct sctp_sockaddr_entry *laddr;
struct list_head *pos;
sctp_scope_t scope; sctp_scope_t scope;
union sctp_addr *baddr = NULL; union sctp_addr *baddr = NULL;
__u8 matchlen = 0; __u8 matchlen = 0;
@ -324,14 +322,14 @@ static void sctp_v6_get_saddr(struct sctp_association *asoc,
scope = sctp_scope(daddr); scope = sctp_scope(daddr);
bp = &asoc->base.bind_addr; bp = &asoc->base.bind_addr;
addr_lock = &asoc->base.addr_lock;
/* Go through the bind address list and find the best source address /* Go through the bind address list and find the best source address
* that matches the scope of the destination address. * that matches the scope of the destination address.
*/ */
sctp_read_lock(addr_lock); rcu_read_lock();
list_for_each(pos, &bp->address_list) { list_for_each_entry_rcu(laddr, &bp->address_list, list) {
laddr = list_entry(pos, struct sctp_sockaddr_entry, list); if (!laddr->valid)
continue;
if ((laddr->use_as_src) && if ((laddr->use_as_src) &&
(laddr->a.sa.sa_family == AF_INET6) && (laddr->a.sa.sa_family == AF_INET6) &&
(scope <= sctp_scope(&laddr->a))) { (scope <= sctp_scope(&laddr->a))) {
@ -353,7 +351,7 @@ static void sctp_v6_get_saddr(struct sctp_association *asoc,
__FUNCTION__, asoc, NIP6(daddr->v6.sin6_addr)); __FUNCTION__, asoc, NIP6(daddr->v6.sin6_addr));
} }
sctp_read_unlock(addr_lock); rcu_read_unlock();
} }
/* Make a copy of all potential local addresses. */ /* Make a copy of all potential local addresses. */

View file

@ -224,7 +224,7 @@ int sctp_copy_local_addr_list(struct sctp_bind_addr *bp, sctp_scope_t scope,
(copy_flags & SCTP_ADDR6_ALLOWED) && (copy_flags & SCTP_ADDR6_ALLOWED) &&
(copy_flags & SCTP_ADDR6_PEERSUPP)))) { (copy_flags & SCTP_ADDR6_PEERSUPP)))) {
error = sctp_add_bind_addr(bp, &addr->a, 1, error = sctp_add_bind_addr(bp, &addr->a, 1,
GFP_ATOMIC); GFP_ATOMIC);
if (error) if (error)
goto end_copy; goto end_copy;
} }
@ -428,9 +428,7 @@ static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc,
struct rtable *rt; struct rtable *rt;
struct flowi fl; struct flowi fl;
struct sctp_bind_addr *bp; struct sctp_bind_addr *bp;
rwlock_t *addr_lock;
struct sctp_sockaddr_entry *laddr; struct sctp_sockaddr_entry *laddr;
struct list_head *pos;
struct dst_entry *dst = NULL; struct dst_entry *dst = NULL;
union sctp_addr dst_saddr; union sctp_addr dst_saddr;
@ -459,23 +457,20 @@ static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc,
goto out; goto out;
bp = &asoc->base.bind_addr; bp = &asoc->base.bind_addr;
addr_lock = &asoc->base.addr_lock;
if (dst) { if (dst) {
/* Walk through the bind address list and look for a bind /* Walk through the bind address list and look for a bind
* address that matches the source address of the returned dst. * address that matches the source address of the returned dst.
*/ */
sctp_read_lock(addr_lock); rcu_read_lock();
list_for_each(pos, &bp->address_list) { list_for_each_entry_rcu(laddr, &bp->address_list, list) {
laddr = list_entry(pos, struct sctp_sockaddr_entry, if (!laddr->valid || !laddr->use_as_src)
list);
if (!laddr->use_as_src)
continue; continue;
sctp_v4_dst_saddr(&dst_saddr, dst, htons(bp->port)); sctp_v4_dst_saddr(&dst_saddr, dst, htons(bp->port));
if (sctp_v4_cmp_addr(&dst_saddr, &laddr->a)) if (sctp_v4_cmp_addr(&dst_saddr, &laddr->a))
goto out_unlock; goto out_unlock;
} }
sctp_read_unlock(addr_lock); rcu_read_unlock();
/* None of the bound addresses match the source address of the /* None of the bound addresses match the source address of the
* dst. So release it. * dst. So release it.
@ -487,10 +482,10 @@ static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc,
/* Walk through the bind address list and try to get a dst that /* Walk through the bind address list and try to get a dst that
* matches a bind address as the source address. * matches a bind address as the source address.
*/ */
sctp_read_lock(addr_lock); rcu_read_lock();
list_for_each(pos, &bp->address_list) { list_for_each_entry_rcu(laddr, &bp->address_list, list) {
laddr = list_entry(pos, struct sctp_sockaddr_entry, list); if (!laddr->valid)
continue;
if ((laddr->use_as_src) && if ((laddr->use_as_src) &&
(AF_INET == laddr->a.sa.sa_family)) { (AF_INET == laddr->a.sa.sa_family)) {
fl.fl4_src = laddr->a.v4.sin_addr.s_addr; fl.fl4_src = laddr->a.v4.sin_addr.s_addr;
@ -502,7 +497,7 @@ static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc,
} }
out_unlock: out_unlock:
sctp_read_unlock(addr_lock); rcu_read_unlock();
out: out:
if (dst) if (dst)
SCTP_DEBUG_PRINTK("rt_dst:%u.%u.%u.%u, rt_src:%u.%u.%u.%u\n", SCTP_DEBUG_PRINTK("rt_dst:%u.%u.%u.%u, rt_src:%u.%u.%u.%u\n",

View file

@ -1531,7 +1531,7 @@ struct sctp_association *sctp_unpack_cookie(
/* Also, add the destination address. */ /* Also, add the destination address. */
if (list_empty(&retval->base.bind_addr.address_list)) { if (list_empty(&retval->base.bind_addr.address_list)) {
sctp_add_bind_addr(&retval->base.bind_addr, &chunk->dest, 1, sctp_add_bind_addr(&retval->base.bind_addr, &chunk->dest, 1,
GFP_ATOMIC); GFP_ATOMIC);
} }
retval->next_tsn = retval->c.initial_tsn; retval->next_tsn = retval->c.initial_tsn;
@ -2613,22 +2613,16 @@ static int sctp_asconf_param_success(struct sctp_association *asoc,
switch (asconf_param->param_hdr.type) { switch (asconf_param->param_hdr.type) {
case SCTP_PARAM_ADD_IP: case SCTP_PARAM_ADD_IP:
sctp_local_bh_disable(); /* This is always done in BH context with a socket lock
sctp_write_lock(&asoc->base.addr_lock); * held, so the list can not change.
list_for_each(pos, &bp->address_list) { */
saddr = list_entry(pos, struct sctp_sockaddr_entry, list); list_for_each_entry(saddr, &bp->address_list, list) {
if (sctp_cmp_addr_exact(&saddr->a, &addr)) if (sctp_cmp_addr_exact(&saddr->a, &addr))
saddr->use_as_src = 1; saddr->use_as_src = 1;
} }
sctp_write_unlock(&asoc->base.addr_lock);
sctp_local_bh_enable();
break; break;
case SCTP_PARAM_DEL_IP: case SCTP_PARAM_DEL_IP:
sctp_local_bh_disable(); retval = sctp_del_bind_addr(bp, &addr, call_rcu_bh);
sctp_write_lock(&asoc->base.addr_lock);
retval = sctp_del_bind_addr(bp, &addr);
sctp_write_unlock(&asoc->base.addr_lock);
sctp_local_bh_enable();
list_for_each(pos, &asoc->peer.transport_addr_list) { list_for_each(pos, &asoc->peer.transport_addr_list) {
transport = list_entry(pos, struct sctp_transport, transport = list_entry(pos, struct sctp_transport,
transports); transports);

View file

@ -367,14 +367,10 @@ SCTP_STATIC int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len)
if (!bp->port) if (!bp->port)
bp->port = inet_sk(sk)->num; bp->port = inet_sk(sk)->num;
/* Add the address to the bind address list. */ /* Add the address to the bind address list.
sctp_local_bh_disable(); * Use GFP_ATOMIC since BHs will be disabled.
sctp_write_lock(&ep->base.addr_lock); */
/* Use GFP_ATOMIC since BHs are disabled. */
ret = sctp_add_bind_addr(bp, addr, 1, GFP_ATOMIC); ret = sctp_add_bind_addr(bp, addr, 1, GFP_ATOMIC);
sctp_write_unlock(&ep->base.addr_lock);
sctp_local_bh_enable();
/* Copy back into socket for getsockname() use. */ /* Copy back into socket for getsockname() use. */
if (!ret) { if (!ret) {
@ -544,15 +540,12 @@ static int sctp_send_asconf_add_ip(struct sock *sk,
if (i < addrcnt) if (i < addrcnt)
continue; continue;
/* Use the first address in bind addr list of association as /* Use the first valid address in bind addr list of
* Address Parameter of ASCONF CHUNK. * association as Address Parameter of ASCONF CHUNK.
*/ */
sctp_read_lock(&asoc->base.addr_lock);
bp = &asoc->base.bind_addr; bp = &asoc->base.bind_addr;
p = bp->address_list.next; p = bp->address_list.next;
laddr = list_entry(p, struct sctp_sockaddr_entry, list); laddr = list_entry(p, struct sctp_sockaddr_entry, list);
sctp_read_unlock(&asoc->base.addr_lock);
chunk = sctp_make_asconf_update_ip(asoc, &laddr->a, addrs, chunk = sctp_make_asconf_update_ip(asoc, &laddr->a, addrs,
addrcnt, SCTP_PARAM_ADD_IP); addrcnt, SCTP_PARAM_ADD_IP);
if (!chunk) { if (!chunk) {
@ -567,8 +560,6 @@ static int sctp_send_asconf_add_ip(struct sock *sk,
/* Add the new addresses to the bind address list with /* Add the new addresses to the bind address list with
* use_as_src set to 0. * use_as_src set to 0.
*/ */
sctp_local_bh_disable();
sctp_write_lock(&asoc->base.addr_lock);
addr_buf = addrs; addr_buf = addrs;
for (i = 0; i < addrcnt; i++) { for (i = 0; i < addrcnt; i++) {
addr = (union sctp_addr *)addr_buf; addr = (union sctp_addr *)addr_buf;
@ -578,8 +569,6 @@ static int sctp_send_asconf_add_ip(struct sock *sk,
GFP_ATOMIC); GFP_ATOMIC);
addr_buf += af->sockaddr_len; addr_buf += af->sockaddr_len;
} }
sctp_write_unlock(&asoc->base.addr_lock);
sctp_local_bh_enable();
} }
out: out:
@ -651,13 +640,7 @@ static int sctp_bindx_rem(struct sock *sk, struct sockaddr *addrs, int addrcnt)
* socket routing and failover schemes. Refer to comments in * socket routing and failover schemes. Refer to comments in
* sctp_do_bind(). -daisy * sctp_do_bind(). -daisy
*/ */
sctp_local_bh_disable(); retval = sctp_del_bind_addr(bp, sa_addr, call_rcu);
sctp_write_lock(&ep->base.addr_lock);
retval = sctp_del_bind_addr(bp, sa_addr);
sctp_write_unlock(&ep->base.addr_lock);
sctp_local_bh_enable();
addr_buf += af->sockaddr_len; addr_buf += af->sockaddr_len;
err_bindx_rem: err_bindx_rem:
@ -748,14 +731,16 @@ static int sctp_send_asconf_del_ip(struct sock *sk,
* make sure that we do not delete all the addresses in the * make sure that we do not delete all the addresses in the
* association. * association.
*/ */
sctp_read_lock(&asoc->base.addr_lock);
bp = &asoc->base.bind_addr; bp = &asoc->base.bind_addr;
laddr = sctp_find_unmatch_addr(bp, (union sctp_addr *)addrs, laddr = sctp_find_unmatch_addr(bp, (union sctp_addr *)addrs,
addrcnt, sp); addrcnt, sp);
sctp_read_unlock(&asoc->base.addr_lock);
if (!laddr) if (!laddr)
continue; continue;
/* We do not need RCU protection throughout this loop
* because this is done under a socket lock from the
* setsockopt call.
*/
chunk = sctp_make_asconf_update_ip(asoc, laddr, addrs, addrcnt, chunk = sctp_make_asconf_update_ip(asoc, laddr, addrs, addrcnt,
SCTP_PARAM_DEL_IP); SCTP_PARAM_DEL_IP);
if (!chunk) { if (!chunk) {
@ -766,23 +751,16 @@ static int sctp_send_asconf_del_ip(struct sock *sk,
/* Reset use_as_src flag for the addresses in the bind address /* Reset use_as_src flag for the addresses in the bind address
* list that are to be deleted. * list that are to be deleted.
*/ */
sctp_local_bh_disable();
sctp_write_lock(&asoc->base.addr_lock);
addr_buf = addrs; addr_buf = addrs;
for (i = 0; i < addrcnt; i++) { for (i = 0; i < addrcnt; i++) {
laddr = (union sctp_addr *)addr_buf; laddr = (union sctp_addr *)addr_buf;
af = sctp_get_af_specific(laddr->v4.sin_family); af = sctp_get_af_specific(laddr->v4.sin_family);
list_for_each(pos1, &bp->address_list) { list_for_each_entry(saddr, &bp->address_list, list) {
saddr = list_entry(pos1,
struct sctp_sockaddr_entry,
list);
if (sctp_cmp_addr_exact(&saddr->a, laddr)) if (sctp_cmp_addr_exact(&saddr->a, laddr))
saddr->use_as_src = 0; saddr->use_as_src = 0;
} }
addr_buf += af->sockaddr_len; addr_buf += af->sockaddr_len;
} }
sctp_write_unlock(&asoc->base.addr_lock);
sctp_local_bh_enable();
/* Update the route and saddr entries for all the transports /* Update the route and saddr entries for all the transports
* as some of the addresses in the bind address list are * as some of the addresses in the bind address list are
@ -4057,11 +4035,9 @@ static int sctp_getsockopt_local_addrs_num_old(struct sock *sk, int len,
int __user *optlen) int __user *optlen)
{ {
sctp_assoc_t id; sctp_assoc_t id;
struct list_head *pos;
struct sctp_bind_addr *bp; struct sctp_bind_addr *bp;
struct sctp_association *asoc; struct sctp_association *asoc;
struct sctp_sockaddr_entry *addr; struct sctp_sockaddr_entry *addr;
rwlock_t *addr_lock;
int cnt = 0; int cnt = 0;
if (len < sizeof(sctp_assoc_t)) if (len < sizeof(sctp_assoc_t))
@ -4078,17 +4054,13 @@ static int sctp_getsockopt_local_addrs_num_old(struct sock *sk, int len,
*/ */
if (0 == id) { if (0 == id) {
bp = &sctp_sk(sk)->ep->base.bind_addr; bp = &sctp_sk(sk)->ep->base.bind_addr;
addr_lock = &sctp_sk(sk)->ep->base.addr_lock;
} else { } else {
asoc = sctp_id2assoc(sk, id); asoc = sctp_id2assoc(sk, id);
if (!asoc) if (!asoc)
return -EINVAL; return -EINVAL;
bp = &asoc->base.bind_addr; bp = &asoc->base.bind_addr;
addr_lock = &asoc->base.addr_lock;
} }
sctp_read_lock(addr_lock);
/* If the endpoint is bound to 0.0.0.0 or ::0, count the valid /* If the endpoint is bound to 0.0.0.0 or ::0, count the valid
* addresses from the global local address list. * addresses from the global local address list.
*/ */
@ -4115,12 +4087,14 @@ static int sctp_getsockopt_local_addrs_num_old(struct sock *sk, int len,
goto done; goto done;
} }
list_for_each(pos, &bp->address_list) { /* Protection on the bound address list is not needed,
* since in the socket option context we hold the socket lock,
* so there is no way that the bound address list can change.
*/
list_for_each_entry(addr, &bp->address_list, list) {
cnt ++; cnt ++;
} }
done: done:
sctp_read_unlock(addr_lock);
return cnt; return cnt;
} }
@ -4204,7 +4178,6 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len,
{ {
struct sctp_bind_addr *bp; struct sctp_bind_addr *bp;
struct sctp_association *asoc; struct sctp_association *asoc;
struct list_head *pos;
int cnt = 0; int cnt = 0;
struct sctp_getaddrs_old getaddrs; struct sctp_getaddrs_old getaddrs;
struct sctp_sockaddr_entry *addr; struct sctp_sockaddr_entry *addr;
@ -4212,7 +4185,6 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len,
union sctp_addr temp; union sctp_addr temp;
struct sctp_sock *sp = sctp_sk(sk); struct sctp_sock *sp = sctp_sk(sk);
int addrlen; int addrlen;
rwlock_t *addr_lock;
int err = 0; int err = 0;
void *addrs; void *addrs;
void *buf; void *buf;
@ -4234,13 +4206,11 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len,
*/ */
if (0 == getaddrs.assoc_id) { if (0 == getaddrs.assoc_id) {
bp = &sctp_sk(sk)->ep->base.bind_addr; bp = &sctp_sk(sk)->ep->base.bind_addr;
addr_lock = &sctp_sk(sk)->ep->base.addr_lock;
} else { } else {
asoc = sctp_id2assoc(sk, getaddrs.assoc_id); asoc = sctp_id2assoc(sk, getaddrs.assoc_id);
if (!asoc) if (!asoc)
return -EINVAL; return -EINVAL;
bp = &asoc->base.bind_addr; bp = &asoc->base.bind_addr;
addr_lock = &asoc->base.addr_lock;
} }
to = getaddrs.addrs; to = getaddrs.addrs;
@ -4254,8 +4224,6 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len,
if (!addrs) if (!addrs)
return -ENOMEM; return -ENOMEM;
sctp_read_lock(addr_lock);
/* If the endpoint is bound to 0.0.0.0 or ::0, get the valid /* If the endpoint is bound to 0.0.0.0 or ::0, get the valid
* addresses from the global local address list. * addresses from the global local address list.
*/ */
@ -4271,8 +4239,11 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len,
} }
buf = addrs; buf = addrs;
list_for_each(pos, &bp->address_list) { /* Protection on the bound address list is not needed since
addr = list_entry(pos, struct sctp_sockaddr_entry, list); * in the socket option context we hold a socket lock and
* thus the bound address list can't change.
*/
list_for_each_entry(addr, &bp->address_list, list) {
memcpy(&temp, &addr->a, sizeof(temp)); memcpy(&temp, &addr->a, sizeof(temp));
sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp); sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp);
addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len; addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len;
@ -4284,8 +4255,6 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len,
} }
copy_getaddrs: copy_getaddrs:
sctp_read_unlock(addr_lock);
/* copy the entire address list into the user provided space */ /* copy the entire address list into the user provided space */
if (copy_to_user(to, addrs, bytes_copied)) { if (copy_to_user(to, addrs, bytes_copied)) {
err = -EFAULT; err = -EFAULT;
@ -4307,7 +4276,6 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
{ {
struct sctp_bind_addr *bp; struct sctp_bind_addr *bp;
struct sctp_association *asoc; struct sctp_association *asoc;
struct list_head *pos;
int cnt = 0; int cnt = 0;
struct sctp_getaddrs getaddrs; struct sctp_getaddrs getaddrs;
struct sctp_sockaddr_entry *addr; struct sctp_sockaddr_entry *addr;
@ -4315,7 +4283,6 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
union sctp_addr temp; union sctp_addr temp;
struct sctp_sock *sp = sctp_sk(sk); struct sctp_sock *sp = sctp_sk(sk);
int addrlen; int addrlen;
rwlock_t *addr_lock;
int err = 0; int err = 0;
size_t space_left; size_t space_left;
int bytes_copied = 0; int bytes_copied = 0;
@ -4336,13 +4303,11 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
*/ */
if (0 == getaddrs.assoc_id) { if (0 == getaddrs.assoc_id) {
bp = &sctp_sk(sk)->ep->base.bind_addr; bp = &sctp_sk(sk)->ep->base.bind_addr;
addr_lock = &sctp_sk(sk)->ep->base.addr_lock;
} else { } else {
asoc = sctp_id2assoc(sk, getaddrs.assoc_id); asoc = sctp_id2assoc(sk, getaddrs.assoc_id);
if (!asoc) if (!asoc)
return -EINVAL; return -EINVAL;
bp = &asoc->base.bind_addr; bp = &asoc->base.bind_addr;
addr_lock = &asoc->base.addr_lock;
} }
to = optval + offsetof(struct sctp_getaddrs,addrs); to = optval + offsetof(struct sctp_getaddrs,addrs);
@ -4352,8 +4317,6 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
if (!addrs) if (!addrs)
return -ENOMEM; return -ENOMEM;
sctp_read_lock(addr_lock);
/* If the endpoint is bound to 0.0.0.0 or ::0, get the valid /* If the endpoint is bound to 0.0.0.0 or ::0, get the valid
* addresses from the global local address list. * addresses from the global local address list.
*/ */
@ -4365,21 +4328,24 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
space_left, &bytes_copied); space_left, &bytes_copied);
if (cnt < 0) { if (cnt < 0) {
err = cnt; err = cnt;
goto error_lock; goto out;
} }
goto copy_getaddrs; goto copy_getaddrs;
} }
} }
buf = addrs; buf = addrs;
list_for_each(pos, &bp->address_list) { /* Protection on the bound address list is not needed since
addr = list_entry(pos, struct sctp_sockaddr_entry, list); * in the socket option context we hold a socket lock and
* thus the bound address list can't change.
*/
list_for_each_entry(addr, &bp->address_list, list) {
memcpy(&temp, &addr->a, sizeof(temp)); memcpy(&temp, &addr->a, sizeof(temp));
sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp); sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp);
addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len; addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len;
if (space_left < addrlen) { if (space_left < addrlen) {
err = -ENOMEM; /*fixme: right error?*/ err = -ENOMEM; /*fixme: right error?*/
goto error_lock; goto out;
} }
memcpy(buf, &temp, addrlen); memcpy(buf, &temp, addrlen);
buf += addrlen; buf += addrlen;
@ -4389,8 +4355,6 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
} }
copy_getaddrs: copy_getaddrs:
sctp_read_unlock(addr_lock);
if (copy_to_user(to, addrs, bytes_copied)) { if (copy_to_user(to, addrs, bytes_copied)) {
err = -EFAULT; err = -EFAULT;
goto out; goto out;
@ -4401,12 +4365,6 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
} }
if (put_user(bytes_copied, optlen)) if (put_user(bytes_copied, optlen))
err = -EFAULT; err = -EFAULT;
goto out;
error_lock:
sctp_read_unlock(addr_lock);
out: out:
kfree(addrs); kfree(addrs);
return err; return err;