mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Thomas Martitz <t.martitz@fritz.com>
To: Jakub Kicinski <kuba@kernel.org>
Cc: horms@kernel.org, andrew+netdev@lunn.ch, davem@davemloft.net,
	edumazet@kernel.org, pabeni@redhat.com, netdev@vger.kernel.org,
	linux-kernel@vger.kernel.org
Subject: Re: [PATCH v6 1/1] macvlan: allow source mode devices along with passthru
Date: Tue, 3 Nov 2026 12:37:56 +0100	[thread overview]
Message-ID: <0559500f-e89c-4ddc-941d-c98f2b1d55f1@fritz.com> (raw)
In-Reply-To: <20261016003009.2692872-1-kuba@kernel.org>

Am 16.10.26 um 02:30 schrieb Jakub Kicinski:
> This is an AI-generated review of your patch. The human sending this
> email has considered the AI review valid, or at least plausible.
> ---
> macvlan: allow source mode devices along with passthru
> 
> This patch lets source-mode macvlan devices share a port with a single
> passthru macvlan. Known senders end on the source devices and all other
> traffic goes to the passthru device. The lower MAC restore moves from
> macvlan_port_destroy() to macvlan_uninit() of the passthru device, and
> several macvlan_passthru(port) checks become per-device vlan->mode checks.


Hello Jakub,

thanks for taking time to deal with my patch. I replied inline to sasihikos
findings.

Besides being busy with other daily business I finally managed set up
sashiko locally. I will probably come up with v7 but this time run sashiko
locally before submitting.

Best regards.

