From: Aurélien <footplus@gmail.com>
To: Ben Hutchings <bhutchings@solarflare.com>
Cc: netdev@vger.kernel.org, jeffrey.t.kirsher@intel.com
Subject: Re: Optics (SFP) monitoring on ixgbe and igbe
Date: Fri, 16 Nov 2012 03:23:40 +0100 [thread overview]
Message-ID: <CAPN4dA_ymo-Bx+GM+JLLKGHghq+qBYxR0zO7K6H6Nn0pqZsJRg@mail.gmail.com> (raw)
In-Reply-To: <1353022205.4867.76.camel@deadeye.wl.decadent.org.uk>
[-- Attachment #1: Type: text/plain, Size: 3723 bytes --]
On Fri, Nov 16, 2012 at 12:30 AM, Ben Hutchings
<bhutchings@solarflare.com> wrote:
>
> Yes, Jeff's the one you should be talking to about these drivers. I
> just look after the ethtool utility and API.
>
Ok, so I will discuss the ixgbe patch with Jeff :)
Ben, on the ethtool side, attached is a patch to enable the following
option and output; It's still missing externally calibrated optics
support (my current one is internally calibrated, so that's difficult
to test anything). What do you think ? Is there any other data that
could be interesting to show with -O or -m options ?
I based the text output on Juniper's, which is quite parsable by other
apps, but if someone sees a more appropriate output mode, let me know.
# ./ethtool -h
ethtool -O|--module-optics DEVNAME Show module optical diagnostics
# ./ethtool -O eth6
Physical interface: eth6
Laser bias current : 27.378 mA
Laser output power : 0.6980 mW / -1.56 dBm
Receiver signal average optical power : 1.0817 mW / 0.34 dBm
Module temperature : 53 degrees C / 128 degrees F
Module voltage : 3.2657 V
Laser bias current high alarm : Off
Laser bias current low alarm : Off
Laser bias current high warning : Off
Laser bias current low warning : Off
Laser output power high alarm : Off
Laser output power low alarm : Off
Laser output power high warning : Off
Laser output power low warning : Off
Module temperature high alarm : Off
Module temperature low alarm : Off
Module temperature high warning : Off
Module temperature low warning : Off
Module voltage high alarm : Off
Module voltage low alarm : Off
Module voltage high warning : Off
Module voltage low warning : Off
Laser rx power high alarm : Off
Laser rx power low alarm : Off
Laser rx power high warning : Off
Laser rx power low warning : Off
Laser bias current high alarm threshold : 100.000 mA
Laser bias current low alarm threshold : 2.000 mA
Laser bias current high warning threshold : 80.000 mA
Laser bias current low warning threshold : 4.000 mA
Laser output power high alarm threshold : 2.2440 mW / 3.51 dBm
Laser output power low alarm threshold : 0.0792 mW / -11.01 dBm
Laser output power high warning threshold : 1.6830 mW / 2.26 dBm
Laser output power low warning threshold : 0.1188 mW / -9.25 dBm
Module temperature high alarm threshold : 125 degrees C / 257 degrees F
Module temperature low alarm threshold : -45 degrees C / -49 degrees F
Module temperature high warning threshold : 115 degrees C / 239 degrees F
Module temperature low warning threshold : -40 degrees C / -40 degrees F
Module voltage high alarm threshold : 3.9000 V
Module voltage low alarm threshold : 2.7000 V
Module voltage high warning threshold : 3.7000 V
Module voltage low warning threshold : 2.9000 V
Laser rx power high alarm threshold : 2.0000 mW / 3.01 dBm
Laser rx power low alarm threshold : 0.0158 mW / -18.01 dBm
Laser rx power high warning threshold : 1.5000 mW / 1.76 dBm
Laser rx power low warning threshold : 0.0237 mW / -16.25 dBm
Best regards,
--
Aurélien Guillaume
[-- Attachment #2: 0001-Implemented-basic-optics-diagnostics-for-SFF-8472-co.patch --]
[-- Type: application/octet-stream, Size: 13855 bytes --]
From f24878834fce1d5464f3175bfe8cad96d9308e16 Mon Sep 17 00:00:00 2001
From: Aurelien Guillaume <aurelien@iwi.me>
Date: Fri, 16 Nov 2012 02:50:00 +0100
Subject: [PATCH] Implemented basic optics diagnostics for SFF-8472 compliant
transceivers in ethtool.
---
Makefile.am | 2 +-
configure.ac | 2 +
ethtool.c | 45 ++++++++++-
internal.h | 3 +
sfpdiag.c | 267 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 317 insertions(+), 2 deletions(-)
create mode 100644 sfpdiag.c
diff --git a/Makefile.am b/Makefile.am
index e33f71f..89a0d1e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -9,7 +9,7 @@ ethtool_SOURCES = ethtool.c ethtool-copy.h internal.h net_tstamp-copy.h \
fec_8xx.c ibm_emac.c ixgb.c ixgbe.c natsemi.c \
pcnet32.c realtek.c tg3.c marvell.c vioc.c \
smsc911x.c at76c50x-usb.c sfc.c stmmac.c \
- rxclass.c sfpid.c
+ rxclass.c sfpid.c sfpdiag.c
TESTS = test-cmdline test-features
check_PROGRAMS = test-cmdline test-features
diff --git a/configure.ac b/configure.ac
index 0c597c6..b87d5d8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -13,9 +13,11 @@ AC_PROG_GCC_TRADITIONAL
AM_PROG_CC_C_O
dnl Checks for libraries.
+AC_CHECK_LIB([m], [log10])
dnl Checks for header files.
AC_CHECK_HEADERS(sys/ioctl.h)
+AC_CHECK_HEADERS(math.h)
dnl Checks for typedefs, structures, and compiler characteristics.
AC_MSG_CHECKING([whether <linux/types.h> defines big-endian types])
diff --git a/ethtool.c b/ethtool.c
index 3db7fec..e18fc85 100644
--- a/ethtool.c
+++ b/ethtool.c
@@ -3549,6 +3549,47 @@ static int do_tsinfo(struct cmd_context *ctx)
return 0;
}
+static int do_getmoduleoptics(struct cmd_context *ctx)
+{
+ struct ethtool_modinfo modinfo;
+ struct ethtool_eeprom *eeprom;
+ int err;
+
+ modinfo.cmd = ETHTOOL_GMODULEINFO;
+ err = send_ioctl(ctx, &modinfo);
+ if (err < 0) {
+ perror("Cannot get module information");
+ return 1;
+ }
+
+ if (modinfo.type != ETH_MODULE_SFF_8472)
+ {
+ perror("Module is not SFF-8472 (DOM) compliant");
+ return 1;
+ }
+
+ eeprom = calloc(1, sizeof(*eeprom) + modinfo.eeprom_len);
+ if (!eeprom) {
+ perror("Cannot allocate memory for module EEPROM data");
+ return 1;
+ }
+
+ eeprom->cmd = ETHTOOL_GMODULEEEPROM;
+ eeprom->len = modinfo.eeprom_len;
+ eeprom->offset = 0;
+ err = send_ioctl(ctx, eeprom);
+ if (err < 0) {
+ perror("Cannot access module EEPROM");
+ free(eeprom);
+ return 1;
+ }
+
+ printf("Physical interface: %s\n", ctx->devname);
+ sff8472_show_all(eeprom->data);
+ free(eeprom);
+ return 0;
+}
+
static int do_getmodule(struct cmd_context *ctx)
{
struct ethtool_modinfo modinfo;
@@ -3832,11 +3873,13 @@ static const struct option {
{ "--set-priv-flags", 1, do_sprivflags, "Set private flags",
" FLAG on|off ...\n" },
{ "-m|--dump-module-eeprom", 1, do_getmodule,
- "Qeuery/Decode Module EEPROM information",
+ "Query/Decode Module EEPROM information",
" [ raw on|off ]\n"
" [ hex on|off ]\n"
" [ offset N ]\n"
" [ length N ]\n" },
+ { "-O|--module-optics", 1, do_getmoduleoptics,
+ "Show module optical diagnostics" },
{ "--show-eee", 1, do_geee, "Show EEE settings"},
{ "--set-eee", 1, do_seee, "Set EEE settings",
" [ eee on|off ]\n"
diff --git a/internal.h b/internal.h
index 4f96fd5..e977a81 100644
--- a/internal.h
+++ b/internal.h
@@ -253,4 +253,7 @@ int rxclass_rule_del(struct cmd_context *ctx, __u32 loc);
/* Module EEPROM parsing code */
void sff8079_show_all(const __u8 *id);
+/* Optics diagnostics */
+void sff8472_show_all(const __u8 *id);
+
#endif /* ETHTOOL_INTERNAL_H__ */
diff --git a/sfpdiag.c b/sfpdiag.c
new file mode 100644
index 0000000..aa7c14c
--- /dev/null
+++ b/sfpdiag.c
@@ -0,0 +1,267 @@
+/*
+ * sfpdiag.c: Implements SFF-8472 optics diagnostics.
+ *
+ * Implemented by Aurelien Guillaume <aurelien@iwi.me>
+ * based on previous works by Robert Olsson <robert@herjulf.se>
+ * and SFF-8472 specs by SFF.
+ */
+
+#include <stdio.h>
+#include "internal.h"
+#ifdef HAVE_MATH_H
+# include <math.h>
+#endif
+
+
+/* EEPROM offsets for DOM */
+#define SFF_A0_DOM 92
+#define SFF_A0_OPTIONS 93
+#define SFF_A0_COMP 94
+
+#define SFF_A0_COMP 94
+
+/* EEPROM bit values */
+#define SFF_A0_DOM_EXTCAL (1 << 4)
+#define SFF_A0_DOM_INTCAL (1 << 5)
+#define SFF_A0_DOM_IMPL (1 << 6)
+#define SFF_A0_DOM_PWRT (1 << 3)
+
+#define SFF_A0_OPTIONS_AW (1 << 7)
+
+#define SFF_A2_TEMP 0x100 + 96
+#define SFF_A2_TEMP_HALRM 0x100 + 0
+#define SFF_A2_TEMP_LALRM 0x100 + 2
+#define SFF_A2_TEMP_HWARN 0x100 + 4
+#define SFF_A2_TEMP_LWARN 0x100 + 6
+
+#define SFF_A2_VCC 0x100 + 98
+#define SFF_A2_VCC_HALRM 0x100 + 8
+#define SFF_A2_VCC_LALRM 0x100 + 10
+#define SFF_A2_VCC_HWARN 0x100 + 12
+#define SFF_A2_VCC_LWARN 0x100 + 14
+
+#define SFF_A2_BIAS 0x100 + 96
+#define SFF_A2_BIAS_HALRM 0x100 + 16
+#define SFF_A2_BIAS_LALRM 0x100 + 18
+#define SFF_A2_BIAS_HWARN 0x100 + 20
+#define SFF_A2_BIAS_LWARN 0x100 + 22
+
+#define SFF_A2_TX_PWR 0x100 + 102
+#define SFF_A2_TX_PWR_HALRM 0x100 + 24
+#define SFF_A2_TX_PWR_LALRM 0x100 + 26
+#define SFF_A2_TX_PWR_HWARN 0x100 + 28
+#define SFF_A2_TX_PWR_LWARN 0x100 + 30
+
+#define SFF_A2_RX_PWR 0x100 + 104
+#define SFF_A2_RX_PWR_HALRM 0x100 + 32
+#define SFF_A2_RX_PWR_LALRM 0x100 + 34
+#define SFF_A2_RX_PWR_HWARN 0x100 + 36
+#define SFF_A2_RX_PWR_LWARN 0x100 + 38
+
+#define SFF_A2_ALRM_FLG 0x100 + 112
+#define SFF_A2_WARN_FLG 0x100 + 116
+
+struct sff8472_diags {
+
+#define MCURR 0
+#define LWARN 1
+#define HWARN 2
+#define LALRM 3
+#define HALRM 4
+
+ /* [5] tables are current, low/high warn, low/high alarm */
+ __u8 supports_dom; /* Supports DOM */
+ __u8 supports_alarms; /* Supports alarm/warning thold */
+ __u8 calibrated_int; /* Is internally calibrated */
+ __u16 bias_cur[5]; /* Measured bias current in 2uA units (cur, l/h warn, l/h alarm) */
+ __u16 tx_power[5]; /* Measured TX Power in 0.1uW units (cur, warn, alarm) */
+ __u16 rx_power[5]; /* Measured RX Power (cur, warn, alarm) */
+ __u8 rx_power_type; /* 0 = OMA, 1 = Average power */
+ __s16 sfp_temp[5]; /* SFP Temp in 0.1 Celcius (cur, warn, alarm) */
+ __u16 sfp_voltage[5]; /* SFP voltage in 0.1mV units (cur, warn, alarm) */
+
+};
+
+static struct sff8472_aw_flags {
+ const char *str; /* Human-readable string, null at the end */
+ int offset; /* A2-relative adress offset */
+ __u8 value; /* 1-bit mask, alarm is on if offset & value != 0. */
+} sff8472_aw_flags[] =
+{
+ { "Laser bias current high alarm", SFF_A2_ALRM_FLG, (1 << 3) },
+ { "Laser bias current low alarm", SFF_A2_ALRM_FLG, (1 << 2) },
+ { "Laser bias current high warning", SFF_A2_WARN_FLG, (1 << 3) },
+ { "Laser bias current low warning", SFF_A2_WARN_FLG, (1 << 2) },
+
+ { "Laser output power high alarm", SFF_A2_ALRM_FLG, (1 << 1) },
+ { "Laser output power low alarm", SFF_A2_ALRM_FLG, (1 << 0) },
+ { "Laser output power high warning", SFF_A2_WARN_FLG, (1 << 1) },
+ { "Laser output power low warning", SFF_A2_WARN_FLG, (1 << 0) },
+
+ { "Module temperature high alarm", SFF_A2_ALRM_FLG, (1 << 7) },
+ { "Module temperature low alarm", SFF_A2_ALRM_FLG, (1 << 6) },
+ { "Module temperature high warning", SFF_A2_WARN_FLG, (1 << 7) },
+ { "Module temperature low warning", SFF_A2_WARN_FLG, (1 << 6) },
+
+ { "Module voltage high alarm", SFF_A2_ALRM_FLG, (1 << 5) },
+ { "Module voltage low alarm", SFF_A2_ALRM_FLG, (1 << 4) },
+ { "Module voltage high warning", SFF_A2_WARN_FLG, (1 << 5) },
+ { "Module voltage low warning", SFF_A2_WARN_FLG, (1 << 4) },
+
+ { "Laser rx power high alarm", SFF_A2_ALRM_FLG + 1, (1 << 7) },
+ { "Laser rx power low alarm", SFF_A2_ALRM_FLG + 1, (1 << 6) },
+ { "Laser rx power high warning", SFF_A2_WARN_FLG + 1, (1 << 7) },
+ { "Laser rx power low warning", SFF_A2_WARN_FLG + 1, (1 << 6) },
+
+ { NULL, 0, 0 },
+};
+
+#ifdef HAVE_LIBM
+
+static double convert_mw_to_dbm(double mw)
+{
+ return (10.f * log10(mw / 1000.f)) + 30.f;
+}
+
+#endif
+
+/* Externally calibrated SFP calculations */
+#define ECAL(v, s, o) (( ((double) (s>>8)) + (s & 0xFF)) * (double) v + o)
+
+static void sff8472_parse_eeprom(const __u8 *id, struct sff8472_diags *sd)
+{
+ sd->supports_dom = id[SFF_A0_DOM] & SFF_A0_DOM_IMPL;
+ sd->supports_alarms = id[SFF_A0_OPTIONS] & SFF_A0_OPTIONS_AW;
+ sd->calibrated_int = id[SFF_A0_DOM] & SFF_A0_DOM_INTCAL;
+ sd->rx_power_type = id[SFF_A0_DOM] & SFF_A0_DOM_PWRT;
+
+
+#define OFFSET_TO_U16(offset) (id[(offset)] << 8 | id[(offset) + 1])
+
+ sd->bias_cur[MCURR] = OFFSET_TO_U16(SFF_A2_BIAS);
+ sd->bias_cur[HALRM] = OFFSET_TO_U16(SFF_A2_BIAS_HALRM);
+ sd->bias_cur[LALRM] = OFFSET_TO_U16(SFF_A2_BIAS_LALRM);
+ sd->bias_cur[HWARN] = OFFSET_TO_U16(SFF_A2_BIAS_HWARN);
+ sd->bias_cur[LWARN] = OFFSET_TO_U16(SFF_A2_BIAS_LWARN);
+
+ sd->sfp_voltage[MCURR] = OFFSET_TO_U16(SFF_A2_VCC);
+ sd->sfp_voltage[HALRM] = OFFSET_TO_U16(SFF_A2_VCC_HALRM);
+ sd->sfp_voltage[LALRM] = OFFSET_TO_U16(SFF_A2_VCC_LALRM);
+ sd->sfp_voltage[HWARN] = OFFSET_TO_U16(SFF_A2_VCC_HWARN);
+ sd->sfp_voltage[LWARN] = OFFSET_TO_U16(SFF_A2_VCC_LWARN);
+
+ sd->tx_power[MCURR] = OFFSET_TO_U16(SFF_A2_TX_PWR);
+ sd->tx_power[HALRM] = OFFSET_TO_U16(SFF_A2_TX_PWR_HALRM);
+ sd->tx_power[LALRM] = OFFSET_TO_U16(SFF_A2_TX_PWR_LALRM);
+ sd->tx_power[HWARN] = OFFSET_TO_U16(SFF_A2_TX_PWR_HWARN);
+ sd->tx_power[LWARN] = OFFSET_TO_U16(SFF_A2_TX_PWR_LWARN);
+
+ sd->rx_power[MCURR] = OFFSET_TO_U16(SFF_A2_RX_PWR);
+ sd->rx_power[HALRM] = OFFSET_TO_U16(SFF_A2_RX_PWR_HALRM);
+ sd->rx_power[LALRM] = OFFSET_TO_U16(SFF_A2_RX_PWR_LALRM);
+ sd->rx_power[HWARN] = OFFSET_TO_U16(SFF_A2_RX_PWR_HWARN);
+ sd->rx_power[LWARN] = OFFSET_TO_U16(SFF_A2_RX_PWR_LWARN);
+
+ /* Temperature conversions */
+#define OFFSET_TO_TEMP(offset) \
+ ((*(__s8 *)(&id[(offset)])) * 1000 + ((id[(offset) + 1] * 1000) / 256)) / 100;
+
+ sd->sfp_temp[MCURR] = OFFSET_TO_TEMP(SFF_A2_TEMP);
+ sd->sfp_temp[HALRM] = OFFSET_TO_TEMP(SFF_A2_TEMP_HALRM);
+ sd->sfp_temp[LALRM] = OFFSET_TO_TEMP(SFF_A2_TEMP_LALRM);
+ sd->sfp_temp[HWARN] = OFFSET_TO_TEMP(SFF_A2_TEMP_HWARN);
+ sd->sfp_temp[LWARN] = OFFSET_TO_TEMP(SFF_A2_TEMP_LWARN);
+
+}
+
+void sff8472_show_all(const __u8 *id)
+{
+ struct sff8472_diags sd;
+ char *rx_power_string = NULL;
+ int i;
+
+ sff8472_parse_eeprom(id, &sd);
+
+ if (!sd.supports_dom)
+ {
+ printf(" Optical diagnostics are not supported.\n");
+ return ;
+ }
+
+#define PRINT_BIAS(string, index) \
+ printf(" %-41s : %.3f mA\n", (string), \
+ (double)(sd.bias_cur[(index)] / 500.f));
+
+#ifdef HAVE_LIBM
+
+# define PRINT_xX_PWR(string, var, index) \
+ printf(" %-41s : %.4f mW / %.2f dBm\n", (string), \
+ (double)((var)[(index)] / 10000.f), \
+ convert_mw_to_dbm((double)((var)[(index)] / 10000.f)));
+
+#else
+
+# define PRINT_xX_PWR(string, var, index) \
+ printf(" %-41s : %.4f mW\n", (string), \
+ (double)((var)[(index)] / 10000.f));
+
+#endif
+
+#define PRINT_TEMP(string, index) \
+ printf(" %-41s : %.0f degrees C / %.0f degrees F\n", (string), \
+ (double)(sd.sfp_temp[(index)] / 10.f), \
+ (double)(sd.sfp_temp[(index)] / 10.f * 1.8f + 32.f));
+
+#define PRINT_VCC(string, index) \
+ printf(" %-41s : %.4f V\n", (string), \
+ (double)(sd.sfp_voltage[(index)] / 10000.f));
+
+
+
+
+ PRINT_BIAS("Laser bias current", MCURR);
+ PRINT_xX_PWR("Laser output power", sd.tx_power, MCURR);
+
+ if (!sd.rx_power_type)
+ rx_power_string = "Receiver signal OMA";
+ else
+ rx_power_string = "Receiver signal average optical power";
+
+ PRINT_xX_PWR(rx_power_string, sd.rx_power, MCURR);
+
+ PRINT_TEMP("Module temperature", MCURR);
+ PRINT_VCC("Module voltage", MCURR);
+
+ for (i = 0; sff8472_aw_flags[i].str; ++i)
+ {
+ printf(" %-41s : %s\n", sff8472_aw_flags[i].str,
+ id[sff8472_aw_flags[i].offset] & sff8472_aw_flags[i].value ? "On" : "Off");
+ }
+
+ PRINT_BIAS("Laser bias current high alarm threshold", HALRM);
+ PRINT_BIAS("Laser bias current low alarm threshold", LALRM);
+ PRINT_BIAS("Laser bias current high warning threshold", HWARN);
+ PRINT_BIAS("Laser bias current low warning threshold", LWARN);
+
+ PRINT_xX_PWR("Laser output power high alarm threshold", sd.tx_power, HALRM);
+ PRINT_xX_PWR("Laser output power low alarm threshold", sd.tx_power, LALRM);
+ PRINT_xX_PWR("Laser output power high warning threshold", sd.tx_power, HWARN);
+ PRINT_xX_PWR("Laser output power low warning threshold", sd.tx_power, LWARN);
+
+ PRINT_TEMP("Module temperature high alarm threshold", HALRM);
+ PRINT_TEMP("Module temperature low alarm threshold", LALRM);
+ PRINT_TEMP("Module temperature high warning threshold", HWARN);
+ PRINT_TEMP("Module temperature low warning threshold", LWARN);
+
+ PRINT_VCC("Module voltage high alarm threshold", HALRM);
+ PRINT_VCC("Module voltage low alarm threshold", LALRM);
+ PRINT_VCC("Module voltage high warning threshold", HWARN);
+ PRINT_VCC("Module voltage low warning threshold", LWARN);
+
+ PRINT_xX_PWR("Laser rx power high alarm threshold", sd.rx_power, HALRM);
+ PRINT_xX_PWR("Laser rx power low alarm threshold", sd.rx_power, LALRM);
+ PRINT_xX_PWR("Laser rx power high warning threshold", sd.rx_power, HWARN);
+ PRINT_xX_PWR("Laser rx power low warning threshold", sd.rx_power, LWARN);
+
+}
+
--
1.7.0.4
next prev parent reply other threads:[~2012-11-16 2:24 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2012-11-07 12:27 Optics (SFP) monitoring on ixgbe and igbe Aurélien
2012-11-07 19:58 ` Ben Hutchings
2012-11-08 0:39 ` Aurélien
2012-11-09 15:08 ` Ben Hutchings
2012-11-09 15:31 ` Aurélien
2012-11-15 21:36 ` Aurélien
2012-11-15 22:46 ` Jeff Kirsher
2012-11-15 23:30 ` Ben Hutchings
2012-11-16 2:23 ` Aurélien [this message]
2012-11-16 6:25 ` Jeff Kirsher
2012-11-16 19:38 ` Ben Hutchings
2012-11-18 21:35 ` Aurélien
2012-11-19 7:27 ` Robert Olsson
2012-12-01 4:18 ` Ben Hutchings
2012-12-02 21:47 ` Aurélien
2012-12-02 22:00 ` Aurélien
2012-12-03 17:43 ` Ben Hutchings
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=CAPN4dA_ymo-Bx+GM+JLLKGHghq+qBYxR0zO7K6H6Nn0pqZsJRg@mail.gmail.com \
--to=footplus@gmail.com \
--cc=bhutchings@solarflare.com \
--cc=jeffrey.t.kirsher@intel.com \
--cc=netdev@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).