1718cf2ccSPedro F. Giffuni /*- 2718cf2ccSPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3718cf2ccSPedro F. Giffuni * 417885a7bSLuigi Rizzo * Copyright (C) 2013-2014 Universita` di Pisa. All rights reserved. 5f9790aebSLuigi Rizzo * 6f9790aebSLuigi Rizzo * Redistribution and use in source and binary forms, with or without 7f9790aebSLuigi Rizzo * modification, are permitted provided that the following conditions 8f9790aebSLuigi Rizzo * are met: 9f9790aebSLuigi Rizzo * 1. Redistributions of source code must retain the above copyright 10f9790aebSLuigi Rizzo * notice, this list of conditions and the following disclaimer. 11f9790aebSLuigi Rizzo * 2. Redistributions in binary form must reproduce the above copyright 12f9790aebSLuigi Rizzo * notice, this list of conditions and the following disclaimer in the 13f9790aebSLuigi Rizzo * documentation and/or other materials provided with the distribution. 14f9790aebSLuigi Rizzo * 15f9790aebSLuigi Rizzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16f9790aebSLuigi Rizzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17f9790aebSLuigi Rizzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18f9790aebSLuigi Rizzo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19f9790aebSLuigi Rizzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20f9790aebSLuigi Rizzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21f9790aebSLuigi Rizzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22f9790aebSLuigi Rizzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23f9790aebSLuigi Rizzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24f9790aebSLuigi Rizzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25f9790aebSLuigi Rizzo * SUCH DAMAGE. 26f9790aebSLuigi Rizzo */ 27f9790aebSLuigi Rizzo 28f9790aebSLuigi Rizzo /* $FreeBSD$ */ 29847bf383SLuigi Rizzo #include "opt_inet.h" 30847bf383SLuigi Rizzo #include "opt_inet6.h" 31f9790aebSLuigi Rizzo 32ffaa5debSSepherosa Ziehau #include <sys/param.h> 33f9790aebSLuigi Rizzo #include <sys/module.h> 34f9790aebSLuigi Rizzo #include <sys/errno.h> 35ffaa5debSSepherosa Ziehau #include <sys/jail.h> 36f0ea3689SLuigi Rizzo #include <sys/poll.h> /* POLLIN, POLLOUT */ 37f9790aebSLuigi Rizzo #include <sys/kernel.h> /* types used in module initialization */ 3837e3a6d3SLuigi Rizzo #include <sys/conf.h> /* DEV_MODULE_ORDERED */ 39f0ea3689SLuigi Rizzo #include <sys/endian.h> 4037e3a6d3SLuigi Rizzo #include <sys/syscallsubr.h> /* kern_ioctl() */ 41f9790aebSLuigi Rizzo 42f9790aebSLuigi Rizzo #include <sys/rwlock.h> 43f9790aebSLuigi Rizzo 44f9790aebSLuigi Rizzo #include <vm/vm.h> /* vtophys */ 45f9790aebSLuigi Rizzo #include <vm/pmap.h> /* vtophys */ 46f9790aebSLuigi Rizzo #include <vm/vm_param.h> 47f9790aebSLuigi Rizzo #include <vm/vm_object.h> 48f9790aebSLuigi Rizzo #include <vm/vm_page.h> 49f9790aebSLuigi Rizzo #include <vm/vm_pager.h> 50f9790aebSLuigi Rizzo #include <vm/uma.h> 51f9790aebSLuigi Rizzo 52f9790aebSLuigi Rizzo 53f9790aebSLuigi Rizzo #include <sys/malloc.h> 54f9790aebSLuigi Rizzo #include <sys/socket.h> /* sockaddrs */ 55f9790aebSLuigi Rizzo #include <sys/selinfo.h> 5637e3a6d3SLuigi Rizzo #include <sys/kthread.h> /* kthread_add() */ 5737e3a6d3SLuigi Rizzo #include <sys/proc.h> /* PROC_LOCK() */ 5837e3a6d3SLuigi Rizzo #include <sys/unistd.h> /* RFNOWAIT */ 5937e3a6d3SLuigi Rizzo #include <sys/sched.h> /* sched_bind() */ 6037e3a6d3SLuigi Rizzo #include <sys/smp.h> /* mp_maxid */ 61f9790aebSLuigi Rizzo #include <net/if.h> 62f9790aebSLuigi Rizzo #include <net/if_var.h> 634bf50f18SLuigi Rizzo #include <net/if_types.h> /* IFT_ETHER */ 644bf50f18SLuigi Rizzo #include <net/ethernet.h> /* ether_ifdetach */ 654bf50f18SLuigi Rizzo #include <net/if_dl.h> /* LLADDR */ 66f9790aebSLuigi Rizzo #include <machine/bus.h> /* bus_dmamap_* */ 67f0ea3689SLuigi Rizzo #include <netinet/in.h> /* in6_cksum_pseudo() */ 68f0ea3689SLuigi Rizzo #include <machine/in_cksum.h> /* in_pseudo(), in_cksum_hdr() */ 69f9790aebSLuigi Rizzo 70f9790aebSLuigi Rizzo #include <net/netmap.h> 71f9790aebSLuigi Rizzo #include <dev/netmap/netmap_kern.h> 7237e3a6d3SLuigi Rizzo #include <net/netmap_virt.h> 73f9790aebSLuigi Rizzo #include <dev/netmap/netmap_mem2.h> 74f9790aebSLuigi Rizzo 75f9790aebSLuigi Rizzo 76f9790aebSLuigi Rizzo /* ======================== FREEBSD-SPECIFIC ROUTINES ================== */ 77f9790aebSLuigi Rizzo 7837e3a6d3SLuigi Rizzo void nm_os_selinfo_init(NM_SELINFO_T *si) { 7937e3a6d3SLuigi Rizzo struct mtx *m = &si->m; 8037e3a6d3SLuigi Rizzo mtx_init(m, "nm_kn_lock", NULL, MTX_DEF); 8137e3a6d3SLuigi Rizzo knlist_init_mtx(&si->si.si_note, m); 8237e3a6d3SLuigi Rizzo } 8337e3a6d3SLuigi Rizzo 8437e3a6d3SLuigi Rizzo void 8537e3a6d3SLuigi Rizzo nm_os_selinfo_uninit(NM_SELINFO_T *si) 8637e3a6d3SLuigi Rizzo { 8737e3a6d3SLuigi Rizzo /* XXX kqueue(9) needed; these will mirror knlist_init. */ 8837e3a6d3SLuigi Rizzo knlist_delete(&si->si.si_note, curthread, 0 /* not locked */ ); 8937e3a6d3SLuigi Rizzo knlist_destroy(&si->si.si_note); 9037e3a6d3SLuigi Rizzo /* now we don't need the mutex anymore */ 9137e3a6d3SLuigi Rizzo mtx_destroy(&si->m); 9237e3a6d3SLuigi Rizzo } 9337e3a6d3SLuigi Rizzo 94c3e9b4dbSLuiz Otavio O Souza void * 95c3e9b4dbSLuiz Otavio O Souza nm_os_malloc(size_t size) 96c3e9b4dbSLuiz Otavio O Souza { 97c3e9b4dbSLuiz Otavio O Souza return malloc(size, M_DEVBUF, M_NOWAIT | M_ZERO); 98c3e9b4dbSLuiz Otavio O Souza } 99c3e9b4dbSLuiz Otavio O Souza 100c3e9b4dbSLuiz Otavio O Souza void * 101c3e9b4dbSLuiz Otavio O Souza nm_os_realloc(void *addr, size_t new_size, size_t old_size __unused) 102c3e9b4dbSLuiz Otavio O Souza { 103c3e9b4dbSLuiz Otavio O Souza return realloc(addr, new_size, M_DEVBUF, M_NOWAIT | M_ZERO); 104c3e9b4dbSLuiz Otavio O Souza } 105c3e9b4dbSLuiz Otavio O Souza 106c3e9b4dbSLuiz Otavio O Souza void 107c3e9b4dbSLuiz Otavio O Souza nm_os_free(void *addr) 108c3e9b4dbSLuiz Otavio O Souza { 109c3e9b4dbSLuiz Otavio O Souza free(addr, M_DEVBUF); 110c3e9b4dbSLuiz Otavio O Souza } 111c3e9b4dbSLuiz Otavio O Souza 11237e3a6d3SLuigi Rizzo void 11337e3a6d3SLuigi Rizzo nm_os_ifnet_lock(void) 11437e3a6d3SLuigi Rizzo { 115869d8878SAdrian Chadd IFNET_RLOCK(); 11637e3a6d3SLuigi Rizzo } 11737e3a6d3SLuigi Rizzo 11837e3a6d3SLuigi Rizzo void 11937e3a6d3SLuigi Rizzo nm_os_ifnet_unlock(void) 12037e3a6d3SLuigi Rizzo { 12167ca1051SAdrian Chadd IFNET_RUNLOCK(); 12237e3a6d3SLuigi Rizzo } 12337e3a6d3SLuigi Rizzo 12437e3a6d3SLuigi Rizzo static int netmap_use_count = 0; 12537e3a6d3SLuigi Rizzo 12637e3a6d3SLuigi Rizzo void 12737e3a6d3SLuigi Rizzo nm_os_get_module(void) 12837e3a6d3SLuigi Rizzo { 12937e3a6d3SLuigi Rizzo netmap_use_count++; 13037e3a6d3SLuigi Rizzo } 13137e3a6d3SLuigi Rizzo 13237e3a6d3SLuigi Rizzo void 13337e3a6d3SLuigi Rizzo nm_os_put_module(void) 13437e3a6d3SLuigi Rizzo { 13537e3a6d3SLuigi Rizzo netmap_use_count--; 13637e3a6d3SLuigi Rizzo } 13737e3a6d3SLuigi Rizzo 13837e3a6d3SLuigi Rizzo static void 13937e3a6d3SLuigi Rizzo netmap_ifnet_arrival_handler(void *arg __unused, struct ifnet *ifp) 14037e3a6d3SLuigi Rizzo { 14137e3a6d3SLuigi Rizzo netmap_undo_zombie(ifp); 14237e3a6d3SLuigi Rizzo } 14337e3a6d3SLuigi Rizzo 14437e3a6d3SLuigi Rizzo static void 14537e3a6d3SLuigi Rizzo netmap_ifnet_departure_handler(void *arg __unused, struct ifnet *ifp) 14637e3a6d3SLuigi Rizzo { 14737e3a6d3SLuigi Rizzo netmap_make_zombie(ifp); 14837e3a6d3SLuigi Rizzo } 14937e3a6d3SLuigi Rizzo 15037e3a6d3SLuigi Rizzo static eventhandler_tag nm_ifnet_ah_tag; 15137e3a6d3SLuigi Rizzo static eventhandler_tag nm_ifnet_dh_tag; 15237e3a6d3SLuigi Rizzo 15337e3a6d3SLuigi Rizzo int 15437e3a6d3SLuigi Rizzo nm_os_ifnet_init(void) 15537e3a6d3SLuigi Rizzo { 15637e3a6d3SLuigi Rizzo nm_ifnet_ah_tag = 15737e3a6d3SLuigi Rizzo EVENTHANDLER_REGISTER(ifnet_arrival_event, 15837e3a6d3SLuigi Rizzo netmap_ifnet_arrival_handler, 15937e3a6d3SLuigi Rizzo NULL, EVENTHANDLER_PRI_ANY); 16037e3a6d3SLuigi Rizzo nm_ifnet_dh_tag = 16137e3a6d3SLuigi Rizzo EVENTHANDLER_REGISTER(ifnet_departure_event, 16237e3a6d3SLuigi Rizzo netmap_ifnet_departure_handler, 16337e3a6d3SLuigi Rizzo NULL, EVENTHANDLER_PRI_ANY); 16437e3a6d3SLuigi Rizzo return 0; 16537e3a6d3SLuigi Rizzo } 16637e3a6d3SLuigi Rizzo 16737e3a6d3SLuigi Rizzo void 16837e3a6d3SLuigi Rizzo nm_os_ifnet_fini(void) 16937e3a6d3SLuigi Rizzo { 17037e3a6d3SLuigi Rizzo EVENTHANDLER_DEREGISTER(ifnet_arrival_event, 17137e3a6d3SLuigi Rizzo nm_ifnet_ah_tag); 17237e3a6d3SLuigi Rizzo EVENTHANDLER_DEREGISTER(ifnet_departure_event, 17337e3a6d3SLuigi Rizzo nm_ifnet_dh_tag); 17437e3a6d3SLuigi Rizzo } 17537e3a6d3SLuigi Rizzo 1764f80b14cSVincenzo Maffione unsigned 1774f80b14cSVincenzo Maffione nm_os_ifnet_mtu(struct ifnet *ifp) 1784f80b14cSVincenzo Maffione { 1794f80b14cSVincenzo Maffione #if __FreeBSD_version < 1100030 1804f80b14cSVincenzo Maffione return ifp->if_data.ifi_mtu; 1814f80b14cSVincenzo Maffione #else /* __FreeBSD_version >= 1100030 */ 1824f80b14cSVincenzo Maffione return ifp->if_mtu; 1834f80b14cSVincenzo Maffione #endif 1844f80b14cSVincenzo Maffione } 1854f80b14cSVincenzo Maffione 186e4166283SLuigi Rizzo rawsum_t 18737e3a6d3SLuigi Rizzo nm_os_csum_raw(uint8_t *data, size_t len, rawsum_t cur_sum) 188f0ea3689SLuigi Rizzo { 189f0ea3689SLuigi Rizzo /* TODO XXX please use the FreeBSD implementation for this. */ 190f0ea3689SLuigi Rizzo uint16_t *words = (uint16_t *)data; 191f0ea3689SLuigi Rizzo int nw = len / 2; 192f0ea3689SLuigi Rizzo int i; 193f0ea3689SLuigi Rizzo 194f0ea3689SLuigi Rizzo for (i = 0; i < nw; i++) 195f0ea3689SLuigi Rizzo cur_sum += be16toh(words[i]); 196f0ea3689SLuigi Rizzo 197f0ea3689SLuigi Rizzo if (len & 1) 198f0ea3689SLuigi Rizzo cur_sum += (data[len-1] << 8); 199f0ea3689SLuigi Rizzo 200f0ea3689SLuigi Rizzo return cur_sum; 201f0ea3689SLuigi Rizzo } 202f0ea3689SLuigi Rizzo 203f0ea3689SLuigi Rizzo /* Fold a raw checksum: 'cur_sum' is in host byte order, while the 204f0ea3689SLuigi Rizzo * return value is in network byte order. 205f0ea3689SLuigi Rizzo */ 206e4166283SLuigi Rizzo uint16_t 20737e3a6d3SLuigi Rizzo nm_os_csum_fold(rawsum_t cur_sum) 208f0ea3689SLuigi Rizzo { 209f0ea3689SLuigi Rizzo /* TODO XXX please use the FreeBSD implementation for this. */ 210f0ea3689SLuigi Rizzo while (cur_sum >> 16) 211f0ea3689SLuigi Rizzo cur_sum = (cur_sum & 0xFFFF) + (cur_sum >> 16); 212f0ea3689SLuigi Rizzo 213f0ea3689SLuigi Rizzo return htobe16((~cur_sum) & 0xFFFF); 214f0ea3689SLuigi Rizzo } 215f0ea3689SLuigi Rizzo 21637e3a6d3SLuigi Rizzo uint16_t nm_os_csum_ipv4(struct nm_iphdr *iph) 217f0ea3689SLuigi Rizzo { 218f0ea3689SLuigi Rizzo #if 0 219f0ea3689SLuigi Rizzo return in_cksum_hdr((void *)iph); 220f0ea3689SLuigi Rizzo #else 22137e3a6d3SLuigi Rizzo return nm_os_csum_fold(nm_os_csum_raw((uint8_t*)iph, sizeof(struct nm_iphdr), 0)); 222f0ea3689SLuigi Rizzo #endif 223f0ea3689SLuigi Rizzo } 224f0ea3689SLuigi Rizzo 225e4166283SLuigi Rizzo void 22637e3a6d3SLuigi Rizzo nm_os_csum_tcpudp_ipv4(struct nm_iphdr *iph, void *data, 227f0ea3689SLuigi Rizzo size_t datalen, uint16_t *check) 228f0ea3689SLuigi Rizzo { 2295a067ae1SLuigi Rizzo #ifdef INET 230f0ea3689SLuigi Rizzo uint16_t pseudolen = datalen + iph->protocol; 231f0ea3689SLuigi Rizzo 232f0ea3689SLuigi Rizzo /* Compute and insert the pseudo-header cheksum. */ 233f0ea3689SLuigi Rizzo *check = in_pseudo(iph->saddr, iph->daddr, 234f0ea3689SLuigi Rizzo htobe16(pseudolen)); 235f0ea3689SLuigi Rizzo /* Compute the checksum on TCP/UDP header + payload 236f0ea3689SLuigi Rizzo * (includes the pseudo-header). 237f0ea3689SLuigi Rizzo */ 23837e3a6d3SLuigi Rizzo *check = nm_os_csum_fold(nm_os_csum_raw(data, datalen, 0)); 2395a067ae1SLuigi Rizzo #else 2405a067ae1SLuigi Rizzo static int notsupported = 0; 2415a067ae1SLuigi Rizzo if (!notsupported) { 2425a067ae1SLuigi Rizzo notsupported = 1; 2435a067ae1SLuigi Rizzo D("inet4 segmentation not supported"); 2445a067ae1SLuigi Rizzo } 2455a067ae1SLuigi Rizzo #endif 246f0ea3689SLuigi Rizzo } 247f0ea3689SLuigi Rizzo 248e4166283SLuigi Rizzo void 24937e3a6d3SLuigi Rizzo nm_os_csum_tcpudp_ipv6(struct nm_ipv6hdr *ip6h, void *data, 250f0ea3689SLuigi Rizzo size_t datalen, uint16_t *check) 251f0ea3689SLuigi Rizzo { 252f0ea3689SLuigi Rizzo #ifdef INET6 253f0ea3689SLuigi Rizzo *check = in6_cksum_pseudo((void*)ip6h, datalen, ip6h->nexthdr, 0); 25437e3a6d3SLuigi Rizzo *check = nm_os_csum_fold(nm_os_csum_raw(data, datalen, 0)); 255f0ea3689SLuigi Rizzo #else 256f0ea3689SLuigi Rizzo static int notsupported = 0; 257f0ea3689SLuigi Rizzo if (!notsupported) { 258f0ea3689SLuigi Rizzo notsupported = 1; 259f0ea3689SLuigi Rizzo D("inet6 segmentation not supported"); 260f0ea3689SLuigi Rizzo } 261f0ea3689SLuigi Rizzo #endif 262f0ea3689SLuigi Rizzo } 263f0ea3689SLuigi Rizzo 26437e3a6d3SLuigi Rizzo /* on FreeBSD we send up one packet at a time */ 26537e3a6d3SLuigi Rizzo void * 26637e3a6d3SLuigi Rizzo nm_os_send_up(struct ifnet *ifp, struct mbuf *m, struct mbuf *prev) 26737e3a6d3SLuigi Rizzo { 26837e3a6d3SLuigi Rizzo NA(ifp)->if_input(ifp, m); 26937e3a6d3SLuigi Rizzo return NULL; 27037e3a6d3SLuigi Rizzo } 27137e3a6d3SLuigi Rizzo 27237e3a6d3SLuigi Rizzo int 27337e3a6d3SLuigi Rizzo nm_os_mbuf_has_offld(struct mbuf *m) 27437e3a6d3SLuigi Rizzo { 27537e3a6d3SLuigi Rizzo return m->m_pkthdr.csum_flags & (CSUM_TCP | CSUM_UDP | CSUM_SCTP | 27637e3a6d3SLuigi Rizzo CSUM_TCP_IPV6 | CSUM_UDP_IPV6 | 27737e3a6d3SLuigi Rizzo CSUM_SCTP_IPV6 | CSUM_TSO); 27837e3a6d3SLuigi Rizzo } 27937e3a6d3SLuigi Rizzo 28037e3a6d3SLuigi Rizzo static void 28137e3a6d3SLuigi Rizzo freebsd_generic_rx_handler(struct ifnet *ifp, struct mbuf *m) 28237e3a6d3SLuigi Rizzo { 283c3e9b4dbSLuiz Otavio O Souza int stolen; 284c3e9b4dbSLuiz Otavio O Souza 285c3e9b4dbSLuiz Otavio O Souza if (!NM_NA_VALID(ifp)) { 286c3e9b4dbSLuiz Otavio O Souza RD(1, "Warning: got RX packet for invalid emulated adapter"); 287c3e9b4dbSLuiz Otavio O Souza return; 288c3e9b4dbSLuiz Otavio O Souza } 289c3e9b4dbSLuiz Otavio O Souza 290c3e9b4dbSLuiz Otavio O Souza stolen = generic_rx_handler(ifp, m); 291c3e9b4dbSLuiz Otavio O Souza if (!stolen) { 29237e3a6d3SLuigi Rizzo struct netmap_generic_adapter *gna = 29337e3a6d3SLuigi Rizzo (struct netmap_generic_adapter *)NA(ifp); 29437e3a6d3SLuigi Rizzo gna->save_if_input(ifp, m); 29537e3a6d3SLuigi Rizzo } 29637e3a6d3SLuigi Rizzo } 297f0ea3689SLuigi Rizzo 298f9790aebSLuigi Rizzo /* 299f9790aebSLuigi Rizzo * Intercept the rx routine in the standard device driver. 300f9790aebSLuigi Rizzo * Second argument is non-zero to intercept, 0 to restore 301f9790aebSLuigi Rizzo */ 302f9790aebSLuigi Rizzo int 30337e3a6d3SLuigi Rizzo nm_os_catch_rx(struct netmap_generic_adapter *gna, int intercept) 304f9790aebSLuigi Rizzo { 305847bf383SLuigi Rizzo struct netmap_adapter *na = &gna->up.up; 306f9790aebSLuigi Rizzo struct ifnet *ifp = na->ifp; 3074f80b14cSVincenzo Maffione int ret = 0; 308f9790aebSLuigi Rizzo 3094f80b14cSVincenzo Maffione nm_os_ifnet_lock(); 310f9790aebSLuigi Rizzo if (intercept) { 311f9790aebSLuigi Rizzo if (gna->save_if_input) { 312f9790aebSLuigi Rizzo D("cannot intercept again"); 3134f80b14cSVincenzo Maffione ret = EINVAL; /* already set */ 3144f80b14cSVincenzo Maffione goto out; 315f9790aebSLuigi Rizzo } 316f9790aebSLuigi Rizzo gna->save_if_input = ifp->if_input; 31737e3a6d3SLuigi Rizzo ifp->if_input = freebsd_generic_rx_handler; 318f9790aebSLuigi Rizzo } else { 319f9790aebSLuigi Rizzo if (!gna->save_if_input){ 320f9790aebSLuigi Rizzo D("cannot restore"); 3214f80b14cSVincenzo Maffione ret = EINVAL; /* not saved */ 3224f80b14cSVincenzo Maffione goto out; 323f9790aebSLuigi Rizzo } 324f9790aebSLuigi Rizzo ifp->if_input = gna->save_if_input; 325f9790aebSLuigi Rizzo gna->save_if_input = NULL; 326f9790aebSLuigi Rizzo } 3274f80b14cSVincenzo Maffione out: 3284f80b14cSVincenzo Maffione nm_os_ifnet_unlock(); 329f9790aebSLuigi Rizzo 3304f80b14cSVincenzo Maffione return ret; 331f9790aebSLuigi Rizzo } 332f9790aebSLuigi Rizzo 33317885a7bSLuigi Rizzo 334f9790aebSLuigi Rizzo /* 335f9790aebSLuigi Rizzo * Intercept the packet steering routine in the tx path, 336f9790aebSLuigi Rizzo * so that we can decide which queue is used for an mbuf. 337f9790aebSLuigi Rizzo * Second argument is non-zero to intercept, 0 to restore. 338f0ea3689SLuigi Rizzo * On freebsd we just intercept if_transmit. 339f9790aebSLuigi Rizzo */ 34037e3a6d3SLuigi Rizzo int 34137e3a6d3SLuigi Rizzo nm_os_catch_tx(struct netmap_generic_adapter *gna, int intercept) 342f9790aebSLuigi Rizzo { 34317885a7bSLuigi Rizzo struct netmap_adapter *na = &gna->up.up; 344847bf383SLuigi Rizzo struct ifnet *ifp = netmap_generic_getifp(gna); 34517885a7bSLuigi Rizzo 3464f80b14cSVincenzo Maffione nm_os_ifnet_lock(); 34737e3a6d3SLuigi Rizzo if (intercept) { 34817885a7bSLuigi Rizzo na->if_transmit = ifp->if_transmit; 34917885a7bSLuigi Rizzo ifp->if_transmit = netmap_transmit; 350f9790aebSLuigi Rizzo } else { 35117885a7bSLuigi Rizzo ifp->if_transmit = na->if_transmit; 352f9790aebSLuigi Rizzo } 3534f80b14cSVincenzo Maffione nm_os_ifnet_unlock(); 35437e3a6d3SLuigi Rizzo 35537e3a6d3SLuigi Rizzo return 0; 356f9790aebSLuigi Rizzo } 357f9790aebSLuigi Rizzo 35817885a7bSLuigi Rizzo 359f0ea3689SLuigi Rizzo /* 360f0ea3689SLuigi Rizzo * Transmit routine used by generic_netmap_txsync(). Returns 0 on success 361f9790aebSLuigi Rizzo * and non-zero on error (which may be packet drops or other errors). 362f9790aebSLuigi Rizzo * addr and len identify the netmap buffer, m is the (preallocated) 363f9790aebSLuigi Rizzo * mbuf to use for transmissions. 364f9790aebSLuigi Rizzo * 365f9790aebSLuigi Rizzo * We should add a reference to the mbuf so the m_freem() at the end 366f9790aebSLuigi Rizzo * of the transmission does not consume resources. 367f9790aebSLuigi Rizzo * 368f9790aebSLuigi Rizzo * On FreeBSD, and on multiqueue cards, we can force the queue using 369c2529042SHans Petter Selasky * if (M_HASHTYPE_GET(m) != M_HASHTYPE_NONE) 370f9790aebSLuigi Rizzo * i = m->m_pkthdr.flowid % adapter->num_queues; 371f9790aebSLuigi Rizzo * else 372f9790aebSLuigi Rizzo * i = curcpu % adapter->num_queues; 373f9790aebSLuigi Rizzo * 374f9790aebSLuigi Rizzo */ 375f9790aebSLuigi Rizzo int 37637e3a6d3SLuigi Rizzo nm_os_generic_xmit_frame(struct nm_os_gen_arg *a) 377f9790aebSLuigi Rizzo { 378f9790aebSLuigi Rizzo int ret; 37937e3a6d3SLuigi Rizzo u_int len = a->len; 38037e3a6d3SLuigi Rizzo struct ifnet *ifp = a->ifp; 38137e3a6d3SLuigi Rizzo struct mbuf *m = a->m; 382f9790aebSLuigi Rizzo 38337e3a6d3SLuigi Rizzo #if __FreeBSD_version < 1100000 384e4166283SLuigi Rizzo /* 38537e3a6d3SLuigi Rizzo * Old FreeBSD versions. The mbuf has a cluster attached, 38637e3a6d3SLuigi Rizzo * we need to copy from the cluster to the netmap buffer. 387e4166283SLuigi Rizzo */ 38837e3a6d3SLuigi Rizzo if (MBUF_REFCNT(m) != 1) { 38937e3a6d3SLuigi Rizzo D("invalid refcnt %d for %p", MBUF_REFCNT(m), m); 390e4166283SLuigi Rizzo panic("in generic_xmit_frame"); 391e4166283SLuigi Rizzo } 392e4166283SLuigi Rizzo if (m->m_ext.ext_size < len) { 393e4166283SLuigi Rizzo RD(5, "size %d < len %d", m->m_ext.ext_size, len); 394e4166283SLuigi Rizzo len = m->m_ext.ext_size; 395e4166283SLuigi Rizzo } 39637e3a6d3SLuigi Rizzo bcopy(a->addr, m->m_data, len); 39737e3a6d3SLuigi Rizzo #else /* __FreeBSD_version >= 1100000 */ 39837e3a6d3SLuigi Rizzo /* New FreeBSD versions. Link the external storage to 39937e3a6d3SLuigi Rizzo * the netmap buffer, so that no copy is necessary. */ 40037e3a6d3SLuigi Rizzo m->m_ext.ext_buf = m->m_data = a->addr; 40137e3a6d3SLuigi Rizzo m->m_ext.ext_size = len; 40237e3a6d3SLuigi Rizzo #endif /* __FreeBSD_version >= 1100000 */ 40337e3a6d3SLuigi Rizzo 404e4166283SLuigi Rizzo m->m_len = m->m_pkthdr.len = len; 40537e3a6d3SLuigi Rizzo 40637e3a6d3SLuigi Rizzo /* mbuf refcnt is not contended, no need to use atomic 40737e3a6d3SLuigi Rizzo * (a memory barrier is enough). */ 40837e3a6d3SLuigi Rizzo SET_MBUF_REFCNT(m, 2); 409c2529042SHans Petter Selasky M_HASHTYPE_SET(m, M_HASHTYPE_OPAQUE); 41037e3a6d3SLuigi Rizzo m->m_pkthdr.flowid = a->ring_nr; 411f9790aebSLuigi Rizzo m->m_pkthdr.rcvif = ifp; /* used for tx notification */ 41217885a7bSLuigi Rizzo ret = NA(ifp)->if_transmit(ifp, m); 41337e3a6d3SLuigi Rizzo return ret ? -1 : 0; 414f9790aebSLuigi Rizzo } 415f9790aebSLuigi Rizzo 41617885a7bSLuigi Rizzo 4170dc809c0SLuigi Rizzo #if __FreeBSD_version >= 1100005 4180dc809c0SLuigi Rizzo struct netmap_adapter * 4190dc809c0SLuigi Rizzo netmap_getna(if_t ifp) 4200dc809c0SLuigi Rizzo { 4210dc809c0SLuigi Rizzo return (NA((struct ifnet *)ifp)); 4220dc809c0SLuigi Rizzo } 4230dc809c0SLuigi Rizzo #endif /* __FreeBSD_version >= 1100005 */ 4240dc809c0SLuigi Rizzo 425f9790aebSLuigi Rizzo /* 426f9790aebSLuigi Rizzo * The following two functions are empty until we have a generic 427f9790aebSLuigi Rizzo * way to extract the info from the ifp 428f9790aebSLuigi Rizzo */ 429f9790aebSLuigi Rizzo int 43037e3a6d3SLuigi Rizzo nm_os_generic_find_num_desc(struct ifnet *ifp, unsigned int *tx, unsigned int *rx) 431f9790aebSLuigi Rizzo { 432f9790aebSLuigi Rizzo return 0; 433f9790aebSLuigi Rizzo } 434f9790aebSLuigi Rizzo 43517885a7bSLuigi Rizzo 436f9790aebSLuigi Rizzo void 43737e3a6d3SLuigi Rizzo nm_os_generic_find_num_queues(struct ifnet *ifp, u_int *txq, u_int *rxq) 438f9790aebSLuigi Rizzo { 439c3e9b4dbSLuiz Otavio O Souza unsigned num_rings = netmap_generic_rings ? netmap_generic_rings : 1; 440c3e9b4dbSLuiz Otavio O Souza 441c3e9b4dbSLuiz Otavio O Souza *txq = num_rings; 442c3e9b4dbSLuiz Otavio O Souza *rxq = num_rings; 443f9790aebSLuigi Rizzo } 444f9790aebSLuigi Rizzo 44537e3a6d3SLuigi Rizzo void 44637e3a6d3SLuigi Rizzo nm_os_generic_set_features(struct netmap_generic_adapter *gna) 44737e3a6d3SLuigi Rizzo { 44837e3a6d3SLuigi Rizzo 44937e3a6d3SLuigi Rizzo gna->rxsg = 1; /* Supported through m_copydata. */ 45037e3a6d3SLuigi Rizzo gna->txqdisc = 0; /* Not supported. */ 45137e3a6d3SLuigi Rizzo } 45217885a7bSLuigi Rizzo 453e4166283SLuigi Rizzo void 45437e3a6d3SLuigi Rizzo nm_os_mitigation_init(struct nm_generic_mit *mit, int idx, struct netmap_adapter *na) 455f9790aebSLuigi Rizzo { 456f9790aebSLuigi Rizzo ND("called"); 457f0ea3689SLuigi Rizzo mit->mit_pending = 0; 4584bf50f18SLuigi Rizzo mit->mit_ring_idx = idx; 459f0ea3689SLuigi Rizzo mit->mit_na = na; 460f9790aebSLuigi Rizzo } 461f9790aebSLuigi Rizzo 462f9790aebSLuigi Rizzo 463e4166283SLuigi Rizzo void 46437e3a6d3SLuigi Rizzo nm_os_mitigation_start(struct nm_generic_mit *mit) 465f9790aebSLuigi Rizzo { 466f9790aebSLuigi Rizzo ND("called"); 467f9790aebSLuigi Rizzo } 468f9790aebSLuigi Rizzo 46917885a7bSLuigi Rizzo 470e4166283SLuigi Rizzo void 47137e3a6d3SLuigi Rizzo nm_os_mitigation_restart(struct nm_generic_mit *mit) 472f9790aebSLuigi Rizzo { 473f9790aebSLuigi Rizzo ND("called"); 474f9790aebSLuigi Rizzo } 475f9790aebSLuigi Rizzo 47617885a7bSLuigi Rizzo 477e4166283SLuigi Rizzo int 47837e3a6d3SLuigi Rizzo nm_os_mitigation_active(struct nm_generic_mit *mit) 479f9790aebSLuigi Rizzo { 480f9790aebSLuigi Rizzo ND("called"); 481f9790aebSLuigi Rizzo return 0; 482f9790aebSLuigi Rizzo } 483f9790aebSLuigi Rizzo 48417885a7bSLuigi Rizzo 485e4166283SLuigi Rizzo void 48637e3a6d3SLuigi Rizzo nm_os_mitigation_cleanup(struct nm_generic_mit *mit) 487f9790aebSLuigi Rizzo { 488f9790aebSLuigi Rizzo ND("called"); 489f9790aebSLuigi Rizzo } 490f9790aebSLuigi Rizzo 4914bf50f18SLuigi Rizzo static int 4924bf50f18SLuigi Rizzo nm_vi_dummy(struct ifnet *ifp, u_long cmd, caddr_t addr) 4934bf50f18SLuigi Rizzo { 4944bf50f18SLuigi Rizzo return EINVAL; 4954bf50f18SLuigi Rizzo } 4964bf50f18SLuigi Rizzo 4974bf50f18SLuigi Rizzo static void 4984bf50f18SLuigi Rizzo nm_vi_start(struct ifnet *ifp) 4994bf50f18SLuigi Rizzo { 5004bf50f18SLuigi Rizzo panic("nm_vi_start() must not be called"); 5014bf50f18SLuigi Rizzo } 5024bf50f18SLuigi Rizzo 5034bf50f18SLuigi Rizzo /* 5044bf50f18SLuigi Rizzo * Index manager of persistent virtual interfaces. 5054bf50f18SLuigi Rizzo * It is used to decide the lowest byte of the MAC address. 5064bf50f18SLuigi Rizzo * We use the same algorithm with management of bridge port index. 5074bf50f18SLuigi Rizzo */ 5084bf50f18SLuigi Rizzo #define NM_VI_MAX 255 5094bf50f18SLuigi Rizzo static struct { 5104bf50f18SLuigi Rizzo uint8_t index[NM_VI_MAX]; /* XXX just for a reasonable number */ 5114bf50f18SLuigi Rizzo uint8_t active; 5124bf50f18SLuigi Rizzo struct mtx lock; 5134bf50f18SLuigi Rizzo } nm_vi_indices; 5144bf50f18SLuigi Rizzo 5154bf50f18SLuigi Rizzo void 51637e3a6d3SLuigi Rizzo nm_os_vi_init_index(void) 5174bf50f18SLuigi Rizzo { 5184bf50f18SLuigi Rizzo int i; 5194bf50f18SLuigi Rizzo for (i = 0; i < NM_VI_MAX; i++) 5204bf50f18SLuigi Rizzo nm_vi_indices.index[i] = i; 5214bf50f18SLuigi Rizzo nm_vi_indices.active = 0; 5224bf50f18SLuigi Rizzo mtx_init(&nm_vi_indices.lock, "nm_vi_indices_lock", NULL, MTX_DEF); 5234bf50f18SLuigi Rizzo } 5244bf50f18SLuigi Rizzo 5254bf50f18SLuigi Rizzo /* return -1 if no index available */ 5264bf50f18SLuigi Rizzo static int 5274bf50f18SLuigi Rizzo nm_vi_get_index(void) 5284bf50f18SLuigi Rizzo { 5294bf50f18SLuigi Rizzo int ret; 5304bf50f18SLuigi Rizzo 5314bf50f18SLuigi Rizzo mtx_lock(&nm_vi_indices.lock); 5324bf50f18SLuigi Rizzo ret = nm_vi_indices.active == NM_VI_MAX ? -1 : 5334bf50f18SLuigi Rizzo nm_vi_indices.index[nm_vi_indices.active++]; 5344bf50f18SLuigi Rizzo mtx_unlock(&nm_vi_indices.lock); 5354bf50f18SLuigi Rizzo return ret; 5364bf50f18SLuigi Rizzo } 5374bf50f18SLuigi Rizzo 5384bf50f18SLuigi Rizzo static void 5394bf50f18SLuigi Rizzo nm_vi_free_index(uint8_t val) 5404bf50f18SLuigi Rizzo { 5414bf50f18SLuigi Rizzo int i, lim; 5424bf50f18SLuigi Rizzo 5434bf50f18SLuigi Rizzo mtx_lock(&nm_vi_indices.lock); 5444bf50f18SLuigi Rizzo lim = nm_vi_indices.active; 5454bf50f18SLuigi Rizzo for (i = 0; i < lim; i++) { 5464bf50f18SLuigi Rizzo if (nm_vi_indices.index[i] == val) { 5474bf50f18SLuigi Rizzo /* swap index[lim-1] and j */ 5484bf50f18SLuigi Rizzo int tmp = nm_vi_indices.index[lim-1]; 5494bf50f18SLuigi Rizzo nm_vi_indices.index[lim-1] = val; 5504bf50f18SLuigi Rizzo nm_vi_indices.index[i] = tmp; 5514bf50f18SLuigi Rizzo nm_vi_indices.active--; 5524bf50f18SLuigi Rizzo break; 5534bf50f18SLuigi Rizzo } 5544bf50f18SLuigi Rizzo } 5554bf50f18SLuigi Rizzo if (lim == nm_vi_indices.active) 5564bf50f18SLuigi Rizzo D("funny, index %u didn't found", val); 5574bf50f18SLuigi Rizzo mtx_unlock(&nm_vi_indices.lock); 5584bf50f18SLuigi Rizzo } 5594bf50f18SLuigi Rizzo #undef NM_VI_MAX 5604bf50f18SLuigi Rizzo 5614bf50f18SLuigi Rizzo /* 5624bf50f18SLuigi Rizzo * Implementation of a netmap-capable virtual interface that 5634bf50f18SLuigi Rizzo * registered to the system. 5644bf50f18SLuigi Rizzo * It is based on if_tap.c and ip_fw_log.c in FreeBSD 9. 5654bf50f18SLuigi Rizzo * 5664bf50f18SLuigi Rizzo * Note: Linux sets refcount to 0 on allocation of net_device, 5674bf50f18SLuigi Rizzo * then increments it on registration to the system. 5684bf50f18SLuigi Rizzo * FreeBSD sets refcount to 1 on if_alloc(), and does not 5694bf50f18SLuigi Rizzo * increment this refcount on if_attach(). 5704bf50f18SLuigi Rizzo */ 5714bf50f18SLuigi Rizzo int 57237e3a6d3SLuigi Rizzo nm_os_vi_persist(const char *name, struct ifnet **ret) 5734bf50f18SLuigi Rizzo { 5744bf50f18SLuigi Rizzo struct ifnet *ifp; 5754bf50f18SLuigi Rizzo u_short macaddr_hi; 5764bf50f18SLuigi Rizzo uint32_t macaddr_mid; 5774bf50f18SLuigi Rizzo u_char eaddr[6]; 5784bf50f18SLuigi Rizzo int unit = nm_vi_get_index(); /* just to decide MAC address */ 5794bf50f18SLuigi Rizzo 5804bf50f18SLuigi Rizzo if (unit < 0) 5814bf50f18SLuigi Rizzo return EBUSY; 5824bf50f18SLuigi Rizzo /* 5834bf50f18SLuigi Rizzo * We use the same MAC address generation method with tap 5844bf50f18SLuigi Rizzo * except for the highest octet is 00:be instead of 00:bd 5854bf50f18SLuigi Rizzo */ 5864bf50f18SLuigi Rizzo macaddr_hi = htons(0x00be); /* XXX tap + 1 */ 5874bf50f18SLuigi Rizzo macaddr_mid = (uint32_t) ticks; 5884bf50f18SLuigi Rizzo bcopy(&macaddr_hi, eaddr, sizeof(short)); 5894bf50f18SLuigi Rizzo bcopy(&macaddr_mid, &eaddr[2], sizeof(uint32_t)); 5904bf50f18SLuigi Rizzo eaddr[5] = (uint8_t)unit; 5914bf50f18SLuigi Rizzo 5924bf50f18SLuigi Rizzo ifp = if_alloc(IFT_ETHER); 5934bf50f18SLuigi Rizzo if (ifp == NULL) { 5944bf50f18SLuigi Rizzo D("if_alloc failed"); 5954bf50f18SLuigi Rizzo return ENOMEM; 5964bf50f18SLuigi Rizzo } 5974bf50f18SLuigi Rizzo if_initname(ifp, name, IF_DUNIT_NONE); 5984bf50f18SLuigi Rizzo ifp->if_mtu = 65536; 5994bf50f18SLuigi Rizzo ifp->if_flags = IFF_UP | IFF_SIMPLEX | IFF_MULTICAST; 6004bf50f18SLuigi Rizzo ifp->if_init = (void *)nm_vi_dummy; 6014bf50f18SLuigi Rizzo ifp->if_ioctl = nm_vi_dummy; 6024bf50f18SLuigi Rizzo ifp->if_start = nm_vi_start; 6034bf50f18SLuigi Rizzo ifp->if_mtu = ETHERMTU; 6044bf50f18SLuigi Rizzo IFQ_SET_MAXLEN(&ifp->if_snd, ifqmaxlen); 6054bf50f18SLuigi Rizzo ifp->if_capabilities |= IFCAP_LINKSTATE; 6064bf50f18SLuigi Rizzo ifp->if_capenable |= IFCAP_LINKSTATE; 6074bf50f18SLuigi Rizzo 6084bf50f18SLuigi Rizzo ether_ifattach(ifp, eaddr); 6094bf50f18SLuigi Rizzo *ret = ifp; 6104bf50f18SLuigi Rizzo return 0; 6114bf50f18SLuigi Rizzo } 61237e3a6d3SLuigi Rizzo 6134bf50f18SLuigi Rizzo /* unregister from the system and drop the final refcount */ 6144bf50f18SLuigi Rizzo void 61537e3a6d3SLuigi Rizzo nm_os_vi_detach(struct ifnet *ifp) 6164bf50f18SLuigi Rizzo { 6174bf50f18SLuigi Rizzo nm_vi_free_index(((char *)IF_LLADDR(ifp))[5]); 6184bf50f18SLuigi Rizzo ether_ifdetach(ifp); 6194bf50f18SLuigi Rizzo if_free(ifp); 6204bf50f18SLuigi Rizzo } 62117885a7bSLuigi Rizzo 622*2ff91c17SVincenzo Maffione #ifdef WITH_EXTMEM 623*2ff91c17SVincenzo Maffione #include <vm/vm_map.h> 624*2ff91c17SVincenzo Maffione #include <vm/vm_kern.h> 625*2ff91c17SVincenzo Maffione struct nm_os_extmem { 626*2ff91c17SVincenzo Maffione vm_object_t obj; 627*2ff91c17SVincenzo Maffione vm_offset_t kva; 628*2ff91c17SVincenzo Maffione vm_offset_t size; 629*2ff91c17SVincenzo Maffione vm_pindex_t scan; 630*2ff91c17SVincenzo Maffione }; 631*2ff91c17SVincenzo Maffione 632*2ff91c17SVincenzo Maffione void 633*2ff91c17SVincenzo Maffione nm_os_extmem_delete(struct nm_os_extmem *e) 634*2ff91c17SVincenzo Maffione { 635*2ff91c17SVincenzo Maffione D("freeing %lx bytes", e->size); 636*2ff91c17SVincenzo Maffione vm_map_remove(kernel_map, e->kva, e->kva + e->size); 637*2ff91c17SVincenzo Maffione nm_os_free(e); 638*2ff91c17SVincenzo Maffione } 639*2ff91c17SVincenzo Maffione 640*2ff91c17SVincenzo Maffione char * 641*2ff91c17SVincenzo Maffione nm_os_extmem_nextpage(struct nm_os_extmem *e) 642*2ff91c17SVincenzo Maffione { 643*2ff91c17SVincenzo Maffione char *rv = NULL; 644*2ff91c17SVincenzo Maffione if (e->scan < e->kva + e->size) { 645*2ff91c17SVincenzo Maffione rv = (char *)e->scan; 646*2ff91c17SVincenzo Maffione e->scan += PAGE_SIZE; 647*2ff91c17SVincenzo Maffione } 648*2ff91c17SVincenzo Maffione return rv; 649*2ff91c17SVincenzo Maffione } 650*2ff91c17SVincenzo Maffione 651*2ff91c17SVincenzo Maffione int 652*2ff91c17SVincenzo Maffione nm_os_extmem_isequal(struct nm_os_extmem *e1, struct nm_os_extmem *e2) 653*2ff91c17SVincenzo Maffione { 654*2ff91c17SVincenzo Maffione return (e1->obj == e1->obj); 655*2ff91c17SVincenzo Maffione } 656*2ff91c17SVincenzo Maffione 657*2ff91c17SVincenzo Maffione int 658*2ff91c17SVincenzo Maffione nm_os_extmem_nr_pages(struct nm_os_extmem *e) 659*2ff91c17SVincenzo Maffione { 660*2ff91c17SVincenzo Maffione return e->size >> PAGE_SHIFT; 661*2ff91c17SVincenzo Maffione } 662*2ff91c17SVincenzo Maffione 663*2ff91c17SVincenzo Maffione struct nm_os_extmem * 664*2ff91c17SVincenzo Maffione nm_os_extmem_create(unsigned long p, struct nmreq_pools_info *pi, int *perror) 665*2ff91c17SVincenzo Maffione { 666*2ff91c17SVincenzo Maffione vm_map_t map; 667*2ff91c17SVincenzo Maffione vm_map_entry_t entry; 668*2ff91c17SVincenzo Maffione vm_object_t obj; 669*2ff91c17SVincenzo Maffione vm_prot_t prot; 670*2ff91c17SVincenzo Maffione vm_pindex_t index; 671*2ff91c17SVincenzo Maffione boolean_t wired; 672*2ff91c17SVincenzo Maffione struct nm_os_extmem *e = NULL; 673*2ff91c17SVincenzo Maffione int rv, error = 0; 674*2ff91c17SVincenzo Maffione 675*2ff91c17SVincenzo Maffione e = nm_os_malloc(sizeof(*e)); 676*2ff91c17SVincenzo Maffione if (e == NULL) { 677*2ff91c17SVincenzo Maffione error = ENOMEM; 678*2ff91c17SVincenzo Maffione goto out; 679*2ff91c17SVincenzo Maffione } 680*2ff91c17SVincenzo Maffione 681*2ff91c17SVincenzo Maffione map = &curthread->td_proc->p_vmspace->vm_map; 682*2ff91c17SVincenzo Maffione rv = vm_map_lookup(&map, p, VM_PROT_RW, &entry, 683*2ff91c17SVincenzo Maffione &obj, &index, &prot, &wired); 684*2ff91c17SVincenzo Maffione if (rv != KERN_SUCCESS) { 685*2ff91c17SVincenzo Maffione D("address %lx not found", p); 686*2ff91c17SVincenzo Maffione goto out_free; 687*2ff91c17SVincenzo Maffione } 688*2ff91c17SVincenzo Maffione /* check that we are given the whole vm_object ? */ 689*2ff91c17SVincenzo Maffione vm_map_lookup_done(map, entry); 690*2ff91c17SVincenzo Maffione 691*2ff91c17SVincenzo Maffione // XXX can we really use obj after releasing the map lock? 692*2ff91c17SVincenzo Maffione e->obj = obj; 693*2ff91c17SVincenzo Maffione vm_object_reference(obj); 694*2ff91c17SVincenzo Maffione /* wire the memory and add the vm_object to the kernel map, 695*2ff91c17SVincenzo Maffione * to make sure that it is not fred even if the processes that 696*2ff91c17SVincenzo Maffione * are mmap()ing it all exit 697*2ff91c17SVincenzo Maffione */ 698*2ff91c17SVincenzo Maffione e->kva = vm_map_min(kernel_map); 699*2ff91c17SVincenzo Maffione e->size = obj->size << PAGE_SHIFT; 700*2ff91c17SVincenzo Maffione rv = vm_map_find(kernel_map, obj, 0, &e->kva, e->size, 0, 701*2ff91c17SVincenzo Maffione VMFS_OPTIMAL_SPACE, VM_PROT_READ | VM_PROT_WRITE, 702*2ff91c17SVincenzo Maffione VM_PROT_READ | VM_PROT_WRITE, 0); 703*2ff91c17SVincenzo Maffione if (rv != KERN_SUCCESS) { 704*2ff91c17SVincenzo Maffione D("vm_map_find(%lx) failed", e->size); 705*2ff91c17SVincenzo Maffione goto out_rel; 706*2ff91c17SVincenzo Maffione } 707*2ff91c17SVincenzo Maffione rv = vm_map_wire(kernel_map, e->kva, e->kva + e->size, 708*2ff91c17SVincenzo Maffione VM_MAP_WIRE_SYSTEM | VM_MAP_WIRE_NOHOLES); 709*2ff91c17SVincenzo Maffione if (rv != KERN_SUCCESS) { 710*2ff91c17SVincenzo Maffione D("vm_map_wire failed"); 711*2ff91c17SVincenzo Maffione goto out_rem; 712*2ff91c17SVincenzo Maffione } 713*2ff91c17SVincenzo Maffione 714*2ff91c17SVincenzo Maffione e->scan = e->kva; 715*2ff91c17SVincenzo Maffione 716*2ff91c17SVincenzo Maffione return e; 717*2ff91c17SVincenzo Maffione 718*2ff91c17SVincenzo Maffione out_rem: 719*2ff91c17SVincenzo Maffione vm_map_remove(kernel_map, e->kva, e->kva + e->size); 720*2ff91c17SVincenzo Maffione e->obj = NULL; 721*2ff91c17SVincenzo Maffione out_rel: 722*2ff91c17SVincenzo Maffione vm_object_deallocate(e->obj); 723*2ff91c17SVincenzo Maffione out_free: 724*2ff91c17SVincenzo Maffione nm_os_free(e); 725*2ff91c17SVincenzo Maffione out: 726*2ff91c17SVincenzo Maffione if (perror) 727*2ff91c17SVincenzo Maffione *perror = error; 728*2ff91c17SVincenzo Maffione return NULL; 729*2ff91c17SVincenzo Maffione } 730*2ff91c17SVincenzo Maffione #endif /* WITH_EXTMEM */ 731*2ff91c17SVincenzo Maffione 73237e3a6d3SLuigi Rizzo /* ======================== PTNETMAP SUPPORT ========================== */ 73337e3a6d3SLuigi Rizzo 73437e3a6d3SLuigi Rizzo #ifdef WITH_PTNETMAP_GUEST 73537e3a6d3SLuigi Rizzo #include <sys/bus.h> 73637e3a6d3SLuigi Rizzo #include <sys/rman.h> 73737e3a6d3SLuigi Rizzo #include <machine/bus.h> /* bus_dmamap_* */ 73837e3a6d3SLuigi Rizzo #include <machine/resource.h> 73937e3a6d3SLuigi Rizzo #include <dev/pci/pcivar.h> 74037e3a6d3SLuigi Rizzo #include <dev/pci/pcireg.h> 74137e3a6d3SLuigi Rizzo /* 74237e3a6d3SLuigi Rizzo * ptnetmap memory device (memdev) for freebsd guest, 74337e3a6d3SLuigi Rizzo * ssed to expose host netmap memory to the guest through a PCI BAR. 74437e3a6d3SLuigi Rizzo */ 74537e3a6d3SLuigi Rizzo 74637e3a6d3SLuigi Rizzo /* 74737e3a6d3SLuigi Rizzo * ptnetmap memdev private data structure 74837e3a6d3SLuigi Rizzo */ 74937e3a6d3SLuigi Rizzo struct ptnetmap_memdev { 75037e3a6d3SLuigi Rizzo device_t dev; 75137e3a6d3SLuigi Rizzo struct resource *pci_io; 75237e3a6d3SLuigi Rizzo struct resource *pci_mem; 75337e3a6d3SLuigi Rizzo struct netmap_mem_d *nm_mem; 75437e3a6d3SLuigi Rizzo }; 75537e3a6d3SLuigi Rizzo 75637e3a6d3SLuigi Rizzo static int ptn_memdev_probe(device_t); 75737e3a6d3SLuigi Rizzo static int ptn_memdev_attach(device_t); 75837e3a6d3SLuigi Rizzo static int ptn_memdev_detach(device_t); 75937e3a6d3SLuigi Rizzo static int ptn_memdev_shutdown(device_t); 76037e3a6d3SLuigi Rizzo 76137e3a6d3SLuigi Rizzo static device_method_t ptn_memdev_methods[] = { 76237e3a6d3SLuigi Rizzo DEVMETHOD(device_probe, ptn_memdev_probe), 76337e3a6d3SLuigi Rizzo DEVMETHOD(device_attach, ptn_memdev_attach), 76437e3a6d3SLuigi Rizzo DEVMETHOD(device_detach, ptn_memdev_detach), 76537e3a6d3SLuigi Rizzo DEVMETHOD(device_shutdown, ptn_memdev_shutdown), 76637e3a6d3SLuigi Rizzo DEVMETHOD_END 76737e3a6d3SLuigi Rizzo }; 76837e3a6d3SLuigi Rizzo 76937e3a6d3SLuigi Rizzo static driver_t ptn_memdev_driver = { 77037e3a6d3SLuigi Rizzo PTNETMAP_MEMDEV_NAME, 77137e3a6d3SLuigi Rizzo ptn_memdev_methods, 77237e3a6d3SLuigi Rizzo sizeof(struct ptnetmap_memdev), 77337e3a6d3SLuigi Rizzo }; 77437e3a6d3SLuigi Rizzo 77537e3a6d3SLuigi Rizzo /* We use (SI_ORDER_MIDDLE+1) here, see DEV_MODULE_ORDERED() invocation 77637e3a6d3SLuigi Rizzo * below. */ 77737e3a6d3SLuigi Rizzo static devclass_t ptnetmap_devclass; 77837e3a6d3SLuigi Rizzo DRIVER_MODULE_ORDERED(ptn_memdev, pci, ptn_memdev_driver, ptnetmap_devclass, 77937e3a6d3SLuigi Rizzo NULL, NULL, SI_ORDER_MIDDLE + 1); 78037e3a6d3SLuigi Rizzo 78137e3a6d3SLuigi Rizzo /* 78237e3a6d3SLuigi Rizzo * Map host netmap memory through PCI-BAR in the guest OS, 78337e3a6d3SLuigi Rizzo * returning physical (nm_paddr) and virtual (nm_addr) addresses 78437e3a6d3SLuigi Rizzo * of the netmap memory mapped in the guest. 78537e3a6d3SLuigi Rizzo */ 78637e3a6d3SLuigi Rizzo int 787a2a74091SLuigi Rizzo nm_os_pt_memdev_iomap(struct ptnetmap_memdev *ptn_dev, vm_paddr_t *nm_paddr, 788844a6f0cSLuigi Rizzo void **nm_addr, uint64_t *mem_size) 78937e3a6d3SLuigi Rizzo { 79037e3a6d3SLuigi Rizzo int rid; 79137e3a6d3SLuigi Rizzo 79237e3a6d3SLuigi Rizzo D("ptn_memdev_driver iomap"); 79337e3a6d3SLuigi Rizzo 79437e3a6d3SLuigi Rizzo rid = PCIR_BAR(PTNETMAP_MEM_PCI_BAR); 795844a6f0cSLuigi Rizzo *mem_size = bus_read_4(ptn_dev->pci_io, PTNET_MDEV_IO_MEMSIZE_HI); 796844a6f0cSLuigi Rizzo *mem_size = bus_read_4(ptn_dev->pci_io, PTNET_MDEV_IO_MEMSIZE_LO) | 797844a6f0cSLuigi Rizzo (*mem_size << 32); 79837e3a6d3SLuigi Rizzo 79937e3a6d3SLuigi Rizzo /* map memory allocator */ 80037e3a6d3SLuigi Rizzo ptn_dev->pci_mem = bus_alloc_resource(ptn_dev->dev, SYS_RES_MEMORY, 801844a6f0cSLuigi Rizzo &rid, 0, ~0, *mem_size, RF_ACTIVE); 80237e3a6d3SLuigi Rizzo if (ptn_dev->pci_mem == NULL) { 80337e3a6d3SLuigi Rizzo *nm_paddr = 0; 8044dd44461SLuiz Otavio O Souza *nm_addr = NULL; 80537e3a6d3SLuigi Rizzo return ENOMEM; 80637e3a6d3SLuigi Rizzo } 80737e3a6d3SLuigi Rizzo 80837e3a6d3SLuigi Rizzo *nm_paddr = rman_get_start(ptn_dev->pci_mem); 80937e3a6d3SLuigi Rizzo *nm_addr = rman_get_virtual(ptn_dev->pci_mem); 81037e3a6d3SLuigi Rizzo 811844a6f0cSLuigi Rizzo D("=== BAR %d start %lx len %lx mem_size %lx ===", 81237e3a6d3SLuigi Rizzo PTNETMAP_MEM_PCI_BAR, 813a2a74091SLuigi Rizzo (unsigned long)(*nm_paddr), 814a2a74091SLuigi Rizzo (unsigned long)rman_get_size(ptn_dev->pci_mem), 815844a6f0cSLuigi Rizzo (unsigned long)*mem_size); 81637e3a6d3SLuigi Rizzo return (0); 81737e3a6d3SLuigi Rizzo } 81837e3a6d3SLuigi Rizzo 819844a6f0cSLuigi Rizzo uint32_t 820844a6f0cSLuigi Rizzo nm_os_pt_memdev_ioread(struct ptnetmap_memdev *ptn_dev, unsigned int reg) 821844a6f0cSLuigi Rizzo { 822844a6f0cSLuigi Rizzo return bus_read_4(ptn_dev->pci_io, reg); 823844a6f0cSLuigi Rizzo } 824844a6f0cSLuigi Rizzo 82537e3a6d3SLuigi Rizzo /* Unmap host netmap memory. */ 82637e3a6d3SLuigi Rizzo void 82737e3a6d3SLuigi Rizzo nm_os_pt_memdev_iounmap(struct ptnetmap_memdev *ptn_dev) 82837e3a6d3SLuigi Rizzo { 82937e3a6d3SLuigi Rizzo D("ptn_memdev_driver iounmap"); 83037e3a6d3SLuigi Rizzo 83137e3a6d3SLuigi Rizzo if (ptn_dev->pci_mem) { 83237e3a6d3SLuigi Rizzo bus_release_resource(ptn_dev->dev, SYS_RES_MEMORY, 83337e3a6d3SLuigi Rizzo PCIR_BAR(PTNETMAP_MEM_PCI_BAR), ptn_dev->pci_mem); 83437e3a6d3SLuigi Rizzo ptn_dev->pci_mem = NULL; 83537e3a6d3SLuigi Rizzo } 83637e3a6d3SLuigi Rizzo } 83737e3a6d3SLuigi Rizzo 83837e3a6d3SLuigi Rizzo /* Device identification routine, return BUS_PROBE_DEFAULT on success, 83937e3a6d3SLuigi Rizzo * positive on failure */ 84037e3a6d3SLuigi Rizzo static int 84137e3a6d3SLuigi Rizzo ptn_memdev_probe(device_t dev) 84237e3a6d3SLuigi Rizzo { 84337e3a6d3SLuigi Rizzo char desc[256]; 84437e3a6d3SLuigi Rizzo 84537e3a6d3SLuigi Rizzo if (pci_get_vendor(dev) != PTNETMAP_PCI_VENDOR_ID) 84637e3a6d3SLuigi Rizzo return (ENXIO); 84737e3a6d3SLuigi Rizzo if (pci_get_device(dev) != PTNETMAP_PCI_DEVICE_ID) 84837e3a6d3SLuigi Rizzo return (ENXIO); 84937e3a6d3SLuigi Rizzo 85037e3a6d3SLuigi Rizzo snprintf(desc, sizeof(desc), "%s PCI adapter", 85137e3a6d3SLuigi Rizzo PTNETMAP_MEMDEV_NAME); 85237e3a6d3SLuigi Rizzo device_set_desc_copy(dev, desc); 85337e3a6d3SLuigi Rizzo 85437e3a6d3SLuigi Rizzo return (BUS_PROBE_DEFAULT); 85537e3a6d3SLuigi Rizzo } 85637e3a6d3SLuigi Rizzo 85737e3a6d3SLuigi Rizzo /* Device initialization routine. */ 85837e3a6d3SLuigi Rizzo static int 85937e3a6d3SLuigi Rizzo ptn_memdev_attach(device_t dev) 86037e3a6d3SLuigi Rizzo { 86137e3a6d3SLuigi Rizzo struct ptnetmap_memdev *ptn_dev; 86237e3a6d3SLuigi Rizzo int rid; 86337e3a6d3SLuigi Rizzo uint16_t mem_id; 86437e3a6d3SLuigi Rizzo 86537e3a6d3SLuigi Rizzo D("ptn_memdev_driver attach"); 86637e3a6d3SLuigi Rizzo 86737e3a6d3SLuigi Rizzo ptn_dev = device_get_softc(dev); 86837e3a6d3SLuigi Rizzo ptn_dev->dev = dev; 86937e3a6d3SLuigi Rizzo 87037e3a6d3SLuigi Rizzo pci_enable_busmaster(dev); 87137e3a6d3SLuigi Rizzo 87237e3a6d3SLuigi Rizzo rid = PCIR_BAR(PTNETMAP_IO_PCI_BAR); 87337e3a6d3SLuigi Rizzo ptn_dev->pci_io = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, 87437e3a6d3SLuigi Rizzo RF_ACTIVE); 87537e3a6d3SLuigi Rizzo if (ptn_dev->pci_io == NULL) { 87637e3a6d3SLuigi Rizzo device_printf(dev, "cannot map I/O space\n"); 87737e3a6d3SLuigi Rizzo return (ENXIO); 87837e3a6d3SLuigi Rizzo } 87937e3a6d3SLuigi Rizzo 880844a6f0cSLuigi Rizzo mem_id = bus_read_4(ptn_dev->pci_io, PTNET_MDEV_IO_MEMID); 88137e3a6d3SLuigi Rizzo 88237e3a6d3SLuigi Rizzo /* create guest allocator */ 88337e3a6d3SLuigi Rizzo ptn_dev->nm_mem = netmap_mem_pt_guest_attach(ptn_dev, mem_id); 88437e3a6d3SLuigi Rizzo if (ptn_dev->nm_mem == NULL) { 88537e3a6d3SLuigi Rizzo ptn_memdev_detach(dev); 88637e3a6d3SLuigi Rizzo return (ENOMEM); 88737e3a6d3SLuigi Rizzo } 88837e3a6d3SLuigi Rizzo netmap_mem_get(ptn_dev->nm_mem); 88937e3a6d3SLuigi Rizzo 890844a6f0cSLuigi Rizzo D("ptn_memdev_driver probe OK - host_mem_id: %d", mem_id); 89137e3a6d3SLuigi Rizzo 89237e3a6d3SLuigi Rizzo return (0); 89337e3a6d3SLuigi Rizzo } 89437e3a6d3SLuigi Rizzo 89537e3a6d3SLuigi Rizzo /* Device removal routine. */ 89637e3a6d3SLuigi Rizzo static int 89737e3a6d3SLuigi Rizzo ptn_memdev_detach(device_t dev) 89837e3a6d3SLuigi Rizzo { 89937e3a6d3SLuigi Rizzo struct ptnetmap_memdev *ptn_dev; 90037e3a6d3SLuigi Rizzo 90137e3a6d3SLuigi Rizzo D("ptn_memdev_driver detach"); 90237e3a6d3SLuigi Rizzo ptn_dev = device_get_softc(dev); 90337e3a6d3SLuigi Rizzo 90437e3a6d3SLuigi Rizzo if (ptn_dev->nm_mem) { 90537e3a6d3SLuigi Rizzo netmap_mem_put(ptn_dev->nm_mem); 90637e3a6d3SLuigi Rizzo ptn_dev->nm_mem = NULL; 90737e3a6d3SLuigi Rizzo } 90837e3a6d3SLuigi Rizzo if (ptn_dev->pci_mem) { 90937e3a6d3SLuigi Rizzo bus_release_resource(dev, SYS_RES_MEMORY, 91037e3a6d3SLuigi Rizzo PCIR_BAR(PTNETMAP_MEM_PCI_BAR), ptn_dev->pci_mem); 91137e3a6d3SLuigi Rizzo ptn_dev->pci_mem = NULL; 91237e3a6d3SLuigi Rizzo } 91337e3a6d3SLuigi Rizzo if (ptn_dev->pci_io) { 91437e3a6d3SLuigi Rizzo bus_release_resource(dev, SYS_RES_IOPORT, 91537e3a6d3SLuigi Rizzo PCIR_BAR(PTNETMAP_IO_PCI_BAR), ptn_dev->pci_io); 91637e3a6d3SLuigi Rizzo ptn_dev->pci_io = NULL; 91737e3a6d3SLuigi Rizzo } 91837e3a6d3SLuigi Rizzo 91937e3a6d3SLuigi Rizzo return (0); 92037e3a6d3SLuigi Rizzo } 92137e3a6d3SLuigi Rizzo 92237e3a6d3SLuigi Rizzo static int 92337e3a6d3SLuigi Rizzo ptn_memdev_shutdown(device_t dev) 92437e3a6d3SLuigi Rizzo { 92537e3a6d3SLuigi Rizzo D("ptn_memdev_driver shutdown"); 92637e3a6d3SLuigi Rizzo return bus_generic_shutdown(dev); 92737e3a6d3SLuigi Rizzo } 92837e3a6d3SLuigi Rizzo 92937e3a6d3SLuigi Rizzo #endif /* WITH_PTNETMAP_GUEST */ 93037e3a6d3SLuigi Rizzo 931f9790aebSLuigi Rizzo /* 932f9790aebSLuigi Rizzo * In order to track whether pages are still mapped, we hook into 933f9790aebSLuigi Rizzo * the standard cdev_pager and intercept the constructor and 934f9790aebSLuigi Rizzo * destructor. 935f9790aebSLuigi Rizzo */ 936f9790aebSLuigi Rizzo 937f9790aebSLuigi Rizzo struct netmap_vm_handle_t { 938f9790aebSLuigi Rizzo struct cdev *dev; 939f9790aebSLuigi Rizzo struct netmap_priv_d *priv; 940f9790aebSLuigi Rizzo }; 941f9790aebSLuigi Rizzo 94217885a7bSLuigi Rizzo 943f9790aebSLuigi Rizzo static int 944f9790aebSLuigi Rizzo netmap_dev_pager_ctor(void *handle, vm_ooffset_t size, vm_prot_t prot, 945f9790aebSLuigi Rizzo vm_ooffset_t foff, struct ucred *cred, u_short *color) 946f9790aebSLuigi Rizzo { 947f9790aebSLuigi Rizzo struct netmap_vm_handle_t *vmh = handle; 948f0ea3689SLuigi Rizzo 949f0ea3689SLuigi Rizzo if (netmap_verbose) 950f9790aebSLuigi Rizzo D("handle %p size %jd prot %d foff %jd", 951f9790aebSLuigi Rizzo handle, (intmax_t)size, prot, (intmax_t)foff); 9524e93beffSLuigi Rizzo if (color) 9534e93beffSLuigi Rizzo *color = 0; 954f9790aebSLuigi Rizzo dev_ref(vmh->dev); 955f9790aebSLuigi Rizzo return 0; 956f9790aebSLuigi Rizzo } 957f9790aebSLuigi Rizzo 958f9790aebSLuigi Rizzo 959f9790aebSLuigi Rizzo static void 960f9790aebSLuigi Rizzo netmap_dev_pager_dtor(void *handle) 961f9790aebSLuigi Rizzo { 962f9790aebSLuigi Rizzo struct netmap_vm_handle_t *vmh = handle; 963f9790aebSLuigi Rizzo struct cdev *dev = vmh->dev; 964f9790aebSLuigi Rizzo struct netmap_priv_d *priv = vmh->priv; 965f0ea3689SLuigi Rizzo 966f0ea3689SLuigi Rizzo if (netmap_verbose) 967f9790aebSLuigi Rizzo D("handle %p", handle); 968f9790aebSLuigi Rizzo netmap_dtor(priv); 969f9790aebSLuigi Rizzo free(vmh, M_DEVBUF); 970f9790aebSLuigi Rizzo dev_rel(dev); 971f9790aebSLuigi Rizzo } 972f9790aebSLuigi Rizzo 97317885a7bSLuigi Rizzo 974f9790aebSLuigi Rizzo static int 975f9790aebSLuigi Rizzo netmap_dev_pager_fault(vm_object_t object, vm_ooffset_t offset, 976f9790aebSLuigi Rizzo int prot, vm_page_t *mres) 977f9790aebSLuigi Rizzo { 978f9790aebSLuigi Rizzo struct netmap_vm_handle_t *vmh = object->handle; 979f9790aebSLuigi Rizzo struct netmap_priv_d *priv = vmh->priv; 980847bf383SLuigi Rizzo struct netmap_adapter *na = priv->np_na; 981f9790aebSLuigi Rizzo vm_paddr_t paddr; 982f9790aebSLuigi Rizzo vm_page_t page; 983f9790aebSLuigi Rizzo vm_memattr_t memattr; 984f9790aebSLuigi Rizzo vm_pindex_t pidx; 985f9790aebSLuigi Rizzo 986f9790aebSLuigi Rizzo ND("object %p offset %jd prot %d mres %p", 987f9790aebSLuigi Rizzo object, (intmax_t)offset, prot, mres); 988f9790aebSLuigi Rizzo memattr = object->memattr; 989f9790aebSLuigi Rizzo pidx = OFF_TO_IDX(offset); 990847bf383SLuigi Rizzo paddr = netmap_mem_ofstophys(na->nm_mem, offset); 991f9790aebSLuigi Rizzo if (paddr == 0) 992f9790aebSLuigi Rizzo return VM_PAGER_FAIL; 993f9790aebSLuigi Rizzo 994f9790aebSLuigi Rizzo if (((*mres)->flags & PG_FICTITIOUS) != 0) { 995f9790aebSLuigi Rizzo /* 996f9790aebSLuigi Rizzo * If the passed in result page is a fake page, update it with 997f9790aebSLuigi Rizzo * the new physical address. 998f9790aebSLuigi Rizzo */ 999f9790aebSLuigi Rizzo page = *mres; 1000f9790aebSLuigi Rizzo vm_page_updatefake(page, paddr, memattr); 1001f9790aebSLuigi Rizzo } else { 1002f9790aebSLuigi Rizzo /* 1003f9790aebSLuigi Rizzo * Replace the passed in reqpage page with our own fake page and 1004f9790aebSLuigi Rizzo * free up the all of the original pages. 1005f9790aebSLuigi Rizzo */ 1006f9790aebSLuigi Rizzo #ifndef VM_OBJECT_WUNLOCK /* FreeBSD < 10.x */ 1007f9790aebSLuigi Rizzo #define VM_OBJECT_WUNLOCK VM_OBJECT_UNLOCK 1008f9790aebSLuigi Rizzo #define VM_OBJECT_WLOCK VM_OBJECT_LOCK 1009f9790aebSLuigi Rizzo #endif /* VM_OBJECT_WUNLOCK */ 1010f9790aebSLuigi Rizzo 1011f9790aebSLuigi Rizzo VM_OBJECT_WUNLOCK(object); 1012f9790aebSLuigi Rizzo page = vm_page_getfake(paddr, memattr); 1013f9790aebSLuigi Rizzo VM_OBJECT_WLOCK(object); 1014f9790aebSLuigi Rizzo vm_page_lock(*mres); 1015f9790aebSLuigi Rizzo vm_page_free(*mres); 1016f9790aebSLuigi Rizzo vm_page_unlock(*mres); 1017f9790aebSLuigi Rizzo *mres = page; 1018f9790aebSLuigi Rizzo vm_page_insert(page, object, pidx); 1019f9790aebSLuigi Rizzo } 1020f9790aebSLuigi Rizzo page->valid = VM_PAGE_BITS_ALL; 1021f9790aebSLuigi Rizzo return (VM_PAGER_OK); 1022f9790aebSLuigi Rizzo } 1023f9790aebSLuigi Rizzo 1024f9790aebSLuigi Rizzo 1025f9790aebSLuigi Rizzo static struct cdev_pager_ops netmap_cdev_pager_ops = { 1026f9790aebSLuigi Rizzo .cdev_pg_ctor = netmap_dev_pager_ctor, 1027f9790aebSLuigi Rizzo .cdev_pg_dtor = netmap_dev_pager_dtor, 1028f9790aebSLuigi Rizzo .cdev_pg_fault = netmap_dev_pager_fault, 1029f9790aebSLuigi Rizzo }; 1030f9790aebSLuigi Rizzo 1031f9790aebSLuigi Rizzo 1032f9790aebSLuigi Rizzo static int 1033f9790aebSLuigi Rizzo netmap_mmap_single(struct cdev *cdev, vm_ooffset_t *foff, 1034f9790aebSLuigi Rizzo vm_size_t objsize, vm_object_t *objp, int prot) 1035f9790aebSLuigi Rizzo { 1036f9790aebSLuigi Rizzo int error; 1037f9790aebSLuigi Rizzo struct netmap_vm_handle_t *vmh; 1038f9790aebSLuigi Rizzo struct netmap_priv_d *priv; 1039f9790aebSLuigi Rizzo vm_object_t obj; 1040f9790aebSLuigi Rizzo 1041f0ea3689SLuigi Rizzo if (netmap_verbose) 1042f9790aebSLuigi Rizzo D("cdev %p foff %jd size %jd objp %p prot %d", cdev, 1043f9790aebSLuigi Rizzo (intmax_t )*foff, (intmax_t )objsize, objp, prot); 1044f9790aebSLuigi Rizzo 1045f9790aebSLuigi Rizzo vmh = malloc(sizeof(struct netmap_vm_handle_t), M_DEVBUF, 1046f9790aebSLuigi Rizzo M_NOWAIT | M_ZERO); 1047f9790aebSLuigi Rizzo if (vmh == NULL) 1048f9790aebSLuigi Rizzo return ENOMEM; 1049f9790aebSLuigi Rizzo vmh->dev = cdev; 1050f9790aebSLuigi Rizzo 1051f9790aebSLuigi Rizzo NMG_LOCK(); 1052f9790aebSLuigi Rizzo error = devfs_get_cdevpriv((void**)&priv); 1053f9790aebSLuigi Rizzo if (error) 1054f9790aebSLuigi Rizzo goto err_unlock; 1055847bf383SLuigi Rizzo if (priv->np_nifp == NULL) { 1056847bf383SLuigi Rizzo error = EINVAL; 1057847bf383SLuigi Rizzo goto err_unlock; 1058847bf383SLuigi Rizzo } 1059f9790aebSLuigi Rizzo vmh->priv = priv; 10608fd44c93SLuigi Rizzo priv->np_refs++; 1061f9790aebSLuigi Rizzo NMG_UNLOCK(); 1062f9790aebSLuigi Rizzo 1063f9790aebSLuigi Rizzo obj = cdev_pager_allocate(vmh, OBJT_DEVICE, 1064f9790aebSLuigi Rizzo &netmap_cdev_pager_ops, objsize, prot, 1065f9790aebSLuigi Rizzo *foff, NULL); 1066f9790aebSLuigi Rizzo if (obj == NULL) { 1067f9790aebSLuigi Rizzo D("cdev_pager_allocate failed"); 1068f9790aebSLuigi Rizzo error = EINVAL; 1069f9790aebSLuigi Rizzo goto err_deref; 1070f9790aebSLuigi Rizzo } 1071f9790aebSLuigi Rizzo 1072f9790aebSLuigi Rizzo *objp = obj; 1073f9790aebSLuigi Rizzo return 0; 1074f9790aebSLuigi Rizzo 1075f9790aebSLuigi Rizzo err_deref: 1076f9790aebSLuigi Rizzo NMG_LOCK(); 10778fd44c93SLuigi Rizzo priv->np_refs--; 1078f9790aebSLuigi Rizzo err_unlock: 1079f9790aebSLuigi Rizzo NMG_UNLOCK(); 1080f9790aebSLuigi Rizzo // err: 1081f9790aebSLuigi Rizzo free(vmh, M_DEVBUF); 1082f9790aebSLuigi Rizzo return error; 1083f9790aebSLuigi Rizzo } 1084f9790aebSLuigi Rizzo 1085847bf383SLuigi Rizzo /* 10868fd44c93SLuigi Rizzo * On FreeBSD the close routine is only called on the last close on 10878fd44c93SLuigi Rizzo * the device (/dev/netmap) so we cannot do anything useful. 10888fd44c93SLuigi Rizzo * To track close() on individual file descriptors we pass netmap_dtor() to 1089847bf383SLuigi Rizzo * devfs_set_cdevpriv() on open(). The FreeBSD kernel will call the destructor 1090847bf383SLuigi Rizzo * when the last fd pointing to the device is closed. 1091847bf383SLuigi Rizzo * 10928fd44c93SLuigi Rizzo * Note that FreeBSD does not even munmap() on close() so we also have 10938fd44c93SLuigi Rizzo * to track mmap() ourselves, and postpone the call to 1094847bf383SLuigi Rizzo * netmap_dtor() is called when the process has no open fds and no active 1095847bf383SLuigi Rizzo * memory maps on /dev/netmap, as in linux. 1096847bf383SLuigi Rizzo */ 1097f9790aebSLuigi Rizzo static int 1098f9790aebSLuigi Rizzo netmap_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 1099f9790aebSLuigi Rizzo { 1100f9790aebSLuigi Rizzo if (netmap_verbose) 1101f9790aebSLuigi Rizzo D("dev %p fflag 0x%x devtype %d td %p", 1102f9790aebSLuigi Rizzo dev, fflag, devtype, td); 1103f9790aebSLuigi Rizzo return 0; 1104f9790aebSLuigi Rizzo } 1105f9790aebSLuigi Rizzo 1106f9790aebSLuigi Rizzo 1107f9790aebSLuigi Rizzo static int 1108f9790aebSLuigi Rizzo netmap_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 1109f9790aebSLuigi Rizzo { 1110f9790aebSLuigi Rizzo struct netmap_priv_d *priv; 1111f9790aebSLuigi Rizzo int error; 1112f9790aebSLuigi Rizzo 1113f9790aebSLuigi Rizzo (void)dev; 1114f9790aebSLuigi Rizzo (void)oflags; 1115f9790aebSLuigi Rizzo (void)devtype; 1116f9790aebSLuigi Rizzo (void)td; 1117f9790aebSLuigi Rizzo 111837e3a6d3SLuigi Rizzo NMG_LOCK(); 111937e3a6d3SLuigi Rizzo priv = netmap_priv_new(); 112037e3a6d3SLuigi Rizzo if (priv == NULL) { 112137e3a6d3SLuigi Rizzo error = ENOMEM; 112237e3a6d3SLuigi Rizzo goto out; 112337e3a6d3SLuigi Rizzo } 1124f9790aebSLuigi Rizzo error = devfs_set_cdevpriv(priv, netmap_dtor); 11258fd44c93SLuigi Rizzo if (error) { 112637e3a6d3SLuigi Rizzo netmap_priv_delete(priv); 11278fd44c93SLuigi Rizzo } 112837e3a6d3SLuigi Rizzo out: 112937e3a6d3SLuigi Rizzo NMG_UNLOCK(); 1130f9790aebSLuigi Rizzo return error; 1131f9790aebSLuigi Rizzo } 1132f9790aebSLuigi Rizzo 113337e3a6d3SLuigi Rizzo /******************** kthread wrapper ****************/ 113437e3a6d3SLuigi Rizzo #include <sys/sysproto.h> 113537e3a6d3SLuigi Rizzo u_int 113637e3a6d3SLuigi Rizzo nm_os_ncpus(void) 113737e3a6d3SLuigi Rizzo { 113837e3a6d3SLuigi Rizzo return mp_maxid + 1; 113937e3a6d3SLuigi Rizzo } 114037e3a6d3SLuigi Rizzo 1141c3e9b4dbSLuiz Otavio O Souza struct nm_kctx_ctx { 114237e3a6d3SLuigi Rizzo struct thread *user_td; /* thread user-space (kthread creator) to send ioctl */ 1143844a6f0cSLuigi Rizzo struct ptnetmap_cfgentry_bhyve cfg; 114437e3a6d3SLuigi Rizzo 114537e3a6d3SLuigi Rizzo /* worker function and parameter */ 1146c3e9b4dbSLuiz Otavio O Souza nm_kctx_worker_fn_t worker_fn; 114737e3a6d3SLuigi Rizzo void *worker_private; 114837e3a6d3SLuigi Rizzo 1149c3e9b4dbSLuiz Otavio O Souza struct nm_kctx *nmk; 115037e3a6d3SLuigi Rizzo 115137e3a6d3SLuigi Rizzo /* integer to manage multiple worker contexts (e.g., RX or TX on ptnetmap) */ 115237e3a6d3SLuigi Rizzo long type; 115337e3a6d3SLuigi Rizzo }; 115437e3a6d3SLuigi Rizzo 1155c3e9b4dbSLuiz Otavio O Souza struct nm_kctx { 115637e3a6d3SLuigi Rizzo struct thread *worker; 115737e3a6d3SLuigi Rizzo struct mtx worker_lock; 115837e3a6d3SLuigi Rizzo uint64_t scheduled; /* pending wake_up request */ 1159c3e9b4dbSLuiz Otavio O Souza struct nm_kctx_ctx worker_ctx; 116037e3a6d3SLuigi Rizzo int run; /* used to stop kthread */ 116137e3a6d3SLuigi Rizzo int attach_user; /* kthread attached to user_process */ 116237e3a6d3SLuigi Rizzo int affinity; 116337e3a6d3SLuigi Rizzo }; 116437e3a6d3SLuigi Rizzo 116537e3a6d3SLuigi Rizzo void inline 1166c3e9b4dbSLuiz Otavio O Souza nm_os_kctx_worker_wakeup(struct nm_kctx *nmk) 116737e3a6d3SLuigi Rizzo { 116837e3a6d3SLuigi Rizzo /* 116937e3a6d3SLuigi Rizzo * There may be a race between FE and BE, 117037e3a6d3SLuigi Rizzo * which call both this function, and worker kthread, 117137e3a6d3SLuigi Rizzo * that reads nmk->scheduled. 117237e3a6d3SLuigi Rizzo * 117337e3a6d3SLuigi Rizzo * For us it is not important the counter value, 117437e3a6d3SLuigi Rizzo * but simply that it has changed since the last 117537e3a6d3SLuigi Rizzo * time the kthread saw it. 117637e3a6d3SLuigi Rizzo */ 117737e3a6d3SLuigi Rizzo mtx_lock(&nmk->worker_lock); 117837e3a6d3SLuigi Rizzo nmk->scheduled++; 1179844a6f0cSLuigi Rizzo if (nmk->worker_ctx.cfg.wchan) { 118054c7693fSEd Maste wakeup((void *)(uintptr_t)nmk->worker_ctx.cfg.wchan); 118137e3a6d3SLuigi Rizzo } 118237e3a6d3SLuigi Rizzo mtx_unlock(&nmk->worker_lock); 118337e3a6d3SLuigi Rizzo } 118437e3a6d3SLuigi Rizzo 118537e3a6d3SLuigi Rizzo void inline 1186c3e9b4dbSLuiz Otavio O Souza nm_os_kctx_send_irq(struct nm_kctx *nmk) 118737e3a6d3SLuigi Rizzo { 1188c3e9b4dbSLuiz Otavio O Souza struct nm_kctx_ctx *ctx = &nmk->worker_ctx; 118937e3a6d3SLuigi Rizzo int err; 119037e3a6d3SLuigi Rizzo 1191844a6f0cSLuigi Rizzo if (ctx->user_td && ctx->cfg.ioctl_fd > 0) { 1192844a6f0cSLuigi Rizzo err = kern_ioctl(ctx->user_td, ctx->cfg.ioctl_fd, ctx->cfg.ioctl_cmd, 1193844a6f0cSLuigi Rizzo (caddr_t)&ctx->cfg.ioctl_data); 119437e3a6d3SLuigi Rizzo if (err) { 1195844a6f0cSLuigi Rizzo D("kern_ioctl error: %d ioctl parameters: fd %d com %lu data %p", 1196844a6f0cSLuigi Rizzo err, ctx->cfg.ioctl_fd, (unsigned long)ctx->cfg.ioctl_cmd, 1197844a6f0cSLuigi Rizzo &ctx->cfg.ioctl_data); 119837e3a6d3SLuigi Rizzo } 119937e3a6d3SLuigi Rizzo } 120037e3a6d3SLuigi Rizzo } 120137e3a6d3SLuigi Rizzo 120237e3a6d3SLuigi Rizzo static void 1203c3e9b4dbSLuiz Otavio O Souza nm_kctx_worker(void *data) 120437e3a6d3SLuigi Rizzo { 1205c3e9b4dbSLuiz Otavio O Souza struct nm_kctx *nmk = data; 1206c3e9b4dbSLuiz Otavio O Souza struct nm_kctx_ctx *ctx = &nmk->worker_ctx; 120737e3a6d3SLuigi Rizzo uint64_t old_scheduled = nmk->scheduled; 120837e3a6d3SLuigi Rizzo 120937e3a6d3SLuigi Rizzo if (nmk->affinity >= 0) { 121037e3a6d3SLuigi Rizzo thread_lock(curthread); 121137e3a6d3SLuigi Rizzo sched_bind(curthread, nmk->affinity); 121237e3a6d3SLuigi Rizzo thread_unlock(curthread); 121337e3a6d3SLuigi Rizzo } 121437e3a6d3SLuigi Rizzo 121537e3a6d3SLuigi Rizzo while (nmk->run) { 121637e3a6d3SLuigi Rizzo /* 121737e3a6d3SLuigi Rizzo * check if the parent process dies 121837e3a6d3SLuigi Rizzo * (when kthread is attached to user process) 121937e3a6d3SLuigi Rizzo */ 122037e3a6d3SLuigi Rizzo if (ctx->user_td) { 122137e3a6d3SLuigi Rizzo PROC_LOCK(curproc); 122237e3a6d3SLuigi Rizzo thread_suspend_check(0); 122337e3a6d3SLuigi Rizzo PROC_UNLOCK(curproc); 122437e3a6d3SLuigi Rizzo } else { 122537e3a6d3SLuigi Rizzo kthread_suspend_check(); 122637e3a6d3SLuigi Rizzo } 122737e3a6d3SLuigi Rizzo 122837e3a6d3SLuigi Rizzo /* 1229844a6f0cSLuigi Rizzo * if wchan is not defined, we don't have notification 123037e3a6d3SLuigi Rizzo * mechanism and we continually execute worker_fn() 123137e3a6d3SLuigi Rizzo */ 1232844a6f0cSLuigi Rizzo if (!ctx->cfg.wchan) { 1233c3e9b4dbSLuiz Otavio O Souza ctx->worker_fn(ctx->worker_private, 1); /* worker body */ 123437e3a6d3SLuigi Rizzo } else { 123537e3a6d3SLuigi Rizzo /* checks if there is a pending notification */ 123637e3a6d3SLuigi Rizzo mtx_lock(&nmk->worker_lock); 123737e3a6d3SLuigi Rizzo if (likely(nmk->scheduled != old_scheduled)) { 123837e3a6d3SLuigi Rizzo old_scheduled = nmk->scheduled; 123937e3a6d3SLuigi Rizzo mtx_unlock(&nmk->worker_lock); 124037e3a6d3SLuigi Rizzo 1241c3e9b4dbSLuiz Otavio O Souza ctx->worker_fn(ctx->worker_private, 1); /* worker body */ 124237e3a6d3SLuigi Rizzo 124337e3a6d3SLuigi Rizzo continue; 124437e3a6d3SLuigi Rizzo } else if (nmk->run) { 124537e3a6d3SLuigi Rizzo /* wait on event with one second timeout */ 1246c3e9b4dbSLuiz Otavio O Souza msleep((void *)(uintptr_t)ctx->cfg.wchan, &nmk->worker_lock, 1247c3e9b4dbSLuiz Otavio O Souza 0, "nmk_ev", hz); 124837e3a6d3SLuigi Rizzo nmk->scheduled++; 124937e3a6d3SLuigi Rizzo } 125037e3a6d3SLuigi Rizzo mtx_unlock(&nmk->worker_lock); 125137e3a6d3SLuigi Rizzo } 125237e3a6d3SLuigi Rizzo } 125337e3a6d3SLuigi Rizzo 125437e3a6d3SLuigi Rizzo kthread_exit(); 125537e3a6d3SLuigi Rizzo } 125637e3a6d3SLuigi Rizzo 125737e3a6d3SLuigi Rizzo void 1258c3e9b4dbSLuiz Otavio O Souza nm_os_kctx_worker_setaff(struct nm_kctx *nmk, int affinity) 125937e3a6d3SLuigi Rizzo { 126037e3a6d3SLuigi Rizzo nmk->affinity = affinity; 126137e3a6d3SLuigi Rizzo } 126237e3a6d3SLuigi Rizzo 1263c3e9b4dbSLuiz Otavio O Souza struct nm_kctx * 1264*2ff91c17SVincenzo Maffione nm_os_kctx_create(struct nm_kctx_cfg *cfg, void *opaque) 126537e3a6d3SLuigi Rizzo { 1266c3e9b4dbSLuiz Otavio O Souza struct nm_kctx *nmk = NULL; 1267844a6f0cSLuigi Rizzo 126837e3a6d3SLuigi Rizzo nmk = malloc(sizeof(*nmk), M_DEVBUF, M_NOWAIT | M_ZERO); 126937e3a6d3SLuigi Rizzo if (!nmk) 127037e3a6d3SLuigi Rizzo return NULL; 127137e3a6d3SLuigi Rizzo 1272869d8878SAdrian Chadd mtx_init(&nmk->worker_lock, "nm_kthread lock", NULL, MTX_DEF); 127337e3a6d3SLuigi Rizzo nmk->worker_ctx.worker_fn = cfg->worker_fn; 127437e3a6d3SLuigi Rizzo nmk->worker_ctx.worker_private = cfg->worker_private; 127537e3a6d3SLuigi Rizzo nmk->worker_ctx.type = cfg->type; 127637e3a6d3SLuigi Rizzo nmk->affinity = -1; 127737e3a6d3SLuigi Rizzo 127837e3a6d3SLuigi Rizzo /* attach kthread to user process (ptnetmap) */ 127937e3a6d3SLuigi Rizzo nmk->attach_user = cfg->attach_user; 128037e3a6d3SLuigi Rizzo 1281844a6f0cSLuigi Rizzo /* store kick/interrupt configuration */ 1282844a6f0cSLuigi Rizzo if (opaque) { 1283844a6f0cSLuigi Rizzo nmk->worker_ctx.cfg = *((struct ptnetmap_cfgentry_bhyve *)opaque); 1284844a6f0cSLuigi Rizzo } 128537e3a6d3SLuigi Rizzo 128637e3a6d3SLuigi Rizzo return nmk; 128737e3a6d3SLuigi Rizzo } 128837e3a6d3SLuigi Rizzo 128937e3a6d3SLuigi Rizzo int 1290c3e9b4dbSLuiz Otavio O Souza nm_os_kctx_worker_start(struct nm_kctx *nmk) 129137e3a6d3SLuigi Rizzo { 129237e3a6d3SLuigi Rizzo struct proc *p = NULL; 129337e3a6d3SLuigi Rizzo int error = 0; 129437e3a6d3SLuigi Rizzo 129537e3a6d3SLuigi Rizzo if (nmk->worker) { 129637e3a6d3SLuigi Rizzo return EBUSY; 129737e3a6d3SLuigi Rizzo } 129837e3a6d3SLuigi Rizzo 129937e3a6d3SLuigi Rizzo /* check if we want to attach kthread to user process */ 130037e3a6d3SLuigi Rizzo if (nmk->attach_user) { 130137e3a6d3SLuigi Rizzo nmk->worker_ctx.user_td = curthread; 130237e3a6d3SLuigi Rizzo p = curthread->td_proc; 130337e3a6d3SLuigi Rizzo } 130437e3a6d3SLuigi Rizzo 130537e3a6d3SLuigi Rizzo /* enable kthread main loop */ 130637e3a6d3SLuigi Rizzo nmk->run = 1; 130737e3a6d3SLuigi Rizzo /* create kthread */ 1308c3e9b4dbSLuiz Otavio O Souza if((error = kthread_add(nm_kctx_worker, nmk, p, 130937e3a6d3SLuigi Rizzo &nmk->worker, RFNOWAIT /* to be checked */, 0, "nm-kthread-%ld", 131037e3a6d3SLuigi Rizzo nmk->worker_ctx.type))) { 131137e3a6d3SLuigi Rizzo goto err; 131237e3a6d3SLuigi Rizzo } 131337e3a6d3SLuigi Rizzo 1314844a6f0cSLuigi Rizzo D("nm_kthread started td %p", nmk->worker); 131537e3a6d3SLuigi Rizzo 131637e3a6d3SLuigi Rizzo return 0; 131737e3a6d3SLuigi Rizzo err: 131837e3a6d3SLuigi Rizzo D("nm_kthread start failed err %d", error); 131937e3a6d3SLuigi Rizzo nmk->worker = NULL; 132037e3a6d3SLuigi Rizzo return error; 132137e3a6d3SLuigi Rizzo } 132237e3a6d3SLuigi Rizzo 132337e3a6d3SLuigi Rizzo void 1324c3e9b4dbSLuiz Otavio O Souza nm_os_kctx_worker_stop(struct nm_kctx *nmk) 132537e3a6d3SLuigi Rizzo { 132637e3a6d3SLuigi Rizzo if (!nmk->worker) { 132737e3a6d3SLuigi Rizzo return; 132837e3a6d3SLuigi Rizzo } 132937e3a6d3SLuigi Rizzo /* tell to kthread to exit from main loop */ 133037e3a6d3SLuigi Rizzo nmk->run = 0; 133137e3a6d3SLuigi Rizzo 133237e3a6d3SLuigi Rizzo /* wake up kthread if it sleeps */ 133337e3a6d3SLuigi Rizzo kthread_resume(nmk->worker); 1334c3e9b4dbSLuiz Otavio O Souza nm_os_kctx_worker_wakeup(nmk); 133537e3a6d3SLuigi Rizzo 133637e3a6d3SLuigi Rizzo nmk->worker = NULL; 133737e3a6d3SLuigi Rizzo } 133837e3a6d3SLuigi Rizzo 133937e3a6d3SLuigi Rizzo void 1340c3e9b4dbSLuiz Otavio O Souza nm_os_kctx_destroy(struct nm_kctx *nmk) 134137e3a6d3SLuigi Rizzo { 134237e3a6d3SLuigi Rizzo if (!nmk) 134337e3a6d3SLuigi Rizzo return; 134437e3a6d3SLuigi Rizzo if (nmk->worker) { 1345c3e9b4dbSLuiz Otavio O Souza nm_os_kctx_worker_stop(nmk); 134637e3a6d3SLuigi Rizzo } 134737e3a6d3SLuigi Rizzo 1348844a6f0cSLuigi Rizzo memset(&nmk->worker_ctx.cfg, 0, sizeof(nmk->worker_ctx.cfg)); 134937e3a6d3SLuigi Rizzo 135037e3a6d3SLuigi Rizzo free(nmk, M_DEVBUF); 135137e3a6d3SLuigi Rizzo } 135237e3a6d3SLuigi Rizzo 1353f0ea3689SLuigi Rizzo /******************** kqueue support ****************/ 1354f0ea3689SLuigi Rizzo 1355f0ea3689SLuigi Rizzo /* 135637e3a6d3SLuigi Rizzo * nm_os_selwakeup also needs to issue a KNOTE_UNLOCKED. 1357f0ea3689SLuigi Rizzo * We use a non-zero argument to distinguish the call from the one 1358f0ea3689SLuigi Rizzo * in kevent_scan() which instead also needs to run netmap_poll(). 1359f0ea3689SLuigi Rizzo * The knote uses a global mutex for the time being. We might 1360f0ea3689SLuigi Rizzo * try to reuse the one in the si, but it is not allocated 1361f0ea3689SLuigi Rizzo * permanently so it might be a bit tricky. 1362f0ea3689SLuigi Rizzo * 1363f0ea3689SLuigi Rizzo * The *kqfilter function registers one or another f_event 1364f0ea3689SLuigi Rizzo * depending on read or write mode. 1365f0ea3689SLuigi Rizzo * In the call to f_event() td_fpop is NULL so any child function 1366f0ea3689SLuigi Rizzo * calling devfs_get_cdevpriv() would fail - and we need it in 1367f0ea3689SLuigi Rizzo * netmap_poll(). As a workaround we store priv into kn->kn_hook 1368f0ea3689SLuigi Rizzo * and pass it as first argument to netmap_poll(), which then 1369f0ea3689SLuigi Rizzo * uses the failure to tell that we are called from f_event() 1370f0ea3689SLuigi Rizzo * and do not need the selrecord(). 1371f0ea3689SLuigi Rizzo */ 1372f0ea3689SLuigi Rizzo 1373f0ea3689SLuigi Rizzo 1374f0ea3689SLuigi Rizzo void 137537e3a6d3SLuigi Rizzo nm_os_selwakeup(struct nm_selinfo *si) 1376f0ea3689SLuigi Rizzo { 1377f0ea3689SLuigi Rizzo if (netmap_verbose) 13780e73f29aSLuigi Rizzo D("on knote %p", &si->si.si_note); 137937e3a6d3SLuigi Rizzo selwakeuppri(&si->si, PI_NET); 1380f0ea3689SLuigi Rizzo /* use a non-zero hint to tell the notification from the 1381f0ea3689SLuigi Rizzo * call done in kqueue_scan() which uses 0 1382f0ea3689SLuigi Rizzo */ 13830e73f29aSLuigi Rizzo KNOTE_UNLOCKED(&si->si.si_note, 0x100 /* notification */); 1384f0ea3689SLuigi Rizzo } 1385f0ea3689SLuigi Rizzo 138637e3a6d3SLuigi Rizzo void 138737e3a6d3SLuigi Rizzo nm_os_selrecord(struct thread *td, struct nm_selinfo *si) 138837e3a6d3SLuigi Rizzo { 138937e3a6d3SLuigi Rizzo selrecord(td, &si->si); 139037e3a6d3SLuigi Rizzo } 139137e3a6d3SLuigi Rizzo 1392f0ea3689SLuigi Rizzo static void 1393f0ea3689SLuigi Rizzo netmap_knrdetach(struct knote *kn) 1394f0ea3689SLuigi Rizzo { 1395f0ea3689SLuigi Rizzo struct netmap_priv_d *priv = (struct netmap_priv_d *)kn->kn_hook; 1396847bf383SLuigi Rizzo struct selinfo *si = &priv->np_si[NR_RX]->si; 1397f0ea3689SLuigi Rizzo 1398f0ea3689SLuigi Rizzo D("remove selinfo %p", si); 1399f0ea3689SLuigi Rizzo knlist_remove(&si->si_note, kn, 0); 1400f0ea3689SLuigi Rizzo } 1401f0ea3689SLuigi Rizzo 1402f0ea3689SLuigi Rizzo static void 1403f0ea3689SLuigi Rizzo netmap_knwdetach(struct knote *kn) 1404f0ea3689SLuigi Rizzo { 1405f0ea3689SLuigi Rizzo struct netmap_priv_d *priv = (struct netmap_priv_d *)kn->kn_hook; 1406847bf383SLuigi Rizzo struct selinfo *si = &priv->np_si[NR_TX]->si; 1407f0ea3689SLuigi Rizzo 1408f0ea3689SLuigi Rizzo D("remove selinfo %p", si); 1409f0ea3689SLuigi Rizzo knlist_remove(&si->si_note, kn, 0); 1410f0ea3689SLuigi Rizzo } 1411f0ea3689SLuigi Rizzo 1412f0ea3689SLuigi Rizzo /* 1413f0ea3689SLuigi Rizzo * callback from notifies (generated externally) and our 1414f0ea3689SLuigi Rizzo * calls to kevent(). The former we just return 1 (ready) 1415f0ea3689SLuigi Rizzo * since we do not know better. 1416f0ea3689SLuigi Rizzo * In the latter we call netmap_poll and return 0/1 accordingly. 1417f0ea3689SLuigi Rizzo */ 1418f0ea3689SLuigi Rizzo static int 1419f0ea3689SLuigi Rizzo netmap_knrw(struct knote *kn, long hint, int events) 1420f0ea3689SLuigi Rizzo { 1421f0ea3689SLuigi Rizzo struct netmap_priv_d *priv; 1422f0ea3689SLuigi Rizzo int revents; 1423f0ea3689SLuigi Rizzo 1424f0ea3689SLuigi Rizzo if (hint != 0) { 1425f0ea3689SLuigi Rizzo ND(5, "call from notify"); 1426f0ea3689SLuigi Rizzo return 1; /* assume we are ready */ 1427f0ea3689SLuigi Rizzo } 1428f0ea3689SLuigi Rizzo priv = kn->kn_hook; 1429f0ea3689SLuigi Rizzo /* the notification may come from an external thread, 1430f0ea3689SLuigi Rizzo * in which case we do not want to run the netmap_poll 1431f0ea3689SLuigi Rizzo * This should be filtered above, but check just in case. 1432f0ea3689SLuigi Rizzo */ 1433f0ea3689SLuigi Rizzo if (curthread != priv->np_td) { /* should not happen */ 1434f0ea3689SLuigi Rizzo RD(5, "curthread changed %p %p", curthread, priv->np_td); 1435f0ea3689SLuigi Rizzo return 1; 1436f0ea3689SLuigi Rizzo } else { 143737e3a6d3SLuigi Rizzo revents = netmap_poll(priv, events, NULL); 1438f0ea3689SLuigi Rizzo return (events & revents) ? 1 : 0; 1439f0ea3689SLuigi Rizzo } 1440f0ea3689SLuigi Rizzo } 1441f0ea3689SLuigi Rizzo 1442f0ea3689SLuigi Rizzo static int 1443f0ea3689SLuigi Rizzo netmap_knread(struct knote *kn, long hint) 1444f0ea3689SLuigi Rizzo { 1445f0ea3689SLuigi Rizzo return netmap_knrw(kn, hint, POLLIN); 1446f0ea3689SLuigi Rizzo } 1447f0ea3689SLuigi Rizzo 1448f0ea3689SLuigi Rizzo static int 1449f0ea3689SLuigi Rizzo netmap_knwrite(struct knote *kn, long hint) 1450f0ea3689SLuigi Rizzo { 1451f0ea3689SLuigi Rizzo return netmap_knrw(kn, hint, POLLOUT); 1452f0ea3689SLuigi Rizzo } 1453f0ea3689SLuigi Rizzo 1454f0ea3689SLuigi Rizzo static struct filterops netmap_rfiltops = { 1455f0ea3689SLuigi Rizzo .f_isfd = 1, 1456f0ea3689SLuigi Rizzo .f_detach = netmap_knrdetach, 1457f0ea3689SLuigi Rizzo .f_event = netmap_knread, 1458f0ea3689SLuigi Rizzo }; 1459f0ea3689SLuigi Rizzo 1460f0ea3689SLuigi Rizzo static struct filterops netmap_wfiltops = { 1461f0ea3689SLuigi Rizzo .f_isfd = 1, 1462f0ea3689SLuigi Rizzo .f_detach = netmap_knwdetach, 1463f0ea3689SLuigi Rizzo .f_event = netmap_knwrite, 1464f0ea3689SLuigi Rizzo }; 1465f0ea3689SLuigi Rizzo 1466f0ea3689SLuigi Rizzo 1467f0ea3689SLuigi Rizzo /* 1468f0ea3689SLuigi Rizzo * This is called when a thread invokes kevent() to record 1469f0ea3689SLuigi Rizzo * a change in the configuration of the kqueue(). 1470f0ea3689SLuigi Rizzo * The 'priv' should be the same as in the netmap device. 1471f0ea3689SLuigi Rizzo */ 1472f0ea3689SLuigi Rizzo static int 1473f0ea3689SLuigi Rizzo netmap_kqfilter(struct cdev *dev, struct knote *kn) 1474f0ea3689SLuigi Rizzo { 1475f0ea3689SLuigi Rizzo struct netmap_priv_d *priv; 1476f0ea3689SLuigi Rizzo int error; 1477f0ea3689SLuigi Rizzo struct netmap_adapter *na; 14780e73f29aSLuigi Rizzo struct nm_selinfo *si; 1479f0ea3689SLuigi Rizzo int ev = kn->kn_filter; 1480f0ea3689SLuigi Rizzo 1481f0ea3689SLuigi Rizzo if (ev != EVFILT_READ && ev != EVFILT_WRITE) { 1482f0ea3689SLuigi Rizzo D("bad filter request %d", ev); 1483f0ea3689SLuigi Rizzo return 1; 1484f0ea3689SLuigi Rizzo } 1485f0ea3689SLuigi Rizzo error = devfs_get_cdevpriv((void**)&priv); 1486f0ea3689SLuigi Rizzo if (error) { 1487f0ea3689SLuigi Rizzo D("device not yet setup"); 1488f0ea3689SLuigi Rizzo return 1; 1489f0ea3689SLuigi Rizzo } 1490f0ea3689SLuigi Rizzo na = priv->np_na; 1491f0ea3689SLuigi Rizzo if (na == NULL) { 1492f0ea3689SLuigi Rizzo D("no netmap adapter for this file descriptor"); 1493f0ea3689SLuigi Rizzo return 1; 1494f0ea3689SLuigi Rizzo } 1495f0ea3689SLuigi Rizzo /* the si is indicated in the priv */ 1496847bf383SLuigi Rizzo si = priv->np_si[(ev == EVFILT_WRITE) ? NR_TX : NR_RX]; 1497f0ea3689SLuigi Rizzo // XXX lock(priv) ? 1498f0ea3689SLuigi Rizzo kn->kn_fop = (ev == EVFILT_WRITE) ? 1499f0ea3689SLuigi Rizzo &netmap_wfiltops : &netmap_rfiltops; 1500f0ea3689SLuigi Rizzo kn->kn_hook = priv; 15010e73f29aSLuigi Rizzo knlist_add(&si->si.si_note, kn, 1); 1502f0ea3689SLuigi Rizzo // XXX unlock(priv) 1503f0ea3689SLuigi Rizzo ND("register %p %s td %p priv %p kn %p np_nifp %p kn_fp/fpop %s", 1504f0ea3689SLuigi Rizzo na, na->ifp->if_xname, curthread, priv, kn, 1505f0ea3689SLuigi Rizzo priv->np_nifp, 1506f0ea3689SLuigi Rizzo kn->kn_fp == curthread->td_fpop ? "match" : "MISMATCH"); 1507f0ea3689SLuigi Rizzo return 0; 1508f0ea3689SLuigi Rizzo } 1509f9790aebSLuigi Rizzo 151037e3a6d3SLuigi Rizzo static int 151137e3a6d3SLuigi Rizzo freebsd_netmap_poll(struct cdev *cdevi __unused, int events, struct thread *td) 151237e3a6d3SLuigi Rizzo { 151337e3a6d3SLuigi Rizzo struct netmap_priv_d *priv; 151437e3a6d3SLuigi Rizzo if (devfs_get_cdevpriv((void **)&priv)) { 151537e3a6d3SLuigi Rizzo return POLLERR; 151637e3a6d3SLuigi Rizzo } 151737e3a6d3SLuigi Rizzo return netmap_poll(priv, events, td); 151837e3a6d3SLuigi Rizzo } 151937e3a6d3SLuigi Rizzo 152037e3a6d3SLuigi Rizzo static int 152137e3a6d3SLuigi Rizzo freebsd_netmap_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t data, 152237e3a6d3SLuigi Rizzo int ffla __unused, struct thread *td) 152337e3a6d3SLuigi Rizzo { 152437e3a6d3SLuigi Rizzo int error; 152537e3a6d3SLuigi Rizzo struct netmap_priv_d *priv; 152637e3a6d3SLuigi Rizzo 1527ffaa5debSSepherosa Ziehau CURVNET_SET(TD_TO_VNET(td)); 152837e3a6d3SLuigi Rizzo error = devfs_get_cdevpriv((void **)&priv); 152937e3a6d3SLuigi Rizzo if (error) { 153037e3a6d3SLuigi Rizzo /* XXX ENOENT should be impossible, since the priv 153137e3a6d3SLuigi Rizzo * is now created in the open */ 153237e3a6d3SLuigi Rizzo if (error == ENOENT) 153337e3a6d3SLuigi Rizzo error = ENXIO; 153437e3a6d3SLuigi Rizzo goto out; 153537e3a6d3SLuigi Rizzo } 1536*2ff91c17SVincenzo Maffione error = netmap_ioctl(priv, cmd, data, td, /*nr_body_is_user=*/1); 153737e3a6d3SLuigi Rizzo out: 153837e3a6d3SLuigi Rizzo CURVNET_RESTORE(); 153937e3a6d3SLuigi Rizzo 154037e3a6d3SLuigi Rizzo return error; 154137e3a6d3SLuigi Rizzo } 154237e3a6d3SLuigi Rizzo 154337e3a6d3SLuigi Rizzo extern struct cdevsw netmap_cdevsw; /* XXX used in netmap.c, should go elsewhere */ 1544f9790aebSLuigi Rizzo struct cdevsw netmap_cdevsw = { 1545f9790aebSLuigi Rizzo .d_version = D_VERSION, 1546f9790aebSLuigi Rizzo .d_name = "netmap", 1547f9790aebSLuigi Rizzo .d_open = netmap_open, 1548f9790aebSLuigi Rizzo .d_mmap_single = netmap_mmap_single, 154937e3a6d3SLuigi Rizzo .d_ioctl = freebsd_netmap_ioctl, 155037e3a6d3SLuigi Rizzo .d_poll = freebsd_netmap_poll, 1551f0ea3689SLuigi Rizzo .d_kqfilter = netmap_kqfilter, 1552f9790aebSLuigi Rizzo .d_close = netmap_close, 1553f9790aebSLuigi Rizzo }; 1554f0ea3689SLuigi Rizzo /*--- end of kqueue support ----*/ 1555f9790aebSLuigi Rizzo 1556f9790aebSLuigi Rizzo /* 1557f9790aebSLuigi Rizzo * Kernel entry point. 1558f9790aebSLuigi Rizzo * 1559f9790aebSLuigi Rizzo * Initialize/finalize the module and return. 1560f9790aebSLuigi Rizzo * 1561f9790aebSLuigi Rizzo * Return 0 on success, errno on failure. 1562f9790aebSLuigi Rizzo */ 1563f9790aebSLuigi Rizzo static int 1564f9790aebSLuigi Rizzo netmap_loader(__unused struct module *module, int event, __unused void *arg) 1565f9790aebSLuigi Rizzo { 1566f9790aebSLuigi Rizzo int error = 0; 1567f9790aebSLuigi Rizzo 1568f9790aebSLuigi Rizzo switch (event) { 1569f9790aebSLuigi Rizzo case MOD_LOAD: 1570f9790aebSLuigi Rizzo error = netmap_init(); 1571f9790aebSLuigi Rizzo break; 1572f9790aebSLuigi Rizzo 1573f9790aebSLuigi Rizzo case MOD_UNLOAD: 1574847adfb7SLuigi Rizzo /* 1575847adfb7SLuigi Rizzo * if some one is still using netmap, 1576847adfb7SLuigi Rizzo * then the module can not be unloaded. 1577847adfb7SLuigi Rizzo */ 1578847adfb7SLuigi Rizzo if (netmap_use_count) { 1579847adfb7SLuigi Rizzo D("netmap module can not be unloaded - netmap_use_count: %d", 1580847adfb7SLuigi Rizzo netmap_use_count); 1581847adfb7SLuigi Rizzo error = EBUSY; 1582847adfb7SLuigi Rizzo break; 1583847adfb7SLuigi Rizzo } 1584f9790aebSLuigi Rizzo netmap_fini(); 1585f9790aebSLuigi Rizzo break; 1586f9790aebSLuigi Rizzo 1587f9790aebSLuigi Rizzo default: 1588f9790aebSLuigi Rizzo error = EOPNOTSUPP; 1589f9790aebSLuigi Rizzo break; 1590f9790aebSLuigi Rizzo } 1591f9790aebSLuigi Rizzo 1592f9790aebSLuigi Rizzo return (error); 1593f9790aebSLuigi Rizzo } 1594f9790aebSLuigi Rizzo 159537e3a6d3SLuigi Rizzo #ifdef DEV_MODULE_ORDERED 159637e3a6d3SLuigi Rizzo /* 159737e3a6d3SLuigi Rizzo * The netmap module contains three drivers: (i) the netmap character device 159837e3a6d3SLuigi Rizzo * driver; (ii) the ptnetmap memdev PCI device driver, (iii) the ptnet PCI 159937e3a6d3SLuigi Rizzo * device driver. The attach() routines of both (ii) and (iii) need the 160037e3a6d3SLuigi Rizzo * lock of the global allocator, and such lock is initialized in netmap_init(), 160137e3a6d3SLuigi Rizzo * which is part of (i). 160237e3a6d3SLuigi Rizzo * Therefore, we make sure that (i) is loaded before (ii) and (iii), using 160337e3a6d3SLuigi Rizzo * the 'order' parameter of driver declaration macros. For (i), we specify 160437e3a6d3SLuigi Rizzo * SI_ORDER_MIDDLE, while higher orders are used with the DRIVER_MODULE_ORDERED 160537e3a6d3SLuigi Rizzo * macros for (ii) and (iii). 160637e3a6d3SLuigi Rizzo */ 160737e3a6d3SLuigi Rizzo DEV_MODULE_ORDERED(netmap, netmap_loader, NULL, SI_ORDER_MIDDLE); 160837e3a6d3SLuigi Rizzo #else /* !DEV_MODULE_ORDERED */ 1609f9790aebSLuigi Rizzo DEV_MODULE(netmap, netmap_loader, NULL); 161037e3a6d3SLuigi Rizzo #endif /* DEV_MODULE_ORDERED */ 161137e3a6d3SLuigi Rizzo MODULE_DEPEND(netmap, pci, 1, 1, 1); 1612735c8d95SLuigi Rizzo MODULE_VERSION(netmap, 1); 161337e3a6d3SLuigi Rizzo /* reduce conditional code */ 161437e3a6d3SLuigi Rizzo // linux API, use for the knlist in FreeBSD 161537e3a6d3SLuigi Rizzo /* use a private mutex for the knlist */ 1616