@@ -372,6 +372,7 @@ static void vhost_vq_reset(struct vhost_dev *dev,
372372 vq -> avail = NULL ;
373373 vq -> used = NULL ;
374374 vq -> last_avail_idx = 0 ;
375+ vq -> next_avail_head = 0 ;
375376 vq -> avail_idx = 0 ;
376377 vq -> last_used_idx = 0 ;
377378 vq -> signalled_used = 0 ;
@@ -501,6 +502,8 @@ static void vhost_vq_free_iovecs(struct vhost_virtqueue *vq)
501502 vq -> log = NULL ;
502503 kfree (vq -> heads );
503504 vq -> heads = NULL ;
505+ kfree (vq -> nheads );
506+ vq -> nheads = NULL ;
504507}
505508
506509/* Helper to allocate iovec buffers for all vqs. */
@@ -518,7 +521,9 @@ static long vhost_dev_alloc_iovecs(struct vhost_dev *dev)
518521 GFP_KERNEL );
519522 vq -> heads = kmalloc_array (dev -> iov_limit , sizeof (* vq -> heads ),
520523 GFP_KERNEL );
521- if (!vq -> indirect || !vq -> log || !vq -> heads )
524+ vq -> nheads = kmalloc_array (dev -> iov_limit , sizeof (* vq -> nheads ),
525+ GFP_KERNEL );
526+ if (!vq -> indirect || !vq -> log || !vq -> heads || !vq -> nheads )
522527 goto err_nomem ;
523528 }
524529 return 0 ;
@@ -2159,14 +2164,15 @@ long vhost_vring_ioctl(struct vhost_dev *d, unsigned int ioctl, void __user *arg
21592164 break ;
21602165 }
21612166 if (vhost_has_feature (vq , VIRTIO_F_RING_PACKED )) {
2162- vq -> last_avail_idx = s .num & 0xffff ;
2167+ vq -> next_avail_head = vq -> last_avail_idx =
2168+ s .num & 0xffff ;
21632169 vq -> last_used_idx = (s .num >> 16 ) & 0xffff ;
21642170 } else {
21652171 if (s .num > 0xffff ) {
21662172 r = - EINVAL ;
21672173 break ;
21682174 }
2169- vq -> last_avail_idx = s .num ;
2175+ vq -> next_avail_head = vq -> last_avail_idx = s .num ;
21702176 }
21712177 /* Forget the cached index value. */
21722178 vq -> avail_idx = vq -> last_avail_idx ;
@@ -2798,11 +2804,12 @@ int vhost_get_vq_desc(struct vhost_virtqueue *vq,
27982804 unsigned int * out_num , unsigned int * in_num ,
27992805 struct vhost_log * log , unsigned int * log_num )
28002806{
2807+ bool in_order = vhost_has_feature (vq , VIRTIO_F_IN_ORDER );
28012808 struct vring_desc desc ;
28022809 unsigned int i , head , found = 0 ;
28032810 u16 last_avail_idx = vq -> last_avail_idx ;
28042811 __virtio16 ring_head ;
2805- int ret , access ;
2812+ int ret , access , c = 0 ;
28062813
28072814 if (vq -> avail_idx == vq -> last_avail_idx ) {
28082815 ret = vhost_get_avail_idx (vq );
@@ -2813,17 +2820,21 @@ int vhost_get_vq_desc(struct vhost_virtqueue *vq,
28132820 return vq -> num ;
28142821 }
28152822
2816- /* Grab the next descriptor number they're advertising, and increment
2817- * the index we've seen. */
2818- if (unlikely (vhost_get_avail_head (vq , & ring_head , last_avail_idx ))) {
2819- vq_err (vq , "Failed to read head: idx %d address %p\n" ,
2820- last_avail_idx ,
2821- & vq -> avail -> ring [last_avail_idx % vq -> num ]);
2822- return - EFAULT ;
2823+ if (in_order )
2824+ head = vq -> next_avail_head & (vq -> num - 1 );
2825+ else {
2826+ /* Grab the next descriptor number they're
2827+ * advertising, and increment the index we've seen. */
2828+ if (unlikely (vhost_get_avail_head (vq , & ring_head ,
2829+ last_avail_idx ))) {
2830+ vq_err (vq , "Failed to read head: idx %d address %p\n" ,
2831+ last_avail_idx ,
2832+ & vq -> avail -> ring [last_avail_idx % vq -> num ]);
2833+ return - EFAULT ;
2834+ }
2835+ head = vhost16_to_cpu (vq , ring_head );
28232836 }
28242837
2825- head = vhost16_to_cpu (vq , ring_head );
2826-
28272838 /* If their number is silly, that's an error. */
28282839 if (unlikely (head >= vq -> num )) {
28292840 vq_err (vq , "Guest says index %u > %u is available" ,
@@ -2866,6 +2877,7 @@ int vhost_get_vq_desc(struct vhost_virtqueue *vq,
28662877 "in indirect descriptor at idx %d\n" , i );
28672878 return ret ;
28682879 }
2880+ ++ c ;
28692881 continue ;
28702882 }
28712883
@@ -2901,10 +2913,12 @@ int vhost_get_vq_desc(struct vhost_virtqueue *vq,
29012913 }
29022914 * out_num += ret ;
29032915 }
2916+ ++ c ;
29042917 } while ((i = next_desc (vq , & desc )) != -1 );
29052918
29062919 /* On success, increment avail index. */
29072920 vq -> last_avail_idx ++ ;
2921+ vq -> next_avail_head += c ;
29082922
29092923 /* Assume notifications from guest are disabled at this point,
29102924 * if they aren't we would need to update avail_event index. */
@@ -2928,8 +2942,9 @@ int vhost_add_used(struct vhost_virtqueue *vq, unsigned int head, int len)
29282942 cpu_to_vhost32 (vq , head ),
29292943 cpu_to_vhost32 (vq , len )
29302944 };
2945+ u16 nheads = 1 ;
29312946
2932- return vhost_add_used_n (vq , & heads , 1 );
2947+ return vhost_add_used_n (vq , & heads , & nheads , 1 );
29332948}
29342949EXPORT_SYMBOL_GPL (vhost_add_used );
29352950
@@ -2965,10 +2980,9 @@ static int __vhost_add_used_n(struct vhost_virtqueue *vq,
29652980 return 0 ;
29662981}
29672982
2968- /* After we've used one of their buffers, we tell them about it. We'll then
2969- * want to notify the guest, using eventfd. */
2970- int vhost_add_used_n (struct vhost_virtqueue * vq , struct vring_used_elem * heads ,
2971- unsigned count )
2983+ static int vhost_add_used_n_ooo (struct vhost_virtqueue * vq ,
2984+ struct vring_used_elem * heads ,
2985+ unsigned count )
29722986{
29732987 int start , n , r ;
29742988
@@ -2981,7 +2995,69 @@ int vhost_add_used_n(struct vhost_virtqueue *vq, struct vring_used_elem *heads,
29812995 heads += n ;
29822996 count -= n ;
29832997 }
2984- r = __vhost_add_used_n (vq , heads , count );
2998+ return __vhost_add_used_n (vq , heads , count );
2999+ }
3000+
3001+ static int vhost_add_used_n_in_order (struct vhost_virtqueue * vq ,
3002+ struct vring_used_elem * heads ,
3003+ const u16 * nheads ,
3004+ unsigned count )
3005+ {
3006+ vring_used_elem_t __user * used ;
3007+ u16 old , new = vq -> last_used_idx ;
3008+ int start , i ;
3009+
3010+ if (!nheads )
3011+ return - EINVAL ;
3012+
3013+ start = vq -> last_used_idx & (vq -> num - 1 );
3014+ used = vq -> used -> ring + start ;
3015+
3016+ for (i = 0 ; i < count ; i ++ ) {
3017+ if (vhost_put_used (vq , & heads [i ], start , 1 )) {
3018+ vq_err (vq , "Failed to write used" );
3019+ return - EFAULT ;
3020+ }
3021+ start += nheads [i ];
3022+ new += nheads [i ];
3023+ if (start >= vq -> num )
3024+ start -= vq -> num ;
3025+ }
3026+
3027+ if (unlikely (vq -> log_used )) {
3028+ /* Make sure data is seen before log. */
3029+ smp_wmb ();
3030+ /* Log used ring entry write. */
3031+ log_used (vq , ((void __user * )used - (void __user * )vq -> used ),
3032+ (vq -> num - start ) * sizeof * used );
3033+ if (start + count > vq -> num )
3034+ log_used (vq , 0 ,
3035+ (start + count - vq -> num ) * sizeof * used );
3036+ }
3037+
3038+ old = vq -> last_used_idx ;
3039+ vq -> last_used_idx = new ;
3040+ /* If the driver never bothers to signal in a very long while,
3041+ * used index might wrap around. If that happens, invalidate
3042+ * signalled_used index we stored. TODO: make sure driver
3043+ * signals at least once in 2^16 and remove this. */
3044+ if (unlikely ((u16 )(new - vq -> signalled_used ) < (u16 )(new - old )))
3045+ vq -> signalled_used_valid = false;
3046+ return 0 ;
3047+ }
3048+
3049+ /* After we've used one of their buffers, we tell them about it. We'll then
3050+ * want to notify the guest, using eventfd. */
3051+ int vhost_add_used_n (struct vhost_virtqueue * vq , struct vring_used_elem * heads ,
3052+ u16 * nheads , unsigned count )
3053+ {
3054+ bool in_order = vhost_has_feature (vq , VIRTIO_F_IN_ORDER );
3055+ int r ;
3056+
3057+ if (!in_order || !nheads )
3058+ r = vhost_add_used_n_ooo (vq , heads , count );
3059+ else
3060+ r = vhost_add_used_n_in_order (vq , heads , nheads , count );
29853061
29863062 if (r < 0 )
29873063 return r ;
@@ -3064,9 +3140,11 @@ EXPORT_SYMBOL_GPL(vhost_add_used_and_signal);
30643140/* multi-buffer version of vhost_add_used_and_signal */
30653141void vhost_add_used_and_signal_n (struct vhost_dev * dev ,
30663142 struct vhost_virtqueue * vq ,
3067- struct vring_used_elem * heads , unsigned count )
3143+ struct vring_used_elem * heads ,
3144+ u16 * nheads ,
3145+ unsigned count )
30683146{
3069- vhost_add_used_n (vq , heads , count );
3147+ vhost_add_used_n (vq , heads , nheads , count );
30703148 vhost_signal (dev , vq );
30713149}
30723150EXPORT_SYMBOL_GPL (vhost_add_used_and_signal_n );
0 commit comments