David S. Miller wrote:
From: Ben Greear <[EMAIL PROTECTED]>
Date: Fri, 26 Aug 2005 16:43:29 -0700
Btw, I think I found a leaked reference off in the ATM code...does this
look legit to you?
Definitely, that reference is leaked.
Ok, I have the beginnings of my attempt at reference count debugging..
Full patch against 2.6.13-rc7 here:
http://www.candelatech.com/oss/rfcnt2.patch
I'm still learning git...I had to post filter that patch to get rid of
of the many diff --git .... lines that didn't seem to help much.
The more interesting parts of the patch is below. The basic
idea is to pass a key in when you hold or put a device. This key
matches puts with holds. #defined macros grab the file name and
line number for printing out later. Duplicate keys are not desired,
but they should not break anything other than possibly pointing to the
wrong code as leak culprit.
If the ref count is buggered when trying to unload a device, we
can print out the table of references to see who is holding it...
Comments welcome...
Thanks,
Ben
--- a/include/linux/inetdevice.h
+++ b/include/linux/inetdevice.h
@@ -103,7 +103,7 @@ struct in_ifaddr
extern int register_inetaddr_notifier(struct notifier_block *nb);
extern int unregister_inetaddr_notifier(struct notifier_block *nb);
-extern struct net_device *ip_dev_find(u32 addr);
+extern struct net_device *ip_dev_find(u32 addr, void* key);
extern int inet_addr_onlink(struct in_device *in_dev, u32 a, u32
b);
extern int devinet_ioctl(unsigned int cmd, void __user *);
extern void devinet_init(void);
--- a/include/linux/netdevice.h
+++ b/include/linux/netdevice.h
@@ -515,14 +515,14 @@ extern rwlock_t dev_base_lock;
/* De
extern int netdev_boot_setup_check(struct net_device *dev);
extern unsigned long netdev_boot_base(const char *prefix, int unit);
extern struct net_device *dev_getbyhwaddr(unsigned short type, char
*hwaddr);
-extern struct net_device *dev_getfirstbyhwtype(unsigned short type);
+extern struct net_device *dev_getfirstbyhwtype(unsigned short type, void* key);
extern void dev_add_pack(struct packet_type *pt);
extern void dev_remove_pack(struct packet_type *pt);
extern void __dev_remove_pack(struct packet_type *pt);
extern struct net_device *dev_get_by_flags(unsigned short flags,
- unsigned short mask);
-extern struct net_device *dev_get_by_name(const char *name);
+ unsigned short mask, void*
key);
+extern struct net_device *dev_get_by_name(const char *name, void* key);
extern struct net_device *__dev_get_by_name(const char *name);
extern int dev_alloc_name(struct net_device *dev, const char
*name);
extern int dev_open(struct net_device *dev);
@@ -535,7 +535,7 @@ extern void synchronize_net(void);
extern int register_netdevice_notifier(struct notifier_block *nb);
extern int unregister_netdevice_notifier(struct notifier_block
*nb);
extern int call_netdevice_notifiers(unsigned long val, void *v);
-extern struct net_device *dev_get_by_index(int ifindex);
+extern struct net_device *dev_get_by_index(int ifindex, void* key);
extern struct net_device *__dev_get_by_index(int ifindex);
extern int dev_restart(struct net_device *dev);
#ifdef CONFIG_NETPOLL_TRAP
@@ -675,13 +675,28 @@ extern int netdev_nit;
/* Called by rtnetlink.c:rtnl_unlock() */
extern void netdev_run_todo(void);
+#ifdef CONFIG_DEBUG_NETDEV_REFCOUNT
+
+extern void __debug_dev_put(struct net_device* dev, void* key,
+ const char* file, u32 line);
+extern void __debug_dev_hold(struct net_device* dev, void* key,
+ const char* file, u32 line);
+
+#define __dev_put(dev, key) __debug_dev_put(dev, key, __FILE__, __LINE__)
+#define dev_put(dev, key) __dev_put(dev, key)
+#define dev_hold(dev, key) __debug_dev_hold(dev, key, __FILE__, __LINE__)
+
+#else
+
static inline void dev_put(struct net_device *dev)
{
atomic_dec(&dev->refcnt);
}
-#define __dev_put(dev) atomic_dec(&(dev)->refcnt)
-#define dev_hold(dev) atomic_inc(&(dev)->refcnt)
+#define __dev_put(dev, key) atomic_dec(&(dev)->refcnt)
+#define dev_hold(dev, key) atomic_inc(&(dev)->refcnt)
+
+#endif
/* Carrier loss detection, dial on demand. The functions netif_carrier_on
* and _off may be called from IRQ context, but it is caller
@@ -801,7 +816,7 @@ static inline void __netif_rx_schedule(s
unsigned long flags;
local_irq_save(flags);
- dev_hold(dev);
+ dev_hold(dev, (void*)(0x01F10)); /* released in net_rx_action */
list_add_tail(&dev->poll_list, &__get_cpu_var(softnet_data).poll_list);
if (dev->quota < 0)
dev->quota += dev->weight;
--- a/net/core/dev.c
+++ b/net/core/dev.c
@@ -1,4 +1,4 @@
-/*
+/* -*- linux-c -*-
* NET3 Protocol independent device support routines.
*
* This program is free software; you can redistribute it and/or
@@ -457,6 +457,155 @@ __setup("netdev=", netdev_boot_setup);
*******************************************************************************/
+#ifdef CONFIG_DEBUG_NETDEV_REFCOUNT
+
+struct dbg_dev_rfcnt_info {
+ struct dbg_dev_rfcnt_info* next;
+ struct net_device* dev;
+ void* key;
+ const char* file;
+ u32 line;
+};
+
+static DEFINE_SPINLOCK(rfcnt_infos_lock);
+
+#define DBG_NETDEV_HASHLEN 512
+static struct dbg_dev_rfcnt_info* rfcnt_infos[DBG_NETDEV_HASHLEN];
+
+static void dump_rfcnt_info(void) {
+ int i;
+ printk(" NetDevice ref count hash table:\n");
+ for (i = 0; i<DBG_NETDEV_HASHLEN; i++) {
+ struct dbg_dev_rfcnt_info* rfi = rfcnt_infos[i];
+ if (rfi) {
+ printk(" [%d]: ", i);
+ }
+ while (rfi) {
+ printk(" dev: %p key: %p file: %s line: %d\n",
+ rfi->dev, rfi->key, rfi->file, rfi->line);
+ rfi = rfi->next;
+ }
+ }/* for */
+}
+
+static inline u32 get_rfcnt_hashkey(void* key) {
+ u32 rv = (((u32)(key)) & 0x1FF0) >> 4;
+ if (rv >= DBG_NETDEV_HASHLEN) {
+ printk("error: hashkey oob: %d\n", rv);
+ rv = (DBG_NETDEV_HASHLEN - 1);
+ }
+ return rv;
+}
+
+struct dbg_dev_rfcnt_info* findDevRfcntInfo(struct net_device* dev, void* key)
{
+ struct dbg_dev_rfcnt_info* rv;
+ long flags;
+ spin_lock_irqsave(&rfcnt_infos_lock, flags);
+ rv = rfcnt_infos[get_rfcnt_hashkey(key)];
+ while (rv) {
+ if ((rv->key == key) && (rv->dev == dev)) {
+ goto out;
+ }
+ rv = rv->next;
+ }
+out:
+ spin_unlock_irqrestore(&rfcnt_infos_lock, flags);
+ return rv;
+}
+
+struct dbg_dev_rfcnt_info* removeDevRfcntInfo(struct net_device* dev, void*
key) {
+ u32 hk = get_rfcnt_hashkey(key);
+ struct dbg_dev_rfcnt_info* prev = NULL;
+ struct dbg_dev_rfcnt_info* rv;
+ long flags;
+ spin_lock_irqsave(&rfcnt_infos_lock, flags);
+ rv = rfcnt_infos[hk];
+ while (rv) {
+ if ((rv->key == key) && (rv->dev == dev)) {
+ if (rv == rfcnt_infos[hk]) {
+ /* First in the list */
+ rfcnt_infos[hk] = rv->next;
+ }
+ else {
+ /* prev should be valid */
+ prev->next = rv->next;
+ }
+ goto out;
+ }
+ prev = rv;
+ rv = rv->next;
+ }
+out:
+ spin_unlock_irqrestore(&rfcnt_infos_lock, flags);
+ return rv;
+}
+
+void __debug_dev_put(struct net_device* dev, void* key,
+ const char* file, u32 line) {
+ struct dbg_dev_rfcnt_info* rfc = removeDevRfcntInfo(dev, key);
+ if (!rfc) {
+ printk("ERROR: can't find dev refcnt info, key: %p dev: %p file:
%s:%d\n",
+ key, dev, file, line);
+ printk(" dev->name: %s\n", dev->name);
+ dump_rfcnt_info();
+ /* BUG(); */
+ atomic_dec(&dev->refcnt);
+ }
+ else {
+ kfree(rfc);
+ atomic_dec(&dev->refcnt);
+ }
+}
+
+
+void __debug_dev_hold(struct net_device* dev, void* key,
+ const char* file, u32 line) {
+ struct dbg_dev_rfcnt_info* rfc = findDevRfcntInfo(dev, key);
+ if (rfc) {
+ /* Keys and devs are not completely unique...so this happens
+ * a lot currently...ignore for now. --Ben
+ *
+ printk("WARNING: found dev refcnt info, key: %p dev: %p file:
%s:%d\n",
+ key, dev, file, line);
+ printk(" rfc: key: %p dev: %p file: %s:%d\n",
+ rfc->key, rfc->dev, rfc->file, rfc->line);
+ printk(" dev->name: %s\n", dev->name);
+ dump_rfcnt_info();
+ */
+ /* BUG(); */
+ }
+
+ /* Allocate a new one. */
+ rfc = kmalloc(sizeof(*rfc), GFP_ATOMIC);
+ if (!rfc) {
+ /* This is not good at all. Panic for now.
+ * for future work, should probably keep a
+ * cache of rfc structs around to use in this
+ * scenario. --Ben Greear
+ */
+ printk("ERROR: Could not allocate netdev debug refcount
struct..!\n");
+ printk(" key: %p dev: %p file: %s:%d\n",
+ key, dev, file, line);
+ BUG();
+ }
+ else {
+ u32 hk = get_rfcnt_hashkey(key);
+ long flags;
+ rfc->key = key;
+ rfc->dev = dev;
+ rfc->file = file;
+ rfc->line = line;
+ spin_lock_irqsave(&rfcnt_infos_lock, flags);
+ rfc->next = rfcnt_infos[hk];
+ rfcnt_infos[hk] = rfc;
+ spin_unlock_irqrestore(&rfcnt_infos_lock, flags);
+ atomic_inc(&dev->refcnt);
+ }
+}
+
+#endif
+
+
/**
* __dev_get_by_name - find a device by its name
* @name: name to find
@@ -492,14 +641,14 @@ struct net_device *__dev_get_by_name(con
* matching device is found.
*/
-struct net_device *dev_get_by_name(const char *name)
+struct net_device *dev_get_by_name(const char *name, void* key)
{
struct net_device *dev;
read_lock(&dev_base_lock);
dev = __dev_get_by_name(name);
if (dev)
- dev_hold(dev);
+ dev_hold(dev, key);
read_unlock(&dev_base_lock);
return dev;
}
@@ -539,14 +688,14 @@ struct net_device *__dev_get_by_index(in
* dev_put to indicate they have finished with it.
*/
-struct net_device *dev_get_by_index(int ifindex)
+struct net_device *dev_get_by_index(int ifindex, void* key)
{
struct net_device *dev;
read_lock(&dev_base_lock);
dev = __dev_get_by_index(ifindex);
if (dev)
- dev_hold(dev);
+ dev_hold(dev, key);
read_unlock(&dev_base_lock);
return dev;
}
@@ -578,14 +727,14 @@ struct net_device *dev_getbyhwaddr(unsig
return dev;
}
-struct net_device *dev_getfirstbyhwtype(unsigned short type)
+struct net_device *dev_getfirstbyhwtype(unsigned short type, void* key)
{
struct net_device *dev;
rtnl_lock();
for (dev = dev_base; dev; dev = dev->next) {
if (dev->type == type) {
- dev_hold(dev);
+ dev_hold(dev, key);
break;
}
}
@@ -606,14 +755,14 @@ EXPORT_SYMBOL(dev_getfirstbyhwtype);
* dev_put to indicate they have finished with it.
*/
-struct net_device * dev_get_by_flags(unsigned short if_flags, unsigned short
mask)
+struct net_device * dev_get_by_flags(unsigned short if_flags, unsigned short
mask, void* key)
{
struct net_device *dev;
read_lock(&dev_base_lock);
for (dev = dev_base; dev != NULL; dev = dev->next) {
if (((dev->flags ^ if_flags) & mask) == 0) {
- dev_hold(dev);
+ dev_hold(dev, key);
break;
}
}
@@ -1393,7 +1542,7 @@ int netif_rx(struct sk_buff *skb)
if (queue->input_pkt_queue.qlen <= netdev_max_backlog) {
if (queue->input_pkt_queue.qlen) {
enqueue:
- dev_hold(skb->dev);
+ dev_hold(skb->dev, &(skb->dev));
__skb_queue_tail(&queue->input_pkt_queue, skb);
local_irq_restore(flags);
return NET_RX_SUCCESS;
@@ -1655,6 +1804,7 @@ static int process_backlog(struct net_de
for (;;) {
struct sk_buff *skb;
struct net_device *dev;
+ void* key;
local_irq_disable();
skb = __skb_dequeue(&queue->input_pkt_queue);
@@ -1663,10 +1813,11 @@ static int process_backlog(struct net_de
local_irq_enable();
dev = skb->dev;
-
+ key = &(skb->dev);
+
netif_receive_skb(skb);
- dev_put(dev);
+ dev_put(dev, key);
work++;
@@ -1697,7 +1848,7 @@ static void net_rx_action(struct softirq
unsigned long start_time = jiffies;
int budget = netdev_budget;
void *have;
-
+
local_irq_disable();
while (!list_empty(&queue->poll_list)) {
@@ -1723,7 +1874,8 @@ static void net_rx_action(struct softirq
dev->quota = dev->weight;
} else {
netpoll_poll_unlock(have);
- dev_put(dev);
+ /* This was acquired in netdevice.h, line 819 or so.
--Ben */
+ dev_put(dev, (void*)(0x01F10));
local_irq_disable();
}
}
@@ -2056,7 +2208,7 @@ int netdev_set_master(struct net_device
if (master) {
if (old)
return -EBUSY;
- dev_hold(master);
+ dev_hold(master, &(slave->master));
}
slave->master = master;
@@ -2064,7 +2216,7 @@ int netdev_set_master(struct net_device
synchronize_net();
if (old)
- dev_put(old);
+ dev_put(old, &slave->master);
if (master)
slave->flags |= IFF_SLAVE;
@@ -2729,7 +2881,7 @@ int register_netdevice(struct net_device
dev_tail = &dev->next;
hlist_add_head(&dev->name_hlist, head);
hlist_add_head(&dev->index_hlist, dev_index_hash(dev->ifindex));
- dev_hold(dev);
+ dev_hold(dev, &dev_base); /* is on the dev_base list now */
dev->reg_state = NETREG_REGISTERING;
write_unlock_bh(&dev_base_lock);
@@ -3089,7 +3241,7 @@ int unregister_netdevice(struct net_devi
synchronize_net();
- dev_put(dev);
+ dev_put(dev, &dev_base);
return 0;
}
@@ -3264,6 +3416,11 @@ EXPORT_SYMBOL(net_enable_timestamp);
EXPORT_SYMBOL(net_disable_timestamp);
EXPORT_SYMBOL(dev_get_flags);
+#ifdef CONFIG_DEBUG_NETDEV_REFCOUNT
+EXPORT_SYMBOL(__debug_dev_put);
+EXPORT_SYMBOL(__debug_dev_hold);
+#endif
+
#if defined(CONFIG_BRIDGE) || defined(CONFIG_BRIDGE_MODULE)
EXPORT_SYMBOL(br_handle_frame_hook);
EXPORT_SYMBOL(br_fdb_get_hook);
--
Ben Greear <[EMAIL PROTECTED]>
Candela Technologies Inc http://www.candelatech.com
-
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at http://vger.kernel.org/majordomo-info.html