static int do_recv(struct libvchan *ctrl, void *data, size_t size) { int real_idx = rd_cons(ctrl) & (rd_ring_size(ctrl) - 1); int avail_contig = rd_ring_size(ctrl) - real_idx; if (avail_contig > size) avail_contig = size; barrier(); // data read must happen after rd_cons read memcpy(data, rd_ring(ctrl) + real_idx, avail_contig); if (avail_contig < size) { // we rolled across the end of the ring memcpy(data + avail_contig, rd_ring(ctrl), size - avail_contig); } rd_cons(ctrl) += size; if (VCHAN_DEBUG) { char metainfo[32]; struct iovec iov[2]; iov[0].iov_base = metainfo; iov[0].iov_len = snprintf(metainfo, 32, "vchan rd %d/%d", ctrl->other_domain_id, ctrl->device_number); iov[1].iov_base = data; iov[1].iov_len = size; writev(-1, iov, 2); } barrier(); // consumption must happen prior to notify of newly freed space if (do_notify(ctrl) < 0) return -1; return size; }
/* * Get the amount of buffer space available, and do nothing about * notifications. */ static inline int raw_get_data_ready(struct libxenvchan *ctrl) { uint32_t ready = rd_prod(ctrl) - rd_cons(ctrl); if (ready >= rd_ring_size(ctrl)) /* We have no way to return errors. Locking up the ring is * better than the alternatives. */ return 0; return ready; }
static int do_recv(struct libxenvchan *ctrl, void *data, size_t size) { int real_idx = rd_cons(ctrl) & (rd_ring_size(ctrl) - 1); int avail_contig = rd_ring_size(ctrl) - real_idx; if (avail_contig > size) avail_contig = size; xen_rmb(); /* data read must happen /after/ rd_cons read */ memcpy(data, rd_ring(ctrl) + real_idx, avail_contig); if (avail_contig < size) { // we rolled across the end of the ring memcpy(data + avail_contig, rd_ring(ctrl), size - avail_contig); } xen_mb(); /* consume /then/ notify */ rd_cons(ctrl) += size; if (send_notify(ctrl, VCHAN_NOTIFY_READ)) return -1; return size; }
/** * reads exactly size bytes from the vchan. * returns 0 if insufficient data is available, -1 on error, or size on success */ int libxenvchan_recv(struct libxenvchan *ctrl, void *data, size_t size) { while (1) { int avail = fast_get_data_ready(ctrl, size); if (size <= avail) return do_recv(ctrl, data, size); if (!libxenvchan_is_open(ctrl)) return -1; if (!ctrl->blocking) return 0; if (size > rd_ring_size(ctrl)) return -1; if (libxenvchan_wait(ctrl)) return -1; } }