/** * returns -1 on error, or size on success */ static int do_send(struct libvchan *ctrl, const void *data, size_t size) { int real_idx = wr_prod(ctrl) & (wr_ring_size(ctrl) - 1); int avail_contig = wr_ring_size(ctrl) - real_idx; if (VCHAN_DEBUG) { char metainfo[32]; struct iovec iov[2]; iov[0].iov_base = metainfo; iov[0].iov_len = snprintf(metainfo, 32, "vchan wr %d/%d", ctrl->other_domain_id, ctrl->device_number); iov[1].iov_base = (void *)data; iov[1].iov_len = size; writev(-1, iov, 2); } if (avail_contig > size) avail_contig = size; memcpy(wr_ring(ctrl) + real_idx, data, avail_contig); if (avail_contig < size) { // we rolled across the end of the ring memcpy(wr_ring(ctrl), data + avail_contig, size - avail_contig); } barrier(); // data must be in the ring prior to increment wr_prod(ctrl) += size; barrier(); // increment must happen prior to notify 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_buffer_space(struct libxenvchan *ctrl) { uint32_t ready = wr_ring_size(ctrl) - (wr_prod(ctrl) - wr_cons(ctrl)); if (ready > wr_ring_size(ctrl)) /* We have no way to return errors. Locking up the ring is * better than the alternatives. */ return 0; return ready; }
/** * Get the amount of buffer space available and enable notifications if needed. */ static inline int fast_get_buffer_space(struct libxenvchan *ctrl, size_t request) { int ready = wr_ring_size(ctrl) - (wr_prod(ctrl) - wr_cons(ctrl)); if (ready >= request) return ready; /* We plan to fill the buffer; please tell us when you've read it */ request_notify(ctrl, VCHAN_NOTIFY_READ); /* * If the reader moved wr_cons after our read but before request, we * will not get notified even though the actual amount of buffer space * is above request. Reread wr_cons to cover this case. */ return wr_ring_size(ctrl) - (wr_prod(ctrl) - wr_cons(ctrl)); }
int libxenvchan_buffer_space(struct libxenvchan *ctrl) { /* Since this value is being used outside libxenvchan, request notification * when it changes */ request_notify(ctrl, VCHAN_NOTIFY_READ); return wr_ring_size(ctrl) - (wr_prod(ctrl) - wr_cons(ctrl)); }
/** * returns -1 on error, or size on success */ static int do_send(struct libxenvchan *ctrl, const void *data, size_t size) { int real_idx = wr_prod(ctrl) & (wr_ring_size(ctrl) - 1); int avail_contig = wr_ring_size(ctrl) - real_idx; if (avail_contig > size) avail_contig = size; xen_mb(); /* read indexes /then/ write data */ memcpy(wr_ring(ctrl) + real_idx, data, avail_contig); if (avail_contig < size) { // we rolled across the end of the ring memcpy(wr_ring(ctrl), data + avail_contig, size - avail_contig); } xen_wmb(); /* write data /then/ notify */ wr_prod(ctrl) += size; if (send_notify(ctrl, VCHAN_NOTIFY_WRITE)) return -1; return size; }
/** * returns 0 if no buffer space is available, -1 on error, or size on success */ int libxenvchan_send(struct libxenvchan *ctrl, const void *data, size_t size) { int avail; while (1) { if (!libxenvchan_is_open(ctrl)) return -1; avail = fast_get_buffer_space(ctrl, size); if (size <= avail) return do_send(ctrl, data, size); if (!ctrl->blocking) return 0; if (size > wr_ring_size(ctrl)) return -1; if (libxenvchan_wait(ctrl)) return -1; } }
int libvchan_buffer_space(struct libvchan *ctrl) { return wr_ring_size(ctrl) - (wr_prod(ctrl) - wr_cons(ctrl)); }