Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/hyperv/vmbus/hv_channel.c
Show First 20 Lines • Show All 69 Lines • ▼ Show 20 Lines | monitor_page = (hv_vmbus_monitor_page *) | ||||
hv_vmbus_g_connection.monitor_pages; | hv_vmbus_g_connection.monitor_pages; | ||||
monitor_page++; /* Get the child to parent monitor page */ | monitor_page++; /* Get the child to parent monitor page */ | ||||
synch_set_bit(channel->monitor_bit, | synch_set_bit(channel->monitor_bit, | ||||
(uint32_t *)&monitor_page-> | (uint32_t *)&monitor_page-> | ||||
trigger_group[channel->monitor_group].u.pending); | trigger_group[channel->monitor_group].u.pending); | ||||
} else { | } else { | ||||
hv_vmbus_set_event(channel->offer_msg.child_rel_id); | hv_vmbus_set_event(channel); | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* @brief Open the specified channel | * @brief Open the specified channel | ||||
*/ | */ | ||||
int | int | ||||
hv_vmbus_channel_open( | hv_vmbus_channel_open( | ||||
hv_vmbus_channel* new_channel, | hv_vmbus_channel* new_channel, | ||||
uint32_t send_ring_buffer_size, | uint32_t send_ring_buffer_size, | ||||
uint32_t recv_ring_buffer_size, | uint32_t recv_ring_buffer_size, | ||||
void* user_data, | void* user_data, | ||||
uint32_t user_data_len, | uint32_t user_data_len, | ||||
hv_vmbus_pfn_channel_callback pfn_on_channel_callback, | hv_vmbus_pfn_channel_callback pfn_on_channel_callback, | ||||
void* context) | void* context) | ||||
{ | { | ||||
int ret = 0; | int ret = 0; | ||||
void *in, *out; | void *in, *out; | ||||
hv_vmbus_channel_open_channel* open_msg; | hv_vmbus_channel_open_channel* open_msg; | ||||
hv_vmbus_channel_msg_info* open_info; | hv_vmbus_channel_msg_info* open_info; | ||||
mtx_lock(&new_channel->sc_lock); | |||||
if (new_channel->state == HV_CHANNEL_OPEN_STATE) { | |||||
new_channel->state = HV_CHANNEL_OPENING_STATE; | |||||
} else { | |||||
mtx_unlock(&new_channel->sc_lock); | |||||
if(bootverbose) | |||||
printf("VMBUS: Trying to open channel <%p> which in " | |||||
"%d state.\n", new_channel, new_channel->state); | |||||
return (EINVAL); | |||||
} | |||||
mtx_unlock(&new_channel->sc_lock); | |||||
new_channel->on_channel_callback = pfn_on_channel_callback; | new_channel->on_channel_callback = pfn_on_channel_callback; | ||||
new_channel->channel_callback_context = context; | new_channel->channel_callback_context = context; | ||||
/* Allocate the ring buffer */ | /* Allocate the ring buffer */ | ||||
out = contigmalloc((send_ring_buffer_size + recv_ring_buffer_size), | out = contigmalloc((send_ring_buffer_size + recv_ring_buffer_size), | ||||
M_DEVBUF, M_ZERO, 0UL, BUS_SPACE_MAXADDR, PAGE_SIZE, 0); | M_DEVBUF, M_ZERO, 0UL, BUS_SPACE_MAXADDR, PAGE_SIZE, 0); | ||||
KASSERT(out != NULL, | KASSERT(out != NULL, | ||||
("Error VMBUS: contigmalloc failed to allocate Ring Buffer!")); | ("Error VMBUS: contigmalloc failed to allocate Ring Buffer!")); | ||||
▲ Show 20 Lines • Show All 47 Lines • ▼ Show 20 Lines | hv_vmbus_channel_open( | ||||
open_msg = (hv_vmbus_channel_open_channel*) open_info->msg; | open_msg = (hv_vmbus_channel_open_channel*) open_info->msg; | ||||
open_msg->header.message_type = HV_CHANNEL_MESSAGE_OPEN_CHANNEL; | open_msg->header.message_type = HV_CHANNEL_MESSAGE_OPEN_CHANNEL; | ||||
open_msg->open_id = new_channel->offer_msg.child_rel_id; | open_msg->open_id = new_channel->offer_msg.child_rel_id; | ||||
open_msg->child_rel_id = new_channel->offer_msg.child_rel_id; | open_msg->child_rel_id = new_channel->offer_msg.child_rel_id; | ||||
open_msg->ring_buffer_gpadl_handle = | open_msg->ring_buffer_gpadl_handle = | ||||
new_channel->ring_buffer_gpadl_handle; | new_channel->ring_buffer_gpadl_handle; | ||||
open_msg->downstream_ring_buffer_page_offset = send_ring_buffer_size | open_msg->downstream_ring_buffer_page_offset = send_ring_buffer_size | ||||
>> PAGE_SHIFT; | >> PAGE_SHIFT; | ||||
open_msg->server_context_area_gpadl_handle = 0; | open_msg->target_vcpu = new_channel->target_vcpu; | ||||
if (user_data_len) | if (user_data_len) | ||||
memcpy(open_msg->user_data, user_data, user_data_len); | memcpy(open_msg->user_data, user_data, user_data_len); | ||||
mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock); | mtx_lock_spin(&hv_vmbus_g_connection.channel_msg_lock); | ||||
TAILQ_INSERT_TAIL( | TAILQ_INSERT_TAIL( | ||||
&hv_vmbus_g_connection.channel_msg_anchor, | &hv_vmbus_g_connection.channel_msg_anchor, | ||||
open_info, | open_info, | ||||
msg_list_entry); | msg_list_entry); | ||||
mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); | mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); | ||||
ret = hv_vmbus_post_message( | ret = hv_vmbus_post_message( | ||||
open_msg, sizeof(hv_vmbus_channel_open_channel)); | open_msg, sizeof(hv_vmbus_channel_open_channel)); | ||||
if (ret != 0) | if (ret != 0) | ||||
goto cleanup; | goto cleanup; | ||||
ret = sema_timedwait(&open_info->wait_sema, 500); /* KYS 5 seconds */ | ret = sema_timedwait(&open_info->wait_sema, 500); /* KYS 5 seconds */ | ||||
if (ret) | if (ret) { | ||||
if(bootverbose) | |||||
printf("VMBUS: channel <%p> open timeout.\n", new_channel); | |||||
goto cleanup; | goto cleanup; | ||||
} | |||||
if (open_info->response.open_result.status == 0) { | if (open_info->response.open_result.status == 0) { | ||||
new_channel->state = HV_CHANNEL_OPENED_STATE; | |||||
if(bootverbose) | if(bootverbose) | ||||
printf("VMBUS: channel <%p> open success.\n", new_channel); | printf("VMBUS: channel <%p> open success.\n", new_channel); | ||||
} else { | } else { | ||||
if(bootverbose) | if(bootverbose) | ||||
printf("Error VMBUS: channel <%p> open failed - %d!\n", | printf("Error VMBUS: channel <%p> open failed - %d!\n", | ||||
new_channel, open_info->response.open_result.status); | new_channel, open_info->response.open_result.status); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 295 Lines • ▼ Show 20 Lines | TAILQ_REMOVE(&hv_vmbus_g_connection.channel_msg_anchor, | ||||
info, msg_list_entry); | info, msg_list_entry); | ||||
mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); | mtx_unlock_spin(&hv_vmbus_g_connection.channel_msg_lock); | ||||
sema_destroy(&info->wait_sema); | sema_destroy(&info->wait_sema); | ||||
free(info, M_DEVBUF); | free(info, M_DEVBUF); | ||||
return (ret); | return (ret); | ||||
} | } | ||||
/** | static void | ||||
* @brief Close the specified channel | hv_vmbus_channel_close_internal(hv_vmbus_channel *channel) | ||||
*/ | |||||
void | |||||
hv_vmbus_channel_close(hv_vmbus_channel *channel) | |||||
{ | { | ||||
int ret = 0; | int ret = 0; | ||||
hv_vmbus_channel_close_channel* msg; | hv_vmbus_channel_close_channel* msg; | ||||
hv_vmbus_channel_msg_info* info; | hv_vmbus_channel_msg_info* info; | ||||
channel->state = HV_CHANNEL_OPEN_STATE; | |||||
channel->sc_creation_callback = NULL; | |||||
/* | |||||
* Grab the lock to prevent race condition when a packet received | |||||
* and unloading driver is in the process. | |||||
*/ | |||||
mtx_lock(&channel->inbound_lock); | mtx_lock(&channel->inbound_lock); | ||||
channel->on_channel_callback = NULL; | channel->on_channel_callback = NULL; | ||||
mtx_unlock(&channel->inbound_lock); | mtx_unlock(&channel->inbound_lock); | ||||
/** | /** | ||||
* Send a closing message | * Send a closing message | ||||
*/ | */ | ||||
info = (hv_vmbus_channel_msg_info *) | info = (hv_vmbus_channel_msg_info *) | ||||
Show All 22 Lines | hv_vmbus_channel_close_internal(hv_vmbus_channel *channel) | ||||
/* cleanup the ring buffers for this channel */ | /* cleanup the ring buffers for this channel */ | ||||
hv_ring_buffer_cleanup(&channel->outbound); | hv_ring_buffer_cleanup(&channel->outbound); | ||||
hv_ring_buffer_cleanup(&channel->inbound); | hv_ring_buffer_cleanup(&channel->inbound); | ||||
contigfree(channel->ring_buffer_pages, channel->ring_buffer_size, | contigfree(channel->ring_buffer_pages, channel->ring_buffer_size, | ||||
M_DEVBUF); | M_DEVBUF); | ||||
free(info, M_DEVBUF); | free(info, M_DEVBUF); | ||||
} | |||||
/* | /** | ||||
* If we are closing the channel during an error path in | * @brief Close the specified channel | ||||
* opening the channel, don't free the channel | |||||
* since the caller will free the channel | |||||
*/ | */ | ||||
if (channel->state == HV_CHANNEL_OPEN_STATE) { | void | ||||
mtx_lock_spin(&hv_vmbus_g_connection.channel_lock); | hv_vmbus_channel_close(hv_vmbus_channel *channel) | ||||
TAILQ_REMOVE( | { | ||||
&hv_vmbus_g_connection.channel_anchor, | hv_vmbus_channel* sub_channel; | ||||
channel, | |||||
list_entry); | |||||
mtx_unlock_spin(&hv_vmbus_g_connection.channel_lock); | |||||
hv_vmbus_free_vmbus_channel(channel); | if (channel->primary_channel != NULL) { | ||||
/* | |||||
* We only close multi-channels when the primary is | |||||
* closed. | |||||
*/ | |||||
return; | |||||
} | } | ||||
/* | |||||
* Close all multi-channels first. | |||||
*/ | |||||
TAILQ_FOREACH(sub_channel, &channel->sc_list_anchor, | |||||
sc_list_entry) { | |||||
if (sub_channel->state != HV_CHANNEL_OPENED_STATE) | |||||
continue; | |||||
hv_vmbus_channel_close_internal(sub_channel); | |||||
} | } | ||||
/* | |||||
* Then close the primary channel. | |||||
*/ | |||||
hv_vmbus_channel_close_internal(channel); | |||||
} | |||||
/** | /** | ||||
* @brief Send the specified buffer on the given channel | * @brief Send the specified buffer on the given channel | ||||
*/ | */ | ||||
int | int | ||||
hv_vmbus_channel_send_packet( | hv_vmbus_channel_send_packet( | ||||
hv_vmbus_channel* channel, | hv_vmbus_channel* channel, | ||||
void* buffer, | void* buffer, | ||||
uint32_t buffer_len, | uint32_t buffer_len, | ||||
uint64_t request_id, | uint64_t request_id, | ||||
hv_vmbus_packet_type type, | hv_vmbus_packet_type type, | ||||
uint32_t flags) | uint32_t flags) | ||||
{ | { | ||||
int ret = 0; | int ret = 0; | ||||
hv_vm_packet_descriptor desc; | hv_vm_packet_descriptor desc; | ||||
uint32_t packet_len; | uint32_t packet_len; | ||||
uint64_t aligned_data; | uint64_t aligned_data; | ||||
uint32_t packet_len_aligned; | uint32_t packet_len_aligned; | ||||
boolean_t need_sig; | |||||
hv_vmbus_sg_buffer_list buffer_list[3]; | hv_vmbus_sg_buffer_list buffer_list[3]; | ||||
packet_len = sizeof(hv_vm_packet_descriptor) + buffer_len; | packet_len = sizeof(hv_vm_packet_descriptor) + buffer_len; | ||||
packet_len_aligned = HV_ALIGN_UP(packet_len, sizeof(uint64_t)); | packet_len_aligned = HV_ALIGN_UP(packet_len, sizeof(uint64_t)); | ||||
aligned_data = 0; | aligned_data = 0; | ||||
/* Setup the descriptor */ | /* Setup the descriptor */ | ||||
desc.type = type; /* HV_VMBUS_PACKET_TYPE_DATA_IN_BAND; */ | desc.type = type; /* HV_VMBUS_PACKET_TYPE_DATA_IN_BAND; */ | ||||
desc.flags = flags; /* HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED */ | desc.flags = flags; /* HV_VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED */ | ||||
/* in 8-bytes granularity */ | /* in 8-bytes granularity */ | ||||
desc.data_offset8 = sizeof(hv_vm_packet_descriptor) >> 3; | desc.data_offset8 = sizeof(hv_vm_packet_descriptor) >> 3; | ||||
desc.length8 = (uint16_t) (packet_len_aligned >> 3); | desc.length8 = (uint16_t) (packet_len_aligned >> 3); | ||||
desc.transaction_id = request_id; | desc.transaction_id = request_id; | ||||
buffer_list[0].data = &desc; | buffer_list[0].data = &desc; | ||||
buffer_list[0].length = sizeof(hv_vm_packet_descriptor); | buffer_list[0].length = sizeof(hv_vm_packet_descriptor); | ||||
buffer_list[1].data = buffer; | buffer_list[1].data = buffer; | ||||
buffer_list[1].length = buffer_len; | buffer_list[1].length = buffer_len; | ||||
buffer_list[2].data = &aligned_data; | buffer_list[2].data = &aligned_data; | ||||
buffer_list[2].length = packet_len_aligned - packet_len; | buffer_list[2].length = packet_len_aligned - packet_len; | ||||
ret = hv_ring_buffer_write(&channel->outbound, buffer_list, 3); | ret = hv_ring_buffer_write(&channel->outbound, buffer_list, 3, | ||||
&need_sig); | |||||
/* TODO: We should determine if this is optional */ | /* TODO: We should determine if this is optional */ | ||||
if (ret == 0 | if (ret == 0 && need_sig) { | ||||
&& !hv_vmbus_get_ring_buffer_interrupt_mask( | |||||
&channel->outbound)) { | |||||
vmbus_channel_set_event(channel); | vmbus_channel_set_event(channel); | ||||
} | } | ||||
return (ret); | return (ret); | ||||
} | } | ||||
/** | /** | ||||
* @brief Send a range of single-page buffer packets using | * @brief Send a range of single-page buffer packets using | ||||
* a GPADL Direct packet type | * a GPADL Direct packet type | ||||
*/ | */ | ||||
int | int | ||||
hv_vmbus_channel_send_packet_pagebuffer( | hv_vmbus_channel_send_packet_pagebuffer( | ||||
hv_vmbus_channel* channel, | hv_vmbus_channel* channel, | ||||
hv_vmbus_page_buffer page_buffers[], | hv_vmbus_page_buffer page_buffers[], | ||||
uint32_t page_count, | uint32_t page_count, | ||||
void* buffer, | void* buffer, | ||||
uint32_t buffer_len, | uint32_t buffer_len, | ||||
uint64_t request_id) | uint64_t request_id) | ||||
{ | { | ||||
int ret = 0; | int ret = 0; | ||||
int i = 0; | int i = 0; | ||||
boolean_t need_sig; | |||||
uint32_t packet_len; | uint32_t packet_len; | ||||
uint32_t packetLen_aligned; | uint32_t packetLen_aligned; | ||||
hv_vmbus_sg_buffer_list buffer_list[3]; | hv_vmbus_sg_buffer_list buffer_list[3]; | ||||
hv_vmbus_channel_packet_page_buffer desc; | hv_vmbus_channel_packet_page_buffer desc; | ||||
uint32_t descSize; | uint32_t descSize; | ||||
uint64_t alignedData = 0; | uint64_t alignedData = 0; | ||||
if (page_count > HV_MAX_PAGE_BUFFER_COUNT) | if (page_count > HV_MAX_PAGE_BUFFER_COUNT) | ||||
Show All 27 Lines | hv_vmbus_channel_send_packet_pagebuffer( | ||||
buffer_list[0].length = descSize; | buffer_list[0].length = descSize; | ||||
buffer_list[1].data = buffer; | buffer_list[1].data = buffer; | ||||
buffer_list[1].length = buffer_len; | buffer_list[1].length = buffer_len; | ||||
buffer_list[2].data = &alignedData; | buffer_list[2].data = &alignedData; | ||||
buffer_list[2].length = packetLen_aligned - packet_len; | buffer_list[2].length = packetLen_aligned - packet_len; | ||||
ret = hv_ring_buffer_write(&channel->outbound, buffer_list, 3); | ret = hv_ring_buffer_write(&channel->outbound, buffer_list, 3, | ||||
&need_sig); | |||||
/* TODO: We should determine if this is optional */ | /* TODO: We should determine if this is optional */ | ||||
if (ret == 0 && | if (ret == 0 && need_sig) { | ||||
!hv_vmbus_get_ring_buffer_interrupt_mask(&channel->outbound)) { | |||||
vmbus_channel_set_event(channel); | vmbus_channel_set_event(channel); | ||||
} | } | ||||
return (ret); | return (ret); | ||||
} | } | ||||
/** | /** | ||||
* @brief Send a multi-page buffer packet using a GPADL Direct packet type | * @brief Send a multi-page buffer packet using a GPADL Direct packet type | ||||
*/ | */ | ||||
int | int | ||||
hv_vmbus_channel_send_packet_multipagebuffer( | hv_vmbus_channel_send_packet_multipagebuffer( | ||||
hv_vmbus_channel* channel, | hv_vmbus_channel* channel, | ||||
hv_vmbus_multipage_buffer* multi_page_buffer, | hv_vmbus_multipage_buffer* multi_page_buffer, | ||||
void* buffer, | void* buffer, | ||||
uint32_t buffer_len, | uint32_t buffer_len, | ||||
uint64_t request_id) | uint64_t request_id) | ||||
{ | { | ||||
int ret = 0; | int ret = 0; | ||||
uint32_t desc_size; | uint32_t desc_size; | ||||
boolean_t need_sig; | |||||
uint32_t packet_len; | uint32_t packet_len; | ||||
uint32_t packet_len_aligned; | uint32_t packet_len_aligned; | ||||
uint32_t pfn_count; | uint32_t pfn_count; | ||||
uint64_t aligned_data = 0; | uint64_t aligned_data = 0; | ||||
hv_vmbus_sg_buffer_list buffer_list[3]; | hv_vmbus_sg_buffer_list buffer_list[3]; | ||||
hv_vmbus_channel_packet_multipage_buffer desc; | hv_vmbus_channel_packet_multipage_buffer desc; | ||||
pfn_count = | pfn_count = | ||||
Show All 34 Lines | hv_vmbus_channel_send_packet_multipagebuffer( | ||||
buffer_list[0].length = desc_size; | buffer_list[0].length = desc_size; | ||||
buffer_list[1].data = buffer; | buffer_list[1].data = buffer; | ||||
buffer_list[1].length = buffer_len; | buffer_list[1].length = buffer_len; | ||||
buffer_list[2].data = &aligned_data; | buffer_list[2].data = &aligned_data; | ||||
buffer_list[2].length = packet_len_aligned - packet_len; | buffer_list[2].length = packet_len_aligned - packet_len; | ||||
ret = hv_ring_buffer_write(&channel->outbound, buffer_list, 3); | ret = hv_ring_buffer_write(&channel->outbound, buffer_list, 3, | ||||
&need_sig); | |||||
/* TODO: We should determine if this is optional */ | /* TODO: We should determine if this is optional */ | ||||
if (ret == 0 && | if (ret == 0 && need_sig) { | ||||
!hv_vmbus_get_ring_buffer_interrupt_mask(&channel->outbound)) { | |||||
vmbus_channel_set_event(channel); | vmbus_channel_set_event(channel); | ||||
} | } | ||||
return (ret); | return (ret); | ||||
} | } | ||||
/** | /** | ||||
* @brief Retrieve the user packet on the specified channel | * @brief Retrieve the user packet on the specified channel | ||||
▲ Show 20 Lines • Show All 80 Lines • Show Last 20 Lines |