> 
>> diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c
>> index afad90b9222a2..4af10cc517993 100644
>> --- a/drivers/net/macvlan.c
>> +++ b/drivers/net/macvlan.c
>> @@ -83,6 +83,11 @@ static inline void macvlan_set_passthru(struct macvlan_port *port)
>>  	port->flags |= MACVLAN_F_PASSTHRU;
>>  }
>>  
>> +static inline void macvlan_clear_passthru(struct macvlan_port *port)
>> +{
>> +	port->flags &= ~MACVLAN_F_PASSTHRU;
>> +}
>> +
> 
> [Severity: Low]
> With this patch, MACVLAN_F_PASSTHRU is set and cleared at runtime while
> the rx_handler stays registered for the remaining source devices.
> macvlan_set_passthru() and macvlan_clear_passthru() are both plain
> read-modify-writes under RTNL. macvlan_handle_frame() reads the flag with
> a plain load in softirq:
> 
> 	if (macvlan_passthru(port))
> 		vlan = list_first_or_null_rcu(&port->vlans,
> 					      struct macvlan_dev, list);
> 
> Nothing orders the flag against the port->vlans update. The reader seems
> to handle both transient states, because the next check passes the frame
> when the list head is NULL or a source device:
> 
> 	if (!vlan || READ_ONCE(vlan->mode) == MAm 16.10.26 um 02:30 schrieb Jakub Kicinski:
> This is an AI-generated review of your patch. The human sending this
> email has considered the AI review valid, or at least plausible.
> ---
> macvlan: allow source mode devices along with passthru
> 
> This patch lets source-mode macvlan devices share a port with a single
> passthru macvlan. Known senders end on the source devices and all other
> traffic goes to the passthru device. The lower MAC restore moves from
> macvlan_port_destroy() to macvlan_uninit() of the passthru device, and
> several macvlan_passthru(port) checks become per-device vlan->mode checks.

Hello Jakub,

thanks for taking time to deal with my patch.

Besides being busy with other daily business I finally managed set up
sashiko locally. I will probably come up with v7 but this time run sashiko
locally before submitting.

Best regards.

> 
>> diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c
>> index afad90b9222a2..4af10cc517993 100644
>> --- a/drivers/net/macvlan.c
>> +++ b/drivers/net/macvlan.c
>> @@ -83,6 +83,11 @@ static inline void macvlan_set_passthru(struct macvlan_port *port)
>>  	port->flags |= MACVLAN_F_PASSTHRU;
>>  }
>>  
>> +static inline void macvlan_clear_passthru(struct macvlan_port *port)
>> +{
>> +	port->flags &= ~MACVLAN_F_PASSTHRU;
>> +}
>> +
> 
> [Severity: Low]
> With this patch, MACVLAN_F_PASSTHRU is set and cleared at runtime while
> the rx_handler stays registered for the remaining source devices.
> macvlan_set_passthru() and macvlan_clear_passthru() are both plain
> read-modify-writes under RTNL. macvlan_handle_frame() reads the flag with
> a plain load in softirq:
> 
> 	if (macvlan_passthru(port))
> 		vlan = list_first_or_null_rcu(&port->vlans,
> 					      struct macvlan_dev, list);
> 
> Nothing orders the flag against the port->vlans update. The reader seems
> to handle both transient states, because the next check passes the frame
> when the list head is NULL or a source device:
> 
> 	if (!vlan || READ_ONCE(vlan->mode) == MACVLAN_MODE_SOURCE)
> 		return RX_HANDLER_PASS;
> 
> The plain-access race on port->flags already existed through
> MACVLAN_F_ADDRCHANGE. Would READ_ONCE()/WRITE_ONCE() on port->flags be a
> reasonable cleanup now that there is another runtime writer?
> 
> [ ... ]
> 
>> @@ -737,17 +741,18 @@ static int macvlan_sync_address(struct net_device *dev,
>>  	struct macvlan_dev *vlan = netdev_priv(dev);
>>  	struct net_device *lowerdev = vlan->lowerdev;
>>  	struct macvlan_port *port = vlan->port;
>> +	bool passthru_dev = vlan->mode == MACVLAN_MODE_PASSTHRU;
>>  	int err;
>>  
>>  	if (!(dev->flags & IFF_UP)) {
>>  		/* Just copy in the new address */
>>  		eth_hw_addr_set(dev, addr);
>>  	} else {
>> -		/* Rehash and update the device filters */
>> -		if (macvlan_addr_busy(vlan->port, addr))
>> -			return -EADDRINUSE;
>> +		if (!passthru_dev) {
>> +			/* Rehash and update the device filters */
>> +			if (macvlan_addr_busy(vlan->port, addr))
>> +					return -EADDRINUSE;
> 
> [Severity: Low]
> For the passthru device, this now skips macvlan_addr_busy() entirely. That
> includes the vlan_hash lookup the old code still did on passthru ports.
> The new check in macvlan_set_mac_address() only covers changes made
> through the passthru device.
> 
> What happens when the change starts on the lower device? For example,
> setting eth0's MAC to the MAC of a source-mode macvlan that is up:
> 
> netif_set_mac_address()
>   NETDEV_CHANGEADDR
>     macvlan_device_event()
>       macvlan_sync_address()
>         macvlan_hash_change_addr()
> 
> Wouldn't this put the passthru device into port->vlan_hash with the same
> MAC the source device already has there?
> 
> The same seems to happen when the passthru device is down.
> eth_hw_addr_set() copies the address, and macvlan_open() later hashes it
> with no busy check.
> 
> After that, lookups through macvlan_hash_lookup(), such as the h_source
> lookup on the multicast rx path, depend on hash order. The source device
> also can't be reopened, because its address now equals the lower's.
> 
> netif_set_mac_address() ignores the NETDEV_CHANGEADDR notifier return
> value, so the old NOTIFY_BAD path would not have vetoed this either. Would
> a NETDEV_PRE_CHANGEADDR check be the right place to reject it?
I will look at this in more detail.

> 
> [ ... ]
> 
>> @@ -781,14 +786,17 @@ static int macvlan_set_mac_address(struct net_device *dev, void *p)
>>  	if (ether_addr_equal(dev->dev_addr, addr->__data))
>>  		return 0;
>>  
>> +	if (macvlan_addr_busy(vlan->port, addr->__data))
>> +		return -EADDRINUSE;
>> +
>>  	if (READ_ONCE(vlan->mode) == MACVLAN_MODE_PASSTHRU) {
>>  		macvlan_set_addr_change(vlan->port);
>> +		/* macvlan_sync_address() via netdev notifier,
>> +		 * to keep passthru and lower in sync.
>> +		 */
>>  		return dev_set_mac_address(vlan->lowerdev, addr, NULL);
>>  	}
> 
> [Severity: Medium]
> macvlan_set_addr_change() is called before
> dev_set_mac_address(vlan->lowerdev, ...). That call can fail, for example
> with -EBUSY from eth_prepare_mac_addr_change() on a running lower device
> without IFF_LIVE_ADDR_CHANGE, or through a NETDEV_PRE_CHANGEADDR veto. In
> that case netif_set_mac_address() returns before NETDEV_CHANGEADDR, and
> nothing clears MACVLAN_F_ADDRCHANGE.
> 
> Before this patch, the port and the flag went away together with the only
> passthru device. Now source devices keep the port alive.
> macvlan_uninit() only calls macvlan_clear_passthru().
> macvlan_common_newlink() resets port->perm_addr for a new passthru device,
> but not the flag.
> 
> Can the following sequence restore an outdated MAC on the lower device?
> 
> 1. ip link set <passthru> address X fails, leaving ADDRCHANGE set.
> 2. The passthru device is deleted while source devices still exist.
> 3. A new passthru device is created, which copies the current lower MAC
>    into perm_addr.
> 4. The lower MAC is changed directly. macvlan_sync_address() sees the
>    stale flag and does not save the new address:
> 
> 	if (passthru_dev && !macvlan_addr_change(port)) {
> 
> 5. The passthru device is deleted. macvlan_uninit() sees
>    dev_addr != perm_addr and writes the old MAC back.
> 
> Should the flag be cleared when dev_set_mac_address() fails, or when the
> passthru device goes away?
> 
Seems valid, I will try to reproduce this and fix this in a follow up.

> [ ... ]
> 
>> @@ -979,7 +987,26 @@ static void macvlan_uninit(struct net_device *dev)
>>  
>>  	macvlan_flush_sources(port, vlan);
>>  	port->count -= 1;
>> -	if (!port->count)
>> +
>> +	if (vlan->mode == MACVLAN_MODE_PASSTHRU) {
>> +		/* If the lower device address has been changed by passthru
>> +		 * macvlan, put it back.
>> +		 *
>> +		 * Caution: This triggers NETDEV_CHANGEADDR on
>> +		 * the lower device. But we're in the green because
>> +		 * the passthru interface is already off the list.
>> +		 */
>> +		if (!ether_addr_equal(port->dev->dev_addr, port->perm_addr)) {
>> +			struct sockaddr_storage ss;
>> +
>> +			ss.ss_family = port->dev->type;
>> +			memcpy(&ss.__data, port->perm_addr, port->dev->addr_len);
>> +			dev_set_mac_address(port->dev, &ss, NULL);
>> +		}
> 
> [Severity: Medium]
> Can this put a MAC on the lower device that a remaining source-mode
> macvlan already uses? Consider this sequence:
> 
> 1. The lower device has address A. A passthru device is created, so
>    perm_addr = A.
> 2. The passthru address is set to B. The lower device changes to B, and
>    perm_addr stays A because ADDRCHANGE is set.
> 3. A source-mode macvlan is created with IFLA_ADDRESS = A.
>    macvlan_common_newlink() does no busy check.
> 4. The source device is brought up. In macvlan_open(),
>    macvlan_addr_busy(A) only compares against the current lower address
>    (B) and vlan_hash, so it succeeds and calls dev_uc_add(lower, A).
> 5. The passthru device is deleted. This code calls
>    dev_set_mac_address(lower, A) without checking the macvlans that are
>    left.
> 
> The lower device and the source device now share A. The next down/up of
> the source device then fails in macvlan_open() with -EADDRINUSE, because
> macvlan_addr_busy() hits this check:
> 
> 	if (ether_addr_equal_64bits(port->dev->dev_addr, addr))
> 		return true;
> 

Like above, I will try to reproduce this and fix this in a follow up.

> [ ... ]
> 
>> @@ -1544,12 +1549,32 @@ int macvlan_common_newlink(struct net_device *dev,
>>  	if (data && data[IFLA_MACVLAN_FLAGS])
>>  		vlan->flags = nla_get_u16(data[IFLA_MACVLAN_FLAGS]);
>>  
>> +	/* Only 1 macvlan device can be created in passthru mode. There may be
>> +	 * additional source mode devices but nothing else at the moment.
>> +	 *
>> +	 * First check if adding a source mode device to an existing passthru vlan.
>> +	 */
>> +	if (macvlan_passthru(port) && vlan->mode != MACVLAN_MODE_SOURCE) {
>> +		/* The macvlan port must be not created this time,
>> +		 * still goto destroy_macvlan_port for readability.
>> +		 */
>> +		err = -EINVAL;
>> +		goto destroy_macvlan_port;
>> +	}
> 
> [Severity: Medium]
> Source-mode devices are now allowed on a passthru port, so the passthru
> device no longer gets all traffic on the lower device. Are there places
> outside macvlan.c that still assume it does?
> 
> In drivers/net/ethernet/mellanox/mlx5/core/en/rep/tc.c,
> mlx5e_rep_check_indr_block_supported() offloads tc ingress filters bound
> to a macvlan onto the real lower representor whenever this returns true:
> 
> mlx5e_rep_macvlan_mode_supported() {
> 	...
> 	return macvlan->mode == MACVLAN_MODE_PASSTHRU;
> }
> 
> In software, macvlan_handle_frame() calls macvlan_forward_source() before
> the passthru lookup. With MACVLAN_FLAG_NODST, known-sender frames are
> consumed there and never reach the passthru device.
> 
> A hardware filter installed for the passthru macvlan could still match
> frames entering the representor, including the inter-SoC ones. It could
> then drop or redirect them before the source devices see them. I could
> not confirm from the mlx5 flow steering code that the offloaded rule
> matches every packet from the vport. If it does, hardware and software
> would behave differently here. Is that a concern?
Hmm, I didn't consider HW offloading. Offloading should probably be
avoided in this configuration. But drivers would probably need some
assistance here.

Do you have an opinion how to deal best with this?

> 
> Also, the uAPI comment in include/uapi/linux/if_link.h still says:
> 
> 	MACVLAN_MODE_PASSTHRU = 8,/* take over the underlying device */
> 
> Should that be updated as well?
Good idea.

> 
> [ ... ]
> 
>> @@ -1652,19 +1682,23 @@ static int macvlan_changelink(struct net_device *dev,
>>  	if (data && data[IFLA_MACVLAN_MODE]) {
>>  		set_mode = true;
>>  		mode = nla_get_u32(data[IFLA_MACVLAN_MODE]);
>> -		/* Passthrough mode can't be set or cleared dynamically */
>> -		if ((mode == MACVLAN_MODE_PASSTHRU) !=
>> -		    (vlan->mode == MACVLAN_MODE_PASSTHRU))
>> -			return -EINVAL;
>> -		if (vlan->mode == MACVLAN_MODE_SOURCE &&
>> -		    vlan->mode != mode)
>> -			macvlan_flush_sources(vlan->port, vlan);
>> +		if (mode != vlan->mode) {
>> +			/* Passthrough mode can't be set or cleared dynamically,
>> +			 * regardless of existing source interfaces. Furthermore, source
>> +			 * interfaces can't switch modes within a passhtrough port.
>> +			 */
>> +			if (mode == MACVLAN_MODE_PASSTHRU ||
>> +			    macvlan_passthru(vlan->port))
>> +				return -EINVAL;
> 
> [Severity: Low]
> This adds a new restriction: while the port is in passthru, any mode
> change of a source-mode device returns -EINVAL. The commit message does
> not mention this or a few other user-visible changes:
> 
> - The lower MAC restore moved from macvlan_port_destroy() to
>   macvlan_uninit() of the passthru device. It now runs even while source
>   devices stay on the port.
> 
> - macvlan_forward_source() only consumes the frame when
>   MACVLAN_FLAG_NODST is set. Without it, known-sender frames are cloned to
>   the source device and also delivered to the passthru device.
> 
> The commit message says:
> 
>   "The source mode devices represent the known senders while
>   all external senders terminate on passthru device."
> 
> That split seems to depend on NODST being set.
> 
> Documentation/networking/device_drivers/ethernet/intel/ice.rst also still
> says:
> 
>   "In passthru mode, you can only set up one MACVLAN device."
> 
> Could the commit message and that document be updated to describe these
> changes?

Okay.

> 
>> +			if (vlan->mode == MACVLAN_MODE_SOURCE)
>> +				macvlan_flush_sources(vlan->port, vlan);
>> +		}
>>  	}
>>  
>>  	if (data && data[IFLA_MACVLAN_FLAGS]) {
>>  		__u16 flags = nla_get_u16(data[IFLA_MACVLAN_FLAGS]);
>>  		bool promisc = (flags ^ vlan->flags) & MACVLAN_FLAG_NOPROMISC;
>> -		if (macvlan_passthru(vlan->port) && promisc) {
>> +		if (vlan->mode == MACVLAN_MODE_PASSTHRU && promisc) {
>>  			int err;
>>  
>>  			if (flags & MACVLAN_FLAG_NOPROMISC)
> 
> [Severity: Medium]
> This is a pre-existing issue and was not introduced by this patch.
> macvlan_changelink() changes the lower device's promiscuity count whenever
> MACVLAN_FLAG_NOPROMISC flips on the passthru device. It does not check
> dev->flags & IFF_UP. macvlan_open() and macvlan_stop() only take or drop
> that reference while the device is up.
> 
> Setting nopromisc on a passthru device that is down calls
> dev_set_promiscuity(lowerdev, -1) with no matching +1. That either drops a
> reference owned by another user, or wraps dev->promiscuity from 0 in
> __dev_set_promiscuity() and leaves IFF_PROMISC set. Clearing nopromisc
> while down takes a +1 that is never released.
> 
> Should this be gated on IFF_UP? For the passthru device, the patch's
> change from macvlan_passthru(vlan->port) to
> vlan->mode == MACVLAN_MODE_PASSTHRU is equivalent, so the behavior is
> unchanged.
> 
> [ ... ]
Do you want me to send a fix for this in a separate thread?

Best regards


-- 
Thomas Martitz <t.martitz@fritz.com>
FRITZ! Technology GmbH, Berlin (Germany)ACVLAN_MODE_SOURCE)
> 		return RX_HANDLER_PASS;
> 
> The plain-access race on port->flags already existed through
> MACVLAN_F_ADDRCHANGE. Would READ_ONCE()/WRITE_ONCE() on port->flags be a
> reasonable cleanup now that there is another runtime writer?
> 
> [ ... ]
> 
>> @@ -737,17 +741,18 @@ static int macvlan_sync_address(struct net_device *dev,
>>  	struct macvlan_dev *vlan = netdev_priv(dev);
>>  	struct net_device *lowerdev = vlan->lowerdev;
>>  	struct macvlan_port *port = vlan->port;
>> +	bool passthru_dev = vlan->mode == MACVLAN_MODE_PASSTHRU;
>>  	int err;
>>  
>>  	if (!(dev->flags & IFF_UP)) {
>>  		/* Just copy in the new address */
>>  		eth_hw_addr_set(dev, addr);
>>  	} else {
>> -		/* Rehash and update the device filters */
>> -		if (macvlan_addr_busy(vlan->port, addr))
>> -			return -EADDRINUSE;
>> +		if (!passthru_dev) {
>> +			/* Rehash and update the device filters */
>> +			if (macvlan_addr_busy(vlan->port, addr))
>> +					return -EADDRINUSE;
> 
> [Severity: Low]
> For the passthru device, this now skips macvlan_addr_busy() entirely. That
> includes the vlan_hash lookup the old code still did on passthru ports.
> The new check in macvlan_set_mac_address() only covers changes made
> through the passthru device.
> 
> What happens when the change starts on the lower device? For example,
> setting eth0's MAC to the MAC of a source-mode macvlan that is up:
> 
> netif_set_mac_address()
>   NETDEV_CHANGEADDR
>     macvlan_device_event()
>       macvlan_sync_address()
>         macvlan_hash_change_addr()
> 
> Wouldn't this put the passthru device into port->vlan_hash with the same
> MAC the source device already has there?
> 
> The same seems to happen when the passthru device is down.
> eth_hw_addr_set() copies the address, and macvlan_open() later hashes it
> with no busy check.
> 
> After that, lookups through macvlan_hash_lookup(), such as the h_source
> lookup on the multicast rx path, depend on hash order. The source device
> also can't be reopened, because its address now equals the lower's.
> 
> netif_set_mac_address() ignores the NETDEV_CHANGEADDR notifier return
> value, so the old NOTIFY_BAD path would not have vetoed this either. Would
> a NETDEV_PRE_CHANGEADDR check be the right place to reject it?

I will look at this in more detail.

> 
> [ ... ]
> 
>> @@ -781,14 +786,17 @@ static int macvlan_set_mac_address(struct net_device *dev, void *p)
>>  	if (ether_addr_equal(dev->dev_addr, addr->__data))
>>  		return 0;
>>  
>> +	if (macvlan_addr_busy(vlan->port, addr->__data))
>> +		return -EADDRINUSE;
>> +
>>  	if (READ_ONCE(vlan->mode) == MACVLAN_MODE_PASSTHRU) {
>>  		macvlan_set_addr_change(vlan->port);
>> +		/* macvlan_sync_address() via netdev notifier,
>> +		 * to keep passthru and lower in sync.
>> +		 */
>>  		return dev_set_mac_address(vlan->lowerdev, addr, NULL);
>>  	}
> 
> [Severity: Medium]
> macvlan_set_addr_change() is called before
> dev_set_mac_address(vlan->lowerdev, ...). That call can fail, for example
> with -EBUSY from eth_prepare_mac_addr_change() on a running lower device
> without IFF_LIVE_ADDR_CHANGE, or through a NETDEV_PRE_CHANGEADDR veto. In
> that case netif_set_mac_address() returns before NETDEV_CHANGEADDR, and
> nothing clears MACVLAN_F_ADDRCHANGE.
> 
> Before this patch, the port and the flag went away together with the only
> passthru device. Now source devices keep the port alive.
> macvlan_uninit() only calls macvlan_clear_passthru().
> macvlan_common_newlink() resets port->perm_addr for a new passthru device,
> but not the flag.
> 
> Can the following sequence restore an outdated MAC on the lower device?
> 
> 1. ip link set <passthru> address X fails, leaving ADDRCHANGE set.
> 2. The passthru device is deleted while source devices still exist.
> 3. A new passthru device is created, which copies the current lower MAC
>    into perm_addr.
> 4. The lower MAC is changed directly. macvlan_sync_address() sees the
>    stale flag and does not save the new address:
> 
> 	if (passthru_dev && !macvlan_addr_change(port)) {
> 
> 5. The passthru device is deleted. macvlan_uninit() sees
>    dev_addr != perm_addr and writes the old MAC back.
> 
> Should the flag be cleared when dev_set_mac_address() fails, or when the
> passthru device goes away?
> 

Seems valid, I will try to reproduce this and fix this in a follow up.

> [ ... ]
> 
>> @@ -979,7 +987,26 @@ static void macvlan_uninit(struct net_device *dev)
>>  
>>  	macvlan_flush_sources(port, vlan);
>>  	port->count -= 1;
>> -	if (!port->count)
>> +
>> +	if (vlan->mode == MACVLAN_MODE_PASSTHRU) {
>> +		/* If the lower device address has been changed by passthru
>> +		 * macvlan, put it back.
>> +		 *
>> +		 * Caution: This triggers NETDEV_CHANGEADDR on
>> +		 * the lower device. But we're in the green because
>> +		 * the passthru interface is already off the list.
>> +		 */
>> +		if (!ether_addr_equal(port->dev->dev_addr, port->perm_addr)) {
>> +			struct sockaddr_storage ss;
>> +
>> +			ss.ss_family = port->dev->type;
>> +			memcpy(&ss.__data, port->perm_addr, port->dev->addr_len);
>> +			dev_set_mac_address(port->dev, &ss, NULL);
>> +		}
> 
> [Severity: Medium]
> Can this put a MAC on the lower device that a remaining source-mode
> macvlan already uses? Consider this sequence:
> 
> 1. The lower device has address A. A passthru device is created, so
>    perm_addr = A.
> 2. The passthru address is set to B. The lower device changes to B, and
>    perm_addr stays A because ADDRCHANGE is set.
> 3. A source-mode macvlan is created with IFLA_ADDRESS = A.
>    macvlan_common_newlink() does no busy check.
> 4. The source device is brought up. In macvlan_open(),
>    macvlan_addr_busy(A) only compares against the current lower address
>    (B) and vlan_hash, so it succeeds and calls dev_uc_add(lower, A).
> 5. The passthru device is deleted. This code calls
>    dev_set_mac_address(lower, A) without checking the macvlans that are
>    left.
> 
> The lower device and the source device now share A. The next down/up of
> the source device then fails in macvlan_open() with -EADDRINUSE, because
> macvlan_addr_busy() hits this check:
> 
> 	if (ether_addr_equal_64bits(port->dev->dev_addr, addr))
> 		return true;
> 


Like above, I will try to reproduce this and fix this in a follow up.

> [ ... ]
> 
>> @@ -1544,12 +1549,32 @@ int macvlan_common_newlink(struct net_device *dev,
>>  	if (data && data[IFLA_MACVLAN_FLAGS])
>>  		vlan->flags = nla_get_u16(data[IFLA_MACVLAN_FLAGS]);
>>  
>> +	/* Only 1 macvlan device can be created in passthru mode. There may be
>> +	 * additional source mode devices but nothing else at the moment.
>> +	 *
>> +	 * First check if adding a source mode device to an existing passthru vlan.
>> +	 */
>> +	if (macvlan_passthru(port) && vlan->mode != MACVLAN_MODE_SOURCE) {
>> +		/* The macvlan port must be not created this time,
>> +		 * still goto destroy_macvlan_port for readability.
>> +		 */
>> +		err = -EINVAL;
>> +		goto destroy_macvlan_port;
>> +	}
> 
> [Severity: Medium]
> Source-mode devices are now allowed on a passthru port, so the passthru
> device no longer gets all traffic on the lower device. Are there places
> outside macvlan.c that still assume it does?
> 
> In drivers/net/ethernet/mellanox/mlx5/core/en/rep/tc.c,
> mlx5e_rep_check_indr_block_supported() offloads tc ingress filters bound
> to a macvlan onto the real lower representor whenever this returns true:
> 
> mlx5e_rep_macvlan_mode_supported() {
> 	...
> 	return macvlan->mode == MACVLAN_MODE_PASSTHRU;
> }
> 
> In software, macvlan_handle_frame() calls macvlan_forward_source() before
> the passthru lookup. With MACVLAN_FLAG_NODST, known-sender frames are
> consumed there and never reach the passthru device.
> 
> A hardware filter installed for the passthru macvlan could still match
> frames entering the representor, including the inter-SoC ones. It could
> then drop or redirect them before the source devices see them. I could
> not confirm from the mlx5 flow steering code that the offloaded rule
> matches every packet from the vport. If it does, hardware and software
> would behave differently here. Is that a concern?

Hmm, I didn't consider HW offloading. Offloading should probably be
avoided in this configuration. But drivers would probably need some
assistance here.

Do you have an opinion how to deal best with this?

> 
> Also, the uAPI comment in include/uapi/linux/if_link.h still says:
> 
> 	MACVLAN_MODE_PASSTHRU = 8,/* take over the underlying device */
> 
> Should that be updated as well?

Good idea.

> 
> [ ... ]
> 
>> @@ -1652,19 +1682,23 @@ static int macvlan_changelink(struct net_device *dev,
>>  	if (data && data[IFLA_MACVLAN_MODE]) {
>>  		set_mode = true;
>>  		mode = nla_get_u32(data[IFLA_MACVLAN_MODE]);
>> -		/* Passthrough mode can't be set or cleared dynamically */
>> -		if ((mode == MACVLAN_MODE_PASSTHRU) !=
>> -		    (vlan->mode == MACVLAN_MODE_PASSTHRU))
>> -			return -EINVAL;
>> -		if (vlan->mode == MACVLAN_MODE_SOURCE &&
>> -		    vlan->mode != mode)
>> -			macvlan_flush_sources(vlan->port, vlan);
>> +		if (mode != vlan->mode) {
>> +			/* Passthrough mode can't be set or cleared dynamically,
>> +			 * regardless of existing source interfaces. Furthermore, source
>> +			 * interfaces can't switch modes within a passhtrough port.
>> +			 */
>> +			if (mode == MACVLAN_MODE_PASSTHRU ||
>> +			    macvlan_passthru(vlan->port))
>> +				return -EINVAL;
> 
> [Severity: Low]
> This adds a new restriction: while the port is in passthru, any mode
> change of a source-mode device returns -EINVAL. The commit message does
> not mention this or a few other user-visible changes:
> 
> - The lower MAC restore moved from macvlan_port_destroy() to
>   macvlan_uninit() of the passthru device. It now runs even while source
>   devices stay on the port.
> 
> - macvlan_forward_source() only consumes the frame when
>   MACVLAN_FLAG_NODST is set. Without it, known-sender frames are cloned to
>   the source device and also delivered to the passthru device.
> 
> The commit message says:
> 
>   "The source mode devices represent the known senders while
>   all external senders terminate on passthru device."
> 
> That split seems to depend on NODST being set.
> 
> Documentation/networking/device_drivers/ethernet/intel/ice.rst also still
> says:
> 
>   "In passthru mode, you can only set up one MACVLAN device."
> 
> Could the commit message and that document be updated to describe these
> changes?


Okay.

> 
>> +			if (vlan->mode == MACVLAN_MODE_SOURCE)
>> +				macvlan_flush_sources(vlan->port, vlan);
>> +		}
>>  	}
>>  
>>  	if (data && data[IFLA_MACVLAN_FLAGS]) {
>>  		__u16 flags = nla_get_u16(data[IFLA_MACVLAN_FLAGS]);
>>  		bool promisc = (flags ^ vlan->flags) & MACVLAN_FLAG_NOPROMISC;
>> -		if (macvlan_passthru(vlan->port) && promisc) {
>> +		if (vlan->mode == MACVLAN_MODE_PASSTHRU && promisc) {
>>  			int err;
>>  
>>  			if (flags & MACVLAN_FLAG_NOPROMISC)
> 
> [Severity: Medium]
> This is a pre-existing issue and was not introduced by this patch.
> macvlan_changelink() changes the lower device's promiscuity count whenever
> MACVLAN_FLAG_NOPROMISC flips on the passthru device. It does not check
> dev->flags & IFF_UP. macvlan_open() and macvlan_stop() only take or drop
> that reference while the device is up.
> 
> Setting nopromisc on a passthru device that is down calls
> dev_set_promiscuity(lowerdev, -1) with no matching +1. That either drops a
> reference owned by another user, or wraps dev->promiscuity from 0 in
> __dev_set_promiscuity() and leaves IFF_PROMISC set. Clearing nopromisc
> while down takes a +1 that is never released.
> 
> Should this be gated on IFF_UP? For the passthru device, the patch's
> change from macvlan_passthru(vlan->port) to
> vlan->mode == MACVLAN_MODE_PASSTHRU is equivalent, so the behavior is
> unchanged.
> 
> [ ... ]

Do you want me to send a fix for this in a separate thread?

Best regards


-- 
Thomas Martitz <t.martitz@fritz.com>
FRITZ! Technology GmbH, Berlin (Germany)

      reply	other threads:[~2026-11-03 11:38 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-07 17:06 [PATCH v6 0/1] " Thomas Martitz
2026-10-07 17:06 ` [PATCH v6 1/1] " Thomas Martitz
2026-10-16  0:30   ` Jakub Kicinski
2026-11-03 11:37     ` Thomas Martitz [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=0559500f-e89c-4ddc-941d-c98f2b1d55f1@fritz.com \
    --to=t.martitz@fritz.com \
    --cc=andrew+netdev@lunn.ch \
    --cc=davem@davemloft.net \
    --cc=edumazet@kernel.org \
    --cc=horms@kernel.org \
    --cc=kuba@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®