aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/vhost/vhost.c
diff options
context:
space:
mode:
authorDavid Stevens <dlstevens@us.ibm.com>2010-07-27 11:52:21 -0400
committerMichael S. Tsirkin <mst@redhat.com>2010-07-28 08:45:36 -0400
commit8dd014adfea6f173c1ef6378f7e5e7924866c923 (patch)
tree303df47f73c53cc4c919e3f8aca8245d50d76a08 /drivers/vhost/vhost.c
parent9e3d195720d1c8b1e09013185ab8c3b749180fc2 (diff)
vhost-net: mergeable buffers support
This adds support for mergeable buffers in vhost-net: this is needed for older guests without indirect buffer support, as well as for zero copy with some devices. Includes changes by Michael S. Tsirkin to make the patch as low risk as possible (i.e., close to no changes when feature is disabled). Signed-off-by: David Stevens <dlstevens@us.ibm.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Diffstat (limited to 'drivers/vhost/vhost.c')
-rw-r--r--drivers/vhost/vhost.c79
1 files changed, 75 insertions, 4 deletions
diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c
index dd2d019b889f..e05557d52999 100644
--- a/drivers/vhost/vhost.c
+++ b/drivers/vhost/vhost.c
@@ -149,7 +149,8 @@ static void vhost_vq_reset(struct vhost_dev *dev,
149 vq->used_flags = 0; 149 vq->used_flags = 0;
150 vq->log_used = false; 150 vq->log_used = false;
151 vq->log_addr = -1ull; 151 vq->log_addr = -1ull;
152 vq->hdr_size = 0; 152 vq->vhost_hlen = 0;
153 vq->sock_hlen = 0;
153 vq->private_data = NULL; 154 vq->private_data = NULL;
154 vq->log_base = NULL; 155 vq->log_base = NULL;
155 vq->error_ctx = NULL; 156 vq->error_ctx = NULL;
@@ -1101,9 +1102,9 @@ int vhost_get_vq_desc(struct vhost_dev *dev, struct vhost_virtqueue *vq,
1101} 1102}
1102 1103
1103/* Reverse the effect of vhost_get_vq_desc. Useful for error handling. */ 1104/* Reverse the effect of vhost_get_vq_desc. Useful for error handling. */
1104void vhost_discard_vq_desc(struct vhost_virtqueue *vq) 1105void vhost_discard_vq_desc(struct vhost_virtqueue *vq, int n)
1105{ 1106{
1106 vq->last_avail_idx--; 1107 vq->last_avail_idx -= n;
1107} 1108}
1108 1109
1109/* After we've used one of their buffers, we tell them about it. We'll then 1110/* After we've used one of their buffers, we tell them about it. We'll then
@@ -1148,6 +1149,67 @@ int vhost_add_used(struct vhost_virtqueue *vq, unsigned int head, int len)
1148 return 0; 1149 return 0;
1149} 1150}
1150 1151
1152static int __vhost_add_used_n(struct vhost_virtqueue *vq,
1153 struct vring_used_elem *heads,
1154 unsigned count)
1155{
1156 struct vring_used_elem __user *used;
1157 int start;
1158
1159 start = vq->last_used_idx % vq->num;
1160 used = vq->used->ring + start;
1161 if (copy_to_user(used, heads, count * sizeof *used)) {
1162 vq_err(vq, "Failed to write used");
1163 return -EFAULT;
1164 }
1165 if (unlikely(vq->log_used)) {
1166 /* Make sure data is seen before log. */
1167 smp_wmb();
1168 /* Log used ring entry write. */
1169 log_write(vq->log_base,
1170 vq->log_addr +
1171 ((void __user *)used - (void __user *)vq->used),
1172 count * sizeof *used);
1173 }
1174 vq->last_used_idx += count;
1175 return 0;
1176}
1177
1178/* After we've used one of their buffers, we tell them about it. We'll then
1179 * want to notify the guest, using eventfd. */
1180int vhost_add_used_n(struct vhost_virtqueue *vq, struct vring_used_elem *heads,
1181 unsigned count)
1182{
1183 int start, n, r;
1184
1185 start = vq->last_used_idx % vq->num;
1186 n = vq->num - start;
1187 if (n < count) {
1188 r = __vhost_add_used_n(vq, heads, n);
1189 if (r < 0)
1190 return r;
1191 heads += n;
1192 count -= n;
1193 }
1194 r = __vhost_add_used_n(vq, heads, count);
1195
1196 /* Make sure buffer is written before we update index. */
1197 smp_wmb();
1198 if (put_user(vq->last_used_idx, &vq->used->idx)) {
1199 vq_err(vq, "Failed to increment used idx");
1200 return -EFAULT;
1201 }
1202 if (unlikely(vq->log_used)) {
1203 /* Log used index update. */
1204 log_write(vq->log_base,
1205 vq->log_addr + offsetof(struct vring_used, idx),
1206 sizeof vq->used->idx);
1207 if (vq->log_ctx)
1208 eventfd_signal(vq->log_ctx, 1);
1209 }
1210 return r;
1211}
1212
1151/* This actually signals the guest, using eventfd. */ 1213/* This actually signals the guest, using eventfd. */
1152void vhost_signal(struct vhost_dev *dev, struct vhost_virtqueue *vq) 1214void vhost_signal(struct vhost_dev *dev, struct vhost_virtqueue *vq)
1153{ 1215{
@@ -1182,6 +1244,15 @@ void vhost_add_used_and_signal(struct vhost_dev *dev,
1182 vhost_signal(dev, vq); 1244 vhost_signal(dev, vq);
1183} 1245}
1184 1246
1247/* multi-buffer version of vhost_add_used_and_signal */
1248void vhost_add_used_and_signal_n(struct vhost_dev *dev,
1249 struct vhost_virtqueue *vq,
1250 struct vring_used_elem *heads, unsigned count)
1251{
1252 vhost_add_used_n(vq, heads, count);
1253 vhost_signal(dev, vq);
1254}
1255
1185/* OK, now we need to know about added descriptors. */ 1256/* OK, now we need to know about added descriptors. */
1186bool vhost_enable_notify(struct vhost_virtqueue *vq) 1257bool vhost_enable_notify(struct vhost_virtqueue *vq)
1187{ 1258{
@@ -1206,7 +1277,7 @@ bool vhost_enable_notify(struct vhost_virtqueue *vq)
1206 return false; 1277 return false;
1207 } 1278 }
1208 1279
1209 return avail_idx != vq->last_avail_idx; 1280 return avail_idx != vq->avail_idx;
1210} 1281}
1211 1282
1212/* We don't need to be notified again. */ 1283/* We don't need to be notified again. */