Skip to content

Commit

Permalink
[SCTP]: Convert bind_addr_list locking to RCU
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Vlad Yasevich authored and David S. Miller committed Sep 16, 2007
1 parent 2930354 commit 559cf71
Show file tree
Hide file tree
Showing 8 changed files with 106 additions and 163 deletions.
7 changes: 3 additions & 4 deletions include/net/sctp/structs.h
Original file line number Diff line number Diff line change
Expand Up @@ -1155,7 +1155,9 @@ int sctp_bind_addr_copy(struct sctp_bind_addr *dest,
int flags);
int sctp_add_bind_addr(struct sctp_bind_addr *, union sctp_addr *,
__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 *,
struct sctp_sock *);
union sctp_addr *sctp_find_unmatch_addr(struct sctp_bind_addr *bp,
Expand Down Expand Up @@ -1226,9 +1228,6 @@ struct sctp_ep_common {
* bind_addr.address_list is our set of local IP addresses.
*/
struct sctp_bind_addr bind_addr;

/* Protection during address list comparisons. */
rwlock_t addr_lock;
};


Expand Down
14 changes: 2 additions & 12 deletions net/sctp/associola.c
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a

/* Initialize the bind addr area. */
sctp_bind_addr_init(&asoc->base.bind_addr, ep->base.bind_addr.port);
rwlock_init(&asoc->base.addr_lock);

asoc->state = SCTP_STATE_CLOSED;

Expand Down Expand Up @@ -937,8 +936,6 @@ struct sctp_transport *sctp_assoc_is_match(struct sctp_association *asoc,
{
struct sctp_transport *transport;

sctp_read_lock(&asoc->base.addr_lock);

if ((htons(asoc->base.bind_addr.port) == laddr->v4.sin_port) &&
(htons(asoc->peer.port) == paddr->v4.sin_port)) {
transport = sctp_assoc_lookup_paddr(asoc, paddr);
Expand All @@ -952,7 +949,6 @@ struct sctp_transport *sctp_assoc_is_match(struct sctp_association *asoc,
transport = NULL;

out:
sctp_read_unlock(&asoc->base.addr_lock);
return transport;
}

Expand Down Expand Up @@ -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,
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)) &&
sctp_bind_addr_match(&asoc->base.bind_addr, laddr,
sctp_sk(asoc->base.sk))) {
sctp_sk(asoc->base.sk)))
found = 1;
goto out;
}

found = 0;
out:
sctp_read_unlock(&asoc->base.addr_lock);
return found;
}

Expand Down
68 changes: 45 additions & 23 deletions net/sctp/bind_addr.c
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,11 @@ int sctp_add_bind_addr(struct sctp_bind_addr *bp, union sctp_addr *new,

INIT_LIST_HEAD(&addr->list);
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);

return 0;
Expand All @@ -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
* 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;
struct sctp_sockaddr_entry *addr, *temp;

list_for_each_safe(pos, temp, &bp->address_list) {
addr = list_entry(pos, struct sctp_sockaddr_entry, list);
/* We hold the socket lock when calling this function,
* 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)) {
/* Found the exact match. */
list_del(pos);
kfree(addr);
SCTP_DBG_OBJCNT_DEC(addr);

return 0;
addr->valid = 0;
list_del_rcu(&addr->list);
break;
}
}

/* 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;
}

Expand Down Expand Up @@ -302,15 +318,20 @@ int sctp_bind_addr_match(struct sctp_bind_addr *bp,
struct sctp_sock *opt)
{
struct sctp_sockaddr_entry *laddr;
struct list_head *pos;

list_for_each(pos, &bp->address_list) {
laddr = list_entry(pos, struct sctp_sockaddr_entry, list);
if (opt->pf->cmp_addr(&laddr->a, addr, opt))
return 1;
int match = 0;

rcu_read_lock();
list_for_each_entry_rcu(laddr, &bp->address_list, list) {
if (!laddr->valid)
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
Expand All @@ -325,18 +346,19 @@ union sctp_addr *sctp_find_unmatch_addr(struct sctp_bind_addr *bp,
union sctp_addr *addr;
void *addr_buf;
struct sctp_af *af;
struct list_head *pos;
int i;

list_for_each(pos, &bp->address_list) {
laddr = list_entry(pos, struct sctp_sockaddr_entry, list);

/* This is only called sctp_send_asconf_del_ip() and we hold
* 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;
for (i = 0; i < addrcnt; i++) {
addr = (union sctp_addr *)addr_buf;
af = sctp_get_af_specific(addr->v4.sin_family);
if (!af)
return NULL;
break;

if (opt->pf->cmp_addr(&laddr->a, addr, opt))
break;
Expand Down
27 changes: 7 additions & 20 deletions net/sctp/endpointola.c
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,6 @@ static struct sctp_endpoint *sctp_endpoint_init(struct sctp_endpoint *ep,

/* Initialize the bind addr area */
sctp_bind_addr_init(&ep->base.bind_addr, 0);
rwlock_init(&ep->base.addr_lock);

