[v6,18/21] gunyah: rsc_mgr: Add platform ops on mem_lend/mem_reclaim
Commit Message
On Qualcomm platforms, there is a firmware entity which controls access
to physical pages. In order to share memory with another VM, this entity
needs to be informed that the guest VM should have access to the memory.
Co-developed-by: Prakruthi Deepak Heragu <quic_pheragu@quicinc.com>
Signed-off-by: Prakruthi Deepak Heragu <quic_pheragu@quicinc.com>
Signed-off-by: Elliot Berman <quic_eberman@quicinc.com>
---
drivers/virt/gunyah/rsc_mgr.c | 52 +++++++++++++++++++++++++++++++
drivers/virt/gunyah/rsc_mgr.h | 3 ++
drivers/virt/gunyah/rsc_mgr_rpc.c | 7 +++++
include/linux/gunyah_rsc_mgr.h | 12 ++++++-
4 files changed, 73 insertions(+), 1 deletion(-)
@@ -105,6 +105,13 @@ struct gh_rsc_mgr {
static struct gh_rsc_mgr *__rsc_mgr;
SRCU_NOTIFIER_HEAD_STATIC(gh_rm_notifier);
+/* Needs to be out of the gh_rsc_mgr struct as platform_ops might probe before
+ * rsc mgr probes. We can't defer the platform_ops because it might be that
+ * Linux is not a Gunyah guest.
+ */
+static struct gunyah_rm_platform_ops *rm_platform_ops;
+static DECLARE_RWSEM(rm_platform_ops_lock);
+
static struct gh_rm_connection *gh_rm_alloc_connection(u32 msg_id, u8 type)
{
struct gh_rm_connection *connection;
@@ -498,6 +505,51 @@ int gh_rm_call(u32 message_id, void *req_buff, size_t req_buff_size,
return ret;
}
+int gh_rm_platform_pre_mem_share(struct gh_rm_mem_parcel *mem_parcel)
+{
+ int ret = 0;
+
+ down_read(&rm_platform_ops_lock);
+ if (rm_platform_ops && rm_platform_ops->pre_mem_share)
+ ret = rm_platform_ops->pre_mem_share(mem_parcel);
+ up_read(&rm_platform_ops_lock);
+ return ret;
+}
+
+int gh_rm_platform_post_mem_reclaim(struct gh_rm_mem_parcel *mem_parcel)
+{
+ int ret = 0;
+
+ down_read(&rm_platform_ops_lock);
+ if (rm_platform_ops && rm_platform_ops->post_mem_reclaim)
+ ret = rm_platform_ops->post_mem_reclaim(mem_parcel);
+ up_read(&rm_platform_ops_lock);
+ return ret;
+}
+
+int gh_rm_register_platform_ops(struct gunyah_rm_platform_ops *platform_ops)
+{
+ int ret = 0;
+
+ down_write(&rm_platform_ops_lock);
+ if (!rm_platform_ops)
+ rm_platform_ops = platform_ops;
+ else
+ ret = -EEXIST;
+ up_write(&rm_platform_ops_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(gh_rm_register_platform_ops);
+
+void gh_rm_unregister_platform_ops(struct gunyah_rm_platform_ops *platform_ops)
+{
+ down_write(&rm_platform_ops_lock);
+ if (rm_platform_ops == platform_ops)
+ rm_platform_ops = NULL;
+ up_write(&rm_platform_ops_lock);
+}
+EXPORT_SYMBOL_GPL(gh_rm_unregister_platform_ops);
+
int gh_rm_register_notifier(struct notifier_block *nb)
{
return srcu_notifier_chain_register(&gh_rm_notifier, nb);
@@ -130,6 +130,9 @@ struct gh_vm_set_boot_context_req {
int gh_rm_call(u32 message_id, void *req_buff, size_t req_buff_size,
void **resp_buf, size_t *resp_buff_size);
+int gh_rm_platform_pre_mem_share(struct gh_rm_mem_parcel *mem_parcel);
+int gh_rm_platform_post_mem_reclaim(struct gh_rm_mem_parcel *mem_parcel);
+
struct gh_rm_device {
struct device dev;
const char *name;
@@ -120,6 +120,10 @@ static int gh_rm_mem_lend_common(u32 message_id, struct gh_rm_mem_parcel *p)
if (initial_n_mem_entries > GH_RM_MAX_MEM_ENTRIES)
initial_n_mem_entries = GH_RM_MAX_MEM_ENTRIES;
+ ret = gh_rm_platform_pre_mem_share(p);
+ if (ret)
+ return ret;
+
/* The format of the message goes:
* request header
* ACL entries (which VMs get what kind of access to this memory parcel)
@@ -163,6 +167,7 @@ static int gh_rm_mem_lend_common(u32 message_id, struct gh_rm_mem_parcel *p)
if (resp_size != sizeof(u32)) {
ret = -EIO;
+ gh_rm_platform_post_mem_reclaim(p);
goto out;
}
@@ -234,6 +239,8 @@ int gh_rm_mem_reclaim(struct gh_rm_mem_parcel *parcel)
if (resp_size)
pr_warn("Received unexpected payload for MEM_RECLAIM: %lu\n", resp_size);
+ ret = gh_rm_platform_post_mem_reclaim(parcel);
+
return ret;
}
EXPORT_SYMBOL_GPL(gh_rm_mem_reclaim);
@@ -119,7 +119,6 @@ struct gh_rm_hyp_resource {
} __packed;
ssize_t gh_rm_get_hyp_resources(u16 vmid, struct gh_rm_hyp_resource **resources);
-int gh_rm_get_vmid(u16 *vmid);
#define GH_RM_BOOT_CONTEXT_REG_SET_REGISTERS 0
#define GH_RM_BOOT_CONTEXT_REG_SET_PC 1
@@ -143,4 +142,15 @@ void gh_rm_driver_unregister(struct gh_rm_driver *ghrm_drv);
#define module_gh_rm_driver(ghrm_drv) \
module_driver(ghrm_drv, gh_rm_driver_register, gh_rm_driver_unregister)
+#if IS_ENABLED(CONFIG_GUNYAH)
+int gh_rm_register_platform_ops(struct gunyah_rm_platform_ops *platform_ops);
+void gh_rm_unregister_platform_ops(struct gunyah_rm_platform_ops *platform_ops);
+int gh_rm_get_vmid(u16 *vmid);
+#else
+static inline int gh_rm_register_platform_ops(struct gunyah_rm_platform_ops *platform_ops)
+ { return 0; }
+static inline void gh_rm_unregister_platform_ops(struct gunyah_rm_platform_ops *platform_ops) { }
+static inline int gh_rm_get_vmid(u16 *vmid) { return -ENODEV; }
+#endif
+
#endif