diff options
Diffstat (limited to 'drivers/net/hyperv')
-rw-r--r-- | drivers/net/hyperv/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/hyperv/hyperv_net.h | 1 | ||||
-rw-r--r-- | drivers/net/hyperv/netvsc.c | 51 | ||||
-rw-r--r-- | drivers/net/hyperv/netvsc_drv.c | 6 | ||||
-rw-r--r-- | drivers/net/hyperv/netvsc_trace.c | 7 | ||||
-rw-r--r-- | drivers/net/hyperv/netvsc_trace.h | 182 | ||||
-rw-r--r-- | drivers/net/hyperv/rndis_filter.c | 32 |
7 files changed, 259 insertions, 22 deletions
diff --git a/drivers/net/hyperv/Makefile b/drivers/net/hyperv/Makefile index c8a66827100c..3f25b9c8ea59 100644 --- a/drivers/net/hyperv/Makefile +++ b/drivers/net/hyperv/Makefile @@ -1,3 +1,3 @@ obj-$(CONFIG_HYPERV_NET) += hv_netvsc.o -hv_netvsc-y := netvsc_drv.o netvsc.o rndis_filter.o +hv_netvsc-y := netvsc_drv.o netvsc.o rndis_filter.o netvsc_trace.o diff --git a/drivers/net/hyperv/hyperv_net.h b/drivers/net/hyperv/hyperv_net.h index 32861036c3fc..960f06141472 100644 --- a/drivers/net/hyperv/hyperv_net.h +++ b/drivers/net/hyperv/hyperv_net.h @@ -793,6 +793,7 @@ struct netvsc_device { /* Receive buffer allocated by us but manages by NetVSP */ void *recv_buf; + u32 recv_buf_size; /* allocated bytes */ u32 recv_buf_gpadl_handle; u32 recv_section_cnt; u32 recv_section_size; diff --git a/drivers/net/hyperv/netvsc.c b/drivers/net/hyperv/netvsc.c index 7472172823f3..c9910c33e671 100644 --- a/drivers/net/hyperv/netvsc.c +++ b/drivers/net/hyperv/netvsc.c @@ -36,6 +36,7 @@ #include <asm/sync_bitops.h> #include "hyperv_net.h" +#include "netvsc_trace.h" /* * Switch the data path from the synthetic interface to the VF @@ -57,6 +58,8 @@ void netvsc_switch_datapath(struct net_device *ndev, bool vf) init_pkt->msg.v4_msg.active_dp.active_datapath = NVSP_DATAPATH_SYNTHETIC; + trace_nvsp_send(ndev, init_pkt); + vmbus_sendpacket(dev->channel, init_pkt, sizeof(struct nvsp_message), (unsigned long)init_pkt, @@ -129,6 +132,8 @@ static void netvsc_revoke_buf(struct hv_device *device, revoke_packet->msg.v1_msg. revoke_recv_buf.id = NETVSC_RECEIVE_BUFFER_ID; + trace_nvsp_send(ndev, revoke_packet); + ret = vmbus_sendpacket(device->channel, revoke_packet, sizeof(struct nvsp_message), @@ -169,6 +174,8 @@ static void netvsc_revoke_buf(struct hv_device *device, revoke_packet->msg.v1_msg.revoke_send_buf.id = NETVSC_SEND_BUFFER_ID; + trace_nvsp_send(ndev, revoke_packet); + ret = vmbus_sendpacket(device->channel, revoke_packet, sizeof(struct nvsp_message), @@ -275,6 +282,8 @@ static int netvsc_init_buf(struct hv_device *device, goto cleanup; } + net_device->recv_buf_size = buf_size; + /* * Establish the gpadl handle for this buffer on this * channel. Note: This call uses the vmbus connection rather @@ -298,6 +307,8 @@ static int netvsc_init_buf(struct hv_device *device, init_packet->msg.v1_msg. send_recv_buf.id = NETVSC_RECEIVE_BUFFER_ID; + trace_nvsp_send(ndev, init_packet); + /* Send the gpadl notification request */ ret = vmbus_sendpacket(device->channel, init_packet, sizeof(struct nvsp_message), @@ -377,6 +388,8 @@ static int netvsc_init_buf(struct hv_device *device, net_device->send_buf_gpadl_handle; init_packet->msg.v1_msg.send_send_buf.id = NETVSC_SEND_BUFFER_ID; + trace_nvsp_send(ndev, init_packet); + /* Send the gpadl notification request */ ret = vmbus_sendpacket(device->channel, init_packet, sizeof(struct nvsp_message), @@ -445,6 +458,8 @@ static int negotiate_nvsp_ver(struct hv_device *device, init_packet->msg.init_msg.init.min_protocol_ver = nvsp_ver; init_packet->msg.init_msg.init.max_protocol_ver = nvsp_ver; + trace_nvsp_send(ndev, init_packet); + /* Send the init request */ ret = vmbus_sendpacket(device->channel, init_packet, sizeof(struct nvsp_message), @@ -477,6 +492,8 @@ static int negotiate_nvsp_ver(struct hv_device *device, init_packet->msg.v2_msg.send_ndis_config.capability.teaming = 1; } + trace_nvsp_send(ndev, init_packet); + ret = vmbus_sendpacket(device->channel, init_packet, sizeof(struct nvsp_message), (unsigned long)init_packet, @@ -489,6 +506,7 @@ static int netvsc_connect_vsp(struct hv_device *device, struct netvsc_device *net_device, const struct netvsc_device_info *device_info) { + struct net_device *ndev = hv_get_drvdata(device); static const u32 ver_list[] = { NVSP_PROTOCOL_VERSION_1, NVSP_PROTOCOL_VERSION_2, NVSP_PROTOCOL_VERSION_4, NVSP_PROTOCOL_VERSION_5 @@ -529,6 +547,8 @@ static int netvsc_connect_vsp(struct hv_device *device, send_ndis_ver.ndis_minor_ver = ndis_version & 0xFFFF; + trace_nvsp_send(ndev, init_packet); + /* Send the init request */ ret = vmbus_sendpacket(device->channel, init_packet, sizeof(struct nvsp_message), @@ -747,7 +767,7 @@ static inline int netvsc_send_pkt( struct sk_buff *skb) { struct nvsp_message nvmsg; - struct nvsp_1_message_send_rndis_packet * const rpkt = + struct nvsp_1_message_send_rndis_packet *rpkt = &nvmsg.msg.v1_msg.send_rndis_pkt; struct netvsc_channel * const nvchan = &net_device->chan_table[packet->q_idx]; @@ -776,6 +796,8 @@ static inline int netvsc_send_pkt( if (out_channel->rescind) return -ENODEV; + trace_nvsp_send_pkt(ndev, out_channel, rpkt); + if (packet->page_buf_cnt) { if (packet->cp_partial) pb += packet->rmsg_pgcnt; @@ -1075,13 +1097,30 @@ static int netvsc_receive(struct net_device *ndev, /* Each range represents 1 RNDIS pkt that contains 1 ethernet frame */ for (i = 0; i < count; i++) { - void *data = recv_buf - + vmxferpage_packet->ranges[i].byte_offset; + u32 offset = vmxferpage_packet->ranges[i].byte_offset; u32 buflen = vmxferpage_packet->ranges[i].byte_count; + void *data; + int ret; + + if (unlikely(offset + buflen > net_device->recv_buf_size)) { + status = NVSP_STAT_FAIL; + netif_err(net_device_ctx, rx_err, ndev, + "Packet offset:%u + len:%u too big\n", + offset, buflen); + + continue; + } + + data = recv_buf + offset; + + trace_rndis_recv(ndev, q_idx, data); /* Pass it to the upper layer */ - status = rndis_filter_receive(ndev, net_device, - channel, data, buflen); + ret = rndis_filter_receive(ndev, net_device, + channel, data, buflen); + + if (unlikely(ret != NVSP_STAT_SUCCESS)) + status = NVSP_STAT_FAIL; } enq_receive_complete(ndev, net_device, q_idx, @@ -1143,6 +1182,8 @@ static int netvsc_process_raw_pkt(struct hv_device *device, struct net_device_context *net_device_ctx = netdev_priv(ndev); struct nvsp_message *nvmsg = hv_pkt_data(desc); + trace_nvsp_recv(ndev, channel, nvmsg); + switch (desc->type) { case VM_PKT_COMP: netvsc_send_completion(net_device, channel, device, diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index f28c85d212ce..ecc84954c511 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -55,7 +55,7 @@ #define VF_TAKEOVER_INT (HZ / 10) static unsigned int ring_size __ro_after_init = 128; -module_param(ring_size, uint, S_IRUGO); +module_param(ring_size, uint, 0444); MODULE_PARM_DESC(ring_size, "Ring buffer size (# of pages)"); unsigned int netvsc_ring_bytes __ro_after_init; struct reciprocal_value netvsc_ring_reciprocal __ro_after_init; @@ -66,7 +66,7 @@ static const u32 default_msg = NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_TX_ERR; static int debug = -1; -module_param(debug, int, S_IRUGO); +module_param(debug, int, 0444); MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)"); static void netvsc_change_rx_flags(struct net_device *net, int change) @@ -831,7 +831,7 @@ int netvsc_recv_callback(struct net_device *net, u64_stats_update_end(&rx_stats->syncp); napi_gro_receive(&nvchan->napi, skb); - return 0; + return NVSP_STAT_SUCCESS; } static void netvsc_get_drvinfo(struct net_device *net, diff --git a/drivers/net/hyperv/netvsc_trace.c b/drivers/net/hyperv/netvsc_trace.c new file mode 100644 index 000000000000..bb0ce5a2bcd5 --- /dev/null +++ b/drivers/net/hyperv/netvsc_trace.c @@ -0,0 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#include <linux/netdevice.h> + +#include "hyperv_net.h" + +#define CREATE_TRACE_POINTS +#include "netvsc_trace.h" diff --git a/drivers/net/hyperv/netvsc_trace.h b/drivers/net/hyperv/netvsc_trace.h new file mode 100644 index 000000000000..f7585563dea5 --- /dev/null +++ b/drivers/net/hyperv/netvsc_trace.h @@ -0,0 +1,182 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#if !defined(_NETVSC_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _NETVSC_TRACE_H + +#include <linux/tracepoint.h> + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM netvsc +#define TRACE_INCLUDE_FILE netvsc_trace + +TRACE_DEFINE_ENUM(RNDIS_MSG_PACKET); +TRACE_DEFINE_ENUM(RNDIS_MSG_INDICATE); +TRACE_DEFINE_ENUM(RNDIS_MSG_INIT); +TRACE_DEFINE_ENUM(RNDIS_MSG_INIT_C); +TRACE_DEFINE_ENUM(RNDIS_MSG_HALT); +TRACE_DEFINE_ENUM(RNDIS_MSG_QUERY); +TRACE_DEFINE_ENUM(RNDIS_MSG_QUERY_C); +TRACE_DEFINE_ENUM(RNDIS_MSG_SET); +TRACE_DEFINE_ENUM(RNDIS_MSG_SET_C); +TRACE_DEFINE_ENUM(RNDIS_MSG_RESET); +TRACE_DEFINE_ENUM(RNDIS_MSG_RESET_C); +TRACE_DEFINE_ENUM(RNDIS_MSG_KEEPALIVE); +TRACE_DEFINE_ENUM(RNDIS_MSG_KEEPALIVE_C); + +#define show_rndis_type(type) \ + __print_symbolic(type, \ + { RNDIS_MSG_PACKET, "PACKET" }, \ + { RNDIS_MSG_INDICATE, "INDICATE", }, \ + { RNDIS_MSG_INIT, "INIT", }, \ + { RNDIS_MSG_INIT_C, "INIT_C", }, \ + { RNDIS_MSG_HALT, "HALT", }, \ + { RNDIS_MSG_QUERY, "QUERY", }, \ + { RNDIS_MSG_QUERY_C, "QUERY_C", }, \ + { RNDIS_MSG_SET, "SET", }, \ + { RNDIS_MSG_SET_C, "SET_C", }, \ + { RNDIS_MSG_RESET, "RESET", }, \ + { RNDIS_MSG_RESET_C, "RESET_C", }, \ + { RNDIS_MSG_KEEPALIVE, "KEEPALIVE", }, \ + { RNDIS_MSG_KEEPALIVE_C, "KEEPALIVE_C", }) + +DECLARE_EVENT_CLASS(rndis_msg_class, + TP_PROTO(const struct net_device *ndev, u16 q, + const struct rndis_message *msg), + TP_ARGS(ndev, q, msg), + TP_STRUCT__entry( + __string( name, ndev->name ) + __field( u16, queue ) + __field( u32, req_id ) + __field( u32, msg_type ) + __field( u32, msg_len ) + ), + TP_fast_assign( + __assign_str(name, ndev->name); + __entry->queue = q; + __entry->req_id = msg->msg.init_req.req_id; + __entry->msg_type = msg->ndis_msg_type; + __entry->msg_len = msg->msg_len; + ), + TP_printk("dev=%s q=%u req=%#x type=%s msg_len=%u", + __get_str(name), __entry->queue, __entry->req_id, + show_rndis_type(__entry->msg_type), __entry->msg_len) +); + +DEFINE_EVENT(rndis_msg_class, rndis_send, + TP_PROTO(const struct net_device *ndev, u16 q, + const struct rndis_message *msg), + TP_ARGS(ndev, q, msg) +); + +DEFINE_EVENT(rndis_msg_class, rndis_recv, + TP_PROTO(const struct net_device *ndev, u16 q, + const struct rndis_message *msg), + TP_ARGS(ndev, q, msg) +); + +TRACE_DEFINE_ENUM(NVSP_MSG_TYPE_INIT); +TRACE_DEFINE_ENUM(NVSP_MSG_TYPE_INIT_COMPLETE); +TRACE_DEFINE_ENUM(NVSP_MSG1_TYPE_SEND_NDIS_VER); +TRACE_DEFINE_ENUM(NVSP_MSG1_TYPE_SEND_RECV_BUF); +TRACE_DEFINE_ENUM(NVSP_MSG1_TYPE_SEND_RECV_BUF_COMPLETE); +TRACE_DEFINE_ENUM(NVSP_MSG1_TYPE_REVOKE_RECV_BUF); +TRACE_DEFINE_ENUM(NVSP_MSG1_TYPE_SEND_SEND_BUF); +TRACE_DEFINE_ENUM(NVSP_MSG1_TYPE_SEND_SEND_BUF_COMPLETE); +TRACE_DEFINE_ENUM(NVSP_MSG1_TYPE_REVOKE_SEND_BUF); +TRACE_DEFINE_ENUM(NVSP_MSG1_TYPE_SEND_RNDIS_PKT); +TRACE_DEFINE_ENUM(NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE); +TRACE_DEFINE_ENUM(NVSP_MSG2_TYPE_SEND_NDIS_CONFIG); + +TRACE_DEFINE_ENUM(NVSP_MSG4_TYPE_SEND_VF_ASSOCIATION); +TRACE_DEFINE_ENUM(NVSP_MSG4_TYPE_SWITCH_DATA_PATH); + +TRACE_DEFINE_ENUM(NVSP_MSG5_TYPE_SUBCHANNEL); +TRACE_DEFINE_ENUM(NVSP_MSG5_TYPE_SEND_INDIRECTION_TABLE); + +#define show_nvsp_type(type) \ + __print_symbolic(type, \ + { NVSP_MSG_TYPE_INIT, "INIT" }, \ + { NVSP_MSG_TYPE_INIT_COMPLETE, "INIT_COMPLETE" }, \ + { NVSP_MSG1_TYPE_SEND_NDIS_VER, "SEND_NDIS_VER" }, \ + { NVSP_MSG1_TYPE_SEND_RECV_BUF, "SEND_RECV_BUF" }, \ + { NVSP_MSG1_TYPE_SEND_RECV_BUF_COMPLETE, "SEND_RECV_BUF_COMPLETE" }, \ + { NVSP_MSG1_TYPE_REVOKE_RECV_BUF, "REVOKE_RECV_BUF" }, \ + { NVSP_MSG1_TYPE_SEND_SEND_BUF, "SEND_SEND_BUF" }, \ + { NVSP_MSG1_TYPE_SEND_SEND_BUF_COMPLETE, "SEND_SEND_BUF_COMPLETE" }, \ + { NVSP_MSG1_TYPE_REVOKE_SEND_BUF, "REVOKE_SEND_BUF" }, \ + { NVSP_MSG1_TYPE_SEND_RNDIS_PKT, "SEND_RNDIS_PKT" }, \ + { NVSP_MSG1_TYPE_SEND_RNDIS_PKT_COMPLETE, "SEND_RNDIS_PKT_COMPLETE" },\ + { NVSP_MSG2_TYPE_SEND_NDIS_CONFIG, "SEND_NDIS_CONFIG" }, \ + { NVSP_MSG4_TYPE_SEND_VF_ASSOCIATION, "SEND_VF_ASSOCIATION" }, \ + { NVSP_MSG4_TYPE_SWITCH_DATA_PATH, "SWITCH_DATA_PATH" }, \ + { NVSP_MSG5_TYPE_SUBCHANNEL, "SUBCHANNEL" }, \ + { NVSP_MSG5_TYPE_SEND_INDIRECTION_TABLE, "SEND_INDIRECTION_TABLE" }) + +TRACE_EVENT(nvsp_send, + TP_PROTO(const struct net_device *ndev, + const struct nvsp_message *msg), + TP_ARGS(ndev, msg), + TP_STRUCT__entry( + __string( name, ndev->name ) + __field( u32, msg_type ) + ), + TP_fast_assign( + __assign_str(name, ndev->name); + __entry->msg_type = msg->hdr.msg_type; + ), + TP_printk("dev=%s type=%s", + __get_str(name), + show_nvsp_type(__entry->msg_type)) +); + +TRACE_EVENT(nvsp_send_pkt, + TP_PROTO(const struct net_device *ndev, + const struct vmbus_channel *chan, + const struct nvsp_1_message_send_rndis_packet *rpkt), + TP_ARGS(ndev, chan, rpkt), + TP_STRUCT__entry( + __string( name, ndev->name ) + __field( u16, qid ) + __field( u32, channel_type ) + __field( u32, section_index ) + __field( u32, section_size ) + ), + TP_fast_assign( + __assign_str(name, ndev->name); + __entry->qid = chan->offermsg.offer.sub_channel_index; + __entry->channel_type = rpkt->channel_type; + __entry->section_index = rpkt->send_buf_section_index; + __entry->section_size = rpkt->send_buf_section_size; + ), + TP_printk("dev=%s qid=%u type=%s section=%u size=%d", + __get_str(name), __entry->qid, + __entry->channel_type ? "CONTROL" : "DATA", + __entry->section_index, __entry->section_size) +); + +TRACE_EVENT(nvsp_recv, + TP_PROTO(const struct net_device *ndev, + const struct vmbus_channel *chan, + const struct nvsp_message *msg), + TP_ARGS(ndev, chan, msg), + TP_STRUCT__entry( + __string( name, ndev->name ) + __field( u16, qid ) + __field( u32, msg_type ) + ), + TP_fast_assign( + __assign_str(name, ndev->name); + __entry->qid = chan->offermsg.offer.sub_channel_index; + __entry->msg_type = msg->hdr.msg_type; + ), + TP_printk("dev=%s qid=%u type=%s", + __get_str(name), __entry->qid, + show_nvsp_type(__entry->msg_type)) +); + +#endif /* _NETVSC_TRACE_H */ + +/* This part must be outside protection */ +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH ../../drivers/net/hyperv +#include <trace/define_trace.h> diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index 465c42e30508..6b127be781d9 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -31,6 +31,7 @@ #include <linux/rtnetlink.h> #include "hyperv_net.h" +#include "netvsc_trace.h" static void rndis_set_multicast(struct work_struct *w); @@ -241,6 +242,8 @@ static int rndis_filter_send_request(struct rndis_device *dev, pb[0].len; } + trace_rndis_send(dev->ndev, 0, &req->request_msg); + rcu_read_lock_bh(); ret = netvsc_send(dev->ndev, packet, NULL, pb, NULL); rcu_read_unlock_bh(); @@ -362,14 +365,15 @@ static inline void *rndis_get_ppi(struct rndis_packet *rpkt, u32 type) static int rndis_filter_receive_data(struct net_device *ndev, struct netvsc_device *nvdev, - struct rndis_message *msg, struct vmbus_channel *channel, - void *data, u32 data_buflen) + struct rndis_message *msg, + u32 data_buflen) { struct rndis_packet *rndis_pkt = &msg->msg.pkt; const struct ndis_tcp_ip_checksum_info *csum_info; const struct ndis_pkt_8021q_info *vlan; u32 data_offset; + void *data; /* Remove the rndis header and pass it back up the stack */ data_offset = RNDIS_HEADER_SIZE + rndis_pkt->data_offset; @@ -390,14 +394,15 @@ static int rndis_filter_receive_data(struct net_device *ndev, vlan = rndis_get_ppi(rndis_pkt, IEEE_8021Q_INFO); + csum_info = rndis_get_ppi(rndis_pkt, TCPIP_CHKSUM_PKTINFO); + + data = (void *)msg + data_offset; + /* * Remove the rndis trailer padding from rndis packet message * rndis_pkt->data_len tell us the real data length, we only copy * the data packet to the stack, without the rndis trailer padding */ - data = (void *)((unsigned long)data + data_offset); - csum_info = rndis_get_ppi(rndis_pkt, TCPIP_CHKSUM_PKTINFO); - return netvsc_recv_callback(ndev, nvdev, channel, data, rndis_pkt->data_len, csum_info, vlan); @@ -416,8 +421,8 @@ int rndis_filter_receive(struct net_device *ndev, switch (rndis_msg->ndis_msg_type) { case RNDIS_MSG_PACKET: - return rndis_filter_receive_data(ndev, net_dev, rndis_msg, - channel, data, buflen); + return rndis_filter_receive_data(ndev, net_dev, channel, + rndis_msg, buflen); case RNDIS_MSG_INIT_C: case RNDIS_MSG_QUERY_C: case RNDIS_MSG_SET_C: @@ -434,10 +439,10 @@ int rndis_filter_receive(struct net_device *ndev, "unhandled rndis message (type %u len %u)\n", rndis_msg->ndis_msg_type, rndis_msg->msg_len); - break; + return NVSP_STAT_FAIL; } - return 0; + return NVSP_STAT_SUCCESS; } static int rndis_filter_query_device(struct rndis_device *dev, @@ -942,12 +947,11 @@ static bool netvsc_device_idle(const struct netvsc_device *nvdev) return true; } -static void rndis_filter_halt_device(struct rndis_device *dev) +static void rndis_filter_halt_device(struct netvsc_device *nvdev, + struct rndis_device *dev) { struct rndis_request *request; struct rndis_halt_request *halt; - struct net_device_context *net_device_ctx = netdev_priv(dev->ndev); - struct netvsc_device *nvdev = rtnl_dereference(net_device_ctx->nvdev); /* Attempt to do a rndis device halt */ request = get_rndis_request(dev, RNDIS_MSG_HALT, @@ -1086,6 +1090,8 @@ void rndis_set_subchannel(struct work_struct *w) init_packet->msg.v5_msg.subchn_req.op = NVSP_SUBCHANNEL_ALLOCATE; init_packet->msg.v5_msg.subchn_req.num_subchannels = nvdev->num_chn - 1; + trace_nvsp_send(ndev, init_packet); + ret = vmbus_sendpacket(hv_dev->channel, init_packet, sizeof(struct nvsp_message), (unsigned long)init_packet, @@ -1350,7 +1356,7 @@ void rndis_filter_device_remove(struct hv_device *dev, struct rndis_device *rndis_dev = net_dev->extension; /* Halt and release the rndis device */ - rndis_filter_halt_device(rndis_dev); + rndis_filter_halt_device(net_dev, rndis_dev); net_dev->extension = NULL; |