Commit 3817854b authored by Nuno Das Neves's avatar Nuno Das Neves Committed by Wei Liu
Browse files

hyperv: Log hypercall status codes as strings



Introduce hv_status_printk() macros as a convenience to log hypercall
errors, formatting them with the status code (HV_STATUS_*) as a raw hex
value and also as a string, which saves some time while debugging.

Create a table of HV_STATUS_ codes with strings and mapped errnos, and
use it for hv_result_to_string() and hv_result_to_errno().

Use the new hv_status_printk()s in hv_proc.c, hyperv-iommu.c, and
irqdomain.c hypercalls to aid debugging in the root partition.

Signed-off-by: default avatarNuno Das Neves <nunodasneves@linux.microsoft.com>
Reviewed-by: default avatarStanislav Kinsburskii <skinsburskii@linux.microsoft.com>
Link: https://lore.kernel.org/r/1741980536-3865-2-git-send-email-nunodasneves@linux.microsoft.com


Signed-off-by: default avatarWei Liu <wei.liu@kernel.org>
Message-ID: <1741980536-3865-2-git-send-email-nunodasneves@linux.microsoft.com>
parent e792d843
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -64,7 +64,7 @@ static int hv_map_interrupt(union hv_device_id device_id, bool level,
	local_irq_restore(flags);

	if (!hv_result_success(status))
		pr_err("%s: hypercall failed, status %lld\n", __func__, status);
		hv_status_err(status, "\n");

	return hv_result(status);
}
@@ -224,7 +224,7 @@ static void hv_irq_compose_msi_msg(struct irq_data *data, struct msi_msg *msg)
		kfree(stored_entry);

		if (status != HV_STATUS_SUCCESS) {
			pr_debug("%s: failed to unmap, status %lld", __func__, status);
			hv_status_debug(status, "failed to unmap\n");
			return;
		}
	}
@@ -273,7 +273,7 @@ static void hv_teardown_msi_irq(struct pci_dev *dev, struct irq_data *irqd)
	status = hv_unmap_msi_interrupt(dev, &old_entry);

	if (status != HV_STATUS_SUCCESS)
		pr_err("%s: hypercall failed, status %lld\n", __func__, status);
		hv_status_err(status, "\n");
}

static void hv_msi_free_irq(struct irq_domain *domain,
+95 −34
Original line number Diff line number Diff line
@@ -684,40 +684,6 @@ u64 __weak hv_tdx_hypercall(u64 control, u64 param1, u64 param2)
}
EXPORT_SYMBOL_GPL(hv_tdx_hypercall);

/* Convert a hypercall result into a linux-friendly error code. */
int hv_result_to_errno(u64 status)
{
	/* hv_do_hypercall() may return U64_MAX, hypercalls aren't possible */
	if (unlikely(status == U64_MAX))
		return -EOPNOTSUPP;
	/*
	 * A failed hypercall is usually only recoverable (or loggable) near
	 * the call site where the HV_STATUS_* code is known. So the errno
	 * it gets converted to is not too useful further up the stack.
	 * Provide a few mappings that could be useful, and revert to -EIO
	 * as a fallback.
	 */
	switch (hv_result(status)) {
	case HV_STATUS_SUCCESS:
		return 0;
	case HV_STATUS_INVALID_HYPERCALL_CODE:
	case HV_STATUS_INVALID_HYPERCALL_INPUT:
	case HV_STATUS_INVALID_PARAMETER:
	case HV_STATUS_INVALID_PARTITION_ID:
	case HV_STATUS_INVALID_VP_INDEX:
	case HV_STATUS_INVALID_PORT_ID:
	case HV_STATUS_INVALID_CONNECTION_ID:
	case HV_STATUS_INVALID_LP_INDEX:
	case HV_STATUS_INVALID_REGISTER_VALUE:
		return -EINVAL;
	case HV_STATUS_INSUFFICIENT_MEMORY:
		return -ENOMEM;
	default:
		break;
	}
	return -EIO;
}

void hv_identify_partition_type(void)
{
	/* Assume guest role */
@@ -740,3 +706,98 @@ void hv_identify_partition_type(void)
			pr_crit("Hyper-V: CONFIG_MSHV_ROOT not enabled!\n");
	}
}

