This patch augments the current reference counting code with:

* Counters for total outstanding instances for each resource type (skb, ath_node and ath_buf)
* One pair of acquisition/release functions per resource type in unlocked and one in locked
* Adds some more _debug versions of functions in the call chain that acquire/release resources so that the original func/line in the driver as well as the func/line that affected the resource use can be shown in the trace.  Intermediate stack frames aren't necessary to trace the leaks.
* Changes naming convention for "lock-required" functions to suffix _locked for the versions that expect locking, to be consistent with some other places in the code.
* Consolidate debug messages to the helper functions that actually affect the reference count or acquire/release a resource
* Additional sanity checks and leak detection (esp for detecting node ref leaks through skb)
* skb references are nulled out by the new sbk unref/free function.

I've tested these changes extensively and found lots of cases where we didn't get enough node references when cloning skbuff, and where the kernel drops packets due to performance issues and leaks our node references.

With these changes and the tracing enabled I have verified that:

* TX BUF: tx buffers always go down to zero when the tx queue is done, and you can watch tx queue usage ratio go up and down again as the driver is working.  There are no leaks here at the moment, although there *are* some in the madwifi-dfs branch during CAC at the moment.

* skbuff leaks in all the common flows are fixed.  We were leaking node references in a lot of places where kernel was dropping skb's due to congestion and we were failing to increment node references when cloning skbuffs.  These are now detected, as are skbuffs that are reaped by the kernel while still holding a node reference.

* the ath_node count works correctly and on an idle system we get about 5 references per station table node, with 2 node instances per VAP.  One for the bss and one for the node in the station table, I believe.  The ath_node count goes up and down but always lands back at the stable number based on the vaps you have configured and the number of actual stations in the station table.  The point here is that it's pretty constant what you will see over time, despite excessive node creation/release in our code during input (esp input_all).  Thank god for the slab allocator.



git-svn-id: http://madwifi-project.org/svn/madwifi/trunk@2902 0192ed92-7a03-0410-a25b-9323aeb14dbd
This commit is contained in:
mtaylor 2007-11-21 20:14:11 +00:00
parent 4ea262da0e
commit ebe8ee9736
20 changed files with 2304 additions and 674 deletions

File diff suppressed because it is too large Load Diff

View File

