/*
* Helpers for the host side of a virtio ring.
*
* Since these may be in userspace, we use (inline) accessors.
*/
#include <linux/module.h>
#include <linux/vringh.h>
#include <linux/virtio_ring.h>
#include <linux/kernel.h>
#include <linux/ratelimit.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/export.h>
static __printf(1,2) __cold void vringh_bad(const char *fmt, ...)
{
static DEFINE_RATELIMIT_STATE(vringh_rs,
DEFAULT_RATELIMIT_INTERVAL,
DEFAULT_RATELIMIT_BURST);
if (__ratelimit(&vringh_rs)) {
va_list ap;
va_start(ap, fmt);
printk(KERN_NOTICE "vringh:");
vprintk(fmt, ap);
va_end(ap);
}
}
/* Returns vring->num if empty, -ve on error. */
static inline int __vringh_get_head(const struct vringh *vrh,
int (*getu16)(u16 *val, const u16 *p),
u16 *last_avail_idx)
{
u16 avail_idx, i, head;
int err;
err = getu16(&avail_idx, &vrh->vring.avail->idx);
if (err) {
vringh_bad("Failed to access avail idx at %p",
&vrh->vring.avail->idx);
return err;
}
if (*last_avail_idx == avail_idx)
return vrh->vring.num;
/* Only get avail ring entries after they have been exposed by guest. */
virtio_rmb(vrh->weak_barriers);
i = *last_avail_idx & (vrh->vring.num - 1);
err = getu16(&head, &vrh->vring.avail->ring[i]);
if (err) {
vringh_bad("Failed to read head: idx %d address %p",
*last_avail_idx, &vrh->vring.avail->ring[i]);
return err;
}
if (head >= vrh->vring.num) {
vringh_bad("Guest says index %u > %u is available",
head, vrh->vring.num);
return -EINVAL;
}
(*last_avail_idx)++;
return head;
}
/* Copy some bytes to/from the iovec. Returns num copied. */
static inline ssize_t vringh_iov_xfer(struct vringh_kiov *iov,
void *ptr, size_t len,
int (*xfer)(void *addr, void *ptr,
size_t len))
{
int err, done = 0;
while (len && iov->i < iov->used) {
size_t partlen;
partlen = min(iov->iov[iov->i].iov_len, len);
err = xfer(iov->iov[iov->i].iov_base, ptr, partlen);
if (err)
return err;
done += partlen;
len -= partlen;
ptr += partlen;
iov->consumed += partlen;
iov->iov[iov->i].iov_len -= partlen;
iov->iov[iov->i].iov_base += partlen;
if (!iov->iov[iov->i].iov_len) {
/* Fix up old iov element then increment. */
iov->iov[iov->i].iov_len = iov->consumed;
iov->iov[iov->i].iov_base -= iov->consumed;
iov->consumed = 0;
iov->i++;
}
}
return done;
}
/* May reduce *len if range is shorter. */
static inline bool range_check(struct vringh *vrh, u64 addr, size_t *len,
struct vringh_range *range,
bool (*getrange)(struct vringh *,
u64, struct vringh_range *))
{
if (addr < range->start || addr > range->end_incl) {
if (!getrange(vrh, addr, range))
return false;
}
BUG_ON(addr < range->start || addr > range->end_incl);
/* To end of memory? */
if (unlikely(addr + *len == 0)) {
if (range->end_incl == -1ULL)
return true;
goto truncate;
}
/* Otherwise, don't wrap. */
if (addr + *