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>
