emx: Support flow control change and default to RX pause
authorSepherosa Ziehau <sephe@dragonflybsd.org>
Fri, 20 Nov 2015 02:57:09 +0000 (10:57 +0800)
committerSepherosa Ziehau <sephe@dragonflybsd.org>
Fri, 20 Nov 2015 03:03:45 +0000 (11:03 +0800)
Tested-by: dillon@
sys/dev/netif/emx/if_emx.c
sys/dev/netif/emx/if_emx.h

index 708eda6..8c9c6c6 100644 (file)
@@ -245,6 +245,8 @@ static void emx_update_link_status(struct emx_softc *);
 static void    emx_smartspeed(struct emx_softc *);
 static void    emx_set_itr(struct emx_softc *, uint32_t);
 static void    emx_disable_aspm(struct emx_softc *);
+static enum e1000_fc_mode emx_str2fc(const char *);
+static void    emx_fc2str(enum e1000_fc_mode, char *, int);
 
 static void    emx_print_debug_info(struct emx_softc *);
 static void    emx_print_nvm_info(struct emx_softc *);
@@ -259,6 +261,7 @@ static int  emx_sysctl_tx_wreg_nsegs(SYSCTL_HANDLER_ARGS);
 static int     emx_sysctl_npoll_rxoff(SYSCTL_HANDLER_ARGS);
 static int     emx_sysctl_npoll_txoff(SYSCTL_HANDLER_ARGS);
 #endif
+static int     emx_sysctl_flowctrl(SYSCTL_HANDLER_ARGS);
 static void    emx_add_sysctl(struct emx_softc *);
 
 static void    emx_serialize_skipmain(struct emx_softc *);
@@ -310,6 +313,8 @@ static int  emx_debug_sbp = 0;
 static int     emx_82573_workaround = 1;
 static int     emx_msi_enable = 1;
 
+static char    emx_flowctrl[16] = "rx_pause";
+
 TUNABLE_INT("hw.emx.int_throttle_ceil", &emx_int_throttle_ceil);
 TUNABLE_INT("hw.emx.rxd", &emx_rxd);
 TUNABLE_INT("hw.emx.rxr", &emx_rxr);
@@ -319,6 +324,7 @@ TUNABLE_INT("hw.emx.smart_pwr_down", &emx_smart_pwr_down);
 TUNABLE_INT("hw.emx.sbp", &emx_debug_sbp);
 TUNABLE_INT("hw.emx.82573_workaround", &emx_82573_workaround);
 TUNABLE_INT("hw.emx.msi.enable", &emx_msi_enable);
+TUNABLE_STR("hw.emx.flow_ctrl", emx_flowctrl, sizeof(emx_flowctrl));
 
 /* Global used in WOL setup with multiport cards */
 static int     emx_global_quad_port_a = 0;
@@ -825,6 +831,9 @@ emx_attach(device_t dev)
 #endif
        sc->tx_ring_inuse = emx_get_txring_inuse(sc, FALSE);
 
+       /* Setup flow control.  TODO: per-device tunable */
+       sc->flow_ctrl = emx_str2fc(emx_flowctrl);
+
        /* Setup OS specific network interface */
        emx_setup_ifp(sc);
 
@@ -1970,7 +1979,7 @@ emx_reset(struct emx_softc *sc)
 
        sc->hw.fc.pause_time = EMX_FC_PAUSE_TIME;
        sc->hw.fc.send_xon = TRUE;
-       sc->hw.fc.requested_mode = e1000_fc_full;
+       sc->hw.fc.requested_mode = sc->flow_ctrl;
 
        /*
         * Device specific overrides/settings
@@ -3696,6 +3705,11 @@ emx_add_sysctl(struct emx_softc *sc)
            OID_AUTO, "tx_ring_inuse", CTLFLAG_RD, &sc->tx_ring_inuse, 0,
            "# of TX rings used");
 
+        SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree),
+           OID_AUTO, "flow_ctrl", CTLTYPE_STRING|CTLFLAG_RW, sc, 0,
+           emx_sysctl_flowctrl, "A",
+           "flow control: full, rx_pause, tx_pause, none");
+
 #ifdef IFPOLL_ENABLE
        SYSCTL_ADD_PROC(ctx, SYSCTL_CHILDREN(tree),
                        OID_AUTO, "npoll_rxoff", CTLTYPE_INT|CTLFLAG_RW,
@@ -4324,3 +4338,70 @@ emx_get_txring_inuse(const struct emx_softc *sc, boolean_t polling)
        else
                return 1;
 }
+
+static enum e1000_fc_mode
+emx_str2fc(const char *str)
+{
+       if (strcmp(str, "none") == 0)
+               return e1000_fc_none;
+       else if (strcmp(str, "rx_pause") == 0)
+               return e1000_fc_rx_pause;
+       else if (strcmp(str, "tx_pause") == 0)
+               return e1000_fc_tx_pause;
+       else
+               return e1000_fc_full;
+}
+
+static void
+emx_fc2str(enum e1000_fc_mode fc, char *str, int len)
+{
+       const char *fc_str = "full";
+
+       switch (fc) {
+       case e1000_fc_none:
+               fc_str = "none";
+               break;
+
+       case e1000_fc_rx_pause:
+               fc_str = "rx_pause";
+               break;
+
+       case e1000_fc_tx_pause:
+               fc_str = "tx_pause";
+               break;
+
+       default:
+               break;
+       }
+       strlcpy(str, fc_str, len);
+}
+
+static int
+emx_sysctl_flowctrl(SYSCTL_HANDLER_ARGS)
+{
+       struct emx_softc *sc = arg1;
+       struct ifnet *ifp = &sc->arpcom.ac_if;
+       char flowctrl[16];
+       enum e1000_fc_mode fc;
+       int error;
+
+       emx_fc2str(sc->flow_ctrl, flowctrl, sizeof(flowctrl));
+       error = sysctl_handle_string(oidp, flowctrl, sizeof(flowctrl), req);
+       if (error != 0 || req->newptr == NULL)
+               return error;
+
+       fc = emx_str2fc(flowctrl);
+
+       ifnet_serialize_all(ifp);
+       if (fc == sc->flow_ctrl)
+               goto done;
+
+       sc->flow_ctrl = fc;
+       sc->hw.fc.requested_mode = sc->flow_ctrl;
+       sc->hw.fc.current_mode = sc->flow_ctrl;
+       e1000_force_mac_fc(&sc->hw);
+done:
+       ifnet_deserialize_all(ifp);
+
+       return 0;
+}
index 05c5e71..da8c11a 100644 (file)
@@ -392,6 +392,8 @@ struct emx_softc {
        int                     rx_ring_cnt;
        struct emx_rxdata       rx_data[EMX_NRX_RING];
 
+       enum e1000_fc_mode      flow_ctrl;
+
        /* Misc stats maintained by the driver */
        unsigned long           rx_overruns;