diff options
Diffstat (limited to 'drivers/hv/channel.c')
| -rw-r--r-- | drivers/hv/channel.c | 208 |
1 files changed, 114 insertions, 94 deletions
diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 773a2f25a8f..284cf66489f 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -27,6 +27,7 @@ #include <linux/slab.h> #include <linux/module.h> #include <linux/hyperv.h> +#include <linux/uio.h> #include "hyperv_vmbus.h" @@ -47,63 +48,19 @@ static void vmbus_setevent(struct vmbus_channel *channel) (unsigned long *) vmbus_connection.send_int_page + (channel->offermsg.child_relid >> 5)); - monitorpage = vmbus_connection.monitor_pages; - monitorpage++; /* Get the child to parent monitor page */ + /* Get the child to parent monitor page */ + monitorpage = vmbus_connection.monitor_pages[1]; sync_set_bit(channel->monitor_bit, (unsigned long *)&monitorpage->trigger_group [channel->monitor_grp].pending); } else { - vmbus_set_event(channel->offermsg.child_relid); + vmbus_set_event(channel); } } /* - * vmbus_get_debug_info -Retrieve various channel debug info - */ -void vmbus_get_debug_info(struct vmbus_channel *channel, - struct vmbus_channel_debug_info *debuginfo) -{ - struct hv_monitor_page *monitorpage; - u8 monitor_group = (u8)channel->offermsg.monitorid / 32; - u8 monitor_offset = (u8)channel->offermsg.monitorid % 32; - - debuginfo->relid = channel->offermsg.child_relid; - debuginfo->state = channel->state; - memcpy(&debuginfo->interfacetype, - &channel->offermsg.offer.if_type, sizeof(uuid_le)); - memcpy(&debuginfo->interface_instance, - &channel->offermsg.offer.if_instance, - sizeof(uuid_le)); - - monitorpage = (struct hv_monitor_page *)vmbus_connection.monitor_pages; - - debuginfo->monitorid = channel->offermsg.monitorid; - - debuginfo->servermonitor_pending = - monitorpage->trigger_group[monitor_group].pending; - debuginfo->servermonitor_latency = - monitorpage->latency[monitor_group][monitor_offset]; - debuginfo->servermonitor_connectionid = - monitorpage->parameter[monitor_group] - [monitor_offset].connectionid.u.id; - - monitorpage++; - - debuginfo->clientmonitor_pending = - monitorpage->trigger_group[monitor_group].pending; - debuginfo->clientmonitor_latency = - monitorpage->latency[monitor_group][monitor_offset]; - debuginfo->clientmonitor_connectionid = - monitorpage->parameter[monitor_group] - [monitor_offset].connectionid.u.id; - - hv_ringbuffer_get_debuginfo(&channel->inbound, &debuginfo->inbound); - hv_ringbuffer_get_debuginfo(&channel->outbound, &debuginfo->outbound); -} - -/* * vmbus_open - Open the specified channel. */ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, @@ -116,6 +73,15 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, unsigned long flags; int ret, t, err = 0; + spin_lock_irqsave(&newchannel->sc_lock, flags); + if (newchannel->state == CHANNEL_OPEN_STATE) { + newchannel->state = CHANNEL_OPENING_STATE; + } else { + spin_unlock_irqrestore(&newchannel->sc_lock, flags); + return -EINVAL; + } + spin_unlock_irqrestore(&newchannel->sc_lock, flags); + newchannel->onchannel_callback = onchannelcallback; newchannel->channel_callback_context = context; @@ -181,7 +147,7 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, open_msg->ringbuffer_gpadlhandle = newchannel->ringbuffer_gpadlhandle; open_msg->downstream_ringbuffer_pageoffset = send_ringbuffer_size >> PAGE_SHIFT; - open_msg->server_contextarea_gpadlhandle = 0; + open_msg->target_vp = newchannel->target_vp; if (userdatalen > MAX_USER_DEFINED_BYTES) { err = -EINVAL; @@ -216,6 +182,9 @@ int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, list_del(&open_info->msglistentry); spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); + if (err == 0) + newchannel->state = CHANNEL_OPENED_STATE; + kfree(open_info); return err; @@ -241,7 +210,6 @@ static int create_gpadl_header(void *kbuffer, u32 size, { int i; int pagecount; - unsigned long long pfn; struct vmbus_channel_gpadl_header *gpadl_header; struct vmbus_channel_gpadl_body *gpadl_body; struct vmbus_channel_msginfo *msgheader; @@ -251,7 +219,6 @@ static int create_gpadl_header(void *kbuffer, u32 size, int pfnsum, pfncount, pfnleft, pfncurr, pfnsize; pagecount = size >> PAGE_SHIFT; - pfn = virt_to_phys(kbuffer) >> PAGE_SHIFT; /* do we need a gpadl body msg */ pfnsize = MAX_SIZE_CHANNEL_MESSAGE - @@ -280,7 +247,8 @@ static int create_gpadl_header(void *kbuffer, u32 size, gpadl_header->range[0].byte_offset = 0; gpadl_header->range[0].byte_count = size; for (i = 0; i < pfncount; i++) - gpadl_header->range[0].pfn_array[i] = pfn+i; + gpadl_header->range[0].pfn_array[i] = slow_virt_to_phys( + kbuffer + PAGE_SIZE * i) >> PAGE_SHIFT; *msginfo = msgheader; *messagecount = 1; @@ -333,7 +301,9 @@ static int create_gpadl_header(void *kbuffer, u32 size, * so the hypervisor gurantees that this is ok. */ for (i = 0; i < pfncurr; i++) - gpadl_body->pfn[i] = pfn + pfnsum + i; + gpadl_body->pfn[i] = slow_virt_to_phys( + kbuffer + PAGE_SIZE * (pfnsum + i)) >> + PAGE_SHIFT; /* add to msg header */ list_add_tail(&msgbody->msglistentry, @@ -359,7 +329,8 @@ static int create_gpadl_header(void *kbuffer, u32 size, gpadl_header->range[0].byte_offset = 0; gpadl_header->range[0].byte_count = size; for (i = 0; i < pagecount; i++) - gpadl_header->range[0].pfn_array[i] = pfn+i; + gpadl_header->range[0].pfn_array[i] = slow_virt_to_phys( + kbuffer + PAGE_SIZE * i) >> PAGE_SHIFT; *msginfo = msgheader; *messagecount = 1; @@ -376,7 +347,7 @@ nomem: * vmbus_establish_gpadl - Estabish a GPADL for the specified buffer * * @channel: a channel - * @kbuffer: from kmalloc + * @kbuffer: from kmalloc or vmalloc * @size: page-size multiple * @gpadl_handle: some funky thing */ @@ -500,19 +471,26 @@ int vmbus_teardown_gpadl(struct vmbus_channel *channel, u32 gpadl_handle) } EXPORT_SYMBOL_GPL(vmbus_teardown_gpadl); -/* - * vmbus_close - Close the specified channel - */ -void vmbus_close(struct vmbus_channel *channel) +static void reset_channel_cb(void *arg) +{ + struct vmbus_channel *channel = arg; + + channel->onchannel_callback = NULL; +} + +static void vmbus_close_internal(struct vmbus_channel *channel) { struct vmbus_channel_close_channel *msg; int ret; - unsigned long flags; + channel->state = CHANNEL_OPEN_STATE; + channel->sc_creation_callback = NULL; /* Stop callback and cancel the timer asap */ - spin_lock_irqsave(&channel->inbound_lock, flags); - channel->onchannel_callback = NULL; - spin_unlock_irqrestore(&channel->inbound_lock, flags); + if (channel->target_cpu != smp_processor_id()) + smp_call_function_single(channel->target_cpu, reset_channel_cb, + channel, true); + else + reset_channel_cb(channel); /* Send a closing message */ @@ -538,6 +516,37 @@ void vmbus_close(struct vmbus_channel *channel) } + +/* + * vmbus_close - Close the specified channel + */ +void vmbus_close(struct vmbus_channel *channel) +{ + struct list_head *cur, *tmp; + struct vmbus_channel *cur_channel; + + if (channel->primary_channel != NULL) { + /* + * We will only close sub-channels when + * the primary is closed. + */ + return; + } + /* + * Close all the sub-channels first and then close the + * primary channel. + */ + list_for_each_safe(cur, tmp, &channel->sc_list) { + cur_channel = list_entry(cur, struct vmbus_channel, sc_list); + if (cur_channel->state != CHANNEL_OPENED_STATE) + continue; + vmbus_close_internal(cur_channel); + } + /* + * Now close the primary. + */ + vmbus_close_internal(channel); +} EXPORT_SYMBOL_GPL(vmbus_close); /** @@ -554,16 +563,17 @@ EXPORT_SYMBOL_GPL(vmbus_close); * * Mainly used by Hyper-V drivers. */ -int vmbus_sendpacket(struct vmbus_channel *channel, const void *buffer, +int vmbus_sendpacket(struct vmbus_channel *channel, void *buffer, u32 bufferlen, u64 requestid, enum vmbus_packet_type type, u32 flags) { struct vmpacket_descriptor desc; u32 packetlen = sizeof(struct vmpacket_descriptor) + bufferlen; u32 packetlen_aligned = ALIGN(packetlen, sizeof(u64)); - struct scatterlist bufferlist[3]; + struct kvec bufferlist[3]; u64 aligned_data = 0; int ret; + bool signal = false; /* Setup the descriptor */ @@ -574,15 +584,16 @@ int vmbus_sendpacket(struct vmbus_channel *channel, const void *buffer, desc.len8 = (u16)(packetlen_aligned >> 3); desc.trans_id = requestid; - sg_init_table(bufferlist, 3); - sg_set_buf(&bufferlist[0], &desc, sizeof(struct vmpacket_descriptor)); - sg_set_buf(&bufferlist[1], buffer, bufferlen); - sg_set_buf(&bufferlist[2], &aligned_data, - packetlen_aligned - packetlen); + bufferlist[0].iov_base = &desc; + bufferlist[0].iov_len = sizeof(struct vmpacket_descriptor); + bufferlist[1].iov_base = buffer; + bufferlist[1].iov_len = bufferlen; + bufferlist[2].iov_base = &aligned_data; + bufferlist[2].iov_len = (packetlen_aligned - packetlen); - ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3); + ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, &signal); - if (ret == 0 && !hv_get_ringbuffer_interrupt_mask(&channel->outbound)) + if (ret == 0 && signal) vmbus_setevent(channel); return ret; @@ -604,8 +615,9 @@ int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel, u32 descsize; u32 packetlen; u32 packetlen_aligned; - struct scatterlist bufferlist[3]; + struct kvec bufferlist[3]; u64 aligned_data = 0; + bool signal = false; if (pagecount > MAX_PAGE_BUFFER_COUNT) return -EINVAL; @@ -635,15 +647,16 @@ int vmbus_sendpacket_pagebuffer(struct vmbus_channel *channel, desc.range[i].pfn = pagebuffers[i].pfn; } - sg_init_table(bufferlist, 3); - sg_set_buf(&bufferlist[0], &desc, descsize); - sg_set_buf(&bufferlist[1], buffer, bufferlen); - sg_set_buf(&bufferlist[2], &aligned_data, - packetlen_aligned - packetlen); + bufferlist[0].iov_base = &desc; + bufferlist[0].iov_len = descsize; + bufferlist[1].iov_base = buffer; + bufferlist[1].iov_len = bufferlen; + bufferlist[2].iov_base = &aligned_data; + bufferlist[2].iov_len = (packetlen_aligned - packetlen); - ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3); + ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, &signal); - if (ret == 0 && !hv_get_ringbuffer_interrupt_mask(&channel->outbound)) + if (ret == 0 && signal) vmbus_setevent(channel); return ret; @@ -663,13 +676,13 @@ int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel, u32 descsize; u32 packetlen; u32 packetlen_aligned; - struct scatterlist bufferlist[3]; + struct kvec bufferlist[3]; u64 aligned_data = 0; + bool signal = false; u32 pfncount = NUM_PAGES_SPANNED(multi_pagebuffer->offset, multi_pagebuffer->len); - - if ((pfncount < 0) || (pfncount > MAX_MULTIPAGE_BUFFER_COUNT)) + if (pfncount > MAX_MULTIPAGE_BUFFER_COUNT) return -EINVAL; /* @@ -697,15 +710,16 @@ int vmbus_sendpacket_multipagebuffer(struct vmbus_channel *channel, memcpy(desc.range.pfn_array, multi_pagebuffer->pfn_array, pfncount * sizeof(u64)); - sg_init_table(bufferlist, 3); - sg_set_buf(&bufferlist[0], &desc, descsize); - sg_set_buf(&bufferlist[1], buffer, bufferlen); - sg_set_buf(&bufferlist[2], &aligned_data, - packetlen_aligned - packetlen); + bufferlist[0].iov_base = &desc; + bufferlist[0].iov_len = descsize; + bufferlist[1].iov_base = buffer; + bufferlist[1].iov_len = bufferlen; + bufferlist[2].iov_base = &aligned_data; + bufferlist[2].iov_len = (packetlen_aligned - packetlen); - ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3); + ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, &signal); - if (ret == 0 && !hv_get_ringbuffer_interrupt_mask(&channel->outbound)) + if (ret == 0 && signal) vmbus_setevent(channel); return ret; @@ -732,6 +746,7 @@ int vmbus_recvpacket(struct vmbus_channel *channel, void *buffer, u32 packetlen; u32 userlen; int ret; + bool signal = false; *buffer_actual_len = 0; *requestid = 0; @@ -758,8 +773,10 @@ int vmbus_recvpacket(struct vmbus_channel *channel, void *buffer, /* Copy over the packet to the user buffer */ ret = hv_ringbuffer_read(&channel->inbound, buffer, userlen, - (desc.offset8 << 3)); + (desc.offset8 << 3), &signal); + if (signal) + vmbus_setevent(channel); return 0; } @@ -774,8 +791,8 @@ int vmbus_recvpacket_raw(struct vmbus_channel *channel, void *buffer, { struct vmpacket_descriptor desc; u32 packetlen; - u32 userlen; int ret; + bool signal = false; *buffer_actual_len = 0; *requestid = 0; @@ -788,7 +805,6 @@ int vmbus_recvpacket_raw(struct vmbus_channel *channel, void *buffer, packetlen = desc.len8 << 3; - userlen = packetlen - (desc.offset8 << 3); *buffer_actual_len = packetlen; @@ -802,8 +818,12 @@ int vmbus_recvpacket_raw(struct vmbus_channel *channel, void *buffer, *requestid = desc.trans_id; /* Copy over the entire packet to the user buffer */ - ret = hv_ringbuffer_read(&channel->inbound, buffer, packetlen, 0); + ret = hv_ringbuffer_read(&channel->inbound, buffer, packetlen, 0, + &signal); - return 0; + if (signal) + vmbus_setevent(channel); + + return ret; } EXPORT_SYMBOL_GPL(vmbus_recvpacket_raw); |
