Date: Tue, 25 Mar 2014 11:36:05 -0700 From: Sean Fagan <sef@ixsystems.com> To: freebsd-net@freebsd.org Subject: Non-interrupt packet sending and receiving Message-ID: <27D25BFC-7BB3-400F-8405-43B8D08135D2@ixsystems.com>
next in thread | raw e-mail | index | archive | help
--Apple-Mail=_CF84D4FE-C7D8-487C-93B9-541771AA3154 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=us-ascii This isn't the same as the polled driver; this is sending and receiving = a single packet at a time. I've gotten (at least to a somewhat workable degree) Apple's KDP ported = to FreeBSD. I've only changed the dev/e1000/if_lem.c driver for now = (that's the one VMWare shows up as :)), but since I'm not particularly = comfortable with device drivers, let alone ethernet drivers, I needed = some feedback. Diffs are attached below. Feedback would be appreciated. (To answer some of the questions I've already gotten: no, i can't use = the DEVICE_POLLING routines, because that still goes through the entire = stack. It's not here, because I am not yet happy with it, but the code = that uses this runs in the kernel debugger, and it needs to be able to = send and receive a single packet at a time -- and it can't let it be = shuffled off through other layers, for reasons that I hope are fairly = clear. Now, one change I would like to make is the mbuf allocation that = it uses; ideally, honestly, it should have its own mbufs -- the protocol = never sends more than 1538 bytes in a UDP packet -- but I would probably = try working on another ethernet driver first, modeling it after this.) (The bulk of the diffs is moving some code out of lem_rxeof into a = function that gets a single packet.) Thanks, Sean. --Apple-Mail=_CF84D4FE-C7D8-487C-93B9-541771AA3154 Content-Disposition: attachment; filename=if_lem-diffs.txt Content-Type: text/plain; name="if_lem-diffs.txt" Content-Transfer-Encoding: quoted-printable diff --git a/dev/e1000/if_lem.c b/dev/e1000/if_lem.c index bfe2c93..90ec8b3 100644 --- a/dev/e1000/if_lem.c +++ b/dev/e1000/if_lem.c @@ -34,6 +34,7 @@ =20 #include "opt_inet.h" #include "opt_inet6.h" +#include "opt_ddb.h" =20 #ifdef HAVE_KERNEL_OPTION_HEADERS #include "opt_device_polling.h" @@ -54,6 +55,7 @@ #include <sys/sysctl.h> #include <sys/taskqueue.h> #include <sys/eventhandler.h> +#include <sys/uio.h> #include <machine/bus.h> #include <machine/resource.h> =20 @@ -191,7 +193,7 @@ static void lem_free_transmit_structures(struct = adapter *); static void lem_free_receive_structures(struct adapter *); static void lem_update_stats_counters(struct adapter *); static void lem_add_hw_stats(struct adapter *adapter); -static void lem_txeof(struct adapter *); +static void lem_txeof(struct adapter *, int); static void lem_tx_purge(struct adapter *); static int lem_allocate_receive_structures(struct adapter *); static int lem_allocate_transmit_structures(struct adapter *); @@ -246,6 +248,74 @@ static void lem_handle_rxtx(void *context, = int pending); static void lem_handle_link(void *context, int pending); static void lem_add_rx_process_limit(struct adapter *, const char *, const char *, int *, int); +#ifdef DDB +typedef uint32_t (*kdp_link_t)(void); +typedef int (*kdp_mode_t)(int); +extern void *kdp_get_interface(void); +typedef void (*kdp_send_t)(void * pkt, unsigned int pkt_len); +typedef void (*kdp_receive_t)(void * pkt, unsigned int * pkt_len, + unsigned int timeout); +extern void kdp_register_send_receive(kdp_send_t send, kdp_receive_t = receive); +extern void kdp_unregister_send_receive(kdp_send_t send, kdp_receive_t = receive); + +/* + * Function called by kdp. + * timeout is in milliseconds. + * The data is 1538 bytes. + * Return the length in *length; set it to 0 if no packet. + */ +static void lem_kdp_recv_pkt(void *data, unsigned int *length, unsigned = int timeout); + +static void lem_kdp_send_pkt(void *pkt, unsigned int pkt_len); + +/* + * For kdp: + * lex_rxeof() may be usable. However, we'd have to + * change the ifp->if_input() function pointer to be + * something more conducive to our needs. + * + * For transmitting, we'd want to use lem_txeof, but + * we have to figure out how to put data into the + * adapter queue. Something like: + * + * struct mbchain *mbp; + * struct mbuf *m; + * mb_init(mbp); + * mb_put_uint8(mbp, 33); + * mb_put_uint16le(mbp, length); + * m =3D m_copym(mbp->mb_top, 0, M_COPYALL, M_WAIT); + * + * Then we have to get the mbuf chain (m) into the + * device's queue. + * + * Then: + * + * mb_done(mbp); + * + */ +/* + * Tell kdp about functions to query status. + * The link parameter is a pointer to a function + * which returns the status (it only cares about + * IFM_AVALID and IFM_ACTIVE). Note that it has no + * parameters -- so it has to use the kdp_get_interface() + * function to find out the current interface. This should + * probably change. + * + * Similarly, the mode parameter is a function which sets the + * status active (if its parameter is non-zero) or inactive (if + * its parameter is 0). + */ +void kdp_register_link(kdp_link_t link, kdp_mode_t mode); +void kdp_unregister_link(kdp_link_t link, kdp_mode_t mode); + +// This is a bit of a lie: it actually takes a pointer to a = kdp_ether_addr_t structure. +void kdp_set_interface(void *interface, const void *macaddr); + +static uint32_t kdp_media_status(void); +static int kdp_set_media_state(int); + +#endif =20 #ifdef DEVICE_POLLING static poll_handler_t lem_poll; @@ -835,7 +905,7 @@ lem_start_locked(struct ifnet *ifp) * available hits the threshold */ if (adapter->num_tx_desc_avail <=3D EM_TX_CLEANUP_THRESHOLD) { - lem_txeof(adapter); + lem_txeof(adapter, 0); /* Now do we at least have a minimal? */ if (adapter->num_tx_desc_avail <=3D EM_TX_OP_THRESHOLD) = { adapter->no_tx_desc_avail1++; @@ -1212,6 +1282,14 @@ lem_init_locked(struct adapter *adapter) /* AMT based hardware can now take control from firmware */ if (adapter->has_manage && adapter->has_amt) lem_get_hw_control(adapter); + +#ifdef DDB + printf("Setting interface to %p\n", ifp); + kdp_set_interface(ifp, adapter->hw.mac.addr); + kdp_register_link(kdp_media_status, NULL = /*kdp_set_media_state*/); + kdp_register_send_receive(lem_kdp_send_pkt, lem_kdp_recv_pkt); + +#endif } =20 static void @@ -1258,7 +1336,7 @@ lem_poll(struct ifnet *ifp, enum poll_cmd cmd, int = count) lem_rxeof(adapter, count, &rx_done); =20 EM_TX_LOCK(adapter); - lem_txeof(adapter); + lem_txeof(adapter, 0); if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) lem_start_locked(ifp); EM_TX_UNLOCK(adapter); @@ -1309,7 +1387,7 @@ lem_intr(void *arg) lem_rxeof(adapter, -1, NULL); =20 EM_TX_LOCK(adapter); - lem_txeof(adapter); + lem_txeof(adapter, 0); if (ifp->if_drv_flags & IFF_DRV_RUNNING && !IFQ_DRV_IS_EMPTY(&ifp->if_snd)) lem_start_locked(ifp); @@ -1348,7 +1426,7 @@ lem_handle_rxtx(void *context, int pending) if (ifp->if_drv_flags & IFF_DRV_RUNNING) { bool more =3D lem_rxeof(adapter, = adapter->rx_process_limit, NULL); EM_TX_LOCK(adapter); - lem_txeof(adapter); + lem_txeof(adapter, 0); if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) lem_start_locked(ifp); EM_TX_UNLOCK(adapter); @@ -1405,6 +1483,34 @@ lem_irq_fast(void *arg) return FILTER_HANDLED; } =20 +#ifdef DDB +static uint32_t __unused +kdp_media_status(void) +{ + struct ifnet *ifp =3D kdp_get_interface(); + struct ifmediareq ifmr =3D { .ifm_status =3D 0 }; + + if (ifp) { + lem_media_status(ifp, &ifmr); + } + return ifmr.ifm_status; +} + +static int __unused +kdp_set_media_state(int onoroff) +{ + struct ifnet *ifp =3D kdp_get_interface(); + if (ifp) { + if (onoroff) { + lem_start(ifp); + } else { + lem_stop(ifp); + } + } + return 1; +} + +#endif =20 /********************************************************************* * @@ -2975,14 +3081,15 @@ lem_transmit_checksum_setup(struct adapter = *adapter, struct mbuf *mp, * = **********************************************************************/ static void -lem_txeof(struct adapter *adapter) +lem_txeof(struct adapter *adapter, int nolock) { int first, last, done, num_avail; struct em_buffer *tx_buffer; struct e1000_tx_desc *tx_desc, *eop_desc; struct ifnet *ifp =3D adapter->ifp; =20 - EM_TX_LOCK_ASSERT(adapter); + if (nolock =3D=3D 0) + EM_TX_LOCK_ASSERT(adapter); =20 #ifdef DEV_NETMAP if (netmap_tx_irq(ifp, 0 | = (NETMAP_LOCKED_ENTER|NETMAP_LOCKED_EXIT))) @@ -3082,7 +3189,7 @@ lem_tx_purge(struct adapter *adapter) { if ((!adapter->link_active) && (adapter->watchdog_check)) { EM_TX_LOCK(adapter); - lem_txeof(adapter); + lem_txeof(adapter, 0); EM_TX_UNLOCK(adapter); if (adapter->watchdog_check) /* Still outstanding? */ lem_init_locked(adapter); @@ -3426,6 +3533,254 @@ lem_free_receive_structures(struct adapter = *adapter) } } =20 +static __unused struct mbuf * +lem_recv_one_packet(struct adapter *adapter, + int *start_desc_index) +{ + struct mbuf *retval =3D NULL; + struct mbuf *mp; // Used to build up retval + int i =3D *start_desc_index; + struct ifnet *ifp =3D adapter->ifp; + u8 status =3D 0, accept_frame =3D 0, eop =3D 0; + u16 len, desc_len, prev_len_adj; + struct e1000_rx_desc *current_desc =3D NULL; + +// printf("%s(%d)\n", __FUNCTION__, __LINE__); + + + while (eop =3D=3D 0) { + current_desc =3D &adapter->rx_desc_base[i]; + status =3D current_desc->status; + + if ((status & E1000_RXD_STAT_DD) =3D=3D 0) { + break; + } + + mp =3D adapter->rx_buffer_area[i].m_head; + /* + * Can't defer bus_dmamap_sync(9) because TBI_ACCEPT + * needs to access the last received byte in the mbuf. + */ + bus_dmamap_sync(adapter->rxtag, = adapter->rx_buffer_area[i].map, + BUS_DMASYNC_POSTREAD); + accept_frame =3D 1; + prev_len_adj =3D 0; + desc_len =3D le16toh(current_desc->length); + + if (status & E1000_RXD_STAT_EOP) { + eop =3D 1; // Found the end of packet + if (desc_len < ETHER_CRC_LEN) { + len =3D 0; + prev_len_adj =3D ETHER_CRC_LEN - = desc_len; + } else { + len =3D desc_len - ETHER_CRC_LEN; + } + } else { + len =3D desc_len; + } + + if (current_desc->errors & E1000_RXD_ERR_FRAME_ERR_MASK) = { + u8 last_byte; + u32 pkt_len =3D desc_len; + =09 + if (adapter->fmp !=3D NULL) + pkt_len +=3D adapter->fmp->m_pkthdr.len; + =09 + last_byte =3D *(mtod(mp, caddr_t) + desc_len - = 1); =09 + if (TBI_ACCEPT(&adapter->hw, status, + current_desc->errors, pkt_len, = last_byte, + adapter->min_frame_size, = adapter->max_frame_size)) { + = e1000_tbi_adjust_stats_82543(&adapter->hw, + = &adapter->stats, pkt_len, + = adapter->hw.mac.addr, + = adapter->max_frame_size); + if (len > 0) + len--; + } else + accept_frame =3D 0; + } + if (accept_frame) { + if (lem_get_buf(adapter, i) !=3D 0) { + ifp->if_iqdrops++; + goto discard; + } + =09 + /* Assign correct length to the current fragment = */ + mp->m_len =3D len; + =09 + if (adapter->fmp =3D=3D NULL) { + mp->m_pkthdr.len =3D len; + adapter->fmp =3D mp; /* Store the first = mbuf */ + adapter->lmp =3D mp; + } else { + /* Chain mbuf's together */ + mp->m_flags &=3D ~M_PKTHDR; + /* + * Adjust length of previous mbuf in = chain if + * we received less than 4 bytes in the = last + * descriptor. + */ + if (prev_len_adj > 0) { + adapter->lmp->m_len -=3D = prev_len_adj; + adapter->fmp->m_pkthdr.len -=3D + prev_len_adj; + } + adapter->lmp->m_next =3D mp; + adapter->lmp =3D adapter->lmp->m_next; + adapter->fmp->m_pkthdr.len +=3D len; + } + =09 + if (eop) { + adapter->fmp->m_pkthdr.rcvif =3D ifp; + ifp->if_ipackets++; + lem_receive_checksum(adapter, = current_desc, + adapter->fmp); +#ifndef __NO_STRICT_ALIGNMENT + if (adapter->max_frame_size > + (MCLBYTES - ETHER_ALIGN) && + lem_fixup_rx(adapter) !=3D 0) + goto skip; +#endif + if (status & E1000_RXD_STAT_VP) { + = adapter->fmp->m_pkthdr.ether_vtag =3D + = le16toh(current_desc->special); + adapter->fmp->m_flags |=3D = M_VLANTAG; + } +#ifndef __NO_STRICT_ALIGNMENT + skip: +#endif + retval =3D adapter->fmp; + adapter->fmp =3D NULL; + adapter->lmp =3D NULL; + } + } else { + adapter->dropped_pkts++; + discard: + /* Reuse loaded DMA map and just update mbuf = chain */ + mp =3D adapter->rx_buffer_area[i].m_head; + mp->m_len =3D mp->m_pkthdr.len =3D MCLBYTES; + mp->m_data =3D mp->m_ext.ext_buf; + mp->m_next =3D NULL; + if (adapter->max_frame_size <=3D + (MCLBYTES - ETHER_ALIGN)) + m_adj(mp, ETHER_ALIGN); + if (adapter->fmp !=3D NULL) { + m_freem(adapter->fmp); + adapter->fmp =3D NULL; + adapter->lmp =3D NULL; + } + retval =3D NULL; + } + /* Zero out the receive descriptors status. */ + current_desc->status =3D 0; + bus_dmamap_sync(adapter->rxdma.dma_tag, = adapter->rxdma.dma_map, + BUS_DMASYNC_PREREAD | = BUS_DMASYNC_PREWRITE); +=09 + /* Advance our pointers to the next descriptor. */ + if (++i =3D=3D adapter->num_rx_desc) + i =3D 0; + } + + *start_desc_index =3D i; + /* + * At this point, either retval has a packet, or is NULL + */ +// printf("%s(%d): retval =3D %p\n", __FUNCTION__, __LINE__, = retval); + return retval; +} + +static void +lem_kdp_send_pkt(__unused void *pkt, __unused unsigned int pkt_len) +{ + printf("%s\n", __FUNCTION__); + struct mbuf *m_head =3D NULL; + struct ifnet *ifp =3D kdp_get_interface(); + struct adapter *adapter =3D ifp->if_softc; + struct iovec iov; + struct uio uio; + int error; + + printf("%s(%p, %u)\n", __FUNCTION__, pkt, pkt_len); + + iov.iov_base =3D (caddr_t)pkt; + iov.iov_len =3D pkt_len; + uio.uio_iov =3D &iov; + uio.uio_iovcnt =3D 1; + uio.uio_offset =3D 0; + uio.uio_resid =3D (ssize_t)pkt_len; + uio.uio_segflg =3D UIO_SYSSPACE; + uio.uio_rw =3D UIO_WRITE; + uio.uio_td =3D curthread; + + + m_head =3D m_uiotombuf(&uio, M_WAITOK, pkt_len, 0, 0); +=09 + if (m_head =3D=3D NULL) { + printf("%s(%d): m_utiotombuf failed us!\n", = __FUNCTION__, __LINE__); + } else { + lem_txeof(adapter, 1); + printf("%s(%d): calling lem_xmit\n", __FUNCTION__, = __LINE__); + error =3D lem_xmit(adapter, &m_head); + =09 + if (error !=3D 0) { + printf("%s(%d): lem_xmit returned %d\n", = __FUNCTION__, __LINE__, error); + } else { + printf("%s(%d): calling lem_txeof\n", = __FUNCTION__, __LINE__); + lem_txeof(adapter, 1); + printf("\tand done\n"); + } + if (m_head) + m_freem(m_head); + } + return; +} + +static void __unused +lem_kdp_recv_pkt(void *data, unsigned int *length, unsigned int = timeout) +{ + *length =3D 0; + int indx =3D 0; + int uSecs =3D timeout * 1000; // DELAY takes microseconds + struct mbuf *packet =3D NULL; + struct ifnet *ifp =3D kdp_get_interface(); + struct adapter *adapter =3D ifp->if_softc; + struct e1000_rx_desc *current_desc; + + indx =3D adapter->next_rx_desc_to_check; + current_desc =3D &adapter->rx_desc_base[indx]; + + bus_dmamap_sync(adapter->rxdma.dma_tag, + adapter->rxdma.dma_map, + BUS_DMASYNC_POSTREAD); + + if ((current_desc->status & E1000_RXD_STAT_DD) =3D=3D 0) { + goto done; + } + + while (packet =3D=3D NULL && + (ifp->if_drv_flags & IFF_DRV_RUNNING) && + uSecs > 0) { + packet =3D lem_recv_one_packet(adapter, &indx); + + if (packet =3D=3D NULL) { + DELAY(50); + uSecs -=3D 50; + } + } + if (packet !=3D NULL) { + int packet_len =3D packet->m_pkthdr.len; + m_copydata(packet, 0, packet_len, data); + *length =3D packet_len; + adapter->next_rx_desc_to_check =3D indx; + if (indx =3D=3D 0) { + E1000_WRITE_REG(&adapter->hw, E1000_RDT(0), = adapter->num_rx_desc - 1); + } + + } +done: + return; +} + /********************************************************************* * * This routine executes in interrupt context. It replenishes @@ -3441,9 +3796,11 @@ static bool lem_rxeof(struct adapter *adapter, int count, int *done) { struct ifnet *ifp =3D adapter->ifp; - struct mbuf *mp; - u8 status =3D 0, accept_frame =3D 0, eop =3D 0; - u16 len, desc_len, prev_len_adj; + u8 status =3D 0; + u8 __unused accept_frame =3D 0; + u8 __unused eop =3D 0; + u16 __unused len; + u16 __unused desc_len, prev_len_adj; int i, rx_sent =3D 0; struct e1000_rx_desc *current_desc; =20 @@ -3467,6 +3824,14 @@ lem_rxeof(struct adapter *adapter, int count, int = *done) =20 while (count !=3D 0 && ifp->if_drv_flags & IFF_DRV_RUNNING) { struct mbuf *m =3D NULL; +#if 1 + current_desc =3D &adapter->rx_desc_base[i]; + status =3D current_desc->status; + if ((status & E1000_RXD_STAT_DD) =3D=3D 0) + break; + m =3D lem_recv_one_packet(adapter, &i); +#else + struct mbuf *mp =3D NULL; =20 status =3D current_desc->status; if ((status & E1000_RXD_STAT_DD) =3D=3D 0) @@ -3598,6 +3963,8 @@ discard: /* Advance our pointers to the next descriptor. */ if (++i =3D=3D adapter->num_rx_desc) i =3D 0; +#endif + /* Call into the stack */ if (m !=3D NULL) { adapter->next_rx_desc_to_check =3D i; --Apple-Mail=_CF84D4FE-C7D8-487C-93B9-541771AA3154 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset=us-ascii --Apple-Mail=_CF84D4FE-C7D8-487C-93B9-541771AA3154--
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?27D25BFC-7BB3-400F-8405-43B8D08135D2>