diff options
author | Sridhar Samudrala <sri@us.ibm.com> | 2006-12-13 16:26:26 -0800 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2006-12-13 16:48:27 -0800 |
commit | 29c7cf96186ac14ce7380633f690fc39732ff03a (patch) | |
tree | ccc95adc0e1185469e77a1adcae1d300d0b534d1 /net/sctp/ipv6.c | |
parent | 6931ba7cef3991fbb970997d33e24139ccdc3c2c (diff) | |
download | linux-3.10-29c7cf96186ac14ce7380633f690fc39732ff03a.tar.gz linux-3.10-29c7cf96186ac14ce7380633f690fc39732ff03a.tar.bz2 linux-3.10-29c7cf96186ac14ce7380633f690fc39732ff03a.zip |
[SCTP]: Handle address add/delete events in a more efficient way.
Currently in SCTP, we maintain a local address list by rebuilding the whole
list from the device list whenever we get a address add/delete event.
This patch fixes it by only adding/deleting the address for which we
receive the event.
Also removed the sctp_local_addr_lock() which is no longer needed as we
now use list_for_each_safe() to traverse this list. This fixes the bugs
in sctp_copy_laddrs_xxx() routines where we do copy_to_user() while
holding this lock.
Signed-off-by: Sridhar Samudrala <sri@us.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/sctp/ipv6.c')
-rw-r--r-- | net/sctp/ipv6.c | 38 |
1 files changed, 37 insertions, 1 deletions
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index 3c3e560087c..d8d36dee5ab 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -78,8 +78,44 @@ #include <asm/uaccess.h> +/* Event handler for inet6 address addition/deletion events. */ +int sctp_inet6addr_event(struct notifier_block *this, unsigned long ev, + void *ptr) +{ + struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr; + struct sctp_sockaddr_entry *addr; + struct list_head *pos, *temp; + + switch (ev) { + case NETDEV_UP: + addr = kmalloc(sizeof(struct sctp_sockaddr_entry), GFP_ATOMIC); + if (addr) { + addr->a.v6.sin6_family = AF_INET6; + addr->a.v6.sin6_port = 0; + memcpy(&addr->a.v6.sin6_addr, &ifa->addr, + sizeof(struct in6_addr)); + addr->a.v6.sin6_scope_id = ifa->idev->dev->ifindex; + list_add_tail(&addr->list, &sctp_local_addr_list); + } + break; + case NETDEV_DOWN: + list_for_each_safe(pos, temp, &sctp_local_addr_list) { + addr = list_entry(pos, struct sctp_sockaddr_entry, list); + if (ipv6_addr_equal(&addr->a.v6.sin6_addr, &ifa->addr)) { + list_del(pos); + kfree(addr); + break; + } + } + + break; + } + + return NOTIFY_DONE; +} + static struct notifier_block sctp_inet6addr_notifier = { - .notifier_call = sctp_inetaddr_event, + .notifier_call = sctp_inet6addr_event, }; /* ICMP error handler. */ |