/* Remember who we are attached to. */
ep->base.sk = sk;
Expand Down Expand Up @@ -225,21 +224,14 @@ void sctp_endpoint_put(struct sctp_endpoint *ep)
struct sctp_endpoint *sctp_endpoint_is_match(struct sctp_endpoint *ep,
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 (sctp_bind_addr_match(&ep->base.bind_addr, laddr,
sctp_sk(ep->base.sk))) {
sctp_sk(ep->base.sk)))
retval = ep;
goto out;
}
}

retval = NULL;

out:
sctp_read_unlock(&ep->base.addr_lock);
return retval;
}

Expand All @@ -261,9 +253,7 @@ static struct sctp_association *__sctp_endpoint_lookup_assoc(
list_for_each(pos, &ep->asocs) {
asoc = list_entry(pos, struct sctp_association, asocs);
if (rport == asoc->peer.port) {
sctp_read_lock(&asoc->base.addr_lock);
*transport = sctp_assoc_lookup_paddr(asoc, paddr);
sctp_read_unlock(&asoc->base.addr_lock);

if (*transport)
return asoc;
Expand Down Expand Up @@ -295,20 +285,17 @@ struct sctp_association *sctp_endpoint_lookup_assoc(
int sctp_endpoint_is_peeled_off(struct sctp_endpoint *ep,
const union sctp_addr *paddr)
{
struct list_head *pos;
struct sctp_sockaddr_entry *addr;
struct sctp_bind_addr *bp;

sctp_read_lock(&ep->base.addr_lock);
bp = &ep->base.bind_addr;
list_for_each(pos, &bp->address_list) {
addr = list_entry(pos, struct sctp_sockaddr_entry, list);
if (sctp_has_association(&addr->a, paddr)) {
sctp_read_unlock(&ep->base.addr_lock);
/* This function is called with the socket lock held,
* so the address_list can not change.
*/
list_for_each_entry(addr, &bp->address_list, list) {
if (sctp_has_association(&addr->a, paddr))
return 1;
}
}
sctp_read_unlock(&ep->base.addr_lock);

return 0;
}
Expand Down
12 changes: 5 additions & 7 deletions net/sctp/ipv6.c
Original file line number Diff line number Diff line change
Expand Up @@ -302,9 +302,7 @@ static void sctp_v6_get_saddr(struct sctp_association *asoc,
union sctp_addr *saddr)
{
struct sctp_bind_addr *bp;
rwlock_t *addr_lock;
struct sctp_sockaddr_entry *laddr;
struct list_head *pos;
sctp_scope_t scope;
union sctp_addr *baddr = NULL;
__u8 matchlen = 0;
Expand All @@ -324,14 +322,14 @@ static void sctp_v6_get_saddr(struct sctp_association *asoc,
scope = sctp_scope(daddr);

bp = &asoc->base.bind_addr;
addr_lock = &asoc->base.addr_lock;

/* Go through the bind address list and find the best source address
* that matches the scope of the destination address.
*/
sctp_read_lock(addr_lock);
list_for_each(pos, &bp->address_list) {
laddr = list_entry(pos, struct sctp_sockaddr_entry, list);
rcu_read_lock();
list_for_each_entry_rcu(laddr, &bp->address_list, list) {
if (!laddr->valid)
continue;
if ((laddr->use_as_src) &&
(laddr->a.sa.sa_family == AF_INET6) &&
(scope <= sctp_scope(&laddr->a))) {
Expand All @@ -353,7 +351,7 @@ static void sctp_v6_get_saddr(struct sctp_association *asoc,
__FUNCTION__, asoc, NIP6(daddr->v6.sin6_addr));
}

sctp_read_unlock(addr_lock);
rcu_read_unlock();
}

/* Make a copy of all potential local addresses. */
Expand Down
25 changes: 10 additions & 15 deletions net/sctp/protocol.c
Original file line number Diff line number Diff line change
Expand Up @@ -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_PEERSUPP)))) {
error = sctp_add_bind_addr(bp, &addr->a, 1,
GFP_ATOMIC);
GFP_ATOMIC);
if (error)
goto end_copy;
}
Expand Down Expand Up @@ -428,9 +428,7 @@ static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc,
struct rtable *rt;
struct flowi fl;
struct sctp_bind_addr *bp;
rwlock_t *addr_lock;
struct sctp_sockaddr_entry *laddr;
struct list_head *pos;
struct dst_entry *dst = NULL;
union sctp_addr dst_saddr;

Expand Down Expand Up @@ -459,23 +457,20 @@ static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc,
goto out;

bp = &asoc->base.bind_addr;
addr_lock = &asoc->base.addr_lock;

if (dst) {
/* Walk through the bind address list and look for a bind
* address that matches the source address of the returned dst.
*/
sctp_read_lock(addr_lock);
list_for_each(pos, &bp->address_list) {
laddr = list_entry(pos, struct sctp_sockaddr_entry,
list);
if (!laddr->use_as_src)
rcu_read_lock();
list_for_each_entry_rcu(laddr, &bp->address_list, list) {
if (!laddr->valid || !laddr->use_as_src)
continue;
sctp_v4_dst_saddr(&dst_saddr, dst, htons(bp->port));
if (sctp_v4_cmp_addr(&dst_saddr, &laddr->a))
goto out_unlock;
}
sctp_read_unlock(addr_lock);
rcu_read_unlock();

/* None of the bound addresses match the source address of the
* dst. So release it.
Expand All @@ -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
* matches a bind address as the source address.
*/
sctp_read_lock(addr_lock);
list_for_each(pos, &bp->address_list) {
laddr = list_entry(pos, struct sctp_sockaddr_entry, list);

rcu_read_lock();
list_for_each_entry_rcu(laddr, &bp->address_list, list) {
if (!laddr->valid)
continue;
if ((laddr->use_as_src) &&
(AF_INET == laddr->a.sa.sa_family)) {
fl.fl4_src = laddr->a.v4.sin_addr.s_addr;
Expand All @@ -502,7 +497,7 @@ static struct dst_entry *sctp_v4_get_dst(struct sctp_association *asoc,
}

out_unlock:
sctp_read_unlock(addr_lock);
rcu_read_unlock();
out:
if (dst)
SCTP_DEBUG_PRINTK("rt_dst:%u.%u.%u.%u, rt_src:%u.%u.%u.%u\n",
Expand Down
18 changes: 6 additions & 12 deletions net/sctp/sm_make_chunk.c
Original file line number Diff line number Diff line change
Expand Up @@ -1531,7 +1531,7 @@ struct sctp_association *sctp_unpack_cookie(
/* Also, add the destination address. */
if (list_empty(&retval->base.bind_addr.address_list)) {
sctp_add_bind_addr(&retval->base.bind_addr, &chunk->dest, 1,
GFP_ATOMIC);
GFP_ATOMIC);
}

retval->next_tsn = retval->c.initial_tsn;
Expand Down Expand Up @@ -2613,22 +2613,16 @@ static int sctp_asconf_param_success(struct sctp_association *asoc,

switch (asconf_param->param_hdr.type) {
case SCTP_PARAM_ADD_IP:
sctp_local_bh_disable();
sctp_write_lock(&asoc->base.addr_lock);
list_for_each(pos, &bp->address_list) {
saddr = list_entry(pos, struct sctp_sockaddr_entry, list);
/* This is always done in BH context with a socket lock
* held, so the list can not change.
*/
list_for_each_entry(saddr, &bp->address_list, list) {
if (sctp_cmp_addr_exact(&saddr->a, &addr))
saddr->use_as_src = 1;
}
sctp_write_unlock(&asoc->base.addr_lock);
sctp_local_bh_enable();
break;
case SCTP_PARAM_DEL_IP:
sctp_local_bh_disable();
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();
retval = sctp_del_bind_addr(bp, &addr, call_rcu_bh);
list_for_each(pos, &asoc->peer.transport_addr_list) {
transport = list_entry(pos, struct sctp_transport,
transports);
Expand Down
Loading

0 comments on commit 559cf71

Please sign in to comment.