Skip to content

Commit

Permalink
hv_netvsc: untangle the pointer mess
Browse files Browse the repository at this point in the history
We have the following structures keeping netvsc adapter state:
- struct net_device
- struct net_device_context
- struct netvsc_device
- struct rndis_device
- struct hv_device
and there are pointers/dependencies between them:
- struct net_device_context is contained in struct net_device
- struct hv_device has driver_data pointer which points to
  'struct net_device' OR 'struct netvsc_device' depending on driver's
  state (!).
- struct net_device_context has a pointer to 'struct hv_device'.
- struct netvsc_device has pointers to 'struct hv_device' and
  'struct net_device_context'.
- struct rndis_device has a pointer to 'struct netvsc_device'.

Different functions get different structures as parameters and use these
pointers for traveling. The problem is (in addition to keeping in mind
this complex graph) that some of these structures (struct netvsc_device
and struct rndis_device) are being removed and re-created on mtu change
(as we implement it as re-creation of hyper-v device) so our travel using
these pointers is dangerous.

Simplify this to a the following:
- add struct netvsc_device pointer to struct net_device_context (which is
  a part of struct net_device and thus never disappears)
- remove struct hv_device and struct net_device_context pointers from
  struct netvsc_device
- replace pointer to 'struct netvsc_device' with pointer to
  'struct net_device'.
- always keep 'struct net_device' in hv_device driver_data.

We'll end up with the following 'circular' structure:

net_device:
 [net_device_context] -> netvsc_device -> rndis_device -> net_device
                      -> hv_device -> net_device

On MTU change we'll be removing the 'netvsc_device -> rndis_device'
branch and re-creating it making the synchronization easier.

There is one additional redundant pointer left, it is struct net_device
link in struct netvsc_device, it is going to be removed in a separate
commit.

Signed-off-by: Vitaly Kuznetsov <vkuznets@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Vitaly Kuznetsov authored and David S. Miller committed May 16, 2016
1 parent 1bdcec8 commit 3d541ac
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 122 deletions.
10 changes: 4 additions & 6 deletions drivers/net/hyperv/hyperv_net.h
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ enum rndis_device_state {
};