struct hv_status_info {
	char *string;
	int errno;
	u16 code;
};

/*
 * Note on the errno mappings:
 * A failed hypercall is usually only recoverable (or loggable) near
 * the call site where the HV_STATUS_* code is known. So the errno
 * it gets converted to is not too useful further up the stack.
 * Provide a few mappings that could be useful, and revert to -EIO
 * as a fallback.
 */
static const struct hv_status_info hv_status_infos[] = {
#define _STATUS_INFO(status, errno) { #status, (errno), (status) }
	_STATUS_INFO(HV_STATUS_SUCCESS,				0),
	_STATUS_INFO(HV_STATUS_INVALID_HYPERCALL_CODE,		-EINVAL),
	_STATUS_INFO(HV_STATUS_INVALID_HYPERCALL_INPUT,		-EINVAL),
	_STATUS_INFO(HV_STATUS_INVALID_ALIGNMENT,		-EIO),
	_STATUS_INFO(HV_STATUS_INVALID_PARAMETER,		-EINVAL),
	_STATUS_INFO(HV_STATUS_ACCESS_DENIED,			-EIO),
	_STATUS_INFO(HV_STATUS_INVALID_PARTITION_STATE,		-EIO),
	_STATUS_INFO(HV_STATUS_OPERATION_DENIED,		-EIO),
	_STATUS_INFO(HV_STATUS_UNKNOWN_PROPERTY,		-EIO),
	_STATUS_INFO(HV_STATUS_PROPERTY_VALUE_OUT_OF_RANGE,	-EIO),
	_STATUS_INFO(HV_STATUS_INSUFFICIENT_MEMORY,		-ENOMEM),
	_STATUS_INFO(HV_STATUS_INVALID_PARTITION_ID,		-EINVAL),
	_STATUS_INFO(HV_STATUS_INVALID_VP_INDEX,		-EINVAL),
	_STATUS_INFO(HV_STATUS_NOT_FOUND,			-EIO),
	_STATUS_INFO(HV_STATUS_INVALID_PORT_ID,			-EINVAL),
	_STATUS_INFO(HV_STATUS_INVALID_CONNECTION_ID,		-EINVAL),
	_STATUS_INFO(HV_STATUS_INSUFFICIENT_BUFFERS,		-EIO),
	_STATUS_INFO(HV_STATUS_NOT_ACKNOWLEDGED,		-EIO),
	_STATUS_INFO(HV_STATUS_INVALID_VP_STATE,		-EIO),
	_STATUS_INFO(HV_STATUS_NO_RESOURCES,			-EIO),
	_STATUS_INFO(HV_STATUS_PROCESSOR_FEATURE_NOT_SUPPORTED,	-EIO),
	_STATUS_INFO(HV_STATUS_INVALID_LP_INDEX,		-EINVAL),
	_STATUS_INFO(HV_STATUS_INVALID_REGISTER_VALUE,		-EINVAL),
	_STATUS_INFO(HV_STATUS_INVALID_LP_INDEX,		-EIO),
	_STATUS_INFO(HV_STATUS_INVALID_REGISTER_VALUE,		-EIO),
	_STATUS_INFO(HV_STATUS_OPERATION_FAILED,		-EIO),
	_STATUS_INFO(HV_STATUS_TIME_OUT,			-EIO),
	_STATUS_INFO(HV_STATUS_CALL_PENDING,			-EIO),
	_STATUS_INFO(HV_STATUS_VTL_ALREADY_ENABLED,		-EIO),
#undef _STATUS_INFO
};

static inline const struct hv_status_info *find_hv_status_info(u64 hv_status)
{
	int i;
	u16 code = hv_result(hv_status);

	for (i = 0; i < ARRAY_SIZE(hv_status_infos); ++i) {
		const struct hv_status_info *info = &hv_status_infos[i];

		if (info->code == code)
			return info;
	}

	return NULL;
}

