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