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 /net/sctp/protocol.c | |
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>
Diffstat (limited to 'net/sctp/protocol.c')
-rw-r--r-- | net/sctp/protocol.c | 69 |
1 files changed, 32 insertions, 37 deletions
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: |