struct rndis_device {
struct netvsc_device *net_dev;
struct net_device *ndev;

enum rndis_device_state state;
bool link_state;
Expand All @@ -173,6 +173,7 @@ struct rndis_device {

/* Interface */
struct rndis_message;
struct netvsc_device;
int netvsc_device_add(struct hv_device *device, void *additional_info);
int netvsc_device_remove(struct hv_device *device);
int netvsc_send(struct hv_device *device,
Expand Down Expand Up @@ -653,6 +654,8 @@ struct garp_wrk {
struct net_device_context {
/* point back to our device context */
struct hv_device *device_ctx;
/* netvsc_device */
struct netvsc_device *nvdev;
/* reconfigure work */
struct delayed_work dwork;
/* last reconfig time */
Expand All @@ -679,8 +682,6 @@ struct net_device_context {

/* Per netvsc device */
struct netvsc_device {
struct hv_device *dev;

u32 nvsp_version;

atomic_t num_outstanding_sends;
Expand Down Expand Up @@ -734,9 +735,6 @@ struct netvsc_device {
u32 max_pkt; /* max number of pkt in one send, e.g. 8 */
u32 pkt_align; /* alignment bytes, e.g. 8 */

/* The net device context */
struct net_device_context *nd_ctx;

/* 1: allocated, serial number is valid. 0: not allocated */
u32 vf_alloc;
/* Serial number of the VF to team with */
Expand Down
82 changes: 33 additions & 49 deletions drivers/net/hyperv/netvsc.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,9 @@
void netvsc_switch_datapath(struct netvsc_device *nv_dev, bool vf)
{
struct nvsp_message *init_pkt = &nv_dev->channel_init_pkt;
struct hv_device *dev = nv_dev->dev;
struct net_device *ndev = nv_dev->ndev;
struct net_device_context *net_device_ctx = netdev_priv(ndev);
struct hv_device *dev = net_device_ctx->device_ctx;

memset(init_pkt, 0, sizeof(struct nvsp_message));
init_pkt->hdr.msg_type = NVSP_MSG4_TYPE_SWITCH_DATA_PATH;
Expand All @@ -62,6 +64,7 @@ static struct netvsc_device *alloc_net_device(struct hv_device *device)
{
struct netvsc_device *net_device;
struct net_device *ndev = hv_get_drvdata(device);
struct net_device_context *net_device_ctx = netdev_priv(ndev);

net_device = kzalloc(sizeof(struct netvsc_device), GFP_KERNEL);
if (!net_device)
Expand All @@ -77,15 +80,15 @@ static struct netvsc_device *alloc_net_device(struct hv_device *device)
net_device->destroy = false;
atomic_set(&net_device->open_cnt, 0);
atomic_set(&net_device->vf_use_cnt, 0);
net_device->dev = device;
net_device->ndev = ndev;
net_device->max_pkt = RNDIS_MAX_PKT_DEFAULT;
net_device->pkt_align = RNDIS_PKT_ALIGN_DEFAULT;

net_device->vf_netdev = NULL;
net_device->vf_inject = false;

hv_set_drvdata(device, net_device);
net_device_ctx->nvdev = net_device;

return net_device;
}

Expand All @@ -97,9 +100,10 @@ static void free_netvsc_device(struct netvsc_device *nvdev)

static struct netvsc_device *get_outbound_net_device(struct hv_device *device)
{
struct netvsc_device *net_device;
struct net_device *ndev = hv_get_drvdata(device);
struct net_device_context *net_device_ctx = netdev_priv(ndev);
struct netvsc_device *net_device = net_device_ctx->nvdev;

net_device = hv_get_drvdata(device);
if (net_device && net_device->destroy)
net_device = NULL;

Expand All @@ -108,9 +112,9 @@ static struct netvsc_device *get_outbound_net_device(struct hv_device *device)

static struct netvsc_device *get_inbound_net_device(struct hv_device *device)
{
struct netvsc_device *net_device;

net_device = hv_get_drvdata(device);
struct net_device *ndev = hv_get_drvdata(device);
struct net_device_context *net_device_ctx = netdev_priv(ndev);
struct netvsc_device *net_device = net_device_ctx->nvdev;

if (!net_device)
goto get_in_err;
Expand All @@ -124,11 +128,13 @@ static struct netvsc_device *get_inbound_net_device(struct hv_device *device)
}


static int netvsc_destroy_buf(struct netvsc_device *net_device)
static int netvsc_destroy_buf(struct hv_device *device)
{
struct nvsp_message *revoke_packet;
int ret = 0;
struct net_device *ndev = net_device->ndev;
struct net_device *ndev = hv_get_drvdata(device);
struct net_device_context *net_device_ctx = netdev_priv(ndev);
struct netvsc_device *net_device = net_device_ctx->nvdev;

/*
* If we got a section count, it means we received a
Expand All @@ -146,7 +152,7 @@ static int netvsc_destroy_buf(struct netvsc_device *net_device)
revoke_packet->msg.v1_msg.
revoke_recv_buf.id = NETVSC_RECEIVE_BUFFER_ID;

ret = vmbus_sendpacket(net_device->dev->channel,
ret = vmbus_sendpacket(device->channel,
revoke_packet,
sizeof(struct nvsp_message),
(unsigned long)revoke_packet,
Expand All @@ -164,8 +170,8 @@ static int netvsc_destroy_buf(struct netvsc_device *net_device)

/* Teardown the gpadl on the vsp end */
if (net_device->recv_buf_gpadl_handle) {
ret = vmbus_teardown_gpadl(net_device->dev->channel,
net_device->recv_buf_gpadl_handle);
ret = vmbus_teardown_gpadl(device->channel,
net_device->recv_buf_gpadl_handle);

/* If we failed here, we might as well return and have a leak
* rather than continue and a bugchk
Expand Down Expand Up @@ -206,7 +212,7 @@ static int netvsc_destroy_buf(struct netvsc_device *net_device)
revoke_packet->msg.v1_msg.revoke_send_buf.id =
NETVSC_SEND_BUFFER_ID;

ret = vmbus_sendpacket(net_device->dev->channel,
ret = vmbus_sendpacket(device->channel,
revoke_packet,
sizeof(struct nvsp_message),
(unsigned long)revoke_packet,
Expand All @@ -222,7 +228,7 @@ static int netvsc_destroy_buf(struct netvsc_device *net_device)
}
/* Teardown the gpadl on the vsp end */
if (net_device->send_buf_gpadl_handle) {
ret = vmbus_teardown_gpadl(net_device->dev->channel,
ret = vmbus_teardown_gpadl(device->channel,
net_device->send_buf_gpadl_handle);

/* If we failed here, we might as well return and have a leak
Expand Down Expand Up @@ -434,7 +440,7 @@ static int netvsc_init_buf(struct hv_device *device)
goto exit;

cleanup:
netvsc_destroy_buf(net_device);
netvsc_destroy_buf(device);

exit:
return ret;
Expand Down Expand Up @@ -565,34 +571,23 @@ static int netvsc_connect_vsp(struct hv_device *device)
return ret;
}

static void netvsc_disconnect_vsp(struct netvsc_device *net_device)
static void netvsc_disconnect_vsp(struct hv_device *device)
{
netvsc_destroy_buf(net_device);
netvsc_destroy_buf(device);
}

/*
* netvsc_device_remove - Callback when the root bus device is removed
*/
int netvsc_device_remove(struct hv_device *device)
{
struct netvsc_device *net_device;
unsigned long flags;

net_device = hv_get_drvdata(device);

netvsc_disconnect_vsp(net_device);
struct net_device *ndev = hv_get_drvdata(device);
struct net_device_context *net_device_ctx = netdev_priv(ndev);
struct netvsc_device *net_device = net_device_ctx->nvdev;

/*
* Since we have already drained, we don't need to busy wait
* as was done in final_release_stor_device()
* Note that we cannot set the ext pointer to NULL until
* we have drained - to drain the outgoing packets, we need to
* allow incoming packets.
*/
netvsc_disconnect_vsp(device);

spin_lock_irqsave(&device->channel->inbound_lock, flags);
hv_set_drvdata(device, NULL);
spin_unlock_irqrestore(&device->channel->inbound_lock, flags);
net_device_ctx->nvdev = NULL;

/*
* At this point, no one should be accessing net_device
Expand Down Expand Up @@ -640,12 +635,11 @@ static void netvsc_send_completion(struct netvsc_device *net_device,
{
struct nvsp_message *nvsp_packet;
struct hv_netvsc_packet *nvsc_packet;
struct net_device *ndev;
struct net_device *ndev = hv_get_drvdata(device);
struct net_device_context *net_device_ctx = netdev_priv(ndev);
u32 send_index;
struct sk_buff *skb;

ndev = net_device->ndev;

nvsp_packet = (struct nvsp_message *)((unsigned long)packet +
(packet->offset8 << 3));

Expand Down Expand Up @@ -690,7 +684,7 @@ static void netvsc_send_completion(struct netvsc_device *net_device,
wake_up(&net_device->wait_drain);

if (netif_tx_queue_stopped(netdev_get_tx_queue(ndev, q_idx)) &&
!net_device->nd_ctx->start_remove &&
!net_device_ctx->start_remove &&
(hv_ringbuf_avail_percent(&channel->outbound) >
RING_AVAIL_PERCENT_HIWATER || queue_sends < 1))
netif_tx_wake_queue(netdev_get_tx_queue(
Expand Down Expand Up @@ -1264,17 +1258,7 @@ int netvsc_device_add(struct hv_device *device, void *additional_info)

net_device->ring_size = ring_size;

/*
* Coming into this function, struct net_device * is
* registered as the driver private data.
* In alloc_net_device(), we register struct netvsc_device *
* as the driver private data and stash away struct net_device *
* in struct netvsc_device *.
*/
ndev = net_device->ndev;

/* Add netvsc_device context to netvsc_device */
net_device->nd_ctx = netdev_priv(ndev);
ndev = hv_get_drvdata(device);

/* Initialize the NetVSC channel extension */
init_completion(&net_device->channel_init_wait);
Expand Down
Loading

0 comments on commit 3d541ac

Please sign in to comment.