diff options
author | Florian Zumbiehl <florz@florz.de> | 2007-04-20 19:57:27 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-04-26 01:29:19 -0400 |
commit | 74b885cf86def9bc836772e3c1788c00b72a35c9 (patch) | |
tree | 2397bcd487363cdda290c71856af59f0e6cf2ffe /drivers/net | |
parent | bfafb26e11849fe99e03cc1902a91f7f65354e47 (diff) |
[PPPOE]: race between interface going down and connect()
below you find a patch that (hopefully) fixes a race between an interface
going down and a connect() to a peer on that interface. Before,
connect() would determine that an interface is up, then the interface
could go down and all entries referring to that interface in the
item_hash_table would be marked as ZOMBIEs and their references to
the device would be freed, and after that, connect() would put a new
entry into the hash table referring to the device that meanwhile is
down already - which also would cause unregister_netdevice() to wait
until the socket has been release()d.
This patch does not suffice if we are not allowed to accept connect()s
referring to a device that we already acked a NETDEV_GOING_DOWN for
(that is: all references are only guaranteed to be freed after
NETDEV_DOWN has been acknowledged, not necessarily after the
NETDEV_GOING_DOWN already). And if we are allowed to, we could avoid
looking through the hash table upon NETDEV_GOING_DOWN completely and
only do that once we get the NETDEV_DOWN ...
mostrows:
pppoe_flush_dev is called on NETDEV_GOING_DOWN and NETDEV_DOWN to deal with
this "late connect" issue. Ideally one would hope to notify users at the
"NETDEV_GOING_DOWN" phase (just to pretend to be nice). However, it is the
NETDEV_DOWN scan that takes all the responsibility for ensuring nobody is
hanging around at that time.
Signed-off-by: Florian Zumbiehl <florz@florz.de>
Acked-by: Michal Ostrowski <mostrows@earthlink.net>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/pppoe.c | 19 |
1 files changed, 6 insertions, 13 deletions
diff --git a/drivers/net/pppoe.c b/drivers/net/pppoe.c index f761a9aae4c8..bc4fc30bc85e 100644 --- a/drivers/net/pppoe.c +++ b/drivers/net/pppoe.c | |||
@@ -218,17 +218,6 @@ static inline struct pppox_sock *get_item_by_addr(struct sockaddr_pppox *sp) | |||
218 | return get_item(sp->sa_addr.pppoe.sid, sp->sa_addr.pppoe.remote, ifindex); | 218 | return get_item(sp->sa_addr.pppoe.sid, sp->sa_addr.pppoe.remote, ifindex); |
219 | } | 219 | } |
220 | 220 | ||
221 | static inline int set_item(struct pppox_sock *po) | ||
222 | { | ||
223 | int i; | ||
224 | |||
225 | write_lock_bh(&pppoe_hash_lock); | ||
226 | i = __set_item(po); | ||
227 | write_unlock_bh(&pppoe_hash_lock); | ||
228 | |||
229 | return i; | ||
230 | } | ||
231 | |||
232 | static inline struct pppox_sock *delete_item(unsigned long sid, char *addr, int ifindex) | 221 | static inline struct pppox_sock *delete_item(unsigned long sid, char *addr, int ifindex) |
233 | { | 222 | { |
234 | struct pppox_sock *ret; | 223 | struct pppox_sock *ret; |
@@ -595,14 +584,18 @@ static int pppoe_connect(struct socket *sock, struct sockaddr *uservaddr, | |||
595 | po->pppoe_dev = dev; | 584 | po->pppoe_dev = dev; |
596 | po->pppoe_ifindex = dev->ifindex; | 585 | po->pppoe_ifindex = dev->ifindex; |
597 | 586 | ||
598 | if (!(dev->flags & IFF_UP)) | 587 | write_lock_bh(&pppoe_hash_lock); |
588 | if (!(dev->flags & IFF_UP)){ | ||
589 | write_unlock_bh(&pppoe_hash_lock); | ||
599 | goto err_put; | 590 | goto err_put; |
591 | } | ||
600 | 592 | ||
601 | memcpy(&po->pppoe_pa, | 593 | memcpy(&po->pppoe_pa, |
602 | &sp->sa_addr.pppoe, | 594 | &sp->sa_addr.pppoe, |
603 | sizeof(struct pppoe_addr)); | 595 | sizeof(struct pppoe_addr)); |
604 | 596 | ||
605 | error = set_item(po); | 597 | error = __set_item(po); |
598 | write_unlock_bh(&pppoe_hash_lock); | ||
606 | if (error < 0) | 599 | if (error < 0) |
607 | goto err_put; | 600 | goto err_put; |
608 | 601 | ||