Skip to content

Drivers: hv: hv_common: use meaningful errnos for hypercall statuses#146

Open
hargar19 wants to merge 1 commit into
product/hcl-main/6.18from
user/hargar/6.18-hv-status-errno-fix
Open

Drivers: hv: hv_common: use meaningful errnos for hypercall statuses#146
hargar19 wants to merge 1 commit into
product/hcl-main/6.18from
user/hargar/6.18-hv-status-errno-fix

Conversation

@hargar19

@hargar19 hargar19 commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

Several HV_STATUS_* codes were collapsed to -EIO in commit 3817854 ("hyperv: Log hypercall status codes as strings") when hv_result_to_errno() was converted from a switch statement to a table-driven lookup. The change was a side-effect of consolidating string/errno mappings into one place; the commit message focused on adding hv_status_printk() for debugging.

The previous mapping in hv_status_to_errno() (used in OHCL v6.12 and earlier) preserved more meaningful errnos for several codes that have natural POSIX counterparts. The collapse to -EIO loses information at call sites that key behavior on the converted errno (for example mshv_vtl_configure_reg_page() whitelists -EINVAL/-EACCES to fall back gracefully when the host refuses to install the register-page overlay; with -EIO that call site now hits a BUG()).

Restore the semantically meaningful mappings for the affected statuses:

Hyper-V Status Old errno New errno
HV_STATUS_ACCESS_DENIED -EIO -EACCES
HV_STATUS_OPERATION_DENIED -EIO -EACCES
HV_STATUS_UNKNOWN_PROPERTY -EIO -EINVAL
HV_STATUS_PROPERTY_VALUE_OUT_OF_RANGE -EIO -EINVAL
HV_STATUS_PROCESSOR_FEATURE_NOT_SUPPORTED -EIO -EOPNOTSUPP

EACCES is the canonical errno for permission denied, EINVAL for invalid argument, and EOPNOTSUPP for unsupported operations. These mappings match both their <errno.h> definitions and the established meanings in the v6.12 mapping.

Also drop two duplicate _STATUS_INFO() rows for HV_STATUS_INVALID_LP_INDEX and HV_STATUS_INVALID_REGISTER_VALUE that were leftover from the original table conversion.

Signed-off-by: Hardik Garg hargar@microsoft.com

Several HV_STATUS_* codes were collapsed to -EIO in commit 3817854
("hyperv: Log hypercall status codes as strings") when hv_result_to_errno()
was converted from a switch statement to a table-driven lookup. The change
was a side-effect of consolidating string/errno mappings into one place;
the commit message focused on adding hv_status_printk() for debugging.

The previous OOT helper hv_status_to_errno() (in v6.12) preserved more
meaningful errnos for several codes that have natural POSIX counterparts.
The collapse-to-EIO loses information at call sites that key behavior on
the converted errno (for example mshv_vtl_configure_reg_page() whitelists
-EINVAL/-EACCES to fall back gracefully when the host refuses to install
the register-page overlay; with -EIO that call site now hits a BUG()).

Restore the semantically meaningful mappings for the affected statuses:

  HV_STATUS_ACCESS_DENIED                    -EIO  -> -EACCES
  HV_STATUS_OPERATION_DENIED                 -EIO  -> -EACCES
  HV_STATUS_UNKNOWN_PROPERTY                 -EIO  -> -EINVAL
  HV_STATUS_PROPERTY_VALUE_OUT_OF_RANGE      -EIO  -> -EINVAL
  HV_STATUS_PROCESSOR_FEATURE_NOT_SUPPORTED  -EIO  -> -EOPNOTSUPP

EACCES is the canonical errno for permission denied, EINVAL for invalid
argument, and EOPNOTSUPP for unsupported operations. These mappings match
both their <errno.h> definitions and the established meanings in the OOT
v6.12 helper.

Also drop two duplicate _STATUS_INFO() rows for HV_STATUS_INVALID_LP_INDEX
and HV_STATUS_INVALID_REGISTER_VALUE that were leftover from the original
table conversion. find_hv_status_info() is first-match-wins so the dead
rows were not reachable, but they are confusing on review.

