From c34b19fb4e243b26fa90a322bf32238356493674 Mon Sep 17 00:00:00 2001 From: Deepak Saxena Date: Sat, 19 May 2007 12:00:11 -0700 Subject: [PATCH 01/10] [IPV6]: Add ip6_tunnel.h to headers_install The Mobile IPv6 package (http://www.mobile-ipv6.org/software/) needs this header file to build the tunnelctl component. The header already looks sanitized so is safe to export. Signed-off-by: Deepak Saxena Signed-off-by: David S. Miller --- include/linux/Kbuild | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/Kbuild b/include/linux/Kbuild index bcd01f269f60..e1013156c25e 100644 --- a/include/linux/Kbuild +++ b/include/linux/Kbuild @@ -239,6 +239,7 @@ unifdef-y += ipc.h unifdef-y += ipmi.h unifdef-y += ipv6.h unifdef-y += ipv6_route.h +unifdef-y += ip6_tunnel.h unifdef-y += isdn.h unifdef-y += isdnif.h unifdef-y += isdn_divertif.h From d007da1fa6f0ad5e01ceae4a1f60cdbb23ecd706 Mon Sep 17 00:00:00 2001 From: Ivo van Doorn Date: Sat, 19 May 2007 12:24:39 -0700 Subject: [PATCH 02/10] [RFKILL]: Fix check for correct rfkill allocation coverity has spotted a bug in rfkill.c (bug id #1627), in rfkill_allocate() NULL was returns if the kzalloc() works, and deref the NULL pointer if it fails, Signed-off-by: Ivo van Doorn Signed-off-by: David S. Miller --- net/rfkill/rfkill.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/rfkill/rfkill.c b/net/rfkill/rfkill.c index a973603e3880..f3986d498b40 100644 --- a/net/rfkill/rfkill.c +++ b/net/rfkill/rfkill.c @@ -296,7 +296,7 @@ struct rfkill *rfkill_allocate(struct device *parent, enum rfkill_type type) struct device *dev; rfkill = kzalloc(sizeof(struct rfkill), GFP_KERNEL); - if (rfkill) + if (!rfkill) return NULL; mutex_init(&rfkill->mutex); From b6ccc67d8e42e38936df330b26ee6d022dda8a64 Mon Sep 17 00:00:00 2001 From: Mikael Pettersson Date: Sat, 19 May 2007 13:55:25 -0700 Subject: [PATCH 03/10] [NET]: Fix net/core/skbuff.c gcc-3.2.3 compilation error Compiling 2.6.22-rc1 with gcc-3.2.3 for i486 fails with: gcc -m32 -Wp,-MD,net/core/.skbuff.o.d -nostdinc -isystem /home/mikpe/pkgs/linux-x86/gnu/lib/gcc-lib/i486-pc-linux-gnu/3.2.3/include -D__KERNEL__ -Iinclude -include include/linux/autoconf.h -Wall -Wundef -Wstrict-prototypes -Wno-trigraphs -fno-strict-aliasing -fno-common -O2 -pipe -msoft-float -mregparm=3 -freg-struct-return -mpreferred-stack-boundary=4 -march=i486 -ffreestanding -maccumulate-outgoing-args -DCONFIG_AS_CFI=1 -Iinclude/asm-i386/mach-default -fomit-frame-pointer -D"KBUILD_STR(s)=#s" -D"KBUILD_BASENAME=KBUILD_STR(skbuff)" -D"KBUILD_MODNAME=KBUILD_STR(skbuff)" -c -o net/core/skbuff.o net/core/skbuff.c net/core/skbuff.c:648:1: directives may not be used inside a macro argument net/core/skbuff.c:647:39: unterminated argument list invoking macro "memcpy" net/core/skbuff.c: In function `pskb_expand_head': net/core/skbuff.c:651: `memcpy' undeclared (first use in this function) net/core/skbuff.c:651: (Each undeclared identifier is reported only once net/core/skbuff.c:651: for each function it appears in.) net/core/skbuff.c:651: syntax error before "skb" make[2]: *** [net/core/skbuff.o] Error 1 make[1]: *** [net/core] Error 2 make: *** [net] Error 2 The patch below implements a simple workaround which is to clone the offending memcpy() call and specialise it for the two different scenarios. Other workarounds are of course possible: e.g. bind the varying parameter in a local variable, or use a macro or inline function to perform the varying computation. Signed-off-by: Mikael Pettersson Signed-off-by: David S. Miller --- net/core/skbuff.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 142257307fa2..7c6a34e21eee 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -644,11 +644,10 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, /* Copy only real data... and, alas, header. This should be * optimized for the cases when header is void. */ - memcpy(data + nhead, skb->head, #ifdef NET_SKBUFF_DATA_USES_OFFSET - skb->tail); + memcpy(data + nhead, skb->head, skb->tail); #else - skb->tail - skb->head); + memcpy(data + nhead, skb->head, skb->tail - skb->head); #endif memcpy(data + size, skb_end_pointer(skb), sizeof(struct skb_shared_info)); From 463236557db4b5d4de9eb3fafa2e7d7905ac65ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Sat, 19 May 2007 13:56:23 -0700 Subject: [PATCH 04/10] [TCP] FRTO: Add missing ECN CWR sending to one of the responses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The conservative spurious RTO response did not queue CWR even though the sending rate was lowered. Whenever reduction happens regardless of reason, CWR should be sent (forgetting to send it is not very fatal though). A better approach would be to queue CWR when one of the sending rate reducing responses (rate-halving one or this conservative response) is used already at RTO. Doing that would allow CWR to be sent along with the two new data segments that are sent during FRTO. However, it's a bit "racy" because userland could tune the response sysctl to a more aggressive one in between. Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller --- net/ipv4/tcp_input.c | 1 + 1 file changed, 1 insertion(+) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 7641b2761a14..7ecdc89229e8 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -2608,6 +2608,7 @@ static void tcp_conservative_spur_to_response(struct tcp_sock *tp) { tp->snd_cwnd = min(tp->snd_cwnd, tp->snd_ssthresh); tp->snd_cwnd_cnt = 0; + TCP_ECN_queue_cwr(tp); tcp_moderate_cwnd(tp); } From 580e572a4a1bfea2f42af63ba4785ac7dfbcb45d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= Date: Sat, 19 May 2007 13:56:57 -0700 Subject: [PATCH 05/10] [TCP] FRTO: Prevent state inconsistency in corner cases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit State could become inconsistent in two cases: 1) Userspace disabled FRTO by tuning sysctl when one of the TCP flows was in the middle of FRTO algorithm (and then RTO is again triggered) 2) SACK reneging occurs during FRTO algorithm A simple solution is just to abort the previous FRTO when such obscure condition occurs... Signed-off-by: Ilpo Järvinen Signed-off-by: David S. Miller --- net/ipv4/tcp_input.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 7ecdc89229e8..38cb25b48bf3 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -1501,6 +1501,8 @@ void tcp_enter_loss(struct sock *sk, int how) tcp_set_ca_state(sk, TCP_CA_Loss); tp->high_seq = tp->snd_nxt; TCP_ECN_queue_cwr(tp); + /* Abort FRTO algorithm if one is in progress */ + tp->frto_counter = 0; clear_all_retrans_hints(tp); } From c92b3a2f1f11655ecf6774b745017a414241d07c Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Sat, 19 May 2007 14:21:18 -0700 Subject: [PATCH 06/10] [IPSEC] pfkey: Load specific algorithm in pfkey_add rather than all This is a natural extension of the changeset [XFRM]: Probe selected algorithm only. which only removed the probe call for xfrm_user. This patch does exactly the same thing for af_key. In other words, we load the algorithm requested by the user rather than everything when adding xfrm states in af_key. Signed-off-by: Herbert Xu Signed-off-by: David S. Miller --- net/key/af_key.c | 2 - net/xfrm/xfrm_algo.c | 148 +++++++++++++++++++++++-------------------- 2 files changed, 79 insertions(+), 71 deletions(-) diff --git a/net/key/af_key.c b/net/key/af_key.c index a99444142dc7..d302ddae580c 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -1448,8 +1448,6 @@ static int pfkey_add(struct sock *sk, struct sk_buff *skb, struct sadb_msg *hdr, int err; struct km_event c; - xfrm_probe_algs(); - x = pfkey_msg2xfrm_state(hdr, ext_hdrs); if (IS_ERR(x)) return PTR_ERR(x); diff --git a/net/xfrm/xfrm_algo.c b/net/xfrm/xfrm_algo.c index 6249a9405bb8..8a72def25a34 100644 --- a/net/xfrm/xfrm_algo.c +++ b/net/xfrm/xfrm_algo.c @@ -347,67 +347,44 @@ static inline int calg_entries(void) return ARRAY_SIZE(calg_list); } -/* Todo: generic iterators */ -struct xfrm_algo_desc *xfrm_aalg_get_byid(int alg_id) -{ - int i; - - for (i = 0; i < aalg_entries(); i++) { - if (aalg_list[i].desc.sadb_alg_id == alg_id) { - if (aalg_list[i].available) - return &aalg_list[i]; - else - break; - } - } - return NULL; -} -EXPORT_SYMBOL_GPL(xfrm_aalg_get_byid); - -struct xfrm_algo_desc *xfrm_ealg_get_byid(int alg_id) -{ - int i; - - for (i = 0; i < ealg_entries(); i++) { - if (ealg_list[i].desc.sadb_alg_id == alg_id) { - if (ealg_list[i].available) - return &ealg_list[i]; - else - break; - } - } - return NULL; -} -EXPORT_SYMBOL_GPL(xfrm_ealg_get_byid); - -struct xfrm_algo_desc *xfrm_calg_get_byid(int alg_id) -{ - int i; - - for (i = 0; i < calg_entries(); i++) { - if (calg_list[i].desc.sadb_alg_id == alg_id) { - if (calg_list[i].available) - return &calg_list[i]; - else - break; - } - } - return NULL; -} -EXPORT_SYMBOL_GPL(xfrm_calg_get_byid); - -static struct xfrm_algo_desc *xfrm_get_byname(struct xfrm_algo_desc *list, - int entries, u32 type, u32 mask, - char *name, int probe) +struct xfrm_algo_list { + struct xfrm_algo_desc *algs; + int entries; + u32 type; + u32 mask; +}; + +static const struct xfrm_algo_list xfrm_aalg_list = { + .algs = aalg_list, + .entries = ARRAY_SIZE(aalg_list), + .type = CRYPTO_ALG_TYPE_HASH, + .mask = CRYPTO_ALG_TYPE_HASH_MASK | CRYPTO_ALG_ASYNC, +}; + +static const struct xfrm_algo_list xfrm_ealg_list = { + .algs = ealg_list, + .entries = ARRAY_SIZE(ealg_list), + .type = CRYPTO_ALG_TYPE_BLKCIPHER, + .mask = CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_ASYNC, +}; + +static const struct xfrm_algo_list xfrm_calg_list = { + .algs = calg_list, + .entries = ARRAY_SIZE(calg_list), + .type = CRYPTO_ALG_TYPE_COMPRESS, + .mask = CRYPTO_ALG_TYPE_MASK | CRYPTO_ALG_ASYNC, +}; + +static struct xfrm_algo_desc *xfrm_find_algo( + const struct xfrm_algo_list *algo_list, + int match(const struct xfrm_algo_desc *entry, const void *data), + const void *data, int probe) { + struct xfrm_algo_desc *list = algo_list->algs; int i, status; - if (!name) - return NULL; - - for (i = 0; i < entries; i++) { - if (strcmp(name, list[i].name) && - (!list[i].compat || strcmp(name, list[i].compat))) + for (i = 0; i < algo_list->entries; i++) { + if (!match(list + i, data)) continue; if (list[i].available) @@ -416,8 +393,8 @@ static struct xfrm_algo_desc *xfrm_get_byname(struct xfrm_algo_desc *list, if (!probe) break; - status = crypto_has_alg(list[i].name, type, - mask | CRYPTO_ALG_ASYNC); + status = crypto_has_alg(list[i].name, algo_list->type, + algo_list->mask); if (!status) break; @@ -427,27 +404,60 @@ static struct xfrm_algo_desc *xfrm_get_byname(struct xfrm_algo_desc *list, return NULL; } +static int xfrm_alg_id_match(const struct xfrm_algo_desc *entry, + const void *data) +{ + return entry->desc.sadb_alg_id == (int)data; +} + +struct xfrm_algo_desc *xfrm_aalg_get_byid(int alg_id) +{ + return xfrm_find_algo(&xfrm_aalg_list, xfrm_alg_id_match, + (void *)alg_id, 1); +} +EXPORT_SYMBOL_GPL(xfrm_aalg_get_byid); + +struct xfrm_algo_desc *xfrm_ealg_get_byid(int alg_id) +{ + return xfrm_find_algo(&xfrm_ealg_list, xfrm_alg_id_match, + (void *)alg_id, 1); +} +EXPORT_SYMBOL_GPL(xfrm_ealg_get_byid); + +struct xfrm_algo_desc *xfrm_calg_get_byid(int alg_id) +{ + return xfrm_find_algo(&xfrm_calg_list, xfrm_alg_id_match, + (void *)alg_id, 1); +} +EXPORT_SYMBOL_GPL(xfrm_calg_get_byid); + +static int xfrm_alg_name_match(const struct xfrm_algo_desc *entry, + const void *data) +{ + const char *name = data; + + return name && (!strcmp(name, entry->name) || + (entry->compat && !strcmp(name, entry->compat))); +} + struct xfrm_algo_desc *xfrm_aalg_get_byname(char *name, int probe) { - return xfrm_get_byname(aalg_list, aalg_entries(), - CRYPTO_ALG_TYPE_HASH, CRYPTO_ALG_TYPE_HASH_MASK, - name, probe); + return xfrm_find_algo(&xfrm_aalg_list, xfrm_alg_name_match, name, + probe); } EXPORT_SYMBOL_GPL(xfrm_aalg_get_byname); struct xfrm_algo_desc *xfrm_ealg_get_byname(char *name, int probe) { - return xfrm_get_byname(ealg_list, ealg_entries(), - CRYPTO_ALG_TYPE_BLKCIPHER, CRYPTO_ALG_TYPE_MASK, - name, probe); + return xfrm_find_algo(&xfrm_ealg_list, xfrm_alg_name_match, name, + probe); } EXPORT_SYMBOL_GPL(xfrm_ealg_get_byname); struct xfrm_algo_desc *xfrm_calg_get_byname(char *name, int probe) { - return xfrm_get_byname(calg_list, calg_entries(), - CRYPTO_ALG_TYPE_COMPRESS, CRYPTO_ALG_TYPE_MASK, - name, probe); + return xfrm_find_algo(&xfrm_calg_list, xfrm_alg_name_match, name, + probe); } EXPORT_SYMBOL_GPL(xfrm_calg_get_byname); From 5397e97d7533a03b28a7b8aeee648cbb36a8afc6 Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Sat, 19 May 2007 14:23:52 -0700 Subject: [PATCH 07/10] [NETFILTER]: nf_conntrack: fix use-after-free in helper destroy callback invocation When the helper module is removed for a master connection that has a fulfilled expectation, but has already timed out and got removed from the hash tables, nf_conntrack_helper_unregister can't find the master connection to unset the helper, causing a use-after-free when the expected connection is destroyed and releases the last reference to the master. The helper destroy callback was introduced for the PPtP helper to clean up expectations and expected connections when the master connection times out, but doing this from destroy_conntrack only works for unfulfilled expectations since expected connections hold a reference to the master, preventing its destruction. Move the destroy callback to the timeout function, which fixes both problems. Reported/tested by Gabor Burjan . Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- net/netfilter/nf_conntrack_core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index e8b5c2d7db62..483e927a9ca4 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -298,7 +298,6 @@ static void destroy_conntrack(struct nf_conntrack *nfct) { struct nf_conn *ct = (struct nf_conn *)nfct; - struct nf_conn_help *help = nfct_help(ct); struct nf_conntrack_l4proto *l4proto; typeof(nf_conntrack_destroyed) destroyed; @@ -309,9 +308,6 @@ destroy_conntrack(struct nf_conntrack *nfct) nf_conntrack_event(IPCT_DESTROY, ct); set_bit(IPS_DYING_BIT, &ct->status); - if (help && help->helper && help->helper->destroy) - help->helper->destroy(ct); - /* To make sure we don't get any weird locking issues here: * destroy_conntrack() MUST NOT be called with a write lock * to nf_conntrack_lock!!! -HW */ @@ -353,6 +349,10 @@ destroy_conntrack(struct nf_conntrack *nfct) static void death_by_timeout(unsigned long ul_conntrack) { struct nf_conn *ct = (void *)ul_conntrack; + struct nf_conn_help *help = nfct_help(ct); + + if (help && help->helper && help->helper->destroy) + help->helper->destroy(ct); write_lock_bh(&nf_conntrack_lock); /* Inside lock so preempt is disabled on module removal path. From 3ad2a6fb6bcc2f464cdde093a76b76b90b90c66c Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Sat, 19 May 2007 14:24:16 -0700 Subject: [PATCH 08/10] [NETFILTER]: nf_conntrack_ipv4: fix incorrect #ifdef config name The option is named CONFIG_NF_NAT not CONFIG_IP_NF_NAT. Remove the ifdef completely since helpers also expect defragmented packet even without NAT. Noticed by Robert P. J. Day Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index 0654eaae70c9..fd62a41d69cc 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -154,12 +154,10 @@ static unsigned int ipv4_conntrack_defrag(unsigned int hooknum, const struct net_device *out, int (*okfn)(struct sk_buff *)) { -#if !defined(CONFIG_IP_NF_NAT) && !defined(CONFIG_IP_NF_NAT_MODULE) /* Previously seen (loopback)? Ignore. Do this before fragment check. */ if ((*pskb)->nfct) return NF_ACCEPT; -#endif /* Gather fragments. */ if (ip_hdr(*pskb)->frag_off & htons(IP_MF | IP_OFFSET)) { From d8cf27287ac7fb5cbfcc4139917a997c39d841ca Mon Sep 17 00:00:00 2001 From: Patrick McHardy Date: Sat, 19 May 2007 14:44:15 -0700 Subject: [PATCH 09/10] [IPV4]: icmp: fix crash with sysctl_icmp_errors_use_inbound_ifaddr When icmp_send is called on the local output path before the packet hits ip_output, skb->dev is not set, causing a crash when sysctl_icmp_errors_use_inbound_ifaddr is set. This can happen with the netfilter REJECT target or IPsec tunnels. Let routing decide the ICMP source address in that case, since the packet is locally generated there is no inbound interface and the sysctl should not apply. The option actually seems to be unfixable broken, on the path after ip_output() skb->dev points to the outgoing device and we don't know the incoming device anymore, so its going to do the absolute wrong thing and pick the address of the outgoing interface. Add a comment about this. Reported by Curtis Doty . Signed-off-by: Patrick McHardy Signed-off-by: David S. Miller --- net/ipv4/icmp.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index d38cbba92a4d..e238b17f554c 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -514,7 +514,10 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) saddr = iph->daddr; if (!(rt->rt_flags & RTCF_LOCAL)) { - if (sysctl_icmp_errors_use_inbound_ifaddr) + /* This is broken, skb_in->dev points to the outgoing device + * after the packet passes through ip_output(). + */ + if (skb_in->dev && sysctl_icmp_errors_use_inbound_ifaddr) saddr = inet_select_addr(skb_in->dev, 0, RT_SCOPE_LINK); else saddr = 0; From 9093bbb2d96d0184f037cea9b4e952a44ebe7c32 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Sat, 19 May 2007 15:39:25 -0700 Subject: [PATCH 10/10] [NET]: Fix race condition about network device name allocation. Kenji Kaneshige found this race between device removal and registration. On unregister it is possible for the old device to exist, because sysfs file is still open. A new device with 'eth%d' will select the same name, but sysfs kobject register will fial. The following changes the shutdown order slightly. It hold a removes the sysfs entries earlier (on unregister_netdevice), but holds a kobject reference. Then when todo runs the actual last put free happens. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/core/dev.c | 10 ++++++---- net/core/net-sysfs.c | 8 +++++++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/net/core/dev.c b/net/core/dev.c index f2b61111e26d..5a7f20f78574 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3314,7 +3314,6 @@ void netdev_run_todo(void) continue; } - netdev_unregister_sysfs(dev); dev->reg_state = NETREG_UNREGISTERED; netdev_wait_allrefs(dev); @@ -3325,11 +3324,11 @@ void netdev_run_todo(void) BUG_TRAP(!dev->ip6_ptr); BUG_TRAP(!dev->dn_ptr); - /* It must be the very last action, - * after this 'dev' may point to freed up memory. - */ if (dev->destructor) dev->destructor(dev); + + /* Free network device */ + kobject_put(&dev->dev.kobj); } out: @@ -3480,6 +3479,9 @@ void unregister_netdevice(struct net_device *dev) /* Notifier chain MUST detach us from master device. */ BUG_TRAP(!dev->master); + /* Remove entries from sysfs */ + netdev_unregister_sysfs(dev); + /* Finish processing unregister after unlock */ net_set_todo(dev); diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index b21307b15b82..5c19b0646d7a 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -456,9 +456,15 @@ static struct class net_class = { #endif }; +/* Delete sysfs entries but hold kobject reference until after all + * netdev references are gone. + */ void netdev_unregister_sysfs(struct net_device * net) { - device_del(&(net->dev)); + struct device *dev = &(net->dev); + + kobject_get(&dev->kobj); + device_del(dev); } /* Create sysfs entries for network device. */