From d7c8186d675306c61b2f42376ad0dcc6813312aa Mon Sep 17 00:00:00 2001 From: Emmanuel Vadot Date: Sat, 8 Aug 2020 16:56:20 +0000 Subject: [PATCH 01/25] release: RPI3: Add the RPI2 DTB The RPI2 v1.2 is using the same SoC as the RPI3 so it can boot this image but needs the RPI2 dtb. MFC after: 3 days --- release/arm64/RPI3.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release/arm64/RPI3.conf b/release/arm64/RPI3.conf index 58b983ea86f..f8da393cb9d 100644 --- a/release/arm64/RPI3.conf +++ b/release/arm64/RPI3.conf @@ -4,7 +4,7 @@ # DTB_DIR="/usr/local/share/rpi-firmware" -DTB="bcm2710-rpi-3-b.dtb bcm2710-rpi-3-b-plus.dtb bcm2711-rpi-4-b.dtb" +DTB="bcm2709-rpi-2-b.dtb bcm2710-rpi-3-b.dtb bcm2710-rpi-3-b-plus.dtb bcm2711-rpi-4-b.dtb" EMBEDDED_TARGET_ARCH="aarch64" EMBEDDED_TARGET="arm64" EMBEDDEDBUILD=1 From 1bea15e601ad4fae5c2912758715fcaa08aa2404 Mon Sep 17 00:00:00 2001 From: Michael Tuexen Date: Sat, 8 Aug 2020 19:39:38 +0000 Subject: [PATCH 02/25] Improve the ECN negotiation when the TCP SYN-cache is used by making sure that * ECN is disabled if the client sends an non-ECN-setup SYN segment. * ECN is disabled is the ECN-setup SYN-ACK segment is retransmitted more than net.inet.tcp.ecn.maxretries times. Reviewed by: rscheff MFC after: 1 week Sponsored by: Netflix, Inc. Differential Revision: https://reviews.freebsd.org/D26008 --- sys/netinet/tcp_syncache.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/sys/netinet/tcp_syncache.c b/sys/netinet/tcp_syncache.c index 7a46acf3a87..e564540b007 100644 --- a/sys/netinet/tcp_syncache.c +++ b/sys/netinet/tcp_syncache.c @@ -510,6 +510,9 @@ syncache_timer(void *xsch) sch->sch_nextc = sc->sc_rxttime; continue; } + if (sc->sc_rxmits > V_tcp_ecn_maxretries) { + sc->sc_flags &= ~SCF_ECN; + } if (sc->sc_rxmits > V_tcp_syncache.rexmt_limit) { if ((s = tcp_log_addrs(&sc->sc_inc, NULL, NULL, NULL))) { log(LOG_DEBUG, "%s; %s: Retransmits exhausted, " @@ -1505,6 +1508,13 @@ syncache_add(struct in_conninfo *inc, struct tcpopt *to, struct tcphdr *th, sc->sc_tsreflect = to->to_tsval; else sc->sc_flags &= ~SCF_TIMESTAMP; + /* + * Disable ECN if needed. + */ + if ((sc->sc_flags & SCF_ECN) && + ((th->th_flags & (TH_ECE|TH_CWR)) != (TH_ECE|TH_CWR))) { + sc->sc_flags &= ~SCF_ECN; + } #ifdef MAC /* * Since we have already unconditionally allocated label From e64c6e21812535195acbca81f5e4e8cfefeb28e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20E=C3=9Fer?= Date: Sat, 8 Aug 2020 19:48:15 +0000 Subject: [PATCH 03/25] Mention the new implementation of bc and dc which has become the default version in FreeBSD-CURRENT. --- RELNOTES | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/RELNOTES b/RELNOTES index d34c504ca76..d610258b40a 100644 --- a/RELNOTES +++ b/RELNOTES @@ -27,6 +27,13 @@ r363180: r363084: nc(1) now implements SCTP mode, enabled by specifying the --sctp option. +r362681: + A new implementation of bc and dc has been imported. It offers + better standards compliance, performance, localization and comes + with extensive test cases that are optionally installed. + Use WITHOUT_GH_BC=yes to build and install the world with the + previous version instead of the new one, if required. + r362158, r362163: struct export_args has changed so that the "user" specified for the -maproot and -mapall exports(5) options may be in more than From 5126cbe91be731e14a607ada007de76149da2b7d Mon Sep 17 00:00:00 2001 From: Greg Lehey Date: Sun, 9 Aug 2020 00:34:35 +0000 Subject: [PATCH 04/25] Remove incorrect duplicate. --- usr.bin/calendar/calendars/calendar.history | 1 - 1 file changed, 1 deletion(-) diff --git a/usr.bin/calendar/calendars/calendar.history b/usr.bin/calendar/calendars/calendar.history index 81288c461c0..f578f236750 100644 --- a/usr.bin/calendar/calendars/calendar.history +++ b/usr.bin/calendar/calendars/calendar.history @@ -408,7 +408,6 @@ 08/06 Caricom in Barbados 08/06 Cy Young pitches first game, 1890 08/07 Jack the Ripper makes his first kill, 1888 -08/08 Atomic bomb dropped on Nagasaki, 1945 08/08 Montenegro declares war on Germany, 1914 08/08 Richard Nixon resigns the US presidency, 1974 08/08 The Great Train Robbery -- $7,368,000, 1963 From cbc0517c2b0be647b5064b9905fddd312feb6142 Mon Sep 17 00:00:00 2001 From: Greg Lehey Date: Sun, 9 Aug 2020 00:35:47 +0000 Subject: [PATCH 05/25] Correct date for Nagasaki bombing. --- usr.bin/calendar/calendars/de_DE.ISO8859-1/calendar.geschichte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usr.bin/calendar/calendars/de_DE.ISO8859-1/calendar.geschichte b/usr.bin/calendar/calendars/de_DE.ISO8859-1/calendar.geschichte index e61c674443d..980e162f0dd 100644 --- a/usr.bin/calendar/calendars/de_DE.ISO8859-1/calendar.geschichte +++ b/usr.bin/calendar/calendars/de_DE.ISO8859-1/calendar.geschichte @@ -104,7 +104,7 @@ LANG=de_DE.ISO8859-1 10/01 Verkündigung der Urteile im Nürnberger Hauptkriegsverbrecherprozeß, 1946 02/25 Auflösung der Landes Preußen durch den Kontrollrat, 1947 08/06 Erster Atombombenabwurf auf Hiroshima, 1945 -08/08 Atombombenabwurf auf Nagasaki, 1945 +08/09 Atombombenabwurf auf Nagasaki, 1945 04/19 Aufstand im Warschauer Ghetto, 1943 12/07 Japan bombardiert Pearl Harbor, 1941 From 94cba8034ba53725c225c85e35724f0c2b13cea5 Mon Sep 17 00:00:00 2001 From: Ryan Moeller Date: Sun, 9 Aug 2020 16:27:28 +0000 Subject: [PATCH 06/25] Move ifconfig SFP status functionality into libifconfig libifconfig_sfp.h provides an API in libifconfig for querying SFP module properties, operational status, and vendor strings, as well as descriptions of the various fields, string conversions, and other useful helpers for implementing user interfaces. SFP module status is obtained by reading registers via an I2C interface. Descriptions of these registers and the values therein have been collected in a Lua table which is used to generate all the boilerplace C headers and source files for accessing these values, their names, and descriptions. The generated code is fully commented and readable. This is the first use of libifconfig in ifconfig itself. For now, the scope remains very limited. Over time, more of ifconfig will be replaced with libifconfig. Some minor changes to the formatting of ifconfig output have been made: - Module memory hex dumps are indented one extra space as a result of using hexdump(3) instead of a bespoke hex dump function. - Media descriptions have an added two-character short-name in parenthesis. - QSFP modules were incorrectly displaying TX bias current as power. Now TX channels display bias current, and this change has been made for both SFP and QSFP modules for consistency. A Lua binding for libifconfig including this functionality is implemented but has not been included in this commit. The plan is for it to be committed after dynamic module loading has been enabled in flua. Reviewed by: kp, melifaro Relnotes: yes Differential Revision: https://reviews.freebsd.org/D25494 --- lib/libifconfig/Makefile | 21 +- lib/libifconfig/libifconfig.h | 4 + lib/libifconfig/libifconfig_sfp.c | 592 ++++++++++ lib/libifconfig/libifconfig_sfp.h | 219 ++++ lib/libifconfig/libifconfig_sfp_tables.tpl.c | 124 ++ lib/libifconfig/libifconfig_sfp_tables.tpl.h | 130 +++ .../libifconfig_sfp_tables_internal.tpl.h | 66 ++ lib/libifconfig/sfp.lua | 367 ++++++ rescue/rescue/Makefile | 3 + sbin/ifconfig/Makefile | 3 +- sbin/ifconfig/sfp.c | 1014 ++--------------- tools/lua/template.lua | 652 +++++++++++ 12 files changed, 2262 insertions(+), 933 deletions(-) create mode 100644 lib/libifconfig/libifconfig_sfp.c create mode 100644 lib/libifconfig/libifconfig_sfp.h create mode 100644 lib/libifconfig/libifconfig_sfp_tables.tpl.c create mode 100644 lib/libifconfig/libifconfig_sfp_tables.tpl.h create mode 100644 lib/libifconfig/libifconfig_sfp_tables_internal.tpl.h create mode 100644 lib/libifconfig/sfp.lua create mode 100644 tools/lua/template.lua diff --git a/lib/libifconfig/Makefile b/lib/libifconfig/Makefile index 8d510c537f8..f7d2dbf1c71 100644 --- a/lib/libifconfig/Makefile +++ b/lib/libifconfig/Makefile @@ -13,15 +13,30 @@ SRCS= libifconfig.c \ libifconfig_inet6.c \ libifconfig_internal.c \ libifconfig_lagg.c \ - libifconfig_media.c + libifconfig_media.c \ + libifconfig_sfp.c + +GEN= libifconfig_sfp_tables.h \ + libifconfig_sfp_tables.c \ + libifconfig_sfp_tables_internal.h + +SRCS+= ${GEN} + +.include + +.SUFFIXES: .tpl.c .tpl.h +.tpl.c.c .tpl.h.h: sfp.lua + ${LUA} ${.CURDIR}/sfp.lua ${.IMPSRC} >${.TARGET} + +CLEANFILES+= ${GEN} # If libifconfig become public uncomment those two lines #INCSDIR= ${INCLUDEDIR} -#INCS= libifconfig.h +#INCS= libifconfig.h libifconfig_sfp.h libifconfig_sfp_tables.h #MAN= libifconfig.3 -CFLAGS+= -I${.CURDIR} +CFLAGS+= -I${.CURDIR} -I${.OBJDIR} NO_WCAST_ALIGN= yes .include diff --git a/lib/libifconfig/libifconfig.h b/lib/libifconfig/libifconfig.h index cd2929f315b..ca8e8e817dc 100644 --- a/lib/libifconfig/libifconfig.h +++ b/lib/libifconfig/libifconfig.h @@ -28,6 +28,10 @@ #pragma once +#include + +#include + #include #include diff --git a/lib/libifconfig/libifconfig_sfp.c b/lib/libifconfig/libifconfig_sfp.c new file mode 100644 index 00000000000..54877cebfb9 --- /dev/null +++ b/lib/libifconfig/libifconfig_sfp.c @@ -0,0 +1,592 @@ +/*- + * Copyright (c) 2014, Alexander V. Chernikov + * Copyright (c) 2020, Ryan Moeller + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define SFF_8636_EXT_COMPLIANCE 0x80 + +struct i2c_info { + struct ifreq ifr; + ifconfig_handle_t *h; + int error; /* Store first error */ + enum sfp_id id; /* Module type */ +}; + +static uint8_t +find_zero_bit(const struct sfp_enum_metadata *table, int value, int sz) +{ + int v, m; + + for (v = 1, m = 1 << (8 * sz); v < m; v <<= 1) { + if ((value & v) == 0) + continue; + if (find_metadata(table, value & v) != NULL) { + return (value & v); + } + } + return (0); +} + +/* + * Reads i2c data from opened kernel socket. + */ +static int +read_i2c(struct i2c_info *ii, uint8_t addr, uint8_t off, uint8_t len, + uint8_t *buf) +{ + struct ifi2creq req; + int i, l; + + if (ii->error != 0) + return (ii->error); + + ii->ifr.ifr_data = (caddr_t)&req; + + i = 0; + l = 0; + memset(&req, 0, sizeof(req)); + req.dev_addr = addr; + req.offset = off; + req.len = len; + + while (len > 0) { + l = MIN(sizeof(req.data), len); + req.len = l; + if (ifconfig_ioctlwrap(ii->h, AF_LOCAL, SIOCGI2C, + &ii->ifr) != 0) { + ii->error = errno; + return (errno); + } + + memcpy(&buf[i], req.data, l); + len -= l; + i += l; + req.offset += l; + } + + return (0); +} + +static int +i2c_info_init(struct i2c_info *ii, ifconfig_handle_t *h, const char *name) +{ + uint8_t id_byte; + + memset(ii, 0, sizeof(*ii)); + strlcpy(ii->ifr.ifr_name, name, sizeof(ii->ifr.ifr_name)); + ii->h = h; + + /* + * Try to read byte 0 from i2c: + * Both SFF-8472 and SFF-8436 use it as + * 'identification byte'. + * Stop reading status on zero as value - + * this might happen in case of empty transceiver slot. + */ + id_byte = 0; + read_i2c(ii, SFF_8472_BASE, SFF_8472_ID, 1, &id_byte); + if (ii->error != 0) + return (-1); + if (id_byte == 0) { + h->error.errtype = OTHER; + h->error.errcode = ENOENT; + return (-1); + } + ii->id = id_byte; + return (0); +} + +static int +get_sfp_info(struct i2c_info *ii, struct ifconfig_sfp_info *sfp) +{ + uint8_t code; + + read_i2c(ii, SFF_8472_BASE, SFF_8472_ID, 1, &sfp->sfp_id); + read_i2c(ii, SFF_8472_BASE, SFF_8472_CONNECTOR, 1, &sfp->sfp_conn); + + /* Use extended compliance code if it's valid */ + read_i2c(ii, SFF_8472_BASE, SFF_8472_TRANS, 1, &sfp->sfp_eth_ext); + if (sfp->sfp_eth_ext == 0) { + /* Next, check 10G Ethernet/IB CCs */ + read_i2c(ii, SFF_8472_BASE, SFF_8472_TRANS_START, 1, &code); + sfp->sfp_eth_10g = find_zero_bit(sfp_eth_10g_table, code, 1); + if (sfp->sfp_eth_10g == 0) { + /* No match. Try Ethernet 1G */ + read_i2c(ii, SFF_8472_BASE, SFF_8472_TRANS_START + 3, + 1, &code); + sfp->sfp_eth = find_zero_bit(sfp_eth_table, code, 1); + } + } + + return (ii->error); +} + +static int +get_qsfp_info(struct i2c_info *ii, struct ifconfig_sfp_info *sfp) +{ + uint8_t code; + + read_i2c(ii, SFF_8436_BASE, SFF_8436_ID, 1, &sfp->sfp_id); + read_i2c(ii, SFF_8436_BASE, SFF_8436_CONNECTOR, 1, &sfp->sfp_conn); + + read_i2c(ii, SFF_8436_BASE, SFF_8436_STATUS, 1, &sfp->sfp_rev); + + /* Check for extended specification compliance */ + read_i2c(ii, SFF_8436_BASE, SFF_8436_CODE_E1040100G, 1, &code); + if (code & SFF_8636_EXT_COMPLIANCE) { + read_i2c(ii, SFF_8436_BASE, SFF_8436_OPTIONS_START, 1, + &sfp->sfp_eth_ext); + } else { + /* Check 10/40G Ethernet class only */ + sfp->sfp_eth_1040g = + find_zero_bit(sfp_eth_1040g_table, code, 1); + } + + return (ii->error); +} + +int +ifconfig_sfp_get_sfp_info(ifconfig_handle_t *h, + const char *name, struct ifconfig_sfp_info *sfp) +{ + struct i2c_info ii; + char buf[8]; + + memset(sfp, 0, sizeof(*sfp)); + + if (i2c_info_init(&ii, h, name) != 0) + return (-1); + + /* Read bytes 3-10 at once */ + read_i2c(&ii, SFF_8472_BASE, SFF_8472_TRANS_START, 8, buf); + if (ii.error != 0) + return (ii.error); + + /* Check 10G ethernet first */ + sfp->sfp_eth_10g = find_zero_bit(sfp_eth_10g_table, buf[0], 1); + if (sfp->sfp_eth_10g == 0) { + /* No match. Try 1G */ + sfp->sfp_eth = find_zero_bit(sfp_eth_table, buf[3], 1); + } + sfp->sfp_fc_len = find_zero_bit(sfp_fc_len_table, buf[4], 1); + sfp->sfp_fc_media = find_zero_bit(sfp_fc_media_table, buf[6], 1); + sfp->sfp_fc_speed = find_zero_bit(sfp_fc_speed_table, buf[7], 1); + sfp->sfp_cab_tech = + find_zero_bit(sfp_cab_tech_table, (buf[4] << 8) | buf[5], 2); + + if (ifconfig_sfp_id_is_qsfp(ii.id)) + return (get_qsfp_info(&ii, sfp)); + return (get_sfp_info(&ii, sfp)); +} + +static size_t +channel_count(enum sfp_id id) +{ + /* TODO: other ids */ + switch (id) { + case SFP_ID_UNKNOWN: + return (0); + case SFP_ID_QSFP: + case SFP_ID_QSFPPLUS: + case SFP_ID_QSFP28: + return (4); + default: + return (1); + } +} + +size_t +ifconfig_sfp_channel_count(const struct ifconfig_sfp_info *sfp) +{ + return (channel_count(sfp->sfp_id)); +} + +/* + * Print SFF-8472/SFF-8436 string to supplied buffer. + * All (vendor-specific) strings are padded right with '0x20'. + */ +static void +get_sff_string(struct i2c_info *ii, uint8_t addr, uint8_t off, char *dst) +{ + read_i2c(ii, addr, off, SFF_VENDOR_STRING_SIZE, dst); + dst += SFF_VENDOR_STRING_SIZE; + do { *dst-- = '\0'; } while (*dst == 0x20); +} + +static void +get_sff_date(struct i2c_info *ii, uint8_t addr, uint8_t off, char *dst) +{ + char buf[SFF_VENDOR_DATE_SIZE]; + + read_i2c(ii, addr, off, SFF_VENDOR_DATE_SIZE, buf); + sprintf(dst, "20%c%c-%c%c-%c%c", buf[0], buf[1], buf[2], buf[3], + buf[4], buf[5]); +} + +static int +get_sfp_vendor_info(struct i2c_info *ii, struct ifconfig_sfp_vendor_info *vi) +{ + get_sff_string(ii, SFF_8472_BASE, SFF_8472_VENDOR_START, vi->name); + get_sff_string(ii, SFF_8472_BASE, SFF_8472_PN_START, vi->pn); + get_sff_string(ii, SFF_8472_BASE, SFF_8472_SN_START, vi->sn); + get_sff_date(ii, SFF_8472_BASE, SFF_8472_DATE_START, vi->date); + return (ii->error); +} + +static int +get_qsfp_vendor_info(struct i2c_info *ii, struct ifconfig_sfp_vendor_info *vi) +{ + get_sff_string(ii, SFF_8436_BASE, SFF_8436_VENDOR_START, vi->name); + get_sff_string(ii, SFF_8436_BASE, SFF_8436_PN_START, vi->pn); + get_sff_string(ii, SFF_8436_BASE, SFF_8436_SN_START, vi->sn); + get_sff_date(ii, SFF_8436_BASE, SFF_8436_DATE_START, vi->date); + return (ii->error); +} + +int +ifconfig_sfp_get_sfp_vendor_info(ifconfig_handle_t *h, + const char *name, struct ifconfig_sfp_vendor_info *vi) +{ + struct i2c_info ii; + + memset(vi, 0, sizeof(*vi)); + + if (i2c_info_init(&ii, h, name) != 0) + return (-1); + + if (ifconfig_sfp_id_is_qsfp(ii.id)) + return (get_qsfp_vendor_info(&ii, vi)); + return (get_sfp_vendor_info(&ii, vi)); +} + +/* + * Converts internal temperature (SFF-8472, SFF-8436) + * 16-bit unsigned value to human-readable representation: + * + * Internally measured Module temperature are represented + * as a 16-bit signed twos complement value in increments of + * 1/256 degrees Celsius, yielding a total range of –128C to +128C + * that is considered valid between –40 and +125C. + */ +static double +get_sff_temp(struct i2c_info *ii, uint8_t addr, uint8_t off) +{ + double d; + uint8_t buf[2]; + + read_i2c(ii, addr, off, 2, buf); + d = (double)buf[0]; + d += (double)buf[1] / 256; + return (d); +} + +/* + * Retrieves supplied voltage (SFF-8472, SFF-8436). + * 16-bit usigned value, treated as range 0..+6.55 Volts + */ +static double +get_sff_voltage(struct i2c_info *ii, uint8_t addr, uint8_t off) +{ + double d; + uint8_t buf[2]; + + read_i2c(ii, addr, off, 2, buf); + d = (double)((buf[0] << 8) | buf[1]); + return (d / 10000); +} + +/* + * The following conversions assume internally-calibrated data. + * This is always true for SFF-8346, and explicitly checked for SFF-8472. + */ + +double +power_mW(uint16_t power) +{ + /* Power is specified in units of 0.1 uW. */ + return (1.0 * power / 10000); +} + +double +power_dBm(uint16_t power) +{ + return (10.0 * log10(power_mW(power))); +} + +double +bias_mA(uint16_t bias) +{ + /* Bias current is specified in units of 2 uA. */ + return (1.0 * bias / 500); +} + +static uint16_t +get_sff_channel(struct i2c_info *ii, uint8_t addr, uint8_t off) +{ + uint8_t buf[2]; + + read_i2c(ii, addr, off, 2, buf); + if (ii->error != 0) + return (0); + + return ((buf[0] << 8) + buf[1]); +} + +static int +get_sfp_status(struct i2c_info *ii, struct ifconfig_sfp_status *ss) +{ + uint8_t diag_type, flags; + + /* Read diagnostic monitoring type */ + read_i2c(ii, SFF_8472_BASE, SFF_8472_DIAG_TYPE, 1, (caddr_t)&diag_type); + if (ii->error != 0) + return (-1); + + /* + * Read monitoring data IFF it is supplied AND is + * internally calibrated + */ + flags = SFF_8472_DDM_DONE | SFF_8472_DDM_INTERNAL; + if ((diag_type & flags) != flags) { + ii->h->error.errtype = OTHER; + ii->h->error.errcode = ENXIO; + return (-1); + } + + ss->temp = get_sff_temp(ii, SFF_8472_DIAG, SFF_8472_TEMP); + ss->voltage = get_sff_voltage(ii, SFF_8472_DIAG, SFF_8472_VCC); + ss->channel = calloc(channel_count(ii->id), sizeof(*ss->channel)); + if (ss->channel == NULL) { + ii->h->error.errtype = OTHER; + ii->h->error.errcode = ENOMEM; + return (-1); + } + ss->channel[0].rx = get_sff_channel(ii, SFF_8472_DIAG, SFF_8472_RX_POWER); + ss->channel[0].tx = get_sff_channel(ii, SFF_8472_DIAG, SFF_8472_TX_BIAS); + return (ii->error); +} + +static uint32_t +get_qsfp_bitrate(struct i2c_info *ii) +{ + uint8_t code; + uint32_t rate; + + code = 0; + read_i2c(ii, SFF_8436_BASE, SFF_8436_BITRATE, 1, &code); + rate = code * 100; + if (code == 0xFF) { + read_i2c(ii, SFF_8436_BASE, SFF_8636_BITRATE, 1, &code); + rate = code * 250; + } + + return (rate); +} + +static int +get_qsfp_status(struct i2c_info *ii, struct ifconfig_sfp_status *ss) +{ + size_t channels; + + ss->temp = get_sff_temp(ii, SFF_8436_BASE, SFF_8436_TEMP); + ss->voltage = get_sff_voltage(ii, SFF_8436_BASE, SFF_8436_VCC); + channels = channel_count(ii->id); + ss->channel = calloc(channels, sizeof(*ss->channel)); + if (ss->channel == NULL) { + ii->h->error.errtype = OTHER; + ii->h->error.errcode = ENOMEM; + return (-1); + } + for (size_t chan = 0; chan < channels; ++chan) { + uint8_t rxoffs = SFF_8436_RX_CH1_MSB + chan * sizeof(uint16_t); + uint8_t txoffs = SFF_8436_TX_CH1_MSB + chan * sizeof(uint16_t); + ss->channel[chan].rx = + get_sff_channel(ii, SFF_8436_BASE, rxoffs); + ss->channel[chan].tx = + get_sff_channel(ii, SFF_8436_BASE, txoffs); + } + ss->bitrate = get_qsfp_bitrate(ii); + return (ii->error); +} + +int +ifconfig_sfp_get_sfp_status(ifconfig_handle_t *h, const char *name, + struct ifconfig_sfp_status *ss) +{ + struct i2c_info ii; + + memset(ss, 0, sizeof(*ss)); + + if (i2c_info_init(&ii, h, name) != 0) + return (-1); + + if (ifconfig_sfp_id_is_qsfp(ii.id)) + return (get_qsfp_status(&ii, ss)); + return (get_sfp_status(&ii, ss)); +} + +void +ifconfig_sfp_free_sfp_status(struct ifconfig_sfp_status *ss) +{ + if (ss != NULL) + free(ss->channel); +} + +static const char * +sfp_id_string_alt(uint8_t value) +{ + const char *id; + + if (value <= SFF_8024_ID_LAST) + id = sff_8024_id[value]; + else if (value > 0x80) + id = "Vendor specific"; + else + id = "Reserved"; + + return (id); +} + +static const char * +sfp_conn_string_alt(uint8_t value) +{ + const char *conn; + + if (value >= 0x0D && value <= 0x1F) + conn = "Unallocated"; + else if (value >= 0x24 && value <= 0x7F) + conn = "Unallocated"; + else + conn = "Vendor specific"; + + return (conn); +} + +void +ifconfig_sfp_get_sfp_info_strings(const struct ifconfig_sfp_info *sfp, + struct ifconfig_sfp_info_strings *strings) +{ + get_sfp_info_strings(sfp, strings); + if (strings->sfp_id == NULL) + strings->sfp_id = sfp_id_string_alt(sfp->sfp_id); + if (strings->sfp_conn == NULL) + strings->sfp_conn = sfp_conn_string_alt(sfp->sfp_conn); + if (strings->sfp_rev == NULL) + strings->sfp_rev = "Unallocated"; +} + +const char * +ifconfig_sfp_physical_spec(const struct ifconfig_sfp_info *sfp, + const struct ifconfig_sfp_info_strings *strings) +{ + switch (sfp->sfp_id) { + case SFP_ID_UNKNOWN: + break; + case SFP_ID_QSFP: + case SFP_ID_QSFPPLUS: + case SFP_ID_QSFP28: + if (sfp->sfp_eth_1040g & SFP_ETH_1040G_EXTENDED) + return (strings->sfp_eth_ext); + else if (sfp->sfp_eth_1040g) + return (strings->sfp_eth_1040g); + break; + default: + if (sfp->sfp_eth_ext) + return (strings->sfp_eth_ext); + else if (sfp->sfp_eth_10g) + return (strings->sfp_eth_10g); + else if (sfp->sfp_eth) + return (strings->sfp_eth); + break; + } + return ("Unknown"); +} + +int +ifconfig_sfp_get_sfp_dump(ifconfig_handle_t *h, const char *name, + struct ifconfig_sfp_dump *dump) +{ + struct i2c_info ii; + uint8_t *buf = dump->data; + + memset(dump->data, 0, sizeof(dump->data)); + + if (i2c_info_init(&ii, h, name) != 0) + return (-1); + + if (ifconfig_sfp_id_is_qsfp(ii.id)) { + read_i2c(&ii, SFF_8436_BASE, QSFP_DUMP0_START, QSFP_DUMP0_SIZE, + buf + QSFP_DUMP0_START); + read_i2c(&ii, SFF_8436_BASE, QSFP_DUMP1_START, QSFP_DUMP1_SIZE, + buf + QSFP_DUMP1_START); + } else { + read_i2c(&ii, SFF_8472_BASE, SFP_DUMP_START, SFP_DUMP_SIZE, + buf + SFP_DUMP_START); + } + + return (ii.error != 0 ? -1 : 0); +} + +size_t +ifconfig_sfp_dump_region_count(const struct ifconfig_sfp_dump *dp) +{ + uint8_t id_byte = dp->data[0]; + + switch ((enum sfp_id)id_byte) { + case SFP_ID_UNKNOWN: + return (0); + case SFP_ID_QSFP: + case SFP_ID_QSFPPLUS: + case SFP_ID_QSFP28: + return (2); + default: + return (1); + } +} diff --git a/lib/libifconfig/libifconfig_sfp.h b/lib/libifconfig/libifconfig_sfp.h new file mode 100644 index 00000000000..e64666b7bd7 --- /dev/null +++ b/lib/libifconfig/libifconfig_sfp.h @@ -0,0 +1,219 @@ +/*- + * Copyright (c) 2014, Alexander V. Chernikov + * Copyright (c) 2020, Ryan Moeller + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#pragma once + +#include +#include + +#include +#include + +/** SFP module information in raw numeric form + * These are static properties of the hardware. + */ +struct ifconfig_sfp_info; + +/** SFP module information formatted as strings + * These are static strings that do not need to be freed. + */ +struct ifconfig_sfp_info_strings; + +#define SFF_VENDOR_STRING_SIZE 16 /**< max chars in a vendor string */ +#define SFF_VENDOR_DATE_SIZE 6 /**< chars in a vendor date code */ + +/** SFP module vendor info strings */ +struct ifconfig_sfp_vendor_info { + char name[SFF_VENDOR_STRING_SIZE + 1]; /**< vendor name */ + char pn[SFF_VENDOR_STRING_SIZE + 1]; /**< vendor part number */ + char sn[SFF_VENDOR_STRING_SIZE + 1]; /**< vendor serial number */ + char date[SFF_VENDOR_DATE_SIZE + 5]; /**< formatted vendor date */ +}; + +/** SFP module status + * These are dynamic properties of the hardware. + */ +struct ifconfig_sfp_status { + double temp; /**< module temperature in degrees C, + valid range -40.0 to 125.0 */ + double voltage; /**< module voltage in volts */ + struct sfp_channel { + uint16_t rx; /**< channel receive power, LSB 0.1uW */ + uint16_t tx; /**< channel transmit bias current, LSB 2uA */ + } *channel; /**< array of channel rx/tx status */ + uint32_t bitrate; /**< link bitrate, + only present for QSFP modules, + zero for SFP modules */ +}; + +#define SFF_DUMP_SIZE 256 /**< size of the memory dump buffer */ + +#define SFP_DUMP_START 0 /**< start address of an SFP module dump */ +#define SFP_DUMP_SIZE 128 /**< bytes in an SFP module dump */ + +#define QSFP_DUMP0_START 0 /**< start address of the first region + in a QSFP module dump */ +#define QSFP_DUMP0_SIZE 82 /**< bytes in the first region + in a QSFP module dump */ +#define QSFP_DUMP1_START 128 /**< start address of the second region + in a QSFP module dump */ +#define QSFP_DUMP1_SIZE 128 /**< bytes in the second region + in a QSFP module dump */ + +/** SFP module I2C memory dump + * SFP modules have one region, QSFP modules have two regions. + */ +struct ifconfig_sfp_dump { + uint8_t data[SFF_DUMP_SIZE]; /**< memory dump data */ +}; + +/** Get information about the static properties of an SFP/QSFP module + * The information is returned in numeric form. + * @see ifconfig_sfp_get_sfp_info_strings to get corresponding strings. + * @param h An open ifconfig state handle + * @param name The name of an interface + * @param sfp Pointer to an object to fill, will be zeroed by this function + * @return 0 if successful, -1 with error info set in the handle otherwise + */ +int ifconfig_sfp_get_sfp_info(ifconfig_handle_t *h, const char *name, + struct ifconfig_sfp_info *sfp); + +/** Get the number of channels present on the given module + * @param sfp Pointer to a filled SFP module info object + * @return The number of channels or 0 if unknown + */ +size_t ifconfig_sfp_channel_count(const struct ifconfig_sfp_info *sfp); + +/** Is the given module ID a QSFP + * NB: This convenience function is implemented in the header to keep the + * classification criteria visible to the user. + * @param id The sfp_id field of a SFP module info object + * @return A bool true if QSFP-type sfp_id otherwise false + */ +static inline bool +ifconfig_sfp_id_is_qsfp(enum sfp_id id) +{ + switch (id) { + case SFP_ID_QSFP: + case SFP_ID_QSFPPLUS: + case SFP_ID_QSFP28: + return (true); + default: + return (false); + } +} + +/** Get string descriptions of the given SFP/QSFP module info + * The strings are static and do not need to be freed. + * @see ifconfig_sfp_get_sfp_info to obtain the input info. + * @param sfp Pointer to a filled SFP module info object + * @param strings Pointer to an object to be filled with pointers to + * static strings describing the given info + */ +void ifconfig_sfp_get_sfp_info_strings(const struct ifconfig_sfp_info *sfp, + struct ifconfig_sfp_info_strings *strings); + +/** Get a string describing the given SFP/QSFP module's physical layer spec + * The correct field in ifconfig_sfp_info varies depending on the module. This + * function chooses the appropriate string based on the provided module info. + * The string returned is static and does not need to be freed. + * @param sfp Pointer to a filled SFP module info object + * @param strings Pointer to a filled SFP module strings object + * @return Pointer to a static string describing the module's spec + */ +const char *ifconfig_sfp_physical_spec(const struct ifconfig_sfp_info *sfp, + const struct ifconfig_sfp_info_strings *strings); + +/** Get the vendor info strings from an SFP/QSFP module + * @param h An open ifconfig state handle + * @param name The name of an interface + * @param vi Pointer to an object to be filled with the vendor info strings, + * will be zeroed by this function + * @return 0 if successful, -1 with error info set in the handle otherwise + */ +int ifconfig_sfp_get_sfp_vendor_info(ifconfig_handle_t *h, const char *name, + struct ifconfig_sfp_vendor_info *vi); + +/** Get the status of an SFP/QSFP module's dynamic properties + * @see ifconfig_sfp_free_sfp_status to free the allocations + * @param h An open ifconfig state handle + * @param name The name of an interface + * @param ss Pointer to an object to be filled with the module's status + * @return 0 if successful, -1 with error info set in the handle otherwise + * where the errcode `ENXIO` indicates an SFP module that is not + * calibrated or does not provide diagnostic status measurements + */ +int ifconfig_sfp_get_sfp_status(ifconfig_handle_t *h, const char *name, + struct ifconfig_sfp_status *ss); + +/** Free the memory allocations in an ifconfig_sfp_status struct + * @param ss Pointer to an object whose internal allocations are to be freed + * if not NULL + */ +void ifconfig_sfp_free_sfp_status(struct ifconfig_sfp_status *ss); + +/** Dump the I2C memory of an SFP/QSFP module + * SFP modules have one memory region dumped, QSFP modules have two. + * @param h An open ifconfig state handle + * @param name The name of an interface + * @param buf Pointer to a dump data buffer object + * @return 0 if successful, -1 with error info set in the handle otherwise + */ +int ifconfig_sfp_get_sfp_dump(ifconfig_handle_t *h, const char *name, + struct ifconfig_sfp_dump *buf); + +/** Get the number of I2C memory dump regions present in the given dump + * @param dp Pointer to a filled dump data buffer object + * @return The number of regions or 0 if unknown + */ +size_t ifconfig_sfp_dump_region_count(const struct ifconfig_sfp_dump *dp); + +/** Convert channel power to milliwatts power + * This is provided as a convenience for displaying channel power levels. + * @see (struct ifconfig_sfp_status).channel + * @param power Power in 0.1 mW units + * @return Power in milliwatts (mW) + */ +double power_mW(uint16_t power); + +/** Convert channel power to decibel-milliwats power level + * This is provided as a convenience for displaying channel power levels. + * @see (struct ifconfig_sfp_status).channel + * @param power Power in 0.1 mW units + * @return Power level in decibel-milliwatts (dBm) + */ + +double power_dBm(uint16_t power); + +/** Convert channel bias current to milliamps + * This is provided as a convenience for displaying channel bias currents. + * @see (struct ifconfig_sfp_status).channel + * @param bias Bias current in 2 mA units + * @return Bias current in milliamps (mA) + */ +double bias_mA(uint16_t bias); diff --git a/lib/libifconfig/libifconfig_sfp_tables.tpl.c b/lib/libifconfig/libifconfig_sfp_tables.tpl.c new file mode 100644 index 00000000000..1397e7d1961 --- /dev/null +++ b/lib/libifconfig/libifconfig_sfp_tables.tpl.c @@ -0,0 +1,124 @@ +/*- + * Copyright (c) 2020, Ryan Moeller + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +{# THIS IS A TEMPLATE PROCESSED BY lib/libifconfig/sfp.lua #} + +#include +#include + +struct sfp_enum_metadata { + int value; /* numeric discriminant value */ + const char *symbol; /* symbolic name */ + const char *description; /* brief description */ + const char *display; /* shortened display name */ +}; + +const struct sfp_enum_metadata * +find_metadata(const struct sfp_enum_metadata *table, int value) +{ + while (table->value != value && table->symbol != NULL) + ++table; + return (table->symbol != NULL ? table : NULL); +} + +{% +for _, ent in ipairs(enums) do + if type(ent) == "string" then +%} +/* + * {*ent*} + */ + +{% + else + local enum = ent + local name = "sfp_"..enum.name + local sym, desc, disp +%} +static const struct sfp_enum_metadata {*name*}_table_[] = { +{% + for _, item in ipairs(enum.values) do + _, sym, desc, disp = table.unpack(item) + local symbol = string.upper(name).."_"..sym +%} + { + .value = {*symbol*}, + .symbol = "{*symbol*}", + .description = "{*desc*}", +{% + if disp then +%} + .display = "{*disp*}", +{% + end +%} + }, +{% + end +%} + {0} +}; +const struct sfp_enum_metadata *{*name*}_table = {*name*}_table_; + +const char * +ifconfig_{*name*}_symbol(enum {*name*} v) +{ + const struct sfp_enum_metadata *metadata; + + if ((metadata = find_metadata({*name*}_table, v)) == NULL) + return (NULL); + return (metadata->symbol); +} + +const char * +ifconfig_{*name*}_description(enum {*name*} v) +{ + const struct sfp_enum_metadata *metadata; + + if ((metadata = find_metadata({*name*}_table, v)) == NULL) + return (NULL); + return (metadata->description); +} + +{% + if disp then +%} +const char * +ifconfig_{*name*}_display(enum {*name*} v) +{ + const struct sfp_enum_metadata *metadata; + + if ((metadata = find_metadata({*name*}_table, v)) == NULL) + return (NULL); + return (metadata->display); +} + +{% + end + end +end +%} diff --git a/lib/libifconfig/libifconfig_sfp_tables.tpl.h b/lib/libifconfig/libifconfig_sfp_tables.tpl.h new file mode 100644 index 00000000000..7e7c4535175 --- /dev/null +++ b/lib/libifconfig/libifconfig_sfp_tables.tpl.h @@ -0,0 +1,130 @@ +/*- + * Copyright (c) 2020, Ryan Moeller + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +{# THIS IS A TEMPLATE PROCESSED BY lib/libifconfig/sfp.lua #} + +#pragma once + +#include + +{% +for _, ent in ipairs(enums) do + if type(ent) == "string" then +%} +/* + * {*ent*} + */ + +{% + else + local enum = ent + local name = "sfp_"..enum.name + local num, sym, desc, disp +%} +/** {*enum.description*} */ +enum {*name*} { +{% + for _, item in ipairs(enum.values) do + val, sym, desc, disp = table.unpack(item) + local symbol = string.upper(name).."_"..sym +%} + {*symbol*} = {*val*}, /**< {*desc*} */ +{% + end +%} +}; + +/** Get the symbolic name of a given {*name*} value */ +const char *ifconfig_{*name*}_symbol(enum {*name*}); + +/** Get a brief description of a given {*name*} value */ +const char *ifconfig_{*name*}_description(enum {*name*}); + +{% + if disp then +%} +/** Get a shortened user-friendly display name for a given {*name*} value */ +const char *ifconfig_{*name*}_display(enum {*name*}); + +{% + end + end +end +%} +/* + * Descriptions of each enum + */ + +{% +for _, ent in ipairs(enums) do + if type(ent) == "table" then + local enum = ent + local name = "sfp_"..enum.name +%} +/** Get a brief description of the {*name*} enum */ +static inline const char * +ifconfig_enum_{*name*}_description(void) +{ + return ("{*enum.description*}"); +} + +{% + end +end +%} +/* + * Info struct definitions + */ + +struct ifconfig_sfp_info { +{% +for _, ent in ipairs(enums) do + if type(ent) == "table" then + local enum = ent + local name = "sfp_"..enum.name + local t = string.format("uint%d_t", enum.bits) +%} + {*t*} {*name*}; /**< {*enum.description*} */ +{% + end +end +%} +}; + +struct ifconfig_sfp_info_strings { +{% +for _, ent in ipairs(enums) do + if type(ent) == "table" then + local enum = ent + local name = "sfp_"..enum.name +%} + const char *{*name*}; /**< {*enum.description*} */ +{% + end +end +%} +}; diff --git a/lib/libifconfig/libifconfig_sfp_tables_internal.tpl.h b/lib/libifconfig/libifconfig_sfp_tables_internal.tpl.h new file mode 100644 index 00000000000..a242d498ef0 --- /dev/null +++ b/lib/libifconfig/libifconfig_sfp_tables_internal.tpl.h @@ -0,0 +1,66 @@ +/*- + * Copyright (c) 2020, Ryan Moeller + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +{# THIS IS A TEMPLATE PROCESSED BY lib/libifconfig/sfp.lua #} + +#pragma once + +#include +#include + +struct sfp_enum_metadata; +const struct sfp_enum_metadata *find_metadata(const struct sfp_enum_metadata *, + int); + +{% +for _, ent in ipairs(enums) do + if type(ent) == "table" then + local enum = ent + local name = "sfp_"..enum.name +%} +extern const struct sfp_enum_metadata *{*name*}_table; +{% + end +end +%} + +static inline void +get_sfp_info_strings(const struct ifconfig_sfp_info *sfp, + struct ifconfig_sfp_info_strings *strings) +{ +{% +for _, ent in ipairs(enums) do + if type(ent) == "table" then + local enum = ent + local name = "sfp_"..enum.name +%} + strings->{*name*} = ifconfig_{*name*}_description(sfp->{*name*}); +{% + end +end +%} +} diff --git a/lib/libifconfig/sfp.lua b/lib/libifconfig/sfp.lua new file mode 100644 index 00000000000..dc471cad567 --- /dev/null +++ b/lib/libifconfig/sfp.lua @@ -0,0 +1,367 @@ +#!/usr/libexec/flua +-- ex: sw=4 et: +--[[ +/*- + * Copyright (c) 2014, Alexander V. Chernikov + * Copyright (c) 2020, Ryan Moeller + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +]] + +-- Try to put the template.lua library in the package search path. +package.path = (os.getenv("SRCTOP") or "/usr/src").."/tools/lua/?.lua" + +-- Render the template named by the first argument to this script. +require("template").render(arg[1], { -- This table is the template's context. + +-- The table `enums' is accessible in the template. It is a list of strings +-- and tables that describe the various enum types we are generating and the +-- ancillary metadata for generating other related code. +enums = { + + -- Strings at this level are rendered as block comments for convenience. + "SFF-8024 Rev. 4.6 Table 4-1: Indentifier Values", + + -- This table describes an enum type, in this case enum sfp_id: + { + name = "id", -- The template prepends the sfp_ prefix to our name. + description = "Transceiver identifier", + + -- What width int is needed to store this type: + bits = 8, -- This could be inferred by the values below... + + -- The values, symbols, display names, and descriptions of this enum: + values = { + -- The prefix SFP_ID_ is prepended to the symbolic names. + -- Only this enum has shortened names for the values, though they + -- could be added to the other enums. + + -- value, symbolic name, description, shortened name + {0x00, "UNKNOWN", "Unknown or unspecified", "Unknown"}, + {0x01, "GBIC", "GBIC", "GBIC"}, + {0x02, "SFF", "Module soldered to motherboard (ex: SFF)", + "SFF"}, + {0x03, "SFP", "SFP or SFP+", "SFP/SFP+/SFP28"}, + {0x04, "XBI", "300 pin XBI", "XBI"}, + {0x05, "XENPAK", "Xenpak", "Xenpak"}, + {0x06, "XFP", "XFP", "XFP"}, + {0x07, "XFF", "XFF", "XFF"}, + {0x08, "XFPE", "XFP-E", "XFP-E"}, + {0x09, "XPAK", "XPAK", "XPAK"}, + {0x0A, "X2", "X2", "X2"}, + {0x0B, "DWDM_SFP", "DWDM-SFP/SFP+", "DWDM-SFP/SFP+"}, + {0x0C, "QSFP", "QSFP", "QSFP"}, + {0x0D, "QSFPPLUS", "QSFP+ or later", "QSFP+"}, + {0x0E, "CXP", "CXP", "CXP"}, + {0x0F, "HD4X", "Shielded Mini Multilane HD 4X", "HD4X"}, + {0x10, "HD8X", "Shielded Mini Multilane HD 8X", "HD8X"}, + {0x11, "QSFP28", "QSFP28 or later", "QSFP28"}, + {0x12, "CXP2", "CXP2 (aka CXP28)", "CXP2"}, + {0x13, "CDFP", "CDFP (Style 1/Style 2)", "CDFP"}, + {0x14, "SMM4", "Shielded Mini Multilane HD 4X fanout", + "SMM4"}, + {0x15, "SMM8", "Shielded Mini Multilane HD 8X fanout", + "SMM8"}, + {0x16, "CDFP3", "CDFP (Style 3)", "CDFP3"}, + {0x17, "MICROQSFP", "microQSFP", "microQSFP"}, + {0x18, "QSFP_DD", "QSFP-DD 8X pluggable transceiver", "QSFP-DD"}, + {0x19, "QSFP8X", "QSFP 8X pluggable transceiver", "QSFP8X"}, + {0x1A, "SFP_DD", "SFP-DD 2X pluggable transceiver", "SFP-DD"}, + {0x1B, "DSFP", "DSFP Dual SFP pluggable transceiver", "DSFP"}, + {0x1C, "X4ML", "x4 MiniLink/OcuLink", "x4MiniLink/OcuLink"}, + {0x1D, "X8ML", "x8 MiniLink", "x8MiniLink"}, + {0x1E, "QSFP_CMIS", + "QSFP+ or later w/Common Management Interface Specification", + "QSFP+(CMIS)"}, + }, + }, + + "SFF-8024 Rev. 4.6 Table 4-3: Connector Types", + { + name = "conn", + description = "Connector type", + bits = 8, + values = { + {0x00, "UNKNOWN", "Unknown"}, + {0x01, "SC", "SC"}, + {0x02, "FC_1_COPPER", "Fibre Channel Style 1 copper"}, + {0x03, "FC_2_COPPER", "Fibre Channel Style 2 copper"}, + {0x04, "BNC_TNC", "BNC/TNC"}, + {0x05, "FC_COAX", "Fibre Channel coaxial"}, + {0x06, "FIBER_JACK", "Fiber Jack"}, + {0x07, "LC", "LC"}, + {0x08, "MT_RJ", "MT-RJ"}, + {0x09, "MU", "MU"}, + {0x0A, "SG", "SG"}, + {0x0B, "OPTICAL_PIGTAIL", "Optical pigtail"}, + {0x0C, "MPO_1X12_POPTIC", "MPO 1x12 Parallel Optic"}, + {0x0D, "MPO_2X16_POPTIC", "MPO 2x16 Parallel Optic"}, + {0x20, "HSSDC_II", "HSSDC II"}, + {0x21, "COPPER_PIGTAIL", "Copper pigtail"}, + {0x22, "RJ45", "RJ45"}, + {0x23, "NONE", "No separable connector"}, + {0x24, "MXC_2X16", "MXC 2x16"}, + {0x25, "CS_OPTICAL", "CS optical connector"}, + {0x26, "MINI_CS_OPTICAL", "Mini CS optical connector"}, + {0x27, "MPO_2X12_POPTIC", "MPO 2x12 Parallel Optic"}, + {0x28, "MPO_1X16_POPTIC", "MPO 1x16 Parallel Optic"}, + }, + }, + "SFF-8472 Rev. 11.4 table 3.5: Transceiver codes", + "10G Ethernet/IB compliance codes, byte 3", + { + name = "eth_10g", + description = "10G Ethernet/IB compliance", + bits = 8, + values = { + {0x80, "10G_BASE_ER", "10G Base-ER"}, + {0x40, "10G_BASE_LRM", "10G Base-LRM"}, + {0x20, "10G_BASE_LR", "10G Base-LR"}, + {0x10, "10G_BASE_SR", "10G Base-SR"}, + {0x08, "1X_SX", "1X SX"}, + {0x04, "1X_LX", "1X LX"}, + {0x02, "1X_COPPER_ACTIVE", "1X Copper Active"}, + {0x01, "1X_COPPER_PASSIVE", "1X Copper Passive"}, + }, + }, + "Ethernet compliance codes, byte 6", + { + name = "eth", + description = "Ethernet compliance", + bits = 8, + values = { + {0x80, "BASE_PX", "BASE-PX"}, + {0x40, "BASE_BX10", "BASE-BX10"}, + {0x20, "100BASE_FX", "100BASE-FX"}, + {0x10, "100BASE_LX_LX10", "100BASE-LX/LX10"}, + {0x08, "1000BASE_T", "1000BASE-T"}, + {0x04, "1000BASE_CX", "1000BASE-CX"}, + {0x02, "1000BASE_LX", "1000BASE-LX"}, + {0x01, "1000BASE_SX", "1000BASE-SX"}, + }, + }, + "FC link length, byte 7", + { + name = "fc_len", + description = "Fibre Channel link length", + bits = 8, + values = { + {0x80, "VERY_LONG", "very long distance"}, + {0x40, "SHORT", "short distance"}, + {0x20, "INTERMEDIATE", "intermediate distance"}, + {0x10, "LONG", "long distance"}, + {0x08, "MEDIUM", "medium distance"}, + }, + }, + "Channel/Cable technology, byte 7-8", + { + name = "cab_tech", + description = "Channel/cable technology", + bits = 16, + values = { + {0x0400, "SA", "Shortwave laser (SA)"}, + {0x0200, "LC", "Longwave laser (LC)"}, + {0x0100, "EL_INTER", "Electrical inter-enclosure (EL)"}, + {0x0080, "EL_INTRA", "Electrical intra-enclosure (EL)"}, + {0x0040, "SN", "Shortwave laser (SN)"}, + {0x0020, "SL", "Shortwave laser (SL)"}, + {0x0010, "LL", "Longwave laser (LL)"}, + {0x0008, "ACTIVE", "Active Cable"}, + {0x0004, "PASSIVE", "Passive Cable"}, + }, + }, + "FC Transmission media, byte 9", + { + name = "fc_media", + description = "Fibre Channel transmission media", + bits = 8, + values = { + {0x80, "TW", "Twin Axial Pair (TW)"}, + {0x40, "TP", "Twisted Pair (TP)"}, + {0x20, "MI", "Miniature Coax (MI)"}, + {0x10, "TV", "Video Coax (TV)"}, + {0x08, "M6", "Miltimode 62.5um (M6)"}, + {0x04, "M5", "Multimode 50um (M5)"}, + {0x02, "RESERVED", "Reserved"}, + {0x01, "SM", "Single Mode (SM)"}, + }, + }, + "FC Speed, byte 10", + { + name = "fc_speed", + description = "Fibre Channel speed", + bits = 8, + values = { + {0x80, "1200", "1200 MBytes/sec"}, + {0x40, "800", "800 MBytes/sec"}, + {0x20, "1600", "1600 MBytes/sec"}, + {0x10, "400", "400 MBytes/sec"}, + {0x08, "3200", "3200 MBytes/sec"}, + {0x04, "200", "200 MBytes/sec"}, + {0x01, "100", "100 MBytes/sec"}, + }, + }, + "SFF-8436 Rev. 4.8 table 33: Specification compliance", + "10/40G Ethernet compliance codes, byte 128 + 3", + { + name = "eth_1040g", + description = "10/40G Ethernet compliance", + bits = 8, + values = { + {0x80, "EXTENDED", "Extended"}, + {0x40, "10GBASE_LRM", "10GBASE-LRM"}, + {0x20, "10GBASE_LR", "10GBASE-LR"}, + {0x10, "10GBASE_SR", "10GBASE-SR"}, + {0x08, "40GBASE_CR4", "40GBASE-CR4"}, + {0x04, "40GBASE_SR4", "40GBASE-SR4"}, + {0x02, "40GBASE_LR4", "40GBASE-LR4"}, + {0x01, "40G_ACTIVE", "40G Active Cable"}, + }, + }, + "SFF-8024 Rev. 4.6 table 4-4: Extended Specification Compliance", + { + name = "eth_ext", + description = "Extended specification compliance", + bits = 8, + values = { + {0xFF, "RESERVED_FF", "Reserved"}, + {0x55, "128GFC_LW", "128GFC LW"}, + {0x54, "128GFC_SW", "128GFC SW"}, + {0x53, "128GFC_EA", "128GFC EA"}, + {0x52, "64GFC_LW", "64GFC LW"}, + {0x51, "64GFC_SW", "64GFC SW"}, + {0x50, "64GFC_EA", "64GFC EA"}, + {0x4F, "RESERVED_4F", "Reserved"}, + {0x4E, "RESERVED_4E", "Reserved"}, + {0x4D, "RESERVED_4D", "Reserved"}, + {0x4C, "RESERVED_4C", "Reserved"}, + {0x4B, "RESERVED_4B", "Reserved"}, + {0x4A, "RESERVED_4A", "Reserved"}, + {0x49, "RESERVED_49", "Reserved"}, + {0x48, "RESERVED_48", "Reserved"}, + {0x47, "RESERVED_47", "Reserved"}, + {0x46, "200GBASE_LR4", "200GBASE-LR4"}, + {0x45, "50GBASE_LR", "50GBASE-LR"}, + {0x44, "200G_1550NM_PSM4", "200G 1550nm PSM4"}, + {0x43, "200GBASE_FR4", "200GBASE-FR4"}, + {0x42, "50GBASE_FR_200GBASE_DR4", "50GBASE-FR or 200GBASE-DR4"}, + {0x41, "50GBASE_SR_100GBASE_SR2_200GBASE_SR4", + "50GBASE-SR/100GBASE-SR2/200GBASE-SR4"}, + {0x40, "50GBASE_CR_100GBASE_CR2_200GBASE_CR4", + "50GBASE-CR/100GBASE-CR2/200GBASE-CR4"}, + {0x3F, "RESERVED_3F", "Reserved"}, + {0x3E, "RESERVED_3E", "Reserved"}, + {0x3D, "RESERVED_3D", "Reserved"}, + {0x3C, "RESERVED_3C", "Reserved"}, + {0x3B, "RESERVED_3B", "Reserved"}, + {0x3A, "RESERVED_3A", "Reserved"}, + {0x39, "RESERVED_39", "Reserved"}, + {0x38, "RESERVED_38", "Reserved"}, + {0x37, "RESERVED_37", "Reserved"}, + {0x36, "RESERVED_36", "Reserved"}, + {0x35, "RESERVED_35", "Reserved"}, + {0x34, "RESERVED_34", "Reserved"}, + {0x33, "50_100_200GAUI_AOC_HI_BER", + "50GAUI/100GAUI-2/200GAUI-4 AOC (BER <2.6e-4)"}, + {0x32, "50_100_200GAUI_ACC_HI_BER", + "50GAUI/100GAUI-2/200GAUI-4 ACC (BER <2.6e-4)"}, + {0x31, "50_100_200GAUI_AOC_LO_BER", + "50GAUI/100GAUI-2/200GAUI-4 AOC (BER <1e-6)"}, + {0x30, "50_100_200GAUI_ACC_LO_BER", + "50GAUI/100GAUI-2/200GAUI-4 ACC (BER <1e-6)"}, + {0x2F, "RESERVED_2F", "Reserved"}, + {0x2E, "RESERVED_2E", "Reserved"}, + {0x2D, "RESERVED_2D", "Reserved"}, + {0x2C, "RESERVED_2C", "Reserved"}, + {0x2B, "RESERVED_2B", "Reserved"}, + {0x2A, "RESERVED_2A", "Reserved"}, + {0x29, "RESERVED_29", "Reserved"}, + {0x28, "RESERVED_28", "Reserved"}, + {0x27, "100G_LR", "100G-LR"}, + {0x26, "100G_FR", "100G-FR"}, + {0x25, "100GBASE_DR", "100GBASE-DR"}, + {0x24, "4WDM_40_MSA", "4WDM-40 MSA"}, + {0x23, "4WDM_20_MSA", "4WDM-20 MSA"}, + {0x22, "4WDM_10_MSA", "4WDM-10 MSA"}, + {0x21, "100G_PAM4_BIDI", "100G PAM4 BiDi"}, + {0x20, "100G_SWDM4", "100G SWDM4"}, + {0x1F, "40G_SWDM4", "40G SWDM4"}, + {0x1E, "2_5GBASE_T", "2.5GBASE-T"}, + {0x1D, "5GBASE_T", "5GBASE-T"}, + {0x1C, "10GBASE_T_SR", "10GBASE-T Short Reach"}, + {0x1B, "100G_1550NM_WDM", "100G 1550nm WDM"}, + {0x1A, "100GE_DWDM2", "100GE-DWDM2"}, + {0x19, "100G_25GAUI_C2M_ACC", "100G ACC or 25GAUI C2M ACC"}, + {0x18, "100G_25GAUI_C2M_AOC", "100G AOC or 25GAUI C2M AOC"}, + {0x17, "100G_CLR4", "100G CLR4"}, + {0x16, "10GBASE_T_SFI", + "10GBASE-T with SFI electrical interface"}, + {0x15, "G959_1_P1L1_2D2", "G959.1 profile P1L1-2D2"}, + {0x14, "G959_1_P1S1_2D2", "G959.1 profile P1S1-2D2"}, + {0x13, "G959_1_P1I1_2D1", "G959.1 profile P1I1-2D1"}, + {0x12, "40G_PSM4", "40G PSM4 Parallel SMF"}, + {0x11, "4X_10GBASE_SR", "4 x 10GBASE-SR"}, + {0x10, "40GBASE_ER4", "40GBASE-ER4"}, + {0x0F, "RESERVED_0F", "Reserved"}, + {0x0E, "RESERVED_0E", "Reserved"}, + {0x0D, "CA_25G_N", "25GBASE-CR CA-25G-N"}, + {0x0C, "CA_25G_S", "25GBASE-CR CA-25G-S"}, + {0x0B, "CA_L", "100GBASE-CR4 or 25GBASE-CR CA-L"}, + {0x0A, "RESERVED_0A", "Reserved"}, + {0x09, "OBSOLETE", "Obsolete"}, + {0x08, "100G_25GAUI_C2M_ACC_1", + "100G ACC (Active Copper Cable"}, + {0x07, "100G_PSM4_P_SMF", "100G PSM4 Parallel SMF"}, + {0x06, "100G_CWDM4", "100G CWDM4"}, + {0x05, "100GBASE_SR10", "100GBASE-SR10"}, + {0x04, "100GBASE_ER4_25GBASE_ER", "100GBASE-ER4 or 25GBASE-ER"}, + {0x03, "100GBASE_LR4_25GBASE_LR", "100GBASE-LR4 or 25GBASE-LR"}, + {0x02, "100GBASE_SR4_25GBASE_SR", "100GBASE-SR4 or 25GBASE-SR"}, + {0x01, "100G_25GAUI_C2M_AOC_1", + "100G AOC (Active Optical Cable"}, + {0x00, "UNSPECIFIED", "Unspecified"}, + }, + }, + "SFF-8636 Rev. 2.9 table 6.3: Revision compliance", + { + name = "rev", + description = "Revision compliance", + bits = 8, + values = { + {0x1, "SFF_8436_REV_LE_4_8", "SFF-8436 rev <=4.8"}, + {0x2, "SFF_8436_REV_LE_4_8_ALT", "SFF-8436 rev <=4.8"}, + {0x3, "SFF_8636_REV_LE_1_3", "SFF-8636 rev <=1.3"}, + {0x4, "SFF_8636_REV_LE_1_4", "SFF-8636 rev <=1.4"}, + {0x5, "SFF_8636_REV_LE_1_5", "SFF-8636 rev <=1.5"}, + {0x6, "SFF_8636_REV_LE_2_0", "SFF-8636 rev <=2.0"}, + {0x7, "SFF_8636_REV_LE_2_7", "SFF-8636 rev <=2.7"}, + {0x8, "SFF_8363_REV_GE_2_8", "SFF-8636 rev >=2.8"}, + {0x0, "UNSPECIFIED", "Unspecified"}, + }, + }, +} + +-- Nothing else in this context. +}) diff --git a/rescue/rescue/Makefile b/rescue/rescue/Makefile index 9681ebaddfa..66aa7f188ec 100644 --- a/rescue/rescue/Makefile +++ b/rescue/rescue/Makefile @@ -224,6 +224,9 @@ CRUNCH_ALIAS_chown= chgrp ################################################################## CRUNCH_LIBS+= -lm +CRUNCH_LIBS+= ${OBJTOP}/lib/libifconfig/libifconfig.a +CRUNCH_BUILDOPTS+= CRUNCH_CFLAGS+=-I${OBJTOP}/lib/libifconfig + .if ${MK_ISCSI} != "no" CRUNCH_PROGS_usr.bin+= iscsictl CRUNCH_PROGS_usr.sbin+= iscsid diff --git a/sbin/ifconfig/Makefile b/sbin/ifconfig/Makefile index b6c9ffabb0e..39050b3a4dc 100644 --- a/sbin/ifconfig/Makefile +++ b/sbin/ifconfig/Makefile @@ -37,7 +37,8 @@ SRCS+= ifgif.c # GIF reversed header workaround SRCS+= ifipsec.c # IPsec VTI SRCS+= sfp.c # SFP/SFP+ information -LIBADD+= m +LIBADD+= ifconfig m util +CFLAGS+= -I${SRCTOP}/lib/libifconfig -I${OBJTOP}/lib/libifconfig .if ${MK_WIRELESS_SUPPORT} != "no" SRCS+= ifieee80211.c # SIOC[GS]IEEE80211 support diff --git a/sbin/ifconfig/sfp.c b/sbin/ifconfig/sfp.c index 49608bdbb57..b7bdc74d42e 100644 --- a/sbin/ifconfig/sfp.c +++ b/sbin/ifconfig/sfp.c @@ -41,946 +41,102 @@ static const char rcsid[] = #include #include #include +#include #include #include #include #include +#include + +#include +#include + #include "ifconfig.h" -struct i2c_info { - int fd; /* fd to issue SIOCGI2C */ - int error; /* Store first error */ - int qsfp; /* True if transceiver is QSFP */ - int do_diag; /* True if we need to request DDM */ - struct ifreq *ifr; /* Pointer to pre-filled ifreq */ -}; - -static int read_i2c(struct i2c_info *ii, uint8_t addr, uint8_t off, - uint8_t len, uint8_t *buf); -static void dump_i2c_data(struct i2c_info *ii, uint8_t addr, uint8_t off, - uint8_t len); - -struct _nv { - int v; - const char *n; -}; - -const char *find_value(struct _nv *x, int value); -const char *find_zero_bit(struct _nv *x, int value, int sz); - -/* SFF-8024 Rev. 4.6 Table 4-3: Connector Types */ -static struct _nv conn[] = { - { 0x00, "Unknown" }, - { 0x01, "SC" }, - { 0x02, "Fibre Channel Style 1 copper" }, - { 0x03, "Fibre Channel Style 2 copper" }, - { 0x04, "BNC/TNC" }, - { 0x05, "Fibre Channel coaxial" }, - { 0x06, "Fiber Jack" }, - { 0x07, "LC" }, - { 0x08, "MT-RJ" }, - { 0x09, "MU" }, - { 0x0A, "SG" }, - { 0x0B, "Optical pigtail" }, - { 0x0C, "MPO 1x12 Parallel Optic" }, - { 0x0D, "MPO 2x16 Parallel Optic" }, - { 0x20, "HSSDC II" }, - { 0x21, "Copper pigtail" }, - { 0x22, "RJ45" }, - { 0x23, "No separable connector" }, - { 0x24, "MXC 2x16" }, - { 0x25, "CS optical connector" }, - { 0x26, "Mini CS optical connector" }, - { 0x27, "MPO 2x12 Parallel Optic" }, - { 0x28, "MPO 1x16 Parallel Optic" }, - { 0, NULL } -}; - -/* SFF-8472 Rev. 11.4 table 3.5: Transceiver codes */ -/* 10G Ethernet/IB compliance codes, byte 3 */ -static struct _nv eth_10g[] = { - { 0x80, "10G Base-ER" }, - { 0x40, "10G Base-LRM" }, - { 0x20, "10G Base-LR" }, - { 0x10, "10G Base-SR" }, - { 0x08, "1X SX" }, - { 0x04, "1X LX" }, - { 0x02, "1X Copper Active" }, - { 0x01, "1X Copper Passive" }, - { 0, NULL } -}; - -/* Ethernet compliance codes, byte 6 */ -static struct _nv eth_compat[] = { - { 0x80, "BASE-PX" }, - { 0x40, "BASE-BX10" }, - { 0x20, "100BASE-FX" }, - { 0x10, "100BASE-LX/LX10" }, - { 0x08, "1000BASE-T" }, - { 0x04, "1000BASE-CX" }, - { 0x02, "1000BASE-LX" }, - { 0x01, "1000BASE-SX" }, - { 0, NULL } -}; - -/* FC link length, byte 7 */ -static struct _nv fc_len[] = { - { 0x80, "very long distance" }, - { 0x40, "short distance" }, - { 0x20, "intermediate distance" }, - { 0x10, "long distance" }, - { 0x08, "medium distance" }, - { 0, NULL } -}; - -/* Channel/Cable technology, byte 7-8 */ -static struct _nv cab_tech[] = { - { 0x0400, "Shortwave laser (SA)" }, - { 0x0200, "Longwave laser (LC)" }, - { 0x0100, "Electrical inter-enclosure (EL)" }, - { 0x80, "Electrical intra-enclosure (EL)" }, - { 0x40, "Shortwave laser (SN)" }, - { 0x20, "Shortwave laser (SL)" }, - { 0x10, "Longwave laser (LL)" }, - { 0x08, "Active Cable" }, - { 0x04, "Passive Cable" }, - { 0, NULL } -}; - -/* FC Transmission media, byte 9 */ -static struct _nv fc_media[] = { - { 0x80, "Twin Axial Pair" }, - { 0x40, "Twisted Pair" }, - { 0x20, "Miniature Coax" }, - { 0x10, "Viao Coax" }, - { 0x08, "Miltimode, 62.5um" }, - { 0x04, "Multimode, 50um" }, - { 0x02, "" }, - { 0x01, "Single Mode" }, - { 0, NULL } -}; - -/* FC Speed, byte 10 */ -static struct _nv fc_speed[] = { - { 0x80, "1200 MBytes/sec" }, - { 0x40, "800 MBytes/sec" }, - { 0x20, "1600 MBytes/sec" }, - { 0x10, "400 MBytes/sec" }, - { 0x08, "3200 MBytes/sec" }, - { 0x04, "200 MBytes/sec" }, - { 0x01, "100 MBytes/sec" }, - { 0, NULL } -}; - -/* SFF-8436 Rev. 4.8 table 33: Specification compliance */ - -/* 10/40G Ethernet compliance codes, byte 128 + 3 */ -static struct _nv eth_1040g[] = { - { 0x80, "Extended" }, - { 0x40, "10GBASE-LRM" }, - { 0x20, "10GBASE-LR" }, - { 0x10, "10GBASE-SR" }, - { 0x08, "40GBASE-CR4" }, - { 0x04, "40GBASE-SR4" }, - { 0x02, "40GBASE-LR4" }, - { 0x01, "40G Active Cable" }, - { 0, NULL } -}; -#define SFF_8636_EXT_COMPLIANCE 0x80 - -/* SFF-8024 Rev. 4.6 table 4-4: Extended Specification Compliance */ -static struct _nv eth_extended_comp[] = { - { 0xFF, "Reserved" }, - { 0x55, "128GFC LW" }, - { 0x54, "128GFC SW" }, - { 0x53, "128GFC EA" }, - { 0x52, "64GFC LW" }, - { 0x51, "64GFC SW" }, - { 0x50, "64GFC EA" }, - { 0x4F, "Reserved" }, - { 0x4E, "Reserved" }, - { 0x4D, "Reserved" }, - { 0x4C, "Reserved" }, - { 0x4B, "Reserved" }, - { 0x4A, "Reserved" }, - { 0x49, "Reserved" }, - { 0x48, "Reserved" }, - { 0x47, "Reserved" }, - { 0x46, "200GBASE-LR4" }, - { 0x45, "50GBASE-LR" }, - { 0x44, "200G 1550nm PSM4" }, - { 0x43, "200GBASE-FR4" }, - { 0x42, "50GBASE-FR or 200GBASE-DR4" }, - { 0x41, "50GBASE-SR/100GBASE-SR2/200GBASE-SR4" }, - { 0x40, "50GBASE-CR/100GBASE-CR2/200GBASE-CR4" }, - { 0x3F, "Reserved" }, - { 0x3E, "Reserved" }, - { 0x3D, "Reserved" }, - { 0x3C, "Reserved" }, - { 0x3B, "Reserved" }, - { 0x3A, "Reserved" }, - { 0x39, "Reserved" }, - { 0x38, "Reserved" }, - { 0x37, "Reserved" }, - { 0x36, "Reserved" }, - { 0x35, "Reserved" }, - { 0x34, "Reserved" }, - { 0x33, "50GAUI/100GAUI-2/200GAUI-4 AOC (BER <2.6e-4)" }, - { 0x32, "50GAUI/100GAUI-2/200GAUI-4 ACC (BER <2.6e-4)" }, - { 0x31, "50GAUI/100GAUI-2/200GAUI-4 AOC (BER <1e-6)" }, - { 0x30, "50GAUI/100GAUI-2/200GAUI-4 ACC (BER <1e-6)" }, - { 0x2F, "Reserved" }, - { 0x2E, "Reserved" }, - { 0x2D, "Reserved" }, - { 0x2C, "Reserved" }, - { 0x2B, "Reserved" }, - { 0x2A, "Reserved" }, - { 0x29, "Reserved" }, - { 0x28, "Reserved" }, - { 0x27, "100G-LR" }, - { 0x26, "100G-FR" }, - { 0x25, "100GBASE-DR" }, - { 0x24, "4WDM-40 MSA" }, - { 0x23, "4WDM-20 MSA" }, - { 0x22, "4WDM-10 MSA" }, - { 0x21, "100G PAM4 BiDi" }, - { 0x20, "100G SWDM4" }, - { 0x1F, "40G SWDM4" }, - { 0x1E, "2.5GBASE-T" }, - { 0x1D, "5GBASE-T" }, - { 0x1C, "10GBASE-T Short Reach" }, - { 0x1B, "100G 1550nm WDM" }, - { 0x1A, "100GE-DWDM2" }, - { 0x19, "100G ACC or 25GAUI C2M ACC" }, - { 0x18, "100G AOC or 25GAUI C2M AOC" }, - { 0x17, "100G CLR4" }, - { 0x16, "10GBASE-T with SFI electrical interface" }, - { 0x15, "G959.1 profile P1L1-2D2" }, - { 0x14, "G959.1 profile P1S1-2D2" }, - { 0x13, "G959.1 profile P1I1-2D1" }, - { 0x12, "40G PSM4 Parallel SMF" }, - { 0x11, "4 x 10GBASE-SR" }, - { 0x10, "40GBASE-ER4" }, - { 0x0F, "Reserved" }, - { 0x0E, "Reserved" }, - { 0x0D, "25GBASE-CR CA-25G-N" }, - { 0x0C, "25GBASE-CR CA-25G-S" }, - { 0x0B, "100GBASE-CR4 or 25GBASE-CR CA-L" }, - { 0x0A, "Reserved" }, - { 0x09, "Obsolete" }, - { 0x08, "100G ACC (Active Copper Cable) or 25GAUI C2M ACC" }, - { 0x07, "100G PSM4 Parallel SMF" }, - { 0x06, "100G CWDM4" }, - { 0x05, "100GBASE-SR10" }, - { 0x04, "100GBASE-ER4 or 25GBASE-ER" }, - { 0x03, "100GBASE-LR4 or 25GBASE-LR" }, - { 0x02, "100GBASE-SR4 or 25GBASE-SR" }, - { 0x01, "100G AOC (Active Optical Cable) or 25GAUI C2M AOC" }, - { 0x00, "Unspecified" } -}; - -/* SFF-8636 Rev. 2.9 table 6.3: Revision compliance */ -static struct _nv rev_compl[] = { - { 0x1, "SFF-8436 rev <=4.8" }, - { 0x2, "SFF-8436 rev <=4.8" }, - { 0x3, "SFF-8636 rev <=1.3" }, - { 0x4, "SFF-8636 rev <=1.4" }, - { 0x5, "SFF-8636 rev <=1.5" }, - { 0x6, "SFF-8636 rev <=2.0" }, - { 0x7, "SFF-8636 rev <=2.7" }, - { 0x8, "SFF-8636 rev >=2.8" }, - { 0x0, "Unspecified" } -}; - -const char * -find_value(struct _nv *x, int value) -{ - for (; x->n != NULL; x++) - if (x->v == value) - return (x->n); - return (NULL); -} - -const char * -find_zero_bit(struct _nv *x, int value, int sz) -{ - int v, m; - const char *s; - - v = 1; - for (v = 1, m = 1 << (8 * sz); v < m; v *= 2) { - if ((value & v) == 0) - continue; - if ((s = find_value(x, value & v)) != NULL) { - value &= ~v; - return (s); - } - } - - return (NULL); -} - -static void -convert_sff_identifier(char *buf, size_t size, uint8_t value) -{ - const char *x; - - x = NULL; - if (value <= SFF_8024_ID_LAST) - x = sff_8024_id[value]; - else { - if (value > 0x80) - x = "Vendor specific"; - else - x = "Reserved"; - } - - snprintf(buf, size, "%s", x); -} - -static void -convert_sff_connector(char *buf, size_t size, uint8_t value) -{ - const char *x; - - if ((x = find_value(conn, value)) == NULL) { - if (value >= 0x0D && value <= 0x1F) - x = "Unallocated"; - else if (value >= 0x24 && value <= 0x7F) - x = "Unallocated"; - else - x = "Vendor specific"; - } - - snprintf(buf, size, "%s", x); -} - -static void -convert_sff_rev_compliance(char *buf, size_t size, uint8_t value) -{ - const char *x; - - if (value > 0x07) - x = "Unallocated"; - else - x = find_value(rev_compl, value); - - snprintf(buf, size, "%s", x); -} - -static void -get_sfp_identifier(struct i2c_info *ii, char *buf, size_t size) -{ - uint8_t data; - - read_i2c(ii, SFF_8472_BASE, SFF_8472_ID, 1, &data); - convert_sff_identifier(buf, size, data); -} - -static void -get_sfp_connector(struct i2c_info *ii, char *buf, size_t size) -{ - uint8_t data; - - read_i2c(ii, SFF_8472_BASE, SFF_8472_CONNECTOR, 1, &data); - convert_sff_connector(buf, size, data); -} - -static void -get_qsfp_identifier(struct i2c_info *ii, char *buf, size_t size) -{ - uint8_t data; - - read_i2c(ii, SFF_8436_BASE, SFF_8436_ID, 1, &data); - convert_sff_identifier(buf, size, data); -} - -static void -get_qsfp_connector(struct i2c_info *ii, char *buf, size_t size) -{ - uint8_t data; - - read_i2c(ii, SFF_8436_BASE, SFF_8436_CONNECTOR, 1, &data); - convert_sff_connector(buf, size, data); -} - -static void -printf_sfp_transceiver_descr(struct i2c_info *ii, char *buf, size_t size) -{ - char xbuf[12]; - const char *tech_class, *tech_len, *tech_tech, *tech_media, *tech_speed; - - tech_class = NULL; - tech_len = NULL; - tech_tech = NULL; - tech_media = NULL; - tech_speed = NULL; - - /* Read bytes 3-10 at once */ - read_i2c(ii, SFF_8472_BASE, SFF_8472_TRANS_START, 8, &xbuf[3]); - - /* Check 10G ethernet first */ - tech_class = find_zero_bit(eth_10g, xbuf[3], 1); - if (tech_class == NULL) { - /* No match. Try 1G */ - tech_class = find_zero_bit(eth_compat, xbuf[6], 1); - } - - tech_len = find_zero_bit(fc_len, xbuf[7], 1); - tech_tech = find_zero_bit(cab_tech, xbuf[7] << 8 | xbuf[8], 2); - tech_media = find_zero_bit(fc_media, xbuf[9], 1); - tech_speed = find_zero_bit(fc_speed, xbuf[10], 1); - - printf("Class: %s\n", tech_class); - printf("Length: %s\n", tech_len); - printf("Tech: %s\n", tech_tech); - printf("Media: %s\n", tech_media); - printf("Speed: %s\n", tech_speed); -} - -static void -get_sfp_transceiver_class(struct i2c_info *ii, char *buf, size_t size) -{ - const char *tech_class; - uint8_t code; - - /* Use extended compliance code if it's valid */ - read_i2c(ii, SFF_8472_BASE, SFF_8472_TRANS, 1, &code); - if (code != 0) - tech_class = find_value(eth_extended_comp, code); - else { - /* Next, check 10G Ethernet/IB CCs */ - read_i2c(ii, SFF_8472_BASE, SFF_8472_TRANS_START, 1, &code); - tech_class = find_zero_bit(eth_10g, code, 1); - if (tech_class == NULL) { - /* No match. Try Ethernet 1G */ - read_i2c(ii, SFF_8472_BASE, SFF_8472_TRANS_START + 3, - 1, (caddr_t)&code); - tech_class = find_zero_bit(eth_compat, code, 1); - } - } - - if (tech_class == NULL) - tech_class = "Unknown"; - - snprintf(buf, size, "%s", tech_class); -} - -static void -get_qsfp_transceiver_class(struct i2c_info *ii, char *buf, size_t size) -{ - const char *tech_class; - uint8_t code; - - read_i2c(ii, SFF_8436_BASE, SFF_8436_CODE_E1040100G, 1, &code); - - /* Check for extended specification compliance */ - if (code & SFF_8636_EXT_COMPLIANCE) { - read_i2c(ii, SFF_8436_BASE, SFF_8436_OPTIONS_START, 1, &code); - tech_class = find_value(eth_extended_comp, code); - } else - /* Check 10/40G Ethernet class only */ - tech_class = find_zero_bit(eth_1040g, code, 1); - - if (tech_class == NULL) - tech_class = "Unknown"; - - snprintf(buf, size, "%s", tech_class); -} - -/* - * Print SFF-8472/SFF-8436 string to supplied buffer. - * All (vendor-specific) strings are padded right with '0x20'. - */ -static void -convert_sff_name(char *buf, size_t size, char *xbuf) -{ - char *p; - - for (p = &xbuf[16]; *(p - 1) == 0x20; p--) - ; - *p = '\0'; - snprintf(buf, size, "%s", xbuf); -} - -static void -convert_sff_date(char *buf, size_t size, char *xbuf) -{ - - snprintf(buf, size, "20%c%c-%c%c-%c%c", xbuf[0], xbuf[1], - xbuf[2], xbuf[3], xbuf[4], xbuf[5]); -} - -static void -get_sfp_vendor_name(struct i2c_info *ii, char *buf, size_t size) -{ - char xbuf[17]; - - memset(xbuf, 0, sizeof(xbuf)); - read_i2c(ii, SFF_8472_BASE, SFF_8472_VENDOR_START, 16, (uint8_t *)xbuf); - convert_sff_name(buf, size, xbuf); -} - -static void -get_sfp_vendor_pn(struct i2c_info *ii, char *buf, size_t size) -{ - char xbuf[17]; - - memset(xbuf, 0, sizeof(xbuf)); - read_i2c(ii, SFF_8472_BASE, SFF_8472_PN_START, 16, (uint8_t *)xbuf); - convert_sff_name(buf, size, xbuf); -} - -static void -get_sfp_vendor_sn(struct i2c_info *ii, char *buf, size_t size) -{ - char xbuf[17]; - - memset(xbuf, 0, sizeof(xbuf)); - read_i2c(ii, SFF_8472_BASE, SFF_8472_SN_START, 16, (uint8_t *)xbuf); - convert_sff_name(buf, size, xbuf); -} - -static void -get_sfp_vendor_date(struct i2c_info *ii, char *buf, size_t size) -{ - char xbuf[6]; - - memset(xbuf, 0, sizeof(xbuf)); - /* Date code, see Table 3.8 for description */ - read_i2c(ii, SFF_8472_BASE, SFF_8472_DATE_START, 6, (uint8_t *)xbuf); - convert_sff_date(buf, size, xbuf); -} - -static void -get_qsfp_vendor_name(struct i2c_info *ii, char *buf, size_t size) -{ - char xbuf[17]; - - memset(xbuf, 0, sizeof(xbuf)); - read_i2c(ii, SFF_8436_BASE, SFF_8436_VENDOR_START, 16, (uint8_t *)xbuf); - convert_sff_name(buf, size, xbuf); -} - -static void -get_qsfp_vendor_pn(struct i2c_info *ii, char *buf, size_t size) -{ - char xbuf[17]; - - memset(xbuf, 0, sizeof(xbuf)); - read_i2c(ii, SFF_8436_BASE, SFF_8436_PN_START, 16, (uint8_t *)xbuf); - convert_sff_name(buf, size, xbuf); -} - -static void -get_qsfp_vendor_sn(struct i2c_info *ii, char *buf, size_t size) -{ - char xbuf[17]; - - memset(xbuf, 0, sizeof(xbuf)); - read_i2c(ii, SFF_8436_BASE, SFF_8436_SN_START, 16, (uint8_t *)xbuf); - convert_sff_name(buf, size, xbuf); -} - -static void -get_qsfp_vendor_date(struct i2c_info *ii, char *buf, size_t size) -{ - char xbuf[6]; - - memset(xbuf, 0, sizeof(xbuf)); - read_i2c(ii, SFF_8436_BASE, SFF_8436_DATE_START, 6, (uint8_t *)xbuf); - convert_sff_date(buf, size, xbuf); -} - -static void -print_sfp_vendor(struct i2c_info *ii, char *buf, size_t size) -{ - char xbuf[80]; - - memset(xbuf, 0, sizeof(xbuf)); - if (ii->qsfp != 0) { - get_qsfp_vendor_name(ii, xbuf, 20); - get_qsfp_vendor_pn(ii, &xbuf[20], 20); - get_qsfp_vendor_sn(ii, &xbuf[40], 20); - get_qsfp_vendor_date(ii, &xbuf[60], 20); - } else { - get_sfp_vendor_name(ii, xbuf, 20); - get_sfp_vendor_pn(ii, &xbuf[20], 20); - get_sfp_vendor_sn(ii, &xbuf[40], 20); - get_sfp_vendor_date(ii, &xbuf[60], 20); - } - - snprintf(buf, size, "vendor: %s PN: %s SN: %s DATE: %s", - xbuf, &xbuf[20], &xbuf[40], &xbuf[60]); -} - -/* - * Converts internal templerature (SFF-8472, SFF-8436) - * 16-bit unsigned value to human-readable representation: - * - * Internally measured Module temperature are represented - * as a 16-bit signed twos complement value in increments of - * 1/256 degrees Celsius, yielding a total range of –128C to +128C - * that is considered valid between –40 and +125C. - * - */ -static void -convert_sff_temp(char *buf, size_t size, uint8_t *xbuf) -{ - double d; - - d = (double)xbuf[0]; - d += (double)xbuf[1] / 256; - - snprintf(buf, size, "%.2f C", d); -} - -/* - * Retrieves supplied voltage (SFF-8472, SFF-8436). - * 16-bit usigned value, treated as range 0..+6.55 Volts - */ -static void -convert_sff_voltage(char *buf, size_t size, uint8_t *xbuf) -{ - double d; - - d = (double)((xbuf[0] << 8) | xbuf[1]); - snprintf(buf, size, "%.2f Volts", d / 10000); -} - -/* - * Converts value in @xbuf to both milliwats and dBm - * human representation. - */ -static void -convert_sff_power(struct i2c_info *ii, char *buf, size_t size, uint8_t *xbuf) -{ - uint16_t mW; - double dbm; - - mW = (xbuf[0] << 8) + xbuf[1]; - - /* Convert mw to dbm */ - dbm = 10.0 * log10(1.0 * mW / 10000); - - /* - * Assume internally-calibrated data. - * This is always true for SFF-8346, and explicitly - * checked for SFF-8472. - */ - - /* Table 3.9, bit 5 is set, internally calibrated */ - snprintf(buf, size, "%d.%02d mW (%.2f dBm)", - mW / 10000, (mW % 10000) / 100, dbm); -} - -static void -get_sfp_temp(struct i2c_info *ii, char *buf, size_t size) -{ - uint8_t xbuf[2]; - - memset(xbuf, 0, sizeof(xbuf)); - read_i2c(ii, SFF_8472_DIAG, SFF_8472_TEMP, 2, xbuf); - convert_sff_temp(buf, size, xbuf); -} - -static void -get_sfp_voltage(struct i2c_info *ii, char *buf, size_t size) -{ - uint8_t xbuf[2]; - - memset(xbuf, 0, sizeof(xbuf)); - read_i2c(ii, SFF_8472_DIAG, SFF_8472_VCC, 2, xbuf); - convert_sff_voltage(buf, size, xbuf); -} - -static int -get_qsfp_temp(struct i2c_info *ii, char *buf, size_t size) -{ - uint8_t xbuf[2]; - - memset(xbuf, 0, sizeof(xbuf)); - read_i2c(ii, SFF_8436_BASE, SFF_8436_TEMP, 2, xbuf); - if ((xbuf[0] == 0xFF && xbuf[1] == 0xFF) || (xbuf[0] == 0 && xbuf[1] == 0)) - return (-1); - convert_sff_temp(buf, size, xbuf); - return (0); -} - -static void -get_qsfp_voltage(struct i2c_info *ii, char *buf, size_t size) -{ - uint8_t xbuf[2]; - - memset(xbuf, 0, sizeof(xbuf)); - read_i2c(ii, SFF_8436_BASE, SFF_8436_VCC, 2, xbuf); - convert_sff_voltage(buf, size, xbuf); -} - -static void -get_sfp_rx_power(struct i2c_info *ii, char *buf, size_t size) -{ - uint8_t xbuf[2]; - - memset(xbuf, 0, sizeof(xbuf)); - read_i2c(ii, SFF_8472_DIAG, SFF_8472_RX_POWER, 2, xbuf); - convert_sff_power(ii, buf, size, xbuf); -} - -static void -get_sfp_tx_power(struct i2c_info *ii, char *buf, size_t size) -{ - uint8_t xbuf[2]; - - memset(xbuf, 0, sizeof(xbuf)); - read_i2c(ii, SFF_8472_DIAG, SFF_8472_TX_POWER, 2, xbuf); - convert_sff_power(ii, buf, size, xbuf); -} - -static void -get_qsfp_rx_power(struct i2c_info *ii, char *buf, size_t size, int chan) -{ - uint8_t xbuf[2]; - - memset(xbuf, 0, sizeof(xbuf)); - read_i2c(ii, SFF_8436_BASE, SFF_8436_RX_CH1_MSB + (chan-1)*2, 2, xbuf); - convert_sff_power(ii, buf, size, xbuf); -} - -static void -get_qsfp_tx_power(struct i2c_info *ii, char *buf, size_t size, int chan) -{ - uint8_t xbuf[2]; - - memset(xbuf, 0, sizeof(xbuf)); - read_i2c(ii, SFF_8436_BASE, SFF_8436_TX_CH1_MSB + (chan-1)*2, 2, xbuf); - convert_sff_power(ii, buf, size, xbuf); -} - -static void -get_qsfp_rev_compliance(struct i2c_info *ii, char *buf, size_t size) -{ - uint8_t xbuf; - - xbuf = 0; - read_i2c(ii, SFF_8436_BASE, SFF_8436_STATUS, 1, &xbuf); - convert_sff_rev_compliance(buf, size, xbuf); -} - -static uint32_t -get_qsfp_br(struct i2c_info *ii) -{ - uint8_t xbuf; - uint32_t rate; - - xbuf = 0; - read_i2c(ii, SFF_8436_BASE, SFF_8436_BITRATE, 1, &xbuf); - rate = xbuf * 100; - if (xbuf == 0xFF) { - read_i2c(ii, SFF_8436_BASE, SFF_8636_BITRATE, 1, &xbuf); - rate = xbuf * 250; - } - - return (rate); -} - -/* - * Reads i2c data from opened kernel socket. - */ -static int -read_i2c(struct i2c_info *ii, uint8_t addr, uint8_t off, uint8_t len, - uint8_t *buf) -{ - struct ifi2creq req; - int i, l; - - if (ii->error != 0) - return (ii->error); - - ii->ifr->ifr_data = (caddr_t)&req; - - i = 0; - l = 0; - memset(&req, 0, sizeof(req)); - req.dev_addr = addr; - req.offset = off; - req.len = len; - - while (len > 0) { - l = MIN(sizeof(req.data), len); - req.len = l; - if (ioctl(ii->fd, SIOCGI2C, ii->ifr) != 0) { - ii->error = errno; - return (errno); - } - - memcpy(&buf[i], req.data, l); - len -= l; - i += l; - req.offset += l; - } - - return (0); -} - -static void -dump_i2c_data(struct i2c_info *ii, uint8_t addr, uint8_t off, uint8_t len) -{ - unsigned char buf[16]; - int i, read; - - while (len > 0) { - memset(buf, 0, sizeof(buf)); - read = MIN(sizeof(buf), len); - read_i2c(ii, addr, off, read, buf); - if (ii->error != 0) { - fprintf(stderr, "Error reading i2c info\n"); - return; - } - - printf("\t"); - for (i = 0; i < read; i++) - printf("%02X ", buf[i]); - printf("\n"); - len -= read; - off += read; - } -} - -static void -print_qsfp_status(struct i2c_info *ii, int verbose) -{ - char buf[80], buf2[40], buf3[40]; - uint32_t bitrate; - int i; - - ii->qsfp = 1; - - /* Transceiver type */ - get_qsfp_identifier(ii, buf, sizeof(buf)); - get_qsfp_transceiver_class(ii, buf2, sizeof(buf2)); - get_qsfp_connector(ii, buf3, sizeof(buf3)); - if (ii->error == 0) - printf("\tplugged: %s %s (%s)\n", buf, buf2, buf3); - print_sfp_vendor(ii, buf, sizeof(buf)); - if (ii->error == 0) - printf("\t%s\n", buf); - - if (verbose > 1) { - get_qsfp_rev_compliance(ii, buf, sizeof(buf)); - if (ii->error == 0) - printf("\tcompliance level: %s\n", buf); - - bitrate = get_qsfp_br(ii); - if (ii->error == 0 && bitrate > 0) - printf("\tnominal bitrate: %u Mbps\n", bitrate); - } - - /* - * The standards in this area are not clear when the - * additional measurements are present or not. Use a valid - * temperature reading as an indicator for the presence of - * voltage and TX/RX power measurements. - */ - if (get_qsfp_temp(ii, buf, sizeof(buf)) == 0) { - get_qsfp_voltage(ii, buf2, sizeof(buf2)); - printf("\tmodule temperature: %s voltage: %s\n", buf, buf2); - for (i = 1; i <= 4; i++) { - get_qsfp_rx_power(ii, buf, sizeof(buf), i); - get_qsfp_tx_power(ii, buf2, sizeof(buf2), i); - printf("\tlane %d: RX: %s TX: %s\n", i, buf, buf2); - } - } - - if (verbose > 2) { - printf("\n\tSFF8436 DUMP (0xA0 128..255 range):\n"); - dump_i2c_data(ii, SFF_8436_BASE, 128, 128); - printf("\n\tSFF8436 DUMP (0xA0 0..81 range):\n"); - dump_i2c_data(ii, SFF_8436_BASE, 0, 82); - } -} - -static void -print_sfp_status(struct i2c_info *ii, int verbose) -{ - char buf[80], buf2[40], buf3[40]; - uint8_t diag_type, flags; - - /* Read diagnostic monitoring type */ - read_i2c(ii, SFF_8472_BASE, SFF_8472_DIAG_TYPE, 1, (caddr_t)&diag_type); - if (ii->error != 0) - return; - - /* - * Read monitoring data IFF it is supplied AND is - * internally calibrated - */ - flags = SFF_8472_DDM_DONE | SFF_8472_DDM_INTERNAL; - if ((diag_type & flags) == flags) - ii->do_diag = 1; - - /* Transceiver type */ - get_sfp_identifier(ii, buf, sizeof(buf)); - get_sfp_transceiver_class(ii, buf2, sizeof(buf2)); - get_sfp_connector(ii, buf3, sizeof(buf3)); - if (ii->error == 0) - printf("\tplugged: %s %s (%s)\n", buf, buf2, buf3); - print_sfp_vendor(ii, buf, sizeof(buf)); - if (ii->error == 0) - printf("\t%s\n", buf); - - if (verbose > 5) - printf_sfp_transceiver_descr(ii, buf, sizeof(buf)); - /* - * Request current measurements iff they are provided: - */ - if (ii->do_diag != 0) { - get_sfp_temp(ii, buf, sizeof(buf)); - get_sfp_voltage(ii, buf2, sizeof(buf2)); - printf("\tmodule temperature: %s Voltage: %s\n", buf, buf2); - get_sfp_rx_power(ii, buf, sizeof(buf)); - get_sfp_tx_power(ii, buf2, sizeof(buf2)); - printf("\tRX: %s TX: %s\n", buf, buf2); - } - - if (verbose > 2) { - printf("\n\tSFF8472 DUMP (0xA0 0..127 range):\n"); - dump_i2c_data(ii, SFF_8472_BASE, 0, 128); - } -} - void sfp_status(int s, struct ifreq *ifr, int verbose) { - struct i2c_info ii; - uint8_t id_byte; + struct ifconfig_sfp_info info; + struct ifconfig_sfp_info_strings strings; + struct ifconfig_sfp_vendor_info vendor_info; + struct ifconfig_sfp_status status; + ifconfig_handle_t *lifh; + const char *name; + size_t channel_count; - /* Prepare necessary into pass to i2c reader */ - memset(&ii, 0, sizeof(ii)); - ii.fd = s; - ii.ifr = ifr; - - /* - * Try to read byte 0 from i2c: - * Both SFF-8472 and SFF-8436 use it as - * 'identification byte'. - * Stop reading status on zero as value - - * this might happen in case of empty transceiver slot. - */ - id_byte = 0; - read_i2c(&ii, SFF_8472_BASE, SFF_8472_ID, 1, (caddr_t)&id_byte); - if (ii.error != 0 || id_byte == 0) + lifh = ifconfig_open(); + if (lifh == NULL) return; - switch (id_byte) { - case SFF_8024_ID_QSFP: - case SFF_8024_ID_QSFPPLUS: - case SFF_8024_ID_QSFP28: - print_qsfp_status(&ii, verbose); - break; - default: - print_sfp_status(&ii, verbose); - } -} + name = ifr->ifr_name; + if (ifconfig_sfp_get_sfp_info(lifh, name, &info) == -1) + goto close; + + ifconfig_sfp_get_sfp_info_strings(&info, &strings); + + printf("\tplugged: %s %s (%s)\n", + ifconfig_sfp_id_display(info.sfp_id), + ifconfig_sfp_physical_spec(&info, &strings), + strings.sfp_conn); + + if (ifconfig_sfp_get_sfp_vendor_info(lifh, name, &vendor_info) == -1) + goto close; + + printf("\tvendor: %s PN: %s SN: %s DATE: %s\n", + vendor_info.name, vendor_info.pn, vendor_info.sn, vendor_info.date); + + if (ifconfig_sfp_id_is_qsfp(info.sfp_id)) { + if (verbose > 1) + printf("\tcompliance level: %s\n", strings.sfp_rev); + } else { + if (verbose > 5) { + printf("Class: %s\n", + ifconfig_sfp_physical_spec(&info, &strings)); + printf("Length: %s\n", strings.sfp_fc_len); + printf("Tech: %s\n", strings.sfp_cab_tech); + printf("Media: %s\n", strings.sfp_fc_media); + printf("Speed: %s\n", strings.sfp_fc_speed); + } + } + + if (ifconfig_sfp_get_sfp_status(lifh, name, &status) == 0) { + if (ifconfig_sfp_id_is_qsfp(info.sfp_id) && verbose > 1) + printf("\tnominal bitrate: %u Mbps\n", status.bitrate); + printf("\tmodule temperature: %.2f C voltage: %.2f Volts\n", + status.temp, status.voltage); + channel_count = ifconfig_sfp_channel_count(&info); + for (size_t chan = 0; chan < channel_count; ++chan) { + uint16_t rx = status.channel[chan].rx; + uint16_t tx = status.channel[chan].tx; + printf("\tlane %zu: " + "RX power: %.2f mW (%.2f dBm) TX bias: %.2f mA\n", + chan + 1, power_mW(rx), power_dBm(rx), bias_mA(tx)); + } + ifconfig_sfp_free_sfp_status(&status); + } + + if (verbose > 2) { + struct ifconfig_sfp_dump dump; + + if (ifconfig_sfp_get_sfp_dump(lifh, name, &dump) == -1) + goto close; + + if (ifconfig_sfp_id_is_qsfp(info.sfp_id)) { + printf("\n\tSFF8436 DUMP (0xA0 128..255 range):\n"); + hexdump(dump.data + QSFP_DUMP1_START, QSFP_DUMP1_SIZE, + "\t", HD_OMIT_COUNT | HD_OMIT_CHARS); + printf("\n\tSFF8436 DUMP (0xA0 0..81 range):\n"); + hexdump(dump.data + QSFP_DUMP0_START, QSFP_DUMP0_SIZE, + "\t", HD_OMIT_COUNT | HD_OMIT_CHARS); + } else { + printf("\n\tSFF8472 DUMP (0xA0 0..127 range):\n"); + hexdump(dump.data + SFP_DUMP_START, SFP_DUMP_SIZE, + "\t", HD_OMIT_COUNT | HD_OMIT_CHARS); + } + } + +close: + ifconfig_close(lifh); +} diff --git a/tools/lua/template.lua b/tools/lua/template.lua new file mode 100644 index 00000000000..3662953b0f2 --- /dev/null +++ b/tools/lua/template.lua @@ -0,0 +1,652 @@ +-- From lua-resty-template (modified to remove external dependencies) +--[[ +Copyright (c) 2014 - 2020 Aapo Talvensaari +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, this + list of conditions and the following disclaimer in the documentation and/or + other materials provided with the distribution. + +* Neither the name of the {organization} nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +]]-- +-- $FreeBSD$ + +local setmetatable = setmetatable +local loadstring = loadstring +local tostring = tostring +local setfenv = setfenv +local require = require +local concat = table.concat +local assert = assert +local write = io.write +local pcall = pcall +local phase +local open = io.open +local load = load +local type = type +local dump = string.dump +local find = string.find +local gsub = string.gsub +local byte = string.byte +local null +local sub = string.sub +local var + +local _VERSION = _VERSION +local _ENV = _ENV -- luacheck: globals _ENV +local _G = _G + +local HTML_ENTITIES = { + ["&"] = "&", + ["<"] = "<", + [">"] = ">", + ['"'] = """, + ["'"] = "'", + ["/"] = "/" +} + +local CODE_ENTITIES = { + ["{"] = "{", + ["}"] = "}", + ["&"] = "&", + ["<"] = "<", + [">"] = ">", + ['"'] = """, + ["'"] = "'", + ["/"] = "/" +} + +local VAR_PHASES + +local ESC = byte("\27") +local NUL = byte("\0") +local HT = byte("\t") +local VT = byte("\v") +local LF = byte("\n") +local SOL = byte("/") +local BSOL = byte("\\") +local SP = byte(" ") +local AST = byte("*") +local NUM = byte("#") +local LPAR = byte("(") +local LSQB = byte("[") +local LCUB = byte("{") +local MINUS = byte("-") +local PERCNT = byte("%") + +local EMPTY = "" + +local VIEW_ENV +if _VERSION == "Lua 5.1" then + VIEW_ENV = { __index = function(t, k) + return t.context[k] or t.template[k] or _G[k] + end } +else + VIEW_ENV = { __index = function(t, k) + return t.context[k] or t.template[k] or _ENV[k] + end } +end + +local newtab +do + local ok + ok, newtab = pcall(require, "table.new") + if not ok then newtab = function() return {} end end +end + +local function enabled(val) + if val == nil then return true end + return val == true or (val == "1" or val == "true" or val == "on") +end + +local function trim(s) + return gsub(gsub(s, "^%s+", EMPTY), "%s+$", EMPTY) +end + +local function rpos(view, s) + while s > 0 do + local c = byte(view, s, s) + if c == SP or c == HT or c == VT or c == NUL then + s = s - 1 + else + break + end + end + return s +end + +local function escaped(view, s) + if s > 1 and byte(view, s - 1, s - 1) == BSOL then + if s > 2 and byte(view, s - 2, s - 2) == BSOL then + return false, 1 + else + return true, 1 + end + end + return false, 0 +end + +local function read_file(path) + local file, err = open(path, "rb") + if not file then return nil, err end + local content + content, err = file:read "*a" + file:close() + return content, err +end + +local function load_view(template) + return function(view, plain) + if plain == true then return view end + local path, root = view, template.root + if root and root ~= EMPTY then + if byte(root, -1) == SOL then root = sub(root, 1, -2) end + if byte(view, 1) == SOL then path = sub(view, 2) end + path = root .. "/" .. path + end + return plain == false and assert(read_file(path)) or read_file(path) or view + end +end + +local function load_file(func) + return function(view) return func(view, false) end +end + +local function load_string(func) + return function(view) return func(view, true) end +end + +local function loader(template) + return function(view) + return assert(load(view, nil, nil, setmetatable({ template = template }, VIEW_ENV))) + end +end + +local function visit(visitors, content, tag, name) + if not visitors then + return content + end + + for i = 1, visitors.n do + content = visitors[i](content, tag, name) + end + + return content +end + +local function new(template, safe) + template = template or newtab(0, 26) + + template._VERSION = "2.0" + template.cache = {} + template.load = load_view(template) + template.load_file = load_file(template.load) + template.load_string = load_string(template.load) + template.print = write + + local load_chunk = loader(template) + + local caching + if VAR_PHASES and VAR_PHASES[phase()] then + caching = enabled(var.template_cache) + else + caching = true + end + + local visitors + function template.visit(func) + if not visitors then + visitors = { func, n = 1 } + return + end + visitors.n = visitors.n + 1 + visitors[visitors.n] = func + end + + function template.caching(enable) + if enable ~= nil then caching = enable == true end + return caching + end + + function template.output(s) + if s == nil or s == null then return EMPTY end + if type(s) == "function" then return template.output(s()) end + return tostring(s) + end + + function template.escape(s, c) + if type(s) == "string" then + if c then return gsub(s, "[}{\">/<'&]", CODE_ENTITIES) end + return gsub(s, "[\">/<'&]", HTML_ENTITIES) + end + return template.output(s) + end + + function template.new(view, layout) + local vt = type(view) + + if vt == "boolean" then return new(nil, view) end + if vt == "table" then return new(view, safe) end + if vt == "nil" then return new(nil, safe) end + + local render + local process + if layout then + if type(layout) == "table" then + render = function(self, context) + context = context or self + context.blocks = context.blocks or {} + context.view = template.process(view, context) + layout.blocks = context.blocks or {} + layout.view = context.view or EMPTY + layout:render() + end + process = function(self, context) + context = context or self + context.blocks = context.blocks or {} + context.view = template.process(view, context) + layout.blocks = context.blocks or {} + layout.view = context.view + return tostring(layout) + end + else + render = function(self, context) + context = context or self + context.blocks = context.blocks or {} + context.view = template.process(view, context) + template.render(layout, context) + end + process = function(self, context) + context = context or self + context.blocks = context.blocks or {} + context.view = template.process(view, context) + return template.process(layout, context) + end + end + else + render = function(self, context) + return template.render(view, context or self) + end + process = function(self, context) + return template.process(view, context or self) + end + end + + if safe then + return setmetatable({ + render = function(...) + local ok, err = pcall(render, ...) + if not ok then + return nil, err + end + end, + process = function(...) + local ok, output = pcall(process, ...) + if not ok then + return nil, output + end + return output + end, + }, { + __tostring = function(...) + local ok, output = pcall(process, ...) + if not ok then + return "" + end + return output + end }) + end + + return setmetatable({ + render = render, + process = process + }, { + __tostring = process + }) + end + + function template.precompile(view, path, strip, plain) + local chunk = dump(template.compile(view, nil, plain), strip ~= false) + if path then + local file = open(path, "wb") + file:write(chunk) + file:close() + end + return chunk + end + + function template.precompile_string(view, path, strip) + return template.precompile(view, path, strip, true) + end + + function template.precompile_file(view, path, strip) + return template.precompile(view, path, strip, false) + end + + function template.compile(view, cache_key, plain) + assert(view, "view was not provided for template.compile(view, cache_key, plain)") + if cache_key == "no-cache" then + return load_chunk(template.parse(view, plain)), false + end + cache_key = cache_key or view + local cache = template.cache + if cache[cache_key] then return cache[cache_key], true end + local func = load_chunk(template.parse(view, plain)) + if caching then cache[cache_key] = func end + return func, false + end + + function template.compile_file(view, cache_key) + return template.compile(view, cache_key, false) + end + + function template.compile_string(view, cache_key) + return template.compile(view, cache_key, true) + end + + function template.parse(view, plain) + assert(view, "view was not provided for template.parse(view, plain)") + if plain ~= true then + view = template.load(view, plain) + if byte(view, 1, 1) == ESC then return view end + end + local j = 2 + local c = {[[ +context=... or {} +local ___,blocks,layout={},blocks or {} +local function include(v, c) return template.process(v, c or context) end +local function echo(...) for i=1,select("#", ...) do ___[#___+1] = tostring(select(i, ...)) end end +]] } + local i, s = 1, find(view, "{", 1, true) + while s do + local t, p = byte(view, s + 1, s + 1), s + 2 + if t == LCUB then + local e = find(view, "}}", p, true) + if e then + local z, w = escaped(view, s) + if i < s - w then + c[j] = "___[#___+1]=[=[\n" + c[j+1] = visit(visitors, sub(view, i, s - 1 - w)) + c[j+2] = "]=]\n" + j=j+3 + end + if z then + i = s + else + c[j] = "___[#___+1]=template.escape(" + c[j+1] = visit(visitors, trim(sub(view, p, e - 1)), "{") + c[j+2] = ")\n" + j=j+3 + s, i = e + 1, e + 2 + end + end + elseif t == AST then + local e = find(view, "*}", p, true) + if e then + local z, w = escaped(view, s) + if i < s - w then + c[j] = "___[#___+1]=[=[\n" + c[j+1] = visit(visitors, sub(view, i, s - 1 - w)) + c[j+2] = "]=]\n" + j=j+3 + end + if z then + i = s + else + c[j] = "___[#___+1]=template.output(" + c[j+1] = visit(visitors, trim(sub(view, p, e - 1)), "*") + c[j+2] = ")\n" + j=j+3 + s, i = e + 1, e + 2 + end + end + elseif t == PERCNT then + local e = find(view, "%}", p, true) + if e then + local z, w = escaped(view, s) + if z then + if i < s - w then + c[j] = "___[#___+1]=[=[\n" + c[j+1] = visit(visitors, sub(view, i, s - 1 - w)) + c[j+2] = "]=]\n" + j=j+3 + end + i = s + else + local n = e + 2 + if byte(view, n, n) == LF then + n = n + 1 + end + local r = rpos(view, s - 1) + if i <= r then + c[j] = "___[#___+1]=[=[\n" + c[j+1] = visit(visitors, sub(view, i, r)) + c[j+2] = "]=]\n" + j=j+3 + end + c[j] = visit(visitors, trim(sub(view, p, e - 1)), "%") + c[j+1] = "\n" + j=j+2 + s, i = n - 1, n + end + end + elseif t == LPAR then + local e = find(view, ")}", p, true) + if e then + local z, w = escaped(view, s) + if i < s - w then + c[j] = "___[#___+1]=[=[\n" + c[j+1] = visit(visitors, sub(view, i, s - 1 - w)) + c[j+2] = "]=]\n" + j=j+3 + end + if z then + i = s + else + local f = visit(visitors, sub(view, p, e - 1), "(") + local x = find(f, ",", 2, true) + if x then + c[j] = "___[#___+1]=include([=[" + c[j+1] = trim(sub(f, 1, x - 1)) + c[j+2] = "]=]," + c[j+3] = trim(sub(f, x + 1)) + c[j+4] = ")\n" + j=j+5 + else + c[j] = "___[#___+1]=include([=[" + c[j+1] = trim(f) + c[j+2] = "]=])\n" + j=j+3 + end + s, i = e + 1, e + 2 + end + end + elseif t == LSQB then + local e = find(view, "]}", p, true) + if e then + local z, w = escaped(view, s) + if i < s - w then + c[j] = "___[#___+1]=[=[\n" + c[j+1] = visit(visitors, sub(view, i, s - 1 - w)) + c[j+2] = "]=]\n" + j=j+3 + end + if z then + i = s + else + c[j] = "___[#___+1]=include(" + c[j+1] = visit(visitors, trim(sub(view, p, e - 1)), "[") + c[j+2] = ")\n" + j=j+3 + s, i = e + 1, e + 2 + end + end + elseif t == MINUS then + local e = find(view, "-}", p, true) + if e then + local x, y = find(view, sub(view, s, e + 1), e + 2, true) + if x then + local z, w = escaped(view, s) + if z then + if i < s - w then + c[j] = "___[#___+1]=[=[\n" + c[j+1] = visit(visitors, sub(view, i, s - 1 - w)) + c[j+2] = "]=]\n" + j=j+3 + end + i = s + else + y = y + 1 + x = x - 1 + if byte(view, y, y) == LF then + y = y + 1 + end + local b = trim(sub(view, p, e - 1)) + if b == "verbatim" or b == "raw" then + if i < s - w then + c[j] = "___[#___+1]=[=[\n" + c[j+1] = visit(visitors, sub(view, i, s - 1 - w)) + c[j+2] = "]=]\n" + j=j+3 + end + c[j] = "___[#___+1]=[=[" + c[j+1] = visit(visitors, sub(view, e + 2, x)) + c[j+2] = "]=]\n" + j=j+3 + else + if byte(view, x, x) == LF then + x = x - 1 + end + local r = rpos(view, s - 1) + if i <= r then + c[j] = "___[#___+1]=[=[\n" + c[j+1] = visit(visitors, sub(view, i, r)) + c[j+2] = "]=]\n" + j=j+3 + end + c[j] = 'blocks["' + c[j+1] = b + c[j+2] = '"]=include[=[' + c[j+3] = visit(visitors, sub(view, e + 2, x), "-", b) + c[j+4] = "]=]\n" + j=j+5 + end + s, i = y - 1, y + end + end + end + elseif t == NUM then + local e = find(view, "#}", p, true) + if e then + local z, w = escaped(view, s) + if i < s - w then + c[j] = "___[#___+1]=[=[\n" + c[j+1] = visit(visitors, sub(view, i, s - 1 - w)) + c[j+2] = "]=]\n" + j=j+3 + end + if z then + i = s + else + e = e + 2 + if byte(view, e, e) == LF then + e = e + 1 + end + s, i = e - 1, e + end + end + end + s = find(view, "{", s + 1, true) + end + s = sub(view, i) + if s and s ~= EMPTY then + c[j] = "___[#___+1]=[=[\n" + c[j+1] = visit(visitors, s) + c[j+2] = "]=]\n" + j=j+3 + end + c[j] = "return layout and include(layout,setmetatable({view=table.concat(___),blocks=blocks},{__index=context})) or table.concat(___)" -- luacheck: ignore + return concat(c) + end + + function template.parse_file(view) + return template.parse(view, false) + end + + function template.parse_string(view) + return template.parse(view, true) + end + + function template.process(view, context, cache_key, plain) + assert(view, "view was not provided for template.process(view, context, cache_key, plain)") + return template.compile(view, cache_key, plain)(context) + end + + function template.process_file(view, context, cache_key) + assert(view, "view was not provided for template.process_file(view, context, cache_key)") + return template.compile(view, cache_key, false)(context) + end + + function template.process_string(view, context, cache_key) + assert(view, "view was not provided for template.process_string(view, context, cache_key)") + return template.compile(view, cache_key, true)(context) + end + + function template.render(view, context, cache_key, plain) + assert(view, "view was not provided for template.render(view, context, cache_key, plain)") + template.print(template.process(view, context, cache_key, plain)) + end + + function template.render_file(view, context, cache_key) + assert(view, "view was not provided for template.render_file(view, context, cache_key)") + template.render(view, context, cache_key, false) + end + + function template.render_string(view, context, cache_key) + assert(view, "view was not provided for template.render_string(view, context, cache_key)") + template.render(view, context, cache_key, true) + end + + if safe then + return setmetatable({}, { + __index = function(_, k) + if type(template[k]) == "function" then + return function(...) + local ok, a, b = pcall(template[k], ...) + if not ok then + return nil, a + end + return a, b + end + end + return template[k] + end, + __new_index = function(_, k, v) + template[k] = v + end, + }) + end + + return template +end + +return new() From f8024c39154c7b433c7bc631588f2bd7b5143a07 Mon Sep 17 00:00:00 2001 From: Alexey Dokuchaev Date: Mon, 10 Aug 2020 09:03:29 +0000 Subject: [PATCH 07/25] Document the order in which the kernel and the user environment versions are printed when both -K and -U options are passed on the command line. Approved by: 0mp Differential Revision: https://reviews.freebsd.org/D25970 --- usr.bin/uname/uname.1 | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/usr.bin/uname/uname.1 b/usr.bin/uname/uname.1 index 7bc7e7ab151..c5676c0d6f9 100644 --- a/usr.bin/uname/uname.1 +++ b/usr.bin/uname/uname.1 @@ -28,7 +28,7 @@ .\" @(#)uname.1 8.3 (Berkeley) 4/8/94 .\" $FreeBSD$ .\" -.Dd June 27, 2019 +.Dd August 10, 2020 .Dt UNAME 1 .Os .Sh NAME @@ -107,6 +107,9 @@ and flags are intended to be used for fine grain differentiation of incremental .Fx development and user visible changes. +Note that when both of these two options are specified, regardless of their +order, the kernel version would be printed first, followed by the user +environment version. .Sh ENVIRONMENT An environment variable composed of the string .Ev UNAME_ From fc9fcee01a1798efd08cc16249504ddb9171e138 Mon Sep 17 00:00:00 2001 From: Mateusz Guzik Date: Mon, 10 Aug 2020 10:31:17 +0000 Subject: [PATCH 08/25] nullfs: add missing VOP_STAT handling Tested by: pho --- sys/fs/nullfs/null_vnops.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/sys/fs/nullfs/null_vnops.c b/sys/fs/nullfs/null_vnops.c index 4dd555a18db..60fd2a2c366 100644 --- a/sys/fs/nullfs/null_vnops.c +++ b/sys/fs/nullfs/null_vnops.c @@ -182,6 +182,7 @@ #include #include #include +#include #include @@ -484,8 +485,20 @@ null_setattr(struct vop_setattr_args *ap) } /* - * We handle getattr only to change the fsid. + * We handle stat and getattr only to change the fsid. */ +static int +null_stat(struct vop_stat_args *ap) +{ + int error; + + if ((error = null_bypass((struct vop_generic_args *)ap)) != 0) + return (error); + + ap->a_sb->st_dev = ap->a_vp->v_mount->mnt_stat.f_fsid.val[0]; + return (0); +} + static int null_getattr(struct vop_getattr_args *ap) { @@ -918,6 +931,7 @@ struct vop_vector null_vnodeops = { .vop_accessx = null_accessx, .vop_advlockpurge = vop_stdadvlockpurge, .vop_bmap = VOP_EOPNOTSUPP, + .vop_stat = null_stat, .vop_getattr = null_getattr, .vop_getwritemount = null_getwritemount, .vop_inactive = null_inactive, From 7f70080150fc4576ea744ccdcc2e60bb14261b66 Mon Sep 17 00:00:00 2001 From: Mateusz Guzik Date: Mon, 10 Aug 2020 10:33:40 +0000 Subject: [PATCH 09/25] vfs: disallow NOCACHE with LOOKUP This means there is no expectation lookup will purge the terminal entry, which simplifies lockless lookup. Tested by: pho Sponsored by: The FreeBSD Foundation --- sys/kern/vfs_lookup.c | 3 +++ sys/kern/vfs_subr.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/sys/kern/vfs_lookup.c b/sys/kern/vfs_lookup.c index 54ae088f4b7..e78fc25ec34 100644 --- a/sys/kern/vfs_lookup.c +++ b/sys/kern/vfs_lookup.c @@ -484,6 +484,9 @@ namei(struct nameidata *ndp) ("namei: nameiop contaminated with flags")); KASSERT((cnp->cn_flags & OPMASK) == 0, ("namei: flags contaminated with nameiops")); + if (cnp->cn_flags & NOCACHE) + KASSERT(cnp->cn_nameiop != LOOKUP, + ("%s: NOCACHE passed with LOOKUP", __func__)); MPASS(ndp->ni_startdir == NULL || ndp->ni_startdir->v_type == VDIR || ndp->ni_startdir->v_type == VBAD); diff --git a/sys/kern/vfs_subr.c b/sys/kern/vfs_subr.c index fb245cb6e3d..4df6be9ace7 100644 --- a/sys/kern/vfs_subr.c +++ b/sys/kern/vfs_subr.c @@ -408,7 +408,7 @@ sysctl_try_reclaim_vnode(SYSCTL_HANDLER_ARGS) buf[req->newlen] = '\0'; - ndflags = LOCKLEAF | NOFOLLOW | AUDITVNODE1 | NOCACHE | SAVENAME; + ndflags = LOCKLEAF | NOFOLLOW | AUDITVNODE1 | SAVENAME; NDINIT(&nd, LOOKUP, ndflags, UIO_SYSSPACE, buf, curthread); if ((error = namei(&nd)) != 0) goto out; From 21d5af2b30d17b4425f729c69a647b6dba6364b7 Mon Sep 17 00:00:00 2001 From: Mateusz Guzik Date: Mon, 10 Aug 2020 10:34:22 +0000 Subject: [PATCH 10/25] vfs: drop the thread argumemnt from vfs_fplookup_vexec It is guaranteed curthread. Tested by: pho Sponsored by: The FreeBSD Foundation --- sys/kern/vfs_cache.c | 2 +- sys/kern/vnode_if.src | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/sys/kern/vfs_cache.c b/sys/kern/vfs_cache.c index 670262ab1fe..deb22bdc8d4 100644 --- a/sys/kern/vfs_cache.c +++ b/sys/kern/vfs_cache.c @@ -3938,7 +3938,7 @@ cache_fplookup_impl(struct vnode *dvp, struct cache_fpl *fpl) VNPASS(cache_fplookup_vnode_supported(fpl->dvp), fpl->dvp); - error = VOP_FPLOOKUP_VEXEC(fpl->dvp, cnp->cn_cred, cnp->cn_thread); + error = VOP_FPLOOKUP_VEXEC(fpl->dvp, cnp->cn_cred); if (__predict_false(error != 0)) { error = cache_fplookup_failed_vexec(fpl, error); break; diff --git a/sys/kern/vnode_if.src b/sys/kern/vnode_if.src index 10bca613606..07e20e6f81c 100644 --- a/sys/kern/vnode_if.src +++ b/sys/kern/vnode_if.src @@ -153,7 +153,6 @@ vop_close { vop_fplookup_vexec { IN struct vnode *vp; IN struct ucred *cred; - IN struct thread *td; }; From 3ba0e51703ab63459136855e3f8ea3d6d4e9f298 Mon Sep 17 00:00:00 2001 From: Mateusz Guzik Date: Mon, 10 Aug 2020 10:35:18 +0000 Subject: [PATCH 11/25] vfs: partially support file create/delete/rename in lockless lookup Perform the lookup until the last 2 elements and fallback to slowpath. Tested by: pho Sponsored by: The FreeBSD Foundation --- sys/kern/vfs_cache.c | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/sys/kern/vfs_cache.c b/sys/kern/vfs_cache.c index deb22bdc8d4..1c51c4907ab 100644 --- a/sys/kern/vfs_cache.c +++ b/sys/kern/vfs_cache.c @@ -3143,8 +3143,8 @@ cache_fpl_handled_impl(struct cache_fpl *fpl, int error, int line) #define cache_fpl_handled(x, e) cache_fpl_handled_impl((x), (e), __LINE__) #define CACHE_FPL_SUPPORTED_CN_FLAGS \ - (LOCKLEAF | LOCKPARENT | WANTPARENT | FOLLOW | LOCKSHARED | SAVENAME | \ - ISOPEN | NOMACCHECK | AUDITVNODE1 | AUDITVNODE2) + (LOCKLEAF | LOCKPARENT | WANTPARENT | NOCACHE | FOLLOW | LOCKSHARED | SAVENAME | \ + WILLBEDIR | ISOPEN | NOMACCHECK | AUDITVNODE1 | AUDITVNODE2) #define CACHE_FPL_INTERNAL_CN_FLAGS \ (ISDOTDOT | MAKEENTRY | ISLASTCN) @@ -3194,10 +3194,6 @@ cache_can_fplookup(struct cache_fpl *fpl) cache_fpl_aborted(fpl); return (false); } - if (cnp->cn_nameiop != LOOKUP) { - cache_fpl_aborted(fpl); - return (false); - } if (ndp->ni_dirfd != AT_FDCWD) { cache_fpl_aborted(fpl); return (false); @@ -3407,6 +3403,22 @@ cache_fplookup_final_child(struct cache_fpl *fpl, enum vgetstate tvs) return (cache_fpl_handled(fpl, 0)); } +/* + * They want to possibly modify the state of the namecache. + * + * Don't try to match the API contract, just leave. + * TODO: this leaves scalability on the table + */ +static int +cache_fplookup_final_modifying(struct cache_fpl *fpl) +{ + struct componentname *cnp; + + cnp = fpl->cnp; + MPASS(cnp->cn_nameiop != LOOKUP); + return (cache_fpl_partial(fpl)); +} + static int __noinline cache_fplookup_final_withparent(struct cache_fpl *fpl) { @@ -3489,6 +3501,10 @@ cache_fplookup_final(struct cache_fpl *fpl) VNPASS(cache_fplookup_vnode_supported(dvp), dvp); + if (cnp->cn_nameiop != LOOKUP) { + return (cache_fplookup_final_modifying(fpl)); + } + if ((cnp->cn_flags & (LOCKPARENT|WANTPARENT)) != 0) return (cache_fplookup_final_withparent(fpl)); @@ -3633,6 +3649,12 @@ cache_fplookup_next(struct cache_fpl *fpl) tvp = atomic_load_ptr(&ncp->nc_vp); nc_flag = atomic_load_char(&ncp->nc_flag); if ((nc_flag & NCF_NEGATIVE) != 0) { + /* + * If they want to create an entry we need to replace this one. + */ + if (__predict_false(fpl->cnp->cn_nameiop == CREATE)) { + return (cache_fpl_partial(fpl)); + } negstate = NCP2NEGSTATE(ncp); neg_hot = ((negstate->neg_flag & NEG_HOT) != 0); if (__predict_false(!cache_ncp_canuse(ncp))) { From f9c13ab856573b95d27b4349b456de513c2d11af Mon Sep 17 00:00:00 2001 From: Mateusz Guzik Date: Mon, 10 Aug 2020 10:35:47 +0000 Subject: [PATCH 12/25] devfs: use vget_prep/vget_finish Tested by: pho --- sys/fs/devfs/devfs_vnops.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/sys/fs/devfs/devfs_vnops.c b/sys/fs/devfs/devfs_vnops.c index 0605fad13cf..22054056ec0 100644 --- a/sys/fs/devfs/devfs_vnops.c +++ b/sys/fs/devfs/devfs_vnops.c @@ -420,6 +420,7 @@ devfs_allocv(struct devfs_dirent *de, struct mount *mp, int lockmode, struct cdev *dev; struct devfs_mount *dmp; struct cdevsw *dsw; + enum vgetstate vs; dmp = VFSTODEVFS(mp); if (de->de_flags & DE_DOOMED) { @@ -432,10 +433,10 @@ devfs_allocv(struct devfs_dirent *de, struct mount *mp, int lockmode, mtx_lock(&devfs_de_interlock); vp = de->de_vnode; if (vp != NULL) { - VI_LOCK(vp); + vs = vget_prep(vp); mtx_unlock(&devfs_de_interlock); sx_xunlock(&dmp->dm_lock); - vget(vp, lockmode | LK_INTERLOCK | LK_RETRY, curthread); + vget_finish(vp, lockmode | LK_RETRY, vs); sx_xlock(&dmp->dm_lock); if (devfs_allocv_drop_refs(0, dmp, de)) { vput(vp); @@ -1492,13 +1493,14 @@ devfs_revoke(struct vop_revoke_args *ap) struct cdev *dev; struct cdev_priv *cdp; struct devfs_dirent *de; + enum vgetstate vs; u_int i; KASSERT((ap->a_flags & REVOKEALL) != 0, ("devfs_revoke !REVOKEALL")); dev = vp->v_rdev; cdp = cdev2priv(dev); - + dev_lock(); cdp->cdp_inuse++; dev_unlock(); @@ -1521,17 +1523,16 @@ devfs_revoke(struct vop_revoke_args *ap) vp2 = de->de_vnode; if (vp2 != NULL) { dev_unlock(); - VI_LOCK(vp2); + vs = vget_prep(vp2); mtx_unlock(&devfs_de_interlock); - if (vget(vp2, LK_EXCLUSIVE | LK_INTERLOCK, - curthread)) + if (vget_finish(vp2, LK_EXCLUSIVE, vs) != 0) goto loop; vhold(vp2); vgone(vp2); vdrop(vp2); vput(vp2); break; - } + } } if (vp2 != NULL) { continue; From f8935a96d1bc34839cf10ea1ddc45afbdab513d4 Mon Sep 17 00:00:00 2001 From: Mateusz Guzik Date: Mon, 10 Aug 2020 10:36:10 +0000 Subject: [PATCH 13/25] devfs: use cheaper lockmgr entry points Tested by: pho --- sys/fs/devfs/devfs_vnops.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sys/fs/devfs/devfs_vnops.c b/sys/fs/devfs/devfs_vnops.c index 22054056ec0..5bd12708b0d 100644 --- a/sys/fs/devfs/devfs_vnops.c +++ b/sys/fs/devfs/devfs_vnops.c @@ -1928,6 +1928,9 @@ static struct vop_vector devfs_vnodeops = { #endif .vop_symlink = devfs_symlink, .vop_vptocnp = devfs_vptocnp, + .vop_lock1 = vop_lock, + .vop_unlock = vop_unlock, + .vop_islocked = vop_islocked, }; VFS_VOP_VECTOR_REGISTER(devfs_vnodeops); @@ -1966,6 +1969,9 @@ static struct vop_vector devfs_specops = { .vop_symlink = VOP_PANIC, .vop_vptocnp = devfs_vptocnp, .vop_write = dead_write, + .vop_lock1 = vop_lock, + .vop_unlock = vop_unlock, + .vop_islocked = vop_islocked, }; VFS_VOP_VECTOR_REGISTER(devfs_specops); From 7b19bddac8f6ae5a356ffbca80f087d3654b8363 Mon Sep 17 00:00:00 2001 From: Mateusz Guzik Date: Mon, 10 Aug 2020 10:36:43 +0000 Subject: [PATCH 14/25] devfs: save on spurious relocking for devfs_populate Tested by: pho --- sys/fs/devfs/devfs.h | 1 + sys/fs/devfs/devfs_devs.c | 11 +++++++++-- sys/fs/devfs/devfs_vnops.c | 6 ++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/sys/fs/devfs/devfs.h b/sys/fs/devfs/devfs.h index 5f64a267279..121d6eb6f0b 100644 --- a/sys/fs/devfs/devfs.h +++ b/sys/fs/devfs/devfs.h @@ -192,6 +192,7 @@ char *devfs_fqpn(char *, struct devfs_mount *, struct devfs_dirent *, struct componentname *); void devfs_delete(struct devfs_mount *, struct devfs_dirent *, int); void devfs_dirent_free(struct devfs_dirent *); +bool devfs_populate_needed(struct devfs_mount *dm); void devfs_populate(struct devfs_mount *); void devfs_cleanup(struct devfs_mount *); void devfs_unmount_final(struct devfs_mount *); diff --git a/sys/fs/devfs/devfs_devs.c b/sys/fs/devfs/devfs_devs.c index 417e13e2757..5df21a3712d 100644 --- a/sys/fs/devfs/devfs_devs.c +++ b/sys/fs/devfs/devfs_devs.c @@ -659,6 +659,13 @@ devfs_populate_loop(struct devfs_mount *dm, int cleanup) return (0); } +bool +devfs_populate_needed(struct devfs_mount *dm) +{ + + return (dm->dm_generation != devfs_generation); +} + /* * The caller needs to hold the dm for the duration of the call. */ @@ -668,9 +675,9 @@ devfs_populate(struct devfs_mount *dm) unsigned gen; sx_assert(&dm->dm_lock, SX_XLOCKED); - gen = devfs_generation; - if (dm->dm_generation == gen) + if (!devfs_populate_needed(dm)) return; + gen = devfs_generation; while (devfs_populate_loop(dm, 0)) continue; dm->dm_generation = gen; diff --git a/sys/fs/devfs/devfs_vnops.c b/sys/fs/devfs/devfs_vnops.c index 5bd12708b0d..f9e29e0b1c7 100644 --- a/sys/fs/devfs/devfs_vnops.c +++ b/sys/fs/devfs/devfs_vnops.c @@ -235,6 +235,11 @@ devfs_populate_vp(struct vnode *vp) ASSERT_VOP_LOCKED(vp, "devfs_populate_vp"); dmp = VFSTODEVFS(vp->v_mount); + if (!devfs_populate_needed(dmp)) { + sx_xlock(&dmp->dm_lock); + goto out_nopopulate; + } + locked = VOP_ISLOCKED(vp); sx_xlock(&dmp->dm_lock); @@ -252,6 +257,7 @@ devfs_populate_vp(struct vnode *vp) devfs_unmount_final(dmp); return (ERESTART); } +out_nopopulate: if (VN_IS_DOOMED(vp)) { sx_xunlock(&dmp->dm_lock); return (ERESTART); From 7416a2d6df47c4bc24a745131461f29482884829 Mon Sep 17 00:00:00 2001 From: Mateusz Guzik Date: Mon, 10 Aug 2020 10:37:16 +0000 Subject: [PATCH 15/25] vfs: garbage collect unused ISUNICODE namei flag --- sys/sys/namei.h | 1 - 1 file changed, 1 deletion(-) diff --git a/sys/sys/namei.h b/sys/sys/namei.h index 0f49ef68454..784eb79311c 100644 --- a/sys/sys/namei.h +++ b/sys/sys/namei.h @@ -159,7 +159,6 @@ int cache_fplookup(struct nameidata *ndp, enum cache_fpl_status *status, #define ISWHITEOUT 0x00020000 /* found whiteout */ #define DOWHITEOUT 0x00040000 /* do whiteouts */ #define WILLBEDIR 0x00080000 /* new files will be dirs; allow trailing / */ -#define ISUNICODE 0x00100000 /* current component name is unicode*/ #define ISOPEN 0x00200000 /* caller is opening; return a real vnode. */ #define NOCROSSMOUNT 0x00400000 /* do not cross mount points */ #define NOMACCHECK 0x00800000 /* do not perform MAC checks */ From c571b99545b713bf8119502ec80f49bcae69596f Mon Sep 17 00:00:00 2001 From: Mateusz Guzik Date: Mon, 10 Aug 2020 10:40:14 +0000 Subject: [PATCH 16/25] cache: strlcpy -> memcpy --- sys/kern/vfs_cache.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sys/kern/vfs_cache.c b/sys/kern/vfs_cache.c index 1c51c4907ab..e6718a9994f 100644 --- a/sys/kern/vfs_cache.c +++ b/sys/kern/vfs_cache.c @@ -1934,7 +1934,8 @@ cache_enter_time(struct vnode *dvp, struct vnode *vp, struct componentname *cnp, } len = ncp->nc_nlen = cnp->cn_namelen; hash = cache_get_hash(cnp->cn_nameptr, len, dvp); - strlcpy(ncp->nc_name, cnp->cn_nameptr, len + 1); + memcpy(ncp->nc_name, cnp->cn_nameptr, len); + ncp->nc_name[len] = '\0'; cache_enter_lock(&cel, dvp, vp, hash); /* From a95ef9d38db52f87c9c55aeb005771fad479da46 Mon Sep 17 00:00:00 2001 From: Hans Petter Selasky Date: Mon, 10 Aug 2020 10:40:19 +0000 Subject: [PATCH 17/25] Use proper prototype for SYSINIT() functions. Mark the unused argument using the __unused macro. Discussed with: kib@ MFC after: 1 week Sponsored by: Mellanox Technologies --- sys/netinet/in_mcast.c | 2 +- sys/netinet6/in6_mcast.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sys/netinet/in_mcast.c b/sys/netinet/in_mcast.c index 39fc82c5372..0b59dd8aeb7 100644 --- a/sys/netinet/in_mcast.c +++ b/sys/netinet/in_mcast.c @@ -229,7 +229,7 @@ static struct in_multi_head inm_free_list = SLIST_HEAD_INITIALIZER(); static void inm_release_task(void *arg __unused, int pending __unused); static void -inm_init(void) +inm_init(void *arg __unused) { TASK_INIT(&free_task, 0, inm_release_task, NULL); } diff --git a/sys/netinet6/in6_mcast.c b/sys/netinet6/in6_mcast.c index 5332aeac99e..f291548d176 100644 --- a/sys/netinet6/in6_mcast.c +++ b/sys/netinet6/in6_mcast.c @@ -521,7 +521,7 @@ static struct in6_multi_head in6m_free_list = SLIST_HEAD_INITIALIZER(); static void in6m_release_task(void *arg __unused, int pending __unused); static void -in6m_init(void) +in6m_init(void *arg __unused) { TASK_INIT(&in6m_free_task, 0, in6m_release_task, NULL); } From 3689652c65cd86ce7cc7c175f5d3ced142b72a84 Mon Sep 17 00:00:00 2001 From: Hans Petter Selasky Date: Mon, 10 Aug 2020 10:46:08 +0000 Subject: [PATCH 18/25] Make sure the multicast release tasks are properly drained when destroying a VNET or a network interface. Else the inm release tasks, both IPv4 and IPv6 may cause a panic accessing a freed VNET or network interface. Reviewed by: jmg@ Discussed with: bz@ Differential Revision: https://reviews.freebsd.org/D24914 MFC after: 1 week Sponsored by: Mellanox Technologies --- sys/netinet/in.c | 7 +++++++ sys/netinet/in_mcast.c | 25 ++++++++++++++++++++++--- sys/netinet/in_var.h | 1 + sys/netinet6/in6_ifattach.c | 2 +- sys/netinet6/in6_mcast.c | 10 +++++++++- sys/netinet6/in6_var.h | 2 +- 6 files changed, 41 insertions(+), 6 deletions(-) diff --git a/sys/netinet/in.c b/sys/netinet/in.c index fb44766fc61..4f4e47916f6 100644 --- a/sys/netinet/in.c +++ b/sys/netinet/in.c @@ -998,6 +998,13 @@ in_ifdetach(struct ifnet *ifp) in_pcbpurgeif0(&V_ulitecbinfo, ifp); in_purgemaddrs(ifp); IN_MULTI_UNLOCK(); + + /* + * Make sure all multicast deletions invoking if_ioctl() are + * completed before returning. Else we risk accessing a freed + * ifnet structure pointer. + */ + inm_release_wait(NULL); } /* diff --git a/sys/netinet/in_mcast.c b/sys/netinet/in_mcast.c index 0b59dd8aeb7..2cc56896780 100644 --- a/sys/netinet/in_mcast.c +++ b/sys/netinet/in_mcast.c @@ -224,17 +224,36 @@ inm_is_ifp_detached(const struct in_multi *inm) } #endif -static struct task free_task; +/* + * Interface detach can happen in a taskqueue thread context, so we must use a + * dedicated thread to avoid deadlocks when draining inm_release tasks. + */ +TASKQUEUE_DEFINE_THREAD(inm_free); +static struct task inm_free_task; static struct in_multi_head inm_free_list = SLIST_HEAD_INITIALIZER(); static void inm_release_task(void *arg __unused, int pending __unused); static void inm_init(void *arg __unused) { - TASK_INIT(&free_task, 0, inm_release_task, NULL); + TASK_INIT(&inm_free_task, 0, inm_release_task, NULL); } SYSINIT(inm_init, SI_SUB_TASKQ, SI_ORDER_ANY, inm_init, NULL); +void +inm_release_wait(void *arg __unused) +{ + + /* + * Make sure all pending multicast addresses are freed before + * the VNET or network device is destroyed: + */ + taskqueue_drain(taskqueue_inm_free, &inm_free_task); +} +#ifdef VIMAGE +VNET_SYSUNINIT(inm_release_wait, SI_SUB_PROTO_DOMAIN, SI_ORDER_FIRST, inm_release_wait, NULL); +#endif + void inm_release_list_deferred(struct in_multi_head *inmh) { @@ -244,7 +263,7 @@ inm_release_list_deferred(struct in_multi_head *inmh) mtx_lock(&in_multi_free_mtx); SLIST_CONCAT(&inm_free_list, inmh, in_multi, inm_nrele); mtx_unlock(&in_multi_free_mtx); - taskqueue_enqueue(taskqueue_thread, &free_task); + taskqueue_enqueue(taskqueue_inm_free, &inm_free_task); } void diff --git a/sys/netinet/in_var.h b/sys/netinet/in_var.h index eeeba62af36..9babe5d053d 100644 --- a/sys/netinet/in_var.h +++ b/sys/netinet/in_var.h @@ -450,6 +450,7 @@ void inm_print(const struct in_multi *); int inm_record_source(struct in_multi *inm, const in_addr_t); void inm_release_deferred(struct in_multi *); void inm_release_list_deferred(struct in_multi_head *); +void inm_release_wait(void *); struct in_multi * in_addmulti(struct in_addr *, struct ifnet *); int in_joingroup(struct ifnet *, const struct in_addr *, diff --git a/sys/netinet6/in6_ifattach.c b/sys/netinet6/in6_ifattach.c index 91ef544d8b2..81cd24823f1 100644 --- a/sys/netinet6/in6_ifattach.c +++ b/sys/netinet6/in6_ifattach.c @@ -871,7 +871,7 @@ in6_purgemaddrs(struct ifnet *ifp) * completed before returning. Else we risk accessing a freed * ifnet structure pointer. */ - in6m_release_wait(); + in6m_release_wait(NULL); } void diff --git a/sys/netinet6/in6_mcast.c b/sys/netinet6/in6_mcast.c index f291548d176..4c462a9e912 100644 --- a/sys/netinet6/in6_mcast.c +++ b/sys/netinet6/in6_mcast.c @@ -539,10 +539,18 @@ in6m_release_list_deferred(struct in6_multi_head *inmh) } void -in6m_release_wait(void) +in6m_release_wait(void *arg __unused) { + + /* + * Make sure all pending multicast addresses are freed before + * the VNET or network device is destroyed: + */ taskqueue_drain_all(taskqueue_in6m_free); } +#ifdef VIMAGE +VNET_SYSUNINIT(in6m_release_wait, SI_SUB_PROTO_DOMAIN, SI_ORDER_FIRST, in6m_release_wait, NULL); +#endif void in6m_disconnect_locked(struct in6_multi_head *inmh, struct in6_multi *inm) diff --git a/sys/netinet6/in6_var.h b/sys/netinet6/in6_var.h index b94e52cac7c..7381ff68064 100644 --- a/sys/netinet6/in6_var.h +++ b/sys/netinet6/in6_var.h @@ -870,7 +870,7 @@ void in6m_commit(struct in6_multi *); void in6m_print(const struct in6_multi *); int in6m_record_source(struct in6_multi *, const struct in6_addr *); void in6m_release_list_deferred(struct in6_multi_head *); -void in6m_release_wait(void); +void in6m_release_wait(void *); void ip6_freemoptions(struct ip6_moptions *); int ip6_getmoptions(struct inpcb *, struct sockopt *); int ip6_setmoptions(struct inpcb *, struct sockopt *); From f9461246a27a8685af8504cbb5cd449c80437990 Mon Sep 17 00:00:00 2001 From: "Bjoern A. Zeeb" Date: Mon, 10 Aug 2020 10:58:43 +0000 Subject: [PATCH 19/25] MC: add a note with reference to the discussion and history as-to why we are where we are now. The main thing is to try to get rid of the delayed freeing to avoid blocking on the taskq when shutting down vnets. X-Timeout: if you still see this before 14-RELEASE remove it. --- sys/netinet/in_mcast.c | 1 + sys/netinet6/in6_mcast.c | 1 + 2 files changed, 2 insertions(+) diff --git a/sys/netinet/in_mcast.c b/sys/netinet/in_mcast.c index 2cc56896780..cf624e8a315 100644 --- a/sys/netinet/in_mcast.c +++ b/sys/netinet/in_mcast.c @@ -251,6 +251,7 @@ inm_release_wait(void *arg __unused) taskqueue_drain(taskqueue_inm_free, &inm_free_task); } #ifdef VIMAGE +/* XXX-BZ FIXME, see D24914. */ VNET_SYSUNINIT(inm_release_wait, SI_SUB_PROTO_DOMAIN, SI_ORDER_FIRST, inm_release_wait, NULL); #endif diff --git a/sys/netinet6/in6_mcast.c b/sys/netinet6/in6_mcast.c index 4c462a9e912..2433dc2ee19 100644 --- a/sys/netinet6/in6_mcast.c +++ b/sys/netinet6/in6_mcast.c @@ -549,6 +549,7 @@ in6m_release_wait(void *arg __unused) taskqueue_drain_all(taskqueue_in6m_free); } #ifdef VIMAGE +/* XXX-BZ FIXME, see D24914. */ VNET_SYSUNINIT(in6m_release_wait, SI_SUB_PROTO_DOMAIN, SI_ORDER_FIRST, in6m_release_wait, NULL); #endif From ca423b858bf4800bb22963e8133b72499bb18d24 Mon Sep 17 00:00:00 2001 From: Mateusz Guzik Date: Mon, 10 Aug 2020 11:46:39 +0000 Subject: [PATCH 20/25] devfs: bool -> int Fixes buildworld after r364069 --- sys/fs/devfs/devfs.h | 2 +- sys/fs/devfs/devfs_devs.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/sys/fs/devfs/devfs.h b/sys/fs/devfs/devfs.h index 121d6eb6f0b..673d9499916 100644 --- a/sys/fs/devfs/devfs.h +++ b/sys/fs/devfs/devfs.h @@ -192,7 +192,7 @@ char *devfs_fqpn(char *, struct devfs_mount *, struct devfs_dirent *, struct componentname *); void devfs_delete(struct devfs_mount *, struct devfs_dirent *, int); void devfs_dirent_free(struct devfs_dirent *); -bool devfs_populate_needed(struct devfs_mount *dm); +int devfs_populate_needed(struct devfs_mount *dm); void devfs_populate(struct devfs_mount *); void devfs_cleanup(struct devfs_mount *); void devfs_unmount_final(struct devfs_mount *); diff --git a/sys/fs/devfs/devfs_devs.c b/sys/fs/devfs/devfs_devs.c index 5df21a3712d..3929cc8b1e8 100644 --- a/sys/fs/devfs/devfs_devs.c +++ b/sys/fs/devfs/devfs_devs.c @@ -659,7 +659,7 @@ devfs_populate_loop(struct devfs_mount *dm, int cleanup) return (0); } -bool +int devfs_populate_needed(struct devfs_mount *dm) { From 03337743dbde267cb9a0e5187f98f6e4bfe8a82b Mon Sep 17 00:00:00 2001 From: Mateusz Guzik Date: Mon, 10 Aug 2020 11:51:21 +0000 Subject: [PATCH 21/25] vfs: clean MNTK_FPLOOKUP if MNT_UNION is set Elides checking it during lookup. --- sys/fs/tmpfs/tmpfs_vfsops.c | 9 ++++++++- sys/kern/vfs_cache.c | 2 -- sys/ufs/ffs/ffs_vfsops.c | 2 +- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/sys/fs/tmpfs/tmpfs_vfsops.c b/sys/fs/tmpfs/tmpfs_vfsops.c index fee923e6132..bb2ae154af9 100644 --- a/sys/fs/tmpfs/tmpfs_vfsops.c +++ b/sys/fs/tmpfs/tmpfs_vfsops.c @@ -372,6 +372,13 @@ tmpfs_mount(struct mount *mp) } tmp->tm_nomtime = vfs_getopt(mp->mnt_optnew, "nomtime", NULL, 0) == 0; + MNT_ILOCK(mp); + if ((mp->mnt_flag & MNT_UNION) == 0) { + mp->mnt_kern_flag |= MNTK_FPLOOKUP; + } else { + mp->mnt_kern_flag &= ~MNTK_FPLOOKUP; + } + MNT_IUNLOCK(mp); return (0); } @@ -462,7 +469,7 @@ tmpfs_mount(struct mount *mp) mp->mnt_flag |= MNT_LOCAL; mp->mnt_kern_flag |= MNTK_LOOKUP_SHARED | MNTK_EXTENDED_SHARED | MNTK_TEXT_REFS | MNTK_NOMSYNC; - if (!nonc) + if (!nonc && (mp->mnt_flag & MNT_UNION) == 0) mp->mnt_kern_flag |= MNTK_FPLOOKUP; MNT_IUNLOCK(mp); diff --git a/sys/kern/vfs_cache.c b/sys/kern/vfs_cache.c index e6718a9994f..7d4e721fb76 100644 --- a/sys/kern/vfs_cache.c +++ b/sys/kern/vfs_cache.c @@ -3701,8 +3701,6 @@ cache_fplookup_mp_supported(struct mount *mp) return (false); if ((mp->mnt_kern_flag & MNTK_FPLOOKUP) == 0) return (false); - if ((mp->mnt_flag & MNT_UNION) != 0) - return (false); return (true); } diff --git a/sys/ufs/ffs/ffs_vfsops.c b/sys/ufs/ffs/ffs_vfsops.c index 8c69212d82e..834930b5e99 100644 --- a/sys/ufs/ffs/ffs_vfsops.c +++ b/sys/ufs/ffs/ffs_vfsops.c @@ -805,7 +805,7 @@ ffs_mount(struct mount *mp) */ if ((mp->mnt_kern_flag & MNTK_FPLOOKUP) != 0) panic("MNTK_FPLOOKUP set on mount %p when it should not be", mp); - if ((mp->mnt_flag & (MNT_ACLS | MNT_NFS4ACLS)) == 0) + if ((mp->mnt_flag & (MNT_ACLS | MNT_NFS4ACLS | MNT_UNION)) == 0) mp->mnt_kern_flag |= MNTK_FPLOOKUP; MNT_IUNLOCK(mp); From 8b62cebea70dc2d8387dac207a4bcdf8b0c73448 Mon Sep 17 00:00:00 2001 From: Mateusz Guzik Date: Mon, 10 Aug 2020 11:51:56 +0000 Subject: [PATCH 22/25] cache: remove unused variables from cache_fplookup_parse --- sys/kern/vfs_cache.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/sys/kern/vfs_cache.c b/sys/kern/vfs_cache.c index 7d4e721fb76..0d176ed8fc5 100644 --- a/sys/kern/vfs_cache.c +++ b/sys/kern/vfs_cache.c @@ -3810,8 +3810,6 @@ cache_fplookup_parse(struct cache_fpl *fpl) struct nameidata *ndp; struct componentname *cnp; char *cp; - char *prev_ni_next; /* saved ndp->ni_next */ - size_t prev_ni_pathlen; /* saved ndp->ni_pathlen */ ndp = fpl->ndp; cnp = fpl->cnp; @@ -3831,11 +3829,9 @@ cache_fplookup_parse(struct cache_fpl *fpl) cache_fpl_smr_exit(fpl); return (cache_fpl_handled(fpl, ENAMETOOLONG)); } - prev_ni_pathlen = ndp->ni_pathlen; ndp->ni_pathlen -= cnp->cn_namelen; KASSERT(ndp->ni_pathlen <= PATH_MAX, ("%s: ni_pathlen underflow to %zd\n", __func__, ndp->ni_pathlen)); - prev_ni_next = ndp->ni_next; ndp->ni_next = cp; /* From bb48255cf5eff1c8e2716a13165cde88af185a8a Mon Sep 17 00:00:00 2001 From: Mateusz Guzik Date: Mon, 10 Aug 2020 12:05:55 +0000 Subject: [PATCH 23/25] cache: resize struct namecache to a multiply of alignment For example struct namecache on amd64 is 100 bytes, but it has to occupies 104. Use the extra bytes to support longer names. --- sys/kern/vfs_cache.c | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/sys/kern/vfs_cache.c b/sys/kern/vfs_cache.c index 0d176ed8fc5..eddc6e20716 100644 --- a/sys/kern/vfs_cache.c +++ b/sys/kern/vfs_cache.c @@ -159,6 +159,17 @@ struct namecache_ts { * alignment for everyone. Note this is a nop for 64-bit platforms. */ #define CACHE_ZONE_ALIGNMENT UMA_ALIGNOF(time_t) +#define CACHE_PATH_CUTOFF 39 + +#define CACHE_ZONE_SMALL_SIZE (sizeof(struct namecache) + CACHE_PATH_CUTOFF + 1) +#define CACHE_ZONE_SMALL_TS_SIZE (sizeof(struct namecache_ts) + CACHE_PATH_CUTOFF + 1) +#define CACHE_ZONE_LARGE_SIZE (sizeof(struct namecache) + NAME_MAX + 1) +#define CACHE_ZONE_LARGE_TS_SIZE (sizeof(struct namecache_ts) + NAME_MAX + 1) + +_Static_assert((CACHE_ZONE_SMALL_SIZE % (CACHE_ZONE_ALIGNMENT + 1)) == 0, "bad zone size"); +_Static_assert((CACHE_ZONE_SMALL_TS_SIZE % (CACHE_ZONE_ALIGNMENT + 1)) == 0, "bad zone size"); +_Static_assert((CACHE_ZONE_LARGE_SIZE % (CACHE_ZONE_ALIGNMENT + 1)) == 0, "bad zone size"); +_Static_assert((CACHE_ZONE_LARGE_TS_SIZE % (CACHE_ZONE_ALIGNMENT + 1)) == 0, "bad zone size"); #define nc_vp n_un.nu_vp #define nc_neg n_un.nu_neg @@ -339,8 +350,6 @@ static uma_zone_t __read_mostly cache_zone_small_ts; static uma_zone_t __read_mostly cache_zone_large; static uma_zone_t __read_mostly cache_zone_large_ts; -#define CACHE_PATH_CUTOFF 35 - static struct namecache * cache_alloc(int len, int ts) { @@ -2095,22 +2104,14 @@ nchinit(void *dummy __unused) { u_int i; - cache_zone_small = uma_zcreate("S VFS Cache", - sizeof(struct namecache) + CACHE_PATH_CUTOFF + 1, - NULL, NULL, NULL, NULL, CACHE_ZONE_ALIGNMENT, - UMA_ZONE_ZINIT); - cache_zone_small_ts = uma_zcreate("STS VFS Cache", - sizeof(struct namecache_ts) + CACHE_PATH_CUTOFF + 1, - NULL, NULL, NULL, NULL, CACHE_ZONE_ALIGNMENT, - UMA_ZONE_ZINIT); - cache_zone_large = uma_zcreate("L VFS Cache", - sizeof(struct namecache) + NAME_MAX + 1, - NULL, NULL, NULL, NULL, CACHE_ZONE_ALIGNMENT, - UMA_ZONE_ZINIT); - cache_zone_large_ts = uma_zcreate("LTS VFS Cache", - sizeof(struct namecache_ts) + NAME_MAX + 1, - NULL, NULL, NULL, NULL, CACHE_ZONE_ALIGNMENT, - UMA_ZONE_ZINIT); + cache_zone_small = uma_zcreate("S VFS Cache", CACHE_ZONE_SMALL_SIZE, + NULL, NULL, NULL, NULL, CACHE_ZONE_ALIGNMENT, UMA_ZONE_ZINIT); + cache_zone_small_ts = uma_zcreate("STS VFS Cache", CACHE_ZONE_SMALL_TS_SIZE, + NULL, NULL, NULL, NULL, CACHE_ZONE_ALIGNMENT, UMA_ZONE_ZINIT); + cache_zone_large = uma_zcreate("L VFS Cache", CACHE_ZONE_LARGE_SIZE, + NULL, NULL, NULL, NULL, CACHE_ZONE_ALIGNMENT, UMA_ZONE_ZINIT); + cache_zone_large_ts = uma_zcreate("LTS VFS Cache", CACHE_ZONE_LARGE_TS_SIZE, + NULL, NULL, NULL, NULL, CACHE_ZONE_ALIGNMENT, UMA_ZONE_ZINIT); VFS_SMR_ZONE_SET(cache_zone_small); VFS_SMR_ZONE_SET(cache_zone_small_ts); From 5e79447d6097781bf7a2f2e4c448b6341305ad5e Mon Sep 17 00:00:00 2001 From: Mateusz Guzik Date: Mon, 10 Aug 2020 12:28:56 +0000 Subject: [PATCH 24/25] cache: let SAVESTART passthrough The flag is only passed for non-LOOKUP ops and those fallback to the slowpath. --- sys/kern/vfs_cache.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sys/kern/vfs_cache.c b/sys/kern/vfs_cache.c index eddc6e20716..bc2fc0c5812 100644 --- a/sys/kern/vfs_cache.c +++ b/sys/kern/vfs_cache.c @@ -3146,7 +3146,7 @@ cache_fpl_handled_impl(struct cache_fpl *fpl, int error, int line) #define CACHE_FPL_SUPPORTED_CN_FLAGS \ (LOCKLEAF | LOCKPARENT | WANTPARENT | NOCACHE | FOLLOW | LOCKSHARED | SAVENAME | \ - WILLBEDIR | ISOPEN | NOMACCHECK | AUDITVNODE1 | AUDITVNODE2) + SAVESTART | WILLBEDIR | ISOPEN | NOMACCHECK | AUDITVNODE1 | AUDITVNODE2) #define CACHE_FPL_INTERNAL_CN_FLAGS \ (ISDOTDOT | MAKEENTRY | ISLASTCN) @@ -3654,7 +3654,7 @@ cache_fplookup_next(struct cache_fpl *fpl) /* * If they want to create an entry we need to replace this one. */ - if (__predict_false(fpl->cnp->cn_nameiop == CREATE)) { + if (__predict_false(fpl->cnp->cn_nameiop != LOOKUP)) { return (cache_fpl_partial(fpl)); } negstate = NCP2NEGSTATE(ncp); @@ -4126,6 +4126,9 @@ cache_fplookup(struct nameidata *ndp, enum cache_fpl_status *status, fpl.cnp = &ndp->ni_cnd; MPASS(curthread == fpl.cnp->cn_thread); + if ((fpl.cnp->cn_flags & SAVESTART) != 0) + MPASS(fpl.cnp->cn_nameiop != LOOKUP); + if (!cache_can_fplookup(&fpl)) { SDT_PROBE3(vfs, fplookup, lookup, done, ndp, fpl.line, fpl.status); *status = fpl.status; From a08d04f4e47535b9db81062d020d10b10e9a4e9d Mon Sep 17 00:00:00 2001 From: Dimitry Andric Date: Mon, 10 Aug 2020 16:55:54 +0000 Subject: [PATCH 25/25] Follow-up to r358851 (llvm-project 10.0.0-rc3 import), where I added subdirectories for compiler-rt's internal fuzzer, profile and xray headers, but forgot to add installing those headers themselves. MFC after: 3 days --- lib/libclang_rt/fuzzer/Makefile | 4 ++++ lib/libclang_rt/profile/Makefile | 4 ++++ lib/libclang_rt/xray/Makefile | 6 ++++++ 3 files changed, 14 insertions(+) diff --git a/lib/libclang_rt/fuzzer/Makefile b/lib/libclang_rt/fuzzer/Makefile index eb675063ddf..91172f4b17c 100644 --- a/lib/libclang_rt/fuzzer/Makefile +++ b/lib/libclang_rt/fuzzer/Makefile @@ -23,4 +23,8 @@ SRCS+= fuzzer/FuzzerUtil.cpp SRCS+= fuzzer/FuzzerUtilLinux.cpp SRCS+= fuzzer/FuzzerUtilPosix.cpp +.PATH: ${CRTSRC}/include/fuzzer +INCSDIR= ${CLANGDIR}/include/fuzzer +INCS+= FuzzedDataProvider.h + .include diff --git a/lib/libclang_rt/profile/Makefile b/lib/libclang_rt/profile/Makefile index 7c2fe3c9c13..c41da991a03 100644 --- a/lib/libclang_rt/profile/Makefile +++ b/lib/libclang_rt/profile/Makefile @@ -25,4 +25,8 @@ SRCS+= profile/InstrProfilingUtil.c SRCS+= profile/InstrProfilingValue.c SRCS+= profile/InstrProfilingWriter.c +.PATH: ${CRTSRC}/include/profile +INCSDIR= ${CLANGDIR}/include/profile +INCS+= InstrProfData.inc + .include diff --git a/lib/libclang_rt/xray/Makefile b/lib/libclang_rt/xray/Makefile index 7289c4a371b..13e084816a3 100644 --- a/lib/libclang_rt/xray/Makefile +++ b/lib/libclang_rt/xray/Makefile @@ -41,4 +41,10 @@ SRCS+= xray/xray_trampoline_x86_64.S SRCS+= xray/xray_utils.cpp SRCS+= xray/xray_x86_64.cpp +.PATH: ${CRTSRC}/include/xray +INCSDIR= ${CLANGDIR}/include/xray +INCS+= xray_interface.h +INCS+= xray_log_interface.h +INCS+= xray_records.h + .include