Impact analysis
---------------

The change only matters at sites that distinguish between specific errnos
(e.g. `ret == -EIO` vs `ret == -EACCES`). To confirm safety, the entire
kernel tree was audited for equality comparisons against any of the
affected errnos in Hyper-V-touching code:

  $ git grep -nE '(ret|status|err|rc|r)[[:space:]]*==[[:space:]]*\
        -(EACCES|EBADFD|ENOTRECOVERABLE|EOPNOTSUPP|EIO|EINVAL|ENOMEM)' \
    -- drivers/hv/ arch/x86/hyperv/ arch/arm64/hyperv/ \
       drivers/iommu/hyperv-iommu.c

Results in Hyper-V code:

  drivers/hv/hv_balloon.c:    ret == -EEXIST | ret == -EAGAIN  (not in table)
  drivers/hv/mshv_eventfd.c:  ret == -EAGAIN                    (not in table)
  drivers/hv/connection.c:    ret == -ETIMEDOUT                 (not in table)
  drivers/hv/mshv_vtl_main.c: ret == -EINVAL || ret == -EACCES  (the consumer
                              this patch fixes)

No other in-tree code path inspects -EOPNOTSUPP, -EACCES, -EINVAL or -EIO
from a hypercall result to make a behavioral decision. The remaining
HV_STATUS_* codes that 3817854 mapped to -EIO are therefore left as
-EIO; nothing keys behavior off the distinction today.

Signed-off-by: Hardik Garg <hargar@microsoft.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR restores more semantically meaningful Linux errno mappings for specific Hyper-V hypercall status codes in hv_result_to_errno() by adjusting the hv_status_infos[] lookup table, and removes unreachable duplicate table rows introduced during the earlier table conversion.

Changes:

  • Map HV_STATUS_ACCESS_DENIED / HV_STATUS_OPERATION_DENIED to -EACCES instead of -EIO.
  • Map HV_STATUS_UNKNOWN_PROPERTY / HV_STATUS_PROPERTY_VALUE_OUT_OF_RANGE to -EINVAL instead of -EIO, and HV_STATUS_PROCESSOR_FEATURE_NOT_SUPPORTED to -EOPNOTSUPP.
  • Remove duplicate (dead) _STATUS_INFO() entries for HV_STATUS_INVALID_LP_INDEX and HV_STATUS_INVALID_REGISTER_VALUE.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@hargar19

hargar19 commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator Author

Why arm VM fails on v6.18 but not v6.12:

The bug surface: same hypercall, same response, same hardware
On the failing Cobalt 100 ARM VM, both v6.12 and v6.18 issue the identical hypercall and receive the identical response from Hyper-V:

Field Value
Hypercall HvCallSetVpRegisters (0x0051) with count=1
VP HV_VP_INDEX_SELF, VTL=0
Register HV_REGISTER_REG_PAGE (0x9001c)
Value overlay.enabled=1, overlay.pfn=<allocated>
Host advertises intercept_page_available = 1 in HvRegisterVsmCapabilities
Host returns HV_STATUS_ACCESS_DENIED (0x6)

This is a benign per-VP policy denial — the host supports the register-page feature in principle, but refuses the install on this configuration. The register page is an optional performance optimization (lets user-mode skip a hypercall for some VP register accesses); kernels are expected to fall back gracefully.

v6.12: failure handled gracefully
OHCL v6.12 used the in-tree helper hv_status_to_errno() (include/asm-generic/mshyperv.h) which mapped:

case HV_STATUS_ACCESS_DENIED:
case HV_STATUS_OPERATION_DENIED:
    return -EACCES;

The caller mshv_vtl_configure_reg_page() (drivers/hv/mshv_vtl_main.c) whitelists exactly this:

if (ret == -EINVAL || ret == -EACCES) {
    pr_info("not using the register page");
} else {
    pr_emerg("error when setting up the register page: %d\n", ret);
    BUG();
}

