Commit 39911731 authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'virtio-net-synchronize-op-admin-state'

Jason Wang says:

====================
virtio-net: synchronize op/admin state

This series tries to synchronize the operstate with the admin state
which allows the lower virtio-net to propagate the link status to the
upper devices like macvlan.

This is done by toggling carrier during ndo_open/stop while doing
other necessary serialization about the carrier settings during probe.

While at it, also fix a race between probe and ndo_set_features as we
didn't initalize the guest offload setting under rtnl lock.
====================

Link: https://patch.msgid.link/20240814052228.4654-1-jasowang@redhat.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents d440af37 c392d601
...@@ -2885,6 +2885,25 @@ static void virtnet_cancel_dim(struct virtnet_info *vi, struct dim *dim) ...@@ -2885,6 +2885,25 @@ static void virtnet_cancel_dim(struct virtnet_info *vi, struct dim *dim)
net_dim_work_cancel(dim); net_dim_work_cancel(dim);
} }
static void virtnet_update_settings(struct virtnet_info *vi)
{
u32 speed;
u8 duplex;
if (!virtio_has_feature(vi->vdev, VIRTIO_NET_F_SPEED_DUPLEX))
return;
virtio_cread_le(vi->vdev, struct virtio_net_config, speed, &speed);
if (ethtool_validate_speed(speed))
vi->speed = speed;
virtio_cread_le(vi->vdev, struct virtio_net_config, duplex, &duplex);
if (ethtool_validate_duplex(duplex))
vi->duplex = duplex;
}
static int virtnet_open(struct net_device *dev) static int virtnet_open(struct net_device *dev)
{ {
struct virtnet_info *vi = netdev_priv(dev); struct virtnet_info *vi = netdev_priv(dev);
...@@ -2903,6 +2922,15 @@ static int virtnet_open(struct net_device *dev) ...@@ -2903,6 +2922,15 @@ static int virtnet_open(struct net_device *dev)
goto err_enable_qp; goto err_enable_qp;
} }
if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_STATUS)) {
if (vi->status & VIRTIO_NET_S_LINK_UP)
netif_carrier_on(vi->dev);
virtio_config_driver_enable(vi->vdev);
} else {
vi->status = VIRTIO_NET_S_LINK_UP;
netif_carrier_on(dev);
}
return 0; return 0;
err_enable_qp: err_enable_qp:
...@@ -3381,12 +3409,22 @@ static int virtnet_close(struct net_device *dev) ...@@ -3381,12 +3409,22 @@ static int virtnet_close(struct net_device *dev)
disable_delayed_refill(vi); disable_delayed_refill(vi);
/* Make sure refill_work doesn't re-enable napi! */ /* Make sure refill_work doesn't re-enable napi! */
cancel_delayed_work_sync(&vi->refill); cancel_delayed_work_sync(&vi->refill);
/* Prevent the config change callback from changing carrier
* after close
*/
virtio_config_driver_disable(vi->vdev);
/* Stop getting status/speed updates: we don't care until next
* open
*/
cancel_work_sync(&vi->config_work);
for (i = 0; i < vi->max_queue_pairs; i++) { for (i = 0; i < vi->max_queue_pairs; i++) {
virtnet_disable_queue_pair(vi, i); virtnet_disable_queue_pair(vi, i);
virtnet_cancel_dim(vi, &vi->rq[i].dim); virtnet_cancel_dim(vi, &vi->rq[i].dim);
} }
netif_carrier_off(dev);
return 0; return 0;
} }
...@@ -5095,25 +5133,6 @@ static void virtnet_init_settings(struct net_device *dev) ...@@ -5095,25 +5133,6 @@ static void virtnet_init_settings(struct net_device *dev)
vi->duplex = DUPLEX_UNKNOWN; vi->duplex = DUPLEX_UNKNOWN;
} }
static void virtnet_update_settings(struct virtnet_info *vi)
{
u32 speed;
u8 duplex;
if (!virtio_has_feature(vi->vdev, VIRTIO_NET_F_SPEED_DUPLEX))
return;
virtio_cread_le(vi->vdev, struct virtio_net_config, speed, &speed);
if (ethtool_validate_speed(speed))
vi->speed = speed;
virtio_cread_le(vi->vdev, struct virtio_net_config, duplex, &duplex);
if (ethtool_validate_duplex(duplex))
vi->duplex = duplex;
}
static u32 virtnet_get_rxfh_key_size(struct net_device *dev) static u32 virtnet_get_rxfh_key_size(struct net_device *dev)
{ {
return ((struct virtnet_info *)netdev_priv(dev))->rss_key_size; return ((struct virtnet_info *)netdev_priv(dev))->rss_key_size;
...@@ -6524,6 +6543,9 @@ static int virtnet_probe(struct virtio_device *vdev) ...@@ -6524,6 +6543,9 @@ static int virtnet_probe(struct virtio_device *vdev)
goto free_failover; goto free_failover;
} }
/* Disable config change notification until ndo_open. */
virtio_config_driver_disable(vi->vdev);
virtio_device_ready(vdev); virtio_device_ready(vdev);
virtnet_set_queues(vi, vi->curr_queue_pairs); virtnet_set_queues(vi, vi->curr_queue_pairs);
...@@ -6573,19 +6595,11 @@ static int virtnet_probe(struct virtio_device *vdev) ...@@ -6573,19 +6595,11 @@ static int virtnet_probe(struct virtio_device *vdev)
vi->device_stats_cap = le64_to_cpu(v); vi->device_stats_cap = le64_to_cpu(v);
} }
rtnl_unlock();
err = virtnet_cpu_notif_add(vi);
if (err) {
pr_debug("virtio_net: registering cpu notifier failed\n");
goto free_unregister_netdev;
}
/* Assume link up if device can't report link status, /* Assume link up if device can't report link status,
otherwise get link status from config. */ otherwise get link status from config. */
netif_carrier_off(dev); netif_carrier_off(dev);
if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_STATUS)) { if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_STATUS)) {
schedule_work(&vi->config_work); virtnet_config_changed_work(&vi->config_work);
} else { } else {
vi->status = VIRTIO_NET_S_LINK_UP; vi->status = VIRTIO_NET_S_LINK_UP;
virtnet_update_settings(vi); virtnet_update_settings(vi);
...@@ -6597,6 +6611,14 @@ static int virtnet_probe(struct virtio_device *vdev) ...@@ -6597,6 +6611,14 @@ static int virtnet_probe(struct virtio_device *vdev)
set_bit(guest_offloads[i], &vi->guest_offloads); set_bit(guest_offloads[i], &vi->guest_offloads);
vi->guest_offloads_capable = vi->guest_offloads; vi->guest_offloads_capable = vi->guest_offloads;
rtnl_unlock();
err = virtnet_cpu_notif_add(vi);
if (err) {
pr_debug("virtio_net: registering cpu notifier failed\n");
goto free_unregister_netdev;
}
pr_debug("virtnet: registered device %s with %d RX and TX vq's\n", pr_debug("virtnet: registered device %s with %d RX and TX vq's\n",
dev->name, max_queue_pairs); dev->name, max_queue_pairs);
......
...@@ -127,10 +127,12 @@ static void __virtio_config_changed(struct virtio_device *dev) ...@@ -127,10 +127,12 @@ static void __virtio_config_changed(struct virtio_device *dev)
{ {
struct virtio_driver *drv = drv_to_virtio(dev->dev.driver); struct virtio_driver *drv = drv_to_virtio(dev->dev.driver);
if (!dev->config_enabled) if (!dev->config_core_enabled || dev->config_driver_disabled)
dev->config_change_pending = true; dev->config_change_pending = true;
else if (drv && drv->config_changed) else if (drv && drv->config_changed) {
drv->config_changed(dev); drv->config_changed(dev);
dev->config_change_pending = false;
}
} }
void virtio_config_changed(struct virtio_device *dev) void virtio_config_changed(struct virtio_device *dev)
...@@ -143,20 +145,51 @@ void virtio_config_changed(struct virtio_device *dev) ...@@ -143,20 +145,51 @@ void virtio_config_changed(struct virtio_device *dev)
} }
EXPORT_SYMBOL_GPL(virtio_config_changed); EXPORT_SYMBOL_GPL(virtio_config_changed);
static void virtio_config_disable(struct virtio_device *dev) /**
* virtio_config_driver_disable - disable config change reporting by drivers
* @dev: the device to reset
*
* This is only allowed to be called by a driver and disabling can't
* be nested.
*/
void virtio_config_driver_disable(struct virtio_device *dev)
{ {
spin_lock_irq(&dev->config_lock); spin_lock_irq(&dev->config_lock);
dev->config_enabled = false; dev->config_driver_disabled = true;
spin_unlock_irq(&dev->config_lock); spin_unlock_irq(&dev->config_lock);
} }
EXPORT_SYMBOL_GPL(virtio_config_driver_disable);
static void virtio_config_enable(struct virtio_device *dev) /**
* virtio_config_driver_enable - enable config change reporting by drivers
* @dev: the device to reset
*
* This is only allowed to be called by a driver and enabling can't
* be nested.
*/
void virtio_config_driver_enable(struct virtio_device *dev)
{ {
spin_lock_irq(&dev->config_lock); spin_lock_irq(&dev->config_lock);
dev->config_enabled = true; dev->config_driver_disabled = false;
if (dev->config_change_pending)
__virtio_config_changed(dev);
spin_unlock_irq(&dev->config_lock);
}
EXPORT_SYMBOL_GPL(virtio_config_driver_enable);
static void virtio_config_core_disable(struct virtio_device *dev)
{
spin_lock_irq(&dev->config_lock);
dev->config_core_enabled = false;
spin_unlock_irq(&dev->config_lock);
}
static void virtio_config_core_enable(struct virtio_device *dev)
{
spin_lock_irq(&dev->config_lock);
dev->config_core_enabled = true;
if (dev->config_change_pending) if (dev->config_change_pending)
__virtio_config_changed(dev); __virtio_config_changed(dev);
dev->config_change_pending = false;
spin_unlock_irq(&dev->config_lock); spin_unlock_irq(&dev->config_lock);
} }
...@@ -316,7 +349,7 @@ static int virtio_dev_probe(struct device *_d) ...@@ -316,7 +349,7 @@ static int virtio_dev_probe(struct device *_d)
if (drv->scan) if (drv->scan)
drv->scan(dev); drv->scan(dev);
virtio_config_enable(dev); virtio_config_core_enable(dev);
return 0; return 0;
...@@ -331,7 +364,7 @@ static void virtio_dev_remove(struct device *_d) ...@@ -331,7 +364,7 @@ static void virtio_dev_remove(struct device *_d)
struct virtio_device *dev = dev_to_virtio(_d); struct virtio_device *dev = dev_to_virtio(_d);
struct virtio_driver *drv = drv_to_virtio(dev->dev.driver); struct virtio_driver *drv = drv_to_virtio(dev->dev.driver);
virtio_config_disable(dev); virtio_config_core_disable(dev);
drv->remove(dev); drv->remove(dev);
...@@ -443,7 +476,7 @@ int register_virtio_device(struct virtio_device *dev) ...@@ -443,7 +476,7 @@ int register_virtio_device(struct virtio_device *dev)
goto out_ida_remove; goto out_ida_remove;
spin_lock_init(&dev->config_lock); spin_lock_init(&dev->config_lock);
dev->config_enabled = false; dev->config_core_enabled = false;
dev->config_change_pending = false; dev->config_change_pending = false;
INIT_LIST_HEAD(&dev->vqs); INIT_LIST_HEAD(&dev->vqs);
...@@ -500,14 +533,14 @@ int virtio_device_freeze(struct virtio_device *dev) ...@@ -500,14 +533,14 @@ int virtio_device_freeze(struct virtio_device *dev)
struct virtio_driver *drv = drv_to_virtio(dev->dev.driver); struct virtio_driver *drv = drv_to_virtio(dev->dev.driver);
int ret; int ret;
virtio_config_disable(dev); virtio_config_core_disable(dev);
dev->failed = dev->config->get_status(dev) & VIRTIO_CONFIG_S_FAILED; dev->failed = dev->config->get_status(dev) & VIRTIO_CONFIG_S_FAILED;
if (drv && drv->freeze) { if (drv && drv->freeze) {
ret = drv->freeze(dev); ret = drv->freeze(dev);
if (ret) { if (ret) {
virtio_config_enable(dev); virtio_config_core_enable(dev);
return ret; return ret;
} }
} }
...@@ -557,7 +590,7 @@ int virtio_device_restore(struct virtio_device *dev) ...@@ -557,7 +590,7 @@ int virtio_device_restore(struct virtio_device *dev)
if (!(dev->config->get_status(dev) & VIRTIO_CONFIG_S_DRIVER_OK)) if (!(dev->config->get_status(dev) & VIRTIO_CONFIG_S_DRIVER_OK))
virtio_device_ready(dev); virtio_device_ready(dev);
virtio_config_enable(dev); virtio_config_core_enable(dev);
return 0; return 0;
......
...@@ -118,7 +118,9 @@ struct virtio_admin_cmd { ...@@ -118,7 +118,9 @@ struct virtio_admin_cmd {
* struct virtio_device - representation of a device using virtio * struct virtio_device - representation of a device using virtio
* @index: unique position on the virtio bus * @index: unique position on the virtio bus
* @failed: saved value for VIRTIO_CONFIG_S_FAILED bit (for restore) * @failed: saved value for VIRTIO_CONFIG_S_FAILED bit (for restore)
* @config_enabled: configuration change reporting enabled * @config_core_enabled: configuration change reporting enabled by core
* @config_driver_disabled: configuration change reporting disabled by
* a driver
* @config_change_pending: configuration change reported while disabled * @config_change_pending: configuration change reported while disabled
* @config_lock: protects configuration change reporting * @config_lock: protects configuration change reporting
* @vqs_list_lock: protects @vqs. * @vqs_list_lock: protects @vqs.
...@@ -135,7 +137,8 @@ struct virtio_admin_cmd { ...@@ -135,7 +137,8 @@ struct virtio_admin_cmd {
struct virtio_device { struct virtio_device {
int index; int index;
bool failed; bool failed;
bool config_enabled; bool config_core_enabled;
bool config_driver_disabled;
bool config_change_pending; bool config_change_pending;
spinlock_t config_lock; spinlock_t config_lock;
spinlock_t vqs_list_lock; spinlock_t vqs_list_lock;
...@@ -166,6 +169,10 @@ void __virtqueue_break(struct virtqueue *_vq); ...@@ -166,6 +169,10 @@ void __virtqueue_break(struct virtqueue *_vq);
void __virtqueue_unbreak(struct virtqueue *_vq); void __virtqueue_unbreak(struct virtqueue *_vq);
void virtio_config_changed(struct virtio_device *dev); void virtio_config_changed(struct virtio_device *dev);
void virtio_config_driver_disable(struct virtio_device *dev);
void virtio_config_driver_enable(struct virtio_device *dev);
#ifdef CONFIG_PM_SLEEP #ifdef CONFIG_PM_SLEEP
int virtio_device_freeze(struct virtio_device *dev); int virtio_device_freeze(struct virtio_device *dev);
int virtio_device_restore(struct virtio_device *dev); int virtio_device_restore(struct virtio_device *dev);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment