From c9f45eda48929d8cd72b1617d7b0c86255e91f13 Mon Sep 17 00:00:00 2001 From: Rachel Menge Date: Thu, 14 Sep 2023 14:45:02 -0400 Subject: [PATCH 1/2] drivers: hv: introduce new api for channel callbacks --- drivers/hv/channel.c | 187 +++++++++++++++++++++++++++++++++++++++- drivers/hv/connection.c | 19 ++-- drivers/hv/vmbus_drv.c | 20 +++-- include/linux/hyperv.h | 21 ++++- 4 files changed, 229 insertions(+), 18 deletions(-) diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 56f7e06c673e42..16ddf82ee42fcc 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -661,6 +661,148 @@ static int __vmbus_open(struct vmbus_channel *newchannel, unsigned long flags; int err; + newchannel->old_callback_api = true; + + if (userdatalen > MAX_USER_DEFINED_BYTES) + return -EINVAL; + + send_pages = newchannel->ringbuffer_send_offset; + recv_pages = newchannel->ringbuffer_pagecount - send_pages; + + if (newchannel->state != CHANNEL_OPEN_STATE) + return -EINVAL; + + /* Create and init requestor */ + if (newchannel->rqstor_size) { + if (vmbus_alloc_requestor(&newchannel->requestor, newchannel->rqstor_size)) + return -ENOMEM; + } + + newchannel->state = CHANNEL_OPENING_STATE; + newchannel->onchannel_callback_old = (void(*)(void*))onchannelcallback; + newchannel->channel_callback_context = context; + + if (!newchannel->max_pkt_size) + newchannel->max_pkt_size = VMBUS_DEFAULT_MAX_PKT_SIZE; + + /* Establish the gpadl for the ring buffer */ + newchannel->ringbuffer_gpadlhandle.gpadl_handle = 0; + + err = __vmbus_establish_gpadl(newchannel, HV_GPADL_RING, + page_address(newchannel->ringbuffer_page), + (send_pages + recv_pages) << PAGE_SHIFT, + newchannel->ringbuffer_send_offset << PAGE_SHIFT, + &newchannel->ringbuffer_gpadlhandle); + if (err) + goto error_clean_ring; + + err = hv_ringbuffer_init(&newchannel->outbound, + page, send_pages, 0); + if (err) + goto error_free_gpadl; + + err = hv_ringbuffer_init(&newchannel->inbound, &page[send_pages], + recv_pages, newchannel->max_pkt_size); + if (err) + goto error_free_gpadl; + + /* Create and init the channel open message */ + open_info = kzalloc(sizeof(*open_info) + + sizeof(struct vmbus_channel_open_channel), + GFP_KERNEL); + if (!open_info) { + err = -ENOMEM; + goto error_free_gpadl; + } + + init_completion(&open_info->waitevent); + open_info->waiting_channel = newchannel; + + open_msg = (struct vmbus_channel_open_channel *)open_info->msg; + open_msg->header.msgtype = CHANNELMSG_OPENCHANNEL; + open_msg->openid = newchannel->offermsg.child_relid; + open_msg->child_relid = newchannel->offermsg.child_relid; + open_msg->ringbuffer_gpadlhandle + = newchannel->ringbuffer_gpadlhandle.gpadl_handle; + /* + * The unit of ->downstream_ringbuffer_pageoffset is HV_HYP_PAGE and + * the unit of ->ringbuffer_send_offset (i.e. send_pages) is PAGE, so + * here we calculate it into HV_HYP_PAGE. + */ + open_msg->downstream_ringbuffer_pageoffset = + hv_ring_gpadl_send_hvpgoffset(send_pages << PAGE_SHIFT); + open_msg->target_vp = hv_cpu_number_to_vp_number(newchannel->target_cpu); + + if (userdatalen) + memcpy(open_msg->userdata, userdata, userdatalen); + + spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); + list_add_tail(&open_info->msglistentry, + &vmbus_connection.chn_msg_list); + spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); + + if (newchannel->rescind) { + err = -ENODEV; + goto error_clean_msglist; + } + + err = vmbus_post_msg(open_msg, + sizeof(struct vmbus_channel_open_channel), true); + + trace_vmbus_open(open_msg, err); + + if (err != 0) + goto error_clean_msglist; + + wait_for_completion(&open_info->waitevent); + + spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); + list_del(&open_info->msglistentry); + spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); + + if (newchannel->rescind) { + err = -ENODEV; + goto error_free_info; + } + + if (open_info->response.open_result.status) { + err = -EAGAIN; + goto error_free_info; + } + + newchannel->state = CHANNEL_OPENED_STATE; + kfree(open_info); + return 0; + +error_clean_msglist: + spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); + list_del(&open_info->msglistentry); + spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); +error_free_info: + kfree(open_info); +error_free_gpadl: + vmbus_teardown_gpadl(newchannel, &newchannel->ringbuffer_gpadlhandle); +error_clean_ring: + hv_ringbuffer_cleanup(&newchannel->outbound); + hv_ringbuffer_cleanup(&newchannel->inbound); + vmbus_free_requestor(&newchannel->requestor); + newchannel->state = CHANNEL_OPEN_STATE; + return err; +} + +static int __vmbus_open_channel(struct vmbus_channel *newchannel, + void *userdata, u32 userdatalen, + void (*onchannelcallback)(struct vmbus_channel *, void *context), void *context) +{ + struct vmbus_channel_open_channel *open_msg; + struct vmbus_channel_msginfo *open_info = NULL; + struct page *page = newchannel->ringbuffer_page; + u32 send_pages, recv_pages; + unsigned long flags; + int err; + + newchannel->old_callback_api = false; + if (userdatalen > MAX_USER_DEFINED_BYTES) return -EINVAL; @@ -677,7 +819,7 @@ static int __vmbus_open(struct vmbus_channel *newchannel, } newchannel->state = CHANNEL_OPENING_STATE; - newchannel->onchannel_callback = onchannelcallback; + newchannel->onchannel_callback = (void(*)(struct vmbus_channel *, void*))onchannelcallback; newchannel->channel_callback_context = context; if (!newchannel->max_pkt_size) @@ -798,13 +940,23 @@ int vmbus_connect_ring(struct vmbus_channel *newchannel, } EXPORT_SYMBOL_GPL(vmbus_connect_ring); +/* + * vmbus_connect_ring - Open the channel but reuse ring buffer + */ +int vmbus_channel_connect_ring(struct vmbus_channel *newchannel, + void (*onchannelcallback)(struct vmbus_channel *, void *context), void *context) +{ + return __vmbus_open_channel(newchannel, NULL, 0, onchannelcallback, context); +} +EXPORT_SYMBOL_GPL(vmbus_channel_connect_ring); + /* * vmbus_open - Open the specified channel. */ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, u32 recv_ringbuffer_size, void *userdata, u32 userdatalen, - void (*onchannelcallback)(void *context), void *context) + void (*onchannelcallback_old)(void *context), void *context) { int err; @@ -814,7 +966,7 @@ int vmbus_open(struct vmbus_channel *newchannel, return err; err = __vmbus_open(newchannel, userdata, userdatalen, - onchannelcallback, context); + onchannelcallback_old, context); if (err) vmbus_free_ring(newchannel); @@ -822,6 +974,30 @@ int vmbus_open(struct vmbus_channel *newchannel, } EXPORT_SYMBOL_GPL(vmbus_open); +/* + * vmbus_open - Open the specified channel. + */ +int vmbus_open_channel(struct vmbus_channel *newchannel, + u32 send_ringbuffer_size, u32 recv_ringbuffer_size, + void *userdata, u32 userdatalen, + void (*onchannelcallback)(struct vmbus_channel *, void *context), void *context) +{ + int err; + + err = vmbus_alloc_ring(newchannel, send_ringbuffer_size, + recv_ringbuffer_size); + if (err) + return err; + + err = __vmbus_open_channel(newchannel, userdata, userdatalen, + onchannelcallback, context); + if (err) + vmbus_free_ring(newchannel); + + return err; +} +EXPORT_SYMBOL_GPL(vmbus_open_channel); + /* * vmbus_teardown_gpadl -Teardown the specified GPADL handle */ @@ -911,7 +1087,10 @@ void vmbus_reset_channel_cb(struct vmbus_channel *channel) /* See the inline comments in vmbus_chan_sched(). */ spin_lock_irqsave(&channel->sched_lock, flags); - channel->onchannel_callback = NULL; + if (channel->old_callback_api) + channel->onchannel_callback_old = NULL; + else + channel->onchannel_callback = NULL; spin_unlock_irqrestore(&channel->sched_lock, flags); channel->sc_creation_callback = NULL; diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index ebf15f31d97e30..3b410a1c6b23ad 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c @@ -377,7 +377,8 @@ struct vmbus_channel *relid2channel(u32 relid) void vmbus_on_event(unsigned long data) { struct vmbus_channel *channel = (void *) data; - void (*callback_fn)(void *context); + void (*callback_fn_old_api)(void *context); + void (*callback_fn)(struct vmbus_channel *chan, void *context); trace_vmbus_on_event(channel); @@ -387,11 +388,17 @@ void vmbus_on_event(unsigned long data) * there is no driver handling the device. An * unloading driver sets the onchannel_callback to NULL. */ - callback_fn = READ_ONCE(channel->onchannel_callback); - if (unlikely(!callback_fn)) - return; - - (*callback_fn)(channel->channel_callback_context); + if (channel->old_callback_api) { + callback_fn_old_api = READ_ONCE(channel->onchannel_callback_old); + if (unlikely(!callback_fn_old_api)) + return; + (*callback_fn_old_api)(channel->channel_callback_context); + } else { + callback_fn = READ_ONCE(channel->onchannel_callback); + if (unlikely(!callback_fn)) + return; + (*callback_fn)(channel, channel->channel_callback_context); + } if (channel->callback_mode != HV_CALL_BATCHED) return; diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index 67f95a29aeca5b..d7b71d85d4be10 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -1224,7 +1224,8 @@ static void vmbus_chan_sched(struct hv_per_cpu_context *hv_cpu) return; for_each_set_bit(relid, recv_int_page, maxbits) { - void (*callback_fn)(void *context); + void (*callback_fn_old_api)(void *context); + void (*callback_fn)(struct vmbus_channel *chan, void *context); struct vmbus_channel *channel; if (!sync_test_and_clear_bit(relid, recv_int_page)) @@ -1259,9 +1260,15 @@ static void vmbus_chan_sched(struct hv_per_cpu_context *hv_cpu) */ spin_lock(&channel->sched_lock); - callback_fn = channel->onchannel_callback; - if (unlikely(callback_fn == NULL)) - goto sched_unlock; + if (channel->old_callback_api) { + callback_fn_old_api = channel->onchannel_callback_old; + if (unlikely(callback_fn_old_api == NULL)) + goto sched_unlock; + } else { + callback_fn = channel->onchannel_callback; + if (unlikely(callback_fn == NULL)) + goto sched_unlock; + } trace_vmbus_chan_sched(channel); @@ -1269,7 +1276,10 @@ static void vmbus_chan_sched(struct hv_per_cpu_context *hv_cpu) switch (channel->callback_mode) { case HV_CALL_ISR: - (*callback_fn)(channel->channel_callback_context); + if (channel->old_callback_api) + (*callback_fn_old_api)(channel->channel_callback_context); + else + (*callback_fn)(channel, channel->channel_callback_context); break; case HV_CALL_BATCHED: diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 3ac3974b3c7819..3a98f79fb592a1 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -863,7 +863,11 @@ struct vmbus_channel { /* Channel callback's invoked in softirq context */ struct tasklet_struct callback_event; - void (*onchannel_callback)(void *context); + bool old_callback_api; + union { + void (*onchannel_callback_old)(void *context); + void (*onchannel_callback)(struct vmbus_channel *chan, void *context); + }; void *channel_callback_context; void (*change_target_cpu_callback)(struct vmbus_channel *channel, @@ -1175,7 +1179,10 @@ int vmbus_alloc_ring(struct vmbus_channel *channel, void vmbus_free_ring(struct vmbus_channel *channel); int vmbus_connect_ring(struct vmbus_channel *channel, - void (*onchannel_callback)(void *context), + void (*onchannel_callback_old)(void *context), + void *context); +int vmbus_channel_connect_ring(struct vmbus_channel *channel, + void (*onchannel_callback)(struct vmbus_channel *, void *context), void *context); int vmbus_disconnect_ring(struct vmbus_channel *channel); @@ -1184,7 +1191,15 @@ extern int vmbus_open(struct vmbus_channel *channel, u32 recv_ringbuffersize, void *userdata, u32 userdatalen, - void (*onchannel_callback)(void *context), + void (*onchannel_callback_old)(void *context), + void *context); + +extern int vmbus_open_channel(struct vmbus_channel *channel, + u32 send_ringbuffersize, + u32 recv_ringbuffersize, + void *userdata, + u32 userdatalen, + void (*onchannel_callback)(struct vmbus_channel *chan, void *context), void *context); extern void vmbus_close(struct vmbus_channel *channel); From 1a9399bb73069ed53fa52e24d890cbc40f3f0f1f Mon Sep 17 00:00:00 2001 From: Rachel Menge Date: Thu, 14 Sep 2023 14:45:25 -0400 Subject: [PATCH 2/2] drivers: hv: use new api for channel callbacks with hv drivers --- drivers/hv/hv_balloon.c | 6 +++--- drivers/hv/hv_fcopy.c | 2 +- drivers/hv/hv_kvp.c | 2 +- drivers/hv/hv_snapshot.c | 2 +- drivers/hv/hv_util.c | 16 ++++++++-------- drivers/hv/hyperv_vmbus.h | 6 +++--- include/linux/hyperv.h | 2 +- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index 0d7a3ba6639649..cadcd7d14e1421 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -1411,7 +1411,7 @@ static void balloon_down(struct hv_dynmem_device *dm, dm->state = DM_INITIALIZED; } -static void balloon_onchannelcallback(void *context); +static void balloon_onchannelcallback(struct vmbus_channel *chan, void *context); static int dm_thread_func(void *dm_dev) { @@ -1515,7 +1515,7 @@ static void cap_resp(struct hv_dynmem_device *dm, complete(&dm->host_event); } -static void balloon_onchannelcallback(void *context) +static void balloon_onchannelcallback(struct vmbus_channel *chan, void *context) { struct hv_device *dev = context; u32 recvlen; @@ -1774,7 +1774,7 @@ static int balloon_connect_vsp(struct hv_device *dev) */ dev->channel->max_pkt_size = HV_HYP_PAGE_SIZE * 2; - ret = vmbus_open(dev->channel, dm_ring_size, dm_ring_size, NULL, 0, + ret = vmbus_open_channel(dev->channel, dm_ring_size, dm_ring_size, NULL, 0, balloon_onchannelcallback, dev); if (ret) return ret; diff --git a/drivers/hv/hv_fcopy.c b/drivers/hv/hv_fcopy.c index 922d83eb7ddfac..ab6751d113c86b 100644 --- a/drivers/hv/hv_fcopy.c +++ b/drivers/hv/hv_fcopy.c @@ -223,7 +223,7 @@ fcopy_respond_to_host(int error) VM_PKT_DATA_INBAND, 0); } -void hv_fcopy_onchannelcallback(void *context) +void hv_fcopy_onchannelcallback(struct vmbus_channel *chan, void *context) { struct vmbus_channel *channel = context; u32 recvlen; diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index d35b60c0611486..e84cde32c874ab 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c @@ -632,7 +632,7 @@ kvp_respond_to_host(struct hv_kvp_msg *msg_to_host, int error) * we stash away the transaction state in a set of global variables. */ -void hv_kvp_onchannelcallback(void *context) +void hv_kvp_onchannelcallback(struct vmbus_channel *chan, void *context) { struct vmbus_channel *channel = context; u32 recvlen; diff --git a/drivers/hv/hv_snapshot.c b/drivers/hv/hv_snapshot.c index 0d2184be169125..f6b403ae9592fc 100644 --- a/drivers/hv/hv_snapshot.c +++ b/drivers/hv/hv_snapshot.c @@ -288,7 +288,7 @@ vss_respond_to_host(int error) * The host ensures that only one VSS transaction can be active at a time. */ -void hv_vss_onchannelcallback(void *context) +void hv_vss_onchannelcallback(struct vmbus_channel *chan, void *context) { struct vmbus_channel *channel = context; u32 recvlen; diff --git a/drivers/hv/hv_util.c b/drivers/hv/hv_util.c index 42aec2c5606af7..3d498b940e713c 100644 --- a/drivers/hv/hv_util.c +++ b/drivers/hv/hv_util.c @@ -115,7 +115,7 @@ static int hv_shutdown_init(struct hv_util_service *srv) return 0; } -static void shutdown_onchannelcallback(void *context); +static void shutdown_onchannelcallback(struct vmbus_channel *, void *context); static struct hv_util_service util_shutdown = { .util_cb = shutdown_onchannelcallback, .util_init = hv_shutdown_init, @@ -125,7 +125,7 @@ static int hv_timesync_init(struct hv_util_service *srv); static int hv_timesync_pre_suspend(void); static void hv_timesync_deinit(void); -static void timesync_onchannelcallback(void *context); +static void timesync_onchannelcallback(struct vmbus_channel *, void *context); static struct hv_util_service util_timesynch = { .util_cb = timesync_onchannelcallback, .util_init = hv_timesync_init, @@ -133,7 +133,7 @@ static struct hv_util_service util_timesynch = { .util_deinit = hv_timesync_deinit, }; -static void heartbeat_onchannelcallback(void *context); +static void heartbeat_onchannelcallback(struct vmbus_channel *, void *context); static struct hv_util_service util_heartbeat = { .util_cb = heartbeat_onchannelcallback, }; @@ -182,7 +182,7 @@ static DECLARE_WORK(shutdown_work, perform_shutdown); */ static DECLARE_WORK(restart_work, perform_restart); -static void shutdown_onchannelcallback(void *context) +static void shutdown_onchannelcallback(struct vmbus_channel *, void *context) { struct vmbus_channel *channel = context; struct work_struct *work = NULL; @@ -391,7 +391,7 @@ static inline void adj_guesttime(u64 hosttime, u64 reftime, u8 adj_flags) /* * Time Sync Channel message handler. */ -static void timesync_onchannelcallback(void *context) +static void timesync_onchannelcallback(struct vmbus_channel *, void *context) { struct vmbus_channel *channel = context; u32 recvlen; @@ -484,7 +484,7 @@ static void timesync_onchannelcallback(void *context) * Every two seconds, Hyper-V send us a heartbeat request message. * we respond to this message, and Hyper-V knows we are alive. */ -static void heartbeat_onchannelcallback(void *context) +static void heartbeat_onchannelcallback(struct vmbus_channel *, void *context) { struct vmbus_channel *channel = context; u32 recvlen; @@ -586,7 +586,7 @@ static int util_probe(struct hv_device *dev, hv_set_drvdata(dev, srv); - ret = vmbus_open(dev->channel, HV_UTIL_RING_SEND_SIZE, + ret = vmbus_open_channel(dev->channel, HV_UTIL_RING_SEND_SIZE, HV_UTIL_RING_RECV_SIZE, NULL, 0, srv->util_cb, dev->channel); if (ret) @@ -644,7 +644,7 @@ static int util_resume(struct hv_device *dev) return ret; } - ret = vmbus_open(dev->channel, HV_UTIL_RING_SEND_SIZE, + ret = vmbus_open_channel(dev->channel, HV_UTIL_RING_SEND_SIZE, HV_UTIL_RING_RECV_SIZE, NULL, 0, srv->util_cb, dev->channel); return ret; diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index 55f2086841ae41..11d77bc92be6ab 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -362,19 +362,19 @@ int hv_kvp_init(struct hv_util_service *srv); void hv_kvp_deinit(void); int hv_kvp_pre_suspend(void); int hv_kvp_pre_resume(void); -void hv_kvp_onchannelcallback(void *context); +void hv_kvp_onchannelcallback(struct vmbus_channel *chan, void *context); int hv_vss_init(struct hv_util_service *srv); void hv_vss_deinit(void); int hv_vss_pre_suspend(void); int hv_vss_pre_resume(void); -void hv_vss_onchannelcallback(void *context); +void hv_vss_onchannelcallback(struct vmbus_channel *chan, void *context); int hv_fcopy_init(struct hv_util_service *srv); void hv_fcopy_deinit(void); int hv_fcopy_pre_suspend(void); int hv_fcopy_pre_resume(void); -void hv_fcopy_onchannelcallback(void *context); +void hv_fcopy_onchannelcallback(struct vmbus_channel *chan, void *context); void vmbus_initiate_unload(bool crash); static inline void hv_poll_channel(struct vmbus_channel *channel, diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 3a98f79fb592a1..103e8d24eeb84e 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -1553,7 +1553,7 @@ void vmbus_free_mmio(resource_size_t start, resource_size_t size); struct hv_util_service { u8 *recv_buffer; void *channel; - void (*util_cb)(void *); + void (*util_cb)(struct vmbus_channel *, void *); int (*util_init)(struct hv_util_service *); void (*util_deinit)(void); int (*util_pre_suspend)(void);