Deleted Added
full compact
virtqueue.c (227652) virtqueue.c (234270)
1/*-
2 * Copyright (c) 2011, Bryan Venteicher <bryanv@daemoninthecloset.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright

--- 16 unchanged lines hidden (view full) ---

25 */
26
27/*
28 * Implements the virtqueue interface as basically described
29 * in the original VirtIO paper.
30 */
31
32#include <sys/cdefs.h>
1/*-
2 * Copyright (c) 2011, Bryan Venteicher <bryanv@daemoninthecloset.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright

--- 16 unchanged lines hidden (view full) ---

25 */
26
27/*
28 * Implements the virtqueue interface as basically described
29 * in the original VirtIO paper.
30 */
31
32#include <sys/cdefs.h>
33__FBSDID("$FreeBSD: head/sys/dev/virtio/virtqueue.c 227652 2011-11-18 05:43:43Z grehan $");
33__FBSDID("$FreeBSD: head/sys/dev/virtio/virtqueue.c 234270 2012-04-14 05:48:04Z grehan $");
34
35#include <sys/param.h>
36#include <sys/systm.h>
37#include <sys/kernel.h>
38#include <sys/malloc.h>
39#include <sys/sglist.h>
40#include <vm/vm.h>
41#include <vm/pmap.h>

--- 13 unchanged lines hidden (view full) ---

55
56struct virtqueue {
57 device_t vq_dev;
58 char vq_name[VIRTQUEUE_MAX_NAME_SZ];
59 uint16_t vq_queue_index;
60 uint16_t vq_nentries;
61 uint32_t vq_flags;
62#define VIRTQUEUE_FLAG_INDIRECT 0x0001
34
35#include <sys/param.h>
36#include <sys/systm.h>
37#include <sys/kernel.h>
38#include <sys/malloc.h>
39#include <sys/sglist.h>
40#include <vm/vm.h>
41#include <vm/pmap.h>

--- 13 unchanged lines hidden (view full) ---

55
56struct virtqueue {
57 device_t vq_dev;
58 char vq_name[VIRTQUEUE_MAX_NAME_SZ];
59 uint16_t vq_queue_index;
60 uint16_t vq_nentries;
61 uint32_t vq_flags;
62#define VIRTQUEUE_FLAG_INDIRECT 0x0001
63#define VIRTQUEUE_FLAG_EVENT_IDX 0x0002
63
64 int vq_alignment;
65 int vq_ring_size;
66 void *vq_ring_mem;
67 int vq_max_indirect_size;
68 int vq_indirect_mem_size;
69 virtqueue_intr_t *vq_intrhand;
70 void *vq_intrhand_arg;

--- 50 unchanged lines hidden (view full) ---

121
122static void vq_ring_init(struct virtqueue *);
123static void vq_ring_update_avail(struct virtqueue *, uint16_t);
124static uint16_t vq_ring_enqueue_segments(struct virtqueue *,
125 struct vring_desc *, uint16_t, struct sglist *, int, int);
126static int vq_ring_use_indirect(struct virtqueue *, int);
127static void vq_ring_enqueue_indirect(struct virtqueue *, void *,
128 struct sglist *, int, int);
64
65 int vq_alignment;
66 int vq_ring_size;
67 void *vq_ring_mem;
68 int vq_max_indirect_size;
69 int vq_indirect_mem_size;
70 virtqueue_intr_t *vq_intrhand;
71 void *vq_intrhand_arg;

--- 50 unchanged lines hidden (view full) ---

122
123static void vq_ring_init(struct virtqueue *);
124static void vq_ring_update_avail(struct virtqueue *, uint16_t);
125static uint16_t vq_ring_enqueue_segments(struct virtqueue *,
126 struct vring_desc *, uint16_t, struct sglist *, int, int);
127static int vq_ring_use_indirect(struct virtqueue *, int);
128static void vq_ring_enqueue_indirect(struct virtqueue *, void *,
129 struct sglist *, int, int);
129static void vq_ring_notify_host(struct virtqueue *, int);
130static int vq_ring_must_notify_host(struct virtqueue *);
131static void vq_ring_notify_host(struct virtqueue *);
130static void vq_ring_free_chain(struct virtqueue *, uint16_t);
131
132uint64_t
133virtqueue_filter_features(uint64_t features)
134{
135 uint64_t mask;
136
137 mask = (1 << VIRTIO_TRANSPORT_F_START) - 1;
138 mask |= VIRTIO_RING_F_INDIRECT_DESC;
132static void vq_ring_free_chain(struct virtqueue *, uint16_t);
133
134uint64_t
135virtqueue_filter_features(uint64_t features)
136{
137 uint64_t mask;
138
139 mask = (1 << VIRTIO_TRANSPORT_F_START) - 1;
140 mask |= VIRTIO_RING_F_INDIRECT_DESC;
141 mask |= VIRTIO_RING_F_EVENT_IDX;
139
140 return (features & mask);
141}
142
143int
144virtqueue_alloc(device_t dev, uint16_t queue, uint16_t size, int align,
145 vm_paddr_t highaddr, struct vq_alloc_info *info, struct virtqueue **vqp)
146{

--- 32 unchanged lines hidden (view full) ---

179 strlcpy(vq->vq_name, info->vqai_name, sizeof(vq->vq_name));
180 vq->vq_queue_index = queue;
181 vq->vq_alignment = align;
182 vq->vq_nentries = size;
183 vq->vq_free_cnt = size;
184 vq->vq_intrhand = info->vqai_intr;
185 vq->vq_intrhand_arg = info->vqai_intr_arg;
186
142
143 return (features & mask);
144}
145
146int
147virtqueue_alloc(device_t dev, uint16_t queue, uint16_t size, int align,
148 vm_paddr_t highaddr, struct vq_alloc_info *info, struct virtqueue **vqp)
149{

--- 32 unchanged lines hidden (view full) ---

182 strlcpy(vq->vq_name, info->vqai_name, sizeof(vq->vq_name));
183 vq->vq_queue_index = queue;
184 vq->vq_alignment = align;
185 vq->vq_nentries = size;
186 vq->vq_free_cnt = size;
187 vq->vq_intrhand = info->vqai_intr;
188 vq->vq_intrhand_arg = info->vqai_intr_arg;
189
190 if (VIRTIO_BUS_WITH_FEATURE(dev, VIRTIO_RING_F_EVENT_IDX) != 0)
191 vq->vq_flags |= VIRTQUEUE_FLAG_EVENT_IDX;
192
187 if (info->vqai_maxindirsz > 1) {
188 error = virtqueue_init_indirect(vq, info->vqai_maxindirsz);
189 if (error)
190 goto fail;
191 }
192
193 vq->vq_ring_size = round_page(vring_size(size, align));
194 vq->vq_ring_mem = contigmalloc(vq->vq_ring_size, M_DEVBUF,

--- 184 unchanged lines hidden (view full) ---

379{
380
381 return (vq->vq_free_cnt == 0);
382}
383
384void
385virtqueue_notify(struct virtqueue *vq)
386{
193 if (info->vqai_maxindirsz > 1) {
194 error = virtqueue_init_indirect(vq, info->vqai_maxindirsz);
195 if (error)
196 goto fail;
197 }
198
199 vq->vq_ring_size = round_page(vring_size(size, align));
200 vq->vq_ring_mem = contigmalloc(vq->vq_ring_size, M_DEVBUF,

--- 184 unchanged lines hidden (view full) ---

385{
386
387 return (vq->vq_free_cnt == 0);
388}
389
390void
391virtqueue_notify(struct virtqueue *vq)
392{
393 /* Ensure updated avail->idx is visible to host. */
394 mb();
387
395
396 if (vq_ring_must_notify_host(vq))
397 vq_ring_notify_host(vq);
388 vq->vq_queued_cnt = 0;
398 vq->vq_queued_cnt = 0;
389 vq_ring_notify_host(vq, 0);
390}
391
392int
393virtqueue_nused(struct virtqueue *vq)
394{
395 uint16_t used_idx, nused;
396
397 used_idx = vq->vq_ring.used->idx;
399}
400
401int
402virtqueue_nused(struct virtqueue *vq)
403{
404 uint16_t used_idx, nused;
405
406 used_idx = vq->vq_ring.used->idx;
398 if (used_idx >= vq->vq_used_cons_idx)
399 nused = used_idx - vq->vq_used_cons_idx;
400 else
401 nused = UINT16_MAX - vq->vq_used_cons_idx +
402 used_idx + 1;
407
408 nused = (uint16_t)(used_idx - vq->vq_used_cons_idx);
403 VQASSERT(vq, nused <= vq->vq_nentries, "used more than available");
404
405 return (nused);
406}
407
408int
409virtqueue_intr(struct virtqueue *vq)
410{

--- 11 unchanged lines hidden (view full) ---

422virtqueue_enable_intr(struct virtqueue *vq)
423{
424
425 /*
426 * Enable interrupts, making sure we get the latest
427 * index of what's already been consumed.
428 */
429 vq->vq_ring.avail->flags &= ~VRING_AVAIL_F_NO_INTERRUPT;
409 VQASSERT(vq, nused <= vq->vq_nentries, "used more than available");
410
411 return (nused);
412}
413
414int
415virtqueue_intr(struct virtqueue *vq)
416{

--- 11 unchanged lines hidden (view full) ---

428virtqueue_enable_intr(struct virtqueue *vq)
429{
430
431 /*
432 * Enable interrupts, making sure we get the latest
433 * index of what's already been consumed.
434 */
435 vq->vq_ring.avail->flags &= ~VRING_AVAIL_F_NO_INTERRUPT;
436 if (vq->vq_flags & VIRTQUEUE_FLAG_EVENT_IDX)
437 vring_used_event(&vq->vq_ring) = vq->vq_used_cons_idx;
438 else
439 vq->vq_ring.avail->flags &= ~VRING_AVAIL_F_NO_INTERRUPT;
430
431 mb();
432
433 /*
434 * Additional items may have been consumed in the time between
435 * since we last checked and enabled interrupts above. Let our
436 * caller know so it processes the new entries.
437 */
438 if (vq->vq_used_cons_idx != vq->vq_ring.used->idx)
439 return (1);
440
441 return (0);
442}
443
440
441 mb();
442
443 /*
444 * Additional items may have been consumed in the time between
445 * since we last checked and enabled interrupts above. Let our
446 * caller know so it processes the new entries.
447 */
448 if (vq->vq_used_cons_idx != vq->vq_ring.used->idx)
449 return (1);
450
451 return (0);
452}
453
454int
455virtqueue_postpone_intr(struct virtqueue *vq)
456{
457 uint16_t ndesc;
458
459 /*
460 * Postpone until at least half of the available descriptors
461 * have been consumed.
462 *
463 * XXX Adaptive factor? (Linux uses 3/4)
464 */
465 ndesc = (uint16_t)(vq->vq_ring.avail->idx - vq->vq_used_cons_idx) / 2;
466
467 if (vq->vq_flags & VIRTQUEUE_FLAG_EVENT_IDX)
468 vring_used_event(&vq->vq_ring) = vq->vq_used_cons_idx + ndesc;
469 else
470 vq->vq_ring.avail->flags &= ~VRING_AVAIL_F_NO_INTERRUPT;
471
472 mb();
473
474 /*
475 * Enough items may have already been consumed to meet our
476 * threshold since we last checked. Let our caller know so
477 * it processes the new entries.
478 */
479 if (virtqueue_nused(vq) > ndesc)
480 return (1);
481
482 return (0);
483}
484
444void
445virtqueue_disable_intr(struct virtqueue *vq)
446{
447
448 /*
449 * Note this is only considered a hint to the host.
450 */
485void
486virtqueue_disable_intr(struct virtqueue *vq)
487{
488
489 /*
490 * Note this is only considered a hint to the host.
491 */
451 vq->vq_ring.avail->flags |= VRING_AVAIL_F_NO_INTERRUPT;
492 if ((vq->vq_flags & VIRTQUEUE_FLAG_EVENT_IDX) == 0)
493 vq->vq_ring.avail->flags |= VRING_AVAIL_F_NO_INTERRUPT;
452}
453
454int
455virtqueue_enqueue(struct virtqueue *vq, void *cookie, struct sglist *sg,
456 int readable, int writable)
457{
458 struct vq_desc_extra *dxp;
459 int needed;

--- 153 unchanged lines hidden (view full) ---

613 * descriptor.
614 */
615 avail_idx = vq->vq_ring.avail->idx & (vq->vq_nentries - 1);
616 vq->vq_ring.avail->ring[avail_idx] = desc_idx;
617
618 mb();
619 vq->vq_ring.avail->idx++;
620
494}
495
496int
497virtqueue_enqueue(struct virtqueue *vq, void *cookie, struct sglist *sg,
498 int readable, int writable)
499{
500 struct vq_desc_extra *dxp;
501 int needed;

--- 153 unchanged lines hidden (view full) ---

655 * descriptor.
656 */
657 avail_idx = vq->vq_ring.avail->idx & (vq->vq_nentries - 1);
658 vq->vq_ring.avail->ring[avail_idx] = desc_idx;
659
660 mb();
661 vq->vq_ring.avail->idx++;
662
621 /* Keep pending count until virtqueue_notify() for debugging. */
663 /* Keep pending count until virtqueue_notify(). */
622 vq->vq_queued_cnt++;
623}
624
625static uint16_t
626vq_ring_enqueue_segments(struct virtqueue *vq, struct vring_desc *desc,
627 uint16_t head_idx, struct sglist *sg, int readable, int writable)
628{
629 struct sglist_seg *seg;

--- 74 unchanged lines hidden (view full) ---

704 if (vq->vq_free_cnt == 0)
705 VQ_RING_ASSERT_CHAIN_TERM(vq);
706 else
707 VQ_RING_ASSERT_VALID_IDX(vq, vq->vq_desc_head_idx);
708
709 vq_ring_update_avail(vq, head_idx);
710}
711
664 vq->vq_queued_cnt++;
665}
666
667static uint16_t
668vq_ring_enqueue_segments(struct virtqueue *vq, struct vring_desc *desc,
669 uint16_t head_idx, struct sglist *sg, int readable, int writable)
670{
671 struct sglist_seg *seg;

--- 74 unchanged lines hidden (view full) ---

746 if (vq->vq_free_cnt == 0)
747 VQ_RING_ASSERT_CHAIN_TERM(vq);
748 else
749 VQ_RING_ASSERT_VALID_IDX(vq, vq->vq_desc_head_idx);
750
751 vq_ring_update_avail(vq, head_idx);
752}
753
712static void
713vq_ring_notify_host(struct virtqueue *vq, int force)
754static int
755vq_ring_must_notify_host(struct virtqueue *vq)
714{
756{
757 uint16_t new_idx, prev_idx, event_idx;
715
758
716 mb();
759 if (vq->vq_flags & VIRTQUEUE_FLAG_EVENT_IDX) {
760 new_idx = vq->vq_ring.avail->idx;
761 prev_idx = new_idx - vq->vq_queued_cnt;
762 event_idx = vring_avail_event(&vq->vq_ring);
717
763
718 if (force ||
719 (vq->vq_ring.used->flags & VRING_USED_F_NO_NOTIFY) == 0)
720 VIRTIO_BUS_NOTIFY_VQ(vq->vq_dev, vq->vq_queue_index);
764 return (vring_need_event(event_idx, new_idx, prev_idx) != 0);
765 }
766
767 return ((vq->vq_ring.used->flags & VRING_USED_F_NO_NOTIFY) == 0);
721}
722
723static void
768}
769
770static void
771vq_ring_notify_host(struct virtqueue *vq)
772{
773
774 VIRTIO_BUS_NOTIFY_VQ(vq->vq_dev, vq->vq_queue_index);
775}
776
777static void
724vq_ring_free_chain(struct virtqueue *vq, uint16_t desc_idx)
725{
726 struct vring_desc *dp;
727 struct vq_desc_extra *dxp;
728
729 VQ_RING_ASSERT_VALID_IDX(vq, desc_idx);
730 dp = &vq->vq_ring.desc[desc_idx];
731 dxp = &vq->vq_descx[desc_idx];

--- 24 unchanged lines hidden ---
778vq_ring_free_chain(struct virtqueue *vq, uint16_t desc_idx)
779{
780 struct vring_desc *dp;
781 struct vq_desc_extra *dxp;
782
783 VQ_RING_ASSERT_VALID_IDX(vq, desc_idx);
784 dp = &vq->vq_ring.desc[desc_idx];
785 dxp = &vq->vq_descx[desc_idx];

--- 24 unchanged lines hidden ---