Commit fc766e4c authored by Eric Dumazet's avatar Eric Dumazet Committed by David S. Miller

decnet: RCU conversion and get rid of dev_base_lock

While tracking dev_base_lock users, I found decnet used it in
dnet_select_source(), but for a wrong purpose:

Writers only hold RTNL, not dev_base_lock, so readers must use RCU if
they cannot use RTNL.

Adds an rcu_head in struct dn_ifaddr and handle proper RCU management.

Adds __rcu annotation in dn_route as well.
Signed-off-by: default avatarEric Dumazet <eric.dumazet@gmail.com>
Acked-by: default avatarSteven Whitehouse <swhiteho@redhat.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent e4a7b93b
...@@ -951,7 +951,7 @@ struct net_device { ...@@ -951,7 +951,7 @@ struct net_device {
#endif #endif
void *atalk_ptr; /* AppleTalk link */ void *atalk_ptr; /* AppleTalk link */
struct in_device __rcu *ip_ptr; /* IPv4 specific data */ struct in_device __rcu *ip_ptr; /* IPv4 specific data */
void *dn_ptr; /* DECnet specific data */ struct dn_dev __rcu *dn_ptr; /* DECnet specific data */
struct inet6_dev __rcu *ip6_ptr; /* IPv6 specific data */ struct inet6_dev __rcu *ip6_ptr; /* IPv6 specific data */
void *ec_ptr; /* Econet specific data */ void *ec_ptr; /* Econet specific data */
void *ax25_ptr; /* AX.25 specific data */ void *ax25_ptr; /* AX.25 specific data */
......
...@@ -5,13 +5,14 @@ ...@@ -5,13 +5,14 @@
struct dn_dev; struct dn_dev;
struct dn_ifaddr { struct dn_ifaddr {
struct dn_ifaddr *ifa_next; struct dn_ifaddr __rcu *ifa_next;
struct dn_dev *ifa_dev; struct dn_dev *ifa_dev;
__le16 ifa_local; __le16 ifa_local;
__le16 ifa_address; __le16 ifa_address;
__u8 ifa_flags; __u8 ifa_flags;
__u8 ifa_scope; __u8 ifa_scope;
char ifa_label[IFNAMSIZ]; char ifa_label[IFNAMSIZ];
struct rcu_head rcu;
}; };
#define DN_DEV_S_RU 0 /* Run - working normally */ #define DN_DEV_S_RU 0 /* Run - working normally */
...@@ -83,7 +84,7 @@ struct dn_dev_parms { ...@@ -83,7 +84,7 @@ struct dn_dev_parms {
struct dn_dev { struct dn_dev {
struct dn_ifaddr *ifa_list; struct dn_ifaddr __rcu *ifa_list;
struct net_device *dev; struct net_device *dev;
struct dn_dev_parms parms; struct dn_dev_parms parms;
char use_long; char use_long;
...@@ -171,19 +172,27 @@ extern int unregister_dnaddr_notifier(struct notifier_block *nb); ...@@ -171,19 +172,27 @@ extern int unregister_dnaddr_notifier(struct notifier_block *nb);
static inline int dn_dev_islocal(struct net_device *dev, __le16 addr) static inline int dn_dev_islocal(struct net_device *dev, __le16 addr)
{ {
struct dn_dev *dn_db = dev->dn_ptr; struct dn_dev *dn_db;
struct dn_ifaddr *ifa; struct dn_ifaddr *ifa;
int res = 0;
rcu_read_lock();
dn_db = rcu_dereference(dev->dn_ptr);
if (dn_db == NULL) { if (dn_db == NULL) {
printk(KERN_DEBUG "dn_dev_islocal: Called for non DECnet device\n"); printk(KERN_DEBUG "dn_dev_islocal: Called for non DECnet device\n");
return 0; goto out;
} }
for(ifa = dn_db->ifa_list; ifa; ifa = ifa->ifa_next) for (ifa = rcu_dereference(dn_db->ifa_list);
if ((addr ^ ifa->ifa_local) == 0) ifa != NULL;
return 1; ifa = rcu_dereference(ifa->ifa_next))
if ((addr ^ ifa->ifa_local) == 0) {
return 0; res = 1;
break;
}
out:
rcu_read_unlock();
return res;
} }
#endif /* _NET_DN_DEV_H */ #endif /* _NET_DN_DEV_H */
...@@ -94,10 +94,10 @@ struct dst_entry { ...@@ -94,10 +94,10 @@ struct dst_entry {
int __use; int __use;
unsigned long lastuse; unsigned long lastuse;
union { union {
struct dst_entry *next; struct dst_entry *next;
struct rtable __rcu *rt_next; struct rtable __rcu *rt_next;
struct rt6_info *rt6_next; struct rt6_info *rt6_next;
struct dn_route *dn_next; struct dn_route __rcu *dn_next;
}; };
}; };
......
...@@ -1848,7 +1848,7 @@ unsigned dn_mss_from_pmtu(struct net_device *dev, int mtu) ...@@ -1848,7 +1848,7 @@ unsigned dn_mss_from_pmtu(struct net_device *dev, int mtu)
{ {
unsigned mss = 230 - DN_MAX_NSP_DATA_HEADER; unsigned mss = 230 - DN_MAX_NSP_DATA_HEADER;
if (dev) { if (dev) {
struct dn_dev *dn_db = dev->dn_ptr; struct dn_dev *dn_db = rcu_dereference_raw(dev->dn_ptr);
mtu -= LL_RESERVED_SPACE(dev); mtu -= LL_RESERVED_SPACE(dev);
if (dn_db->use_long) if (dn_db->use_long)
mtu -= 21; mtu -= 21;
......
This diff is collapsed.
...@@ -610,10 +610,12 @@ static void dn_fib_del_ifaddr(struct dn_ifaddr *ifa) ...@@ -610,10 +610,12 @@ static void dn_fib_del_ifaddr(struct dn_ifaddr *ifa)
/* Scan device list */ /* Scan device list */
rcu_read_lock(); rcu_read_lock();
for_each_netdev_rcu(&init_net, dev) { for_each_netdev_rcu(&init_net, dev) {
dn_db = dev->dn_ptr; dn_db = rcu_dereference(dev->dn_ptr);
if (dn_db == NULL) if (dn_db == NULL)
continue; continue;
for(ifa2 = dn_db->ifa_list; ifa2; ifa2 = ifa2->ifa_next) { for (ifa2 = rcu_dereference(dn_db->ifa_list);
ifa2 != NULL;
ifa2 = rcu_dereference(ifa2->ifa_next)) {
if (ifa2->ifa_local == ifa->ifa_local) { if (ifa2->ifa_local == ifa->ifa_local) {
found_it = 1; found_it = 1;
break; break;
......
...@@ -391,7 +391,7 @@ int dn_neigh_router_hello(struct sk_buff *skb) ...@@ -391,7 +391,7 @@ int dn_neigh_router_hello(struct sk_buff *skb)
write_lock(&neigh->lock); write_lock(&neigh->lock);
neigh->used = jiffies; neigh->used = jiffies;
dn_db = (struct dn_dev *)neigh->dev->dn_ptr; dn_db = rcu_dereference(neigh->dev->dn_ptr);
if (!(neigh->nud_state & NUD_PERMANENT)) { if (!(neigh->nud_state & NUD_PERMANENT)) {
neigh->updated = jiffies; neigh->updated = jiffies;
......
...@@ -93,7 +93,7 @@ ...@@ -93,7 +93,7 @@
struct dn_rt_hash_bucket struct dn_rt_hash_bucket
{ {
struct dn_route *chain; struct dn_route __rcu *chain;
spinlock_t lock; spinlock_t lock;
}; };
...@@ -157,15 +157,17 @@ static inline void dnrt_drop(struct dn_route *rt) ...@@ -157,15 +157,17 @@ static inline void dnrt_drop(struct dn_route *rt)
static void dn_dst_check_expire(unsigned long dummy) static void dn_dst_check_expire(unsigned long dummy)
{ {
int i; int i;
struct dn_route *rt, **rtp; struct dn_route *rt;
struct dn_route __rcu **rtp;
unsigned long now = jiffies; unsigned long now = jiffies;
unsigned long expire = 120 * HZ; unsigned long expire = 120 * HZ;
for(i = 0; i <= dn_rt_hash_mask; i++) { for (i = 0; i <= dn_rt_hash_mask; i++) {
rtp = &dn_rt_hash_table[i].chain; rtp = &dn_rt_hash_table[i].chain;
spin_lock(&dn_rt_hash_table[i].lock); spin_lock(&dn_rt_hash_table[i].lock);
while((rt=*rtp) != NULL) { while ((rt = rcu_dereference_protected(*rtp,
lockdep_is_held(&dn_rt_hash_table[i].lock))) != NULL) {
if (atomic_read(&rt->dst.__refcnt) || if (atomic_read(&rt->dst.__refcnt) ||
(now - rt->dst.lastuse) < expire) { (now - rt->dst.lastuse) < expire) {
rtp = &rt->dst.dn_next; rtp = &rt->dst.dn_next;
...@@ -186,17 +188,19 @@ static void dn_dst_check_expire(unsigned long dummy) ...@@ -186,17 +188,19 @@ static void dn_dst_check_expire(unsigned long dummy)
static int dn_dst_gc(struct dst_ops *ops) static int dn_dst_gc(struct dst_ops *ops)
{ {
struct dn_route *rt, **rtp; struct dn_route *rt;
struct dn_route __rcu **rtp;
int i; int i;
unsigned long now = jiffies; unsigned long now = jiffies;
unsigned long expire = 10 * HZ; unsigned long expire = 10 * HZ;
for(i = 0; i <= dn_rt_hash_mask; i++) { for (i = 0; i <= dn_rt_hash_mask; i++) {
spin_lock_bh(&dn_rt_hash_table[i].lock); spin_lock_bh(&dn_rt_hash_table[i].lock);
rtp = &dn_rt_hash_table[i].chain; rtp = &dn_rt_hash_table[i].chain;
while((rt=*rtp) != NULL) { while ((rt = rcu_dereference_protected(*rtp,
lockdep_is_held(&dn_rt_hash_table[i].lock))) != NULL) {
if (atomic_read(&rt->dst.__refcnt) || if (atomic_read(&rt->dst.__refcnt) ||
(now - rt->dst.lastuse) < expire) { (now - rt->dst.lastuse) < expire) {
rtp = &rt->dst.dn_next; rtp = &rt->dst.dn_next;
...@@ -227,7 +231,7 @@ static void dn_dst_update_pmtu(struct dst_entry *dst, u32 mtu) ...@@ -227,7 +231,7 @@ static void dn_dst_update_pmtu(struct dst_entry *dst, u32 mtu)
{ {
u32 min_mtu = 230; u32 min_mtu = 230;
struct dn_dev *dn = dst->neighbour ? struct dn_dev *dn = dst->neighbour ?
(struct dn_dev *)dst->neighbour->dev->dn_ptr : NULL; rcu_dereference_raw(dst->neighbour->dev->dn_ptr) : NULL;
if (dn && dn->use_long == 0) if (dn && dn->use_long == 0)
min_mtu -= 6; min_mtu -= 6;
...@@ -277,13 +281,15 @@ static inline int compare_keys(struct flowi *fl1, struct flowi *fl2) ...@@ -277,13 +281,15 @@ static inline int compare_keys(struct flowi *fl1, struct flowi *fl2)
static int dn_insert_route(struct dn_route *rt, unsigned hash, struct dn_route **rp) static int dn_insert_route(struct dn_route *rt, unsigned hash, struct dn_route **rp)
{ {
struct dn_route *rth, **rthp; struct dn_route *rth;
struct dn_route __rcu **rthp;
unsigned long now = jiffies; unsigned long now = jiffies;
rthp = &dn_rt_hash_table[hash].chain; rthp = &dn_rt_hash_table[hash].chain;
spin_lock_bh(&dn_rt_hash_table[hash].lock); spin_lock_bh(&dn_rt_hash_table[hash].lock);
while((rth = *rthp) != NULL) { while ((rth = rcu_dereference_protected(*rthp,
lockdep_is_held(&dn_rt_hash_table[hash].lock))) != NULL) {
if (compare_keys(&rth->fl, &rt->fl)) { if (compare_keys(&rth->fl, &rt->fl)) {
/* Put it first */ /* Put it first */
*rthp = rth->dst.dn_next; *rthp = rth->dst.dn_next;
...@@ -315,15 +321,15 @@ static void dn_run_flush(unsigned long dummy) ...@@ -315,15 +321,15 @@ static void dn_run_flush(unsigned long dummy)
int i; int i;
struct dn_route *rt, *next; struct dn_route *rt, *next;
for(i = 0; i < dn_rt_hash_mask; i++) { for (i = 0; i < dn_rt_hash_mask; i++) {
spin_lock_bh(&dn_rt_hash_table[i].lock); spin_lock_bh(&dn_rt_hash_table[i].lock);
if ((rt = xchg(&dn_rt_hash_table[i].chain, NULL)) == NULL) if ((rt = xchg((struct dn_route **)&dn_rt_hash_table[i].chain, NULL)) == NULL)
goto nothing_to_declare; goto nothing_to_declare;
for(; rt; rt=next) { for(; rt; rt = next) {
next = rt->dst.dn_next; next = rcu_dereference_raw(rt->dst.dn_next);
rt->dst.dn_next = NULL; RCU_INIT_POINTER(rt->dst.dn_next, NULL);
dst_free((struct dst_entry *)rt); dst_free((struct dst_entry *)rt);
} }
...@@ -458,15 +464,16 @@ static int dn_return_long(struct sk_buff *skb) ...@@ -458,15 +464,16 @@ static int dn_return_long(struct sk_buff *skb)
*/ */
static int dn_route_rx_packet(struct sk_buff *skb) static int dn_route_rx_packet(struct sk_buff *skb)
{ {
struct dn_skb_cb *cb = DN_SKB_CB(skb); struct dn_skb_cb *cb;
int err; int err;
if ((err = dn_route_input(skb)) == 0) if ((err = dn_route_input(skb)) == 0)
return dst_input(skb); return dst_input(skb);
cb = DN_SKB_CB(skb);
if (decnet_debug_level & 4) { if (decnet_debug_level & 4) {
char *devname = skb->dev ? skb->dev->name : "???"; char *devname = skb->dev ? skb->dev->name : "???";
struct dn_skb_cb *cb = DN_SKB_CB(skb);
printk(KERN_DEBUG printk(KERN_DEBUG
"DECnet: dn_route_rx_packet: rt_flags=0x%02x dev=%s len=%d src=0x%04hx dst=0x%04hx err=%d type=%d\n", "DECnet: dn_route_rx_packet: rt_flags=0x%02x dev=%s len=%d src=0x%04hx dst=0x%04hx err=%d type=%d\n",
(int)cb->rt_flags, devname, skb->len, (int)cb->rt_flags, devname, skb->len,
...@@ -573,7 +580,7 @@ int dn_route_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type ...@@ -573,7 +580,7 @@ int dn_route_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type
struct dn_skb_cb *cb; struct dn_skb_cb *cb;
unsigned char flags = 0; unsigned char flags = 0;
__u16 len = le16_to_cpu(*(__le16 *)skb->data); __u16 len = le16_to_cpu(*(__le16 *)skb->data);
struct dn_dev *dn = (struct dn_dev *)dev->dn_ptr; struct dn_dev *dn = rcu_dereference(dev->dn_ptr);
unsigned char padlen = 0; unsigned char padlen = 0;
if (!net_eq(dev_net(dev), &init_net)) if (!net_eq(dev_net(dev), &init_net))
...@@ -728,7 +735,7 @@ static int dn_forward(struct sk_buff *skb) ...@@ -728,7 +735,7 @@ static int dn_forward(struct sk_buff *skb)
{ {
struct dn_skb_cb *cb = DN_SKB_CB(skb); struct dn_skb_cb *cb = DN_SKB_CB(skb);
struct dst_entry *dst = skb_dst(skb); struct dst_entry *dst = skb_dst(skb);
struct dn_dev *dn_db = dst->dev->dn_ptr; struct dn_dev *dn_db = rcu_dereference(dst->dev->dn_ptr);
struct dn_route *rt; struct dn_route *rt;
struct neighbour *neigh = dst->neighbour; struct neighbour *neigh = dst->neighbour;
int header_len; int header_len;
...@@ -835,13 +842,16 @@ static inline int dn_match_addr(__le16 addr1, __le16 addr2) ...@@ -835,13 +842,16 @@ static inline int dn_match_addr(__le16 addr1, __le16 addr2)
static __le16 dnet_select_source(const struct net_device *dev, __le16 daddr, int scope) static __le16 dnet_select_source(const struct net_device *dev, __le16 daddr, int scope)
{ {
__le16 saddr = 0; __le16 saddr = 0;
struct dn_dev *dn_db = dev->dn_ptr; struct dn_dev *dn_db;
struct dn_ifaddr *ifa; struct dn_ifaddr *ifa;
int best_match = 0; int best_match = 0;
int ret; int ret;
read_lock(&dev_base_lock); rcu_read_lock();
for(ifa = dn_db->ifa_list; ifa; ifa = ifa->ifa_next) { dn_db = rcu_dereference(dev->dn_ptr);
for (ifa = rcu_dereference(dn_db->ifa_list);
ifa != NULL;
ifa = rcu_dereference(ifa->ifa_next)) {
if (ifa->ifa_scope > scope) if (ifa->ifa_scope > scope)
continue; continue;
if (!daddr) { if (!daddr) {
...@@ -854,7 +864,7 @@ static __le16 dnet_select_source(const struct net_device *dev, __le16 daddr, int ...@@ -854,7 +864,7 @@ static __le16 dnet_select_source(const struct net_device *dev, __le16 daddr, int
if (best_match == 0) if (best_match == 0)
saddr = ifa->ifa_local; saddr = ifa->ifa_local;
} }
read_unlock(&dev_base_lock); rcu_read_unlock();
return saddr; return saddr;
} }
...@@ -1020,7 +1030,7 @@ static int dn_route_output_slow(struct dst_entry **pprt, const struct flowi *old ...@@ -1020,7 +1030,7 @@ static int dn_route_output_slow(struct dst_entry **pprt, const struct flowi *old
err = -ENODEV; err = -ENODEV;
if (dev_out == NULL) if (dev_out == NULL)
goto out; goto out;
dn_db = dev_out->dn_ptr; dn_db = rcu_dereference_raw(dev_out->dn_ptr);
/* Possible improvement - check all devices for local addr */ /* Possible improvement - check all devices for local addr */
if (dn_dev_islocal(dev_out, fl.fld_dst)) { if (dn_dev_islocal(dev_out, fl.fld_dst)) {
dev_put(dev_out); dev_put(dev_out);
...@@ -1233,7 +1243,7 @@ static int dn_route_input_slow(struct sk_buff *skb) ...@@ -1233,7 +1243,7 @@ static int dn_route_input_slow(struct sk_buff *skb)
dev_hold(in_dev); dev_hold(in_dev);
if ((dn_db = in_dev->dn_ptr) == NULL) if ((dn_db = rcu_dereference(in_dev->dn_ptr)) == NULL)
goto out; goto out;
/* Zero source addresses are not allowed */ /* Zero source addresses are not allowed */
...@@ -1677,15 +1687,15 @@ static struct dn_route *dn_rt_cache_get_next(struct seq_file *seq, struct dn_rou ...@@ -1677,15 +1687,15 @@ static struct dn_route *dn_rt_cache_get_next(struct seq_file *seq, struct dn_rou
{ {
struct dn_rt_cache_iter_state *s = seq->private; struct dn_rt_cache_iter_state *s = seq->private;
rt = rt->dst.dn_next; rt = rcu_dereference_bh(rt->dst.dn_next);
while(!rt) { while (!rt) {
rcu_read_unlock_bh(); rcu_read_unlock_bh();
if (--s->bucket < 0) if (--s->bucket < 0)
break; break;
rcu_read_lock_bh(); rcu_read_lock_bh();
rt = dn_rt_hash_table[s->bucket].chain; rt = rcu_dereference_bh(dn_rt_hash_table[s->bucket].chain);
} }
return rcu_dereference_bh(rt); return rt;
} }
static void *dn_rt_cache_seq_start(struct seq_file *seq, loff_t *pos) static void *dn_rt_cache_seq_start(struct seq_file *seq, loff_t *pos)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment