Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  vhost_net_test.c   Sprache: C

 
// SPDX-License-Identifier: GPL-2.0
#define _GNU_SOURCE
#include <getopt.h>
#include <limits.h>
#include <string.h>
#include <poll.h>
#include <sys/eventfd.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdbool.h>
#include <linux/vhost.h>
#include <linux/if.h>
#include <linux/if_tun.h>
#include <linux/in.h>
#include <linux/if_packet.h>
#include <linux/virtio_net.h>
#include <netinet/ether.h>

#define HDR_LEN  sizeof(struct virtio_net_hdr_mrg_rxbuf)
#define TEST_BUF_LEN 256
#define TEST_PTYPE ETH_P_LOOPBACK
#define DESC_NUM 256

/* Used by implementation of kmalloc() in tools/virtio/linux/kernel.h */
void *__kmalloc_fake, *__kfree_ignore_start, *__kfree_ignore_end;

struct vq_info {
 int kick;
 int call;
 int idx;
 long started;
 long completed;
 struct pollfd fds;
 void *ring;
 /* copy used for control */
 struct vring vring;
 struct virtqueue *vq;
};

struct vdev_info {
 struct virtio_device vdev;
 int control;
 struct vq_info vqs[2];
 int nvqs;
 void *buf;
 size_t buf_size;
 char *test_buf;
 char *res_buf;
 struct vhost_memory *mem;
 int sock;
 int ifindex;
 unsigned char mac[ETHER_ADDR_LEN];
};

static int tun_alloc(struct vdev_info *dev, char *tun_name)
{
 struct ifreq ifr;
 int len = HDR_LEN;
 int fd, e;

 fd = open("/dev/net/tun", O_RDWR);
 if (fd < 0) {
  perror("Cannot open /dev/net/tun");
  return fd;
 }

 memset(&ifr, 0, sizeof(ifr));

 ifr.ifr_flags = IFF_TAP | IFF_NO_PI | IFF_VNET_HDR;
 strncpy(ifr.ifr_name, tun_name, IFNAMSIZ);

 e = ioctl(fd, TUNSETIFF, &ifr);
 if (e < 0) {
  perror("ioctl[TUNSETIFF]");
  close(fd);
  return e;
 }

 e = ioctl(fd, TUNSETVNETHDRSZ, &len);
 if (e < 0) {
  perror("ioctl[TUNSETVNETHDRSZ]");
  close(fd);
  return e;
 }

 e = ioctl(fd, SIOCGIFHWADDR, &ifr);
 if (e < 0) {
  perror("ioctl[SIOCGIFHWADDR]");
  close(fd);
  return e;
 }

 memcpy(dev->mac, &ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN);
 return fd;
}

static void vdev_create_socket(struct vdev_info *dev, char *tun_name)
{
 struct ifreq ifr;

 dev->sock = socket(AF_PACKET, SOCK_RAW, htons(TEST_PTYPE));
 assert(dev->sock != -1);

 strncpy(ifr.ifr_name, tun_name, IFNAMSIZ);
 assert(ioctl(dev->sock, SIOCGIFINDEX, &ifr) >= 0);

 dev->ifindex = ifr.ifr_ifindex;

 /* Set the flags that bring the device up */
 assert(ioctl(dev->sock, SIOCGIFFLAGS, &ifr) >= 0);
 ifr.ifr_flags |= (IFF_UP | IFF_RUNNING);
 assert(ioctl(dev->sock, SIOCSIFFLAGS, &ifr) >= 0);
}

static void vdev_send_packet(struct vdev_info *dev)
{
 char *sendbuf = dev->test_buf + HDR_LEN;
 struct sockaddr_ll saddrll = {0};
 int sockfd = dev->sock;
 int ret;

 saddrll.sll_family = PF_PACKET;
 saddrll.sll_ifindex = dev->ifindex;
 saddrll.sll_halen = ETH_ALEN;
 saddrll.sll_protocol = htons(TEST_PTYPE);

 ret = sendto(sockfd, sendbuf, TEST_BUF_LEN, 0,
       (struct sockaddr *)&saddrll,
       sizeof(struct sockaddr_ll));
 assert(ret >= 0);
}

static bool vq_notify(struct virtqueue *vq)
{
 struct vq_info *info = vq->priv;
 unsigned long long v = 1;
 int r;

 r = write(info->kick, &v, sizeof(v));
 assert(r == sizeof(v));

 return true;
}

static void vhost_vq_setup(struct vdev_info *dev, struct vq_info *info)
{
 struct vhost_vring_addr addr = {
  .index = info->idx,
  .desc_user_addr = (uint64_t)(unsigned long)info->vring.desc,
  .avail_user_addr = (uint64_t)(unsigned long)info->vring.avail,
  .used_user_addr = (uint64_t)(unsigned long)info->vring.used,
 };
 struct vhost_vring_state state = { .index = info->idx };
 struct vhost_vring_file file = { .index = info->idx };
 int r;

 state.num = info->vring.num;
 r = ioctl(dev->control, VHOST_SET_VRING_NUM, &state);
 assert(r >= 0);

 state.num = 0;
 r = ioctl(dev->control, VHOST_SET_VRING_BASE, &state);
 assert(r >= 0);

 r = ioctl(dev->control, VHOST_SET_VRING_ADDR, &addr);
 assert(r >= 0);

 file.fd = info->kick;
 r = ioctl(dev->control, VHOST_SET_VRING_KICK, &file);
 assert(r >= 0);
}

static void vq_reset(struct vq_info *info, int num, struct virtio_device *vdev)
{
 if (info->vq)
  vring_del_virtqueue(info->vq);

 memset(info->ring, 0, vring_size(num, 4096));
 vring_init(&info->vring, num, info->ring, 4096);
 info->vq = vring_new_virtqueue(info->idx, num, 4096, vdev, truefalse,
           info->ring, vq_notify, NULL, "test");
 assert(info->vq);
 info->vq->priv = info;
}

static void vq_info_add(struct vdev_info *dev, int idx, int num, int fd)
{
 struct vhost_vring_file backend = { .index = idx, .fd = fd };
 struct vq_info *info = &dev->vqs[idx];
 int r;

 info->idx = idx;
 info->kick = eventfd(0, EFD_NONBLOCK);
 r = posix_memalign(&info->ring, 4096, vring_size(num, 4096));
 assert(r >= 0);
 vq_reset(info, num, &dev->vdev);
 vhost_vq_setup(dev, info);

 r = ioctl(dev->control, VHOST_NET_SET_BACKEND, &backend);
 assert(!r);
}

static void vdev_info_init(struct vdev_info *dev, unsigned long long features)
{
 struct ether_header *eh;
 int i, r;

 dev->vdev.features = features;
 INIT_LIST_HEAD(&dev->vdev.vqs);
 spin_lock_init(&dev->vdev.vqs_list_lock);

 dev->buf_size = (HDR_LEN + TEST_BUF_LEN) * 2;
 dev->buf = malloc(dev->buf_size);
 assert(dev->buf);
 dev->test_buf = dev->buf;
 dev->res_buf = dev->test_buf + HDR_LEN + TEST_BUF_LEN;

 memset(dev->test_buf, 0, HDR_LEN + TEST_BUF_LEN);
 eh = (struct ether_header *)(dev->test_buf + HDR_LEN);
 eh->ether_type = htons(TEST_PTYPE);
 memcpy(eh->ether_dhost, dev->mac, ETHER_ADDR_LEN);
 memcpy(eh->ether_shost, dev->mac, ETHER_ADDR_LEN);

 for (i = sizeof(*eh); i < TEST_BUF_LEN; i++)
  dev->test_buf[i + HDR_LEN] = (char)i;

 dev->control = open("/dev/vhost-net", O_RDWR);
 assert(dev->control >= 0);

 r = ioctl(dev->control, VHOST_SET_OWNER, NULL);
 assert(r >= 0);

 dev->mem = malloc(offsetof(struct vhost_memory, regions) +
     sizeof(dev->mem->regions[0]));
 assert(dev->mem);
 memset(dev->mem, 0, offsetof(struct vhost_memory, regions) +
        sizeof(dev->mem->regions[0]));
 dev->mem->nregions = 1;
 dev->mem->regions[0].guest_phys_addr = (long)dev->buf;
 dev->mem->regions[0].userspace_addr = (long)dev->buf;
 dev->mem->regions[0].memory_size = dev->buf_size;

 r = ioctl(dev->control, VHOST_SET_MEM_TABLE, dev->mem);
 assert(r >= 0);

 r = ioctl(dev->control, VHOST_SET_FEATURES, &features);
 assert(r >= 0);

 dev->nvqs = 2;
}

static void wait_for_interrupt(struct vq_info *vq)
{
 unsigned long long val;

 poll(&vq->fds, 1, 100);

 if (vq->fds.revents & POLLIN)
  read(vq->fds.fd, &val, sizeof(val));
}

static void verify_res_buf(char *res_buf)
{
 int i;

 for (i = ETHER_HDR_LEN; i < TEST_BUF_LEN; i++)
  assert(res_buf[i] == (char)i);
}

static void run_tx_test(struct vdev_info *dev, struct vq_info *vq,
   bool delayed, int bufs)
{
 long long spurious = 0;
 struct scatterlist sl;
 unsigned int len;
 int r;

 for (;;) {
  long started_before = vq->started;
  long completed_before = vq->completed;

  virtqueue_disable_cb(vq->vq);
  do {
   while (vq->started < bufs &&
          (vq->started - vq->completed) < 1) {
    sg_init_one(&sl, dev->test_buf, HDR_LEN + TEST_BUF_LEN);
    r = virtqueue_add_outbuf(vq->vq, &sl, 1,
        dev->test_buf + vq->started,
        GFP_ATOMIC);
    if (unlikely(r != 0))
     break;

    ++vq->started;

    if (unlikely(!virtqueue_kick(vq->vq))) {
     r = -1;
     break;
    }
   }

   if (vq->started >= bufs)
    r = -1;

   /* Flush out completed bufs if any */
   while (virtqueue_get_buf(vq->vq, &len)) {
    int n;

    n = recvfrom(dev->sock, dev->res_buf, TEST_BUF_LEN, 0, NULL, NULL);
    assert(n == TEST_BUF_LEN);
    verify_res_buf(dev->res_buf);

    ++vq->completed;
    r = 0;
   }
  } while (r == 0);

  if (vq->completed == completed_before && vq->started == started_before)
   ++spurious;

  assert(vq->completed <= bufs);
  assert(vq->started <= bufs);
  if (vq->completed == bufs)
   break;

  if (delayed) {
   if (virtqueue_enable_cb_delayed(vq->vq))
    wait_for_interrupt(vq);
  } else {
   if (virtqueue_enable_cb(vq->vq))
    wait_for_interrupt(vq);
  }
 }
 printf("TX spurious wakeups: 0x%llx started=0x%lx completed=0x%lx\n",
        spurious, vq->started, vq->completed);
}

static void run_rx_test(struct vdev_info *dev, struct vq_info *vq,
   bool delayed, int bufs)
{
 long long spurious = 0;
 struct scatterlist sl;
 unsigned int len;
 int r;

 for (;;) {
  long started_before = vq->started;
  long completed_before = vq->completed;

  do {
   while (vq->started < bufs &&
          (vq->started - vq->completed) < 1) {
    sg_init_one(&sl, dev->res_buf, HDR_LEN + TEST_BUF_LEN);

    r = virtqueue_add_inbuf(vq->vq, &sl, 1,
       dev->res_buf + vq->started,
       GFP_ATOMIC);
    if (unlikely(r != 0))
     break;

    ++vq->started;

    vdev_send_packet(dev);

    if (unlikely(!virtqueue_kick(vq->vq))) {
     r = -1;
     break;
    }
   }

   if (vq->started >= bufs)
    r = -1;

   /* Flush out completed bufs if any */
   while (virtqueue_get_buf(vq->vq, &len)) {
    struct ether_header *eh;

    eh = (struct ether_header *)(dev->res_buf + HDR_LEN);

    /* tun netdev is up and running, only handle the
 * TEST_PTYPE packet.
 */

    if (eh->ether_type == htons(TEST_PTYPE)) {
     assert(len == TEST_BUF_LEN + HDR_LEN);
     verify_res_buf(dev->res_buf + HDR_LEN);
    }

    ++vq->completed;
    r = 0;
   }
  } while (r == 0);

  if (vq->completed == completed_before && vq->started == started_before)
   ++spurious;

  assert(vq->completed <= bufs);
  assert(vq->started <= bufs);
  if (vq->completed == bufs)
   break;
 }

 printf("RX spurious wakeups: 0x%llx started=0x%lx completed=0x%lx\n",
        spurious, vq->started, vq->completed);
}

static const char optstring[] = "h";
static const struct option longopts[] = {
 {
  .name = "help",
  .val = 'h',
 },
 {
  .name = "event-idx",
  .val = 'E',
 },
 {
  .name = "no-event-idx",
  .val = 'e',
 },
 {
  .name = "indirect",
  .val = 'I',
 },
 {
  .name = "no-indirect",
  .val = 'i',
 },
 {
  .name = "virtio-1",
  .val = '1',
 },
 {
  .name = "no-virtio-1",
  .val = '0',
 },
 {
  .name = "delayed-interrupt",
  .val = 'D',
 },
 {
  .name = "no-delayed-interrupt",
  .val = 'd',
 },
 {
  .name = "buf-num",
  .val = 'n',
  .has_arg = required_argument,
 },
 {
  .name = "batch",
  .val = 'b',
  .has_arg = required_argument,
 },
 {
 }
};

static void help(int status)
{
 fprintf(stderr, "Usage: vhost_net_test [--help]"
  " [--no-indirect]"
  " [--no-event-idx]"
  " [--no-virtio-1]"
  " [--delayed-interrupt]"
  " [--buf-num]"
  "\n");

 exit(status);
}

int main(int argc, char **argv)
{
 unsigned long long features = (1ULL << VIRTIO_RING_F_INDIRECT_DESC) |
  (1ULL << VIRTIO_RING_F_EVENT_IDX) | (1ULL << VIRTIO_F_VERSION_1);
 char tun_name[IFNAMSIZ];
 long nbufs = 0x100000;
 struct vdev_info dev;
 bool delayed = false;
 int o, fd;

 for (;;) {
  o = getopt_long(argc, argv, optstring, longopts, NULL);
  switch (o) {
  case -1:
   goto done;
  case '?':
   help(2);
  case 'e':
   features &= ~(1ULL << VIRTIO_RING_F_EVENT_IDX);
   break;
  case 'h':
   help(0);
  case 'i':
   features &= ~(1ULL << VIRTIO_RING_F_INDIRECT_DESC);
   break;
  case '0':
   features &= ~(1ULL << VIRTIO_F_VERSION_1);
   break;
  case 'D':
   delayed = true;
   break;
  case 'n':
   nbufs = strtol(optarg, NULL, 10);
   assert(nbufs > 0);
   break;
  default:
   assert(0);
   break;
  }
 }

done:
 memset(&dev, 0, sizeof(dev));
 snprintf(tun_name, IFNAMSIZ, "tun_%d", getpid());

 fd = tun_alloc(&dev, tun_name);
 assert(fd >= 0);

 vdev_info_init(&dev, features);
 vq_info_add(&dev, 0, DESC_NUM, fd);
 vq_info_add(&dev, 1, DESC_NUM, fd);
 vdev_create_socket(&dev, tun_name);

 run_rx_test(&dev, &dev.vqs[0], delayed, nbufs);
 run_tx_test(&dev, &dev.vqs[1], delayed, nbufs);

 return 0;
}

Messung V0.5
C=93 H=96 G=94

¤ Dauer der Verarbeitung: 0.12 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge