Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

vmbus: split ring buffer allocation from open

The UIO driver needs the ring buffer to be persistent(reused)
across open/close. Split the allocation and setup of ring buffer
out of vmbus_open. For normal usage vmbus_open/vmbus_close there
are no changes; only impacts uio_hv_generic which needs to keep
ring buffer memory and reuse when application restarts.

Signed-off-by: Stephen Hemminger <sthemmin@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Stephen Hemminger and committed by
Greg Kroah-Hartman
ae6935ed 52a42c2a

+169 -122
+159 -122
drivers/hv/channel.c
··· 79 79 } 80 80 EXPORT_SYMBOL_GPL(vmbus_setevent); 81 81 82 - /* 83 - * vmbus_open - Open the specified channel. 84 - */ 85 - int vmbus_open(struct vmbus_channel *newchannel, u32 send_ringbuffer_size, 86 - u32 recv_ringbuffer_size, void *userdata, u32 userdatalen, 87 - void (*onchannelcallback)(void *context), void *context) 82 + /* vmbus_free_ring - drop mapping of ring buffer */ 83 + void vmbus_free_ring(struct vmbus_channel *channel) 88 84 { 89 - struct vmbus_channel_open_channel *open_msg; 90 - struct vmbus_channel_msginfo *open_info = NULL; 91 - unsigned long flags; 92 - int ret, err = 0; 93 - struct page *page; 94 - unsigned int order; 85 + hv_ringbuffer_cleanup(&channel->outbound); 86 + hv_ringbuffer_cleanup(&channel->inbound); 95 87 96 - if (send_ringbuffer_size % PAGE_SIZE || 97 - recv_ringbuffer_size % PAGE_SIZE) 98 - return -EINVAL; 99 - 100 - order = get_order(send_ringbuffer_size + recv_ringbuffer_size); 101 - 102 - spin_lock_irqsave(&newchannel->lock, flags); 103 - if (newchannel->state == CHANNEL_OPEN_STATE) { 104 - newchannel->state = CHANNEL_OPENING_STATE; 105 - } else { 106 - spin_unlock_irqrestore(&newchannel->lock, flags); 107 - return -EINVAL; 88 + if (channel->ringbuffer_page) { 89 + __free_pages(channel->ringbuffer_page, 90 + get_order(channel->ringbuffer_pagecount 91 + << PAGE_SHIFT)); 92 + channel->ringbuffer_page = NULL; 108 93 } 109 - spin_unlock_irqrestore(&newchannel->lock, flags); 94 + } 95 + EXPORT_SYMBOL_GPL(vmbus_free_ring); 110 96 111 - newchannel->onchannel_callback = onchannelcallback; 112 - newchannel->channel_callback_context = context; 97 + /* vmbus_alloc_ring - allocate and map pages for ring buffer */ 98 + int vmbus_alloc_ring(struct vmbus_channel *newchannel, 99 + u32 send_size, u32 recv_size) 100 + { 101 + struct page *page; 102 + int order; 103 + 104 + if (send_size % PAGE_SIZE || recv_size % PAGE_SIZE) 105 + return -EINVAL; 113 106 114 107 /* Allocate the ring buffer */ 108 + order = get_order(send_size + recv_size); 115 109 page = alloc_pages_node(cpu_to_node(newchannel->target_cpu), 116 110 GFP_KERNEL|__GFP_ZERO, order); 117 111 118 112 if (!page) 119 113 page = alloc_pages(GFP_KERNEL|__GFP_ZERO, order); 120 114 121 - if (!page) { 122 - err = -ENOMEM; 123 - goto error_set_chnstate; 124 - } 115 + if (!page) 116 + return -ENOMEM; 125 117 126 118 newchannel->ringbuffer_page = page; 127 - newchannel->ringbuffer_pagecount = (send_ringbuffer_size + 128 - recv_ringbuffer_size) >> PAGE_SHIFT; 119 + newchannel->ringbuffer_pagecount = (send_size + recv_size) >> PAGE_SHIFT; 120 + newchannel->ringbuffer_send_offset = send_size >> PAGE_SHIFT; 129 121 130 - ret = hv_ringbuffer_init(&newchannel->outbound, page, 131 - send_ringbuffer_size >> PAGE_SHIFT); 122 + return 0; 123 + } 124 + EXPORT_SYMBOL_GPL(vmbus_alloc_ring); 132 125 133 - if (ret != 0) { 134 - err = ret; 135 - goto error_free_pages; 126 + static int __vmbus_open(struct vmbus_channel *newchannel, 127 + void *userdata, u32 userdatalen, 128 + void (*onchannelcallback)(void *context), void *context) 129 + { 130 + struct vmbus_channel_open_channel *open_msg; 131 + struct vmbus_channel_msginfo *open_info = NULL; 132 + struct page *page = newchannel->ringbuffer_page; 133 + u32 send_pages, recv_pages; 134 + unsigned long flags; 135 + int err; 136 + 137 + if (userdatalen > MAX_USER_DEFINED_BYTES) 138 + return -EINVAL; 139 + 140 + send_pages = newchannel->ringbuffer_send_offset; 141 + recv_pages = newchannel->ringbuffer_pagecount - send_pages; 142 + 143 + spin_lock_irqsave(&newchannel->lock, flags); 144 + if (newchannel->state != CHANNEL_OPEN_STATE) { 145 + spin_unlock_irqrestore(&newchannel->lock, flags); 146 + return -EINVAL; 136 147 } 148 + spin_unlock_irqrestore(&newchannel->lock, flags); 137 149 138 - ret = hv_ringbuffer_init(&newchannel->inbound, 139 - &page[send_ringbuffer_size >> PAGE_SHIFT], 140 - recv_ringbuffer_size >> PAGE_SHIFT); 141 - if (ret != 0) { 142 - err = ret; 143 - goto error_free_pages; 144 - } 150 + newchannel->state = CHANNEL_OPENING_STATE; 151 + newchannel->onchannel_callback = onchannelcallback; 152 + newchannel->channel_callback_context = context; 145 153 154 + err = hv_ringbuffer_init(&newchannel->outbound, page, send_pages); 155 + if (err) 156 + goto error_clean_ring; 157 + 158 + err = hv_ringbuffer_init(&newchannel->inbound, 159 + &page[send_pages], recv_pages); 160 + if (err) 161 + goto error_clean_ring; 146 162 147 163 /* Establish the gpadl for the ring buffer */ 148 164 newchannel->ringbuffer_gpadlhandle = 0; 149 165 150 - ret = vmbus_establish_gpadl(newchannel, 151 - page_address(page), 152 - send_ringbuffer_size + 153 - recv_ringbuffer_size, 166 + err = vmbus_establish_gpadl(newchannel, 167 + page_address(newchannel->ringbuffer_page), 168 + (send_pages + recv_pages) << PAGE_SHIFT, 154 169 &newchannel->ringbuffer_gpadlhandle); 155 - 156 - if (ret != 0) { 157 - err = ret; 158 - goto error_free_pages; 159 - } 170 + if (err) 171 + goto error_clean_ring; 160 172 161 173 /* Create and init the channel open message */ 162 174 open_info = kmalloc(sizeof(*open_info) + ··· 187 175 open_msg->openid = newchannel->offermsg.child_relid; 188 176 open_msg->child_relid = newchannel->offermsg.child_relid; 189 177 open_msg->ringbuffer_gpadlhandle = newchannel->ringbuffer_gpadlhandle; 190 - open_msg->downstream_ringbuffer_pageoffset = send_ringbuffer_size >> 191 - PAGE_SHIFT; 178 + open_msg->downstream_ringbuffer_pageoffset = newchannel->ringbuffer_send_offset; 192 179 open_msg->target_vp = newchannel->target_vp; 193 - 194 - if (userdatalen > MAX_USER_DEFINED_BYTES) { 195 - err = -EINVAL; 196 - goto error_free_gpadl; 197 - } 198 180 199 181 if (userdatalen) 200 182 memcpy(open_msg->userdata, userdata, userdatalen); ··· 200 194 201 195 if (newchannel->rescind) { 202 196 err = -ENODEV; 203 - goto error_free_gpadl; 197 + goto error_free_info; 204 198 } 205 199 206 - ret = vmbus_post_msg(open_msg, 200 + err = vmbus_post_msg(open_msg, 207 201 sizeof(struct vmbus_channel_open_channel), true); 208 202 209 - trace_vmbus_open(open_msg, ret); 203 + trace_vmbus_open(open_msg, err); 210 204 211 - if (ret != 0) { 212 - err = ret; 205 + if (err != 0) 213 206 goto error_clean_msglist; 214 - } 215 207 216 208 wait_for_completion(&open_info->waitevent); 217 209 ··· 219 215 220 216 if (newchannel->rescind) { 221 217 err = -ENODEV; 222 - goto error_free_gpadl; 218 + goto error_free_info; 223 219 } 224 220 225 221 if (open_info->response.open_result.status) { 226 222 err = -EAGAIN; 227 - goto error_free_gpadl; 223 + goto error_free_info; 228 224 } 229 225 230 226 newchannel->state = CHANNEL_OPENED_STATE; ··· 235 231 spin_lock_irqsave(&vmbus_connection.channelmsg_lock, flags); 236 232 list_del(&open_info->msglistentry); 237 233 spin_unlock_irqrestore(&vmbus_connection.channelmsg_lock, flags); 238 - 234 + error_free_info: 235 + kfree(open_info); 239 236 error_free_gpadl: 240 237 vmbus_teardown_gpadl(newchannel, newchannel->ringbuffer_gpadlhandle); 241 - kfree(open_info); 242 - error_free_pages: 238 + newchannel->ringbuffer_gpadlhandle = 0; 239 + error_clean_ring: 243 240 hv_ringbuffer_cleanup(&newchannel->outbound); 244 241 hv_ringbuffer_cleanup(&newchannel->inbound); 245 - __free_pages(page, order); 246 - error_set_chnstate: 247 242 newchannel->state = CHANNEL_OPEN_STATE; 243 + return err; 244 + } 245 + 246 + /* 247 + * vmbus_connect_ring - Open the channel but reuse ring buffer 248 + */ 249 + int vmbus_connect_ring(struct vmbus_channel *newchannel, 250 + void (*onchannelcallback)(void *context), void *context) 251 + { 252 + return __vmbus_open(newchannel, NULL, 0, onchannelcallback, context); 253 + } 254 + EXPORT_SYMBOL_GPL(vmbus_connect_ring); 255 + 256 + /* 257 + * vmbus_open - Open the specified channel. 258 + */ 259 + int vmbus_open(struct vmbus_channel *newchannel, 260 + u32 send_ringbuffer_size, u32 recv_ringbuffer_size, 261 + void *userdata, u32 userdatalen, 262 + void (*onchannelcallback)(void *context), void *context) 263 + { 264 + int err; 265 + 266 + err = vmbus_alloc_ring(newchannel, send_ringbuffer_size, 267 + recv_ringbuffer_size); 268 + if (err) 269 + return err; 270 + 271 + err = __vmbus_open(newchannel, userdata, userdatalen, 272 + onchannelcallback, context); 273 + if (err) 274 + vmbus_free_ring(newchannel); 275 + 248 276 return err; 249 277 } 250 278 EXPORT_SYMBOL_GPL(vmbus_open); ··· 646 610 * in Hyper-V Manager), the driver's remove() invokes vmbus_close(): 647 611 * here we should skip most of the below cleanup work. 648 612 */ 649 - if (channel->state != CHANNEL_OPENED_STATE) { 650 - ret = -EINVAL; 651 - goto out; 652 - } 613 + if (channel->state != CHANNEL_OPENED_STATE) 614 + return -EINVAL; 653 615 654 616 channel->state = CHANNEL_OPEN_STATE; 655 617 ··· 669 635 * If we failed to post the close msg, 670 636 * it is perhaps better to leak memory. 671 637 */ 672 - goto out; 673 638 } 674 639 675 640 /* Tear down the gpadl for the channel's ring buffer */ 676 - if (channel->ringbuffer_gpadlhandle) { 641 + else if (channel->ringbuffer_gpadlhandle) { 677 642 ret = vmbus_teardown_gpadl(channel, 678 643 channel->ringbuffer_gpadlhandle); 679 644 if (ret) { ··· 681 648 * If we failed to teardown gpadl, 682 649 * it is perhaps better to leak memory. 683 650 */ 684 - goto out; 685 651 } 652 + 653 + channel->ringbuffer_gpadlhandle = 0; 686 654 } 687 655 688 - /* Cleanup the ring buffers for this channel */ 689 - hv_ringbuffer_cleanup(&channel->outbound); 690 - hv_ringbuffer_cleanup(&channel->inbound); 691 - 692 - __free_pages(channel->ringbuffer_page, 693 - get_order(channel->ringbuffer_pagecount << PAGE_SHIFT)); 694 - 695 - out: 696 656 return ret; 697 657 } 658 + 659 + /* disconnect ring - close all channels */ 660 + int vmbus_disconnect_ring(struct vmbus_channel *channel) 661 + { 662 + struct vmbus_channel *cur_channel, *tmp; 663 + unsigned long flags; 664 + LIST_HEAD(list); 665 + int ret; 666 + 667 + if (channel->primary_channel != NULL) 668 + return -EINVAL; 669 + 670 + /* Snapshot the list of subchannels */ 671 + spin_lock_irqsave(&channel->lock, flags); 672 + list_splice_init(&channel->sc_list, &list); 673 + channel->num_sc = 0; 674 + spin_unlock_irqrestore(&channel->lock, flags); 675 + 676 + list_for_each_entry_safe(cur_channel, tmp, &list, sc_list) { 677 + if (cur_channel->rescind) 678 + wait_for_completion(&cur_channel->rescind_event); 679 + 680 + mutex_lock(&vmbus_connection.channel_mutex); 681 + if (vmbus_close_internal(cur_channel) == 0) { 682 + vmbus_free_ring(cur_channel); 683 + 684 + if (cur_channel->rescind) 685 + hv_process_channel_removal(cur_channel); 686 + } 687 + mutex_unlock(&vmbus_connection.channel_mutex); 688 + } 689 + 690 + /* 691 + * Now close the primary. 692 + */ 693 + mutex_lock(&vmbus_connection.channel_mutex); 694 + ret = vmbus_close_internal(channel); 695 + mutex_unlock(&vmbus_connection.channel_mutex); 696 + 697 + return ret; 698 + } 699 + EXPORT_SYMBOL_GPL(vmbus_disconnect_ring); 698 700 699 701 /* 700 702 * vmbus_close - Close the specified channel 701 703 */ 702 704 void vmbus_close(struct vmbus_channel *channel) 703 705 { 704 - struct list_head *cur, *tmp; 705 - struct vmbus_channel *cur_channel; 706 - 707 - if (channel->primary_channel != NULL) { 708 - /* 709 - * We will only close sub-channels when 710 - * the primary is closed. 711 - */ 712 - return; 713 - } 714 - /* 715 - * Close all the sub-channels first and then close the 716 - * primary channel. 717 - */ 718 - list_for_each_safe(cur, tmp, &channel->sc_list) { 719 - cur_channel = list_entry(cur, struct vmbus_channel, sc_list); 720 - if (cur_channel->rescind) { 721 - wait_for_completion(&cur_channel->rescind_event); 722 - mutex_lock(&vmbus_connection.channel_mutex); 723 - vmbus_close_internal(cur_channel); 724 - hv_process_channel_removal(cur_channel); 725 - } else { 726 - mutex_lock(&vmbus_connection.channel_mutex); 727 - vmbus_close_internal(cur_channel); 728 - } 729 - mutex_unlock(&vmbus_connection.channel_mutex); 730 - } 731 - /* 732 - * Now close the primary. 733 - */ 734 - mutex_lock(&vmbus_connection.channel_mutex); 735 - vmbus_close_internal(channel); 736 - mutex_unlock(&vmbus_connection.channel_mutex); 706 + if (vmbus_disconnect_ring(channel) == 0) 707 + vmbus_free_ring(channel); 737 708 } 738 709 EXPORT_SYMBOL_GPL(vmbus_close); 739 710
+1
drivers/hv/ring_buffer.c
··· 241 241 void hv_ringbuffer_cleanup(struct hv_ring_buffer_info *ring_info) 242 242 { 243 243 vunmap(ring_info->ring_buffer); 244 + ring_info->ring_buffer = NULL; 244 245 } 245 246 246 247 /* Write to the ring buffer. */
+9
include/linux/hyperv.h
··· 741 741 /* Allocated memory for ring buffer */ 742 742 struct page *ringbuffer_page; 743 743 u32 ringbuffer_pagecount; 744 + u32 ringbuffer_send_offset; 744 745 struct hv_ring_buffer_info outbound; /* send to parent */ 745 746 struct hv_ring_buffer_info inbound; /* receive from parent */ 746 747 ··· 1022 1021 struct hv_mpb_array range; 1023 1022 } __packed; 1024 1023 1024 + int vmbus_alloc_ring(struct vmbus_channel *channel, 1025 + u32 send_size, u32 recv_size); 1026 + void vmbus_free_ring(struct vmbus_channel *channel); 1027 + 1028 + int vmbus_connect_ring(struct vmbus_channel *channel, 1029 + void (*onchannel_callback)(void *context), 1030 + void *context); 1031 + int vmbus_disconnect_ring(struct vmbus_channel *channel); 1025 1032 1026 1033 extern int vmbus_open(struct vmbus_channel *channel, 1027 1034 u32 send_ringbuffersize,