Unlike VFIO/IOMMUFD paging domains, external attach bypasses the DMA map flow that pins mapped memory. The ownership model is also different for MSHV external attach: MSHV, as the L0 hypervisor, owns address translation and DMA fault handling.
Guest memory must therefore be pinned through the MSHV/userspace VMM contract before it is mapped for DMA, rather than by the VFIO/IOMMUFD pin-and-map flow. Add a partition-file prepare_attach callback that lets MSHV apply its guest-memory pinning policy and perform any other preparation required before an external device is attached. A later external-domain patch invokes the callback through the vIOMMU-held VM file. The per-region conversion helper is intentionally left as a stub for this RFC to show where MSHV will pin guest memory. Assisted-by: GPT-5.6 Sol Signed-off-by: Jacob Pan <[email protected]> --- drivers/hv/hv_common.c | 24 ++++++++++++++++++++++- drivers/hv/mshv_regions.c | 6 ++++++ drivers/hv/mshv_root.h | 1 + drivers/hv/mshv_root_main.c | 35 ++++++++++++++++++++++++++++++++++ include/asm-generic/mshyperv.h | 7 +++++++ 5 files changed, 72 insertions(+), 1 deletion(-) diff --git a/drivers/hv/hv_common.c b/drivers/hv/hv_common.c index d259cb833376..7acea1bceae1 100644 --- a/drivers/hv/hv_common.c +++ b/drivers/hv/hv_common.c @@ -46,7 +46,8 @@ int mshv_partition_file_ops_register(const struct mshv_partition_file_ops *ops) { int ret = 0; - if (!ops || !ops->file_is_partition || !ops->get_partid) + if (!ops || !ops->file_is_partition || !ops->get_partid || + !ops->prepare_attach) return -EINVAL; mutex_lock(&mshv_partition_file_ops_lock); @@ -106,6 +107,27 @@ u64 mshv_partition_file_get_partid(struct file *file) } EXPORT_SYMBOL_GPL(mshv_partition_file_get_partid); +int mshv_partition_file_prepare_attach(struct file *file) +{ + const struct mshv_partition_file_ops *ops; + int ret = -EOPNOTSUPP; + + if (!file) + return -EINVAL; + + mutex_lock(&mshv_partition_file_ops_lock); + ops = mshv_partition_file_ops; + if (ops && ops->file_is_partition(file)) + ret = 0; + mutex_unlock(&mshv_partition_file_ops_lock); + + if (!ret) + ret = ops->prepare_attach(file); + + return ret; +} +EXPORT_SYMBOL_GPL(mshv_partition_file_prepare_attach); + /* * ms_hyperv and hv_nested are defined here with other * Hyper-V specific globals so they are shared across all architectures and are diff --git a/drivers/hv/mshv_regions.c b/drivers/hv/mshv_regions.c index dddaade31b5d..b98e5c3e7819 100644 --- a/drivers/hv/mshv_regions.c +++ b/drivers/hv/mshv_regions.c @@ -324,6 +324,12 @@ int mshv_region_pin(struct mshv_mem_region *region) return ret < 0 ? ret : -ENOMEM; } +int mshv_region_make_pinned(struct mshv_mem_region *region) +{ + /* Guest-memory pin conversion is not implemented in this RFC. */ + return 0; +} + static int mshv_region_chunk_unmap(struct mshv_mem_region *region, u32 flags, u64 page_offset, u64 page_count, diff --git a/drivers/hv/mshv_root.h b/drivers/hv/mshv_root.h index d57c26950203..d0f3a28abb3b 100644 --- a/drivers/hv/mshv_root.h +++ b/drivers/hv/mshv_root.h @@ -393,6 +393,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); +int mshv_region_make_pinned(struct mshv_mem_region *region); void mshv_region_put(struct mshv_mem_region *region); int mshv_region_get(struct mshv_mem_region *region); bool mshv_region_handle_gfn_fault(struct mshv_mem_region *region, u64 gfn); diff --git a/drivers/hv/mshv_root_main.c b/drivers/hv/mshv_root_main.c index 838ea6397c9f..fb422ed23662 100644 --- a/drivers/hv/mshv_root_main.c +++ b/drivers/hv/mshv_root_main.c @@ -2182,9 +2182,44 @@ static u64 mshv_partition_file_get_partid_impl(struct file *file) return HV_PARTITION_ID_INVALID; } +static int mshv_partition_file_prepare_attach_impl(struct file *file) +{ + struct mshv_mem_region *region; + struct mshv_partition *partition; + int ret = 0; + +#if IS_ENABLED(CONFIG_IOMMUFD_TEST) + if (file->f_op == &mshv_fake_partition_fops) + return 0; +#endif + if (file->f_op != &mshv_partition_fops) + return -EINVAL; + + partition = file->private_data; + if (!partition) + return -EINVAL; + + mutex_lock(&partition->pt_mutex); + if (partition->pt_regions_pinned) + goto out_unlock; + + hlist_for_each_entry(region, &partition->pt_mem_regions, hnode) { + ret = mshv_region_make_pinned(region); + if (ret) + goto out_unlock; + } + + partition->pt_regions_pinned = true; + +out_unlock: + mutex_unlock(&partition->pt_mutex); + return ret; +} + static const struct mshv_partition_file_ops mshv_partition_file_ops = { .file_is_partition = mshv_partition_file_is_valid, .get_partid = mshv_partition_file_get_partid_impl, + .prepare_attach = mshv_partition_file_prepare_attach_impl, }; static int diff --git a/include/asm-generic/mshyperv.h b/include/asm-generic/mshyperv.h index ed7ab21d68a2..6af793444a4b 100644 --- a/include/asm-generic/mshyperv.h +++ b/include/asm-generic/mshyperv.h @@ -405,6 +405,7 @@ struct file; struct mshv_partition_file_ops { bool (*file_is_partition)(struct file *file); u64 (*get_partid)(struct file *file); + int (*prepare_attach)(struct file *file); }; #if IS_ENABLED(CONFIG_HYPERV) @@ -413,6 +414,7 @@ void mshv_partition_file_ops_unregister(const struct mshv_partition_file_ops *ops); bool file_is_mshv_partition(struct file *file); u64 mshv_partition_file_get_partid(struct file *file); +int mshv_partition_file_prepare_attach(struct file *file); #else static inline int mshv_partition_file_ops_register(const struct mshv_partition_file_ops *ops) @@ -434,6 +436,11 @@ static inline u64 mshv_partition_file_get_partid(struct file *file) { return HV_PARTITION_ID_INVALID; } + +static inline int mshv_partition_file_prepare_attach(struct file *file) +{ + return -EOPNOTSUPP; +} #endif static inline int hv_deposit_memory(u64 partition_id, u64 status) -- 2.43.0