@ -213,6 +213,8 @@ static inline struct net_device *_alloc_netdev(int sizeof_priv, const char *mask
/* free buffer threshold to restart net dev */
#define ATH_TXBUF_FREE_THRESHOLD (ATH_TXBUF / 20)
/* number of TX buffers reserved for mgt frames */
#define ATH_TXBUF_MGT_RESERVED 5
#define TAIL_DROP_COUNT 50 /* maximum number of queued frames allowed */
@ -549,8 +551,13 @@ struct ath_softc {
int sc_debug;
int sc_default_ieee80211_debug; /* default debug flags for new VAPs */
void (*sc_recv_mgmt)(struct ieee80211_node *, struct sk_buff *, int, int, u_int64_t);
#ifdef IEEE80211_DEBUG_REFCNT
void (*sc_node_cleanup_debug)(struct ieee80211_node *, const char* func, int line);
void (*sc_node_free_debug)(struct ieee80211_node *, const char* func, int line);
#else /* #ifdef IEEE80211_DEBUG_REFCNT */
void (*sc_node_cleanup)(struct ieee80211_node *);
void (*sc_node_free)(struct ieee80211_node *);
#endif /* #ifdef IEEE80211_DEBUG_REFCNT */
void *sc_bdev; /* associated bus device */
struct ath_hal *sc_ah; /* Atheros HAL */
spinlock_t sc_hal_lock; /* hardware access lock */

View File

@ -68,6 +68,7 @@ MOD_INSTALL := wlan.o wlan_wep.o wlan_tkip.o wlan_ccmp.o wlan_acl.o \
obj-m += $(MOD_INSTALL)
wlan-objs := if_media.o \
ieee80211_skb.o \
ieee80211.o ieee80211_beacon.o ieee80211_crypto.o \
ieee80211_crypto_none.o ieee80211_input.o ieee80211_node.o \
ieee80211_output.o ieee80211_power.o ieee80211_proto.o \

View File

@ -43,7 +43,7 @@ wlan-objs := if_media.o \
ieee80211_crypto_none.o ieee80211_input.o ieee80211_node.o \
ieee80211_output.o ieee80211_proto.o ieee80211_power.o \
ieee80211_scan.o ieee80211_wireless.o ieee80211_linux.o \
ieee80211_monitor.o ieee80211_rate.o
ieee80211_monitor.o ieee80211_rate.o ieee80211_skb.o
wlan_wep-objs := ieee80211_crypto_wep.o
wlan_tkip-objs := ieee80211_crypto_tkip.o
wlan_ccmp-objs := ieee80211_crypto_ccmp.o
@ -59,7 +59,8 @@ export-objs := if_media.o \
ieee80211_output.o ieee80211_power.o ieee80211_proto.o \
ieee80211_scan.o ieee80211_wireless.o ieee80211_linux.o \
ieee80211_crypto_wep.o ieee80211_crypto_tkip.o \
ieee80211_crypto_ccmp.o ieee80211_xauth.o ieee80211_rate.o
ieee80211_crypto_ccmp.o ieee80211_xauth.o ieee80211_rate.o \
ieee80211_skb.o
list-multi := wlan.o wlan_wep.o wlan_tkip.o wlan_ccmp.o wlan_acl.o \
$(MOD_AUTH) $(MOD_SCAN)

View File

@ -198,7 +198,6 @@ ieee80211_beacon_alloc(struct ieee80211_node *ni,
struct ieee80211vap *vap = ni->ni_vap;
struct ieee80211com *ic = ni->ni_ic;
struct ieee80211_frame *wh;
struct ieee80211_cb *cb = NULL;
struct sk_buff *skb;
int pktlen;
u_int8_t *frm;
@ -256,8 +255,7 @@ ieee80211_beacon_alloc(struct ieee80211_node *ni,
return NULL;
}
cb = (struct ieee80211_cb *)skb->cb;
cb->ni = ieee80211_ref_node(ni);
SKB_CB(skb)->ni = ieee80211_ref_node(ni);
frm = ieee80211_beacon_init(ni, bo, frm);
@ -292,6 +290,8 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
IEEE80211_LOCK_IRQ(ic);
/* Check if we need to change channel right now */
if ((ic->ic_flags & IEEE80211_F_DOTH) &&
(vap->iv_flags & IEEE80211_F_CHANSWITCH)) {
struct ieee80211_channel *c =
@ -306,11 +306,11 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
"%s: reinit beacon\n", __func__);
/* NB: ic_bsschan is in the DSPARMS beacon IE, so must set this
* prior to the beacon re-init, below. */
/* NB: ic_bsschan is in the DSPARMS beacon IE, so must
* set this prior to the beacon re-init, below. */
if (c == NULL) {
/* Requested channel invalid; drop the channel switch
* announcement and do nothing. */
/* Requested channel invalid; drop the channel
* switch announcement and do nothing. */
IEEE80211_DPRINTF(vap, IEEE80211_MSG_DOTH,
"%s: find channel failure\n", __func__);
} else
@ -326,8 +326,8 @@ ieee80211_beacon_update(struct ieee80211_node *ni,
vap->iv_flags &= ~IEEE80211_F_CHANSWITCH;
ic->ic_flags &= ~IEEE80211_F_CHANSWITCH;
/* NB: Only for the first VAP to get here, and when we have a
* valid channel to which to change. */
/* NB: Only for the first VAP to get here, and when we
* have a valid channel to which to change. */
if (c && (ic->ic_curchan != c)) {
ic->ic_curchan = c;
ic->ic_set_channel(ic);

View File

@ -729,7 +729,7 @@ ieee80211_input(struct ieee80211_node *ni,
ni->ni_macaddr, "data", "%s", "Decapsulation error");
vap->iv_stats.is_rx_decap++;
IEEE80211_NODE_STAT(ni, rx_decap);
dev_kfree_skb(skb1);
ieee80211_dev_kfree_skb(&skb1);
goto err;
}
@ -826,7 +826,7 @@ err:
vap->iv_devstats.rx_errors++;
out:
if (skb != NULL)
dev_kfree_skb(skb);
ieee80211_dev_kfree_skb(&skb);
return type;
#undef HAS_SEQ
}
@ -937,7 +937,7 @@ ieee80211_input_all(struct ieee80211com *ic,
ieee80211_unref_node(&ni);
}
if (skb != NULL) /* no vaps, reclaim skb */
dev_kfree_skb(skb);
ieee80211_dev_kfree_skb(&skb);
return type;
}
EXPORT_SYMBOL(ieee80211_input_all);
@ -979,7 +979,7 @@ ieee80211_defrag(struct ieee80211_node *ni, struct sk_buff *skb, int hdrlen)
* here and just bail
*/
/* XXX need msg+stat */
dev_kfree_skb(skb);
ieee80211_dev_kfree_skb(&skb);
return NULL;
}
@ -1014,7 +1014,7 @@ ieee80211_defrag(struct ieee80211_node *ni, struct sk_buff *skb, int hdrlen)
* Unrelated fragment or no space for it,
* clear current fragments
*/
dev_kfree_skb(ni->ni_rxfrag);
ieee80211_dev_kfree_skb(&ni->ni_rxfrag);
ni->ni_rxfrag = NULL;
}
}
@ -1034,7 +1034,7 @@ ieee80211_defrag(struct ieee80211_node *ni, struct sk_buff *skb, int hdrlen)
if (SKB_CB(skb)->ni != NULL) {
SKB_CB(ni->ni_rxfrag)->ni = ieee80211_ref_node(SKB_CB(skb)->ni);
}
dev_kfree_skb(skb);
ieee80211_dev_kfree_skb(&skb);
}
/*
* Check that we have enough space to hold
@ -1051,7 +1051,7 @@ ieee80211_defrag(struct ieee80211_node *ni, struct sk_buff *skb, int hdrlen)
if (SKB_CB(skb)->ni != NULL && (skb != ni->ni_rxfrag)) {
SKB_CB(ni->ni_rxfrag)->ni = ieee80211_ref_node(SKB_CB(skb)->ni);
}
dev_kfree_skb(skb);
ieee80211_dev_kfree_skb(&skb);
}
}
} else {
@ -1072,7 +1072,7 @@ ieee80211_defrag(struct ieee80211_node *ni, struct sk_buff *skb, int hdrlen)
*(__le16 *) lwh->i_seq = *(__le16 *) wh->i_seq;
}
/* we're done with the fragment */
dev_kfree_skb(skb);
ieee80211_dev_kfree_skb(&skb);
}
if (more_frag) {
@ -1261,7 +1261,7 @@ ieee80211_decap(struct ieee80211vap *vap, struct sk_buff *skb, int hdrlen)
if (SKB_CB(skb)->ni != NULL) {
SKB_CB(tskb)->ni = ieee80211_ref_node(SKB_CB(skb)->ni);
}
dev_kfree_skb(skb);
ieee80211_dev_kfree_skb(&skb);
skb = tskb;
}
return skb;
@ -1318,10 +1318,6 @@ ieee80211_auth_open(struct ieee80211_node *ni, struct ieee80211_frame *wh,
ni = ieee80211_dup_bss(vap, wh->i_addr2, 0);
if (ni == NULL)
return;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
"%s: %p<%s> refcnt %d\n", __func__, ni, ether_sprintf(ni->ni_macaddr),
ieee80211_node_refcnt(ni));
tmpnode = 1;
}
IEEE80211_SEND_MGMT(ni, IEEE80211_FC0_SUBTYPE_AUTH,
@ -1359,10 +1355,6 @@ ieee80211_auth_open(struct ieee80211_node *ni, struct ieee80211_frame *wh,
ni = ieee80211_dup_bss(vap, wh->i_addr2, 0);
if (ni == NULL)
return;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
"%s: %p<%s> refcnt %d\n", __func__, ni, ether_sprintf(ni->ni_macaddr),
ieee80211_node_refcnt(ni));
tmpnode = 1;
}
@ -1545,13 +1537,6 @@ ieee80211_auth_shared(struct ieee80211_node *ni, struct ieee80211_frame *wh,
/* NB: no way to return an error */
return;
}
IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
"%s: %p<%s> refcnt %d\n",
__func__, ni,
ether_sprintf(ni->ni_macaddr),
ieee80211_node_refcnt(ni));
allocbs = 1;
}
@ -2576,7 +2561,7 @@ ieee80211_deliver_l2uf(struct ieee80211_node *ni)
struct l2_update_frame *l2uf;
struct ether_header *eh;
skb = dev_alloc_skb(sizeof(*l2uf));
skb = ieee80211_dev_alloc_skb(sizeof(*l2uf));
if (skb == NULL) {
return;
}
@ -3118,7 +3103,7 @@ ieee80211_recv_mgmt(struct ieee80211_node *ni, struct sk_buff *skb,
IEEE80211_SEND_MGMT(ni,
IEEE80211_FC0_SUBTYPE_PROBE_RESP, 0);
}
if (allocbs && vap->iv_opmode != IEEE80211_M_IBSS) {
if (allocbs) {
/*
* Temporary node created just to send a
* response, reclaim immediately
@ -3236,6 +3221,7 @@ ieee80211_recv_mgmt(struct ieee80211_node *ni, struct sk_buff *skb,
* [tlv] ssid
* [tlv] supported rates
* [tlv] extended supported rates
* [tlv] supported channels
* [tlv] wpa or RSN
* [tlv] WME
* [tlv] Atheros Advanced Capabilities

View File

@ -134,14 +134,22 @@ if_printf(struct net_device *dev, const char *fmt, ...)
* can use this interface too.
*/
struct sk_buff *
#ifdef IEEE80211_DEBUG_REFCNT
ieee80211_getmgtframe_debug(u_int8_t **frm, u_int pktlen, const char* func, int line)
#else
ieee80211_getmgtframe(u_int8_t **frm, u_int pktlen)
#endif
{
const u_int align = sizeof(u_int32_t);
struct sk_buff *skb;
u_int len;
len = roundup(sizeof(struct ieee80211_frame) + pktlen, 4);
skb = dev_alloc_skb(len + align - 1);
#ifdef IEEE80211_DEBUG_REFCNT
skb = ieee80211_dev_alloc_skb_debug(len + align - 1, func, line);
#else
skb = ieee80211_dev_alloc_skb(len + align - 1);
#endif
if (skb != NULL) {
u_int off = ((unsigned long) skb->data) % align;
if (off != 0)
@ -156,6 +164,11 @@ ieee80211_getmgtframe(u_int8_t **frm, u_int pktlen)
}
return skb;
}
#ifdef IEEE80211_DEBUG_REFCNT
EXPORT_SYMBOL(ieee80211_getmgtframe_debug);
#else
EXPORT_SYMBOL(ieee80211_getmgtframe);
#endif
#if IEEE80211_VLAN_TAG_USED
/*

View File

@ -314,6 +314,9 @@ struct ieee80211_cb {
#define M_PWR_SAV 0x04 /* bypass power save handling */
#define M_UAPSD 0x08 /* frame flagged for u-apsd handling */
#define M_RAW 0x10
#ifdef IEEE80211_DEBUG_REFCNT
int tracked;
#endif
struct sk_buff *next; /* fast frame sk_buf chain */
};
@ -354,23 +357,6 @@ struct ieee80211vap;
int ieee80211_load_module(const char *);
/*
* Node reference counting definitions.
*
* ieee80211_node_initref initialize the reference count to 1
* ieee80211_node_incref add a reference
* ieee80211_node_decref remove a reference
* ieee80211_node_dectestref remove a reference and return 1 if this
* is the last reference, otherwise 0
* ieee80211_node_refcnt reference count for printing (only)
*/
typedef atomic_t ieee80211_node_ref_count_t;
#define ieee80211_node_initref(_ni) atomic_set(&(_ni)->ni_refcnt, 1)
#define ieee80211_node_incref(_ni) atomic_inc(&(_ni)->ni_refcnt)
#define ieee80211_node_decref(_ni) atomic_dec(&(_ni)->ni_refcnt)
#define ieee80211_node_dectestref(_ni) atomic_dec_and_test(&(_ni)->ni_refcnt)
#define ieee80211_node_refcnt(_ni) atomic_read(&(_ni)->ni_refcnt)
#define le16toh(_x) le16_to_cpu(_x)
#define htole16(_x) cpu_to_le16(_x)
#define le32toh(_x) le32_to_cpu(_x)
@ -414,7 +400,15 @@ extern const char *ether_sprintf(const u_int8_t *);
/*
* Queue write-arounds and support routines.
*/
#ifdef IEEE80211_DEBUG_REFCNT
#define ieee80211_getmgtframe(_ppfrm, _pktlen) \
ieee80211_getmgtframe_debug(_ppfrm, _pktlen, __func__, __LINE__)
extern struct sk_buff * ieee80211_getmgtframe_debug(u_int8_t **frm, u_int pktlen,
const char* func, int line);
#else
extern struct sk_buff * ieee80211_getmgtframe(u_int8_t **frm, u_int pktlen);
#endif
#define IF_ENQUEUE(_q,_skb) skb_queue_tail(_q, _skb)
#define IF_DEQUEUE(_q,_skb) (_skb = skb_dequeue(_q))
#define _IF_QLEN(_q) skb_queue_len(_q)

View File

@ -388,8 +388,7 @@ ieee80211_input_monitor(struct ieee80211com *ic, struct sk_buff *skb,
case ARPHRD_IEEE80211_PRISM: {
struct wlan_ng_prism2_header *ph;
if (skb_headroom(skb1) < sizeof(struct wlan_ng_prism2_header)) {
dev_kfree_skb(skb1);
skb1 = NULL;
ieee80211_dev_kfree_skb(&skb1);
break;
}
@ -456,8 +455,7 @@ ieee80211_input_monitor(struct ieee80211com *ic, struct sk_buff *skb,
struct ath_tx_radiotap_header *th;
if (skb_headroom(skb1) < sizeof(struct ath_tx_radiotap_header)) {
printk("%s:%d %s\n", __FILE__, __LINE__, __func__);
dev_kfree_skb(skb1);
skb1 = NULL;
ieee80211_dev_kfree_skb(&skb1);
break;
}
@ -486,8 +484,7 @@ ieee80211_input_monitor(struct ieee80211com *ic, struct sk_buff *skb,
struct ath_rx_radiotap_header *th;
if (skb_headroom(skb1) < sizeof(struct ath_rx_radiotap_header)) {
printk("%s:%d %s\n", __FILE__, __LINE__, __func__);
dev_kfree_skb(skb1);
skb1 = NULL;
ieee80211_dev_kfree_skb(&skb1);
break;
}
@ -547,8 +544,7 @@ ieee80211_input_monitor(struct ieee80211com *ic, struct sk_buff *skb,
case ARPHRD_IEEE80211_ATHDESC: {
if (skb_headroom(skb1) < ATHDESC_HEADER_SIZE) {
printk("%s:%d %s\n", __FILE__, __LINE__, __func__);
dev_kfree_skb(skb1);
skb1 = NULL;
ieee80211_dev_kfree_skb(&skb1);
break;
}
memcpy(skb_push(skb1, ATHDESC_HEADER_SIZE), ds, ATHDESC_HEADER_SIZE);

File diff suppressed because it is too large Load Diff

View File

@ -82,6 +82,8 @@ struct ieee80211_rsnparms {
struct ieee80211_node_table;
struct ieee80211com;
struct ieee80211vap;
struct ath_buf;
struct ath_softc;
/*
* Node specific information. Note that drivers are expected
@ -95,7 +97,7 @@ struct ieee80211_node {
struct ieee80211_node_table *ni_table;
TAILQ_ENTRY(ieee80211_node) ni_list;
LIST_ENTRY(ieee80211_node) ni_hash;
ieee80211_node_ref_count_t ni_refcnt;
atomic_t ni_refcnt;
u_int ni_scangen; /* gen# for timeout scan */
u_int8_t ni_authmode; /* authentication algorithm */
u_int16_t ni_flags; /* special-purpose state */
@ -246,112 +248,179 @@ struct ieee80211_node_table {
struct timer_list nt_wds_aging_timer; /* timer to age out wds entries */
};
/* Allocates a new ieee80211_node* that has a reference count of one, and adds it to the node table. */
#ifdef IEEE80211_DEBUG_REFCNT
#define ieee80211_alloc_node_table(_vap, _mac) \
ieee80211_alloc_node_table_debug(_vap, _mac, __func__, __LINE__)
struct ieee80211_node *ieee80211_alloc_node_table_debug(struct ieee80211vap *,
const u_int8_t *, const char* name, int line);
#else
struct ieee80211_node *ieee80211_alloc_node_table(struct ieee80211vap *,
const u_int8_t *);
struct ieee80211_node *ieee80211_dup_bss(struct ieee80211vap *,
const u_int8_t *, unsigned char);
void ieee80211_node_reset(struct ieee80211_node *, struct ieee80211vap *);
#endif /* #ifdef IEEE80211_DEBUG_REFCNT */
/* Allocates a new ieee80211_node* that has a reference count.
* If tmp is 0, it is added to the node table and the reference is used.
* If tmp is 1, then the caller gets to use the reference. */
#ifdef IEEE80211_DEBUG_REFCNT
#define ieee80211_dup_bss(_vap, _mac, _tmp) \
ieee80211_dup_bss_debug(_vap, _mac, _tmp, __func__, __LINE__)
struct ieee80211_node *ieee80211_dup_bss_debug(struct ieee80211vap *,
const u_int8_t *, unsigned char tmp, const char*, int);
#else
struct ieee80211_node *ieee80211_dup_bss(struct ieee80211vap *,
const u_int8_t *, unsigned char tmp);
#endif /* #ifdef IEEE80211_DEBUG_REFCNT */
void ieee80211_node_reset(struct ieee80211_node *, struct ieee80211vap *);
/* Returns a ieee80211_node* with refcount incremented, if found */
#ifdef IEEE80211_DEBUG_REFCNT
struct ieee80211_node *ieee80211_find_node_debug(struct ieee80211_node_table *,
const u_int8_t *, const char *, int);
struct ieee80211_node *ieee80211_find_rxnode_debug(struct ieee80211com *,
const struct ieee80211_frame_min *, const char *, int);
struct ieee80211_node *ieee80211_find_txnode_debug(struct ieee80211vap *,
const u_int8_t *, const char *, int);
#define ieee80211_unref_node(_ni) \
ieee80211_unref_node_debug(_ni, __func__, __LINE__)
#define ieee80211_find_node(_nt, _mac) \
ieee80211_find_node_debug(_nt, _mac, __func__, __LINE__)
#define ieee80211_find_rxnode(_nt, _wh) \
ieee80211_find_rxnode_debug(_nt, _wh, __func__, __LINE__)
#define ieee80211_find_txnode(_nt, _mac) \
ieee80211_find_txnode_debug(_nt, _mac, __func__, __LINE__)
struct ieee80211_node *ieee80211_find_node_debug(struct ieee80211_node_table *,
const u_int8_t *, const char *, int);
#else
struct ieee80211_node *ieee80211_find_node(struct ieee80211_node_table *,
const u_int8_t *);
#endif /* #ifdef IEEE80211_DEBUG_REFCNT */
/* Returns a ieee80211_node* with refcount incremented, if found */
#ifdef IEEE80211_DEBUG_REFCNT
#define ieee80211_find_rxnode(_nt, _wh) \
ieee80211_find_rxnode_debug(_nt, _wh, __func__, __LINE__)
struct ieee80211_node *ieee80211_find_rxnode_debug(struct ieee80211com *,
const struct ieee80211_frame_min *, const char *, int);
#else
struct ieee80211_node *ieee80211_find_rxnode(struct ieee80211com *,
const struct ieee80211_frame_min *);
#endif /* #ifdef IEEE80211_DEBUG_REFCNT */
/* Returns a ieee80211_node* with refcount incremented, if found */
#ifdef IEEE80211_DEBUG_REFCNT
#define ieee80211_find_txnode(_nt, _mac) \
ieee80211_find_txnode_debug(_nt, _mac, __func__, __LINE__)
struct ieee80211_node *ieee80211_find_txnode_debug(struct ieee80211vap *,
const u_int8_t *, const char *, int);
#else
struct ieee80211_node *ieee80211_find_txnode(struct ieee80211vap *,
const u_int8_t *);
#endif
#endif /* #ifdef IEEE80211_DEBUG_REFCNT */
void _ieee80211_free_node(struct ieee80211_node *);
#ifdef IEEE80211_DEBUG_REFCNT
#define ieee80211_free_node(_ni) \
ieee80211_free_node_debug(_ni, __func__, __LINE__)
void ieee80211_free_node_debug(struct ieee80211_node *ni, const char *func, int line);
#else
void ieee80211_free_node(struct ieee80211_node *ni);
#endif /* #ifdef IEEE80211_DEBUG_REFCNT */
/* Reference counting only needs to be locked out against the transitions,
* 0->1 and 1->0 (i.e., when we do not own the reference we are getting).
* This only happens when finding the a node reference from the node table,
* which is locked seperately. Thus, we do not need to lock the follwoing
* functions. */
* functions.
* Increment the reference counter for ieee80211_node*
*/
#ifdef IEEE80211_DEBUG_REFCNT
#define ieee80211_ref_node(_ni) \
ieee80211_ref_node_debug(_ni, __func__, __LINE__)
struct ieee80211_node *
ieee80211_ref_node_debug(struct ieee80211_node *ni, const char *func, int line);
#else
struct ieee80211_node *
ieee80211_ref_node(struct ieee80211_node *ni);
#endif /* #ifdef IEEE80211_DEBUG_REFCNT */
#define PASS_NODE(_ni) \
ieee80211_pass_node(&_ni)
static __inline struct ieee80211_node *
ieee80211_ref_node(struct ieee80211_node *ni)
{
ieee80211_node_incref(ni);
return ni;
}
static __inline struct ieee80211_node *
_ieee80211_pass_node(struct ieee80211_node **pni) {
ieee80211_pass_node(struct ieee80211_node **pni) {
struct ieee80211_node *tmp = *pni;
*pni = NULL;
return (tmp);
}
#define PASS_NODE(_ni) \
_ieee80211_pass_node(&_ni)
static __inline int
_ieee80211_unref_node(struct ieee80211_node *ni) {
if (ieee80211_node_dectestref(ni)) {
_ieee80211_free_node(ni);
return 1;
} else {
return 0;
}
}
static __inline void
/* Decrement ieee80211_node* refcunt, and relinquish the pointer. */
#ifdef IEEE80211_DEBUG_REFCNT
ieee80211_unref_node_debug(struct ieee80211_node **pni, const char *func, int line)
#define ieee80211_unref_node(_pni) \
ieee80211_unref_node_debug(_pni, __func__, __LINE__)
void
ieee80211_unref_node_debug(struct ieee80211_node **pni, const char *func, int line);
#else
ieee80211_unref_node(struct ieee80211_node **pni)
#endif
{
struct ieee80211_node *ni = *pni;
#ifdef IEEE80211_DEBUG_REFCNT
IEEE80211_DPRINTF(ni->ni_vap, IEEE80211_MSG_NODE,
"%s (%s:%u) %p<%s> refcnt %d\n", __func__, func, line, ni,
ether_sprintf(ni->ni_macaddr), ieee80211_node_refcnt(ni) - 1);
#endif
_ieee80211_unref_node(ni);
*pni = NULL; /* guard against use */
}
void
ieee80211_unref_node(struct ieee80211_node **pni);
#endif /* #ifdef IEEE80211_DEBUG_REFCNT */
/* Increments reference count of ieee80211_node *ni */
#ifdef IEEE80211_DEBUG_REFCNT
#define ieee80211_add_wds_addr(_table, _node, _mac, _static) \
ieee80211_add_wds_addr_debug(_table, _node, _mac, _static, __func__, __LINE__)
int ieee80211_add_wds_addr_debug(struct ieee80211_node_table *, struct ieee80211_node *,
const u_int8_t *, u_int8_t, const char* func, int line);
#else
int ieee80211_add_wds_addr(struct ieee80211_node_table *, struct ieee80211_node *,
const u_int8_t *, u_int8_t);
#endif /* #ifdef IEEE80211_DEBUG_REFCNT */
/* Decrements reference count of ieee80211_node *ni */
#ifdef IEEE80211_DEBUG_REFCNT
#define ieee80211_remove_wds_addr(_table, _mac) \
ieee80211_remove_wds_addr_debug(_table, _mac, __func__, __LINE__)
void ieee80211_remove_wds_addr_debug(struct ieee80211_node_table *, const u_int8_t *,
const char* func, int line);
#else
void ieee80211_remove_wds_addr(struct ieee80211_node_table *, const u_int8_t *);
void ieee80211_del_wds_node(struct ieee80211_node_table *,
struct ieee80211_node *);
#endif /* #ifdef IEEE80211_DEBUG_REFCNT */
/* Decrements reference count of node, if found */
#ifdef IEEE80211_DEBUG_REFCNT
#define ieee80211_del_wds_node(_table, _node) \
ieee80211_del_wds_node_debug(_table, _node, __func__, __LINE__)
void ieee80211_del_wds_node_debug(struct ieee80211_node_table *, struct ieee80211_node *,
const char* func, int line);
#else
void ieee80211_del_wds_node(struct ieee80211_node_table *, struct ieee80211_node *);
#endif /* #ifdef IEEE80211_DEBUG_REFCNT */
/* Increments reference count of node, if found */
#ifdef IEEE80211_DEBUG_REFCNT
#define ieee80211_find_wds_node(_table, _mac) \
ieee80211_find_wds_node_debug(_table, _mac, __func__, __LINE__)
struct ieee80211_node *ieee80211_find_wds_node_debug(struct ieee80211_node_table *,
const u_int8_t *, const char* func, int line);
#else
struct ieee80211_node *ieee80211_find_wds_node(struct ieee80211_node_table *,
const u_int8_t *);
#endif /* #ifdef IEEE80211_DEBUG_REFCNT */
typedef void ieee80211_iter_func(void *, struct ieee80211_node *);
void ieee80211_iterate_nodes(struct ieee80211_node_table *,
ieee80211_iter_func *, void *);
void ieee80211_iterate_dev_nodes(struct net_device *,
struct ieee80211_node_table *, ieee80211_iter_func *, void *);
void ieee80211_dump_node(struct ieee80211_node_table *,
struct ieee80211_node *);
void ieee80211_dump_nodes(struct ieee80211_node_table *);
/* Returns a node with refcount of one. Caller must release that reference */
#ifdef IEEE80211_DEBUG_REFCNT
#define ieee80211_fakeup_adhoc_node(_vap, _mac) \
ieee80211_fakeup_adhoc_node_debug(_vap, _mac, __func__, __LINE__)
struct ieee80211_node *ieee80211_fakeup_adhoc_node_debug(struct ieee80211vap *,
const u_int8_t macaddr[], const char*, int);
#else
struct ieee80211_node *ieee80211_fakeup_adhoc_node(struct ieee80211vap *,
const u_int8_t macaddr[]);
#endif /* #ifdef IEEE80211_DEBUG_REFCNT */
struct ieee80211_scanparams;
/* Returns a node with refcount of one. Caller must release that reference */
struct ieee80211_node *ieee80211_add_neighbor(struct ieee80211vap *,
const struct ieee80211_frame *, const struct ieee80211_scanparams *);
/* Increments reference count of node */
void ieee80211_node_join(struct ieee80211_node *, int);
/* Decrements reference count of node */
void ieee80211_node_leave(struct ieee80211_node *);
u_int8_t ieee80211_getrssi(struct ieee80211com *);
int32_t ieee80211_get_node_count(struct ieee80211com *);
#endif /* _NET80211_IEEE80211_NODE_H_ */

View File

@ -206,8 +206,14 @@ ieee80211_hardstart(struct sk_buff *skb, struct net_device *dev)
struct ieee80211_node *ni = NULL;
struct ether_header *eh;
/* MT: reset the skb of new frames reaching this layer BEFORE
* we invoke ieee80211_skb_track. */
memset(SKB_CB(skb), 0, sizeof(struct ieee80211_cb));
/* If an skb is passed in directly from the kernel,
* we take responsibility for the reference */
ieee80211_skb_track(skb);
/* NB: parent must be up and running */
if ((parent->flags & (IFF_RUNNING|IFF_UP)) != (IFF_RUNNING|IFF_UP))
goto bad;
@ -281,6 +287,9 @@ ieee80211_hardstart(struct sk_buff *skb, struct net_device *dev)
struct sk_buff *skb1 = skb_clone(skb, GFP_ATOMIC);
if (skb1) {
memset(SKB_CB(skb1), 0, sizeof(struct ieee80211_cb));
#ifdef IEEE80211_DEBUG_REFCNT
SKB_CB(skb1)->tracked = 1;
#endif /* #ifdef IEEE80211_DEBUG_REFCNT */
SKB_CB(skb1)->ni = ieee80211_find_txnode(vap->iv_xrvap,
eh->ether_dhost);
ieee80211_parent_queue_xmit(skb1);
@ -293,7 +302,7 @@ ieee80211_hardstart(struct sk_buff *skb, struct net_device *dev)
bad:
if (skb != NULL)
dev_kfree_skb(skb);
ieee80211_dev_kfree_skb(&skb);
if (ni != NULL)
ieee80211_unref_node(&ni);
return 0;
@ -484,12 +493,6 @@ ieee80211_send_nulldata(struct ieee80211_node *ni)
ieee80211_chan2ieee(ic, ic->ic_curchan),
wh->i_fc[1] & IEEE80211_FC1_PWR_MGT ? "ena" : "dis");
IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
"ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n",
__func__, __LINE__,
ni, ether_sprintf(ni->ni_macaddr),
ieee80211_node_refcnt(ni));
/* XXX assign some priority; this probably is wrong */
skb->priority = WME_AC_BE;
SKB_CB(skb)->ni = PASS_NODE(ni);
@ -553,12 +556,6 @@ ieee80211_send_qosnulldata(struct ieee80211_node *ni, int ac)
M_FLAG_SET(skb, M_UAPSD);
}
IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
"ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n",
__func__, __LINE__,
ni, ether_sprintf(ni->ni_macaddr),
ieee80211_node_refcnt(ni));
(void) ic->ic_mgtstart(ic, skb); /* cheat */
return 0;
@ -634,7 +631,7 @@ ieee80211_skbhdr_adjust(struct ieee80211vap *vap, int hdrsize,
"%s: cannot unshare for encapsulation\n",
__func__);
vap->iv_stats.is_tx_nobuf++;
dev_kfree_skb(skb2);
ieee80211_dev_kfree_skb(&skb2);
return NULL;
}
@ -647,13 +644,13 @@ ieee80211_skbhdr_adjust(struct ieee80211vap *vap, int hdrsize,
if (NULL != skb && SKB_CB(tmp)->ni != NULL) {
SKB_CB(skb)->ni = ieee80211_ref_node(SKB_CB(tmp)->ni);
}
dev_kfree_skb(tmp);
ieee80211_dev_kfree_skb(&tmp);
if (skb == NULL) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT,
"%s: cannot expand storage (head1)\n",
__func__);
vap->iv_stats.is_tx_nobuf++;
dev_kfree_skb(skb2);
ieee80211_dev_kfree_skb(&skb2);
return NULL;
}
/* NB: cb[] area was copied, but not next ptr. must do that
@ -668,14 +665,13 @@ ieee80211_skbhdr_adjust(struct ieee80211vap *vap, int hdrsize,
n = inter_headroom - skb_headroom(skb2);
if (pskb_expand_head(skb2, n,
need_tailroom - skb_tailroom(skb2), GFP_ATOMIC)) {
dev_kfree_skb(skb2);
ieee80211_dev_kfree_skb(&skb2);
IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT,
"%s: cannot expand storage (tail2)\n",
__func__);
vap->iv_stats.is_tx_nobuf++;
/* this shouldn't happen, but don't send first ff either */
dev_kfree_skb(skb);
skb = NULL;
ieee80211_dev_kfree_skb(&skb);
}
} else if (skb_headroom(skb2) < inter_headroom) {
struct sk_buff *tmp = skb2;
@ -685,14 +681,14 @@ ieee80211_skbhdr_adjust(struct ieee80211vap *vap, int hdrsize,
if (NULL != skb2 && SKB_CB(tmp)->ni != NULL) {
SKB_CB(skb2)->ni = ieee80211_ref_node(SKB_CB(tmp)->ni);
}
dev_kfree_skb(tmp);
ieee80211_dev_kfree_skb(&tmp);
if (skb2 == NULL) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT,
"%s: cannot expand storage (head2)\n",
__func__);
vap->iv_stats.is_tx_nobuf++;
/* this shouldn't happen, but don't send first ff either */
dev_kfree_skb(skb);
ieee80211_dev_kfree_skb(&skb);
skb = NULL;
}
}
@ -712,11 +708,11 @@ ieee80211_skbhdr_adjust(struct ieee80211vap *vap, int hdrsize,
n = need_headroom - skb_headroom(skb);
if (pskb_expand_head(skb, n,
need_tailroom - skb_tailroom(skb), GFP_ATOMIC)) {
dev_kfree_skb(skb);
ieee80211_dev_kfree_skb(&skb);
IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT,
"%s: cannot expand storage (tail)\n", __func__);
vap->iv_stats.is_tx_nobuf++;
skb = NULL;
ieee80211_dev_kfree_skb(&skb);
}
} else if (skb_headroom(skb) < need_headroom) {
struct sk_buff *tmp = skb;
@ -725,7 +721,7 @@ ieee80211_skbhdr_adjust(struct ieee80211vap *vap, int hdrsize,
if (NULL != skb && SKB_CB(tmp)->ni != NULL) {
SKB_CB(skb)->ni = ieee80211_ref_node(SKB_CB(tmp)->ni);
}
dev_kfree_skb(tmp);
ieee80211_dev_kfree_skb(&tmp);
if (skb == NULL) {
IEEE80211_DPRINTF(vap, IEEE80211_MSG_OUTPUT,
"%s: cannot expand storage (head)\n", __func__);
@ -1110,7 +1106,7 @@ ieee80211_encap(struct ieee80211_node *ni, struct sk_buff *skb, int *framecnt)
* reuses input skb.
*/
for (skbcnt = 1; skbcnt < fragcnt; skbcnt++) {
tskb = dev_alloc_skb(hdrsize + ciphdrsize + pdusize + tailsize);
tskb = ieee80211_dev_alloc_skb(hdrsize + ciphdrsize + pdusize + tailsize);
if (tskb == NULL)
break;
@ -1224,26 +1220,11 @@ ieee80211_encap(struct ieee80211_node *ni, struct sk_buff *skb, int *framecnt)
return skb;
bad:
if (framelist != NULL) {
struct sk_buff *temp;
tskb = framelist;
while (tskb) {
temp = tskb->next;
tskb->next = NULL;
dev_kfree_skb(tskb);
tskb = temp;
}
ieee80211_dev_kfree_skb_list(&framelist);
}
if (skb != NULL) {
#ifdef ATH_SUPERG_FF
/* FFXXX: rather specific to ff case of only 2 skbs chained */
if (skb->next) {
dev_kfree_skb(skb->next);
skb->next = NULL;
}
#endif
dev_kfree_skb(skb);
ieee80211_dev_kfree_skb_list(&skb);
}
return NULL;
#undef WH4
@ -2022,7 +2003,7 @@ ieee80211_send_mgmt(struct ieee80211_node *ni, int type, int arg)
* [tlv] ssid
* [tlv] supported rates
* [4] power capability (802.11h)
* [28] supported channels element (802.11h)
* [tlv] supported channels element (802.11h)
* [tlv] extended supported rates
* [tlv] WME [if enabled and AP capable]
* [tlv] Atheros advanced capabilities
@ -2233,7 +2214,8 @@ ieee80211_send_pspoll(struct ieee80211_node *ni)
struct sk_buff *skb;
struct ieee80211_ctlframe_addr2 *wh;
skb = dev_alloc_skb(sizeof(struct ieee80211_ctlframe_addr2));
skb = ieee80211_dev_alloc_skb(sizeof(struct ieee80211_ctlframe_addr2));
if (skb == NULL) return;
SKB_CB(skb)->ni = ieee80211_ref_node(ni);
skb->priority = WME_AC_VO;
@ -2250,12 +2232,6 @@ ieee80211_send_pspoll(struct ieee80211_node *ni)
if (IEEE80211_VAP_IS_SLEEPING(ni->ni_vap))
wh->i_fc[1] |= IEEE80211_FC1_PWR_MGT;
IEEE80211_DPRINTF(vap, IEEE80211_MSG_NODE,
"ieee80211_ref_node (%s:%u) %p<%s> refcnt %d\n",
__func__, __LINE__,
ni, ether_sprintf(ni->ni_macaddr),
ieee80211_node_refcnt(ni));
(void) ic->ic_mgtstart(ic, skb); /* cheat */
}

View File

@ -109,16 +109,15 @@ ieee80211_power_vdetach(struct ieee80211vap *vap)
int
ieee80211_node_saveq_drain(struct ieee80211_node *ni)
{
struct ieee80211_cb *cb = NULL;
struct sk_buff *skb;
int qlen;
IEEE80211_NODE_SAVEQ_LOCK_IRQ(ni);
qlen = IEEE80211_NODE_SAVEQ_QLEN(ni);
while ((skb = __skb_dequeue(&ni->ni_savedq)) != NULL) {
cb = (struct ieee80211_cb *) skb->cb;
ieee80211_unref_node(&cb->ni);
dev_kfree_skb_any(skb);
if (SKB_CB(skb)->ni != NULL)
ieee80211_unref_node(&SKB_CB(skb)->ni);
ieee80211_dev_kfree_skb(&skb);
}
IEEE80211_NODE_SAVEQ_UNLOCK_IRQ(ni);
@ -154,7 +153,9 @@ ieee80211_node_saveq_age(struct ieee80211_node *ni)
"discard frame, age %u", M_AGE_GET(skb));
skb = __skb_dequeue(&ni->ni_savedq);
dev_kfree_skb_any(skb);
if( SKB_CB(skb)->ni != NULL )
ieee80211_unref_node(&SKB_CB(skb)->ni);
ieee80211_dev_kfree_skb(&skb);
discard++;
}
if (skb != NULL)
@ -223,7 +224,9 @@ ieee80211_pwrsave(struct ieee80211_node *ni, struct sk_buff *skb)
if (ieee80211_msg_dumppkts(vap))
ieee80211_dump_pkt(ni->ni_ic, skb->data, skb->len, -1, -1);
#endif
dev_kfree_skb(skb);
if( SKB_CB(skb)->ni != NULL )
ieee80211_unref_node(&SKB_CB(skb)->ni);
ieee80211_dev_kfree_skb(&skb);
return;
}

775
net80211/ieee80211_skb.c Normal file
View File

@ -0,0 +1,775 @@
/*-
* Copyright (c) 2007 Michael Taylor, Apprion
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $Id: ieee80211_linux.c 2829 2007-11-05 20:43:50Z mtaylor $
*/
#ifndef EXPORT_SYMTAB
#define EXPORT_SYMTAB
#endif
/*
* IEEE 802.11 support (Linux-specific code)
*/
#ifndef AUTOCONF_INCLUDED
#include <linux/config.h>
#endif
#include <linux/version.h>
#include <linux/module.h>
#include <linux/kmod.h>
#include <linux/init.h>
#include <linux/skbuff.h>
#include <linux/sysctl.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/if_vlan.h>
#include <linux/vmalloc.h>
#include <linux/proc_fs.h>
#include <net/iw_handler.h>
#include <linux/wireless.h>
#include <linux/if_arp.h> /* XXX for ARPHRD_* */
#include <asm/uaccess.h>
#include "if_media.h"
#include "if_ethersubr.h"
#include <net80211/ieee80211_var.h>
#include <net80211/ieee80211_monitor.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,17)
#include <linux/device.h>
#endif
#undef alloc_skb
#undef dev_alloc_skb
#undef dev_kfree_skb
#undef dev_kfree_skb_any
#undef dev_kfree_skb_irq
#undef dev_queue_xmit
#undef kfree_skb
#undef kfree_skb_fast
#undef netif_rx
#undef pskb_copy
#undef skb_clone
#undef skb_copy
#undef skb_copy_expand
#undef skb_get
#undef skb_realloc_headroom
#undef skb_share_check
#undef skb_unshare
#undef vlan_hwaccel_receive_skb
atomic_t skb_total_counter = ATOMIC_INIT(0);
EXPORT_SYMBOL(skb_total_counter);
#ifdef IEEE80211_DEBUG_REFCNT
atomic_t skb_refs_counter = ATOMIC_INIT(0);
EXPORT_SYMBOL(skb_refs_counter);
#endif
/*******************************************************************************
* Debug Helpers
******************************************************************************/
static void skb_print_message(
int show_counter,
const struct sk_buff *skb,
#ifdef IEEE80211_DEBUG_REFCNT
const char* func1, int line1,
#endif
const char* func2, int line2,
const char* message,
...)
{
va_list args;
char skb_count[32] = { '\0' };
char expanded_message[1024] = { '\0' };
if (show_counter) {
#ifdef IEEE80211_DEBUG_REFCNT
snprintf(skb_count,
sizeof(skb_count),
"[#SKB=%05d #REF=%05d] ",
atomic_read(&skb_total_counter),
atomic_read(&skb_refs_counter));
#else
snprintf(skb_count,
sizeof(skb_count),
"[#SKB=%05d] ",
atomic_read(&skb_total_counter));
#endif
}
va_start(args, message);
vsnprintf(expanded_message, sizeof(expanded_message), message, args);
#ifdef IEEE80211_DEBUG_REFCNT
printk("%s: %s%s:%d -> %s:%d %s\n",
#else
printk("%s: %s%s:%d %s\n",
#endif
(skb->input_dev != NULL ?
DEV_NAME(skb->input_dev) :
DEV_NAME(skb->dev)),
skb_count,
#ifdef IEEE80211_DEBUG_REFCNT
func1, line1,
#endif
func2, line2,
expanded_message);
va_end(args);
}
#ifdef IEEE80211_DEBUG_REFCNT
static void
print_skb_refchange_message(
const struct sk_buff *skb, int users_adjustment,
const char* func1, int line1,
const char* func2, int line2);
static void
print_skb_trackchange_message(
const struct sk_buff *skb, int users_adjustment,
const char* func1, int line1,
const char* func2, int line2,
char* message);
/* Called automatically when an SKB reaches zero users,
* reporting any leaked node references. */
static void skb_destructor(struct sk_buff* skb);
static void get_skb_description(char *dst, int dst_size, const char* label, const struct sk_buff *skb, int users_adjustment);
static struct sk_buff *
clean_clone_or_copy(struct sk_buff *skb);
static struct sk_buff *
track_skb(struct sk_buff *skb, int users_adjustment,
const char* func1, int line1,
const char* func2, int line2);
static struct sk_buff *
untrack_skb(struct sk_buff *skb, int users_adjustment,
const char* func1, int line1,
const char* func2, int line2);
#define UNREF_USE_KFREE_SKB 0
#define UNREF_USE_DEV_KFREE_SKB_ANY 1
#define UNREF_USE_DEV_KFREE_SKB 2
#define UNREF_USE_DEV_KFREE_SKB_IRQ 3
/* Assumes SKB is not yet freed at the time of the call and shows the new users
* count as (users - 1). */
static void unref_skb(struct sk_buff *skb, int type,
const char* func1, int line1,
const char* func2, int line2);
/* Assumes SKB reference counter has already been updated and reports count as
* atomic_read(&skb->users). */
static struct sk_buff *
ref_skb(struct sk_buff *skb,
const char* func1, int line1,
const char* func2, int line2);
/* Destructor for reporting node reference leaks */
static void skb_destructor(struct sk_buff* skb) {
/* Report any node reference leaks - caused by kernel net device queue
* dropping buffer, rather than passing it to the driver. */
if ( (ath_debug_global & GLOBAL_DEBUG_SKB) && SKB_CB(skb)->ni != NULL ) {
printk(KERN_ERR "%s:%d - ERROR: non-NULL node pointer in %p, %p<%s>! "
"Leak Detected!\n",
__func__, __LINE__,
skb, SKB_CB(skb)->ni, ether_sprintf(SKB_CB(skb)->ni->ni_macaddr));
}
}
EXPORT_SYMBOL(skb_destructor);
static void get_skb_description(char *dst, int dst_size, const char* label, const struct sk_buff *skb, int users_adjustment) {
dst[0] = '\0';
if (NULL != skb) {
int adj_users = atomic_read(&skb->users) + users_adjustment;
snprintf(dst, dst_size,
" [%s%s%p,users=%d,node=%p%s%s%s,aid=%d%s%s]",
label,
(label != NULL ? ": " : ""),
skb,
adj_users,
(SKB_CB(skb)->ni ? SKB_CB(skb)->ni : NULL),
(SKB_CB(skb)->ni ? "<" : ""),
(SKB_CB(skb)->ni ? ether_sprintf(SKB_CB(skb)->ni->ni_macaddr) : ""),
(SKB_CB(skb)->ni ? ">" : ""),
(SKB_CB(skb)->ni ? SKB_CB(skb)->ni->ni_associd : -1),
((adj_users < 0) ? " ** CORRUPTED **" : ""),
((adj_users == 0) ? " ** RELEASED **" : "")
);
dst[dst_size-1] = '\0';
}
}
static void print_skb_refchange_message(
const struct sk_buff *skb, int users_adjustment,
const char* func1, int line1,
const char* func2, int line2)
{
char skb_desc[128] = { '\0' };
if ( 0 == (ath_debug_global & GLOBAL_DEBUG_SKB))
return;
get_skb_description( skb_desc, sizeof( skb_desc),
"skb", skb, users_adjustment);
skb_print_message(0 /* no global count */, skb,
func1, line1, func2, line2,
skb_desc);
}
static void print_skb_trackchange_message(
const struct sk_buff *skb, int users_adjustment,
const char* func1, int line1,
const char* func2, int line2,
char* message)
{
char skb_desc[128] = { '\0' };
if ( 0 == (ath_debug_global & GLOBAL_DEBUG_SKB))
return;
get_skb_description( skb_desc, sizeof( skb_desc),
"skb", skb, users_adjustment);
skb_print_message(1 /* show global count */, skb,
func1, line1, func2, line2,
"%s%s", skb_desc, message);
}
static struct sk_buff *
clean_clone_or_copy(struct sk_buff *skb) {
SKB_CB(skb)->tracked = 0;
return skb;
}
static struct sk_buff *
track_skb(struct sk_buff *skb, int users_adjustment,
const char* func1, int line1,
const char* func2, int line2)
{
if (NULL == skb) {
skb_print_message(0 /* show_counter */,
skb, func1, line1, func2, line2,
"ERROR: NULL skb received. Skipping.");
return NULL;
}
if (SKB_CB(skb)->tracked) {
skb_print_message(0 /* show_counter */,
skb, func1, line1, func2, line2,
"ERROR: Already tracked skb received. Skipping.");
dump_stack();
return skb;
}
if ( skb_shared(skb) ) {
skb_print_message(0 /* show_counter */,
skb, func1, line1, func2, line2,
"ERROR: Shared skb received. References leaked??");
dump_stack();
}
atomic_inc(&skb_total_counter);
atomic_inc(&skb_refs_counter);
SKB_CB(skb)->tracked = 1;
print_skb_trackchange_message(skb, users_adjustment,
func1, line1, func2, line2,
" is now ** TRACKED **");
/* Always use our debug destructor */
skb->destructor = skb_destructor;
return skb;
}
static struct sk_buff *
untrack_skb(struct sk_buff *skb, int users_adjustment,
const char* func1, int line1,
const char* func2, int line2)
{
if (NULL == skb) {
skb_print_message(0 /* show_counter */,
skb, func1, line1, func2, line2,
"ERROR: NULL skb received. No changes made.");
return NULL;
}
if (!SKB_CB(skb)->tracked) {
skb_print_message(0 /* show_counter */,
skb, func1, line1, func2, line2,
"ERROR: Untracked skb received. No changes made.");
dump_stack();
return skb;
}
if ( skb_shared(skb) ) {
skb_print_message(0 /* show_counter */,
skb, func1, line1, func2, line2,
"ERROR: Shared skb received. References leaked??");
dump_stack();
}
atomic_dec(&skb_total_counter);
atomic_dec(&skb_refs_counter);
SKB_CB(skb)->tracked = 0;
print_skb_trackchange_message(skb, users_adjustment,
func1, line1, func2, line2,
" is now ** UNTRACKED **");
skb->destructor = skb_destructor;
return skb;
}
#define UNREF_USE_KFREE_SKB 0
#define UNREF_USE_DEV_KFREE_SKB_ANY 1
#define UNREF_USE_DEV_KFREE_SKB 2
#define UNREF_USE_DEV_KFREE_SKB_IRQ 3
/* Assumes SKB is not yet freed at the time of the call and shows the new users
* count as (users - 1). */
static void
unref_skb(struct sk_buff *skb, int type,
const char* func1, int line1,
const char* func2, int line2)
{
if (NULL == skb) {
skb_print_message(0 /* show_counter */,
skb, func1, line1, func2, line2,
"ERROR: NULL skb received.");
dump_stack();
return;
}
if (!SKB_CB(skb)->tracked) {
skb_print_message(0 /* show_counter */,
skb, func1, line1, func2, line2,
"ERROR: Untracked skb received. Probable duplicate free error!");
dump_stack();
return;
}
/* If free is unacceptable for current user count, report the error. */
if (atomic_read(&skb->users) < 1) {
skb_print_message(0 /* show_counter */,
skb, func1, line1, func2, line2,
"ERROR: free an skb with %d users",
atomic_read(&skb->users));
dump_stack();
return;
}
/* decrement skb_refs_counter and print a message */
if (skb_shared(skb)) {
atomic_dec(&skb_refs_counter);
print_skb_refchange_message(skb, -1, func1, line1, func2, line2);
}
else {
if (SKB_CB(skb)->ni != NULL) {
printk(KERN_ERR "%s:%d - ERROR: non-NULL node pointer in %p, %p<%s>! "
"Driver Leak Detected!\n",
__func__, __LINE__,
skb, SKB_CB(skb)->ni, ether_sprintf(SKB_CB(skb)->ni->ni_macaddr));
dump_stack();
/* Allow the leak and let programmer fix it, but do not
* report it again in the destructor. */
SKB_CB(skb)->ni = NULL;
}
untrack_skb(skb, -1, func1, line1, func2, line2);
}
if ( (in_irq() || irqs_disabled())
&& (type == UNREF_USE_KFREE_SKB || type == UNREF_USE_DEV_KFREE_SKB))
{
skb_print_message(0 /* show_counter */,
skb, func1, line1, func2, line2,
"ERROR: free an skb in interrupt context using a non-"
"safe form of skb free function.");
type = UNREF_USE_DEV_KFREE_SKB_ANY;
dump_stack();
}
switch(type) {
case UNREF_USE_DEV_KFREE_SKB_ANY:
dev_kfree_skb_any(skb);
break;
case UNREF_USE_DEV_KFREE_SKB_IRQ:
dev_kfree_skb_irq(skb);
break;
case UNREF_USE_DEV_KFREE_SKB:
/* NOTE: dev_kfree_skb is a macro pointing to kfree_skb, so
* fallthrough... */
case UNREF_USE_KFREE_SKB:
/* fallthrough */
default:
kfree_skb(skb);
break;
}
}
/* Assumes SKB reference counter has already been updated and reports count as
* atomic_read(&skb->users). */
static struct sk_buff *
ref_skb(struct sk_buff *skb,
const char* func1, int line1,
const char* func2, int line2)
{
if (NULL == skb) {
skb_print_message(0 /* show_counter */,
skb, func1, line1, func2, line2,
"ERROR: NULL skb received. No changes made.");
dump_stack();
return NULL;
}
if (!SKB_CB(skb)->tracked) {
skb_print_message(0 /* show_counter */,
skb, func1, line1, func2, line2,
"ERROR: Untracked skb received. Probable use after free! "
"No changes made.");
dump_stack();
return skb;
}
print_skb_refchange_message(skb, 0, func1, line1, func2, line2);
return skb;
}
#endif /* #ifdef IEEE80211_DEBUG_REFCNT */
/*******************************************************************************
* Public API
******************************************************************************/
/* ieee80211_dev_kfree_skb will release one reference from SKB.
* If SKB refcount is going to zero:
* - Free the node reference and set it to null.
* - Break the linked list, clearing next skb's prev pointer if possible. */
#ifdef IEEE80211_DEBUG_REFCNT
void ieee80211_dev_kfree_skb_debug(struct sk_buff** pskb, const char* func, int line)
#else
void ieee80211_dev_kfree_skb(struct sk_buff** pskb)
#endif
{
struct sk_buff *skb;
/* Do not fail on null, we are going to use this in cleanup code */
if (!pskb || !(skb = *pskb))
return;
if ( !skb_shared(skb) ) {
/* Release the SKB references, for fragments of chain that are
* unshared... starting at skb passed in.
*/
if (skb->prev == NULL) {
if (skb->next != NULL) {
skb->next->prev = NULL;
}
skb->next = NULL;
skb->prev = NULL;
}
/* Release node reference, if any */
if ( SKB_CB(skb)->ni != NULL ) {
#ifdef IEEE80211_DEBUG_REFCNT
ieee80211_unref_node_debug(&SKB_CB(skb)->ni, func, line);
#else
ieee80211_unref_node(&SKB_CB(skb)->ni);
#endif
}
}
/* Decrement the ref count for the skb, possibly freeing the memory */
#ifdef IEEE80211_DEBUG_REFCNT
unref_skb(skb, UNREF_USE_DEV_KFREE_SKB_ANY,
func, line, __func__, __LINE__);
#else
dev_kfree_skb_any(skb);
#endif
/* guard against use */
*pskb = NULL;
}
/*
* ieee80211_dev_kfree_skb_list will invoke ieee80211_dev_kfree_skb on each node in
* a list of skbs, starting with the first.
*/
#ifdef IEEE80211_DEBUG_REFCNT
void
ieee80211_dev_kfree_skb_list_debug(struct sk_buff** pskb, const char* func, int line)
#else
void
ieee80211_dev_kfree_skb_list(struct sk_buff** pskb)
#endif
{
struct sk_buff *skb, *tskb;
/* Do not fail on null, we are going to use this in cleanup code */
if (!pskb || !(skb = *pskb))
return;
/* free sk_buffs */
while (skb) {
/* Save next skb */
tskb = skb->next;
/* Free the skb, and remove it from next in chain if unshared
* and in a list. */
#ifdef IEEE80211_DEBUG_REFCNT
ieee80211_dev_kfree_skb_debug(&skb, func, line);
#else
ieee80211_dev_kfree_skb(&skb);
#endif
/* Advance to next skb */
skb = tskb;
}
/* guard against use */
*pskb = NULL;
}
#ifdef IEEE80211_DEBUG_REFCNT
struct sk_buff*
ieee80211_dev_alloc_skb_debug(int size, const char* func, int line)
#else
struct sk_buff*
ieee80211_dev_alloc_skb(int size)
#endif
{
/* allocate the skb */
struct sk_buff *skb = dev_alloc_skb(size);
if (skb == NULL) {
skb_print_message(
0 /* show_counter */,
NULL /* skb */,
#ifdef IEEE80211_DEBUG_REFCNT
func, line,
#endif
__func__, __LINE__,
"sk_buff allocation of size %u failed",
size);
return NULL;
}
#ifdef IEEE80211_DEBUG_REFCNT
return track_skb(skb, 0, func, line, __func__, __LINE__);
#else
return skb;
#endif
}
#ifdef IEEE80211_DEBUG_REFCNT
void
ieee80211_skb_track_debug(struct sk_buff *skb, const char* func, int line) {
track_skb(skb, 0 /* users_adjustment */,
func, line, __func__, __LINE__);
}
#else
void
ieee80211_skb_track(struct sk_buff *skb) {
/* Just a dumb counter, in no-debug builds */
atomic_inc(&skb_total_counter);
}
#endif /* #ifdef IEEE80211_DEBUG_REFCNT */
#ifdef IEEE80211_DEBUG_REFCNT
void
ieee80211_skb_untrack_debug(struct sk_buff *skb, const char* func, int line) {
untrack_skb(skb, 0 /* users_adjustment */,
func, line, __func__, __LINE__);
}
#else
void
ieee80211_skb_untrack(struct sk_buff *skb) {
/* Just a dumb counter, in no-debug builds */
atomic_dec(&skb_total_counter);
}
#endif /* #ifdef IEEE80211_DEBUG_REFCNT */
#ifdef IEEE80211_DEBUG_REFCNT
int
ieee80211_skb_counter(void) {
return atomic_read(&skb_total_counter);
}
int
ieee80211_skb_references(void) {
return atomic_read(&skb_refs_counter);
}
#endif /* #ifdef IEEE80211_DEBUG_REFCNT */
/*******************************************************************************
* skbuff leak/refcount debugging Replacement Functions
* PUT last in order to avoid conflicts with use of original functions in
* inline functions above this point.
******************************************************************************/
#ifdef IEEE80211_DEBUG_REFCNT
int vlan_hwaccel_receive_skb_debug(struct sk_buff *skb,
struct vlan_group *grp, unsigned short vlan_tag,
const char* func, int line) {
return vlan_hwaccel_receive_skb(
untrack_skb(skb, 0, func, line, __func__, __LINE__),
grp, vlan_tag);
}
int netif_rx_debug(struct sk_buff *skb, const char* func, int line) {
return netif_rx(untrack_skb(skb, 0, func, line, __func__, __LINE__));
}
struct sk_buff * alloc_skb_debug(unsigned int length, int gfp_mask,
const char *func, int line) {
return track_skb(alloc_skb(length, gfp_mask), 0 /* users_adjustment */,
func, line, __func__, __LINE__);
}
struct sk_buff * dev_alloc_skb_debug(unsigned int length,
const char *func, int line)
{
return track_skb(dev_alloc_skb(length), 0 /* users_adjustment */,
func, line, __func__, __LINE__);
}
struct sk_buff * skb_clone_debug(struct sk_buff *skb, int pri,
const char *func, int line)
{
return track_skb(
clean_clone_or_copy(skb_clone(skb, pri)), 0 /* users_adjustment */,
func, line, __func__, __LINE__);
}
struct sk_buff * skb_copy_debug(struct sk_buff *skb, int pri,
const char *func, int line)
{
return track_skb(
clean_clone_or_copy(skb_copy(skb, pri)), 0 /* users_adjustment */,
func, line, __func__, __LINE__);
}
struct sk_buff * skb_get_debug(struct sk_buff *skb,
const char *func, int line)
{
return ref_skb(skb_get(skb),
func, line, __func__, __LINE__);
}
struct sk_buff * skb_realloc_headroom_debug(struct sk_buff *skb, unsigned int headroom,
const char *func, int line)
{
/* skb_realloc_headroom ALWAYS returns a copy or a clone, refcount of
* new one is always zero and refcount of original is not touched. */
return track_skb(
clean_clone_or_copy(skb_realloc_headroom(skb, headroom)),
0 /* users_adjustment */,
func, line, __func__, __LINE__);
}
struct sk_buff * pskb_copy_debug(struct sk_buff *skb, int pri,
const char *func, int line)
{
return track_skb(
clean_clone_or_copy(pskb_copy(skb, pri)),
0 /* users_adjustment */,
func, line, __func__, __LINE__);
}
int dev_queue_xmit_debug(struct sk_buff *skb,
const char *func, int line)
{
return dev_queue_xmit(untrack_skb(skb, 0, func, line, __func__, __LINE__));
}
struct sk_buff * skb_share_check_debug(struct sk_buff *skb, int pri,
const char *func, int line)
{
might_sleep_if(pri & __GFP_WAIT);
if (skb_shared(skb)) {
struct sk_buff *nskb = track_skb(
clean_clone_or_copy(skb_clone(skb, pri)),
0,
func, line, __func__, __LINE__);
unref_skb(skb, UNREF_USE_DEV_KFREE_SKB_ANY,
func, line, __func__, __LINE__);
skb = nskb;
}
return skb;
}
void kfree_skb_fast_debug(struct sk_buff *skb,
const char* func, int line)
{
/* NOT so fast... */
unref_skb(skb, UNREF_USE_DEV_KFREE_SKB_ANY, func, line, __func__, __LINE__);
}
struct sk_buff * skb_unshare_debug(struct sk_buff *skb, int pri,
const char *func, int line)
{
might_sleep_if(pri & __GFP_WAIT);
if (skb_cloned(skb)) {
struct sk_buff *nskb = track_skb(
clean_clone_or_copy(skb_copy(skb, pri)), 0,
func, line, __func__, __LINE__);
unref_skb(skb, UNREF_USE_DEV_KFREE_SKB_ANY,
func, line, __func__, __LINE__);
skb = nskb;
}
return skb;
}
struct sk_buff * skb_copy_expand_debug(const struct sk_buff *skb, int newheadroom,
int newtailroom, int gfp_mask,
const char *func, int line)
{
return track_skb(
clean_clone_or_copy(
skb_copy_expand(skb, newheadroom, newtailroom, gfp_mask)),
0 /* users_adjustment */,
func, line, __func__, __LINE__);
}
EXPORT_SYMBOL(vlan_hwaccel_receive_skb_debug);
EXPORT_SYMBOL(netif_rx_debug);
EXPORT_SYMBOL(alloc_skb_debug);
EXPORT_SYMBOL(dev_alloc_skb_debug);
EXPORT_SYMBOL(skb_clone_debug);
EXPORT_SYMBOL(skb_copy_debug);
EXPORT_SYMBOL(skb_get_debug);
EXPORT_SYMBOL(skb_realloc_headroom_debug);
EXPORT_SYMBOL(pskb_copy_debug);
EXPORT_SYMBOL(dev_queue_xmit_debug);
EXPORT_SYMBOL(skb_share_check_debug);
EXPORT_SYMBOL(kfree_skb_fast_debug);
EXPORT_SYMBOL(skb_unshare_debug);
EXPORT_SYMBOL(skb_copy_expand_debug);
#endif /* #ifdef IEEE80211_DEBUG_REFCNT */
#ifdef IEEE80211_DEBUG_REFCNT
EXPORT_SYMBOL(ieee80211_dev_kfree_skb_debug);
EXPORT_SYMBOL(ieee80211_dev_kfree_skb_list_debug);
EXPORT_SYMBOL(ieee80211_dev_alloc_skb_debug);
EXPORT_SYMBOL(ieee80211_skb_track_debug);
EXPORT_SYMBOL(ieee80211_skb_untrack_debug);
EXPORT_SYMBOL(ieee80211_skb_counter);
EXPORT_SYMBOL(ieee80211_skb_references);
#else
EXPORT_SYMBOL(ieee80211_dev_kfree_skb);
EXPORT_SYMBOL(ieee80211_dev_kfree_skb_list);
EXPORT_SYMBOL(ieee80211_dev_alloc_skb);
EXPORT_SYMBOL(ieee80211_skb_track);
EXPORT_SYMBOL(ieee80211_skb_untrack);
#endif

200
net80211/ieee80211_skb.h Normal file
View File

@ -0,0 +1,200 @@
/*-
* Copyright (c) 2007 Michael Taylor, Apprion
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $Id: ieee80211_skb.h 2647 2007-08-09 08:43:58Z mtaylor $
*/
#ifndef _NET80211_IEEE80211_SKB_H_
#define _NET80211_IEEE80211_SKB_H_
#include <linux/skbuff.h>
#include <linux/if_vlan.h>
/*******************************************************************************
* Globals
******************************************************************************/
#ifdef IEEE80211_DEBUG_REFCNT
/* Count of currently tracked skbs */
extern atomic_t skb_total_counter;
/* Count of currently tracked skbs' references */
extern atomic_t skb_refs_counter;
#endif /* #ifdef IEEE80211_DEBUG_REFCNT */
/*******************************************************************************
* Public API
******************************************************************************/
/* ieee80211_dev_kfree_skb will release one reference from SKB.
* If SKB refcount is going to zero:
* - Free the node reference and set it to null.
* - Break the linked list, clearing next skb's prev pointer if possible. */
#ifdef IEEE80211_DEBUG_REFCNT
#define ieee80211_dev_kfree_skb(_pskb) \
ieee80211_dev_kfree_skb_debug(_pskb, __func__, __LINE__)
void ieee80211_dev_kfree_skb_debug(struct sk_buff** pskb, const char* func, int line);
#else
void ieee80211_dev_kfree_skb(struct sk_buff** pskb);
#endif
/*
* ieee80211_dev_kfree_skb_list will invoke ieee80211_dev_kfree_skb on each node in
* a list of skbs, starting with the first.
*/
#ifdef IEEE80211_DEBUG_REFCNT
#define ieee80211_dev_kfree_skb_list(_pskb) \
ieee80211_dev_kfree_skb_list_debug(_pskb, __func__, __LINE__)
void ieee80211_dev_kfree_skb_list_debug(struct sk_buff** pskb, const char* func, int line);
#else
void ieee80211_dev_kfree_skb_list(struct sk_buff** pskb);
#endif
#ifdef IEEE80211_DEBUG_REFCNT
#define ieee80211_dev_alloc_skb(_size) \
ieee80211_dev_alloc_skb_debug(_size, __func__, __LINE__)
struct sk_buff* ieee80211_dev_alloc_skb_debug(int size, const char* func, int line);
#else
struct sk_buff* ieee80211_dev_alloc_skb(int size);
#endif
#ifdef IEEE80211_DEBUG_REFCNT
#define ieee80211_skb_track(_skb) \
ieee80211_skb_track_debug(_skb, __func__, __LINE__)
#define ieee80211_skb_untrack(_skb) \
ieee80211_skb_untrack_debug(_skb, __func__, __LINE__)
void ieee80211_skb_track_debug(struct sk_buff *skb,
const char* func, int line);
void ieee80211_skb_untrack_debug(struct sk_buff *skb,
const char* func, int line);
#else
void ieee80211_skb_track(struct sk_buff *skb);
void ieee80211_skb_untrack(struct sk_buff *skb);
#endif
#ifdef IEEE80211_DEBUG_REFCNT
int ieee80211_skb_counter(void);
int ieee80211_skb_references(void);
#else
#define ieee80211_skb_counter() (0)
#define ieee80211_skb_references() (0)
#endif
/*******************************************************************************
* skbuff leak/refcount debugging Replacement Functions
******************************************************************************/
#ifdef IEEE80211_DEBUG_REFCNT
int vlan_hwaccel_receive_skb_debug(struct sk_buff *skb,
struct vlan_group *grp, unsigned short vlan_tag,
const char* func, int line);
int netif_rx_debug(struct sk_buff *skb, const char* func, int line);
struct sk_buff * alloc_skb_debug(unsigned int length, int gfp_mask,
const char *func, int line);
struct sk_buff * dev_alloc_skb_debug(unsigned int length,
const char *func, int line);
struct sk_buff * skb_clone_debug(struct sk_buff *skb, int pri,
const char *func, int line);
struct sk_buff * skb_copy_debug(struct sk_buff *skb, int pri,
const char *func, int line);
struct sk_buff * skb_get_debug(struct sk_buff *skb,
const char *func, int line);
struct sk_buff * skb_realloc_headroom_debug(struct sk_buff *skb, unsigned int headroom,
const char *func, int line);
struct sk_buff * pskb_copy_debug(struct sk_buff *skb, int pri,
const char *func, int line);
int dev_queue_xmit_debug(struct sk_buff *skb,
const char *func, int line);
struct sk_buff * skb_share_check_debug(struct sk_buff *skb, int pri,
const char *func, int line);
void kfree_skb_fast_debug(struct sk_buff *skb,
const char* func, int line);
struct sk_buff * skb_unshare_debug(struct sk_buff *skb, int pri,
const char *func, int line);
struct sk_buff * skb_copy_expand_debug(const struct sk_buff *skb, int newheadroom,
int newtailroom, int gfp_mask,
const char *func, int line);
#undef alloc_skb
#undef dev_alloc_skb
#undef dev_kfree_skb
#undef dev_kfree_skb_any
#undef dev_kfree_skb_irq
#undef dev_queue_xmit
#undef kfree_skb
#undef kfree_skb_fast
#undef netif_rx
#undef pskb_copy
#undef skb_clone
#undef skb_copy
#undef skb_copy_expand
#undef skb_get
#undef skb_realloc_headroom
#undef skb_share_check
#undef skb_unshare
#undef vlan_hwaccel_receive_skb
#define skb_unshare(_skb, _pri) \
skb_unshare_debug(_skb, _pri, __func__, __LINE__)
#define skb_copy_expand(_skb, _newheadroom, _newtailroom, _gfp_mask) \
skb_copy_expand_debug(_skb, _newheadroom, _newtailroom, _gfp_mask, __func__, __LINE__)
#define vlan_hwaccel_receive_skb(_skb, _grp, _tag) \
vlan_hwaccel_receive_skb_debug(_skb, _grp, _tag, __func__, __LINE__)
#define netif_rx(_skb) \
netif_rx_debug(_skb, __func__, __LINE__)
#define alloc_skb(_length, _gfp_mask) \
alloc_skb_debug(_length, _gfp_mask, __func__, __LINE__)
#define dev_alloc_skb(_length) \
dev_alloc_skb_debug(_length, __func__, __LINE__)
#define dev_kfree_skb_irq(_skb) \
unref_skb(_skb, UNREF_USE_DEV_KFREE_SKB_IRQ, __func__, __LINE__)
#define dev_kfree_skb_any(_skb) \
unref_skb(_skb, UNREF_USE_DEV_KFREE_SKB_ANY, __func__, __LINE__)
#define dev_kfree_skb(_skb) \
unref_skb(_skb, UNREF_USE_DEV_KFREE_SKB, __func__, __LINE__)
#define kfree_skb(_skb) \
unref_skb(_skb, UNREF_USE_KFREE_SKB, __func__, __LINE__)
#define skb_clone(_skb, _pri) \
skb_clone_debug(_skb, _pri, __func__, __LINE__)
#define skb_share_check(_skb, _pri) \
skb_share_check_debug(_skb, _pri, __func__, __LINE__)
#define kfree_skb_fast(_skb) \
kfree_skb_fast_debug(_skb, __func__, __LINE__)
#define skb_realloc_headroom(_skb, _headroom) \
skb_realloc_headroom_debug(_skb, _headroom, __func__, __LINE__)
#define pskb_copy(_skb, _pri) \
pskb_copy_debug(_skb, _pri, __func__, __LINE__)
#define skb_get(_skb) \
skb_get_debug(_skb, __func__, __LINE__)
#define skb_copy(_skb, _pri) \
skb_copy_debug(_skb, _pri, __func__, __LINE__)
#define dev_queue_xmit(_skb) \
dev_queue_xmit_debug(_skb, __func__, __LINE__)
#endif /* #ifdef IEEE80211_DEBUG_REFCNT */
#endif /* _NET80211_IEEE80211_SKB_H_ */

View File

@ -266,6 +266,7 @@ struct ieee80211vap {
*/
#include <net80211/ieee80211_debug.h>
#include <net80211/ieee80211_node.h>
#include <net80211/ieee80211_skb.h>
struct ieee80211com {
struct net_device *ic_dev; /* associated device */
@ -365,6 +366,9 @@ struct ieee80211com {
/* Global debug flags applicable to all VAPs */
int ic_debug;
/* used for reference tracking/counting. Nodes are shared between VAPs,
* so we put this here. */
atomic_t ic_node_counter;
/* Virtual AP create/delete */
struct ieee80211vap *(*ic_vap_create)(struct ieee80211com *,
const char *, int, int, struct net_device *);
@ -389,9 +393,17 @@ struct ieee80211com {
void (*ic_newassoc)(struct ieee80211_node *, int);
/* Node state management */
int32_t (*ic_node_count)(struct ieee80211com *);
#ifdef IEEE80211_DEBUG_REFCNT
struct ieee80211_node *(*ic_node_alloc_debug)(struct ieee80211vap *, const char* func, int line);
void (*ic_node_cleanup_debug)(struct ieee80211_node *, const char* func, int line);
void (*ic_node_free_debug)(struct ieee80211_node *, const char* func, int line);
#else
struct ieee80211_node *(*ic_node_alloc)(struct ieee80211vap *);
void (*ic_node_free)(struct ieee80211_node *);
void (*ic_node_cleanup)(struct ieee80211_node *);
void (*ic_node_free)(struct ieee80211_node *);
#endif
u_int8_t (*ic_node_getrssi)(const struct ieee80211_node *);
u_int8_t (*ic_node_move_data)(const struct ieee80211_node *);

View File

@ -3281,8 +3281,10 @@ ieee80211_ioctl_setkey(struct net_device *dev, struct iw_request_info *info,
return -EINVAL;
if (vap->iv_opmode == IEEE80211_M_STA) {
ni = ieee80211_ref_node(vap->iv_bss);
if (!IEEE80211_ADDR_EQ(ik->ik_macaddr, ni->ni_bssid))
if (!IEEE80211_ADDR_EQ(ik->ik_macaddr, ni->ni_bssid)) {
ieee80211_unref_node(&ni);
return -EADDRNOTAVAIL;
}
} else
ni = ieee80211_find_node(&ic->ic_sta, ik->ik_macaddr);
if (ni == NULL)

View File

@ -612,7 +612,7 @@ runtest(struct ieee80211com *ic, struct ciphertest *t)
* Craft frame from plaintext data.
*/
cip = key.wk_cipher;
skb = dev_alloc_skb(t->plaintext_len +
skb = ieee80211_dev_alloc_skb(t->plaintext_len +
cip->ic_header + cip->ic_trailer);
if (skb == NULL) {
printk("FAIL: unable to allocate skbuff\n");
@ -667,13 +667,13 @@ runtest(struct ieee80211com *ic, struct ciphertest *t)
t->plaintext, sizeof(t->plaintext));
goto bad;
}
dev_kfree_skb(skb);
ieee80211_dev_kfree_skb(&skb);
ieee80211_crypto_delkey(ic, &key);
printk("PASS\n");
return 1;
bad:
if (skb != NULL)
dev_kfree_skb(skb);
ieee80211_dev_kfree_skb(&skb);
ieee80211_crypto_delkey(ic, &key);
return 0;
}

View File

@ -184,7 +184,7 @@ tkip_test(struct ieee80211com *ic)
* and then check it against the reference data.
*/
cip = key.wk_cipher;
skb = dev_alloc_skb(sizeof(ref_plaintext) +
skb = ieee80211_dev_alloc_skb(sizeof(ref_plaintext) +
cip->ic_miclen + cip->ic_header + cip->ic_trailer);
if (skb == NULL) {
printk("unable to allocate skbuff\n");
@ -299,7 +299,7 @@ tkip_test(struct ieee80211com *ic)
printk("802.11i TKIP test vectors passed\n");
bad:
if (skb != NULL)
dev_kfree_skb(skb);
ieee80211_dev_kfree_skb(&skb);
ieee80211_crypto_delkey(ic, &key);
}

View File

@ -214,7 +214,7 @@ runtest(struct ieee80211com *ic, struct ciphertest *t)
/*
* Craft encrypted frame from known data.
*/
skb = dev_alloc_skb(t->encrypted_len);
skb = ieee80211_dev_alloc_skb(t->encrypted_len);
if (skb == NULL) {
printk("FAIL: unable to allocate skbuff\n");
goto bad;
@ -270,13 +270,13 @@ runtest(struct ieee80211com *ic, struct ciphertest *t)
goto bad;
}
if (skb != NULL)
dev_kfree_skb(skb);
ieee80211_dev_kfree_skb(&skb);
ieee80211_crypto_delkey(ic, &key);
printk("PASS\n");
return 1;
bad:
if (skb != NULL)
dev_kfree_skb(skb);
ieee80211_dev_kfree_skb(&skb);
ieee80211_crypto_delkey(ic, &key);
return 0;
}