/* Convert a hypercall result into a linux-friendly error code. */
int hv_result_to_errno(u64 status)
{
	const struct hv_status_info *info;

	/* hv_do_hypercall() may return U64_MAX, hypercalls aren't possible */
	if (unlikely(status == U64_MAX))
		return -EOPNOTSUPP;

	info = find_hv_status_info(status);
	if (info)
		return info->errno;

	return -EIO;
}
EXPORT_SYMBOL_GPL(hv_result_to_errno);

const char *hv_result_to_string(u64 status)
{
	const struct hv_status_info *info;

	if (unlikely(status == U64_MAX))
		return "Hypercall page missing!";

	info = find_hv_status_info(status);
	if (info)
		return info->string;

	return "Unknown";
}
EXPORT_SYMBOL_GPL(hv_result_to_string);
+5 −5
Original line number Diff line number Diff line
@@ -87,7 +87,7 @@ int hv_call_deposit_pages(int node, u64 partition_id, u32 num_pages)
				     page_count, 0, input_page, NULL);
	local_irq_restore(flags);
	if (!hv_result_success(status)) {
		pr_err("Failed to deposit pages: %lld\n", status);
		hv_status_err(status, "\n");
		ret = hv_result_to_errno(status);
		goto err_free_allocations;
	}
@@ -137,8 +137,8 @@ int hv_call_add_logical_proc(int node, u32 lp_index, u32 apic_id)

		if (hv_result(status) != HV_STATUS_INSUFFICIENT_MEMORY) {
			if (!hv_result_success(status)) {
				pr_err("%s: cpu %u apic ID %u, %lld\n", __func__,
				       lp_index, apic_id, status);
				hv_status_err(status, "cpu %u apic ID: %u\n",
					      lp_index, apic_id);
				ret = hv_result_to_errno(status);
			}
			break;
@@ -179,8 +179,8 @@ int hv_call_create_vp(int node, u64 partition_id, u32 vp_index, u32 flags)

		if (hv_result(status) != HV_STATUS_INSUFFICIENT_MEMORY) {
			if (!hv_result_success(status)) {
				pr_err("%s: vcpu %u, lp %u, %lld\n", __func__,
				       vp_index, flags, status);
				hv_status_err(status, "vcpu: %u, lp: %u\n",
					      vp_index, flags);
				ret = hv_result_to_errno(status);
			}
			break;
+2 −2
Original line number Diff line number Diff line
@@ -217,7 +217,7 @@ hyperv_root_ir_compose_msi_msg(struct irq_data *irq_data, struct msi_msg *msg)
		status = hv_unmap_ioapic_interrupt(ioapic_id, &entry);

		if (status != HV_STATUS_SUCCESS)
			pr_debug("%s: unexpected unmap status %lld\n", __func__, status);
			hv_status_debug(status, "failed to unmap\n");

		data->entry.ioapic_rte.as_uint64 = 0;
		data->entry.source = 0; /* Invalid source */
@@ -228,7 +228,7 @@ hyperv_root_ir_compose_msi_msg(struct irq_data *irq_data, struct msi_msg *msg)
					vector, &entry);

	if (status != HV_STATUS_SUCCESS) {
		pr_err("%s: map hypercall failed, status %lld\n", __func__, status);
		hv_status_err(status, "map failed\n");
		return;
	}

+13 −0
Original line number Diff line number Diff line
@@ -298,6 +298,19 @@ static inline int cpumask_to_vpset_skip(struct hv_vpset *vpset,
	return __cpumask_to_vpset(vpset, cpus, func);
}

#define _hv_status_fmt(fmt) "%s: Hyper-V status: %#x = %s: " fmt
#define hv_status_printk(level, status, fmt, ...) \
do { \
	u64 __status = (status); \
	pr_##level(_hv_status_fmt(fmt), __func__, hv_result(__status), \
		   hv_result_to_string(__status), ##__VA_ARGS__); \
} while (0)
#define hv_status_err(status, fmt, ...) \
	hv_status_printk(err, status, fmt, ##__VA_ARGS__)
#define hv_status_debug(status, fmt, ...) \
	hv_status_printk(debug, status, fmt, ##__VA_ARGS__)

const char *hv_result_to_string(u64 hv_status);
int hv_result_to_errno(u64 status);
void hyperv_report_panic(struct pt_regs *regs, long err, bool in_die);
bool hv_is_hyperv_initialized(void);