So -EACCES lands in the silent-fallback path. v6.12 dmesg on Cobalt 100:
[0.013509] not using the register page (×8, once per CPU)

Kernel boots normally. The host's denial is logged and ignored, hypercall-based path takes over.

v6.18: same response, but mapped to -EIO
Upstream commit 3817854ba892 ("hyperv: Log hypercall status codes as strings") refactored hv_result_to_errno() from a switch statement to a table-driven lookup. The author's stated intent was to add string lookup support for hv_status_printk(); the errno mapping table was a side-effect. The new table preserves the existing -EIO fallback for most codes, including HV_STATUS_ACCESS_DENIED:
_STATUS_INFO(HV_STATUS_ACCESS_DENIED, -EIO),

Result: HV_STATUS_ACCESS_DENIED is now mapped to -EIO, the whitelist misses it, and the code hits BUG():

[0.020022] error when setting up the register page: -5
[0.020031] Kernel BUG at mshv_vtl_alloc_context+0x178/0x210
[0.020036] Internal error: Oops - BUG: 00000000f2000800 [#1]  SMP
[0.020080] Kernel panic - not syncing: Oops - BUG: Fatal exception

-5 is -EIO. BUG() in a CPU hotplug callback during boot → fatal.

Diagnostic instrumentation confirming the diagnosis
Captured on both kernels with debug pr_warns added to mshv_vtl_configure_reg_page() and hv_call_set_vp_registers():

v6.12 (boots):

mshv_vtl_configure_reg_page: cpu=0 ... reg_name=0x9001c overlay.enabled=1 overlay.pfn=0x42205f
                              vsm_caps=0xf8020002 intercept_page_available=1
hv_call_set_vp_registers: ... status=0x6 (HV_STATUS_ACCESS_DENIED) errno=-13
not using the register page

v6.18 (crashes pre-patch):

mshv_vtl_configure_reg_page: cpu=0 ... reg_name=0x9001c overlay.enabled=1 overlay.pfn=0x4221fc
                              vsm_caps=0xf8020002 intercept_page_available=1
hv_call_set_vp_registers: ... status=0x6 (HV_STATUS_ACCESS_DENIED) errno=-5
Kernel BUG at mshv_vtl_alloc_context+0x178/0x210

Identical hypercall, identical raw hypervisor response (0x6), different errno → different kernel reaction.

Impact analysis for this change:

The change only matters at sites that distinguish between specific errnos (e.g. ret == -EIO vs ret == -EACCES). To confirm safety, the entire kernel tree was audited for equality comparisons against any of the affected errnos in Hyper-V-touching code:

$ git grep -nE '(ret|status|err|rc|r)[[:space:]]*==[[:space:]]*\
      -(EACCES|EBADFD|ENOTRECOVERABLE|EOPNOTSUPP|EIO|EINVAL|ENOMEM)' \
  -- drivers/hv/ arch/x86/hyperv/ arch/arm64/hyperv/ \
     drivers/iommu/hyperv-iommu.c

Results in Hyper-V code:

Consumer Expected errno(s) Status
drivers/hv/hv_balloon.c -EEXIST, -EAGAIN Not in table
drivers/hv/mshv_eventfd.c -EAGAIN Not in table
drivers/hv/connection.c -ETIMEDOUT Not in table
drivers/hv/mshv_vtl_main.c -EINVAL, -EACCES Covered by this patch

No other in-tree code path inspects -EOPNOTSUPP, -EACCES, -EINVAL or -EIO from a hypercall result to make a behavioral decision. The remaining HV_STATUS_* codes that 3817854 mapped to -EIO are therefore left as -EIO; nothing keys behavior off the distinction today.

@hargar19 hargar19 marked this pull request as ready for review June 22, 2026 23:46
@namancse

namancse commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Change LGTM, have we run Sashiko or AI agent on this?

Nit, subject: s/"Drivers: hv: hv_common:"/Drivers: hv:"

@namancse namancse left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues with Sashiko, Opus 4.7.

@saurabh-sengar saurabh-sengar left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, please make sure we do proper testing

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants