diff options
| author | Sridhar Samudrala <sri@us.ibm.com> | 2006-12-13 19:26:26 -0500 |
|---|---|---|
| committer | David S. Miller <davem@sunset.davemloft.net> | 2006-12-13 19:48:27 -0500 |
| commit | 29c7cf96186ac14ce7380633f690fc39732ff03a (patch) | |
| tree | ccc95adc0e1185469e77a1adcae1d300d0b534d1 | |
| parent | 6931ba7cef3991fbb970997d33e24139ccdc3c2c (diff) | |
[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>
| -rw-r--r-- | include/net/sctp/structs.h | 6 | ||||
| -rw-r--r-- | net/sctp/ipv6.c | 38 | ||||
| -rw-r--r-- | net/sctp/protocol.c | 69 | ||||
| -rw-r--r-- | net/sctp/socket.c | 34 |
4 files changed, 81 insertions, 66 deletions
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index c089f93ba591..b00d85e14fe1 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h | |||
| @@ -201,13 +201,12 @@ extern struct sctp_globals { | |||
| 201 | struct sctp_bind_hashbucket *port_hashtable; | 201 | struct sctp_bind_hashbucket *port_hashtable; |
| 202 | 202 | ||
| 203 | /* This is the global local address list. | 203 | /* This is the global local address list. |
| 204 | * We actively maintain this complete list of interfaces on | 204 | * We actively maintain this complete list of addresses on |
| 205 | * the system by catching routing events. | 205 | * the system by catching address add/delete events. |
| 206 | * | 206 | * |
| 207 | * It is a list of sctp_sockaddr_entry. | 207 | * It is a list of sctp_sockaddr_entry. |
| 208 | */ | 208 | */ |
| 209 | struct list_head local_addr_list; | 209 | struct list_head local_addr_list; |
| 210 | spinlock_t local_addr_lock; | ||
| 211 | 210 | ||
| 212 | /* Flag to indicate if addip is enabled. */ | 211 | /* Flag to indicate if addip is enabled. */ |
| 213 | int addip_enable; | 212 | int addip_enable; |
| @@ -243,7 +242,6 @@ extern struct sctp_globals { | |||
| 243 | #define sctp_port_alloc_lock (sctp_globals.port_alloc_lock) | 242 | #define sctp_port_alloc_lock (sctp_globals.port_alloc_lock) |
| 244 | #define sctp_port_hashtable (sctp_globals.port_hashtable) | 243 | #define sctp_port_hashtable (sctp_globals.port_hashtable) |
| 245 | #define sctp_local_addr_list (sctp_globals.local_addr_list) | 244 | #define sctp_local_addr_list (sctp_globals.local_addr_list) |
| 246 | #define sctp_local_addr_lock (sctp_globals.local_addr_lock) | ||
| 247 | #define sctp_addip_enable (sctp_globals.addip_enable) | 245 | #define sctp_addip_enable (sctp_globals.addip_enable) |
| 248 | #define sctp_prsctp_enable (sctp_globals.prsctp_enable) | 246 | #define sctp_prsctp_enable (sctp_globals.prsctp_enable) |
| 249 | 247 | ||
diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index 3c3e560087ca..d8d36dee5ab6 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c | |||
| @@ -78,8 +78,44 @@ | |||
| 78 | 78 | ||
| 79 | #include <asm/uaccess.h> | 79 | #include <asm/uaccess.h> |
| 80 | 80 | ||
| 81 | /* Event handler for inet6 address addition/deletion events. */ | ||
| 82 | int sctp_inet6addr_event(struct notifier_block *this, unsigned long ev, | ||
| 83 | void *ptr) | ||
| 84 | { | ||
| 85 | struct inet6_ifaddr *ifa = (struct inet6_ifaddr *)ptr; | ||
| 86 | struct sctp_sockaddr_entry *addr; | ||
| 87 | struct list_head *pos, *temp; | ||
| 88 | |||
| 89 | switch (ev) { | ||
| 90 | case NETDEV_UP: | ||
| 91 | addr = kmalloc(sizeof(struct sctp_sockaddr_entry), GFP_ATOMIC); | ||
| 92 | if (addr) { | ||
| 93 | addr->a.v6.sin6_family = AF_INET6; | ||
| 94 | addr->a.v6.sin6_port = 0; | ||
| 95 | memcpy(&addr->a.v6.sin6_addr, &ifa->addr, | ||
| 96 | sizeof(struct in6_addr)); | ||
| 97 | addr->a.v6.sin6_scope_id = ifa->idev->dev->ifindex; | ||
| 98 | list_add_tail(&addr->list, &sctp_local_addr_list); | ||
| 99 | } | ||
| 100 | break; | ||
| 101 | case NETDEV_DOWN: | ||
| 102 | list_for_each_safe(pos, temp, &sctp_local_addr_list) { | ||
| 103 | addr = list_entry(pos, struct sctp_sockaddr_entry, list); | ||
| 104 | if (ipv6_addr_equal(&addr->a.v6.sin6_addr, &ifa->addr)) { | ||
| 105 | list_del(pos); | ||
| 106 | kfree(addr); | ||
| 107 | break; | ||
| 108 | } | ||
| 109 | } | ||
| 110 | |||
| 111 | break; | ||
| 112 | } | ||
| 113 | |||
| 114 | return NOTIFY_DONE; | ||
| 115 | } | ||
| 116 | |||
| 81 | static struct notifier_block sctp_inet6addr_notifier = { | 117 | static struct notifier_block sctp_inet6addr_notifier = { |
| 82 | .notifier_call = sctp_inetaddr_event, | 118 | .notifier_call = sctp_inet6addr_event, |
| 83 | }; | 119 | }; |
| 84 | 120 | ||
| 85 | /* ICMP error handler. */ | 121 | /* ICMP error handler. */ |
diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index f2ba8615895b..b61f3341e0a2 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c | |||
| @@ -163,7 +163,7 @@ static void sctp_v4_copy_addrlist(struct list_head *addrlist, | |||
| 163 | /* Extract our IP addresses from the system and stash them in the | 163 | /* Extract our IP addresses from the system and stash them in the |
| 164 | * protocol structure. | 164 | * protocol structure. |
| 165 | */ | 165 | */ |
| 166 | static void __sctp_get_local_addr_list(void) | 166 | static void sctp_get_local_addr_list(void) |
| 167 | { | 167 | { |
| 168 | struct net_device *dev; | 168 | struct net_device *dev; |
| 169 | struct list_head *pos; | 169 | struct list_head *pos; |
| @@ -179,17 +179,8 @@ static void __sctp_get_local_addr_list(void) | |||
| 179 | read_unlock(&dev_base_lock); | 179 | read_unlock(&dev_base_lock); |
| 180 | } | 180 | } |
| 181 | 181 | ||
| 182 | static void sctp_get_local_addr_list(void) | ||
| 183 | { | ||
| 184 | unsigned long flags; | ||
| 185 | |||
| 186 | sctp_spin_lock_irqsave(&sctp_local_addr_lock, flags); | ||
| 187 | __sctp_get_local_addr_list(); | ||
| 188 | sctp_spin_unlock_irqrestore(&sctp_local_addr_lock, flags); | ||
| 189 | } | ||
| 190 | |||
| 191 | /* Free the existing local addresses. */ | 182 | /* Free the existing local addresses. */ |
| 192 | static void __sctp_free_local_addr_list(void) | 183 | static void sctp_free_local_addr_list(void) |
| 193 | { | 184 | { |
| 194 | struct sctp_sockaddr_entry *addr; | 185 | struct sctp_sockaddr_entry *addr; |
| 195 | struct list_head *pos, *temp; | 186 | struct list_head *pos, *temp; |
| @@ -201,27 +192,15 @@ static void __sctp_free_local_addr_list(void) | |||
| 201 | } | 192 | } |
| 202 | } | 193 | } |
| 203 | 194 | ||
| 204 | /* Free the existing local addresses. */ | ||
| 205 | static void sctp_free_local_addr_list(void) | ||
| 206 | { | ||
| 207 | unsigned long flags; | ||
| 208 | |||
| 209 | sctp_spin_lock_irqsave(&sctp_local_addr_lock, flags); | ||
| 210 | __sctp_free_local_addr_list(); | ||
| 211 | sctp_spin_unlock_irqrestore(&sctp_local_addr_lock, flags); | ||
| 212 | } | ||
| 213 | |||
| 214 | /* Copy the local addresses which are valid for 'scope' into 'bp'. */ | 195 | /* Copy the local addresses which are valid for 'scope' into 'bp'. */ |
| 215 | int sctp_copy_local_addr_list(struct sctp_bind_addr *bp, sctp_scope_t scope, | 196 | int sctp_copy_local_addr_list(struct sctp_bind_addr *bp, sctp_scope_t scope, |
| 216 | gfp_t gfp, int copy_flags) | 197 | gfp_t gfp, int copy_flags) |
| 217 | { | 198 | { |
| 218 | struct sctp_sockaddr_entry *addr; | 199 | struct sctp_sockaddr_entry *addr; |
| 219 | int error = 0; | 200 | int error = 0; |
| 220 | struct list_head *pos; | 201 | struct list_head *pos, *temp; |
| 221 | unsigned long flags; | ||
| 222 | 202 | ||
| 223 | sctp_spin_lock_irqsave(&sctp_local_addr_lock, flags); | 203 | list_for_each_safe(pos, temp, &sctp_local_addr_list) { |
| 224 | list_for_each(pos, &sctp_local_addr_list) { | ||
| 225 | addr = list_entry(pos, struct sctp_sockaddr_entry, list); | 204 | addr = list_entry(pos, struct sctp_sockaddr_entry, list); |
| 226 | if (sctp_in_scope(&addr->a, scope)) { | 205 | if (sctp_in_scope(&addr->a, scope)) { |
| 227 | /* Now that the address is in scope, check to see if | 206 | /* Now that the address is in scope, check to see if |
| @@ -242,7 +221,6 @@ int sctp_copy_local_addr_list(struct sctp_bind_addr *bp, sctp_scope_t scope, | |||
| 242 | } | 221 | } |
| 243 | 222 | ||
| 244 | end_copy: | 223 | end_copy: |
| 245 | sctp_spin_unlock_irqrestore(&sctp_local_addr_lock, flags); | ||
| 246 | return error; | 224 | return error; |
| 247 | } | 225 | } |
| 248 | 226 | ||
| @@ -622,18 +600,36 @@ static void sctp_v4_seq_dump_addr(struct seq_file *seq, union sctp_addr *addr) | |||
| 622 | seq_printf(seq, "%d.%d.%d.%d ", NIPQUAD(addr->v4.sin_addr)); | 600 | seq_printf(seq, "%d.%d.%d.%d ", NIPQUAD(addr->v4.sin_addr)); |
| 623 | } | 601 | } |
| 624 | 602 | ||
| 625 | /* Event handler for inet address addition/deletion events. | 603 | /* Event handler for inet address addition/deletion events. */ |
| 626 | * Basically, whenever there is an event, we re-build our local address list. | ||
| 627 | */ | ||
| 628 | int sctp_inetaddr_event(struct notifier_block *this, unsigned long ev, | 604 | int sctp_inetaddr_event(struct notifier_block *this, unsigned long ev, |
| 629 | void *ptr) | 605 | void *ptr) |
| 630 | { | 606 | { |
| 631 | unsigned long flags; | 607 | struct in_ifaddr *ifa = (struct in_ifaddr *)ptr; |
| 608 | struct sctp_sockaddr_entry *addr; | ||
| 609 | struct list_head *pos, *temp; | ||
| 632 | 610 | ||
| 633 | sctp_spin_lock_irqsave(&sctp_local_addr_lock, flags); | 611 | switch (ev) { |
| 634 | __sctp_free_local_addr_list(); | 612 | case NETDEV_UP: |
| 635 | __sctp_get_local_addr_list(); | 613 | addr = kmalloc(sizeof(struct sctp_sockaddr_entry), GFP_ATOMIC); |
| 636 | sctp_spin_unlock_irqrestore(&sctp_local_addr_lock, flags); | 614 | if (addr) { |
| 615 | addr->a.v4.sin_family = AF_INET; | ||
| 616 | addr->a.v4.sin_port = 0; | ||
| 617 | addr->a.v4.sin_addr.s_addr = ifa->ifa_local; | ||
| 618 | list_add_tail(&addr->list, &sctp_local_addr_list); | ||
| 619 | } | ||
| 620 | break; | ||
| 621 | case NETDEV_DOWN: | ||
| 622 | list_for_each_safe(pos, temp, &sctp_local_addr_list) { | ||
| 623 | addr = list_entry(pos, struct sctp_sockaddr_entry, list); | ||
| 624 | if (addr->a.v4.sin_addr.s_addr == ifa->ifa_local) { | ||
| 625 | list_del(pos); | ||
| 626 | kfree(addr); | ||
| 627 | break; | ||
| 628 | } | ||
| 629 | } | ||
| 630 | |||
| 631 | break; | ||
| 632 | } | ||
| 637 | 633 | ||
| 638 | return NOTIFY_DONE; | 634 | return NOTIFY_DONE; |
| 639 | } | 635 | } |
| @@ -1172,13 +1168,12 @@ SCTP_STATIC __init int sctp_init(void) | |||
| 1172 | 1168 | ||
| 1173 | /* Initialize the local address list. */ | 1169 | /* Initialize the local address list. */ |
| 1174 | INIT_LIST_HEAD(&sctp_local_addr_list); | 1170 | INIT_LIST_HEAD(&sctp_local_addr_list); |
| 1175 | spin_lock_init(&sctp_local_addr_lock); | 1171 | |
| 1172 | sctp_get_local_addr_list(); | ||
| 1176 | 1173 | ||
| 1177 | /* Register notifier for inet address additions/deletions. */ | 1174 | /* Register notifier for inet address additions/deletions. */ |
| 1178 | register_inetaddr_notifier(&sctp_inetaddr_notifier); | 1175 | register_inetaddr_notifier(&sctp_inetaddr_notifier); |
| 1179 | 1176 | ||
| 1180 | sctp_get_local_addr_list(); | ||
| 1181 | |||
| 1182 | __unsafe(THIS_MODULE); | 1177 | __unsafe(THIS_MODULE); |
| 1183 | status = 0; | 1178 | status = 0; |
| 1184 | out: | 1179 | out: |
diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 1e8132b8c4d9..fa29eae83e91 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c | |||
| @@ -3821,10 +3821,9 @@ static int sctp_getsockopt_local_addrs_num_old(struct sock *sk, int len, | |||
| 3821 | sctp_assoc_t id; | 3821 | sctp_assoc_t id; |
| 3822 | struct sctp_bind_addr *bp; | 3822 | struct sctp_bind_addr *bp; |
| 3823 | struct sctp_association *asoc; | 3823 | struct sctp_association *asoc; |
| 3824 | struct list_head *pos; | 3824 | struct list_head *pos, *temp; |
| 3825 | struct sctp_sockaddr_entry *addr; | 3825 | struct sctp_sockaddr_entry *addr; |
| 3826 | rwlock_t *addr_lock; | 3826 | rwlock_t *addr_lock; |
| 3827 | unsigned long flags; | ||
| 3828 | int cnt = 0; | 3827 | int cnt = 0; |
| 3829 | 3828 | ||
| 3830 | if (len != sizeof(sctp_assoc_t)) | 3829 | if (len != sizeof(sctp_assoc_t)) |
| @@ -3859,8 +3858,7 @@ static int sctp_getsockopt_local_addrs_num_old(struct sock *sk, int len, | |||
| 3859 | addr = list_entry(bp->address_list.next, | 3858 | addr = list_entry(bp->address_list.next, |
| 3860 | struct sctp_sockaddr_entry, list); | 3859 | struct sctp_sockaddr_entry, list); |
| 3861 | if (sctp_is_any(&addr->a)) { | 3860 | if (sctp_is_any(&addr->a)) { |
| 3862 | sctp_spin_lock_irqsave(&sctp_local_addr_lock, flags); | 3861 | list_for_each_safe(pos, temp, &sctp_local_addr_list) { |
| 3863 | list_for_each(pos, &sctp_local_addr_list) { | ||
| 3864 | addr = list_entry(pos, | 3862 | addr = list_entry(pos, |
| 3865 | struct sctp_sockaddr_entry, | 3863 | struct sctp_sockaddr_entry, |
| 3866 | list); | 3864 | list); |
| @@ -3869,8 +3867,6 @@ static int sctp_getsockopt_local_addrs_num_old(struct sock *sk, int len, | |||
| 3869 | continue; | 3867 | continue; |
| 3870 | cnt++; | 3868 | cnt++; |
| 3871 | } | 3869 | } |
| 3872 | sctp_spin_unlock_irqrestore(&sctp_local_addr_lock, | ||
| 3873 | flags); | ||
| 3874 | } else { | 3870 | } else { |
| 3875 | cnt = 1; | 3871 | cnt = 1; |
| 3876 | } | 3872 | } |
| @@ -3892,15 +3888,13 @@ done: | |||
| 3892 | static int sctp_copy_laddrs_to_user_old(struct sock *sk, __u16 port, int max_addrs, | 3888 | static int sctp_copy_laddrs_to_user_old(struct sock *sk, __u16 port, int max_addrs, |
| 3893 | void __user *to) | 3889 | void __user *to) |
| 3894 | { | 3890 | { |
| 3895 | struct list_head *pos; | 3891 | struct list_head *pos, *next; |
| 3896 | struct sctp_sockaddr_entry *addr; | 3892 | struct sctp_sockaddr_entry *addr; |
| 3897 | unsigned long flags; | ||
| 3898 | union sctp_addr temp; | 3893 | union sctp_addr temp; |
| 3899 | int cnt = 0; | 3894 | int cnt = 0; |
| 3900 | int addrlen; | 3895 | int addrlen; |
| 3901 | 3896 | ||
| 3902 | sctp_spin_lock_irqsave(&sctp_local_addr_lock, flags); | 3897 | list_for_each_safe(pos, next, &sctp_local_addr_list) { |
| 3903 | list_for_each(pos, &sctp_local_addr_list) { | ||
| 3904 | addr = list_entry(pos, struct sctp_sockaddr_entry, list); | 3898 | addr = list_entry(pos, struct sctp_sockaddr_entry, list); |
| 3905 | if ((PF_INET == sk->sk_family) && | 3899 | if ((PF_INET == sk->sk_family) && |
| 3906 | (AF_INET6 == addr->a.sa.sa_family)) | 3900 | (AF_INET6 == addr->a.sa.sa_family)) |
| @@ -3909,16 +3903,13 @@ static int sctp_copy_laddrs_to_user_old(struct sock *sk, __u16 port, int max_add | |||
| 3909 | sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk), | 3903 | sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk), |
| 3910 | &temp); | 3904 | &temp); |
| 3911 | addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len; | 3905 | addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len; |
| 3912 | if (copy_to_user(to, &temp, addrlen)) { | 3906 | if (copy_to_user(to, &temp, addrlen)) |
| 3913 | sctp_spin_unlock_irqrestore(&sctp_local_addr_lock, | ||
| 3914 | flags); | ||
| 3915 | return -EFAULT; | 3907 | return -EFAULT; |
| 3916 | } | 3908 | |
| 3917 | to += addrlen; | 3909 | to += addrlen; |
| 3918 | cnt ++; | 3910 | cnt ++; |
| 3919 | if (cnt >= max_addrs) break; | 3911 | if (cnt >= max_addrs) break; |
| 3920 | } | 3912 | } |
| 3921 | sctp_spin_unlock_irqrestore(&sctp_local_addr_lock, flags); | ||
| 3922 | 3913 | ||
| 3923 | return cnt; | 3914 | return cnt; |
| 3924 | } | 3915 | } |
| @@ -3926,15 +3917,13 @@ static int sctp_copy_laddrs_to_user_old(struct sock *sk, __u16 port, int max_add | |||
| 3926 | static int sctp_copy_laddrs_to_user(struct sock *sk, __u16 port, | 3917 | static int sctp_copy_laddrs_to_user(struct sock *sk, __u16 port, |
| 3927 | void __user **to, size_t space_left) | 3918 | void __user **to, size_t space_left) |
| 3928 | { | 3919 | { |
| 3929 | struct list_head *pos; | 3920 | struct list_head *pos, *next; |
| 3930 | struct sctp_sockaddr_entry *addr; | 3921 | struct sctp_sockaddr_entry *addr; |
| 3931 | unsigned long flags; | ||
| 3932 | union sctp_addr temp; | 3922 | union sctp_addr temp; |
| 3933 | int cnt = 0; | 3923 | int cnt = 0; |
| 3934 | int addrlen; | 3924 | int addrlen; |
| 3935 | 3925 | ||
| 3936 | sctp_spin_lock_irqsave(&sctp_local_addr_lock, flags); | 3926 | list_for_each_safe(pos, next, &sctp_local_addr_list) { |
| 3937 | list_for_each(pos, &sctp_local_addr_list) { | ||
| 3938 | addr = list_entry(pos, struct sctp_sockaddr_entry, list); | 3927 | addr = list_entry(pos, struct sctp_sockaddr_entry, list); |
| 3939 | if ((PF_INET == sk->sk_family) && | 3928 | if ((PF_INET == sk->sk_family) && |
| 3940 | (AF_INET6 == addr->a.sa.sa_family)) | 3929 | (AF_INET6 == addr->a.sa.sa_family)) |
| @@ -3945,16 +3934,13 @@ static int sctp_copy_laddrs_to_user(struct sock *sk, __u16 port, | |||
| 3945 | addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len; | 3934 | addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len; |
| 3946 | if(space_left<addrlen) | 3935 | if(space_left<addrlen) |
| 3947 | return -ENOMEM; | 3936 | return -ENOMEM; |
| 3948 | if (copy_to_user(*to, &temp, addrlen)) { | 3937 | if (copy_to_user(*to, &temp, addrlen)) |
| 3949 | sctp_spin_unlock_irqrestore(&sctp_local_addr_lock, | ||
| 3950 | flags); | ||
| 3951 | return -EFAULT; | 3938 | return -EFAULT; |
| 3952 | } | 3939 | |
| 3953 | *to += addrlen; | 3940 | *to += addrlen; |
| 3954 | cnt ++; | 3941 | cnt ++; |
| 3955 | space_left -= addrlen; | 3942 | space_left -= addrlen; |
| 3956 | } | 3943 | } |
| 3957 | sctp_spin_unlock_irqrestore(&sctp_local_addr_lock, flags); | ||
| 3958 | 3944 | ||
| 3959 | return cnt; | 3945 | return cnt; |
| 3960 | } | 3946 | } |
