aboutsummaryrefslogtreecommitdiffstats
path: root/net/sctp
diff options
context:
space:
mode:
authorSridhar Samudrala <sri@us.ibm.com>2006-12-13 19:26:26 -0500
committerDavid S. Miller <davem@sunset.davemloft.net>2006-12-13 19:48:27 -0500
commit29c7cf96186ac14ce7380633f690fc39732ff03a (patch)
treeccc95adc0e1185469e77a1adcae1d300d0b534d1 /net/sctp
parent6931ba7cef3991fbb970997d33e24139ccdc3c2c (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>
Diffstat (limited to 'net/sctp')
-rw-r--r--net/sctp/ipv6.c38
-rw-r--r--net/sctp/protocol.c69
-rw-r--r--net/sctp/socket.c34
3 files changed, 79 insertions, 62 deletions
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. */
82int 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
81static struct notifier_block sctp_inet6addr_notifier = { 117static 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 */
166static void __sctp_get_local_addr_list(void) 166static 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
182static 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. */
192static void __sctp_free_local_addr_list(void) 183static 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. */
205static 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'. */
215int sctp_copy_local_addr_list(struct sctp_bind_addr *bp, sctp_scope_t scope, 196int 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
244end_copy: 223end_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 */
628int sctp_inetaddr_event(struct notifier_block *this, unsigned long ev, 604int 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;
1184out: 1179out:
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:
3892static int sctp_copy_laddrs_to_user_old(struct sock *sk, __u16 port, int max_addrs, 3888static 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
3926static int sctp_copy_laddrs_to_user(struct sock *sk, __u16 port, 3917static 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}