Linux-HyperV List
 help / color / mirror / Atom feed
* Re: [PATCH net-next v11 03/13] vsock: reject bad VSOCK_NET_MODE_LOCAL configuration for G2H
From: Bobby Eshleman @ 2025-11-24 17:29 UTC (permalink / raw)
  To: Stefano Garzarella
  Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Stefan Hajnoczi, Michael S. Tsirkin, Jason Wang,
	Eugenio Pérez, Xuan Zhuo, K. Y. Srinivasan, Haiyang Zhang,
	Wei Liu, Dexuan Cui, Bryan Tan, Vishnu Dasa,
	Broadcom internal kernel review list, Shuah Khan, linux-kernel,
	virtualization, netdev, kvm, linux-hyperv, linux-kselftest,
	berrange, Sargun Dhillon, Bobby Eshleman
In-Reply-To: <g4xir3lupnjybh7fqig6xonp32ubotdf3emmrozdm52tpaxvxn@2t4ueynb7hqr>

On Mon, Nov 24, 2025 at 11:10:19AM +0100, Stefano Garzarella wrote:
> On Fri, Nov 21, 2025 at 11:01:53AM -0800, Bobby Eshleman wrote:
> > On Fri, Nov 21, 2025 at 03:24:25PM +0100, Stefano Garzarella wrote:
> > > On Thu, Nov 20, 2025 at 09:44:35PM -0800, Bobby Eshleman wrote:
> > > > From: Bobby Eshleman <bobbyeshleman@meta.com>
> > > >
> > > > Reject setting VSOCK_NET_MODE_LOCAL with -EOPNOTSUPP if a G2H transport
> > > > is operational. Additionally, reject G2H transport registration if there
> > > > already exists a namespace in local mode.
> > > >
> > > > G2H sockets break in local mode because the G2H transports don't support
> > > > namespacing yet. The current approach is to coerce packets coming out of
> > > > G2H transports into VSOCK_NET_MODE_GLOBAL mode, but it is not possible
> > > > to coerce sockets in the same way because it cannot be deduced which
> > > > transport will be used by the socket. Specifically, when bound to
> > > > VMADDR_CID_ANY in a nested VM (both G2H and H2G available), it is not
> > > > until a packet is received and matched to the bound socket that we
> > > > assign the transport. This presents a chicken-and-egg problem, because
> > > > we need the namespace to lookup the socket and resolve the transport,
> > > > but we need the transport to know how to use the namespace during
> > > > lookup.
> > > >
> > > > For that reason, this patch prevents VSOCK_NET_MODE_LOCAL from being
> > > > used on systems that support G2H, even nested systems that also have H2G
> > > > transports.
> > > >
> > > > Local mode is blocked based on detecting the presence of G2H devices
> > > > (when possible, as hyperv is special). This means that a host kernel
> > > > with G2H support compiled in (or has the module loaded), will still
> > > > support local mode if there is no G2H (e.g., virtio-vsock) device
> > > > detected. This enables using the same kernel in the host and in the
> > > > guest, as we do in kselftest.
> > > >
> > > > Systems with only namespace-aware transports (vhost-vsock, loopback) can
> > > > still use both VSOCK_NET_MODE_GLOBAL and VSOCK_NET_MODE_LOCAL modes as
> > > > intended.
> > > >
> > > > Add supports_local_mode() transport callback to indicate
> > > > transport-specific local mode support.
> > > >
> > > > These restrictions can be lifted in a future patch series when G2H
> > > > transports gain namespace support.
> > > >
> > > > Signed-off-by: Bobby Eshleman <bobbyeshleman@meta.com>
> > > > ---
> > > > Changes in v11:
> > > > - vhost_transport_supports_local_mode() returns false to keep things
> > > >  disabled until support comes online (Stefano)
> > > > - add comment above supports_local_mode() cb to clarify (Stefano)
> > > > - Remove redundant `ret = 0` initialization in vsock_net_mode_string()
> > > >  (Stefano)
> > > > - Refactor vsock_net_mode_string() to separate parsing from validation
> > > >  (Stefano)
> > > > - vmci returns false for supports_local_mode(), with comment
> > > >
> > > > Changes in v10:
> > > > - move this patch before any transports bring online namespacing (Stefano)
> > > > - move vsock_net_mode_string into critical section (Stefano)
> > > > - add ->supports_local_mode() callback to transports (Stefano)
> > > > ---
> > > > drivers/vhost/vsock.c            |  6 ++++++
> > > > include/net/af_vsock.h           | 11 +++++++++++
> > > > net/vmw_vsock/af_vsock.c         | 32 ++++++++++++++++++++++++++++++++
> > > > net/vmw_vsock/hyperv_transport.c |  6 ++++++
> > > > net/vmw_vsock/virtio_transport.c | 13 +++++++++++++
> > > > net/vmw_vsock/vmci_transport.c   | 12 ++++++++++++
> > > > net/vmw_vsock/vsock_loopback.c   |  6 ++++++
> > > > 7 files changed, 86 insertions(+)
> > > >
> > > > diff --git a/drivers/vhost/vsock.c b/drivers/vhost/vsock.c
> > > > index 69074656263d..4e3856aa2479 100644
> > > > --- a/drivers/vhost/vsock.c
> > > > +++ b/drivers/vhost/vsock.c
> > > > @@ -64,6 +64,11 @@ static u32 vhost_transport_get_local_cid(void)
> > > > 	return VHOST_VSOCK_DEFAULT_HOST_CID;
> > > > }
> > > >
> > > > +static bool vhost_transport_supports_local_mode(void)
> > > > +{
> > > > +	return false;
> > > > +}
> > > > +
> > > > /* Callers that dereference the return value must hold vhost_vsock_mutex or the
> > > >  * RCU read lock.
> > > >  */
> > > > @@ -412,6 +417,7 @@ static struct virtio_transport vhost_transport = {
> > > > 		.module                   = THIS_MODULE,
> > > >
> > > > 		.get_local_cid            = vhost_transport_get_local_cid,
> > > > +		.supports_local_mode	  = vhost_transport_supports_local_mode,
> > > >
> > > > 		.init                     = virtio_transport_do_socket_init,
> > > > 		.destruct                 = virtio_transport_destruct,
> > > > diff --git a/include/net/af_vsock.h b/include/net/af_vsock.h
> > > > index 59d97a143204..e24ef1d9fe02 100644
> > > > --- a/include/net/af_vsock.h
> > > > +++ b/include/net/af_vsock.h
> > > > @@ -180,6 +180,17 @@ struct vsock_transport {
> > > > 	/* Addressing. */
> > > > 	u32 (*get_local_cid)(void);
> > > >
> > > > +	/* Return true if the transport is compatible with
> > > > +	 * VSOCK_NET_MODE_LOCAL. Otherwise, return false.
> > > > +	 *
> > > > +	 * Transports should return false if they lack local-mode namespace
> > > > +	 * support (e.g., G2H transports like hyperv-vsock and vmci-vsock).
> > > > +	 * virtio-vsock returns true only if no device is present in order to
> > > > +	 * enable local mode in nested scenarios in which virtio-vsock is
> > > > +	 * loaded or built-in, but nonetheless unusable by sockets.
> > > > +	 */
> > > > +	bool (*supports_local_mode)(void);
> > > > +
> > > > 	/* Read a single skb */
> > > > 	int (*read_skb)(struct vsock_sock *, skb_read_actor_t);
> > > >
> > > > diff --git a/net/vmw_vsock/af_vsock.c b/net/vmw_vsock/af_vsock.c
> > > > index 243c0d588682..120adb9dad9f 100644
> > > > --- a/net/vmw_vsock/af_vsock.c
> > > > +++ b/net/vmw_vsock/af_vsock.c
> > > > @@ -91,6 +91,12 @@
> > > >  *   and locked down by a namespace manager. The default is "global". The mode
> > > >  *   is set per-namespace.
> > > >  *
> > > > + *   Note: LOCAL mode is only supported when using namespace-aware transports
> > > > + *   (vhost-vsock, loopback). If a guest-to-host transport (virtio-vsock,
> > > > + *   hyperv-vsock, vmci-vsock) is operational, attempts to set LOCAL mode will
> > > > + *   fail with EOPNOTSUPP, as these transports do not support per-namespace
> > > > + *   isolation.
> > > > + *
> > > >  *   The modes affect the allocation and accessibility of CIDs as follows:
> > > >  *
> > > >  *   - global - access and allocation are all system-wide
> > > > @@ -2794,6 +2800,15 @@ static int vsock_net_mode_string(const struct ctl_table *table, int write,
> > > > 	else
> > > > 		return -EINVAL;
> > > >
> > > > +	mutex_lock(&vsock_register_mutex);
> > > > +	if (mode == VSOCK_NET_MODE_LOCAL &&
> > > > +	    transport_g2h && transport_g2h->supports_local_mode &&
> > > > +	    !transport_g2h->supports_local_mode()) {
> > > > +		mutex_unlock(&vsock_register_mutex);
> > > > +		return -EOPNOTSUPP;
> > > > +	}
> > > > +	mutex_unlock(&vsock_register_mutex);
> > > 
> > > Wait, I think we already discussed about this, vsock_net_write_mode() must
> > > be called with the lock held.
> > > 
> > > See
> > > https://lore.kernel.org/netdev/aRTTwuuXSz5CvNjt@devvm11784.nha0.facebook.com/
> > > 
> > 
> > Ah right, oversight on my part.
> > 
> > > Since I guess we need another version of this patch, can you check the
> > > commit description to see if it reflects what we are doing now
> > > (e.g vhost is not enabled)?
> > > 
> > > Also I don't understand why for vhost we will enable it later, but for
> > > virtio_transport and vsock_loopback we are enabling it now, also if this
> > > patch is before the support on that transports. I'm a bit confused.
> > > 
> > > If something is unclear, let's discuss it before sending a new version.
> > > 
> > > 
> > > What I had in mind was, add this patch and explain why we need this new
> > > callback (like you did), but enable the support in the patches that
> > > really enable it for any transport. But maybe what is not clear to me is
> > > that we need this only for G2H. But now I'm confused about the discussion
> > > around vmci H2G. We decided to discard also that one, but here we are not
> > > checking that?
> > > I mean here we are calling supports_local_mode() only on G2H IIUC.
> > 
> > Ah right, VMCI broke my original mental model of only needing this check
> > for G2H (originally I didn't realize VMCI was H2G too).
> > 
> > I think now, we actually need to do this check for all of the transports
> > no? Including h2g, g2h, local, and dgram?
> > 
> > Additionally, the commit description needs to be updated to reflect that.
> 
> Let's take a step back, though, because I tried to understand the problem
> better and I'm confused.
> 
> For example, in vmci (G2H side), when a packet arrives, we always use
> vsock_find_connected_socket(), which only searches in GLOBAL. So connections
> originating from the host can only reach global sockets in the guest. In
> this direction (host -> guest), we should be fine, right?
> 
> Now let's consider the other direction, from guest to host, so the
> connection should be generated via vsock_connect().
> Here I see that we are not doing anything with regard to the source
> namespace. At this point, my question is whether we should modify
> vsock_assign_transport() or transport->stream_allow() to do this for each
> stream, and not prevent loading a G2H module a priori.
> 
> For example, stream_allow() could check that the socket namespace is
> supported by the assigned transport. E.g., vmci can check that if the
> namespace mode is not GLOBAL, then it returns false. (Same thing in
> virtio-vsock, I mean the G2H driver).
> 
> This should solve the guest -> host direction, but at this point I wonder if
> I'm missing something.

For the G2H connect case that is true, but the situation gets a little
fuzzier on the G2H RX side w/ VMADDR_CID_ANY listeners.

Let's say we have a nested system w/ both virtio-vsock and vhost-vsock.
We have a listener in namespace local on VMADDR_CID_ANY. So far, no
transport is assigned, so we can't call t->stream_allow() yet.
virtio-vsock only knows of global mode, so its lookup will fail (unless
we hack in some special case to virtio_transport_recv_pkt() to scan
local namespaces). vhost-vsock will work as expected. Letting local mode
sockets be silently unreachable by virtio-vsock seems potentially
confusing for users. If the system were not nested, we can pre-resolve
VMADDR_CID_ANY in bind() and handle things upfront as well. Rejecting
local mode outright is just a broad guardrail.

If we're trying to find a less heavy-handed option, we might be able to
do the following:

- change bind(cid) w/ cid != VMADDR_CID_ANY to directly assign the transport
  for all socket types (not just SOCK_DGRAM)

- vsock_assign_transport() can outright fail if !t->supports_local_mode()
  and sock_net(sk) has mode local

- bind(VMADDR_CID_ANY) can maybe print (once) to dmesg a warning that
  only the H2G transport will land on VMADDR_CID_ANY sockets.

I'm certainly open to other suggestions.

Best,
Bobby

^ permalink raw reply

* Re: [PATCH net-next v11 03/13] vsock: reject bad VSOCK_NET_MODE_LOCAL configuration for G2H
From: Stefano Garzarella @ 2025-11-24 17:54 UTC (permalink / raw)
  To: Bobby Eshleman
  Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Stefan Hajnoczi, Michael S. Tsirkin, Jason Wang,
	Eugenio Pérez, Xuan Zhuo, K. Y. Srinivasan, Haiyang Zhang,
	Wei Liu, Dexuan Cui, Bryan Tan, Vishnu Dasa,
	Broadcom internal kernel review list, Shuah Khan, linux-kernel,
	virtualization, netdev, kvm, linux-hyperv, linux-kselftest,
	berrange, Sargun Dhillon, Bobby Eshleman
In-Reply-To: <aSSV4RlRcW+uGy+n@devvm11784.nha0.facebook.com>

On Mon, Nov 24, 2025 at 09:29:05AM -0800, Bobby Eshleman wrote:
>On Mon, Nov 24, 2025 at 11:10:19AM +0100, Stefano Garzarella wrote:
>> On Fri, Nov 21, 2025 at 11:01:53AM -0800, Bobby Eshleman wrote:
>> > On Fri, Nov 21, 2025 at 03:24:25PM +0100, Stefano Garzarella wrote:
>> > > On Thu, Nov 20, 2025 at 09:44:35PM -0800, Bobby Eshleman wrote:

[...]

>> >
>> > > Since I guess we need another version of this patch, can you check the
>> > > commit description to see if it reflects what we are doing now
>> > > (e.g vhost is not enabled)?
>> > >
>> > > Also I don't understand why for vhost we will enable it later, but for
>> > > virtio_transport and vsock_loopback we are enabling it now, also if this
>> > > patch is before the support on that transports. I'm a bit confused.
>> > >
>> > > If something is unclear, let's discuss it before sending a new version.
>> > >
>> > >
>> > > What I had in mind was, add this patch and explain why we need this new
>> > > callback (like you did), but enable the support in the patches that
>> > > really enable it for any transport. But maybe what is not clear to me is
>> > > that we need this only for G2H. But now I'm confused about the discussion
>> > > around vmci H2G. We decided to discard also that one, but here we are not
>> > > checking that?
>> > > I mean here we are calling supports_local_mode() only on G2H IIUC.
>> >
>> > Ah right, VMCI broke my original mental model of only needing this check
>> > for G2H (originally I didn't realize VMCI was H2G too).
>> >
>> > I think now, we actually need to do this check for all of the transports
>> > no? Including h2g, g2h, local, and dgram?
>> >
>> > Additionally, the commit description needs to be updated to reflect that.
>>
>> Let's take a step back, though, because I tried to understand the problem
>> better and I'm confused.
>>
>> For example, in vmci (G2H side), when a packet arrives, we always use
>> vsock_find_connected_socket(), which only searches in GLOBAL. So connections
>> originating from the host can only reach global sockets in the guest. In
>> this direction (host -> guest), we should be fine, right?
>>
>> Now let's consider the other direction, from guest to host, so the
>> connection should be generated via vsock_connect().
>> Here I see that we are not doing anything with regard to the source
>> namespace. At this point, my question is whether we should modify
>> vsock_assign_transport() or transport->stream_allow() to do this for each
>> stream, and not prevent loading a G2H module a priori.
>>
>> For example, stream_allow() could check that the socket namespace is
>> supported by the assigned transport. E.g., vmci can check that if the
>> namespace mode is not GLOBAL, then it returns false. (Same thing in
>> virtio-vsock, I mean the G2H driver).
>>
>> This should solve the guest -> host direction, but at this point I wonder if
>> I'm missing something.
>
>For the G2H connect case that is true, but the situation gets a little
>fuzzier on the G2H RX side w/ VMADDR_CID_ANY listeners.
>
>Let's say we have a nested system w/ both virtio-vsock and vhost-vsock.
>We have a listener in namespace local on VMADDR_CID_ANY. So far, no
>transport is assigned, so we can't call t->stream_allow() yet.
>virtio-vsock only knows of global mode, so its lookup will fail (unless

What is the problem of failing in this case?
I mean, we are documenting that G2H will not be able to reach socket in 
namespaces with "local" mode. Old (and default) behaviour is still 
allowing them, right?

I don't think it conflicts with the definition of “local” either, 
because these connections are coming from outside, and the user doesn't 
expect to be able to receive them in a “local” namespace, unless there 
is a way to put the device in the namespace (as with net). But this 
method doesn't exist yet, and by documenting it sufficiently, we can say 
that it will be supported in the future, but not for now.

>we hack in some special case to virtio_transport_recv_pkt() to scan
>local namespaces). vhost-vsock will work as expected. Letting local mode
>sockets be silently unreachable by virtio-vsock seems potentially
>confusing for users. If the system were not nested, we can pre-resolve
>VMADDR_CID_ANY in bind() and handle things upfront as well. Rejecting
>local mode outright is just a broad guardrail.

Okay, but in that case, we are not supporting “local” mode too, but we 
are also preventing “global” from being used on these when we are in a 
nested environment. What is the advantage of this approach?

>
>If we're trying to find a less heavy-handed option, we might be able to
>do the following:
>
>- change bind(cid) w/ cid != VMADDR_CID_ANY to directly assign the 
>transport
>  for all socket types (not just SOCK_DGRAM)

That would be nice, but it wouldn't solve the problem with 
VMADDR_CID_ANY, which I guess is the use case in 99% of cases.

>
>- vsock_assign_transport() can outright fail if !t->supports_local_mode()
>  and sock_net(sk) has mode local

But in this case, why not reusing stream_allow() ?

>
>- bind(VMADDR_CID_ANY) can maybe print (once) to dmesg a warning that
>  only the H2G transport will land on VMADDR_CID_ANY sockets.

mmm, I'm not sure about that, we should ask net maintainer, but IMO 
documenting that in af_vsock.c and man pages should be fine, till G2H 
will support that.

>
>I'm certainly open to other suggestions.

IMO we should avoid the failure when loading G2H, which is more 
confusing than just discard connection from the host to a "local" 
namespace. We should try at least to support the "global" namespace.

Thanks,
Stefano


^ permalink raw reply

* Re: [PATCH net-next v11 03/13] vsock: reject bad VSOCK_NET_MODE_LOCAL configuration for G2H
From: Bobby Eshleman @ 2025-11-24 18:25 UTC (permalink / raw)
  To: Stefano Garzarella
  Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Stefan Hajnoczi, Michael S. Tsirkin, Jason Wang,
	Eugenio Pérez, Xuan Zhuo, K. Y. Srinivasan, Haiyang Zhang,
	Wei Liu, Dexuan Cui, Bryan Tan, Vishnu Dasa,
	Broadcom internal kernel review list, Shuah Khan, linux-kernel,
	virtualization, netdev, kvm, linux-hyperv, linux-kselftest,
	berrange, Sargun Dhillon, Bobby Eshleman
In-Reply-To: <qvu2mgxs7scbuwcb2ui7eh3qe3l7mlcjq6e2favd4aqcs52r2r@oqbrlp4gxdwl>

On Mon, Nov 24, 2025 at 06:54:45PM +0100, Stefano Garzarella wrote:
> On Mon, Nov 24, 2025 at 09:29:05AM -0800, Bobby Eshleman wrote:
> > On Mon, Nov 24, 2025 at 11:10:19AM +0100, Stefano Garzarella wrote:
> > > On Fri, Nov 21, 2025 at 11:01:53AM -0800, Bobby Eshleman wrote:
> > > > On Fri, Nov 21, 2025 at 03:24:25PM +0100, Stefano Garzarella wrote:
> > > > > On Thu, Nov 20, 2025 at 09:44:35PM -0800, Bobby Eshleman wrote:
> 
> [...]
> 
> > > >
> > > > > Since I guess we need another version of this patch, can you check the
> > > > > commit description to see if it reflects what we are doing now
> > > > > (e.g vhost is not enabled)?
> > > > >
> > > > > Also I don't understand why for vhost we will enable it later, but for
> > > > > virtio_transport and vsock_loopback we are enabling it now, also if this
> > > > > patch is before the support on that transports. I'm a bit confused.
> > > > >
> > > > > If something is unclear, let's discuss it before sending a new version.
> > > > >
> > > > >
> > > > > What I had in mind was, add this patch and explain why we need this new
> > > > > callback (like you did), but enable the support in the patches that
> > > > > really enable it for any transport. But maybe what is not clear to me is
> > > > > that we need this only for G2H. But now I'm confused about the discussion
> > > > > around vmci H2G. We decided to discard also that one, but here we are not
> > > > > checking that?
> > > > > I mean here we are calling supports_local_mode() only on G2H IIUC.
> > > >
> > > > Ah right, VMCI broke my original mental model of only needing this check
> > > > for G2H (originally I didn't realize VMCI was H2G too).
> > > >
> > > > I think now, we actually need to do this check for all of the transports
> > > > no? Including h2g, g2h, local, and dgram?
> > > >
> > > > Additionally, the commit description needs to be updated to reflect that.
> > > 
> > > Let's take a step back, though, because I tried to understand the problem
> > > better and I'm confused.
> > > 
> > > For example, in vmci (G2H side), when a packet arrives, we always use
> > > vsock_find_connected_socket(), which only searches in GLOBAL. So connections
> > > originating from the host can only reach global sockets in the guest. In
> > > this direction (host -> guest), we should be fine, right?
> > > 
> > > Now let's consider the other direction, from guest to host, so the
> > > connection should be generated via vsock_connect().
> > > Here I see that we are not doing anything with regard to the source
> > > namespace. At this point, my question is whether we should modify
> > > vsock_assign_transport() or transport->stream_allow() to do this for each
> > > stream, and not prevent loading a G2H module a priori.
> > > 
> > > For example, stream_allow() could check that the socket namespace is
> > > supported by the assigned transport. E.g., vmci can check that if the
> > > namespace mode is not GLOBAL, then it returns false. (Same thing in
> > > virtio-vsock, I mean the G2H driver).
> > > 
> > > This should solve the guest -> host direction, but at this point I wonder if
> > > I'm missing something.
> > 
> > For the G2H connect case that is true, but the situation gets a little
> > fuzzier on the G2H RX side w/ VMADDR_CID_ANY listeners.
> > 
> > Let's say we have a nested system w/ both virtio-vsock and vhost-vsock.
> > We have a listener in namespace local on VMADDR_CID_ANY. So far, no
> > transport is assigned, so we can't call t->stream_allow() yet.
> > virtio-vsock only knows of global mode, so its lookup will fail (unless
> 
> What is the problem of failing in this case?
> I mean, we are documenting that G2H will not be able to reach socket in
> namespaces with "local" mode. Old (and default) behaviour is still allowing
> them, right?
> 
> I don't think it conflicts with the definition of “local” either, because
> these connections are coming from outside, and the user doesn't expect to be
> able to receive them in a “local” namespace, unless there is a way to put
> the device in the namespace (as with net). But this method doesn't exist
> yet, and by documenting it sufficiently, we can say that it will be
> supported in the future, but not for now.
> 
> > we hack in some special case to virtio_transport_recv_pkt() to scan
> > local namespaces). vhost-vsock will work as expected. Letting local mode
> > sockets be silently unreachable by virtio-vsock seems potentially
> > confusing for users. If the system were not nested, we can pre-resolve
> > VMADDR_CID_ANY in bind() and handle things upfront as well. Rejecting
> > local mode outright is just a broad guardrail.
> 
> Okay, but in that case, we are not supporting “local” mode too, but we are
> also preventing “global” from being used on these when we are in a nested
> environment. What is the advantage of this approach?
> 
> > 
> > If we're trying to find a less heavy-handed option, we might be able to
> > do the following:
> > 
> > - change bind(cid) w/ cid != VMADDR_CID_ANY to directly assign the
> > transport
> >  for all socket types (not just SOCK_DGRAM)
> 
> That would be nice, but it wouldn't solve the problem with VMADDR_CID_ANY,
> which I guess is the use case in 99% of cases.
> 
> > 
> > - vsock_assign_transport() can outright fail if !t->supports_local_mode()
> >  and sock_net(sk) has mode local
> 
> But in this case, why not reusing stream_allow() ?
> 
> > 
> > - bind(VMADDR_CID_ANY) can maybe print (once) to dmesg a warning that
> >  only the H2G transport will land on VMADDR_CID_ANY sockets.
> 
> mmm, I'm not sure about that, we should ask net maintainer, but IMO
> documenting that in af_vsock.c and man pages should be fine, till G2H will
> support that.
> 
> > 
> > I'm certainly open to other suggestions.
> 
> IMO we should avoid the failure when loading G2H, which is more confusing
> than just discard connection from the host to a "local" namespace. We should
> try at least to support the "global" namespace.
> 
> Thanks,
> Stefano


I'm 100% fine with that approach. I just wanted to make sure we landed
in the right place for how users may encounter places that there is no
local mode support.

So for next steps, we can drop this patch and add explicit logic in
->stream_allow() to allow local mode for vhost/loopback and reject for
others? Plus, add documentation about what happens for VMADDR_CID_ANY
(will only receive vhost/loopback traffic in local mode)?

Best,
Bobby

^ permalink raw reply

* [RFC PATCH] Drivers: hv: Confidential VMBus exernal memory support
From: Tianyu Lan @ 2025-11-24 18:29 UTC (permalink / raw)
  To: kys, haiyangz, wei.liu, decui, longli, vdso
  Cc: Tianyu Lan, linux-hyperv, linux-kernel

In CVM(Confidential VM), system memory is encrypted
by default. Device drivers typically use the swiotlb
bounce buffer for DMA memory, which is decrypted
and shared between the guest and host. Confidential
Vmbus, however, supports a confidential channel
that employs encrypted memory for the Vmbus ring
buffer and external DMA memory. The support for
the confidential ring buffer has already been
integrated.

In CVM, device drivers usually employ the standard
DMA API to map DMA memory with the bounce buffer,
which remains transparent to the device driver.
For external DMA memory support, Hyper-V specific
DMA operations are introduced, bypassing the bounce
buffer when the confidential external memory flag
is set. These DMA operations might also be reused
for TDISP devices in the future, which also support
DMA operations with encrypted memory.

The DMA operations used are global architecture
DMA operations (for details, see get_arch_dma_ops()
and get_dma_ops()), and there is no need to set up
for each device individually.

Signed-off-by: Tianyu Lan <tiala@microsoft.com>
---
 drivers/hv/vmbus_drv.c | 90 +++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 89 insertions(+), 1 deletion(-)

diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
index 0dc4692b411a..ca31231b2c32 100644
--- a/drivers/hv/vmbus_drv.c
+++ b/drivers/hv/vmbus_drv.c
@@ -39,6 +39,9 @@
 #include <clocksource/hyperv_timer.h>
 #include <asm/mshyperv.h>
 #include "hyperv_vmbus.h"
+#include "../../kernel/dma/direct.h"
+
+extern const struct dma_map_ops *dma_ops;
 
 struct vmbus_dynid {
 	struct list_head node;
@@ -1429,6 +1432,88 @@ static int vmbus_alloc_synic_and_connect(void)
 	return -ENOMEM;
 }
 
+
+static bool hyperv_private_memory_dma(struct device *dev)
+{
+	struct hv_device *hv_dev = device_to_hv_device(dev);
+
+	if (hv_dev && hv_dev->channel && hv_dev->channel->co_external_memory)
+		return true;
+	else
+		return false;
+}
+
+static dma_addr_t hyperv_dma_map_page(struct device *dev, struct page *page,
+		unsigned long offset, size_t size,
+		enum dma_data_direction dir,
+		unsigned long attrs)
+{
+	phys_addr_t phys = page_to_phys(page) + offset;
+
+	if (hyperv_private_memory_dma(dev))
+		return __phys_to_dma(dev, phys);
+	else
+		return dma_direct_map_phys(dev, phys, size, dir, attrs);
+}
+
+static void hyperv_dma_unmap_page(struct device *dev, dma_addr_t dma_handle,
+		size_t size, enum dma_data_direction dir, unsigned long attrs)
+{
+	if (!hyperv_private_memory_dma(dev))
+		dma_direct_unmap_phys(dev, dma_handle, size, dir, attrs);
+}
+
+static int hyperv_dma_map_sg(struct device *dev, struct scatterlist *sgl,
+		int nelems, enum dma_data_direction dir,
+		unsigned long attrs)
+{
+	struct scatterlist *sg;
+	dma_addr_t dma_addr;
+	int i;
+
+	if (hyperv_private_memory_dma(dev)) {
+		for_each_sg(sgl, sg, nelems, i) {
+			dma_addr = __phys_to_dma(dev, sg_phys(sg));
+			sg_dma_address(sg) = dma_addr;
+			sg_dma_len(sg) = sg->length;
+		}
+
+		return nelems;
+	} else {
+		return dma_direct_map_sg(dev, sgl, nelems, dir, attrs);
+	}
+}
+
+static void hyperv_dma_unmap_sg(struct device *dev, struct scatterlist *sgl,
+		int nelems, enum dma_data_direction dir, unsigned long attrs)
+{
+	if (!hyperv_private_memory_dma(dev))
+		dma_direct_unmap_sg(dev, sgl, nelems, dir, attrs);
+}
+
+static int hyperv_dma_supported(struct device *dev, u64 mask)
+{
+	dev->coherent_dma_mask = mask;
+	return 1;
+}
+
+static size_t hyperv_dma_max_mapping_size(struct device *dev)
+{
+	if (hyperv_private_memory_dma(dev))
+		return SIZE_MAX;
+	else
+		return swiotlb_max_mapping_size(dev);
+}
+
+const struct dma_map_ops hyperv_dma_ops = {
+	.map_page               = hyperv_dma_map_page,
+	.unmap_page             = hyperv_dma_unmap_page,
+	.map_sg                 = hyperv_dma_map_sg,
+	.unmap_sg               = hyperv_dma_unmap_sg,
+	.dma_supported          = hyperv_dma_supported,
+	.max_mapping_size	= hyperv_dma_max_mapping_size,
+};
+
 /*
  * vmbus_bus_init -Main vmbus driver initialization routine.
  *
@@ -1479,8 +1564,11 @@ static int vmbus_bus_init(void)
 	 * doing that on each VP while initializing SynIC's wastes time.
 	 */
 	is_confidential = ms_hyperv.confidential_vmbus_available;
-	if (is_confidential)
+	if (is_confidential) {
+		dma_ops = &hyperv_dma_ops;
 		pr_info("Establishing connection to the confidential VMBus\n");
+	}
+
 	hv_para_set_sint_proxy(!is_confidential);
 	ret = vmbus_alloc_synic_and_connect();
 	if (ret)
-- 
2.50.1


^ permalink raw reply related

* Re: [PATCH v6 5/5] Drivers: hv: Add support for movable memory regions
From: Stanislav Kinsburskii @ 2025-11-24 21:28 UTC (permalink / raw)
  To: Michael Kelley
  Cc: kys@microsoft.com, haiyangz@microsoft.com, wei.liu@kernel.org,
	decui@microsoft.com, linux-hyperv@vger.kernel.org,
	linux-kernel@vger.kernel.org
In-Reply-To: <SN6PR02MB415740A80DA4FF9661040147D4D5A@SN6PR02MB4157.namprd02.prod.outlook.com>

On Fri, Nov 21, 2025 at 05:45:20AM +0000, Michael Kelley wrote:
> From: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com> Sent: Wednesday, November 19, 2025 4:45 PM
> > 
> > On Tue, Nov 18, 2025 at 04:29:56PM +0000, Michael Kelley wrote:
> > > From: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com> Sent: Monday, November 17, 2025 8:53 AM
> 
> [snip]
> 
> > > > diff --git a/drivers/hv/Kconfig b/drivers/hv/Kconfig
> > > > index 0b8c391a0342..5f1637cbb6e3 100644
> > > > --- a/drivers/hv/Kconfig
> > > > +++ b/drivers/hv/Kconfig
> > > > @@ -75,6 +75,7 @@ config MSHV_ROOT
> > > >  	depends on PAGE_SIZE_4KB
> > > >  	select EVENTFD
> > > >  	select VIRT_XFER_TO_GUEST_WORK
> > > > +	select HMM_MIRROR
> > >
> > > Couldn't you also do "select MMU_NOTIFIER" to avoid the #ifdef's
> > > and stubs for when it isn't selected? There are other Linux kernel
> > > drivers that select it. Or is the intent to allow building an image that
> > > doesn't support unpinned memory, and the #ifdef's save space?
> > >
> > 
> > That's an interesting question. This driver can function without MMU notifiers
> > by pinning all memory, which might be advantageous for certain real-time
> > applications.
> > However, since most other virtualization solutions use MMU_NOTIFIER, there
> > doesn't appear to be a strong reason for this driver to deviate.
> 
> I'm not clear on your last sentence. Could you elaborate?
> 

I meant I'll select MMU_NOTIFIER.

> 
> Right. But even for pinned regions as coded today, is that assumption
> correct? Due to memory being fragmented at the time of region creation,
> it would be possible that some 2Meg ranges in a region are backed by a large
> page, while other 2Meg ranges are not. In that case, a single per-region flag
> isn't enough information. Or does the hypercall work OK if the "map huge
> page" flag is passed when the range isn't a huge page? I'm not clear on what
> the hypercall requires as input.
> 

It is not with THP enabled and missing MAP_HUGETLB mmap flag.
I'll fix it in the next revision.

> 
> OK.  So what is the impact? Losing the perf benefit of mapping guest
> memory in the SLAT as a 2 Meg large page vs. a bunch of individual 4K
> pages? Anything else?
> 

I decided to rework it in scope of these the series.

> > 
> > This is possible, if hypervisor returns some invalid GFN.
> > But there is also a possibility, that this code can race with region removal from a guest.
> > I'll address it in the next revision.
> 
> In either of these cases, what happens next? The MSHV_RUN_VP ioctl
> will return to user space with the unhandled HVMSG_GPA_INTERCEPT
> message. Is there anything user space can do to enable the VP to make
> progress past the fault? Or does user space just have to terminate the
> guest VM?
> 

I don't think there is much to be done here in kernel.
Control will be returned to VMM, which will likely kill the guest.

> > >
> > > I'm pretty sure region->start_uaddr is always page aligned. But what
> > > about range->start and range->end?  The code here and below assumes
> > > they are page aligned. It also assumes that range->end is greater than
> > > range->start so the computation of page_count doesn't wrap and so
> > > page_count is >= 1. I don't know whether checks for these assumptions
> > > are appropriate.
> > >
> > 
> > There is a check for memory region size to be non-zero and page aligned
> > in mshv_partition_ioct_set_memory function, which is the only caller for
> > memory region creation. And region start is defined in PFNs.
> > 
> 
> Right -- no disagreement that the region start and size are page aligned
> and non-zero. But what about the range that is being invalidated?
> (i.e., range->start and range->end) The values in that range are coming
> from the mm subsystem, and aren't governed by how a region is created.
> If that range is a subset of the MSHV region, then
> mshv_region_internal_invalidate() will be operating on whatever subset
> was provided in the 'range' argument.
> 
> mshv_region_interval_invalidate() is ultimately called from
> mmu_notifier_invalidate_range_start(), which has about 30 different
> callers in the kernel, mostly in the mm subsystem. It wasn't
> clear to me what rules, if any, those 30 callers are following when they
> set up the range to be invalidated. 
> 
> Michael

I see what you mean. Yes, these values are guarantee to be page aligned.
And it must be as anything else can't be invalidated.

Thanks,
Stanislav



^ permalink raw reply

* Re: [PATCH v2 04/10] sysfb: Replace screen_info with sysfb_primary_display
From: Bjorn Helgaas @ 2025-11-24 23:56 UTC (permalink / raw)
  To: Thomas Zimmermann
  Cc: ardb, javierm, arnd, richard.lyu, x86, linux-arm-kernel,
	linux-kernel, linux-efi, loongarch, linux-riscv, dri-devel,
	linux-hyperv, linux-pci, linux-fbdev
In-Reply-To: <20251124165116.502813-5-tzimmermann@suse.de>

On Mon, Nov 24, 2025 at 05:40:16PM +0100, Thomas Zimmermann wrote:
> Replace the global screen_info with sysfb_primary_display of type
> struct sysfb_display_info. Adapt all users of screen_info.
> 
> Instances of screen_info are defined for x86, loongarch and EFI,
> with only one instance compiled into a specific build. Replace all
> of them with sysfb_primary_display.
> 
> All existing users of screen_info are updated by pointing them to
> sysfb_primary_display.screen instead. This introduces some churn to
> the code, but has no impact on functionality.
> 
> Boot parameters and EFI config tables are unchanged. They transfer
> screen_info as before. The logic in EFI's alloc_screen_info() changes
> slightly, as it now returns the screen field of sysfb_primary_display.
> 
> v2:
> - update comment
> - rename init_screen_info() to init_primary_display()
> 
> Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
> Acked-by: Arnd Bergmann <arnd@arndb.de>
> Acked-by: Ard Biesheuvel <ardb@kernel.org>

Acked-by: Bjorn Helgaas <bhelgaas@google.com>  # drivers/pci/

> ---
>  arch/arm64/kernel/image-vars.h                |  2 +-
>  arch/loongarch/kernel/efi.c                   | 15 +++++++------
>  arch/loongarch/kernel/image-vars.h            |  2 +-
>  arch/riscv/kernel/image-vars.h                |  2 +-
>  arch/x86/kernel/kexec-bzimage64.c             |  4 +++-
>  arch/x86/kernel/setup.c                       | 10 +++++----
>  arch/x86/video/video-common.c                 |  4 ++--
>  drivers/firmware/efi/earlycon.c               |  8 +++----
>  drivers/firmware/efi/efi-init.c               | 22 +++++++++----------
>  drivers/firmware/efi/libstub/efi-stub-entry.c | 18 ++++++++++-----
>  drivers/firmware/efi/sysfb_efi.c              |  4 ++--
>  drivers/firmware/sysfb.c                      |  6 ++---
>  drivers/hv/vmbus_drv.c                        |  6 ++---
>  drivers/pci/vgaarb.c                          |  4 ++--
>  drivers/video/screen_info_pci.c               |  5 +++--
>  include/linux/screen_info.h                   |  2 --
>  include/linux/sysfb.h                         |  5 +++--
>  17 files changed, 66 insertions(+), 53 deletions(-)
> 
> diff --git a/arch/arm64/kernel/image-vars.h b/arch/arm64/kernel/image-vars.h
> index 85bc629270bd..d7b0d12b1015 100644
> --- a/arch/arm64/kernel/image-vars.h
> +++ b/arch/arm64/kernel/image-vars.h
> @@ -38,7 +38,7 @@ PROVIDE(__efistub__end			= _end);
>  PROVIDE(__efistub___inittext_end       	= __inittext_end);
>  PROVIDE(__efistub__edata		= _edata);
>  #if defined(CONFIG_EFI_EARLYCON) || defined(CONFIG_SYSFB)
> -PROVIDE(__efistub_screen_info		= screen_info);
> +PROVIDE(__efistub_sysfb_primary_display	= sysfb_primary_display);
>  #endif
>  PROVIDE(__efistub__ctype		= _ctype);
>  
> diff --git a/arch/loongarch/kernel/efi.c b/arch/loongarch/kernel/efi.c
> index 860a3bc030e0..638a392d2cd2 100644
> --- a/arch/loongarch/kernel/efi.c
> +++ b/arch/loongarch/kernel/efi.c
> @@ -18,7 +18,7 @@
>  #include <linux/kobject.h>
>  #include <linux/memblock.h>
>  #include <linux/reboot.h>
> -#include <linux/screen_info.h>
> +#include <linux/sysfb.h>
>  #include <linux/uaccess.h>
>  
>  #include <asm/early_ioremap.h>
> @@ -75,11 +75,11 @@ bool efi_poweroff_required(void)
>  unsigned long __initdata screen_info_table = EFI_INVALID_TABLE_ADDR;
>  
>  #if defined(CONFIG_SYSFB) || defined(CONFIG_EFI_EARLYCON)
> -struct screen_info screen_info __section(".data");
> -EXPORT_SYMBOL_GPL(screen_info);
> +struct sysfb_display_info sysfb_primary_display __section(".data");
> +EXPORT_SYMBOL_GPL(sysfb_primary_display);
>  #endif
>  
> -static void __init init_screen_info(void)
> +static void __init init_primary_display(void)
>  {
>  	struct screen_info *si;
>  
> @@ -91,11 +91,12 @@ static void __init init_screen_info(void)
>  		pr_err("Could not map screen_info config table\n");
>  		return;
>  	}
> -	screen_info = *si;
> +	sysfb_primary_display.screen = *si;
>  	memset(si, 0, sizeof(*si));
>  	early_memunmap(si, sizeof(*si));
>  
> -	memblock_reserve(__screen_info_lfb_base(&screen_info), screen_info.lfb_size);
> +	memblock_reserve(__screen_info_lfb_base(&sysfb_primary_display.screen),
> +			 sysfb_primary_display.screen.lfb_size);
>  }
>  
>  void __init efi_init(void)
> @@ -127,7 +128,7 @@ void __init efi_init(void)
>  	set_bit(EFI_CONFIG_TABLES, &efi.flags);
>  
>  	if (IS_ENABLED(CONFIG_EFI_EARLYCON) || IS_ENABLED(CONFIG_SYSFB))
> -		init_screen_info();
> +		init_primary_display();
>  
>  	if (boot_memmap == EFI_INVALID_TABLE_ADDR)
>  		return;
> diff --git a/arch/loongarch/kernel/image-vars.h b/arch/loongarch/kernel/image-vars.h
> index 41ddcf56d21c..e557ebd46c2b 100644
> --- a/arch/loongarch/kernel/image-vars.h
> +++ b/arch/loongarch/kernel/image-vars.h
> @@ -12,7 +12,7 @@ __efistub_kernel_entry		= kernel_entry;
>  __efistub_kernel_asize		= kernel_asize;
>  __efistub_kernel_fsize		= kernel_fsize;
>  #if defined(CONFIG_EFI_EARLYCON) || defined(CONFIG_SYSFB)
> -__efistub_screen_info		= screen_info;
> +__efistub_sysfb_primary_display	= sysfb_primary_display;
>  #endif
>  
>  #endif
> diff --git a/arch/riscv/kernel/image-vars.h b/arch/riscv/kernel/image-vars.h
> index 3df30dd1c458..3bd9d06a8b8f 100644
> --- a/arch/riscv/kernel/image-vars.h
> +++ b/arch/riscv/kernel/image-vars.h
> @@ -29,7 +29,7 @@ __efistub__end			= _end;
>  __efistub__edata		= _edata;
>  __efistub___init_text_end	= __init_text_end;
>  #if defined(CONFIG_EFI_EARLYCON) || defined(CONFIG_SYSFB)
> -__efistub_screen_info		= screen_info;
> +__efistub_sysfb_primary_display	= sysfb_primary_display;
>  #endif
>  
>  #endif
> diff --git a/arch/x86/kernel/kexec-bzimage64.c b/arch/x86/kernel/kexec-bzimage64.c
> index c3244ac680d1..7508d0ccc740 100644
> --- a/arch/x86/kernel/kexec-bzimage64.c
> +++ b/arch/x86/kernel/kexec-bzimage64.c
> @@ -20,6 +20,7 @@
>  #include <linux/of_fdt.h>
>  #include <linux/efi.h>
>  #include <linux/random.h>
> +#include <linux/sysfb.h>
>  
>  #include <asm/bootparam.h>
>  #include <asm/setup.h>
> @@ -303,7 +304,8 @@ setup_boot_parameters(struct kimage *image, struct boot_params *params,
>  	params->hdr.hardware_subarch = boot_params.hdr.hardware_subarch;
>  
>  	/* Copying screen_info will do? */
> -	memcpy(&params->screen_info, &screen_info, sizeof(struct screen_info));
> +	memcpy(&params->screen_info, &sysfb_primary_display.screen,
> +	       sizeof(sysfb_primary_display.screen));
>  
>  	/* Fill in memsize later */
>  	params->screen_info.ext_mem_k = 0;
> diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c
> index 1b2edd07a3e1..675e4b9deb1f 100644
> --- a/arch/x86/kernel/setup.c
> +++ b/arch/x86/kernel/setup.c
> @@ -22,6 +22,7 @@
>  #include <linux/random.h>
>  #include <linux/root_dev.h>
>  #include <linux/static_call.h>
> +#include <linux/sysfb.h>
>  #include <linux/swiotlb.h>
>  #include <linux/tboot.h>
>  #include <linux/usb/xhci-dbgp.h>
> @@ -211,8 +212,9 @@ arch_initcall(init_x86_sysctl);
>  /*
>   * Setup options
>   */
> -struct screen_info screen_info;
> -EXPORT_SYMBOL(screen_info);
> +
> +struct sysfb_display_info sysfb_primary_display;
> +EXPORT_SYMBOL(sysfb_primary_display);
>  #if defined(CONFIG_FIRMWARE_EDID)
>  struct edid_info edid_info;
>  EXPORT_SYMBOL_GPL(edid_info);
> @@ -526,7 +528,7 @@ static void __init parse_setup_data(void)
>  static void __init parse_boot_params(void)
>  {
>  	ROOT_DEV = old_decode_dev(boot_params.hdr.root_dev);
> -	screen_info = boot_params.screen_info;
> +	sysfb_primary_display.screen = boot_params.screen_info;
>  #if defined(CONFIG_FIRMWARE_EDID)
>  	edid_info = boot_params.edid_info;
>  #endif
> @@ -1254,7 +1256,7 @@ void __init setup_arch(char **cmdline_p)
>  #ifdef CONFIG_VT
>  #if defined(CONFIG_VGA_CONSOLE)
>  	if (!efi_enabled(EFI_BOOT) || (efi_mem_type(0xa0000) != EFI_CONVENTIONAL_MEMORY))
> -		vgacon_register_screen(&screen_info);
> +		vgacon_register_screen(&sysfb_primary_display.screen);
>  #endif
>  #endif
>  	x86_init.oem.banner();
> diff --git a/arch/x86/video/video-common.c b/arch/x86/video/video-common.c
> index e0aeee99bc99..152789f00fcd 100644
> --- a/arch/x86/video/video-common.c
> +++ b/arch/x86/video/video-common.c
> @@ -9,7 +9,7 @@
>  
>  #include <linux/module.h>
>  #include <linux/pci.h>
> -#include <linux/screen_info.h>
> +#include <linux/sysfb.h>
>  #include <linux/vgaarb.h>
>  
>  #include <asm/video.h>
> @@ -29,7 +29,7 @@ EXPORT_SYMBOL(pgprot_framebuffer);
>  bool video_is_primary_device(struct device *dev)
>  {
>  #ifdef CONFIG_SCREEN_INFO
> -	struct screen_info *si = &screen_info;
> +	struct screen_info *si = &sysfb_primary_display.screen;
>  	struct resource res[SCREEN_INFO_MAX_RESOURCES];
>  	ssize_t i, numres;
>  #endif
> diff --git a/drivers/firmware/efi/earlycon.c b/drivers/firmware/efi/earlycon.c
> index 42e3a173dac1..3d060d59968c 100644
> --- a/drivers/firmware/efi/earlycon.c
> +++ b/drivers/firmware/efi/earlycon.c
> @@ -9,7 +9,7 @@
>  #include <linux/io.h>
>  #include <linux/kernel.h>
>  #include <linux/serial_core.h>
> -#include <linux/screen_info.h>
> +#include <linux/sysfb.h>
>  #include <linux/string.h>
>  
>  #include <asm/early_ioremap.h>
> @@ -32,7 +32,7 @@ static void *efi_fb;
>   */
>  static int __init efi_earlycon_remap_fb(void)
>  {
> -	const struct screen_info *si = &screen_info;
> +	const struct screen_info *si = &sysfb_primary_display.screen;
>  
>  	/* bail if there is no bootconsole or it was unregistered already */
>  	if (!earlycon_console || !console_is_registered(earlycon_console))
> @@ -147,7 +147,7 @@ static void efi_earlycon_write_char(u32 *dst, unsigned char c, unsigned int h,
>  static void
>  efi_earlycon_write(struct console *con, const char *str, unsigned int num)
>  {
> -	const struct screen_info *si = &screen_info;
> +	const struct screen_info *si = &sysfb_primary_display.screen;
>  	u32 cur_efi_x = efi_x;
>  	unsigned int len;
>  	const char *s;
> @@ -227,7 +227,7 @@ void __init efi_earlycon_reprobe(void)
>  static int __init efi_earlycon_setup(struct earlycon_device *device,
>  				     const char *opt)
>  {
> -	const struct screen_info *si = &screen_info;
> +	const struct screen_info *si = &sysfb_primary_display.screen;
>  	u16 xres, yres;
>  	u32 i;
>  
> diff --git a/drivers/firmware/efi/efi-init.c b/drivers/firmware/efi/efi-init.c
> index a65c2d5b9e7b..d1d418a34407 100644
> --- a/drivers/firmware/efi/efi-init.c
> +++ b/drivers/firmware/efi/efi-init.c
> @@ -19,7 +19,7 @@
>  #include <linux/of_address.h>
>  #include <linux/of_fdt.h>
>  #include <linux/platform_device.h>
> -#include <linux/screen_info.h>
> +#include <linux/sysfb.h>
>  
>  #include <asm/efi.h>
>  
> @@ -57,15 +57,15 @@ static phys_addr_t __init efi_to_phys(unsigned long addr)
>  extern __weak const efi_config_table_type_t efi_arch_tables[];
>  
>  /*
> - * x86 defines its own screen_info and uses it even without EFI,
> - * everything else can get it from here.
> + * x86 defines its own instance of sysfb_primary_display and uses
> + * it even without EFI, everything else can get them from here.
>   */
>  #if !defined(CONFIG_X86) && (defined(CONFIG_SYSFB) || defined(CONFIG_EFI_EARLYCON))
> -struct screen_info screen_info __section(".data");
> -EXPORT_SYMBOL_GPL(screen_info);
> +struct sysfb_display_info sysfb_primary_display __section(".data");
> +EXPORT_SYMBOL_GPL(sysfb_primary_display);
>  #endif
>  
> -static void __init init_screen_info(void)
> +static void __init init_primary_display(void)
>  {
>  	struct screen_info *si;
>  
> @@ -75,13 +75,13 @@ static void __init init_screen_info(void)
>  			pr_err("Could not map screen_info config table\n");
>  			return;
>  		}
> -		screen_info = *si;
> +		sysfb_primary_display.screen = *si;
>  		memset(si, 0, sizeof(*si));
>  		early_memunmap(si, sizeof(*si));
>  
> -		if (memblock_is_map_memory(screen_info.lfb_base))
> -			memblock_mark_nomap(screen_info.lfb_base,
> -					    screen_info.lfb_size);
> +		if (memblock_is_map_memory(sysfb_primary_display.screen.lfb_base))
> +			memblock_mark_nomap(sysfb_primary_display.screen.lfb_base,
> +					    sysfb_primary_display.screen.lfb_size);
>  
>  		if (IS_ENABLED(CONFIG_EFI_EARLYCON))
>  			efi_earlycon_reprobe();
> @@ -274,5 +274,5 @@ void __init efi_init(void)
>  	if (IS_ENABLED(CONFIG_X86) ||
>  	    IS_ENABLED(CONFIG_SYSFB) ||
>  	    IS_ENABLED(CONFIG_EFI_EARLYCON))
> -		init_screen_info();
> +		init_primary_display();
>  }
> diff --git a/drivers/firmware/efi/libstub/efi-stub-entry.c b/drivers/firmware/efi/libstub/efi-stub-entry.c
> index a6c049835190..401ecbbdf331 100644
> --- a/drivers/firmware/efi/libstub/efi-stub-entry.c
> +++ b/drivers/firmware/efi/libstub/efi-stub-entry.c
> @@ -1,13 +1,18 @@
>  // SPDX-License-Identifier: GPL-2.0-only
>  
>  #include <linux/efi.h>
> -#include <linux/screen_info.h>
> +#include <linux/sysfb.h>
>  
>  #include <asm/efi.h>
>  
>  #include "efistub.h"
>  
> -static unsigned long screen_info_offset;
> +static unsigned long kernel_image_offset;
> +
> +static void *kernel_image_addr(void *addr)
> +{
> +	return addr + kernel_image_offset;
> +}
>  
>  struct screen_info *alloc_screen_info(void)
>  {
> @@ -16,8 +21,11 @@ struct screen_info *alloc_screen_info(void)
>  
>  	if (IS_ENABLED(CONFIG_X86) ||
>  	    IS_ENABLED(CONFIG_EFI_EARLYCON) ||
> -	    IS_ENABLED(CONFIG_SYSFB))
> -		return (void *)&screen_info + screen_info_offset;
> +	    IS_ENABLED(CONFIG_SYSFB)) {
> +		struct sysfb_display_info *dpy = kernel_image_addr(&sysfb_primary_display);
> +
> +		return &dpy->screen;
> +	}
>  
>  	return NULL;
>  }
> @@ -73,7 +81,7 @@ efi_status_t __efiapi efi_pe_entry(efi_handle_t handle,
>  		return status;
>  	}
>  
> -	screen_info_offset = image_addr - (unsigned long)image->image_base;
> +	kernel_image_offset = image_addr - (unsigned long)image->image_base;
>  
>  	status = efi_stub_common(handle, image, image_addr, cmdline_ptr);
>  
> diff --git a/drivers/firmware/efi/sysfb_efi.c b/drivers/firmware/efi/sysfb_efi.c
> index 8e0f9d08397f..46ad95084b50 100644
> --- a/drivers/firmware/efi/sysfb_efi.c
> +++ b/drivers/firmware/efi/sysfb_efi.c
> @@ -176,7 +176,7 @@ static int __init efifb_set_system(struct screen_info *si, const struct dmi_syst
>  
>  static int __init efifb_set_system_callback(const struct dmi_system_id *id)
>  {
> -	return efifb_set_system(&screen_info, id);
> +	return efifb_set_system(&sysfb_primary_display.screen, id);
>  }
>  
>  #define EFIFB_DMI_SYSTEM_ID(vendor, name, enumid)		\
> @@ -316,7 +316,7 @@ static struct device_node *find_pci_overlap_node(void)
>  		}
>  
>  		for_each_of_pci_range(&parser, &range)
> -			if (efifb_overlaps_pci_range(&screen_info, &range))
> +			if (efifb_overlaps_pci_range(&sysfb_primary_display.screen, &range))
>  				return np;
>  	}
>  	return NULL;
> diff --git a/drivers/firmware/sysfb.c b/drivers/firmware/sysfb.c
> index 916b28538a29..1f671f9219b0 100644
> --- a/drivers/firmware/sysfb.c
> +++ b/drivers/firmware/sysfb.c
> @@ -66,7 +66,7 @@ static bool sysfb_unregister(void)
>   */
>  void sysfb_disable(struct device *dev)
>  {
> -	struct screen_info *si = &screen_info;
> +	struct screen_info *si = &sysfb_primary_display.screen;
>  	struct device *parent;
>  
>  	mutex_lock(&disable_lock);
> @@ -92,7 +92,7 @@ EXPORT_SYMBOL_GPL(sysfb_disable);
>   */
>  bool sysfb_handles_screen_info(void)
>  {
> -	const struct screen_info *si = &screen_info;
> +	const struct screen_info *si = &sysfb_primary_display.screen;
>  
>  	return !!screen_info_video_type(si);
>  }
> @@ -141,7 +141,7 @@ static struct device *sysfb_parent_dev(const struct screen_info *si)
>  
>  static __init int sysfb_init(void)
>  {
> -	struct screen_info *si = &screen_info;
> +	struct screen_info *si = &sysfb_primary_display.screen;
>  	struct device *parent;
>  	unsigned int type;
>  	struct simplefb_platform_data mode;
> diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c
> index a53af6fe81a6..9c937190be81 100644
> --- a/drivers/hv/vmbus_drv.c
> +++ b/drivers/hv/vmbus_drv.c
> @@ -29,7 +29,7 @@
>  #include <linux/delay.h>
>  #include <linux/panic_notifier.h>
>  #include <linux/ptrace.h>
> -#include <linux/screen_info.h>
> +#include <linux/sysfb.h>
>  #include <linux/efi.h>
>  #include <linux/random.h>
>  #include <linux/kernel.h>
> @@ -2340,8 +2340,8 @@ static void __maybe_unused vmbus_reserve_fb(void)
>  	if (efi_enabled(EFI_BOOT)) {
>  		/* Gen2 VM: get FB base from EFI framebuffer */
>  		if (IS_ENABLED(CONFIG_SYSFB)) {
> -			start = screen_info.lfb_base;
> -			size = max_t(__u32, screen_info.lfb_size, 0x800000);
> +			start = sysfb_primary_display.screen.lfb_base;
> +			size = max_t(__u32, sysfb_primary_display.screen.lfb_size, 0x800000);
>  		}
>  	} else {
>  		/* Gen1 VM: get FB base from PCI */
> diff --git a/drivers/pci/vgaarb.c b/drivers/pci/vgaarb.c
> index 436fa7f4c387..805be9ea4a34 100644
> --- a/drivers/pci/vgaarb.c
> +++ b/drivers/pci/vgaarb.c
> @@ -26,7 +26,7 @@
>  #include <linux/poll.h>
>  #include <linux/miscdevice.h>
>  #include <linux/slab.h>
> -#include <linux/screen_info.h>
> +#include <linux/sysfb.h>
>  #include <linux/vt.h>
>  #include <linux/console.h>
>  #include <linux/acpi.h>
> @@ -557,7 +557,7 @@ EXPORT_SYMBOL(vga_put);
>  static bool vga_is_firmware_default(struct pci_dev *pdev)
>  {
>  #if defined CONFIG_X86
> -	return pdev == screen_info_pci_dev(&screen_info);
> +	return pdev == screen_info_pci_dev(&sysfb_primary_display.screen);
>  #else
>  	return false;
>  #endif
> diff --git a/drivers/video/screen_info_pci.c b/drivers/video/screen_info_pci.c
> index 66bfc1d0a6dc..8f34d8a74f09 100644
> --- a/drivers/video/screen_info_pci.c
> +++ b/drivers/video/screen_info_pci.c
> @@ -4,6 +4,7 @@
>  #include <linux/printk.h>
>  #include <linux/screen_info.h>
>  #include <linux/string.h>
> +#include <linux/sysfb.h>
>  
>  static struct pci_dev *screen_info_lfb_pdev;
>  static size_t screen_info_lfb_bar;
> @@ -26,7 +27,7 @@ static bool __screen_info_relocation_is_valid(const struct screen_info *si, stru
>  
>  void screen_info_apply_fixups(void)
>  {
> -	struct screen_info *si = &screen_info;
> +	struct screen_info *si = &sysfb_primary_display.screen;
>  
>  	if (screen_info_lfb_pdev) {
>  		struct resource *pr = &screen_info_lfb_pdev->resource[screen_info_lfb_bar];
> @@ -75,7 +76,7 @@ static void screen_info_fixup_lfb(struct pci_dev *pdev)
>  		.flags = IORESOURCE_MEM,
>  	};
>  	const struct resource *pr;
> -	const struct screen_info *si = &screen_info;
> +	const struct screen_info *si = &sysfb_primary_display.screen;
>  
>  	if (screen_info_lfb_pdev)
>  		return; // already found
> diff --git a/include/linux/screen_info.h b/include/linux/screen_info.h
> index 1690706206e8..c022403c599a 100644
> --- a/include/linux/screen_info.h
> +++ b/include/linux/screen_info.h
> @@ -151,6 +151,4 @@ static inline struct pci_dev *screen_info_pci_dev(const struct screen_info *si)
>  }
>  #endif
>  
> -extern struct screen_info screen_info;
> -
>  #endif /* _SCREEN_INFO_H */
> diff --git a/include/linux/sysfb.h b/include/linux/sysfb.h
> index 8b37247528bf..e8bde392c690 100644
> --- a/include/linux/sysfb.h
> +++ b/include/linux/sysfb.h
> @@ -8,11 +8,10 @@
>   */
>  
>  #include <linux/err.h>
> +#include <linux/platform_data/simplefb.h>
>  #include <linux/screen_info.h>
>  #include <linux/types.h>
>  
> -#include <linux/platform_data/simplefb.h>
> -
>  struct device;
>  struct platform_device;
>  struct screen_info;
> @@ -65,6 +64,8 @@ struct sysfb_display_info {
>  	struct screen_info screen;
>  };
>  
> +extern struct sysfb_display_info sysfb_primary_display;
> +
>  #ifdef CONFIG_SYSFB
>  
>  void sysfb_disable(struct device *dev);
> -- 
> 2.51.1
> 

^ permalink raw reply

* Re: [PATCH net-next] net: hyperv: convert to use .get_rx_ring_count
From: patchwork-bot+netdevbpf @ 2025-11-25  3:50 UTC (permalink / raw)
  To: Breno Leitao
  Cc: kys, haiyangz, wei.liu, decui, andrew+netdev, davem, edumazet,
	kuba, pabeni, linux-hyperv, netdev, linux-kernel, kernel-team
In-Reply-To: <20251121-hyperv_gxrings-v1-1-31293104953b@debian.org>

Hello:

This patch was applied to netdev/net-next.git (main)
by Jakub Kicinski <kuba@kernel.org>:

On Fri, 21 Nov 2025 01:59:23 -0800 you wrote:
> Convert the hyperv netvsc driver to use the new .get_rx_ring_count
> ethtool operation instead of implementing .get_rxnfc solely for handling
> ETHTOOL_GRXRINGS command. This simplifies the code by replacing the
> switch statement with a direct return of the queue count.
> 
> The new callback provides the same functionality in a more direct way,
> following the ongoing ethtool API modernization.
> 
> [...]

Here is the summary with links:
  - [net-next] net: hyperv: convert to use .get_rx_ring_count
    https://git.kernel.org/netdev/net-next/c/a8ff4842da50

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply

* Re: [PATCH v2 08/10] efi: Support EDID information
From: Thomas Zimmermann @ 2025-11-25  7:19 UTC (permalink / raw)
  To: Ard Biesheuvel
  Cc: javierm, arnd, richard.lyu, x86, linux-arm-kernel, linux-kernel,
	linux-efi, loongarch, linux-riscv, dri-devel, linux-hyperv,
	linux-pci, linux-fbdev
In-Reply-To: <CAMj1kXFtsneE3dFgUx6Hd=iBhD8YpvjfTSi-KZpuNaXfX07KyA@mail.gmail.com>

Hi

Am 24.11.25 um 18:04 schrieb Ard Biesheuvel:
> On Mon, 24 Nov 2025 at 18:01, Ard Biesheuvel <ardb@kernel.org> wrote:
>> On Mon, 24 Nov 2025 at 17:52, Thomas Zimmermann <tzimmermann@suse.de> wrote:
>>> Add LINUX_EFI_PRIMARY_DISPLAY_TABLE_GUID to the list of config-table
>>> UUIDs. Read sysfb_primary_display from the entry. The UUID has been
>>> generated with uuidgen.
>>>
>>> Still support LINUX_EFI_SCREEN_INFO_TABLE_GUID as fallback in case an
>>> older boot loader invokes the kernel.
>>>
>>> If CONFIG_FIRMWARE_EDID=n, EDID information is disabled.
>>>
>>> Make the Kconfig symbol CONFIG_FIRMWARE_EDID available with EFI. Setting
>>> the value to 'n' disables EDID support.
>>>
>>> Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
>> Why are we adding a new config table again?
>>
>>
> Note that LINUX_EFI_SCREEN_INFO_TABLE_GUID is internal ABI only
> between the EFI stub and the core kernel.

Ah, ok. That's my misconception. I was thinking that we have to support 
external boot loaders building a config table.

I'll just extend the existing UUID then.

Best regards
Thomas


-- 
--
Thomas Zimmermann
Graphics Driver Developer
SUSE Software Solutions Germany GmbH
Frankenstr. 146, 90461 Nürnberg, Germany, www.suse.com
GF: Jochen Jaser, Andrew McDonald, Werner Knoblich, (HRB 36809, AG Nürnberg)



^ permalink raw reply

* Re: [PATCH net-next v11 03/13] vsock: reject bad VSOCK_NET_MODE_LOCAL configuration for G2H
From: Stefano Garzarella @ 2025-11-25  9:24 UTC (permalink / raw)
  To: Bobby Eshleman
  Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Stefan Hajnoczi, Michael S. Tsirkin, Jason Wang,
	Eugenio Pérez, Xuan Zhuo, K. Y. Srinivasan, Haiyang Zhang,
	Wei Liu, Dexuan Cui, Bryan Tan, Vishnu Dasa,
	Broadcom internal kernel review list, Shuah Khan, linux-kernel,
	virtualization, netdev, kvm, linux-hyperv, linux-kselftest,
	berrange, Sargun Dhillon, Bobby Eshleman
In-Reply-To: <aSSjNLrRmaOLkuBN@devvm11784.nha0.facebook.com>

On Mon, Nov 24, 2025 at 10:25:56AM -0800, Bobby Eshleman wrote:
>On Mon, Nov 24, 2025 at 06:54:45PM +0100, Stefano Garzarella wrote:
>> On Mon, Nov 24, 2025 at 09:29:05AM -0800, Bobby Eshleman wrote:
>> > On Mon, Nov 24, 2025 at 11:10:19AM +0100, Stefano Garzarella wrote:
>> > > On Fri, Nov 21, 2025 at 11:01:53AM -0800, Bobby Eshleman wrote:
>> > > > On Fri, Nov 21, 2025 at 03:24:25PM +0100, Stefano Garzarella wrote:
>> > > > > On Thu, Nov 20, 2025 at 09:44:35PM -0800, Bobby Eshleman wrote:
>>
>> [...]
>>
>> > > >
>> > > > > Since I guess we need another version of this patch, can you check the
>> > > > > commit description to see if it reflects what we are doing now
>> > > > > (e.g vhost is not enabled)?
>> > > > >
>> > > > > Also I don't understand why for vhost we will enable it later, but for
>> > > > > virtio_transport and vsock_loopback we are enabling it now, also if this
>> > > > > patch is before the support on that transports. I'm a bit confused.
>> > > > >
>> > > > > If something is unclear, let's discuss it before sending a new version.
>> > > > >
>> > > > >
>> > > > > What I had in mind was, add this patch and explain why we need this new
>> > > > > callback (like you did), but enable the support in the patches that
>> > > > > really enable it for any transport. But maybe what is not clear to me is
>> > > > > that we need this only for G2H. But now I'm confused about the discussion
>> > > > > around vmci H2G. We decided to discard also that one, but here we are not
>> > > > > checking that?
>> > > > > I mean here we are calling supports_local_mode() only on G2H IIUC.
>> > > >
>> > > > Ah right, VMCI broke my original mental model of only needing this check
>> > > > for G2H (originally I didn't realize VMCI was H2G too).
>> > > >
>> > > > I think now, we actually need to do this check for all of the transports
>> > > > no? Including h2g, g2h, local, and dgram?
>> > > >
>> > > > Additionally, the commit description needs to be updated to reflect that.
>> > >
>> > > Let's take a step back, though, because I tried to understand the problem
>> > > better and I'm confused.
>> > >
>> > > For example, in vmci (G2H side), when a packet arrives, we always use
>> > > vsock_find_connected_socket(), which only searches in GLOBAL. So connections
>> > > originating from the host can only reach global sockets in the guest. In
>> > > this direction (host -> guest), we should be fine, right?
>> > >
>> > > Now let's consider the other direction, from guest to host, so the
>> > > connection should be generated via vsock_connect().
>> > > Here I see that we are not doing anything with regard to the source
>> > > namespace. At this point, my question is whether we should modify
>> > > vsock_assign_transport() or transport->stream_allow() to do this for each
>> > > stream, and not prevent loading a G2H module a priori.
>> > >
>> > > For example, stream_allow() could check that the socket namespace is
>> > > supported by the assigned transport. E.g., vmci can check that if the
>> > > namespace mode is not GLOBAL, then it returns false. (Same thing in
>> > > virtio-vsock, I mean the G2H driver).
>> > >
>> > > This should solve the guest -> host direction, but at this point I wonder if
>> > > I'm missing something.
>> >
>> > For the G2H connect case that is true, but the situation gets a little
>> > fuzzier on the G2H RX side w/ VMADDR_CID_ANY listeners.
>> >
>> > Let's say we have a nested system w/ both virtio-vsock and vhost-vsock.
>> > We have a listener in namespace local on VMADDR_CID_ANY. So far, no
>> > transport is assigned, so we can't call t->stream_allow() yet.
>> > virtio-vsock only knows of global mode, so its lookup will fail (unless
>>
>> What is the problem of failing in this case?
>> I mean, we are documenting that G2H will not be able to reach socket in
>> namespaces with "local" mode. Old (and default) behaviour is still allowing
>> them, right?
>>
>> I don't think it conflicts with the definition of “local” either, because
>> these connections are coming from outside, and the user doesn't expect to be
>> able to receive them in a “local” namespace, unless there is a way to put
>> the device in the namespace (as with net). But this method doesn't exist
>> yet, and by documenting it sufficiently, we can say that it will be
>> supported in the future, but not for now.
>>
>> > we hack in some special case to virtio_transport_recv_pkt() to scan
>> > local namespaces). vhost-vsock will work as expected. Letting local mode
>> > sockets be silently unreachable by virtio-vsock seems potentially
>> > confusing for users. If the system were not nested, we can pre-resolve
>> > VMADDR_CID_ANY in bind() and handle things upfront as well. Rejecting
>> > local mode outright is just a broad guardrail.
>>
>> Okay, but in that case, we are not supporting “local” mode too, but we are
>> also preventing “global” from being used on these when we are in a nested
>> environment. What is the advantage of this approach?
>>
>> >
>> > If we're trying to find a less heavy-handed option, we might be able to
>> > do the following:
>> >
>> > - change bind(cid) w/ cid != VMADDR_CID_ANY to directly assign the
>> > transport
>> >  for all socket types (not just SOCK_DGRAM)
>>
>> That would be nice, but it wouldn't solve the problem with VMADDR_CID_ANY,
>> which I guess is the use case in 99% of cases.
>>
>> >
>> > - vsock_assign_transport() can outright fail if !t->supports_local_mode()
>> >  and sock_net(sk) has mode local
>>
>> But in this case, why not reusing stream_allow() ?
>>
>> >
>> > - bind(VMADDR_CID_ANY) can maybe print (once) to dmesg a warning that
>> >  only the H2G transport will land on VMADDR_CID_ANY sockets.
>>
>> mmm, I'm not sure about that, we should ask net maintainer, but IMO
>> documenting that in af_vsock.c and man pages should be fine, till G2H will
>> support that.
>>
>> >
>> > I'm certainly open to other suggestions.
>>
>> IMO we should avoid the failure when loading G2H, which is more confusing
>> than just discard connection from the host to a "local" namespace. We should
>> try at least to support the "global" namespace.
>>
>> Thanks,
>> Stefano
>
>
>I'm 100% fine with that approach. I just wanted to make sure we landed
>in the right place for how users may encounter places that there is no
>local mode support.

Yeah, I see, thanks for that!

>
>So for next steps, we can drop this patch and add explicit logic in
>->stream_allow() to allow local mode for vhost/loopback and reject for
>others?

Yep, I would add the logic in the "vsock: add netns to vsock core" 
patch, including the changes to stream_allow(), supporting in all 
transports only the global mode. In the next patches we can support 
`local` mode in related transports (I guess for now just loopback and 
vhost-vsock).

> Plus, add documentation about what happens for VMADDR_CID_ANY
>(will only receive vhost/loopback traffic in local mode)?

I'd document that in af_vsock.c when we talk about "local". I'll make it 
clear that not all transports support it, and we can mention that 
example.

When we will merge this series, we should also send a patch to the 
vsock(7) manpage [1] to describe namespace support because I guess that 
will be the entry point of the user.

Thanks,
Stefano

[1] 
https://git.kernel.org/pub/scm/docs/man-pages/man-pages.git/tree/man/man7/vsock.7


^ permalink raw reply

* Re: [Patch net-next v3] net: mana: Handle hardware recovery events when probing the device
From: Simon Horman @ 2025-11-25 10:51 UTC (permalink / raw)
  To: longli
  Cc: K. Y. Srinivasan, Haiyang Zhang, Wei Liu, Dexuan Cui,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Shradha Gupta, Konstantin Taranov, Souradeep Chakrabarti,
	Erick Archer, linux-hyperv, netdev, linux-kernel, linux-rdma,
	Long Li
In-Reply-To: <1763680033-5835-1-git-send-email-longli@linux.microsoft.com>

On Thu, Nov 20, 2025 at 03:07:13PM -0800, longli@linux.microsoft.com wrote:
> From: Long Li <longli@microsoft.com>
> 
> When MANA is being probed, it's possible that hardware is in recovery
> mode and the device may get GDMA_EQE_HWC_RESET_REQUEST over HWC in the
> middle of the probe. Detect such condition and go through the recovery
> service procedure.
> 
> Fixes: fbe346ce9d62 ("net: mana: Handle Reset Request from MANA NIC")

If this is a fix, should it be targeted at net rather than net-next?

Alternatively, if it is not a fix for net, then I suggest dropping
the Fixes tag and adding something about commit fbe346ce9d62 ("net: mana:
Handle Reset Request from MANA NIC") to the patch description, above the
tags (with a blank line in between).

> Signed-off-by: Long Li <longli@microsoft.com>
> Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
> ---
> Changes
> v2: Use a list for handling multiple devices.
>     Use disable_delayed_work_sync() on driver exit.
>     Replace atomic_t with flags to detect if interrupt happens before probe finishes
> 
> v3: Rebase to latest net-next. Change list_for_each_entry_safe() to while(!list_empty()).

Code changes look good to me.

Reviewed-by: Simon Horman <horms@kernel.org>


^ permalink raw reply

* [PATCH 0/3] MSHV intercepts support on arm64
From: Anirudh Raybharam @ 2025-11-25 17:01 UTC (permalink / raw)
  To: kys, haiyangz, wei.liu, decui, longli, catalin.marinas, will, maz,
	tglx, Arnd Bergmann, akpm, anirudh, agordeev, guoweikang.kernel,
	osandov, bsz, linux-hyperv, linux-arm-kernel, linux-kernel,
	linux-arch

From: Anirudh Rayabharam <anirudh@anirudhrb.com>

To receive hypervisor intercept interrupts on arm64 under MSHV, the parent
partition must allocate an interrupt in the SGI or PPI range and program it
into the SYNIC. Currently, Linux provides no mechanism for drivers to
dynamically allocate either SGIs or PPIs. SGIs are allocated exclusively by
the GIC driver for use by the SMP subsystem as IPIs, while PPIs must be
described in firmware (DT or ACPI), leaving the OS unable to allocate new
ones at runtime.

This series introduces support for MSHV by extending the GICv3 driver to
reserve one additional SGI specifically for use by the MSHV driver. The
reserved SGI is then used by the MSHV driver to program the SYNIC and
receive hypervisor intercept notifications.

This mechanism allows the MSHV driver to function correctly on arm64
without requiring firmware changes or altering the semantics of PPIs, while
keeping the SGI allocation model self-contained within the GIC driver.

Anirudh Rayabharam (Microsoft) (3):
  arm64: hyperv: move hyperv detection earlier in boot
  irqchip/gic-v3: allocate one SGI for MSHV
  mshv: add support for VMEXIT interrupts on aarch64

 arch/arm64/hyperv/mshyperv.c      | 31 +++++++++++++---
 arch/arm64/include/asm/mshyperv.h | 10 ++++++
 arch/arm64/kernel/setup.c         |  6 ++++
 drivers/hv/mshv_root_main.c       | 59 +++++++++++++++++++++++++++++++
 drivers/hv/mshv_synic.c           | 15 ++++----
 drivers/irqchip/irq-gic-v3.c      | 29 +++++++++++++--
 include/asm-generic/mshyperv.h    |  3 ++
 7 files changed, 139 insertions(+), 14 deletions(-)

-- 
2.34.1


^ permalink raw reply

* [PATCH 1/3] arm64: hyperv: move hyperv detection earlier in boot
From: Anirudh Raybharam @ 2025-11-25 17:01 UTC (permalink / raw)
  To: kys, haiyangz, wei.liu, decui, longli, catalin.marinas, will, maz,
	tglx, Arnd Bergmann, akpm, anirudh, agordeev, guoweikang.kernel,
	osandov, bsz, linux-hyperv, linux-arm-kernel, linux-kernel,
	linux-arch
In-Reply-To: <20251125170124.2443340-1-anirudh@anirudhrb.com>

From: Anirudh Rayabharam <anirudh@anirudhrb.com>

From: Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>

Move hyperv detection earlier in the boot. The goal is to detect hyperv
and the type of partition we're running in before the GICv3 setup.

This will be used in the subsequent patches to allocate an SGI for use
by the hyperv subsystem.

Signed-off-by: Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>
---
 arch/arm64/hyperv/mshyperv.c      | 18 ++++++++++++++----
 arch/arm64/include/asm/mshyperv.h |  2 ++
 arch/arm64/kernel/setup.c         |  6 ++++++
 3 files changed, 22 insertions(+), 4 deletions(-)

diff --git a/arch/arm64/hyperv/mshyperv.c b/arch/arm64/hyperv/mshyperv.c
index 4fdc26ade1d7..cc443a5d6c71 100644
--- a/arch/arm64/hyperv/mshyperv.c
+++ b/arch/arm64/hyperv/mshyperv.c
@@ -17,6 +17,7 @@
 #include <linux/cpuhotplug.h>
 #include <asm/mshyperv.h>
 
+static bool hyperv_detected;
 static bool hyperv_initialized;
 
 int hv_get_hypervisor_version(union hv_hypervisor_version_info *info)
@@ -70,20 +71,21 @@ static bool __init hyperv_detect_via_smccc(void)
 	return arm_smccc_hypervisor_has_uuid(&hyperv_uuid);
 }
 
-static int __init hyperv_init(void)
+void __init hyperv_early_init(void)
 {
 	struct hv_get_vp_registers_output	result;
 	u64	guest_id;
-	int	ret;
 
 	/*
 	 * Allow for a kernel built with CONFIG_HYPERV to be running in
 	 * a non-Hyper-V environment.
 	 *
-	 * In such cases, do nothing and return success.
+	 * In such cases, do nothing and return.
 	 */
 	if (!hyperv_detect_via_acpi() && !hyperv_detect_via_smccc())
-		return 0;
+		return;
+
+	hyperv_detected = true;
 
 	/* Setup the guest ID */
 	guest_id = hv_generate_guest_id(LINUX_VERSION_CODE);
@@ -103,6 +105,14 @@ static int __init hyperv_init(void)
 		ms_hyperv.misc_features);
 
 	hv_identify_partition_type();
+}
+
+static int __init hyperv_init(void)
+{
+	int	ret;
+
+	if (!hyperv_detected)
+		return 0;
 
 	ret = hv_common_init();
 	if (ret)
diff --git a/arch/arm64/include/asm/mshyperv.h b/arch/arm64/include/asm/mshyperv.h
index b721d3134ab6..58fde70c2e39 100644
--- a/arch/arm64/include/asm/mshyperv.h
+++ b/arch/arm64/include/asm/mshyperv.h
@@ -53,6 +53,8 @@ static inline u64 hv_get_non_nested_msr(unsigned int reg)
 	return hv_get_msr(reg);
 }
 
+void hyperv_early_init(void);
+
 /* SMCCC hypercall parameters */
 #define HV_SMCCC_FUNC_NUMBER	1
 #define HV_FUNC_ID	ARM_SMCCC_CALL_VAL(			\
diff --git a/arch/arm64/kernel/setup.c b/arch/arm64/kernel/setup.c
index 23c05dc7a8f2..eccf5f19da6b 100644
--- a/arch/arm64/kernel/setup.c
+++ b/arch/arm64/kernel/setup.c
@@ -54,6 +54,7 @@
 #include <asm/efi.h>
 #include <asm/xen/hypervisor.h>
 #include <asm/mmu_context.h>
+#include <asm/mshyperv.h>
 
 static int num_standard_resources;
 static struct resource *standard_resources;
@@ -354,6 +355,11 @@ void __init __no_sanitize_address setup_arch(char **cmdline_p)
 	else
 		psci_acpi_init();
 
+	/*
+	 * This must come after psci init since Hyper-V detection uses SMCCC
+	 */
+	hyperv_early_init();
+
 	arm64_rsi_init();
 
 	init_bootcpu_ops();
-- 
2.34.1


^ permalink raw reply related

* [PATCH 2/3] irqchip/gic-v3: allocate one SGI for MSHV
From: Anirudh Raybharam @ 2025-11-25 17:01 UTC (permalink / raw)
  To: kys, haiyangz, wei.liu, decui, longli, catalin.marinas, will, maz,
	tglx, Arnd Bergmann, akpm, anirudh, agordeev, guoweikang.kernel,
	osandov, bsz, linux-hyperv, linux-arm-kernel, linux-kernel,
	linux-arch
In-Reply-To: <20251125170124.2443340-1-anirudh@anirudhrb.com>

From: Anirudh Rayabharam <anirudh@anirudhrb.com>

From: Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>

Currently SGIs are allocated only for the smp subsystem. The MSHV
(Microsoft Hypervisor aka Hyper-V) code also needs an SGI that can be
programmed into the SYNIC to receive intercepts from the hypervisor. The
hypervisor would then assert this SGI whenever there is a guest
VMEXIT.

Allocate one SGI for MSHV use in addition to the SGIs allocated for
IPIs. When running under MSHV, the full SGI range can be used i.e. no
need to reserve SGIs 8-15 for the secure firmware.

Since this SGI is needed only when running as a parent partition (i.e.
we can create guest partitions), check for it before allocating an SGI.

Signed-off-by: Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>
---
 arch/arm64/hyperv/mshyperv.c      | 13 +++++++++++++
 arch/arm64/include/asm/mshyperv.h |  8 ++++++++
 drivers/irqchip/irq-gic-v3.c      | 29 ++++++++++++++++++++++++++---
 3 files changed, 47 insertions(+), 3 deletions(-)

diff --git a/arch/arm64/hyperv/mshyperv.c b/arch/arm64/hyperv/mshyperv.c
index cc443a5d6c71..99690ae9b53f 100644
--- a/arch/arm64/hyperv/mshyperv.c
+++ b/arch/arm64/hyperv/mshyperv.c
@@ -20,6 +20,8 @@
 static bool hyperv_detected;
 static bool hyperv_initialized;
 
+static int mshv_intercept_irq;
+
 int hv_get_hypervisor_version(union hv_hypervisor_version_info *info)
 {
 	hv_get_vpreg_128(HV_REGISTER_HYPERVISOR_VERSION,
@@ -137,6 +139,17 @@ static int __init hyperv_init(void)
 	return 0;
 }
 
+void __init mshv_set_intercept_irq(int irq)
+{
+	mshv_intercept_irq = irq;
+}
+
+int mshv_get_intercept_irq(void)
+{
+	return mshv_intercept_irq;
+}
+EXPORT_SYMBOL_GPL(mshv_get_intercept_irq);
+
 early_initcall(hyperv_init);
 
 bool hv_is_hyperv_initialized(void)
diff --git a/arch/arm64/include/asm/mshyperv.h b/arch/arm64/include/asm/mshyperv.h
index 58fde70c2e39..f3f6e82a9cb6 100644
--- a/arch/arm64/include/asm/mshyperv.h
+++ b/arch/arm64/include/asm/mshyperv.h
@@ -55,6 +55,14 @@ static inline u64 hv_get_non_nested_msr(unsigned int reg)
 
 void hyperv_early_init(void);
 
+#if IS_ENABLED(CONFIG_MSHV_ROOT)
+void mshv_set_intercept_irq(int irq);
+#else
+static inline void mshv_set_intercept_irq(int irq) {}
+#endif
+
+int mshv_get_intercept_irq(void);
+
 /* SMCCC hypercall parameters */
 #define HV_SMCCC_FUNC_NUMBER	1
 #define HV_FUNC_ID	ARM_SMCCC_CALL_VAL(			\
diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c
index 3de351e66ee8..56013dd0564c 100644
--- a/drivers/irqchip/irq-gic-v3.c
+++ b/drivers/irqchip/irq-gic-v3.c
@@ -35,6 +35,7 @@
 #include <asm/exception.h>
 #include <asm/smp_plat.h>
 #include <asm/virt.h>
+#include <asm/mshyperv.h>
 
 #include "irq-gic-common.h"
 
@@ -1456,8 +1457,24 @@ static void __init gic_smp_init(void)
 		.fwnode		= gic_data.fwnode,
 		.param_count	= 1,
 	};
+	/* Register all 8 non-secure SGIs */
+	const int NR_SMP_SGIS = 8;
+	int nr_sgis = NR_SMP_SGIS;
 	int base_sgi;
 
+	/*
+	 * Allocate one more SGI for use by Hyper-V. This is only needed when
+	 * Linux is running in a parent partition. Hyper-V will use this interrupt
+	 * to notify the parent partition of intercepts.
+	 *
+	 * When running on Hyper-V, it is okay to use SGIs 8-15. They're not reserved
+	 * for secure firmware.
+	 */
+#if IS_ENABLED(CONFIG_HYPERV)
+	if (hv_parent_partition())
+		nr_sgis += 1;
+#endif
+
 	cpuhp_setup_state_nocalls(CPUHP_BP_PREPARE_DYN,
 				  "irqchip/arm/gicv3:checkrdist",
 				  gic_check_rdist, NULL);
@@ -1466,12 +1483,18 @@ static void __init gic_smp_init(void)
 				  "irqchip/arm/gicv3:starting",
 				  gic_starting_cpu, NULL);
 
-	/* Register all 8 non-secure SGIs */
-	base_sgi = irq_domain_alloc_irqs(gic_data.domain, 8, NUMA_NO_NODE, &sgi_fwspec);
+	base_sgi = irq_domain_alloc_irqs(gic_data.domain, nr_sgis, NUMA_NO_NODE, &sgi_fwspec);
 	if (WARN_ON(base_sgi <= 0))
 		return;
 
-	set_smp_ipi_range(base_sgi, 8);
+	set_smp_ipi_range(base_sgi, NR_SMP_SGIS);
+
+#if IS_ENABLED(CONFIG_HYPERV)
+	if (hv_parent_partition()) {
+		base_sgi += NR_SMP_SGIS;
+		mshv_set_intercept_irq(base_sgi);
+	}
+#endif
 }
 
 static int gic_set_affinity(struct irq_data *d, const struct cpumask *mask_val,
-- 
2.34.1


^ permalink raw reply related

* [PATCH 3/3] mshv: add support for VMEXIT interrupts on aarch64
From: Anirudh Raybharam @ 2025-11-25 17:01 UTC (permalink / raw)
  To: kys, haiyangz, wei.liu, decui, longli, catalin.marinas, will, maz,
	tglx, Arnd Bergmann, akpm, anirudh, agordeev, guoweikang.kernel,
	osandov, bsz, linux-hyperv, linux-arm-kernel, linux-kernel,
	linux-arch
  Cc: Jinank Jain
In-Reply-To: <20251125170124.2443340-1-anirudh@anirudhrb.com>

From: Anirudh Rayabharam <anirudh@anirudhrb.com>

From: Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>

Use the SGI allocated for MSHV as the interrupt vector to get notified
of hypervisor intercepts.

Currently, HYPERVISOR_CALLBACK_VECTOR is hardcoded for this. This macro
exists only for x86. To make things generic, introduce an arch-specific
init function mshv_arch_parent_partition_init() which, for now, is
responsible for setting up the interception interrupt and writing it to
the mshv_interrupt global. mshv_interrupt is then used when programming
the SYNIC.

Co-developed-by: Jinank Jain <jinankjain@microsoft.com>
Signed-off-by: Jinank Jain <jinankjain@microsoft.com>
Signed-off-by: Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>
---
 drivers/hv/mshv_root_main.c    | 59 ++++++++++++++++++++++++++++++++++
 drivers/hv/mshv_synic.c        | 15 +++++----
 include/asm-generic/mshyperv.h |  3 ++
 3 files changed, 70 insertions(+), 7 deletions(-)

diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
index bc15d6f6922f..e48a89688ecb 100644
--- a/drivers/hv/mshv_root_main.c
+++ b/drivers/hv/mshv_root_main.c
@@ -17,7 +17,9 @@
 #include <linux/file.h>
 #include <linux/anon_inodes.h>
 #include <linux/mm.h>
+#include <linux/interrupt.h>
 #include <linux/io.h>
+#include <linux/irq.h>
 #include <linux/cpuhotplug.h>
 #include <linux/random.h>
 #include <asm/mshyperv.h>
@@ -75,6 +77,11 @@ static vm_fault_t mshv_vp_fault(struct vm_fault *vmf);
 static int mshv_init_async_handler(struct mshv_partition *partition);
 static void mshv_async_hvcall_handler(void *data, u64 *status);
 
+
+int mshv_interrupt = -1;
+int mshv_irq = -1;
+static long __percpu *mshv_evt;
+
 static const union hv_input_vtl input_vtl_zero;
 static const union hv_input_vtl input_vtl_normal = {
 	.target_vtl = HV_NORMAL_VTL,
@@ -2311,6 +2318,47 @@ static void mshv_init_vmm_caps(struct device *dev)
 	dev_dbg(dev, "vmm_caps = %#llx\n", mshv_root.vmm_caps.as_uint64[0]);
 }
 
+#if IS_ENABLED(CONFIG_ARM64)
+static irqreturn_t mshv_percpu_isr(int irq, void *dev_id)
+{
+	mshv_isr();
+	add_interrupt_randomness(irq);
+	return IRQ_HANDLED;
+}
+
+static int mshv_arch_parent_partition_init(struct device *dev)
+{
+	int ret;
+
+	mshv_irq = mshv_get_intercept_irq();
+	mshv_interrupt = irq_get_irq_data(mshv_irq)->hwirq;
+
+	mshv_evt = alloc_percpu(long);
+	if (!mshv_evt) {
+		dev_err(dev, "Failed to allocate percpu event\n");
+		return -ENOMEM;
+	}
+
+	ret = request_percpu_irq(mshv_irq, mshv_percpu_isr, "MSHV", mshv_evt);
+	if (ret) {
+		dev_err(dev, "Failed to request percpu irq\n");
+		goto free_percpu_buf;
+	}
+
+	return ret;
+
+free_percpu_buf:
+	free_percpu(mshv_evt);
+	return ret;
+}
+#elif IS_ENABLED(CONFIG_X86_64)
+static int mshv_arch_parent_partition_init(struct device *dev)
+{
+	mshv_interrupt = HYPERVISOR_CALLBACK_VECTOR;
+	return 0;
+}
+#endif
+
 static int __init mshv_parent_partition_init(void)
 {
 	int ret;
@@ -2329,6 +2377,10 @@ static int __init mshv_parent_partition_init(void)
 
 	dev = mshv_dev.this_device;
 
+	ret = mshv_arch_parent_partition_init(dev);
+	if (ret)
+		return ret;
+
 	if (version_info.build_number < MSHV_HV_MIN_VERSION ||
 	    version_info.build_number > MSHV_HV_MAX_VERSION) {
 		dev_err(dev, "Running on unvalidated Hyper-V version\n");
@@ -2396,6 +2448,13 @@ static void __exit mshv_parent_partition_exit(void)
 	mshv_irqfd_wq_cleanup();
 	if (hv_root_partition())
 		mshv_root_partition_exit();
+	if (mshv_irq >= 0) {
+		if (mshv_evt) {
+			free_percpu_irq(mshv_irq, mshv_evt);
+			free_percpu(mshv_evt);
+			mshv_evt = NULL;
+		}
+	}
 	cpuhp_remove_state(mshv_cpuhp_online);
 	free_percpu(mshv_root.synic_pages);
 }
diff --git a/drivers/hv/mshv_synic.c b/drivers/hv/mshv_synic.c
index f8b0337cdc82..3bdb798f8948 100644
--- a/drivers/hv/mshv_synic.c
+++ b/drivers/hv/mshv_synic.c
@@ -10,6 +10,7 @@
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/mm.h>
+#include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/random.h>
 #include <asm/mshyperv.h>
@@ -451,9 +452,7 @@ int mshv_synic_init(unsigned int cpu)
 	union hv_synic_simp simp;
 	union hv_synic_siefp siefp;
 	union hv_synic_sirbp sirbp;
-#ifdef HYPERVISOR_CALLBACK_VECTOR
 	union hv_synic_sint sint;
-#endif
 	union hv_synic_scontrol sctrl;
 	struct hv_synic_pages *spages = this_cpu_ptr(mshv_root.synic_pages);
 	struct hv_message_page **msg_page = &spages->hyp_synic_message_page;
@@ -496,10 +495,13 @@ int mshv_synic_init(unsigned int cpu)
 
 	hv_set_non_nested_msr(HV_MSR_SIRBP, sirbp.as_uint64);
 
-#ifdef HYPERVISOR_CALLBACK_VECTOR
+
+	if (mshv_irq > 0)
+		enable_percpu_irq(mshv_irq, 0);
+
 	/* Enable intercepts */
 	sint.as_uint64 = 0;
-	sint.vector = HYPERVISOR_CALLBACK_VECTOR;
+	sint.vector = mshv_interrupt;
 	sint.masked = false;
 	sint.auto_eoi = hv_recommend_using_aeoi();
 	hv_set_non_nested_msr(HV_MSR_SINT0 + HV_SYNIC_INTERCEPTION_SINT_INDEX,
@@ -507,13 +509,12 @@ int mshv_synic_init(unsigned int cpu)
 
 	/* Doorbell SINT */
 	sint.as_uint64 = 0;
-	sint.vector = HYPERVISOR_CALLBACK_VECTOR;
+	sint.vector = mshv_interrupt;
 	sint.masked = false;
-	sint.as_intercept = 1;
 	sint.auto_eoi = hv_recommend_using_aeoi();
+	sint.as_intercept = 1;
 	hv_set_non_nested_msr(HV_MSR_SINT0 + HV_SYNIC_DOORBELL_SINT_INDEX,
 			      sint.as_uint64);
-#endif
 
 	/* Enable global synic bit */
 	sctrl.as_uint64 = hv_get_non_nested_msr(HV_MSR_SCONTROL);
diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h
index ecedab554c80..8e30347f7946 100644
--- a/include/asm-generic/mshyperv.h
+++ b/include/asm-generic/mshyperv.h
@@ -189,6 +189,9 @@ void hv_setup_crash_handler(void (*handler)(struct pt_regs *regs));
 void hv_remove_crash_handler(void);
 void hv_setup_mshv_handler(void (*handler)(void));
 
+extern int mshv_interrupt;
+extern int mshv_irq;
+
 #if IS_ENABLED(CONFIG_HYPERV)
 /*
  * Hypervisor's notion of virtual processor ID is different from
-- 
2.34.1


^ permalink raw reply related

* Re: [PATCH 2/3] irqchip/gic-v3: allocate one SGI for MSHV
From: Marc Zyngier @ 2025-11-25 18:01 UTC (permalink / raw)
  To: Anirudh Raybharam
  Cc: kys, haiyangz, wei.liu, decui, longli, catalin.marinas, will,
	tglx, Arnd Bergmann, akpm, agordeev, guoweikang.kernel, osandov,
	bsz, linux-hyperv, linux-arm-kernel, linux-kernel, linux-arch
In-Reply-To: <20251125170124.2443340-3-anirudh@anirudhrb.com>

On Tue, 25 Nov 2025 17:01:23 +0000,
Anirudh Raybharam <anirudh@anirudhrb.com> wrote:
> 
> From: Anirudh Rayabharam <anirudh@anirudhrb.com>
> 
> From: Anirudh Rayabharam (Microsoft) <anirudh@anirudhrb.com>
> 
> Currently SGIs are allocated only for the smp subsystem. The MSHV
> (Microsoft Hypervisor aka Hyper-V) code also needs an SGI that can be
> programmed into the SYNIC to receive intercepts from the hypervisor. The
> hypervisor would then assert this SGI whenever there is a guest
> VMEXIT.
> 
> Allocate one SGI for MSHV use in addition to the SGIs allocated for
> IPIs. When running under MSHV, the full SGI range can be used i.e. no
> need to reserve SGIs 8-15 for the secure firmware.
> 
> Since this SGI is needed only when running as a parent partition (i.e.
> we can create guest partitions), check for it before allocating an SGI.

Sorry, but that's not an acceptable situation.

SGIs are for Linux to use, nobody else, and that allocation must be
the same irrespective of whether Linux runs virtualised or not. This
also won't work with GICv5 (there are no SGIs at all), so this is
doomed from the very start, and would immediately create technical
debt.

If you want to signal an interrupt to Linux, expose a device with an
interrupt in a firmware table (i.e. not an SGI), and use that in your
driver.

Thanks,

	M.

-- 
Without deviation from the norm, progress is not possible.

^ permalink raw reply

* Re: [PATCH 1/3] arm64: hyperv: move hyperv detection earlier in boot
From: kernel test robot @ 2025-11-25 21:19 UTC (permalink / raw)
  To: Anirudh Raybharam, kys, haiyangz, wei.liu, decui, longli,
	catalin.marinas, will, maz, tglx, Arnd Bergmann, akpm, agordeev,
	guoweikang.kernel, osandov, bsz, linux-hyperv, linux-arm-kernel,
	linux-kernel, linux-arch
  Cc: oe-kbuild-all
In-Reply-To: <20251125170124.2443340-2-anirudh@anirudhrb.com>

Hi Anirudh,

kernel test robot noticed the following build errors:

[auto build test ERROR on next-20251125]
[also build test ERROR on v6.18-rc7]
[cannot apply to arm64/for-next/core tip/irq/core arnd-asm-generic/master linus/master v6.18-rc7 v6.18-rc6 v6.18-rc5]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Anirudh-Raybharam/arm64-hyperv-move-hyperv-detection-earlier-in-boot/20251126-011057
base:   next-20251125
patch link:    https://lore.kernel.org/r/20251125170124.2443340-2-anirudh%40anirudhrb.com
patch subject: [PATCH 1/3] arm64: hyperv: move hyperv detection earlier in boot
config: arm64-allnoconfig (https://download.01.org/0day-ci/archive/20251126/202511260546.RGx7vwyX-lkp@intel.com/config)
compiler: aarch64-linux-gcc (GCC) 15.1.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20251126/202511260546.RGx7vwyX-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202511260546.RGx7vwyX-lkp@intel.com/

All errors (new ones prefixed by >>):

   In file included from arch/arm64/include/asm/mshyperv.h:66,
                    from arch/arm64/kernel/setup.c:57:
>> include/asm-generic/mshyperv.h:326:38: error: return type is an incomplete type
     326 | static inline enum hv_isolation_type hv_get_isolation_type(void)
         |                                      ^~~~~~~~~~~~~~~~~~~~~
   include/asm-generic/mshyperv.h: In function 'hv_get_isolation_type':
>> include/asm-generic/mshyperv.h:328:16: error: 'HV_ISOLATION_TYPE_NONE' undeclared (first use in this function)
     328 |         return HV_ISOLATION_TYPE_NONE;
         |                ^~~~~~~~~~~~~~~~~~~~~~
   include/asm-generic/mshyperv.h:328:16: note: each undeclared identifier is reported only once for each function it appears in
>> include/asm-generic/mshyperv.h:328:16: error: 'return' with a value, in function returning void [-Wreturn-mismatch]
   include/asm-generic/mshyperv.h:326:38: note: declared here
     326 | static inline enum hv_isolation_type hv_get_isolation_type(void)
         |                                      ^~~~~~~~~~~~~~~~~~~~~


vim +326 include/asm-generic/mshyperv.h

7ad9bb9d0f357d Wei Liu                  2021-09-10  289  
3817854ba89201 Nuno Das Neves           2025-03-14  290  #define _hv_status_fmt(fmt) "%s: Hyper-V status: %#x = %s: " fmt
3817854ba89201 Nuno Das Neves           2025-03-14  291  #define hv_status_printk(level, status, fmt, ...) \
3817854ba89201 Nuno Das Neves           2025-03-14  292  do { \
3817854ba89201 Nuno Das Neves           2025-03-14  293  	u64 __status = (status); \
3817854ba89201 Nuno Das Neves           2025-03-14  294  	pr_##level(_hv_status_fmt(fmt), __func__, hv_result(__status), \
3817854ba89201 Nuno Das Neves           2025-03-14  295  		   hv_result_to_string(__status), ##__VA_ARGS__); \
3817854ba89201 Nuno Das Neves           2025-03-14  296  } while (0)
3817854ba89201 Nuno Das Neves           2025-03-14  297  #define hv_status_err(status, fmt, ...) \
3817854ba89201 Nuno Das Neves           2025-03-14  298  	hv_status_printk(err, status, fmt, ##__VA_ARGS__)
3817854ba89201 Nuno Das Neves           2025-03-14  299  #define hv_status_debug(status, fmt, ...) \
3817854ba89201 Nuno Das Neves           2025-03-14  300  	hv_status_printk(debug, status, fmt, ##__VA_ARGS__)
3817854ba89201 Nuno Das Neves           2025-03-14  301  
3817854ba89201 Nuno Das Neves           2025-03-14  302  const char *hv_result_to_string(u64 hv_status);
9d8731a1757bef Nuno Das Neves           2025-02-21  303  int hv_result_to_errno(u64 status);
f3a99e761efa61 Tianyu Lan               2020-04-06  304  void hyperv_report_panic(struct pt_regs *regs, long err, bool in_die);
765e33f5211ab6 Michael Kelley           2019-05-30  305  bool hv_is_hyperv_initialized(void);
b96f86534fa310 Dexuan Cui               2019-11-19  306  bool hv_is_hibernation_supported(void);
a6c76bb08dc7f7 Andrea Parri (Microsoft  2021-02-01  307) enum hv_isolation_type hv_get_isolation_type(void);
a6c76bb08dc7f7 Andrea Parri (Microsoft  2021-02-01  308) bool hv_is_isolation_supported(void);
0cc4f6d9f0b9f2 Tianyu Lan               2021-10-25  309  bool hv_isolation_type_snp(void);
20c89a559e00df Tianyu Lan               2021-10-25  310  u64 hv_ghcb_hypercall(u64 control, void *input, void *output, u32 input_size);
d6e0228d265f29 Dexuan Cui               2023-08-24  311  u64 hv_tdx_hypercall(u64 control, u64 param1, u64 param2);
3e1b611515d286 Tianyu Lan               2025-09-18  312  void hv_enable_coco_interrupt(unsigned int cpu, unsigned int vector, bool set);
a156ad8c508209 Roman Kisel              2025-10-08  313  void hv_para_set_sint_proxy(bool enable);
e6eeb3c782739c Roman Kisel              2025-10-08  314  u64 hv_para_get_synic_register(unsigned int reg);
e6eeb3c782739c Roman Kisel              2025-10-08  315  void hv_para_set_synic_register(unsigned int reg, u64 val);
765e33f5211ab6 Michael Kelley           2019-05-30  316  void hyperv_cleanup(void);
6dc2a774cb4fdb Sunil Muthuswamy         2021-03-23  317  bool hv_query_ext_cap(u64 cap_query);
37200078ed6aa2 Michael Kelley           2022-03-24  318  void hv_setup_dma_ops(struct device *dev, bool coherent);
765e33f5211ab6 Michael Kelley           2019-05-30  319  #else /* CONFIG_HYPERV */
db912b8954c23a Nuno Das Neves           2025-02-21  320  static inline void hv_identify_partition_type(void) {}
765e33f5211ab6 Michael Kelley           2019-05-30  321  static inline bool hv_is_hyperv_initialized(void) { return false; }
b96f86534fa310 Dexuan Cui               2019-11-19  322  static inline bool hv_is_hibernation_supported(void) { return false; }
765e33f5211ab6 Michael Kelley           2019-05-30  323  static inline void hyperv_cleanup(void) {}
f2580a907e5c0e Michael Kelley           2024-03-18  324  static inline void ms_hyperv_late_init(void) {}
0cc4f6d9f0b9f2 Tianyu Lan               2021-10-25  325  static inline bool hv_is_isolation_supported(void) { return false; }
0cc4f6d9f0b9f2 Tianyu Lan               2021-10-25 @326  static inline enum hv_isolation_type hv_get_isolation_type(void)
0cc4f6d9f0b9f2 Tianyu Lan               2021-10-25  327  {
0cc4f6d9f0b9f2 Tianyu Lan               2021-10-25 @328  	return HV_ISOLATION_TYPE_NONE;
0cc4f6d9f0b9f2 Tianyu Lan               2021-10-25  329  }
765e33f5211ab6 Michael Kelley           2019-05-30  330  #endif /* CONFIG_HYPERV */
765e33f5211ab6 Michael Kelley           2019-05-30  331  

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply

* RE: [EXTERNAL] Re: [Patch net-next v3] net: mana: Handle hardware recovery events when probing the device
From: Long Li @ 2025-11-26  0:40 UTC (permalink / raw)
  To: Simon Horman, longli@linux.microsoft.com
  Cc: KY Srinivasan, Haiyang Zhang, Wei Liu, Dexuan Cui,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Shradha Gupta, Konstantin Taranov, Souradeep Chakrabarti,
	Erick Archer, linux-hyperv@vger.kernel.org,
	netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-rdma@vger.kernel.org
In-Reply-To: <aSWKLefd_mhycGDv@horms.kernel.org>

> Subject: [EXTERNAL] Re: [Patch net-next v3] net: mana: Handle hardware
> recovery events when probing the device
> 
> On Thu, Nov 20, 2025 at 03:07:13PM -0800, longli@linux.microsoft.com
> wrote:
> > From: Long Li <longli@microsoft.com>
> >
> > When MANA is being probed, it's possible that hardware is in recovery
> > mode and the device may get GDMA_EQE_HWC_RESET_REQUEST over HWC
> in the
> > middle of the probe. Detect such condition and go through the recovery
> > service procedure.
> >
> > Fixes: fbe346ce9d62 ("net: mana: Handle Reset Request from MANA NIC")
> 
> If this is a fix, should it be targeted at net rather than net-next?
> 
> Alternatively, if it is not a fix for net, then I suggest dropping the Fixes tag and
> adding something about commit fbe346ce9d62 ("net: mana:
> Handle Reset Request from MANA NIC") to the patch description, above the
> tags (with a blank line in between).

Can we keep the "Fixes" tag and queue the patch to net-next?

There are other MANA patches pending in net-next and they will conflict with each other when merging.

Thank you,
Long

> 
> > Signed-off-by: Long Li <longli@microsoft.com>
> > Reviewed-by: Haiyang Zhang <haiyangz@microsoft.com>
> > ---
> > Changes
> > v2: Use a list for handling multiple devices.
> >     Use disable_delayed_work_sync() on driver exit.
> >     Replace atomic_t with flags to detect if interrupt happens before
> > probe finishes
> >
> > v3: Rebase to latest net-next. Change list_for_each_entry_safe() to
> while(!list_empty()).
> 
> Code changes look good to me.
> 
> Reviewed-by: Simon Horman <horms@kernel.org>


^ permalink raw reply

* Re: [EXTERNAL] Re: [Patch net-next v3] net: mana: Handle hardware recovery events when probing the device
From: Jakub Kicinski @ 2025-11-26  1:24 UTC (permalink / raw)
  To: Long Li
  Cc: Simon Horman, longli@linux.microsoft.com, KY Srinivasan,
	Haiyang Zhang, Wei Liu, Dexuan Cui, David S. Miller, Eric Dumazet,
	Paolo Abeni, Shradha Gupta, Konstantin Taranov,
	Souradeep Chakrabarti, Erick Archer, linux-hyperv@vger.kernel.org,
	netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-rdma@vger.kernel.org
In-Reply-To: <DS3PR21MB5735CC21A800623D2DB5D144CEDEA@DS3PR21MB5735.namprd21.prod.outlook.com>

On Wed, 26 Nov 2025 00:40:08 +0000 Long Li wrote:
> > tags (with a blank line in between).  
> 
> Can we keep the "Fixes" tag and queue the patch to net-next?

No.

> There are other MANA patches pending in net-next and they will
> conflict with each other when merging.

We merge the trees every Thursday.

^ permalink raw reply

* [PATCH v7 0/7] Introduce movable pages for Hyper-V guests
From: Stanislav Kinsburskii @ 2025-11-26  2:08 UTC (permalink / raw)
  To: kys, haiyangz, wei.liu, decui; +Cc: linux-hyperv, linux-kernel

From the start, the root-partition driver allocates, pins, and maps all
guest memory into the hypervisor at guest creation. This is simple: Linux
cannot move the pages, so the guest’s view in Linux and in Microsoft
Hypervisor never diverges.

However, this approach has major drawbacks:
 - NUMA: affinity can’t be changed at runtime, so you can’t migrate guest memory closer to the CPUs running it → performance hit.
 - Memory management: unused guest memory can’t be swapped out, compacted, or merged.
 - Provisioning time: upfront allocation/pinning slows guest create/destroy.
 - Overcommit: no memory overcommit on hosts with pinned-guest memory.

This series adds movable memory pages for Hyper-V child partitions. Guest
pages are no longer allocated upfront; they’re allocated and mapped into
the hypervisor on demand (i.e., when the guest touches a GFN that isn’t yet
backed by a host PFN).
When a page is moved, Linux no longer holds it and it is unmapped from the hypervisor.
As a result, Hyper-V guests behave like regular Linux processes, enabling standard Linux memory features to apply to guests.

Exceptions (still pinned):
 1. Encrypted guests (explicit).
 2. Guests with passthrough devices (implicitly pinned by the VFIO framework).

v7:
 - Only the first two patches remain unchanged from v6.
 - Introduced reference counting for memory regions to resolve a race
   condition between region servicing (faulting and invalidation) and region
   destruction.
 - Corrected the assumption that regions starting with a huge page contain
   only huge pages; the code now properly handles regions with mixed page
   size segments.
 - Consolidated region management logic into a dedicated file.
 - Updated the driver to select MMU_NOTIFIER, removing support for
   configurations without this option.
 - Cleaned up and refactored the region management code.
 - Fixed a build issue reported by the kernel test robot for configurations
   where HPAGE_PMD_NR is defined to result in build bug.
 - Replaced VALUE_PMD_ALIGNED with the generic IS_ALIGNED macro.
 - Simplified region flags by introducing a region type for clarity.
 - Improved commit messages.

v6:
 - Fix a bug in large page remapping where setting the large map flag based
   on the PFN offset's large page alignment within the region implicitly
   assumed that the region's start offset was also large page aligned,
   which could cause map hypercall failures.
 - Fix a bug in large page unmapping where setting the large unmap flag for
   an unaligned guest PFN range could result in unmap hypercall failures.

v5:
 - Fix a bug in MMU notifier handling where an uninitialized 'ret' variable
   could cause the warning about failed page invalidation to be skipped.
 - Improve comment grammar regarding skipping the unmapping of non-mapped pages.

v4:
 - Fix a bug in batch unmapping can skip mapped pages when selecting a new
   batch due to wrong offset calculation.
 - Fix an error message in case of failed memory region pinning.

v3:
 - Region is invalidated even if the mm has no users.
 - Page remapping logic is updated to support 2M-unaligned remappings for
   regions that are PMD-aligned, which can occur during both faults and
   invalidations.

v2:
 - Split unmap batching into a separate patch.
 - Fixed commit messages from v1 review.
 - Renamed a few functions for clarity.

---

Stanislav Kinsburskii (7):
      Drivers: hv: Refactor and rename memory region handling functions
      Drivers: hv: Centralize guest memory region destruction
      Drivers: hv: Move region management to mshv_regions.c
      Drivers: hv: Fix huge page handling in memory region traversal
      Drivers: hv: Improve region overlap detection in partition create
      Drivers: hv: Add refcount and locking to mem regions
      Drivers: hv: Add support for movable memory regions


 drivers/hv/Kconfig          |    2 
 drivers/hv/Makefile         |    2 
 drivers/hv/mshv_regions.c   |  548 +++++++++++++++++++++++++++++++++++++++++++
 drivers/hv/mshv_root.h      |   32 ++-
 drivers/hv/mshv_root_main.c |  382 +++++++++++++-----------------
 5 files changed, 745 insertions(+), 221 deletions(-)
 create mode 100644 drivers/hv/mshv_regions.c


^ permalink raw reply

* [PATCH v7 1/7] Drivers: hv: Refactor and rename memory region handling functions
From: Stanislav Kinsburskii @ 2025-11-26  2:08 UTC (permalink / raw)
  To: kys, haiyangz, wei.liu, decui; +Cc: linux-hyperv, linux-kernel
In-Reply-To: <176412196000.447063.4256335030026363827.stgit@skinsburskii-cloud-desktop.internal.cloudapp.net>

Simplify and unify memory region management to improve code clarity and
reliability. Consolidate pinning and invalidation logic, adopt consistent
naming, and remove redundant checks to reduce complexity.

Enhance documentation and update call sites for maintainability.

Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
Reviewed-by: Nuno Das Neves <nunodasneves@linux.microsoft.com>
---
 drivers/hv/mshv_root_main.c |   80 +++++++++++++++++++------------------------
 1 file changed, 36 insertions(+), 44 deletions(-)

diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
index bc15d6f6922f..fec82619684a 100644
--- a/drivers/hv/mshv_root_main.c
+++ b/drivers/hv/mshv_root_main.c
@@ -1114,8 +1114,8 @@ mshv_region_map(struct mshv_mem_region *region)
 }
 
 static void
-mshv_region_evict_pages(struct mshv_mem_region *region,
-			u64 page_offset, u64 page_count)
+mshv_region_invalidate_pages(struct mshv_mem_region *region,
+			     u64 page_offset, u64 page_count)
 {
 	if (region->flags.range_pinned)
 		unpin_user_pages(region->pages + page_offset, page_count);
@@ -1125,29 +1125,24 @@ mshv_region_evict_pages(struct mshv_mem_region *region,
 }
 
 static void
-mshv_region_evict(struct mshv_mem_region *region)
+mshv_region_invalidate(struct mshv_mem_region *region)
 {
-	mshv_region_evict_pages(region, 0, region->nr_pages);
+	mshv_region_invalidate_pages(region, 0, region->nr_pages);
 }
 
 static int
-mshv_region_populate_pages(struct mshv_mem_region *region,
-			   u64 page_offset, u64 page_count)
+mshv_region_pin(struct mshv_mem_region *region)
 {
 	u64 done_count, nr_pages;
 	struct page **pages;
 	__u64 userspace_addr;
 	int ret;
 
-	if (page_offset + page_count > region->nr_pages)
-		return -EINVAL;
-
-	for (done_count = 0; done_count < page_count; done_count += ret) {
-		pages = region->pages + page_offset + done_count;
+	for (done_count = 0; done_count < region->nr_pages; done_count += ret) {
+		pages = region->pages + done_count;
 		userspace_addr = region->start_uaddr +
-				(page_offset + done_count) *
-				HV_HYP_PAGE_SIZE;
-		nr_pages = min(page_count - done_count,
+				 done_count * HV_HYP_PAGE_SIZE;
+		nr_pages = min(region->nr_pages - done_count,
 			       MSHV_PIN_PAGES_BATCH_SIZE);
 
 		/*
@@ -1158,34 +1153,23 @@ mshv_region_populate_pages(struct mshv_mem_region *region,
 		 * with the FOLL_LONGTERM flag does a large temporary
 		 * allocation of contiguous memory.
 		 */
-		if (region->flags.range_pinned)
-			ret = pin_user_pages_fast(userspace_addr,
-						  nr_pages,
-						  FOLL_WRITE | FOLL_LONGTERM,
-						  pages);
-		else
-			ret = -EOPNOTSUPP;
-
+		ret = pin_user_pages_fast(userspace_addr, nr_pages,
+					  FOLL_WRITE | FOLL_LONGTERM,
+					  pages);
 		if (ret < 0)
 			goto release_pages;
 	}
 
-	if (PageHuge(region->pages[page_offset]))
+	if (PageHuge(region->pages[0]))
 		region->flags.large_pages = true;
 
 	return 0;
 
 release_pages:
-	mshv_region_evict_pages(region, page_offset, done_count);
+	mshv_region_invalidate_pages(region, 0, done_count);
 	return ret;
 }
 
-static int
-mshv_region_populate(struct mshv_mem_region *region)
-{
-	return mshv_region_populate_pages(region, 0, region->nr_pages);
-}
-
 static struct mshv_mem_region *
 mshv_partition_region_by_gfn(struct mshv_partition *partition, u64 gfn)
 {
@@ -1245,19 +1229,27 @@ static int mshv_partition_create_region(struct mshv_partition *partition,
 	return 0;
 }
 
-/*
- * Map guest ram. if snp, make sure to release that from the host first
- * Side Effects: In case of failure, pages are unpinned when feasible.
+/**
+ * mshv_prepare_pinned_region - Pin and map memory regions
+ * @region: Pointer to the memory region structure
+ *
+ * This function processes memory regions that are explicitly marked as pinned.
+ * Pinned regions are preallocated, mapped upfront, and do not rely on fault-based
+ * population. The function ensures the region is properly populated, handles
+ * encryption requirements for SNP partitions if applicable, maps the region,
+ * and performs necessary sharing or eviction operations based on the mapping
+ * result.
+ *
+ * Return: 0 on success, negative error code on failure.
  */
-static int
-mshv_partition_mem_region_map(struct mshv_mem_region *region)
+static int mshv_prepare_pinned_region(struct mshv_mem_region *region)
 {
 	struct mshv_partition *partition = region->partition;
 	int ret;
 
-	ret = mshv_region_populate(region);
+	ret = mshv_region_pin(region);
 	if (ret) {
-		pt_err(partition, "Failed to populate memory region: %d\n",
+		pt_err(partition, "Failed to pin memory region: %d\n",
 		       ret);
 		goto err_out;
 	}
@@ -1275,7 +1267,7 @@ mshv_partition_mem_region_map(struct mshv_mem_region *region)
 			pt_err(partition,
 			       "Failed to unshare memory region (guest_pfn: %llu): %d\n",
 			       region->start_gfn, ret);
-			goto evict_region;
+			goto invalidate_region;
 		}
 	}
 
@@ -1285,7 +1277,7 @@ mshv_partition_mem_region_map(struct mshv_mem_region *region)
 
 		shrc = mshv_partition_region_share(region);
 		if (!shrc)
-			goto evict_region;
+			goto invalidate_region;
 
 		pt_err(partition,
 		       "Failed to share memory region (guest_pfn: %llu): %d\n",
@@ -1299,8 +1291,8 @@ mshv_partition_mem_region_map(struct mshv_mem_region *region)
 
 	return 0;
 
-evict_region:
-	mshv_region_evict(region);
+invalidate_region:
+	mshv_region_invalidate(region);
 err_out:
 	return ret;
 }
@@ -1349,7 +1341,7 @@ mshv_map_user_memory(struct mshv_partition *partition,
 		ret = hv_call_map_mmio_pages(partition->pt_id, mem.guest_pfn,
 					     mmio_pfn, HVPFN_DOWN(mem.size));
 	else
-		ret = mshv_partition_mem_region_map(region);
+		ret = mshv_prepare_pinned_region(region);
 
 	if (ret)
 		goto errout;
@@ -1394,7 +1386,7 @@ mshv_unmap_user_memory(struct mshv_partition *partition,
 	hv_call_unmap_gpa_pages(partition->pt_id, region->start_gfn,
 				region->nr_pages, unmap_flags);
 
-	mshv_region_evict(region);
+	mshv_region_invalidate(region);
 
 	vfree(region);
 	return 0;
@@ -1812,7 +1804,7 @@ static void destroy_partition(struct mshv_partition *partition)
 			}
 		}
 
-		mshv_region_evict(region);
+		mshv_region_invalidate(region);
 
 		vfree(region);
 	}



^ permalink raw reply related

* [PATCH v7 2/7] Drivers: hv: Centralize guest memory region destruction
From: Stanislav Kinsburskii @ 2025-11-26  2:08 UTC (permalink / raw)
  To: kys, haiyangz, wei.liu, decui; +Cc: linux-hyperv, linux-kernel
In-Reply-To: <176412196000.447063.4256335030026363827.stgit@skinsburskii-cloud-desktop.internal.cloudapp.net>

Centralize guest memory region destruction to prevent resource leaks and
inconsistent cleanup across unmap and partition destruction paths.

Unify region removal, encrypted partition access recovery, and region
invalidation to improve maintainability and reliability. Reduce code
duplication and make future updates less error-prone by encapsulating
cleanup logic in a single helper.

Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
Reviewed-by: Nuno Das Neves <nunodasneves@linux.microsoft.com>
---
 drivers/hv/mshv_root_main.c |   65 ++++++++++++++++++++++---------------------
 1 file changed, 34 insertions(+), 31 deletions(-)

diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
index fec82619684a..ec18984c3f2d 100644
--- a/drivers/hv/mshv_root_main.c
+++ b/drivers/hv/mshv_root_main.c
@@ -1356,13 +1356,42 @@ mshv_map_user_memory(struct mshv_partition *partition,
 	return ret;
 }
 
+static void mshv_partition_destroy_region(struct mshv_mem_region *region)
+{
+	struct mshv_partition *partition = region->partition;
+	u32 unmap_flags = 0;
+	int ret;
+
+	hlist_del(&region->hnode);
+
+	if (mshv_partition_encrypted(partition)) {
+		ret = mshv_partition_region_share(region);
+		if (ret) {
+			pt_err(partition,
+			       "Failed to regain access to memory, unpinning user pages will fail and crash the host error: %d\n",
+			       ret);
+			return;
+		}
+	}
+
+	if (region->flags.large_pages)
+		unmap_flags |= HV_UNMAP_GPA_LARGE_PAGE;
+
+	/* ignore unmap failures and continue as process may be exiting */
+	hv_call_unmap_gpa_pages(partition->pt_id, region->start_gfn,
+				region->nr_pages, unmap_flags);
+
+	mshv_region_invalidate(region);
+
+	vfree(region);
+}
+
 /* Called for unmapping both the guest ram and the mmio space */
 static long
 mshv_unmap_user_memory(struct mshv_partition *partition,
 		       struct mshv_user_mem_region mem)
 {
 	struct mshv_mem_region *region;
-	u32 unmap_flags = 0;
 
 	if (!(mem.flags & BIT(MSHV_SET_MEM_BIT_UNMAP)))
 		return -EINVAL;
@@ -1377,18 +1406,8 @@ mshv_unmap_user_memory(struct mshv_partition *partition,
 	    region->nr_pages != HVPFN_DOWN(mem.size))
 		return -EINVAL;
 
-	hlist_del(&region->hnode);
+	mshv_partition_destroy_region(region);
 
-	if (region->flags.large_pages)
-		unmap_flags |= HV_UNMAP_GPA_LARGE_PAGE;
-
-	/* ignore unmap failures and continue as process may be exiting */
-	hv_call_unmap_gpa_pages(partition->pt_id, region->start_gfn,
-				region->nr_pages, unmap_flags);
-
-	mshv_region_invalidate(region);
-
-	vfree(region);
 	return 0;
 }
 
@@ -1724,8 +1743,8 @@ static void destroy_partition(struct mshv_partition *partition)
 {
 	struct mshv_vp *vp;
 	struct mshv_mem_region *region;
-	int i, ret;
 	struct hlist_node *n;
+	int i;
 
 	if (refcount_read(&partition->pt_ref_count)) {
 		pt_err(partition,
@@ -1789,25 +1808,9 @@ static void destroy_partition(struct mshv_partition *partition)
 
 	remove_partition(partition);
 
-	/* Remove regions, regain access to the memory and unpin the pages */
 	hlist_for_each_entry_safe(region, n, &partition->pt_mem_regions,
-				  hnode) {
-		hlist_del(&region->hnode);
-
-		if (mshv_partition_encrypted(partition)) {
-			ret = mshv_partition_region_share(region);
-			if (ret) {
-				pt_err(partition,
-				       "Failed to regain access to memory, unpinning user pages will fail and crash the host error: %d\n",
-				      ret);
-				return;
-			}
-		}
-
-		mshv_region_invalidate(region);
-
-		vfree(region);
-	}
+				  hnode)
+		mshv_partition_destroy_region(region);
 
 	/* Withdraw and free all pages we deposited */
 	hv_call_withdraw_memory(U64_MAX, NUMA_NO_NODE, partition->pt_id);



^ permalink raw reply related

* [PATCH v7 3/7] Drivers: hv: Move region management to mshv_regions.c
From: Stanislav Kinsburskii @ 2025-11-26  2:09 UTC (permalink / raw)
  To: kys, haiyangz, wei.liu, decui; +Cc: linux-hyperv, linux-kernel
In-Reply-To: <176412196000.447063.4256335030026363827.stgit@skinsburskii-cloud-desktop.internal.cloudapp.net>

Refactor memory region management functions from mshv_root_main.c into
mshv_regions.c for better modularity and code organization.

Adjust function calls and headers to use the new implementation. Improve
maintainability and separation of concerns in the mshv_root module.

Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
---
 drivers/hv/Makefile         |    2 
 drivers/hv/mshv_regions.c   |  175 +++++++++++++++++++++++++++++++++++++++++++
 drivers/hv/mshv_root.h      |   10 ++
 drivers/hv/mshv_root_main.c |  176 +++----------------------------------------
 4 files changed, 198 insertions(+), 165 deletions(-)
 create mode 100644 drivers/hv/mshv_regions.c

diff --git a/drivers/hv/Makefile b/drivers/hv/Makefile
index 58b8d07639f3..46d4f4f1b252 100644
--- a/drivers/hv/Makefile
+++ b/drivers/hv/Makefile
@@ -14,7 +14,7 @@ hv_vmbus-y := vmbus_drv.o \
 hv_vmbus-$(CONFIG_HYPERV_TESTING)	+= hv_debugfs.o
 hv_utils-y := hv_util.o hv_kvp.o hv_snapshot.o hv_utils_transport.o
 mshv_root-y := mshv_root_main.o mshv_synic.o mshv_eventfd.o mshv_irq.o \
-	       mshv_root_hv_call.o mshv_portid_table.o
+	       mshv_root_hv_call.o mshv_portid_table.o mshv_regions.o
 mshv_vtl-y := mshv_vtl_main.o
 
 # Code that must be built-in
diff --git a/drivers/hv/mshv_regions.c b/drivers/hv/mshv_regions.c
new file mode 100644
index 000000000000..35b866670840
--- /dev/null
+++ b/drivers/hv/mshv_regions.c
@@ -0,0 +1,175 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2025, Microsoft Corporation.
+ *
+ * Memory region management for mshv_root module.
+ *
+ * Authors: Microsoft Linux virtualization team
+ */
+
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+
+#include <asm/mshyperv.h>
+
+#include "mshv_root.h"
+
+struct mshv_mem_region *mshv_region_create(u64 guest_pfn, u64 nr_pages,
+					   u64 uaddr, u32 flags,
+					   bool is_mmio)
+{
+	struct mshv_mem_region *region;
+
+	region = vzalloc(sizeof(*region) + sizeof(struct page *) * nr_pages);
+	if (!region)
+		return ERR_PTR(-ENOMEM);
+
+	region->nr_pages = nr_pages;
+	region->start_gfn = guest_pfn;
+	region->start_uaddr = uaddr;
+	region->hv_map_flags = HV_MAP_GPA_READABLE | HV_MAP_GPA_ADJUSTABLE;
+	if (flags & BIT(MSHV_SET_MEM_BIT_WRITABLE))
+		region->hv_map_flags |= HV_MAP_GPA_WRITABLE;
+	if (flags & BIT(MSHV_SET_MEM_BIT_EXECUTABLE))
+		region->hv_map_flags |= HV_MAP_GPA_EXECUTABLE;
+
+	/* Note: large_pages flag populated when we pin the pages */
+	if (!is_mmio)
+		region->flags.range_pinned = true;
+
+	return region;
+}
+
+int mshv_region_share(struct mshv_mem_region *region)
+{
+	u32 flags = HV_MODIFY_SPA_PAGE_HOST_ACCESS_MAKE_SHARED;
+
+	if (region->flags.large_pages)
+		flags |= HV_MODIFY_SPA_PAGE_HOST_ACCESS_LARGE_PAGE;
+
+	return hv_call_modify_spa_host_access(region->partition->pt_id,
+			region->pages, region->nr_pages,
+			HV_MAP_GPA_READABLE | HV_MAP_GPA_WRITABLE,
+			flags, true);
+}
+
+int mshv_region_unshare(struct mshv_mem_region *region)
+{
+	u32 flags = HV_MODIFY_SPA_PAGE_HOST_ACCESS_MAKE_EXCLUSIVE;
+
+	if (region->flags.large_pages)
+		flags |= HV_MODIFY_SPA_PAGE_HOST_ACCESS_LARGE_PAGE;
+
+	return hv_call_modify_spa_host_access(region->partition->pt_id,
+			region->pages, region->nr_pages,
+			0,
+			flags, false);
+}
+
+static int mshv_region_remap_pages(struct mshv_mem_region *region,
+				   u32 map_flags,
+				   u64 page_offset, u64 page_count)
+{
+	if (page_offset + page_count > region->nr_pages)
+		return -EINVAL;
+
+	if (region->flags.large_pages)
+		map_flags |= HV_MAP_GPA_LARGE_PAGE;
+
+	return hv_call_map_gpa_pages(region->partition->pt_id,
+				     region->start_gfn + page_offset,
+				     page_count, map_flags,
+				     region->pages + page_offset);
+}
+
+int mshv_region_map(struct mshv_mem_region *region)
+{
+	u32 map_flags = region->hv_map_flags;
+
+	return mshv_region_remap_pages(region, map_flags,
+				       0, region->nr_pages);
+}
+
+static void mshv_region_invalidate_pages(struct mshv_mem_region *region,
+					 u64 page_offset, u64 page_count)
+{
+	if (region->flags.range_pinned)
+		unpin_user_pages(region->pages + page_offset, page_count);
+
+	memset(region->pages + page_offset, 0,
+	       page_count * sizeof(struct page *));
+}
+
+void mshv_region_invalidate(struct mshv_mem_region *region)
+{
+	mshv_region_invalidate_pages(region, 0, region->nr_pages);
+}
+
+int mshv_region_pin(struct mshv_mem_region *region)
+{
+	u64 done_count, nr_pages;
+	struct page **pages;
+	__u64 userspace_addr;
+	int ret;
+
+	for (done_count = 0; done_count < region->nr_pages; done_count += ret) {
+		pages = region->pages + done_count;
+		userspace_addr = region->start_uaddr +
+				 done_count * HV_HYP_PAGE_SIZE;
+		nr_pages = min(region->nr_pages - done_count,
+			       MSHV_PIN_PAGES_BATCH_SIZE);
+
+		/*
+		 * Pinning assuming 4k pages works for large pages too.
+		 * All page structs within the large page are returned.
+		 *
+		 * Pin requests are batched because pin_user_pages_fast
+		 * with the FOLL_LONGTERM flag does a large temporary
+		 * allocation of contiguous memory.
+		 */
+		ret = pin_user_pages_fast(userspace_addr, nr_pages,
+					  FOLL_WRITE | FOLL_LONGTERM,
+					  pages);
+		if (ret < 0)
+			goto release_pages;
+	}
+
+	if (PageHuge(region->pages[0]))
+		region->flags.large_pages = true;
+
+	return 0;
+
+release_pages:
+	mshv_region_invalidate_pages(region, 0, done_count);
+	return ret;
+}
+
+void mshv_region_destroy(struct mshv_mem_region *region)
+{
+	struct mshv_partition *partition = region->partition;
+	u32 unmap_flags = 0;
+	int ret;
+
+	hlist_del(&region->hnode);
+
+	if (mshv_partition_encrypted(partition)) {
+		ret = mshv_region_share(region);
+		if (ret) {
+			pt_err(partition,
+			       "Failed to regain access to memory, unpinning user pages will fail and crash the host error: %d\n",
+			       ret);
+			return;
+		}
+	}
+
+	if (region->flags.large_pages)
+		unmap_flags |= HV_UNMAP_GPA_LARGE_PAGE;
+
+	/* ignore unmap failures and continue as process may be exiting */
+	hv_call_unmap_gpa_pages(partition->pt_id, region->start_gfn,
+				region->nr_pages, unmap_flags);
+
+	mshv_region_invalidate(region);
+
+	vfree(region);
+}
diff --git a/drivers/hv/mshv_root.h b/drivers/hv/mshv_root.h
index 3eb815011b46..0366f416c2f0 100644
--- a/drivers/hv/mshv_root.h
+++ b/drivers/hv/mshv_root.h
@@ -312,4 +312,14 @@ extern struct mshv_root mshv_root;
 extern enum hv_scheduler_type hv_scheduler_type;
 extern u8 * __percpu *hv_synic_eventring_tail;
 
+struct mshv_mem_region *mshv_region_create(u64 guest_pfn, u64 nr_pages,
+					   u64 uaddr, u32 flags,
+					   bool is_mmio);
+int mshv_region_share(struct mshv_mem_region *region);
+int mshv_region_unshare(struct mshv_mem_region *region);
+int mshv_region_map(struct mshv_mem_region *region);
+void mshv_region_invalidate(struct mshv_mem_region *region);
+int mshv_region_pin(struct mshv_mem_region *region);
+void mshv_region_destroy(struct mshv_mem_region *region);
+
 #endif /* _MSHV_ROOT_H_ */
diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
index ec18984c3f2d..5dfb933da981 100644
--- a/drivers/hv/mshv_root_main.c
+++ b/drivers/hv/mshv_root_main.c
@@ -1059,117 +1059,6 @@ static void mshv_async_hvcall_handler(void *data, u64 *status)
 	*status = partition->async_hypercall_status;
 }
 
-static int
-mshv_partition_region_share(struct mshv_mem_region *region)
-{
-	u32 flags = HV_MODIFY_SPA_PAGE_HOST_ACCESS_MAKE_SHARED;
-
-	if (region->flags.large_pages)
-		flags |= HV_MODIFY_SPA_PAGE_HOST_ACCESS_LARGE_PAGE;
-
-	return hv_call_modify_spa_host_access(region->partition->pt_id,
-			region->pages, region->nr_pages,
-			HV_MAP_GPA_READABLE | HV_MAP_GPA_WRITABLE,
-			flags, true);
-}
-
-static int
-mshv_partition_region_unshare(struct mshv_mem_region *region)
-{
-	u32 flags = HV_MODIFY_SPA_PAGE_HOST_ACCESS_MAKE_EXCLUSIVE;
-
-	if (region->flags.large_pages)
-		flags |= HV_MODIFY_SPA_PAGE_HOST_ACCESS_LARGE_PAGE;
-
-	return hv_call_modify_spa_host_access(region->partition->pt_id,
-			region->pages, region->nr_pages,
-			0,
-			flags, false);
-}
-
-static int
-mshv_region_remap_pages(struct mshv_mem_region *region, u32 map_flags,
-			u64 page_offset, u64 page_count)
-{
-	if (page_offset + page_count > region->nr_pages)
-		return -EINVAL;
-
-	if (region->flags.large_pages)
-		map_flags |= HV_MAP_GPA_LARGE_PAGE;
-
-	/* ask the hypervisor to map guest ram */
-	return hv_call_map_gpa_pages(region->partition->pt_id,
-				     region->start_gfn + page_offset,
-				     page_count, map_flags,
-				     region->pages + page_offset);
-}
-
-static int
-mshv_region_map(struct mshv_mem_region *region)
-{
-	u32 map_flags = region->hv_map_flags;
-
-	return mshv_region_remap_pages(region, map_flags,
-				       0, region->nr_pages);
-}
-
-static void
-mshv_region_invalidate_pages(struct mshv_mem_region *region,
-			     u64 page_offset, u64 page_count)
-{
-	if (region->flags.range_pinned)
-		unpin_user_pages(region->pages + page_offset, page_count);
-
-	memset(region->pages + page_offset, 0,
-	       page_count * sizeof(struct page *));
-}
-
-static void
-mshv_region_invalidate(struct mshv_mem_region *region)
-{
-	mshv_region_invalidate_pages(region, 0, region->nr_pages);
-}
-
-static int
-mshv_region_pin(struct mshv_mem_region *region)
-{
-	u64 done_count, nr_pages;
-	struct page **pages;
-	__u64 userspace_addr;
-	int ret;
-
-	for (done_count = 0; done_count < region->nr_pages; done_count += ret) {
-		pages = region->pages + done_count;
-		userspace_addr = region->start_uaddr +
-				 done_count * HV_HYP_PAGE_SIZE;
-		nr_pages = min(region->nr_pages - done_count,
-			       MSHV_PIN_PAGES_BATCH_SIZE);
-
-		/*
-		 * Pinning assuming 4k pages works for large pages too.
-		 * All page structs within the large page are returned.
-		 *
-		 * Pin requests are batched because pin_user_pages_fast
-		 * with the FOLL_LONGTERM flag does a large temporary
-		 * allocation of contiguous memory.
-		 */
-		ret = pin_user_pages_fast(userspace_addr, nr_pages,
-					  FOLL_WRITE | FOLL_LONGTERM,
-					  pages);
-		if (ret < 0)
-			goto release_pages;
-	}
-
-	if (PageHuge(region->pages[0]))
-		region->flags.large_pages = true;
-
-	return 0;
-
-release_pages:
-	mshv_region_invalidate_pages(region, 0, done_count);
-	return ret;
-}
-
 static struct mshv_mem_region *
 mshv_partition_region_by_gfn(struct mshv_partition *partition, u64 gfn)
 {
@@ -1193,7 +1082,7 @@ static int mshv_partition_create_region(struct mshv_partition *partition,
 					struct mshv_mem_region **regionpp,
 					bool is_mmio)
 {
-	struct mshv_mem_region *region, *rg;
+	struct mshv_mem_region *rg;
 	u64 nr_pages = HVPFN_DOWN(mem->size);
 
 	/* Reject overlapping regions */
@@ -1205,26 +1094,15 @@ static int mshv_partition_create_region(struct mshv_partition *partition,
 		return -EEXIST;
 	}
 
-	region = vzalloc(sizeof(*region) + sizeof(struct page *) * nr_pages);
-	if (!region)
-		return -ENOMEM;
-
-	region->nr_pages = nr_pages;
-	region->start_gfn = mem->guest_pfn;
-	region->start_uaddr = mem->userspace_addr;
-	region->hv_map_flags = HV_MAP_GPA_READABLE | HV_MAP_GPA_ADJUSTABLE;
-	if (mem->flags & BIT(MSHV_SET_MEM_BIT_WRITABLE))
-		region->hv_map_flags |= HV_MAP_GPA_WRITABLE;
-	if (mem->flags & BIT(MSHV_SET_MEM_BIT_EXECUTABLE))
-		region->hv_map_flags |= HV_MAP_GPA_EXECUTABLE;
-
-	/* Note: large_pages flag populated when we pin the pages */
-	if (!is_mmio)
-		region->flags.range_pinned = true;
+	rg = mshv_region_create(mem->guest_pfn, nr_pages,
+				mem->userspace_addr, mem->flags,
+				is_mmio);
+	if (IS_ERR(rg))
+		return PTR_ERR(rg);
 
-	region->partition = partition;
+	rg->partition = partition;
 
-	*regionpp = region;
+	*regionpp = rg;
 
 	return 0;
 }
@@ -1262,7 +1140,7 @@ static int mshv_prepare_pinned_region(struct mshv_mem_region *region)
 	 * access to guest memory regions.
 	 */
 	if (mshv_partition_encrypted(partition)) {
-		ret = mshv_partition_region_unshare(region);
+		ret = mshv_region_unshare(region);
 		if (ret) {
 			pt_err(partition,
 			       "Failed to unshare memory region (guest_pfn: %llu): %d\n",
@@ -1275,7 +1153,7 @@ static int mshv_prepare_pinned_region(struct mshv_mem_region *region)
 	if (ret && mshv_partition_encrypted(partition)) {
 		int shrc;
 
-		shrc = mshv_partition_region_share(region);
+		shrc = mshv_region_share(region);
 		if (!shrc)
 			goto invalidate_region;
 
@@ -1356,36 +1234,6 @@ mshv_map_user_memory(struct mshv_partition *partition,
 	return ret;
 }
 
-static void mshv_partition_destroy_region(struct mshv_mem_region *region)
-{
-	struct mshv_partition *partition = region->partition;
-	u32 unmap_flags = 0;
-	int ret;
-
-	hlist_del(&region->hnode);
-
-	if (mshv_partition_encrypted(partition)) {
-		ret = mshv_partition_region_share(region);
-		if (ret) {
-			pt_err(partition,
-			       "Failed to regain access to memory, unpinning user pages will fail and crash the host error: %d\n",
-			       ret);
-			return;
-		}
-	}
-
-	if (region->flags.large_pages)
-		unmap_flags |= HV_UNMAP_GPA_LARGE_PAGE;
-
-	/* ignore unmap failures and continue as process may be exiting */
-	hv_call_unmap_gpa_pages(partition->pt_id, region->start_gfn,
-				region->nr_pages, unmap_flags);
-
-	mshv_region_invalidate(region);
-
-	vfree(region);
-}
-
 /* Called for unmapping both the guest ram and the mmio space */
 static long
 mshv_unmap_user_memory(struct mshv_partition *partition,
@@ -1406,7 +1254,7 @@ mshv_unmap_user_memory(struct mshv_partition *partition,
 	    region->nr_pages != HVPFN_DOWN(mem.size))
 		return -EINVAL;
 
-	mshv_partition_destroy_region(region);
+	mshv_region_destroy(region);
 
 	return 0;
 }
@@ -1810,7 +1658,7 @@ static void destroy_partition(struct mshv_partition *partition)
 
 	hlist_for_each_entry_safe(region, n, &partition->pt_mem_regions,
 				  hnode)
-		mshv_partition_destroy_region(region);
+		mshv_region_destroy(region);
 
 	/* Withdraw and free all pages we deposited */
 	hv_call_withdraw_memory(U64_MAX, NUMA_NO_NODE, partition->pt_id);



^ permalink raw reply related

* [PATCH v7 4/7] Drivers: hv: Fix huge page handling in memory region traversal
From: Stanislav Kinsburskii @ 2025-11-26  2:09 UTC (permalink / raw)
  To: kys, haiyangz, wei.liu, decui; +Cc: linux-hyperv, linux-kernel
In-Reply-To: <176412196000.447063.4256335030026363827.stgit@skinsburskii-cloud-desktop.internal.cloudapp.net>

The previous code assumed that if a region's first page was huge, the
entire region consisted of huge pages and stored this in a large_pages
flag. This premise is incorrect not only for movable regions (where
pages can be split and merged on invalidate callbacks or page faults),
but even for pinned regions: THPs can be split and merged during
allocation, so a large, pinned region may contain a mix of huge and
regular pages.

This change removes the large_pages flag and replaces region-wide
assumptions with per-chunk inspection of the actual page size when
mapping, unmapping, sharing, and unsharing. This makes huge page
handling correct for mixed-page regions and avoids relying on stale
metadata that can easily become invalid as memory is remapped.

Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
---
 drivers/hv/mshv_regions.c |  213 +++++++++++++++++++++++++++++++++++++++------
 drivers/hv/mshv_root.h    |    3 -
 2 files changed, 184 insertions(+), 32 deletions(-)

diff --git a/drivers/hv/mshv_regions.c b/drivers/hv/mshv_regions.c
index 35b866670840..d535d2e3e811 100644
--- a/drivers/hv/mshv_regions.c
+++ b/drivers/hv/mshv_regions.c
@@ -14,6 +14,124 @@
 
 #include "mshv_root.h"
 
+/**
+ * mshv_region_process_chunk - Processes a contiguous chunk of memory pages
+ *                             in a region.
+ * @region     : Pointer to the memory region structure.
+ * @flags      : Flags to pass to the handler.
+ * @page_offset: Offset into the region's pages array to start processing.
+ * @page_count : Number of pages to process.
+ * @handler    : Callback function to handle the chunk.
+ *
+ * This function scans the region's pages starting from @page_offset,
+ * checking for contiguous present pages of the same size (normal or huge).
+ * It invokes @handler for the chunk of contiguous pages found. Returns the
+ * number of pages handled, or a negative error code if the first page is
+ * not present or the handler fails.
+ *
+ * Note: The @handler callback must be able to handle both normal and huge
+ * pages.
+ *
+ * Return: Number of pages handled, or negative error code.
+ */
+static long mshv_region_process_chunk(struct mshv_mem_region *region,
+				      u32 flags,
+				      u64 page_offset, u64 page_count,
+				      int (*handler)(struct mshv_mem_region *region,
+						     u32 flags,
+						     u64 page_offset,
+						     u64 page_count))
+{
+	u64 count, stride;
+	unsigned int page_order;
+	struct page *page;
+	int ret;
+
+	page = region->pages[page_offset];
+	if (!page)
+		return -EINVAL;
+
+	page_order = folio_order(page_folio(page));
+	/* 1G huge pages aren't supported by the hypercalls */
+	if (page_order == PUD_ORDER)
+		return -EINVAL;
+
+	stride = 1 << page_order;
+
+	/* Start at stride since the first page is validated */
+	for (count = stride; count < page_count; count += stride) {
+		page = region->pages[page_offset + count];
+
+		/* Break if current page is not present */
+		if (!page)
+			break;
+
+		/* Break if page size changes */
+		if (page_order != folio_order(page_folio(page)))
+			break;
+	}
+
+	ret = handler(region, flags, page_offset, count);
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+/**
+ * mshv_region_process_range - Processes a range of memory pages in a
+ *                             region.
+ * @region     : Pointer to the memory region structure.
+ * @flags      : Flags to pass to the handler.
+ * @page_offset: Offset into the region's pages array to start processing.
+ * @page_count : Number of pages to process.
+ * @handler    : Callback function to handle each chunk of contiguous
+ *               pages.
+ *
+ * Iterates over the specified range of pages in @region, skipping
+ * non-present pages. For each contiguous chunk of present pages, invokes
+ * @handler via mshv_region_process_chunk.
+ *
+ * Note: The @handler callback must be able to handle both normal and huge
+ * pages.
+ *
+ * Returns 0 on success, or a negative error code on failure.
+ */
+static int mshv_region_process_range(struct mshv_mem_region *region,
+				     u32 flags,
+				     u64 page_offset, u64 page_count,
+				     int (*handler)(struct mshv_mem_region *region,
+						    u32 flags,
+						    u64 page_offset,
+						    u64 page_count))
+{
+	long ret;
+
+	if (page_offset + page_count > region->nr_pages)
+		return -EINVAL;
+
+	while (page_count) {
+		/* Skip non-present pages */
+		if (!region->pages[page_offset]) {
+			page_offset++;
+			page_count--;
+			continue;
+		}
+
+		ret = mshv_region_process_chunk(region, flags,
+						page_offset,
+						page_count,
+						handler);
+		if (ret < 0)
+			return ret;
+
+		page_offset += ret;
+		page_count -= ret;
+	}
+
+	return 0;
+}
+
 struct mshv_mem_region *mshv_region_create(u64 guest_pfn, u64 nr_pages,
 					   u64 uaddr, u32 flags,
 					   bool is_mmio)
@@ -33,55 +151,80 @@ struct mshv_mem_region *mshv_region_create(u64 guest_pfn, u64 nr_pages,
 	if (flags & BIT(MSHV_SET_MEM_BIT_EXECUTABLE))
 		region->hv_map_flags |= HV_MAP_GPA_EXECUTABLE;
 
-	/* Note: large_pages flag populated when we pin the pages */
 	if (!is_mmio)
 		region->flags.range_pinned = true;
 
 	return region;
 }
 
+static int mshv_region_chunk_share(struct mshv_mem_region *region,
+				   u32 flags,
+				   u64 page_offset, u64 page_count)
+{
+	if (PageTransCompound(region->pages[page_offset]))
+		flags |= HV_MODIFY_SPA_PAGE_HOST_ACCESS_LARGE_PAGE;
+
+	return hv_call_modify_spa_host_access(region->partition->pt_id,
+					      region->pages + page_offset,
+					      page_count,
+					      HV_MAP_GPA_READABLE |
+					      HV_MAP_GPA_WRITABLE,
+					      flags, true);
+}
+
 int mshv_region_share(struct mshv_mem_region *region)
 {
 	u32 flags = HV_MODIFY_SPA_PAGE_HOST_ACCESS_MAKE_SHARED;
 
-	if (region->flags.large_pages)
+	return mshv_region_process_range(region, flags,
+					 0, region->nr_pages,
+					 mshv_region_chunk_share);
+}
+
+static int mshv_region_chunk_unshare(struct mshv_mem_region *region,
+				     u32 flags,
+				     u64 page_offset, u64 page_count)
+{
+	if (PageTransCompound(region->pages[page_offset]))
 		flags |= HV_MODIFY_SPA_PAGE_HOST_ACCESS_LARGE_PAGE;
 
 	return hv_call_modify_spa_host_access(region->partition->pt_id,
-			region->pages, region->nr_pages,
-			HV_MAP_GPA_READABLE | HV_MAP_GPA_WRITABLE,
-			flags, true);
+					      region->pages + page_offset,
+					      page_count, 0,
+					      flags, false);
 }
 
 int mshv_region_unshare(struct mshv_mem_region *region)
 {
 	u32 flags = HV_MODIFY_SPA_PAGE_HOST_ACCESS_MAKE_EXCLUSIVE;
 
-	if (region->flags.large_pages)
-		flags |= HV_MODIFY_SPA_PAGE_HOST_ACCESS_LARGE_PAGE;
-
-	return hv_call_modify_spa_host_access(region->partition->pt_id,
-			region->pages, region->nr_pages,
-			0,
-			flags, false);
+	return mshv_region_process_range(region, flags,
+					 0, region->nr_pages,
+					 mshv_region_chunk_unshare);
 }
 
-static int mshv_region_remap_pages(struct mshv_mem_region *region,
-				   u32 map_flags,
+static int mshv_region_chunk_remap(struct mshv_mem_region *region,
+				   u32 flags,
 				   u64 page_offset, u64 page_count)
 {
-	if (page_offset + page_count > region->nr_pages)
-		return -EINVAL;
-
-	if (region->flags.large_pages)
-		map_flags |= HV_MAP_GPA_LARGE_PAGE;
+	if (PageTransCompound(region->pages[page_offset]))
+		flags |= HV_MAP_GPA_LARGE_PAGE;
 
 	return hv_call_map_gpa_pages(region->partition->pt_id,
 				     region->start_gfn + page_offset,
-				     page_count, map_flags,
+				     page_count, flags,
 				     region->pages + page_offset);
 }
 
+static int mshv_region_remap_pages(struct mshv_mem_region *region,
+				   u32 map_flags,
+				   u64 page_offset, u64 page_count)
+{
+	return mshv_region_process_range(region, map_flags,
+					 page_offset, page_count,
+					 mshv_region_chunk_remap);
+}
+
 int mshv_region_map(struct mshv_mem_region *region)
 {
 	u32 map_flags = region->hv_map_flags;
@@ -134,9 +277,6 @@ int mshv_region_pin(struct mshv_mem_region *region)
 			goto release_pages;
 	}
 
-	if (PageHuge(region->pages[0]))
-		region->flags.large_pages = true;
-
 	return 0;
 
 release_pages:
@@ -144,10 +284,28 @@ int mshv_region_pin(struct mshv_mem_region *region)
 	return ret;
 }
 
+static int mshv_region_chunk_unmap(struct mshv_mem_region *region,
+				   u32 flags,
+				   u64 page_offset, u64 page_count)
+{
+	if (PageTransCompound(region->pages[page_offset]))
+		flags |= HV_UNMAP_GPA_LARGE_PAGE;
+
+	return hv_call_unmap_gpa_pages(region->partition->pt_id,
+				       region->start_gfn + page_offset,
+				       page_count, 0);
+}
+
+static int mshv_region_unmap(struct mshv_mem_region *region)
+{
+	return mshv_region_process_range(region, 0,
+					 0, region->nr_pages,
+					 mshv_region_chunk_unmap);
+}
+
 void mshv_region_destroy(struct mshv_mem_region *region)
 {
 	struct mshv_partition *partition = region->partition;
-	u32 unmap_flags = 0;
 	int ret;
 
 	hlist_del(&region->hnode);
@@ -162,12 +320,7 @@ void mshv_region_destroy(struct mshv_mem_region *region)
 		}
 	}
 
-	if (region->flags.large_pages)
-		unmap_flags |= HV_UNMAP_GPA_LARGE_PAGE;
-
-	/* ignore unmap failures and continue as process may be exiting */
-	hv_call_unmap_gpa_pages(partition->pt_id, region->start_gfn,
-				region->nr_pages, unmap_flags);
+	mshv_region_unmap(region);
 
 	mshv_region_invalidate(region);
 
diff --git a/drivers/hv/mshv_root.h b/drivers/hv/mshv_root.h
index 0366f416c2f0..ff3374f13691 100644
--- a/drivers/hv/mshv_root.h
+++ b/drivers/hv/mshv_root.h
@@ -77,9 +77,8 @@ struct mshv_mem_region {
 	u64 start_uaddr;
 	u32 hv_map_flags;
 	struct {
-		u64 large_pages:  1; /* 2MiB */
 		u64 range_pinned: 1;
-		u64 reserved:	 62;
+		u64 reserved:	 63;
 	} flags;
 	struct mshv_partition *partition;
 	struct page *pages[];



^ permalink raw reply related

* [PATCH v7 5/7] Drivers: hv: Improve region overlap detection in partition create
From: Stanislav Kinsburskii @ 2025-11-26  2:09 UTC (permalink / raw)
  To: kys, haiyangz, wei.liu, decui; +Cc: linux-hyperv, linux-kernel
In-Reply-To: <176412196000.447063.4256335030026363827.stgit@skinsburskii-cloud-desktop.internal.cloudapp.net>

Refactor region overlap check in mshv_partition_create_region to use
mshv_partition_region_by_gfn for both start and end guest PFNs, replacing
manual iteration.

This is a cleaner approach that leverages existing functionality to
accurately detect overlapping memory regions.

Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
---
 drivers/hv/mshv_root_main.c |    8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
index 5dfb933da981..ae600b927f49 100644
--- a/drivers/hv/mshv_root_main.c
+++ b/drivers/hv/mshv_root_main.c
@@ -1086,13 +1086,9 @@ static int mshv_partition_create_region(struct mshv_partition *partition,
 	u64 nr_pages = HVPFN_DOWN(mem->size);
 
 	/* Reject overlapping regions */
-	hlist_for_each_entry(rg, &partition->pt_mem_regions, hnode) {
-		if (mem->guest_pfn + nr_pages <= rg->start_gfn ||
-		    rg->start_gfn + rg->nr_pages <= mem->guest_pfn)
-			continue;
-
+	if (mshv_partition_region_by_gfn(partition, mem->guest_pfn) ||
+	    mshv_partition_region_by_gfn(partition, mem->guest_pfn + nr_pages - 1))
 		return -EEXIST;
-	}
 
 	rg = mshv_region_create(mem->guest_pfn, nr_pages,
 				mem->userspace_addr, mem->flags,



^ permalink raw reply related

* [PATCH v7 6/7] Drivers: hv: Add refcount and locking to mem regions
From: Stanislav Kinsburskii @ 2025-11-26  2:09 UTC (permalink / raw)
  To: kys, haiyangz, wei.liu, decui; +Cc: linux-hyperv, linux-kernel
In-Reply-To: <176412196000.447063.4256335030026363827.stgit@skinsburskii-cloud-desktop.internal.cloudapp.net>

Introduce kref-based reference counting and spinlock protection for
memory regions in Hyper-V partition management. This change improves
memory region lifecycle management and ensures thread-safe access to the
region list.

Also improves the check for overlapped memory regions during region
creation, preventing duplicate or conflicting mappings.

Previously, the regions list was protected by the partition mutex.
However, this approach is too heavy for frequent fault and invalidation
operations. Finer grained locking is now used to improve efficiency and
concurrency.

This is a precursor to supporting movable memory regions. Fault and
invalidation handling for movable regions will require safe traversal of
the region list and holding a region reference while performing
invalidation or fault operations.

Signed-off-by: Stanislav Kinsburskii <skinsburskii@linux.microsoft.com>
---
 drivers/hv/mshv_regions.c   |   19 ++++++++++++++++---
 drivers/hv/mshv_root.h      |    6 +++++-
 drivers/hv/mshv_root_main.c |   34 ++++++++++++++++++++++++++--------
 3 files changed, 47 insertions(+), 12 deletions(-)

diff --git a/drivers/hv/mshv_regions.c b/drivers/hv/mshv_regions.c
index d535d2e3e811..6450a7ed8493 100644
--- a/drivers/hv/mshv_regions.c
+++ b/drivers/hv/mshv_regions.c
@@ -7,6 +7,7 @@
  * Authors: Microsoft Linux virtualization team
  */
 
+#include <linux/kref.h>
 #include <linux/mm.h>
 #include <linux/vmalloc.h>
 
@@ -154,6 +155,8 @@ struct mshv_mem_region *mshv_region_create(u64 guest_pfn, u64 nr_pages,
 	if (!is_mmio)
 		region->flags.range_pinned = true;
 
+	kref_init(&region->refcount);
+
 	return region;
 }
 
@@ -303,13 +306,13 @@ static int mshv_region_unmap(struct mshv_mem_region *region)
 					 mshv_region_chunk_unmap);
 }
 
-void mshv_region_destroy(struct mshv_mem_region *region)
+static void mshv_region_destroy(struct kref *ref)
 {
+	struct mshv_mem_region *region =
+		container_of(ref, struct mshv_mem_region, refcount);
 	struct mshv_partition *partition = region->partition;
 	int ret;
 
-	hlist_del(&region->hnode);
-
 	if (mshv_partition_encrypted(partition)) {
 		ret = mshv_region_share(region);
 		if (ret) {
@@ -326,3 +329,13 @@ void mshv_region_destroy(struct mshv_mem_region *region)
 
 	vfree(region);
 }
+
+void mshv_region_put(struct mshv_mem_region *region)
+{
+	kref_put(&region->refcount, mshv_region_destroy);
+}
+
+int mshv_region_get(struct mshv_mem_region *region)
+{
+	return kref_get_unless_zero(&region->refcount);
+}
diff --git a/drivers/hv/mshv_root.h b/drivers/hv/mshv_root.h
index ff3374f13691..4249534ba900 100644
--- a/drivers/hv/mshv_root.h
+++ b/drivers/hv/mshv_root.h
@@ -72,6 +72,7 @@ do { \
 
 struct mshv_mem_region {
 	struct hlist_node hnode;
+	struct kref refcount;
 	u64 nr_pages;
 	u64 start_gfn;
 	u64 start_uaddr;
@@ -97,6 +98,8 @@ struct mshv_partition {
 	u64 pt_id;
 	refcount_t pt_ref_count;
 	struct mutex pt_mutex;
+
+	spinlock_t pt_mem_regions_lock;
 	struct hlist_head pt_mem_regions; // not ordered
 
 	u32 pt_vp_count;
@@ -319,6 +322,7 @@ int mshv_region_unshare(struct mshv_mem_region *region);
 int mshv_region_map(struct mshv_mem_region *region);
 void mshv_region_invalidate(struct mshv_mem_region *region);
 int mshv_region_pin(struct mshv_mem_region *region);
-void mshv_region_destroy(struct mshv_mem_region *region);
+void mshv_region_put(struct mshv_mem_region *region);
+int mshv_region_get(struct mshv_mem_region *region);
 
 #endif /* _MSHV_ROOT_H_ */
diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c
index ae600b927f49..1ef2a28beb17 100644
--- a/drivers/hv/mshv_root_main.c
+++ b/drivers/hv/mshv_root_main.c
@@ -1086,9 +1086,13 @@ static int mshv_partition_create_region(struct mshv_partition *partition,
 	u64 nr_pages = HVPFN_DOWN(mem->size);
 
 	/* Reject overlapping regions */
+	spin_lock(&partition->pt_mem_regions_lock);
 	if (mshv_partition_region_by_gfn(partition, mem->guest_pfn) ||
-	    mshv_partition_region_by_gfn(partition, mem->guest_pfn + nr_pages - 1))
+	    mshv_partition_region_by_gfn(partition, mem->guest_pfn + nr_pages - 1)) {
+		spin_unlock(&partition->pt_mem_regions_lock);
 		return -EEXIST;
+	}
+	spin_unlock(&partition->pt_mem_regions_lock);
 
 	rg = mshv_region_create(mem->guest_pfn, nr_pages,
 				mem->userspace_addr, mem->flags,
@@ -1220,8 +1224,9 @@ mshv_map_user_memory(struct mshv_partition *partition,
 	if (ret)
 		goto errout;
 
-	/* Install the new region */
+	spin_lock(&partition->pt_mem_regions_lock);
 	hlist_add_head(&region->hnode, &partition->pt_mem_regions);
+	spin_unlock(&partition->pt_mem_regions_lock);
 
 	return 0;
 
@@ -1240,17 +1245,27 @@ mshv_unmap_user_memory(struct mshv_partition *partition,
 	if (!(mem.flags & BIT(MSHV_SET_MEM_BIT_UNMAP)))
 		return -EINVAL;
 
+	spin_lock(&partition->pt_mem_regions_lock);
+
 	region = mshv_partition_region_by_gfn(partition, mem.guest_pfn);
-	if (!region)
-		return -EINVAL;
+	if (!region) {
+		spin_unlock(&partition->pt_mem_regions_lock);
+		return -ENOENT;
+	}
 
 	/* Paranoia check */
 	if (region->start_uaddr != mem.userspace_addr ||
 	    region->start_gfn != mem.guest_pfn ||
-	    region->nr_pages != HVPFN_DOWN(mem.size))
+	    region->nr_pages != HVPFN_DOWN(mem.size)) {
+		spin_unlock(&partition->pt_mem_regions_lock);
 		return -EINVAL;
+	}
+
+	hlist_del(&region->hnode);
 
-	mshv_region_destroy(region);
+	spin_unlock(&partition->pt_mem_regions_lock);
+
+	mshv_region_put(region);
 
 	return 0;
 }
@@ -1653,8 +1668,10 @@ static void destroy_partition(struct mshv_partition *partition)
 	remove_partition(partition);
 
 	hlist_for_each_entry_safe(region, n, &partition->pt_mem_regions,
-				  hnode)
-		mshv_region_destroy(region);
+				  hnode) {
+		hlist_del(&region->hnode);
+		mshv_region_put(region);
+	}
 
 	/* Withdraw and free all pages we deposited */
 	hv_call_withdraw_memory(U64_MAX, NUMA_NO_NODE, partition->pt_id);
@@ -1852,6 +1869,7 @@ mshv_ioctl_create_partition(void __user *user_arg, struct device *module_dev)
 
 	INIT_HLIST_HEAD(&partition->pt_devices);
 
+	spin_lock_init(&partition->pt_mem_regions_lock);
 	INIT_HLIST_HEAD(&partition->pt_mem_regions);
 
 	mshv_eventfd_init(partition);



^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox