diff options
| author | Vincenzo Maffione <vmaffione@FreeBSD.org> | 2018-12-05 11:57:16 +0000 |
|---|---|---|
| committer | Vincenzo Maffione <vmaffione@FreeBSD.org> | 2018-12-05 11:57:16 +0000 |
| commit | b6e66be22bdce2aadcf52ee6230faa1e6cd3f805 (patch) | |
| tree | e9311c45835e19b2d58f105e0627078f15f1cd63 /sys/net | |
| parent | abc73e04c2a0add60dea19c7aab5a0a4d51ab418 (diff) | |
Notes
Diffstat (limited to 'sys/net')
| -rw-r--r-- | sys/net/netmap.h | 212 | ||||
| -rw-r--r-- | sys/net/netmap_user.h | 31 | ||||
| -rw-r--r-- | sys/net/netmap_virt.h | 214 |
3 files changed, 230 insertions, 227 deletions
diff --git a/sys/net/netmap.h b/sys/net/netmap.h index 5c756526fb34..681e50d2b8a9 100644 --- a/sys/net/netmap.h +++ b/sys/net/netmap.h @@ -41,9 +41,9 @@ #ifndef _NET_NETMAP_H_ #define _NET_NETMAP_H_ -#define NETMAP_API 12 /* current API version */ +#define NETMAP_API 13 /* current API version */ -#define NETMAP_MIN_API 11 /* min and max versions accepted */ +#define NETMAP_MIN_API 13 /* min and max versions accepted */ #define NETMAP_MAX_API 15 /* * Some fields should be cache-aligned to reduce contention. @@ -333,12 +333,17 @@ struct netmap_ring { */ /* - * check if space is available in the ring. + * Check if space is available in the ring. We use ring->head, which + * points to the next netmap slot to be published to netmap. It is + * possible that the applications moves ring->cur ahead of ring->tail + * (e.g., by setting ring->cur <== ring->tail), if it wants more slots + * than the ones currently available, and it wants to be notified when + * more arrive. See netmap(4) for more details and examples. */ static inline int nm_ring_empty(struct netmap_ring *ring) { - return (ring->cur == ring->tail); + return (ring->head == ring->tail); } /* @@ -479,6 +484,10 @@ struct nmreq_option { * !=0: errno value */ uint32_t nro_status; + /* Option size, used only for options that can have variable size + * (e.g. because they contain arrays). For fixed-size options this + * field should be set to zero. */ + uint64_t nro_size; }; /* Header common to all requests. Do not reorder these fields, as we need @@ -518,12 +527,32 @@ enum { NETMAP_REQ_VALE_POLLING_DISABLE, /* Get info about the pools of a memory allocator. */ NETMAP_REQ_POOLS_INFO_GET, + /* Start an in-kernel loop that syncs the rings periodically or + * on notifications. The loop runs in the context of the ioctl + * syscall, and only stops on NETMAP_REQ_SYNC_KLOOP_STOP. */ + NETMAP_REQ_SYNC_KLOOP_START, + /* Stops the thread executing the in-kernel loop. The thread + * returns from the ioctl syscall. */ + NETMAP_REQ_SYNC_KLOOP_STOP, + /* Enable CSB mode on a registered netmap control device. */ + NETMAP_REQ_CSB_ENABLE, }; enum { /* On NETMAP_REQ_REGISTER, ask netmap to use memory allocated * from user-space allocated memory pools (e.g. hugepages). */ NETMAP_REQ_OPT_EXTMEM = 1, + + /* ON NETMAP_REQ_SYNC_KLOOP_START, ask netmap to use eventfd-based + * notifications to synchronize the kernel loop with the application. + */ + NETMAP_REQ_OPT_SYNC_KLOOP_EVENTFDS, + + /* On NETMAP_REQ_REGISTER, ask netmap to work in CSB mode, where + * head, cur and tail pointers are not exchanged through the + * struct netmap_ring header, but rather using an user-provided + * memory area (see struct nm_csb_atok and struct nm_csb_ktoa). */ + NETMAP_REQ_OPT_CSB, }; /* @@ -541,6 +570,7 @@ struct nmreq_register { uint16_t nr_mem_id; /* id of the memory allocator */ uint16_t nr_ringid; /* ring(s) we care about */ uint32_t nr_mode; /* specify NR_REG_* modes */ + uint32_t nr_extra_bufs; /* number of requested extra buffers */ uint64_t nr_flags; /* additional flags (see below) */ /* monitors use nr_ringid and nr_mode to select the rings to monitor */ @@ -549,9 +579,7 @@ struct nmreq_register { #define NR_ZCOPY_MON 0x400 /* request exclusive access to the selected rings */ #define NR_EXCLUSIVE 0x800 -/* request ptnetmap host support */ -#define NR_PASSTHROUGH_HOST NR_PTNETMAP_HOST /* deprecated */ -#define NR_PTNETMAP_HOST 0x1000 +/* 0x1000 unused */ #define NR_RX_RINGS_ONLY 0x2000 #define NR_TX_RINGS_ONLY 0x4000 /* Applications set this flag if they are able to deal with virtio-net headers, @@ -564,8 +592,6 @@ struct nmreq_register { * NETMAP_DO_RX_POLL. */ #define NR_DO_RX_POLL 0x10000 #define NR_NO_TX_POLL 0x20000 - - uint32_t nr_extra_bufs; /* number of requested extra buffers */ }; /* Valid values for nmreq_register.nr_mode (see above). */ @@ -576,10 +602,11 @@ enum { NR_REG_DEFAULT = 0, /* backward compat, should not be used. */ NR_REG_ONE_NIC = 4, NR_REG_PIPE_MASTER = 5, /* deprecated, use "x{y" port name syntax */ NR_REG_PIPE_SLAVE = 6, /* deprecated, use "x}y" port name syntax */ + NR_REG_NULL = 7, }; /* A single ioctl number is shared by all the new API command. - * Demultiplexing is done using the nr_hdr.nr_reqtype field. + * Demultiplexing is done using the hdr.nr_reqtype field. * FreeBSD uses the size value embedded in the _IOWR to determine * how much to copy in/out, so we define the ioctl() command * specifying only nmreq_header, and copyin/copyout the rest. */ @@ -595,16 +622,18 @@ enum { NR_REG_DEFAULT = 0, /* backward compat, should not be used. */ /* * nr_reqtype: NETMAP_REQ_PORT_INFO_GET * Get information about a netmap port, including number of rings. - * slots per ring, id of the memory allocator, etc. + * slots per ring, id of the memory allocator, etc. The netmap + * control device used for this operation does not need to be bound + * to a netmap port. */ struct nmreq_port_info_get { - uint64_t nr_offset; /* nifp offset in the shared region */ uint64_t nr_memsize; /* size of the shared region */ uint32_t nr_tx_slots; /* slots in tx rings */ uint32_t nr_rx_slots; /* slots in rx rings */ uint16_t nr_tx_rings; /* number of tx rings */ uint16_t nr_rx_rings; /* number of rx rings */ - uint16_t nr_mem_id; /* id of the memory allocator */ + uint16_t nr_mem_id; /* memory allocator id (in/out) */ + uint16_t pad1; }; #define NM_BDG_NAME "vale" /* prefix for bridge port name */ @@ -620,6 +649,7 @@ struct nmreq_port_info_get { struct nmreq_vale_attach { struct nmreq_register reg; uint32_t port_index; + uint32_t pad1; }; /* @@ -630,6 +660,7 @@ struct nmreq_vale_attach { */ struct nmreq_vale_detach { uint32_t port_index; + uint32_t pad1; }; /* @@ -639,15 +670,18 @@ struct nmreq_vale_detach { struct nmreq_vale_list { /* Name of the VALE port (valeXXX:YYY) or empty. */ uint16_t nr_bridge_idx; + uint16_t pad1; uint32_t nr_port_idx; }; /* * nr_reqtype: NETMAP_REQ_PORT_HDR_SET or NETMAP_REQ_PORT_HDR_GET - * Set the port header length. + * Set or get the port header length of the port identified by hdr.nr_name. + * The control device does not need to be bound to a netmap port. */ struct nmreq_port_hdr { uint32_t nr_hdr_len; + uint32_t pad1; }; /* @@ -660,6 +694,7 @@ struct nmreq_vale_newif { uint16_t nr_tx_rings; /* number of tx rings */ uint16_t nr_rx_rings; /* number of rx rings */ uint16_t nr_mem_id; /* id of the memory allocator */ + uint16_t pad1; }; /* @@ -672,17 +707,20 @@ struct nmreq_vale_polling { #define NETMAP_POLLING_MODE_MULTI_CPU 2 uint32_t nr_first_cpu_id; uint32_t nr_num_polling_cpus; + uint32_t pad1; }; /* * nr_reqtype: NETMAP_REQ_POOLS_INFO_GET - * Get info about the pools of the memory allocator of the port bound - * to a given netmap control device (used i.e. by a ptnetmap-enabled - * hypervisor). The nr_hdr.nr_name field is ignored. + * Get info about the pools of the memory allocator of the netmap + * port specified by hdr.nr_name and nr_mem_id. The netmap control + * device used for this operation does not need to be bound to a netmap + * port. */ struct nmreq_pools_info { uint64_t nr_memsize; - uint16_t nr_mem_id; + uint16_t nr_mem_id; /* in/out argument */ + uint16_t pad1[3]; uint64_t nr_if_pool_offset; uint32_t nr_if_pool_objtotal; uint32_t nr_if_pool_objsize; @@ -695,13 +733,151 @@ struct nmreq_pools_info { }; /* + * nr_reqtype: NETMAP_REQ_SYNC_KLOOP_START + * Start an in-kernel loop that syncs the rings periodically or on + * notifications. The loop runs in the context of the ioctl syscall, + * and only stops on NETMAP_REQ_SYNC_KLOOP_STOP. + * The registered netmap port must be open in CSB mode. + */ +struct nmreq_sync_kloop_start { + /* Sleeping is the default synchronization method for the kloop. + * The 'sleep_us' field specifies how many microsconds to sleep for + * when there is no work to do, before doing another kloop iteration. + */ + uint32_t sleep_us; + uint32_t pad1; +}; + +/* A CSB entry for the application --> kernel direction. */ +struct nm_csb_atok { + uint32_t head; /* AW+ KR+ the head of the appl netmap_ring */ + uint32_t cur; /* AW+ KR+ the cur of the appl netmap_ring */ + uint32_t appl_need_kick; /* AW+ KR+ kern --> appl notification enable */ + uint32_t sync_flags; /* AW+ KR+ the flags of the appl [tx|rx]sync() */ + uint32_t pad[12]; /* pad to a 64 bytes cacheline */ +}; + +/* A CSB entry for the application <-- kernel direction. */ +struct nm_csb_ktoa { + uint32_t hwcur; /* AR+ KW+ the hwcur of the kern netmap_kring */ + uint32_t hwtail; /* AR+ KW+ the hwtail of the kern netmap_kring */ + uint32_t kern_need_kick; /* AR+ KW+ appl-->kern notification enable */ + uint32_t pad[13]; +}; + +#ifdef __linux__ + +#ifdef __KERNEL__ +#define nm_stst_barrier smp_wmb +#else /* !__KERNEL__ */ +static inline void nm_stst_barrier(void) +{ + /* A memory barrier with release semantic has the combined + * effect of a store-store barrier and a load-store barrier, + * which is fine for us. */ + __atomic_thread_fence(__ATOMIC_RELEASE); +} +#endif /* !__KERNEL__ */ + +#elif defined(__FreeBSD__) + +#ifdef _KERNEL +#define nm_stst_barrier atomic_thread_fence_rel +#else /* !_KERNEL */ +static inline void nm_stst_barrier(void) +{ + __atomic_thread_fence(__ATOMIC_RELEASE); +} +#endif /* !_KERNEL */ + +#else /* !__linux__ && !__FreeBSD__ */ +#error "OS not supported" +#endif /* !__linux__ && !__FreeBSD__ */ + +/* Application side of sync-kloop: Write ring pointers (cur, head) to the CSB. + * This routine is coupled with sync_kloop_kernel_read(). */ +static inline void +nm_sync_kloop_appl_write(struct nm_csb_atok *atok, uint32_t cur, + uint32_t head) +{ + /* + * We need to write cur and head to the CSB but we cannot do it atomically. + * There is no way we can prevent the host from reading the updated value + * of one of the two and the old value of the other. However, if we make + * sure that the host never reads a value of head more recent than the + * value of cur we are safe. We can allow the host to read a value of cur + * more recent than the value of head, since in the netmap ring cur can be + * ahead of head and cur cannot wrap around head because it must be behind + * tail. Inverting the order of writes below could instead result into the + * host to think head went ahead of cur, which would cause the sync + * prologue to fail. + * + * The following memory barrier scheme is used to make this happen: + * + * Guest Host + * + * STORE(cur) LOAD(head) + * mb() <-----------> mb() + * STORE(head) LOAD(cur) + * + */ + atok->cur = cur; + nm_stst_barrier(); + atok->head = head; +} + +/* Application side of sync-kloop: Read kring pointers (hwcur, hwtail) from + * the CSB. This routine is coupled with sync_kloop_kernel_write(). */ +static inline void +nm_sync_kloop_appl_read(struct nm_csb_ktoa *ktoa, uint32_t *hwtail, + uint32_t *hwcur) +{ + /* + * We place a memory barrier to make sure that the update of hwtail never + * overtakes the update of hwcur. + * (see explanation in sync_kloop_kernel_write). + */ + *hwtail = ktoa->hwtail; + nm_stst_barrier(); + *hwcur = ktoa->hwcur; +} + +/* * data for NETMAP_REQ_OPT_* options */ +struct nmreq_opt_sync_kloop_eventfds { + struct nmreq_option nro_opt; /* common header */ + /* An array of N entries for bidirectional notifications between + * the kernel loop and the application. The number of entries and + * their order must agree with the CSB arrays passed in the + * NETMAP_REQ_OPT_CSB option. Each entry contains a file descriptor + * backed by an eventfd. + */ + struct { + /* Notifier for the application --> kernel loop direction. */ + int32_t ioeventfd; + /* Notifier for the kernel loop --> application direction. */ + int32_t irqfd; + } eventfds[0]; +}; + struct nmreq_opt_extmem { struct nmreq_option nro_opt; /* common header */ uint64_t nro_usrptr; /* (in) ptr to usr memory */ struct nmreq_pools_info nro_info; /* (in/out) */ }; +struct nmreq_opt_csb { + struct nmreq_option nro_opt; + + /* Array of CSB entries for application --> kernel communication + * (N entries). */ + uint64_t csb_atok; + + /* Array of CSB entries for kernel --> application communication + * (N entries). */ + uint64_t csb_ktoa; +}; + #endif /* _NET_NETMAP_H_ */ diff --git a/sys/net/netmap_user.h b/sys/net/netmap_user.h index a86bcb6b6e3e..e9ce9c43e278 100644 --- a/sys/net/netmap_user.h +++ b/sys/net/netmap_user.h @@ -138,11 +138,12 @@ nm_tx_pending(struct netmap_ring *r) return nm_ring_next(r, r->tail) != r->head; } - +/* Compute the number of slots available in the netmap ring. We use + * ring->head as explained in the comment above nm_ring_empty(). */ static inline uint32_t nm_ring_space(struct netmap_ring *ring) { - int ret = ring->tail - ring->cur; + int ret = ring->tail - ring->head; if (ret < 0) ret += ring->num_slots; return ret; @@ -1091,18 +1092,36 @@ nm_dispatch(struct nm_desc *d, int cnt, nm_cb_t cb, u_char *arg) ring = NETMAP_RXRING(d->nifp, ri); for ( ; !nm_ring_empty(ring) && cnt != got; got++) { u_int idx, i; + u_char *oldbuf; + struct netmap_slot *slot; if (d->hdr.buf) { /* from previous round */ cb(arg, &d->hdr, d->hdr.buf); } i = ring->cur; - idx = ring->slot[i].buf_idx; + slot = &ring->slot[i]; + idx = slot->buf_idx; /* d->cur_rx_ring doesn't change inside this loop, but * set it here, so it reflects d->hdr.buf's ring */ d->cur_rx_ring = ri; - d->hdr.slot = &ring->slot[i]; - d->hdr.buf = (u_char *)NETMAP_BUF(ring, idx); + d->hdr.slot = slot; + oldbuf = d->hdr.buf = (u_char *)NETMAP_BUF(ring, idx); // __builtin_prefetch(buf); - d->hdr.len = d->hdr.caplen = ring->slot[i].len; + d->hdr.len = d->hdr.caplen = slot->len; + while (slot->flags & NS_MOREFRAG) { + u_char *nbuf; + u_int oldlen = slot->len; + i = nm_ring_next(ring, i); + slot = &ring->slot[i]; + d->hdr.len += slot->len; + nbuf = (u_char *)NETMAP_BUF(ring, slot->buf_idx); + if (oldbuf != NULL && nbuf - oldbuf == ring->nr_buf_size && + oldlen == ring->nr_buf_size) { + d->hdr.caplen += slot->len; + oldbuf = nbuf; + } else { + oldbuf = NULL; + } + } d->hdr.ts = ring->ts; ring->head = ring->cur = nm_ring_next(ring, i); } diff --git a/sys/net/netmap_virt.h b/sys/net/netmap_virt.h index 1b8b26cc9fd3..07e551aff009 100644 --- a/sys/net/netmap_virt.h +++ b/sys/net/netmap_virt.h @@ -1,7 +1,7 @@ /* * Copyright (C) 2013-2016 Luigi Rizzo * Copyright (C) 2013-2016 Giuseppe Lettieri - * Copyright (C) 2013-2016 Vincenzo Maffione + * Copyright (C) 2013-2018 Vincenzo Maffione * Copyright (C) 2015 Stefano Garzarella * All rights reserved. * @@ -33,14 +33,15 @@ #define NETMAP_VIRT_H /* - * ptnetmap_memdev: device used to expose memory into the guest VM + * Register offsets and other macros for the ptnetmap paravirtual devices: + * ptnetmap-memdev: device used to expose memory into the guest + * ptnet: paravirtualized NIC exposing a netmap port in the guest * * These macros are used in the hypervisor frontend (QEMU, bhyve) and in the * guest device driver. */ -/* PCI identifiers and PCI BARs for the ptnetmap memdev - * and ptnetmap network interface. */ +/* PCI identifiers and PCI BARs for ptnetmap-memdev and ptnet. */ #define PTNETMAP_MEMDEV_NAME "ptnetmap-memdev" #define PTNETMAP_PCI_VENDOR_ID 0x1b36 /* QEMU virtual devices */ #define PTNETMAP_PCI_DEVICE_ID 0x000c /* memory device */ @@ -49,7 +50,7 @@ #define PTNETMAP_MEM_PCI_BAR 1 #define PTNETMAP_MSIX_PCI_BAR 2 -/* Registers for the ptnetmap memdev */ +/* Device registers for ptnetmap-memdev */ #define PTNET_MDEV_IO_MEMSIZE_LO 0 /* netmap memory size (low) */ #define PTNET_MDEV_IO_MEMSIZE_HI 4 /* netmap_memory_size (high) */ #define PTNET_MDEV_IO_MEMID 8 /* memory allocator ID in the host */ @@ -64,74 +65,10 @@ #define PTNET_MDEV_IO_BUF_POOL_OBJSZ 96 #define PTNET_MDEV_IO_END 100 -/* - * ptnetmap configuration - * - * The ptnet kthreads (running in host kernel-space) need to be configured - * in order to know how to intercept guest kicks (I/O register writes) and - * how to inject MSI-X interrupts to the guest. The configuration may vary - * depending on the hypervisor. Currently, we support QEMU/KVM on Linux and - * and bhyve on FreeBSD. - * The configuration is passed by the hypervisor to the host netmap module - * by means of an ioctl() with nr_cmd=NETMAP_PT_HOST_CREATE, and it is - * specified by the ptnetmap_cfg struct. This struct contains an header - * with general informations and an array of entries whose size depends - * on the hypervisor. The NETMAP_PT_HOST_CREATE command is issued every - * time the kthreads are started. - */ -struct ptnetmap_cfg { -#define PTNETMAP_CFGTYPE_QEMU 0x1 -#define PTNETMAP_CFGTYPE_BHYVE 0x2 - uint16_t cfgtype; /* how to interpret the cfg entries */ - uint16_t entry_size; /* size of a config entry */ - uint32_t num_rings; /* number of config entries */ - void *csb_gh; /* CSB for guest --> host communication */ - void *csb_hg; /* CSB for host --> guest communication */ - /* Configuration entries are allocated right after the struct. */ -}; - -/* Configuration of a ptnetmap ring for QEMU. */ -struct ptnetmap_cfgentry_qemu { - uint32_t ioeventfd; /* to intercept guest register access */ - uint32_t irqfd; /* to inject guest interrupts */ -}; - -/* Configuration of a ptnetmap ring for bhyve. */ -struct ptnetmap_cfgentry_bhyve { - uint64_t wchan; /* tsleep() parameter, to wake up kthread */ - uint32_t ioctl_fd; /* ioctl fd */ - /* ioctl parameters to send irq */ - uint32_t ioctl_cmd; - /* vmm.ko MSIX parameters for IOCTL */ - struct { - uint64_t msg_data; - uint64_t addr; - } ioctl_data; -}; - -/* - * Pass a pointer to a userspace buffer to be passed to kernelspace for write - * or read. Used by NETMAP_PT_HOST_CREATE. - * XXX deprecated - */ -static inline void -nmreq_pointer_put(struct nmreq *nmr, void *userptr) -{ - uintptr_t *pp = (uintptr_t *)&nmr->nr_arg1; - *pp = (uintptr_t)userptr; -} - -static inline void * -nmreq_pointer_get(const struct nmreq *nmr) -{ - const uintptr_t *pp = (const uintptr_t *)&nmr->nr_arg1; - return (void *)*pp; -} - /* ptnetmap features */ #define PTNETMAP_F_VNET_HDR 1 -/* I/O registers for the ptnet device. */ +/* Device registers for the ptnet network device. */ #define PTNET_IO_PTFEAT 0 #define PTNET_IO_PTCTL 4 #define PTNET_IO_MAC_LO 8 @@ -153,140 +90,11 @@ nmreq_pointer_get(const struct nmreq *nmr) #define PTNET_IO_KICK_BASE 128 #define PTNET_IO_MASK 0xff -/* ptnetmap control commands (values for PTCTL register) */ +/* ptnet control commands (values for PTCTL register): + * - CREATE starts the host sync-kloop + * - DELETE stops the host sync-kloop + */ #define PTNETMAP_PTCTL_CREATE 1 #define PTNETMAP_PTCTL_DELETE 2 -/* ptnetmap synchronization variables shared between guest and host */ -struct ptnet_csb_gh { - uint32_t head; /* GW+ HR+ the head of the guest netmap_ring */ - uint32_t cur; /* GW+ HR+ the cur of the guest netmap_ring */ - uint32_t guest_need_kick; /* GW+ HR+ host-->guest notification enable */ - uint32_t sync_flags; /* GW+ HR+ the flags of the guest [tx|rx]sync() */ - char pad[48]; /* pad to a 64 bytes cacheline */ -}; -struct ptnet_csb_hg { - uint32_t hwcur; /* GR+ HW+ the hwcur of the host netmap_kring */ - uint32_t hwtail; /* GR+ HW+ the hwtail of the host netmap_kring */ - uint32_t host_need_kick; /* GR+ HW+ guest-->host notification enable */ - char pad[4+48]; -}; - -#ifdef WITH_PTNETMAP_GUEST - -/* ptnetmap_memdev routines used to talk with ptnetmap_memdev device driver */ -struct ptnetmap_memdev; -int nm_os_pt_memdev_iomap(struct ptnetmap_memdev *, vm_paddr_t *, void **, - uint64_t *); -void nm_os_pt_memdev_iounmap(struct ptnetmap_memdev *); -uint32_t nm_os_pt_memdev_ioread(struct ptnetmap_memdev *, unsigned int); - -/* Guest driver: Write kring pointers (cur, head) to the CSB. - * This routine is coupled with ptnetmap_host_read_kring_csb(). */ -static inline void -ptnetmap_guest_write_kring_csb(struct ptnet_csb_gh *ptr, uint32_t cur, - uint32_t head) -{ - /* - * We need to write cur and head to the CSB but we cannot do it atomically. - * There is no way we can prevent the host from reading the updated value - * of one of the two and the old value of the other. However, if we make - * sure that the host never reads a value of head more recent than the - * value of cur we are safe. We can allow the host to read a value of cur - * more recent than the value of head, since in the netmap ring cur can be - * ahead of head and cur cannot wrap around head because it must be behind - * tail. Inverting the order of writes below could instead result into the - * host to think head went ahead of cur, which would cause the sync - * prologue to fail. - * - * The following memory barrier scheme is used to make this happen: - * - * Guest Host - * - * STORE(cur) LOAD(head) - * mb() <-----------> mb() - * STORE(head) LOAD(cur) - */ - ptr->cur = cur; - mb(); - ptr->head = head; -} - -/* Guest driver: Read kring pointers (hwcur, hwtail) from the CSB. - * This routine is coupled with ptnetmap_host_write_kring_csb(). */ -static inline void -ptnetmap_guest_read_kring_csb(struct ptnet_csb_hg *pthg, struct netmap_kring *kring) -{ - /* - * We place a memory barrier to make sure that the update of hwtail never - * overtakes the update of hwcur. - * (see explanation in ptnetmap_host_write_kring_csb). - */ - kring->nr_hwtail = pthg->hwtail; - mb(); - kring->nr_hwcur = pthg->hwcur; -} - -#endif /* WITH_PTNETMAP_GUEST */ - -#ifdef WITH_PTNETMAP_HOST -/* - * ptnetmap kernel thread routines - * */ - -/* Functions to read and write CSB fields in the host */ -#if defined (linux) -#define CSB_READ(csb, field, r) (get_user(r, &csb->field)) -#define CSB_WRITE(csb, field, v) (put_user(v, &csb->field)) -#else /* ! linux */ -#define CSB_READ(csb, field, r) (r = fuword32(&csb->field)) -#define CSB_WRITE(csb, field, v) (suword32(&csb->field, v)) -#endif /* ! linux */ - -/* Host netmap: Write kring pointers (hwcur, hwtail) to the CSB. - * This routine is coupled with ptnetmap_guest_read_kring_csb(). */ -static inline void -ptnetmap_host_write_kring_csb(struct ptnet_csb_hg __user *ptr, uint32_t hwcur, - uint32_t hwtail) -{ - /* - * The same scheme used in ptnetmap_guest_write_kring_csb() applies here. - * We allow the guest to read a value of hwcur more recent than the value - * of hwtail, since this would anyway result in a consistent view of the - * ring state (and hwcur can never wraparound hwtail, since hwcur must be - * behind head). - * - * The following memory barrier scheme is used to make this happen: - * - * Guest Host - * - * STORE(hwcur) LOAD(hwtail) - * mb() <-------------> mb() - * STORE(hwtail) LOAD(hwcur) - */ - CSB_WRITE(ptr, hwcur, hwcur); - mb(); - CSB_WRITE(ptr, hwtail, hwtail); -} - -/* Host netmap: Read kring pointers (head, cur, sync_flags) from the CSB. - * This routine is coupled with ptnetmap_guest_write_kring_csb(). */ -static inline void -ptnetmap_host_read_kring_csb(struct ptnet_csb_gh __user *ptr, - struct netmap_ring *shadow_ring, - uint32_t num_slots) -{ - /* - * We place a memory barrier to make sure that the update of head never - * overtakes the update of cur. - * (see explanation in ptnetmap_guest_write_kring_csb). - */ - CSB_READ(ptr, head, shadow_ring->head); - mb(); - CSB_READ(ptr, cur, shadow_ring->cur); - CSB_READ(ptr, sync_flags, shadow_ring->flags); -} - -#endif /* WITH_PTNETMAP_HOST */ - #endif /* NETMAP_VIRT_H */ |
