xref: /qemu/hw/vfio-user/device.c (revision aec6836c73403cffa56b9a4c5556451ee16071fe)
13bdb738bSJohn Levon /*
23bdb738bSJohn Levon  * vfio protocol over a UNIX socket device handling.
33bdb738bSJohn Levon  *
43bdb738bSJohn Levon  * Copyright © 2018, 2021 Oracle and/or its affiliates.
53bdb738bSJohn Levon  *
63bdb738bSJohn Levon  * SPDX-License-Identifier: GPL-2.0-or-later
73bdb738bSJohn Levon  */
83bdb738bSJohn Levon 
93bdb738bSJohn Levon #include "qemu/osdep.h"
103bdb738bSJohn Levon #include "qapi/error.h"
113bdb738bSJohn Levon #include "qemu/error-report.h"
12*1a0c32a9SJohn Levon #include "qemu/lockable.h"
13*1a0c32a9SJohn Levon #include "qemu/thread.h"
143bdb738bSJohn Levon 
153bdb738bSJohn Levon #include "hw/vfio-user/device.h"
163bdb738bSJohn Levon #include "hw/vfio-user/trace.h"
173bdb738bSJohn Levon 
183bdb738bSJohn Levon /*
193bdb738bSJohn Levon  * These are to defend against a malign server trying
203bdb738bSJohn Levon  * to force us to run out of memory.
213bdb738bSJohn Levon  */
223bdb738bSJohn Levon #define VFIO_USER_MAX_REGIONS   100
233bdb738bSJohn Levon #define VFIO_USER_MAX_IRQS      50
243bdb738bSJohn Levon 
vfio_user_get_device_info(VFIOUserProxy * proxy,struct vfio_device_info * info,Error ** errp)253bdb738bSJohn Levon bool vfio_user_get_device_info(VFIOUserProxy *proxy,
263bdb738bSJohn Levon                                struct vfio_device_info *info, Error **errp)
273bdb738bSJohn Levon {
283bdb738bSJohn Levon     VFIOUserDeviceInfo msg;
293bdb738bSJohn Levon     uint32_t argsz = sizeof(msg) - sizeof(msg.hdr);
303bdb738bSJohn Levon 
313bdb738bSJohn Levon     memset(&msg, 0, sizeof(msg));
323bdb738bSJohn Levon     vfio_user_request_msg(&msg.hdr, VFIO_USER_DEVICE_GET_INFO, sizeof(msg), 0);
333bdb738bSJohn Levon     msg.argsz = argsz;
343bdb738bSJohn Levon 
353bdb738bSJohn Levon     if (!vfio_user_send_wait(proxy, &msg.hdr, NULL, 0, errp)) {
363bdb738bSJohn Levon         return false;
373bdb738bSJohn Levon     }
383bdb738bSJohn Levon 
393bdb738bSJohn Levon     if (msg.hdr.flags & VFIO_USER_ERROR) {
403bdb738bSJohn Levon         error_setg_errno(errp, -msg.hdr.error_reply,
413bdb738bSJohn Levon                          "VFIO_USER_DEVICE_GET_INFO failed");
423bdb738bSJohn Levon         return false;
433bdb738bSJohn Levon     }
443bdb738bSJohn Levon 
453bdb738bSJohn Levon     trace_vfio_user_get_info(msg.num_regions, msg.num_irqs);
463bdb738bSJohn Levon 
473bdb738bSJohn Levon     memcpy(info, &msg.argsz, argsz);
483bdb738bSJohn Levon 
493bdb738bSJohn Levon     /* defend against a malicious server */
503bdb738bSJohn Levon     if (info->num_regions > VFIO_USER_MAX_REGIONS ||
513bdb738bSJohn Levon         info->num_irqs > VFIO_USER_MAX_IRQS) {
523bdb738bSJohn Levon         error_setg_errno(errp, EINVAL, "invalid reply");
533bdb738bSJohn Levon         return false;
543bdb738bSJohn Levon     }
553bdb738bSJohn Levon 
563bdb738bSJohn Levon     return true;
573bdb738bSJohn Levon }
58667866d6SJohn Levon 
vfio_user_device_reset(VFIOUserProxy * proxy)5901923235SJohn Levon void vfio_user_device_reset(VFIOUserProxy *proxy)
6001923235SJohn Levon {
6101923235SJohn Levon     Error *local_err = NULL;
6201923235SJohn Levon     VFIOUserHdr hdr;
6301923235SJohn Levon 
6401923235SJohn Levon     vfio_user_request_msg(&hdr, VFIO_USER_DEVICE_RESET, sizeof(hdr), 0);
6501923235SJohn Levon 
6601923235SJohn Levon     if (!vfio_user_send_wait(proxy, &hdr, NULL, 0, &local_err)) {
6701923235SJohn Levon         error_prepend(&local_err, "%s: ", __func__);
6801923235SJohn Levon         error_report_err(local_err);
6901923235SJohn Levon         return;
7001923235SJohn Levon     }
7101923235SJohn Levon 
7201923235SJohn Levon     if (hdr.flags & VFIO_USER_ERROR) {
7301923235SJohn Levon         error_printf("reset reply error %d\n", hdr.error_reply);
7401923235SJohn Levon     }
7501923235SJohn Levon }
7601923235SJohn Levon 
vfio_user_get_region_info(VFIOUserProxy * proxy,struct vfio_region_info * info,VFIOUserFDs * fds)77667866d6SJohn Levon static int vfio_user_get_region_info(VFIOUserProxy *proxy,
78667866d6SJohn Levon                                      struct vfio_region_info *info,
79667866d6SJohn Levon                                      VFIOUserFDs *fds)
80667866d6SJohn Levon {
81667866d6SJohn Levon     g_autofree VFIOUserRegionInfo *msgp = NULL;
82667866d6SJohn Levon     Error *local_err = NULL;
83667866d6SJohn Levon     uint32_t size;
84667866d6SJohn Levon 
85667866d6SJohn Levon     /* data returned can be larger than vfio_region_info */
86667866d6SJohn Levon     if (info->argsz < sizeof(*info)) {
87667866d6SJohn Levon         error_printf("vfio_user_get_region_info argsz too small\n");
88667866d6SJohn Levon         return -E2BIG;
89667866d6SJohn Levon     }
90667866d6SJohn Levon     if (fds != NULL && fds->send_fds != 0) {
91667866d6SJohn Levon         error_printf("vfio_user_get_region_info can't send FDs\n");
92667866d6SJohn Levon         return -EINVAL;
93667866d6SJohn Levon     }
94667866d6SJohn Levon 
95667866d6SJohn Levon     size = info->argsz + sizeof(VFIOUserHdr);
96667866d6SJohn Levon     msgp = g_malloc0(size);
97667866d6SJohn Levon 
98667866d6SJohn Levon     vfio_user_request_msg(&msgp->hdr, VFIO_USER_DEVICE_GET_REGION_INFO,
99667866d6SJohn Levon                           sizeof(*msgp), 0);
100667866d6SJohn Levon     msgp->argsz = info->argsz;
101667866d6SJohn Levon     msgp->index = info->index;
102667866d6SJohn Levon 
103667866d6SJohn Levon     if (!vfio_user_send_wait(proxy, &msgp->hdr, fds, size, &local_err)) {
104667866d6SJohn Levon         error_prepend(&local_err, "%s: ", __func__);
105667866d6SJohn Levon         error_report_err(local_err);
106667866d6SJohn Levon         return -EFAULT;
107667866d6SJohn Levon     }
108667866d6SJohn Levon 
109667866d6SJohn Levon     if (msgp->hdr.flags & VFIO_USER_ERROR) {
110667866d6SJohn Levon         return -msgp->hdr.error_reply;
111667866d6SJohn Levon     }
112667866d6SJohn Levon     trace_vfio_user_get_region_info(msgp->index, msgp->flags, msgp->size);
113667866d6SJohn Levon 
114667866d6SJohn Levon     memcpy(info, &msgp->argsz, info->argsz);
11598a906d9SJohn Levon 
11698a906d9SJohn Levon     /*
11798a906d9SJohn Levon      * If at least one region is directly mapped into the VM, then we can no
11898a906d9SJohn Levon      * longer rely on the sequential nature of vfio-user request handling to
11998a906d9SJohn Levon      * ensure that posted writes are completed before a subsequent read. In this
12098a906d9SJohn Levon      * case, disable posted write support. This is a per-device property, not
12198a906d9SJohn Levon      * per-region.
12298a906d9SJohn Levon      */
12398a906d9SJohn Levon     if (info->flags & VFIO_REGION_INFO_FLAG_MMAP) {
12498a906d9SJohn Levon         vfio_user_disable_posted_writes(proxy);
125667866d6SJohn Levon     }
126667866d6SJohn Levon 
12798a906d9SJohn Levon     return 0;
12898a906d9SJohn Levon }
129667866d6SJohn Levon 
vfio_user_device_io_get_region_info(VFIODevice * vbasedev,struct vfio_region_info * info,int * fd)130667866d6SJohn Levon static int vfio_user_device_io_get_region_info(VFIODevice *vbasedev,
131667866d6SJohn Levon                                                struct vfio_region_info *info,
132667866d6SJohn Levon                                                int *fd)
133667866d6SJohn Levon {
134667866d6SJohn Levon     VFIOUserFDs fds = { 0, 1, fd};
135667866d6SJohn Levon     int ret;
136667866d6SJohn Levon 
137667866d6SJohn Levon     if (info->index > vbasedev->num_regions) {
138667866d6SJohn Levon         return -EINVAL;
139667866d6SJohn Levon     }
140667866d6SJohn Levon 
141667866d6SJohn Levon     ret = vfio_user_get_region_info(vbasedev->proxy, info, &fds);
142667866d6SJohn Levon     if (ret) {
143667866d6SJohn Levon         return ret;
144667866d6SJohn Levon     }
145667866d6SJohn Levon 
146667866d6SJohn Levon     /* cap_offset in valid area */
147667866d6SJohn Levon     if ((info->flags & VFIO_REGION_INFO_FLAG_CAPS) &&
148667866d6SJohn Levon         (info->cap_offset < sizeof(*info) || info->cap_offset > info->argsz)) {
149667866d6SJohn Levon         return -EINVAL;
150667866d6SJohn Levon     }
151667866d6SJohn Levon 
152667866d6SJohn Levon     return 0;
153667866d6SJohn Levon }
154667866d6SJohn Levon 
vfio_user_device_io_get_irq_info(VFIODevice * vbasedev,struct vfio_irq_info * info)155ca1add16SJohn Levon static int vfio_user_device_io_get_irq_info(VFIODevice *vbasedev,
156ca1add16SJohn Levon                                             struct vfio_irq_info *info)
157ca1add16SJohn Levon {
158ca1add16SJohn Levon     VFIOUserProxy *proxy = vbasedev->proxy;
159ca1add16SJohn Levon     Error *local_err = NULL;
160ca1add16SJohn Levon     VFIOUserIRQInfo msg;
161ca1add16SJohn Levon 
162ca1add16SJohn Levon     memset(&msg, 0, sizeof(msg));
163ca1add16SJohn Levon     vfio_user_request_msg(&msg.hdr, VFIO_USER_DEVICE_GET_IRQ_INFO,
164ca1add16SJohn Levon                           sizeof(msg), 0);
165ca1add16SJohn Levon     msg.argsz = info->argsz;
166ca1add16SJohn Levon     msg.index = info->index;
167ca1add16SJohn Levon 
168ca1add16SJohn Levon     if (!vfio_user_send_wait(proxy, &msg.hdr, NULL, 0, &local_err)) {
169ca1add16SJohn Levon         error_prepend(&local_err, "%s: ", __func__);
170ca1add16SJohn Levon         error_report_err(local_err);
171ca1add16SJohn Levon         return -EFAULT;
172ca1add16SJohn Levon     }
173ca1add16SJohn Levon 
174ca1add16SJohn Levon     if (msg.hdr.flags & VFIO_USER_ERROR) {
175ca1add16SJohn Levon         return -msg.hdr.error_reply;
176ca1add16SJohn Levon     }
177ca1add16SJohn Levon     trace_vfio_user_get_irq_info(msg.index, msg.flags, msg.count);
178ca1add16SJohn Levon 
179ca1add16SJohn Levon     memcpy(info, &msg.argsz, sizeof(*info));
180ca1add16SJohn Levon     return 0;
181ca1add16SJohn Levon }
182ca1add16SJohn Levon 
irq_howmany(int * fdp,uint32_t cur,uint32_t max)183ca1add16SJohn Levon static int irq_howmany(int *fdp, uint32_t cur, uint32_t max)
184ca1add16SJohn Levon {
185ca1add16SJohn Levon     int n = 0;
186ca1add16SJohn Levon 
187ca1add16SJohn Levon     if (fdp[cur] != -1) {
188ca1add16SJohn Levon         do {
189ca1add16SJohn Levon             n++;
190ca1add16SJohn Levon         } while (n < max && fdp[cur + n] != -1);
191ca1add16SJohn Levon     } else {
192ca1add16SJohn Levon         do {
193ca1add16SJohn Levon             n++;
194ca1add16SJohn Levon         } while (n < max && fdp[cur + n] == -1);
195ca1add16SJohn Levon     }
196ca1add16SJohn Levon 
197ca1add16SJohn Levon     return n;
198ca1add16SJohn Levon }
199ca1add16SJohn Levon 
vfio_user_device_io_set_irqs(VFIODevice * vbasedev,struct vfio_irq_set * irq)200ca1add16SJohn Levon static int vfio_user_device_io_set_irqs(VFIODevice *vbasedev,
201ca1add16SJohn Levon                                         struct vfio_irq_set *irq)
202ca1add16SJohn Levon {
203ca1add16SJohn Levon     VFIOUserProxy *proxy = vbasedev->proxy;
204ca1add16SJohn Levon     g_autofree VFIOUserIRQSet *msgp = NULL;
205ca1add16SJohn Levon     uint32_t size, nfds, send_fds, sent_fds, max;
206ca1add16SJohn Levon     Error *local_err = NULL;
207ca1add16SJohn Levon 
208ca1add16SJohn Levon     if (irq->argsz < sizeof(*irq)) {
209ca1add16SJohn Levon         error_printf("vfio_user_set_irqs argsz too small\n");
210ca1add16SJohn Levon         return -EINVAL;
211ca1add16SJohn Levon     }
212ca1add16SJohn Levon 
213ca1add16SJohn Levon     /*
214ca1add16SJohn Levon      * Handle simple case
215ca1add16SJohn Levon      */
216ca1add16SJohn Levon     if ((irq->flags & VFIO_IRQ_SET_DATA_EVENTFD) == 0) {
217ca1add16SJohn Levon         size = sizeof(VFIOUserHdr) + irq->argsz;
218ca1add16SJohn Levon         msgp = g_malloc0(size);
219ca1add16SJohn Levon 
220ca1add16SJohn Levon         vfio_user_request_msg(&msgp->hdr, VFIO_USER_DEVICE_SET_IRQS, size, 0);
221ca1add16SJohn Levon         msgp->argsz = irq->argsz;
222ca1add16SJohn Levon         msgp->flags = irq->flags;
223ca1add16SJohn Levon         msgp->index = irq->index;
224ca1add16SJohn Levon         msgp->start = irq->start;
225ca1add16SJohn Levon         msgp->count = irq->count;
226ca1add16SJohn Levon         trace_vfio_user_set_irqs(msgp->index, msgp->start, msgp->count,
227ca1add16SJohn Levon                                  msgp->flags);
228ca1add16SJohn Levon 
229ca1add16SJohn Levon         if (!vfio_user_send_wait(proxy, &msgp->hdr, NULL, 0, &local_err)) {
230ca1add16SJohn Levon             error_prepend(&local_err, "%s: ", __func__);
231ca1add16SJohn Levon             error_report_err(local_err);
232ca1add16SJohn Levon             return -EFAULT;
233ca1add16SJohn Levon         }
234ca1add16SJohn Levon 
235ca1add16SJohn Levon         if (msgp->hdr.flags & VFIO_USER_ERROR) {
236ca1add16SJohn Levon             return -msgp->hdr.error_reply;
237ca1add16SJohn Levon         }
238ca1add16SJohn Levon 
239ca1add16SJohn Levon         return 0;
240ca1add16SJohn Levon     }
241ca1add16SJohn Levon 
242ca1add16SJohn Levon     /*
243ca1add16SJohn Levon      * Calculate the number of FDs to send
244ca1add16SJohn Levon      * and adjust argsz
245ca1add16SJohn Levon      */
246ca1add16SJohn Levon     nfds = (irq->argsz - sizeof(*irq)) / sizeof(int);
247ca1add16SJohn Levon     irq->argsz = sizeof(*irq);
248ca1add16SJohn Levon     msgp = g_malloc0(sizeof(*msgp));
249ca1add16SJohn Levon     /*
250ca1add16SJohn Levon      * Send in chunks if over max_send_fds
251ca1add16SJohn Levon      */
252ca1add16SJohn Levon     for (sent_fds = 0; nfds > sent_fds; sent_fds += send_fds) {
253ca1add16SJohn Levon         VFIOUserFDs *arg_fds, loop_fds;
254ca1add16SJohn Levon 
255ca1add16SJohn Levon         /* must send all valid FDs or all invalid FDs in single msg */
256ca1add16SJohn Levon         max = nfds - sent_fds;
257ca1add16SJohn Levon         if (max > proxy->max_send_fds) {
258ca1add16SJohn Levon             max = proxy->max_send_fds;
259ca1add16SJohn Levon         }
260ca1add16SJohn Levon         send_fds = irq_howmany((int *)irq->data, sent_fds, max);
261ca1add16SJohn Levon 
262ca1add16SJohn Levon         vfio_user_request_msg(&msgp->hdr, VFIO_USER_DEVICE_SET_IRQS,
263ca1add16SJohn Levon                               sizeof(*msgp), 0);
264ca1add16SJohn Levon         msgp->argsz = irq->argsz;
265ca1add16SJohn Levon         msgp->flags = irq->flags;
266ca1add16SJohn Levon         msgp->index = irq->index;
267ca1add16SJohn Levon         msgp->start = irq->start + sent_fds;
268ca1add16SJohn Levon         msgp->count = send_fds;
269ca1add16SJohn Levon         trace_vfio_user_set_irqs(msgp->index, msgp->start, msgp->count,
270ca1add16SJohn Levon                                  msgp->flags);
271ca1add16SJohn Levon 
272ca1add16SJohn Levon         loop_fds.send_fds = send_fds;
273ca1add16SJohn Levon         loop_fds.recv_fds = 0;
274ca1add16SJohn Levon         loop_fds.fds = (int *)irq->data + sent_fds;
275ca1add16SJohn Levon         arg_fds = loop_fds.fds[0] != -1 ? &loop_fds : NULL;
276ca1add16SJohn Levon 
277ca1add16SJohn Levon         if (!vfio_user_send_wait(proxy, &msgp->hdr, arg_fds, 0, &local_err)) {
278ca1add16SJohn Levon             error_prepend(&local_err, "%s: ", __func__);
279ca1add16SJohn Levon             error_report_err(local_err);
280ca1add16SJohn Levon             return -EFAULT;
281ca1add16SJohn Levon         }
282ca1add16SJohn Levon 
283ca1add16SJohn Levon         if (msgp->hdr.flags & VFIO_USER_ERROR) {
284ca1add16SJohn Levon             return -msgp->hdr.error_reply;
285ca1add16SJohn Levon         }
286ca1add16SJohn Levon     }
287ca1add16SJohn Levon 
288ca1add16SJohn Levon     return 0;
289ca1add16SJohn Levon }
290ca1add16SJohn Levon 
vfio_user_device_io_region_read(VFIODevice * vbasedev,uint8_t index,off_t off,uint32_t count,void * data)2911ed50fcbSJohn Levon static int vfio_user_device_io_region_read(VFIODevice *vbasedev, uint8_t index,
2921ed50fcbSJohn Levon                                            off_t off, uint32_t count,
2931ed50fcbSJohn Levon                                            void *data)
2941ed50fcbSJohn Levon {
2951ed50fcbSJohn Levon     g_autofree VFIOUserRegionRW *msgp = NULL;
2961ed50fcbSJohn Levon     VFIOUserProxy *proxy = vbasedev->proxy;
2971ed50fcbSJohn Levon     int size = sizeof(*msgp) + count;
2981ed50fcbSJohn Levon     Error *local_err = NULL;
2991ed50fcbSJohn Levon 
3001ed50fcbSJohn Levon     if (count > proxy->max_xfer_size) {
3011ed50fcbSJohn Levon         return -EINVAL;
3021ed50fcbSJohn Levon     }
3031ed50fcbSJohn Levon 
3041ed50fcbSJohn Levon     msgp = g_malloc0(size);
3051ed50fcbSJohn Levon     vfio_user_request_msg(&msgp->hdr, VFIO_USER_REGION_READ, sizeof(*msgp), 0);
3061ed50fcbSJohn Levon     msgp->offset = off;
3071ed50fcbSJohn Levon     msgp->region = index;
3081ed50fcbSJohn Levon     msgp->count = count;
3091ed50fcbSJohn Levon     trace_vfio_user_region_rw(msgp->region, msgp->offset, msgp->count);
3101ed50fcbSJohn Levon 
3111ed50fcbSJohn Levon     if (!vfio_user_send_wait(proxy, &msgp->hdr, NULL, size, &local_err)) {
3121ed50fcbSJohn Levon         error_prepend(&local_err, "%s: ", __func__);
3131ed50fcbSJohn Levon         error_report_err(local_err);
3141ed50fcbSJohn Levon         return -EFAULT;
3151ed50fcbSJohn Levon     }
3161ed50fcbSJohn Levon 
3171ed50fcbSJohn Levon     if (msgp->hdr.flags & VFIO_USER_ERROR) {
3181ed50fcbSJohn Levon         return -msgp->hdr.error_reply;
3191ed50fcbSJohn Levon     } else if (msgp->count > count) {
3201ed50fcbSJohn Levon         return -E2BIG;
3211ed50fcbSJohn Levon     } else {
3221ed50fcbSJohn Levon         memcpy(data, &msgp->data, msgp->count);
3231ed50fcbSJohn Levon     }
3241ed50fcbSJohn Levon 
3251ed50fcbSJohn Levon     return msgp->count;
3261ed50fcbSJohn Levon }
3271ed50fcbSJohn Levon 
32898a906d9SJohn Levon /*
32998a906d9SJohn Levon  * If this is a posted write, and VFIO_PROXY_NO_POST is not set, then we are OK
33098a906d9SJohn Levon  * to send the write to the socket without waiting for the server's reply:
33198a906d9SJohn Levon  * a subsequent read (of any region) will not pass the posted write, as all
33298a906d9SJohn Levon  * messages are handled sequentially.
33398a906d9SJohn Levon  */
vfio_user_device_io_region_write(VFIODevice * vbasedev,uint8_t index,off_t off,unsigned count,void * data,bool post)3341ed50fcbSJohn Levon static int vfio_user_device_io_region_write(VFIODevice *vbasedev, uint8_t index,
3351ed50fcbSJohn Levon                                             off_t off, unsigned count,
3361ed50fcbSJohn Levon                                             void *data, bool post)
3371ed50fcbSJohn Levon {
33898a906d9SJohn Levon     VFIOUserRegionRW *msgp = NULL;
3391ed50fcbSJohn Levon     VFIOUserProxy *proxy = vbasedev->proxy;
3401ed50fcbSJohn Levon     int size = sizeof(*msgp) + count;
3411ed50fcbSJohn Levon     Error *local_err = NULL;
342*1a0c32a9SJohn Levon     bool can_multi;
34398a906d9SJohn Levon     int flags = 0;
3441ed50fcbSJohn Levon     int ret;
3451ed50fcbSJohn Levon 
3461ed50fcbSJohn Levon     if (count > proxy->max_xfer_size) {
3471ed50fcbSJohn Levon         return -EINVAL;
3481ed50fcbSJohn Levon     }
3491ed50fcbSJohn Levon 
35098a906d9SJohn Levon     if (proxy->flags & VFIO_PROXY_NO_POST) {
35198a906d9SJohn Levon         post = false;
35298a906d9SJohn Levon     }
35398a906d9SJohn Levon 
35498a906d9SJohn Levon     if (post) {
35598a906d9SJohn Levon         flags |= VFIO_USER_NO_REPLY;
35698a906d9SJohn Levon     }
35798a906d9SJohn Levon 
358*1a0c32a9SJohn Levon     /* write eligible to be in a WRITE_MULTI msg ? */
359*1a0c32a9SJohn Levon     can_multi = (proxy->flags & VFIO_PROXY_USE_MULTI) && post &&
360*1a0c32a9SJohn Levon         count <= VFIO_USER_MULTI_DATA;
361*1a0c32a9SJohn Levon 
362*1a0c32a9SJohn Levon     /*
363*1a0c32a9SJohn Levon      * This should be a rare case, so first check without the lock,
364*1a0c32a9SJohn Levon      * if we're wrong, vfio_send_queued() will flush any posted writes
365*1a0c32a9SJohn Levon      * we missed here
366*1a0c32a9SJohn Levon      */
367*1a0c32a9SJohn Levon     if (proxy->wr_multi != NULL ||
368*1a0c32a9SJohn Levon         (proxy->num_outgoing > VFIO_USER_OUT_HIGH && can_multi)) {
369*1a0c32a9SJohn Levon 
370*1a0c32a9SJohn Levon         /*
371*1a0c32a9SJohn Levon          * re-check with lock
372*1a0c32a9SJohn Levon          *
373*1a0c32a9SJohn Levon          * if already building a WRITE_MULTI msg,
374*1a0c32a9SJohn Levon          *  add this one if possible else flush pending before
375*1a0c32a9SJohn Levon          *  sending the current one
376*1a0c32a9SJohn Levon          *
377*1a0c32a9SJohn Levon          * else if outgoing queue is over the highwater,
378*1a0c32a9SJohn Levon          *  start a new WRITE_MULTI message
379*1a0c32a9SJohn Levon          */
380*1a0c32a9SJohn Levon         WITH_QEMU_LOCK_GUARD(&proxy->lock) {
381*1a0c32a9SJohn Levon             if (proxy->wr_multi != NULL) {
382*1a0c32a9SJohn Levon                 if (can_multi) {
383*1a0c32a9SJohn Levon                     vfio_user_add_multi(proxy, index, off, count, data);
384*1a0c32a9SJohn Levon                     return count;
385*1a0c32a9SJohn Levon                 }
386*1a0c32a9SJohn Levon                 vfio_user_flush_multi(proxy);
387*1a0c32a9SJohn Levon             } else if (proxy->num_outgoing > VFIO_USER_OUT_HIGH && can_multi) {
388*1a0c32a9SJohn Levon                 vfio_user_create_multi(proxy);
389*1a0c32a9SJohn Levon                 vfio_user_add_multi(proxy, index, off, count, data);
390*1a0c32a9SJohn Levon                 return count;
391*1a0c32a9SJohn Levon             }
392*1a0c32a9SJohn Levon         }
393*1a0c32a9SJohn Levon     }
394*1a0c32a9SJohn Levon 
3951ed50fcbSJohn Levon     msgp = g_malloc0(size);
39698a906d9SJohn Levon     vfio_user_request_msg(&msgp->hdr, VFIO_USER_REGION_WRITE, size, flags);
3971ed50fcbSJohn Levon     msgp->offset = off;
3981ed50fcbSJohn Levon     msgp->region = index;
3991ed50fcbSJohn Levon     msgp->count = count;
4001ed50fcbSJohn Levon     memcpy(&msgp->data, data, count);
4011ed50fcbSJohn Levon     trace_vfio_user_region_rw(msgp->region, msgp->offset, msgp->count);
4021ed50fcbSJohn Levon 
40398a906d9SJohn Levon     /* async send will free msg after it's sent */
40498a906d9SJohn Levon     if (post) {
40598a906d9SJohn Levon         if (!vfio_user_send_async(proxy, &msgp->hdr, NULL, &local_err)) {
40698a906d9SJohn Levon             error_prepend(&local_err, "%s: ", __func__);
40798a906d9SJohn Levon             error_report_err(local_err);
40898a906d9SJohn Levon             return -EFAULT;
40998a906d9SJohn Levon         }
41098a906d9SJohn Levon 
41198a906d9SJohn Levon         return count;
41298a906d9SJohn Levon     }
4131ed50fcbSJohn Levon 
4141ed50fcbSJohn Levon     if (!vfio_user_send_wait(proxy, &msgp->hdr, NULL, 0, &local_err)) {
4151ed50fcbSJohn Levon         error_prepend(&local_err, "%s: ", __func__);
4161ed50fcbSJohn Levon         error_report_err(local_err);
41798a906d9SJohn Levon         g_free(msgp);
4181ed50fcbSJohn Levon         return -EFAULT;
4191ed50fcbSJohn Levon     }
4201ed50fcbSJohn Levon 
4211ed50fcbSJohn Levon     if (msgp->hdr.flags & VFIO_USER_ERROR) {
4221ed50fcbSJohn Levon         ret = -msgp->hdr.error_reply;
4231ed50fcbSJohn Levon     } else {
4241ed50fcbSJohn Levon         ret = count;
4251ed50fcbSJohn Levon     }
4261ed50fcbSJohn Levon 
42798a906d9SJohn Levon     g_free(msgp);
4281ed50fcbSJohn Levon     return ret;
4291ed50fcbSJohn Levon }
4301ed50fcbSJohn Levon 
431667866d6SJohn Levon /*
432667866d6SJohn Levon  * Socket-based io_ops
433667866d6SJohn Levon  */
434667866d6SJohn Levon VFIODeviceIOOps vfio_user_device_io_ops_sock = {
435667866d6SJohn Levon     .get_region_info = vfio_user_device_io_get_region_info,
436ca1add16SJohn Levon     .get_irq_info = vfio_user_device_io_get_irq_info,
437ca1add16SJohn Levon     .set_irqs = vfio_user_device_io_set_irqs,
4381ed50fcbSJohn Levon     .region_read = vfio_user_device_io_region_read,
4391ed50fcbSJohn Levon     .region_write = vfio_user_device_io_region_write,
4401ed50fcbSJohn Levon 
441667866d6SJohn Levon };
442