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: Eric Dumazet <eric.dumazet@gmail.com> Acked-by: Steven Whitehouse <swhiteho@redhat.com> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:

committed by
David S. Miller

parent
e4a7b93bd5
commit
fc766e4c49
@@ -93,7 +93,7 @@
|
||||
|
||||
struct dn_rt_hash_bucket
|
||||
{
|
||||
struct dn_route *chain;
|
||||
struct dn_route __rcu *chain;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
@@ -157,15 +157,17 @@ static inline void dnrt_drop(struct dn_route *rt)
|
||||
static void dn_dst_check_expire(unsigned long dummy)
|
||||
{
|
||||
int i;
|
||||
struct dn_route *rt, **rtp;
|
||||
struct dn_route *rt;
|
||||
struct dn_route __rcu **rtp;
|
||||
unsigned long now = jiffies;
|
||||
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;
|
||||
|
||||
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) ||
|
||||
(now - rt->dst.lastuse) < expire) {
|
||||
rtp = &rt->dst.dn_next;
|
||||
@@ -186,17 +188,19 @@ static void dn_dst_check_expire(unsigned long dummy)
|
||||
|
||||
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;
|
||||
unsigned long now = jiffies;
|
||||
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);
|
||||
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) ||
|
||||
(now - rt->dst.lastuse) < expire) {
|
||||
rtp = &rt->dst.dn_next;
|
||||
@@ -227,7 +231,7 @@ static void dn_dst_update_pmtu(struct dst_entry *dst, u32 mtu)
|
||||
{
|
||||
u32 min_mtu = 230;
|
||||
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)
|
||||
min_mtu -= 6;
|
||||
@@ -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)
|
||||
{
|
||||
struct dn_route *rth, **rthp;
|
||||
struct dn_route *rth;
|
||||
struct dn_route __rcu **rthp;
|
||||
unsigned long now = jiffies;
|
||||
|
||||
rthp = &dn_rt_hash_table[hash].chain;
|
||||
|
||||
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)) {
|
||||
/* Put it first */
|
||||
*rthp = rth->dst.dn_next;
|
||||
@@ -315,15 +321,15 @@ static void dn_run_flush(unsigned long dummy)
|
||||
int i;
|
||||
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);
|
||||
|
||||
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;
|
||||
|
||||
for(; rt; rt=next) {
|
||||
next = rt->dst.dn_next;
|
||||
rt->dst.dn_next = NULL;
|
||||
for(; rt; rt = next) {
|
||||
next = rcu_dereference_raw(rt->dst.dn_next);
|
||||
RCU_INIT_POINTER(rt->dst.dn_next, NULL);
|
||||
dst_free((struct dst_entry *)rt);
|
||||
}
|
||||
|
||||
@@ -458,15 +464,16 @@ static int dn_return_long(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;
|
||||
|
||||
if ((err = dn_route_input(skb)) == 0)
|
||||
return dst_input(skb);
|
||||
|
||||
cb = DN_SKB_CB(skb);
|
||||
if (decnet_debug_level & 4) {
|
||||
char *devname = skb->dev ? skb->dev->name : "???";
|
||||
struct dn_skb_cb *cb = DN_SKB_CB(skb);
|
||||
|
||||
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",
|
||||
(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
|
||||
struct dn_skb_cb *cb;
|
||||
unsigned char flags = 0;
|
||||
__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;
|
||||
|
||||
if (!net_eq(dev_net(dev), &init_net))
|
||||
@@ -728,7 +735,7 @@ static int dn_forward(struct sk_buff *skb)
|
||||
{
|
||||
struct dn_skb_cb *cb = DN_SKB_CB(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 neighbour *neigh = dst->neighbour;
|
||||
int header_len;
|
||||
@@ -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)
|
||||
{
|
||||
__le16 saddr = 0;
|
||||
struct dn_dev *dn_db = dev->dn_ptr;
|
||||
struct dn_dev *dn_db;
|
||||
struct dn_ifaddr *ifa;
|
||||
int best_match = 0;
|
||||
int ret;
|
||||
|
||||
read_lock(&dev_base_lock);
|
||||
for(ifa = dn_db->ifa_list; ifa; ifa = ifa->ifa_next) {
|
||||
rcu_read_lock();
|
||||
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)
|
||||
continue;
|
||||
if (!daddr) {
|
||||
@@ -854,7 +864,7 @@ static __le16 dnet_select_source(const struct net_device *dev, __le16 daddr, int
|
||||
if (best_match == 0)
|
||||
saddr = ifa->ifa_local;
|
||||
}
|
||||
read_unlock(&dev_base_lock);
|
||||
rcu_read_unlock();
|
||||
|
||||
return saddr;
|
||||
}
|
||||
@@ -1020,7 +1030,7 @@ source_ok:
|
||||
err = -ENODEV;
|
||||
if (dev_out == NULL)
|
||||
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 */
|
||||
if (dn_dev_islocal(dev_out, fl.fld_dst)) {
|
||||
dev_put(dev_out);
|
||||
@@ -1233,7 +1243,7 @@ static int dn_route_input_slow(struct sk_buff *skb)
|
||||
|
||||
dev_hold(in_dev);
|
||||
|
||||
if ((dn_db = in_dev->dn_ptr) == NULL)
|
||||
if ((dn_db = rcu_dereference(in_dev->dn_ptr)) == NULL)
|
||||
goto out;
|
||||
|
||||
/* 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
|
||||
{
|
||||
struct dn_rt_cache_iter_state *s = seq->private;
|
||||
|
||||
rt = rt->dst.dn_next;
|
||||
while(!rt) {
|
||||
rt = rcu_dereference_bh(rt->dst.dn_next);
|
||||
while (!rt) {
|
||||
rcu_read_unlock_bh();
|
||||
if (--s->bucket < 0)
|
||||
break;
|
||||
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)
|
||||
|
Reference in New Issue
Block a user