Merge tag 'net-6.3-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 9 Mar 2023 18:56:58 +0000 (10:56 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 9 Mar 2023 18:56:58 +0000 (10:56 -0800)
Pull networking fixes from Paolo Abeni:
 "Including fixes from netfilter and bpf.

  Current release - regressions:

   - core: avoid skb end_offset change in __skb_unclone_keeptruesize()

   - sched:
      - act_connmark: handle errno on tcf_idr_check_alloc
      - flower: fix fl_change() error recovery path

   - ieee802154: prevent user from crashing the host

  Current release - new code bugs:

   - eth: bnxt_en: fix the double free during device removal

   - tools: ynl:
      - fix enum-as-flags in the generic CLI
      - fully inherit attrs in subsets
      - re-license uniformly under GPL-2.0 or BSD-3-clause

  Previous releases - regressions:

   - core: use indirect calls helpers for sk_exit_memory_pressure()

   - tls:
      - fix return value for async crypto
      - avoid hanging tasks on the tx_lock

   - eth: ice: copy last block omitted in ice_get_module_eeprom()

  Previous releases - always broken:

   - core: avoid double iput when sock_alloc_file fails

   - af_unix: fix struct pid leaks in OOB support

   - tls:
      - fix possible race condition
      - fix device-offloaded sendpage straddling records

   - bpf:
      - sockmap: fix an infinite loop error
      - test_run: fix &xdp_frame misplacement for LIVE_FRAMES
      - fix resolving BTF_KIND_VAR after ARRAY, STRUCT, UNION, PTR

   - netfilter: tproxy: fix deadlock due to missing BH disable

   - phylib: get rid of unnecessary locking

   - eth: bgmac: fix *initial* chip reset to support BCM5358

   - eth: nfp: fix csum for ipsec offload

   - eth: mtk_eth_soc: fix RX data corruption issue

  Misc:

   - usb: qmi_wwan: add telit 0x1080 composition"

* tag 'net-6.3-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net: (64 commits)
  tools: ynl: fix enum-as-flags in the generic CLI
  tools: ynl: move the enum classes to shared code
  net: avoid double iput when sock_alloc_file fails
  af_unix: fix struct pid leaks in OOB support
  eth: fealnx: bring back this old driver
  net: dsa: mt7530: permit port 5 to work without port 6 on MT7621 SoC
  net: microchip: sparx5: fix deletion of existing DSCP mappings
  octeontx2-af: Unlock contexts in the queue context cache in case of fault detection
  net/smc: fix fallback failed while sendmsg with fastopen
  ynl: re-license uniformly under GPL-2.0 OR BSD-3-Clause
  mailmap: update entries for Stephen Hemminger
  mailmap: add entry for Maxim Mikityanskiy
  nfc: change order inside nfc_se_io error path
  ethernet: ice: avoid gcc-9 integer overflow warning
  ice: don't ignore return codes in VSI related code
  ice: Fix DSCP PFC TLV creation
  net: usb: qmi_wwan: add Telit 0x1080 composition
  net: usb: cdc_mbim: avoid altsetting toggling for Telit FE990
  netfilter: conntrack: adopt safer max chain length
  net: tls: fix device-offloaded sendpage straddling records
  ...

92 files changed:
.mailmap
Documentation/bpf/bpf_devel_QA.rst
Documentation/netlink/genetlink-c.yaml
Documentation/netlink/genetlink-legacy.yaml
Documentation/netlink/genetlink.yaml
Documentation/netlink/specs/ethtool.yaml
Documentation/netlink/specs/fou.yaml
Documentation/netlink/specs/netdev.yaml
Documentation/userspace-api/netlink/specs.rst
arch/mips/configs/mtx1_defconfig
arch/powerpc/boot/dts/fsl/t1040rdb-rev-a.dts
arch/powerpc/boot/dts/fsl/t1040rdb.dts
arch/powerpc/boot/dts/fsl/t1040si-post.dtsi
arch/powerpc/configs/ppc6xx_defconfig
arch/riscv/net/bpf_jit_comp64.c
drivers/net/dsa/mt7530.c
drivers/net/ethernet/Kconfig
drivers/net/ethernet/Makefile
drivers/net/ethernet/broadcom/bgmac.c
drivers/net/ethernet/broadcom/bgmac.h
drivers/net/ethernet/broadcom/bnxt/bnxt.c
drivers/net/ethernet/broadcom/bnxt/bnxt_ulp.c
drivers/net/ethernet/fealnx.c [new file with mode: 0644]
drivers/net/ethernet/intel/ice/ice_dcb.c
drivers/net/ethernet/intel/ice/ice_ethtool.c
drivers/net/ethernet/intel/ice/ice_lib.c
drivers/net/ethernet/intel/ice/ice_tc_lib.c
drivers/net/ethernet/marvell/octeontx2/af/rvu.h
drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c
drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
drivers/net/ethernet/marvell/octeontx2/af/rvu_npa.c
drivers/net/ethernet/marvell/octeontx2/af/rvu_reg.h
drivers/net/ethernet/mediatek/mtk_eth_soc.c
drivers/net/ethernet/mediatek/mtk_eth_soc.h
drivers/net/ethernet/microchip/lan966x/lan966x_police.c
drivers/net/ethernet/microchip/sparx5/sparx5_dcb.c
drivers/net/ethernet/netronome/nfp/nfd3/dp.c
drivers/net/ethernet/netronome/nfp/nfd3/ipsec.c
drivers/net/ethernet/netronome/nfp/nfdk/dp.c
drivers/net/ethernet/netronome/nfp/nfdk/ipsec.c
drivers/net/ethernet/netronome/nfp/nfp_net_common.c
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
drivers/net/ieee802154/ca8210.c
drivers/net/phy/microchip.c
drivers/net/phy/phy_device.c
drivers/net/phy/smsc.c
drivers/net/usb/cdc_mbim.c
drivers/net/usb/lan78xx.c
drivers/net/usb/qmi_wwan.c
drivers/nfc/fdp/i2c.c
include/net/netfilter/nf_tproxy.h
include/uapi/linux/fou.h
include/uapi/linux/netdev.h
kernel/bpf/btf.c
net/bpf/test_run.c
net/caif/caif_usb.c
net/core/netdev-genl-gen.c
net/core/netdev-genl-gen.h
net/core/skbuff.c
net/core/sock.c
net/ieee802154/nl802154.c
net/ipv4/fou_nl.c
net/ipv4/fou_nl.h
net/ipv4/netfilter/nf_tproxy_ipv4.c
net/ipv4/tcp_bpf.c
net/ipv4/udp_bpf.c
net/ipv6/ila/ila_xlat.c
net/ipv6/netfilter/nf_tproxy_ipv6.c
net/netfilter/nf_conntrack_core.c
net/netfilter/nf_conntrack_netlink.c
net/netfilter/nft_last.c
net/netfilter/nft_quota.c
net/nfc/netlink.c
net/sched/act_connmark.c
net/sched/cls_flower.c
net/smc/af_smc.c
net/socket.c
net/tls/tls_device.c
net/tls/tls_main.c
net/tls/tls_sw.c
net/unix/af_unix.c
net/unix/unix_bpf.c
tools/include/uapi/linux/netdev.h
tools/net/ynl/cli.py
tools/net/ynl/lib/__init__.py
tools/net/ynl/lib/nlspec.py
tools/net/ynl/lib/ynl.py
tools/net/ynl/ynl-gen-c.py
tools/net/ynl/ynl-regen.sh
tools/testing/selftests/bpf/prog_tests/btf.c
tools/testing/selftests/bpf/prog_tests/xdp_do_redirect.c
tools/testing/selftests/netfilter/nft_nat.sh

index 5367faa..66754ca 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -306,6 +306,8 @@ Mauro Carvalho Chehab <mchehab@kernel.org> <mchehab@osg.samsung.com>
 Mauro Carvalho Chehab <mchehab@kernel.org> <mchehab@redhat.com>
 Mauro Carvalho Chehab <mchehab@kernel.org> <m.chehab@samsung.com>
 Mauro Carvalho Chehab <mchehab@kernel.org> <mchehab@s-opensource.com>
+Maxim Mikityanskiy <maxtram95@gmail.com> <maximmi@mellanox.com>
+Maxim Mikityanskiy <maxtram95@gmail.com> <maximmi@nvidia.com>
 Maxime Ripard <mripard@kernel.org> <maxime.ripard@bootlin.com>
 Maxime Ripard <mripard@kernel.org> <maxime.ripard@free-electrons.com>
 Mayuresh Janorkar <mayur@ti.com>
@@ -411,7 +413,10 @@ Shuah Khan <shuah@kernel.org> <shuah.kh@samsung.com>
 Simon Arlott <simon@octiron.net> <simon@fire.lp0.eu>
 Simon Kelley <simon@thekelleys.org.uk>
 Stéphane Witzmann <stephane.witzmann@ubpmes.univ-bpclermont.fr>
-Stephen Hemminger <shemminger@osdl.org>
+Stephen Hemminger <stephen@networkplumber.org> <shemminger@linux-foundation.org>
+Stephen Hemminger <stephen@networkplumber.org> <shemminger@osdl.org>
+Stephen Hemminger <stephen@networkplumber.org> <sthemmin@microsoft.com>
+Stephen Hemminger <stephen@networkplumber.org> <sthemmin@vyatta.com>
 Steve Wise <larrystevenwise@gmail.com> <swise@chelsio.com>
 Steve Wise <larrystevenwise@gmail.com> <swise@opengridcomputing.com>
 Subash Abhinov Kasiviswanathan <subashab@codeaurora.org>
index 03d4993..b421d94 100644 (file)
@@ -7,8 +7,8 @@ workflows related to reporting bugs, submitting patches, and queueing
 patches for stable kernels.
 
 For general information about submitting patches, please refer to
-`Documentation/process/`_. This document only describes additional specifics
-related to BPF.
+Documentation/process/submitting-patches.rst. This document only describes
+additional specifics related to BPF.
 
 .. contents::
     :local:
@@ -461,15 +461,15 @@ needed::
 
   $ sudo make run_tests
 
-See the kernels selftest `Documentation/dev-tools/kselftest.rst`_
-document for further documentation.
+See :doc:`kernel selftest documentation </dev-tools/kselftest>`
+for details.
 
 To maximize the number of tests passing, the .config of the kernel
 under test should match the config file fragment in
 tools/testing/selftests/bpf as closely as possible.
 
 Finally to ensure support for latest BPF Type Format features -
-discussed in `Documentation/bpf/btf.rst`_ - pahole version 1.16
+discussed in Documentation/bpf/btf.rst - pahole version 1.16
 is required for kernels built with CONFIG_DEBUG_INFO_BTF=y.
 pahole is delivered in the dwarves package or can be built
 from source at
@@ -684,12 +684,8 @@ when:
 
 
 .. Links
-.. _Documentation/process/: https://www.kernel.org/doc/html/latest/process/
 .. _netdev-FAQ: Documentation/process/maintainer-netdev.rst
 .. _selftests:
    https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/tools/testing/selftests/bpf/
-.. _Documentation/dev-tools/kselftest.rst:
-   https://www.kernel.org/doc/html/latest/dev-tools/kselftest.html
-.. _Documentation/bpf/btf.rst: btf.rst
 
 Happy BPF hacking!
index bbcfa24..f082a5a 100644 (file)
@@ -1,4 +1,4 @@
-# SPDX-License-Identifier: GPL-2.0
+# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
 %YAML 1.2
 ---
 $id: http://kernel.org/schemas/netlink/genetlink-c.yaml#
index 5642925..c6b8c77 100644 (file)
@@ -1,4 +1,4 @@
-# SPDX-License-Identifier: GPL-2.0
+# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
 %YAML 1.2
 ---
 $id: http://kernel.org/schemas/netlink/genetlink-legacy.yaml#
index 62a9227..b2d56ab 100644 (file)
@@ -1,4 +1,4 @@
-# SPDX-License-Identifier: GPL-2.0
+# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
 %YAML 1.2
 ---
 $id: http://kernel.org/schemas/netlink/genetlink-legacy.yaml#
index 08b7769..18ecb7d 100644 (file)
@@ -1,3 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+
 name: ethtool
 
 protocol: genetlink-legacy
@@ -11,7 +13,6 @@ attribute-sets:
       -
         name: dev-index
         type: u32
-        value: 1
       -
         name: dev-name
         type: string
@@ -25,7 +26,6 @@ attribute-sets:
       -
         name: index
         type: u32
-        value: 1
       -
         name: name
         type: string
@@ -39,14 +39,12 @@ attribute-sets:
         name: bit
         type: nest
         nested-attributes: bitset-bit
-        value: 1
   -
     name: bitset
     attributes:
       -
         name: nomask
         type: flag
-        value: 1
       -
         name: size
         type: u32
@@ -61,7 +59,6 @@ attribute-sets:
       -
         name: index
         type: u32
-        value: 1
       -
         name: value
         type: string
@@ -71,7 +68,6 @@ attribute-sets:
       -
         name: string
         type: nest
-        value: 1
         multi-attr: true
         nested-attributes: string
   -
@@ -80,7 +76,6 @@ attribute-sets:
       -
         name: id
         type: u32
-        value: 1
       -
         name: count
         type: u32
@@ -96,14 +91,12 @@ attribute-sets:
         name: stringset
         type: nest
         multi-attr: true
-        value: 1
         nested-attributes: stringset
   -
     name: strset
     attributes:
       -
         name: header
-        value: 1
         type: nest
         nested-attributes: header
       -
@@ -119,7 +112,6 @@ attribute-sets:
     attributes:
       -
         name: header
-        value: 1
         type: nest
         nested-attributes: header
       -
@@ -132,7 +124,6 @@ attribute-sets:
     attributes:
       -
         name: header
-        value: 1
         type: nest
         nested-attributes: header
       -
@@ -180,7 +171,6 @@ attribute-sets:
     attributes:
       -
         name: pad
-        value: 1
         type: pad
       -
         name: reassembly-errors
@@ -205,7 +195,6 @@ attribute-sets:
     attributes:
       -
         name: header
-        value: 1
         type: nest
         nested-attributes: header
       -
@@ -251,13 +240,11 @@ operations:
 
       do: &strset-get-op
         request:
-          value: 1
           attributes:
             - header
             - stringsets
             - counts-only
         reply:
-          value: 1
           attributes:
             - header
             - stringsets
index 266c386..cff1042 100644 (file)
@@ -1,3 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+
 name: fou
 
 protocol: genetlink-legacy
@@ -26,6 +28,7 @@ attribute-sets:
       -
         name: unspec
         type: unused
+        value: 0
       -
         name: port
         type: u16
@@ -71,6 +74,7 @@ operations:
     -
       name: unspec
       doc: unused
+      value: 0
 
     -
       name: add
index cffef09..24de747 100644 (file)
@@ -1,3 +1,5 @@
+# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
+
 name: netdev
 
 doc:
@@ -48,7 +50,6 @@ attribute-sets:
         name: ifindex
         doc: netdev ifindex
         type: u32
-        value: 1
         checks:
           min: 1
       -
@@ -66,7 +67,6 @@ operations:
     -
       name: dev-get
       doc: Get / dump information about a netdev.
-      value: 1
       attribute-set: dev
       do:
         request:
index 6ffe813..2122e0c 100644 (file)
@@ -24,6 +24,9 @@ YAML specifications can be found under ``Documentation/netlink/specs/``
 This document describes details of the schema.
 See :doc:`intro-specs` for a practical starting guide.
 
+All specs must be licensed under ``GPL-2.0-only OR BSD-3-Clause``
+to allow for easy adoption in user space code.
+
 Compatibility levels
 ====================
 
@@ -197,9 +200,15 @@ value
 Numerical attribute ID, used in serialized Netlink messages.
 The ``value`` property can be skipped, in which case the attribute ID
 will be the value of the previous attribute plus one (recursively)
-and ``0`` for the first attribute in the attribute set.
+and ``1`` for the first attribute in the attribute set.
+
+Attributes (and operations) use ``1`` as the default value for the first
+entry (unlike enums in definitions which start from ``0``) because
+entry ``0`` is almost always reserved as undefined. Spec can explicitly
+set value to ``0`` if needed.
 
-Note that the ``value`` of an attribute is defined only in its main set.
+Note that the ``value`` of an attribute is defined only in its main set
+(not in subsets).
 
 enum
 ~~~~
index 89a1511..edf9634 100644 (file)
@@ -284,6 +284,7 @@ CONFIG_IXGB=m
 CONFIG_SKGE=m
 CONFIG_SKY2=m
 CONFIG_MYRI10GE=m
+CONFIG_FEALNX=m
 CONFIG_NATSEMI=m
 CONFIG_NS83820=m
 CONFIG_S2IO=m
index 73f8c99..d4f5f15 100644 (file)
@@ -10,7 +10,6 @@
 
 / {
        model = "fsl,T1040RDB-REV-A";
-       compatible = "fsl,T1040RDB-REV-A";
 };
 
 &seville_port0 {
index b6733e7..dd3aab8 100644 (file)
 };
 
 &seville_port8 {
-       ethernet = <&enet0>;
+       status = "okay";
+};
+
+&seville_port9 {
        status = "okay";
 };
index f58eb82..ad0ab33 100644 (file)
                        seville_port8: port@8 {
                                reg = <8>;
                                phy-mode = "internal";
+                               ethernet = <&enet0>;
                                status = "disabled";
 
                                fixed-link {
                        seville_port9: port@9 {
                                reg = <9>;
                                phy-mode = "internal";
+                               ethernet = <&enet1>;
                                status = "disabled";
 
                                fixed-link {
index 1102582..f73c98b 100644 (file)
@@ -461,6 +461,7 @@ CONFIG_MV643XX_ETH=m
 CONFIG_SKGE=m
 CONFIG_SKY2=m
 CONFIG_MYRI10GE=m
+CONFIG_FEALNX=m
 CONFIG_NATSEMI=m
 CONFIG_NS83820=m
 CONFIG_PCMCIA_AXNET=m
index f5a6687..acdc3f0 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/filter.h>
 #include <linux/memory.h>
 #include <linux/stop_machine.h>
+#include <asm/patch.h>
 #include "bpf_jit.h"
 
 #define RV_REG_TCC RV_REG_A6
index 3a15015..a508402 100644 (file)
@@ -393,6 +393,24 @@ mt7530_fdb_write(struct mt7530_priv *priv, u16 vid,
                mt7530_write(priv, MT7530_ATA1 + (i * 4), reg[i]);
 }
 
+/* Set up switch core clock for MT7530 */
+static void mt7530_pll_setup(struct mt7530_priv *priv)
+{
+       /* Disable PLL */
+       core_write(priv, CORE_GSWPLL_GRP1, 0);
+
+       /* Set core clock into 500Mhz */
+       core_write(priv, CORE_GSWPLL_GRP2,
+                  RG_GSWPLL_POSDIV_500M(1) |
+                  RG_GSWPLL_FBKDIV_500M(25));
+
+       /* Enable PLL */
+       core_write(priv, CORE_GSWPLL_GRP1,
+                  RG_GSWPLL_EN_PRE |
+                  RG_GSWPLL_POSDIV_200M(2) |
+                  RG_GSWPLL_FBKDIV_200M(32));
+}
+
 /* Setup TX circuit including relevant PAD and driving */
 static int
 mt7530_pad_clk_setup(struct dsa_switch *ds, phy_interface_t interface)
@@ -453,21 +471,6 @@ mt7530_pad_clk_setup(struct dsa_switch *ds, phy_interface_t interface)
        core_clear(priv, CORE_TRGMII_GSW_CLK_CG,
                   REG_GSWCK_EN | REG_TRGMIICK_EN);
 
-       /* Setup core clock for MT7530 */
-       /* Disable PLL */
-       core_write(priv, CORE_GSWPLL_GRP1, 0);
-
-       /* Set core clock into 500Mhz */
-       core_write(priv, CORE_GSWPLL_GRP2,
-                  RG_GSWPLL_POSDIV_500M(1) |
-                  RG_GSWPLL_FBKDIV_500M(25));
-
-       /* Enable PLL */
-       core_write(priv, CORE_GSWPLL_GRP1,
-                  RG_GSWPLL_EN_PRE |
-                  RG_GSWPLL_POSDIV_200M(2) |
-                  RG_GSWPLL_FBKDIV_200M(32));
-
        /* Setup the MT7530 TRGMII Tx Clock */
        core_write(priv, CORE_PLL_GROUP5, RG_LCDDS_PCW_NCPO1(ncpo1));
        core_write(priv, CORE_PLL_GROUP6, RG_LCDDS_PCW_NCPO0(0));
@@ -2196,6 +2199,8 @@ mt7530_setup(struct dsa_switch *ds)
                     SYS_CTRL_PHY_RST | SYS_CTRL_SW_RST |
                     SYS_CTRL_REG_RST);
 
+       mt7530_pll_setup(priv);
+
        /* Enable Port 6 only; P5 as GMAC5 which currently is not supported */
        val = mt7530_read(priv, MT7530_MHWTRAP);
        val &= ~MHWTRAP_P6_DIS & ~MHWTRAP_PHY_ACCESS;
index 323ec56..1917da7 100644 (file)
@@ -132,6 +132,16 @@ source "drivers/net/ethernet/mscc/Kconfig"
 source "drivers/net/ethernet/microsoft/Kconfig"
 source "drivers/net/ethernet/moxa/Kconfig"
 source "drivers/net/ethernet/myricom/Kconfig"
+
+config FEALNX
+       tristate "Myson MTD-8xx PCI Ethernet support"
+       depends on PCI
+       select CRC32
+       select MII
+       help
+         Say Y here to support the Myson MTD-800 family of PCI-based Ethernet
+         cards. <http://www.myson.com.tw/>
+
 source "drivers/net/ethernet/ni/Kconfig"
 source "drivers/net/ethernet/natsemi/Kconfig"
 source "drivers/net/ethernet/neterion/Kconfig"
index 2fedbaa..0d872d4 100644 (file)
@@ -64,6 +64,7 @@ obj-$(CONFIG_NET_VENDOR_MICROCHIP) += microchip/
 obj-$(CONFIG_NET_VENDOR_MICROSEMI) += mscc/
 obj-$(CONFIG_NET_VENDOR_MOXART) += moxa/
 obj-$(CONFIG_NET_VENDOR_MYRI) += myricom/
+obj-$(CONFIG_FEALNX) += fealnx.o
 obj-$(CONFIG_NET_VENDOR_NATSEMI) += natsemi/
 obj-$(CONFIG_NET_VENDOR_NETERION) += neterion/
 obj-$(CONFIG_NET_VENDOR_NETRONOME) += netronome/
index 3038386..1761df8 100644 (file)
@@ -890,13 +890,13 @@ static void bgmac_chip_reset_idm_config(struct bgmac *bgmac)
 
                if (iost & BGMAC_BCMA_IOST_ATTACHED) {
                        flags = BGMAC_BCMA_IOCTL_SW_CLKEN;
-                       if (!bgmac->has_robosw)
+                       if (bgmac->in_init || !bgmac->has_robosw)
                                flags |= BGMAC_BCMA_IOCTL_SW_RESET;
                }
                bgmac_clk_enable(bgmac, flags);
        }
 
-       if (iost & BGMAC_BCMA_IOST_ATTACHED && !bgmac->has_robosw)
+       if (iost & BGMAC_BCMA_IOST_ATTACHED && (bgmac->in_init || !bgmac->has_robosw))
                bgmac_idm_write(bgmac, BCMA_IOCTL,
                                bgmac_idm_read(bgmac, BCMA_IOCTL) &
                                ~BGMAC_BCMA_IOCTL_SW_RESET);
@@ -1490,6 +1490,8 @@ int bgmac_enet_probe(struct bgmac *bgmac)
        struct net_device *net_dev = bgmac->net_dev;
        int err;
 
+       bgmac->in_init = true;
+
        bgmac_chip_intrs_off(bgmac);
 
        net_dev->irq = bgmac->irq;
@@ -1542,6 +1544,8 @@ int bgmac_enet_probe(struct bgmac *bgmac)
        /* Omit FCS from max MTU size */
        net_dev->max_mtu = BGMAC_RX_MAX_FRAME_SIZE - ETH_FCS_LEN;
 
+       bgmac->in_init = false;
+
        err = register_netdev(bgmac->net_dev);
        if (err) {
                dev_err(bgmac->dev, "Cannot register net device\n");
index e05ac92..d73ef26 100644 (file)
@@ -472,6 +472,8 @@ struct bgmac {
        int irq;
        u32 int_mask;
 
+       bool in_init;
+
        /* Current MAC state */
        int mac_speed;
        int mac_duplex;
index 5d4b1f2..808236d 100644 (file)
@@ -3145,7 +3145,7 @@ static int bnxt_alloc_ring(struct bnxt *bp, struct bnxt_ring_mem_info *rmem)
 
 static void bnxt_free_tpa_info(struct bnxt *bp)
 {
-       int i;
+       int i, j;
 
        for (i = 0; i < bp->rx_nr_rings; i++) {
                struct bnxt_rx_ring_info *rxr = &bp->rx_ring[i];
@@ -3153,8 +3153,10 @@ static void bnxt_free_tpa_info(struct bnxt *bp)
                kfree(rxr->rx_tpa_idx_map);
                rxr->rx_tpa_idx_map = NULL;
                if (rxr->rx_tpa) {
-                       kfree(rxr->rx_tpa[0].agg_arr);
-                       rxr->rx_tpa[0].agg_arr = NULL;
+                       for (j = 0; j < bp->max_tpa; j++) {
+                               kfree(rxr->rx_tpa[j].agg_arr);
+                               rxr->rx_tpa[j].agg_arr = NULL;
+                       }
                }
                kfree(rxr->rx_tpa);
                rxr->rx_tpa = NULL;
@@ -3163,14 +3165,13 @@ static void bnxt_free_tpa_info(struct bnxt *bp)
 
 static int bnxt_alloc_tpa_info(struct bnxt *bp)
 {
-       int i, j, total_aggs = 0;
+       int i, j;
 
        bp->max_tpa = MAX_TPA;
        if (bp->flags & BNXT_FLAG_CHIP_P5) {
                if (!bp->max_tpa_v2)
                        return 0;
                bp->max_tpa = max_t(u16, bp->max_tpa_v2, MAX_TPA_P5);
-               total_aggs = bp->max_tpa * MAX_SKB_FRAGS;
        }
 
        for (i = 0; i < bp->rx_nr_rings; i++) {
@@ -3184,12 +3185,12 @@ static int bnxt_alloc_tpa_info(struct bnxt *bp)
 
                if (!(bp->flags & BNXT_FLAG_CHIP_P5))
                        continue;
-               agg = kcalloc(total_aggs, sizeof(*agg), GFP_KERNEL);
-               rxr->rx_tpa[0].agg_arr = agg;
-               if (!agg)
-                       return -ENOMEM;
-               for (j = 1; j < bp->max_tpa; j++)
-                       rxr->rx_tpa[j].agg_arr = agg + j * MAX_SKB_FRAGS;
+               for (j = 0; j < bp->max_tpa; j++) {
+                       agg = kcalloc(MAX_SKB_FRAGS, sizeof(*agg), GFP_KERNEL);
+                       if (!agg)
+                               return -ENOMEM;
+                       rxr->rx_tpa[j].agg_arr = agg;
+               }
                rxr->rx_tpa_idx_map = kzalloc(sizeof(*rxr->rx_tpa_idx_map),
                                              GFP_KERNEL);
                if (!rxr->rx_tpa_idx_map)
@@ -13204,8 +13205,6 @@ static void bnxt_remove_one(struct pci_dev *pdev)
        bnxt_free_hwrm_resources(bp);
        bnxt_ethtool_free(bp);
        bnxt_dcb_free(bp);
-       kfree(bp->edev);
-       bp->edev = NULL;
        kfree(bp->ptp_cfg);
        bp->ptp_cfg = NULL;
        kfree(bp->fw_health);
index d4cc9c3..e7b5e28 100644 (file)
@@ -317,9 +317,11 @@ static void bnxt_aux_dev_release(struct device *dev)
 {
        struct bnxt_aux_priv *aux_priv =
                container_of(dev, struct bnxt_aux_priv, aux_dev.dev);
+       struct bnxt *bp = netdev_priv(aux_priv->edev->net);
 
        ida_free(&bnxt_aux_dev_ids, aux_priv->id);
        kfree(aux_priv->edev->ulp_tbl);
+       bp->edev = NULL;
        kfree(aux_priv->edev);
        kfree(aux_priv);
 }
diff --git a/drivers/net/ethernet/fealnx.c b/drivers/net/ethernet/fealnx.c
new file mode 100644 (file)
index 0000000..ed18450
--- /dev/null
@@ -0,0 +1,1953 @@
+/*
+       Written 1998-2000 by Donald Becker.
+
+       This software may be used and distributed according to the terms of
+       the GNU General Public License (GPL), incorporated herein by reference.
+       Drivers based on or derived from this code fall under the GPL and must
+       retain the authorship, copyright and license notice.  This file is not
+       a complete program and may only be used when the entire operating
+       system is licensed under the GPL.
+
+       The author may be reached as becker@scyld.com, or C/O
+       Scyld Computing Corporation
+       410 Severn Ave., Suite 210
+       Annapolis MD 21403
+
+       Support information and updates available at
+       http://www.scyld.com/network/pci-skeleton.html
+
+       Linux kernel updates:
+
+       Version 2.51, Nov 17, 2001 (jgarzik):
+       - Add ethtool support
+       - Replace some MII-related magic numbers with constants
+
+*/
+
+#define DRV_NAME       "fealnx"
+
+static int debug;              /* 1-> print debug message */
+static int max_interrupt_work = 20;
+
+/* Maximum number of multicast addresses to filter (vs. Rx-all-multicast). */
+static int multicast_filter_limit = 32;
+
+/* Set the copy breakpoint for the copy-only-tiny-frames scheme. */
+/* Setting to > 1518 effectively disables this feature.          */
+static int rx_copybreak;
+
+/* Used to pass the media type, etc.                            */
+/* Both 'options[]' and 'full_duplex[]' should exist for driver */
+/* interoperability.                                            */
+/* The media type is usually passed in 'options[]'.             */
+#define MAX_UNITS 8            /* More are supported, limit only on options */
+static int options[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1 };
+static int full_duplex[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1 };
+
+/* Operational parameters that are set at compile time.                 */
+/* Keep the ring sizes a power of two for compile efficiency.           */
+/* The compiler will convert <unsigned>'%'<2^N> into a bit mask.        */
+/* Making the Tx ring too large decreases the effectiveness of channel  */
+/* bonding and packet priority.                                         */
+/* There are no ill effects from too-large receive rings.               */
+// 88-12-9 modify,
+// #define TX_RING_SIZE    16
+// #define RX_RING_SIZE    32
+#define TX_RING_SIZE    6
+#define RX_RING_SIZE    12
+#define TX_TOTAL_SIZE  TX_RING_SIZE*sizeof(struct fealnx_desc)
+#define RX_TOTAL_SIZE  RX_RING_SIZE*sizeof(struct fealnx_desc)
+
+/* Operational parameters that usually are not changed. */
+/* Time in jiffies before concluding the transmitter is hung. */
+#define TX_TIMEOUT      (2*HZ)
+
+#define PKT_BUF_SZ      1536   /* Size of each temporary Rx buffer. */
+
+
+/* Include files, designed to support most kernel versions 2.0.0 and later. */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/init.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/crc32.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+
+#include <asm/processor.h>     /* Processor type for cache alignment. */
+#include <asm/io.h>
+#include <linux/uaccess.h>
+#include <asm/byteorder.h>
+
+/* This driver was written to use PCI memory space, however some x86 systems
+   work only with I/O space accesses. */
+#ifndef __alpha__
+#define USE_IO_OPS
+#endif
+
+/* Kernel compatibility defines, some common to David Hinds' PCMCIA package. */
+/* This is only in the support-all-kernels source code. */
+
+#define RUN_AT(x) (jiffies + (x))
+
+MODULE_AUTHOR("Myson or whoever");
+MODULE_DESCRIPTION("Myson MTD-8xx 100/10M Ethernet PCI Adapter Driver");
+MODULE_LICENSE("GPL");
+module_param(max_interrupt_work, int, 0);
+module_param(debug, int, 0);
+module_param(rx_copybreak, int, 0);
+module_param(multicast_filter_limit, int, 0);
+module_param_array(options, int, NULL, 0);
+module_param_array(full_duplex, int, NULL, 0);
+MODULE_PARM_DESC(max_interrupt_work, "fealnx maximum events handled per interrupt");
+MODULE_PARM_DESC(debug, "fealnx enable debugging (0-1)");
+MODULE_PARM_DESC(rx_copybreak, "fealnx copy breakpoint for copy-only-tiny-frames");
+MODULE_PARM_DESC(multicast_filter_limit, "fealnx maximum number of filtered multicast addresses");
+MODULE_PARM_DESC(options, "fealnx: Bits 0-3: media type, bit 17: full duplex");
+MODULE_PARM_DESC(full_duplex, "fealnx full duplex setting(s) (1)");
+
+enum {
+       MIN_REGION_SIZE         = 136,
+};
+
+/* A chip capabilities table, matching the entries in pci_tbl[] above. */
+enum chip_capability_flags {
+       HAS_MII_XCVR,
+       HAS_CHIP_XCVR,
+};
+
+/* 89/6/13 add, */
+/* for different PHY */
+enum phy_type_flags {
+       MysonPHY = 1,
+       AhdocPHY = 2,
+       SeeqPHY = 3,
+       MarvellPHY = 4,
+       Myson981 = 5,
+       LevelOnePHY = 6,
+       OtherPHY = 10,
+};
+
+struct chip_info {
+       char *chip_name;
+       int flags;
+};
+
+static const struct chip_info skel_netdrv_tbl[] = {
+       { "100/10M Ethernet PCI Adapter",       HAS_MII_XCVR },
+       { "100/10M Ethernet PCI Adapter",       HAS_CHIP_XCVR },
+       { "1000/100/10M Ethernet PCI Adapter",  HAS_MII_XCVR },
+};
+
+/* Offsets to the Command and Status Registers. */
+enum fealnx_offsets {
+       PAR0 = 0x0,             /* physical address 0-3 */
+       PAR1 = 0x04,            /* physical address 4-5 */
+       MAR0 = 0x08,            /* multicast address 0-3 */
+       MAR1 = 0x0C,            /* multicast address 4-7 */
+       FAR0 = 0x10,            /* flow-control address 0-3 */
+       FAR1 = 0x14,            /* flow-control address 4-5 */
+       TCRRCR = 0x18,          /* receive & transmit configuration */
+       BCR = 0x1C,             /* bus command */
+       TXPDR = 0x20,           /* transmit polling demand */
+       RXPDR = 0x24,           /* receive polling demand */
+       RXCWP = 0x28,           /* receive current word pointer */
+       TXLBA = 0x2C,           /* transmit list base address */
+       RXLBA = 0x30,           /* receive list base address */
+       ISR = 0x34,             /* interrupt status */
+       IMR = 0x38,             /* interrupt mask */
+       FTH = 0x3C,             /* flow control high/low threshold */
+       MANAGEMENT = 0x40,      /* bootrom/eeprom and mii management */
+       TALLY = 0x44,           /* tally counters for crc and mpa */
+       TSR = 0x48,             /* tally counter for transmit status */
+       BMCRSR = 0x4c,          /* basic mode control and status */
+       PHYIDENTIFIER = 0x50,   /* phy identifier */
+       ANARANLPAR = 0x54,      /* auto-negotiation advertisement and link
+                                  partner ability */
+       ANEROCR = 0x58,         /* auto-negotiation expansion and pci conf. */
+       BPREMRPSR = 0x5c,       /* bypass & receive error mask and phy status */
+};
+
+/* Bits in the interrupt status/enable registers. */
+/* The bits in the Intr Status/Enable registers, mostly interrupt sources. */
+enum intr_status_bits {
+       RFCON = 0x00020000,     /* receive flow control xon packet */
+       RFCOFF = 0x00010000,    /* receive flow control xoff packet */
+       LSCStatus = 0x00008000, /* link status change */
+       ANCStatus = 0x00004000, /* autonegotiation completed */
+       FBE = 0x00002000,       /* fatal bus error */
+       FBEMask = 0x00001800,   /* mask bit12-11 */
+       ParityErr = 0x00000000, /* parity error */
+       TargetErr = 0x00001000, /* target abort */
+       MasterErr = 0x00000800, /* master error */
+       TUNF = 0x00000400,      /* transmit underflow */
+       ROVF = 0x00000200,      /* receive overflow */
+       ETI = 0x00000100,       /* transmit early int */
+       ERI = 0x00000080,       /* receive early int */
+       CNTOVF = 0x00000040,    /* counter overflow */
+       RBU = 0x00000020,       /* receive buffer unavailable */
+       TBU = 0x00000010,       /* transmit buffer unavilable */
+       TI = 0x00000008,        /* transmit interrupt */
+       RI = 0x00000004,        /* receive interrupt */
+       RxErr = 0x00000002,     /* receive error */
+};
+
+/* Bits in the NetworkConfig register, W for writing, R for reading */
+/* FIXME: some names are invented by me. Marked with (name?) */
+/* If you have docs and know bit names, please fix 'em */
+enum rx_mode_bits {
+       CR_W_ENH        = 0x02000000,   /* enhanced mode (name?) */
+       CR_W_FD         = 0x00100000,   /* full duplex */
+       CR_W_PS10       = 0x00080000,   /* 10 mbit */
+       CR_W_TXEN       = 0x00040000,   /* tx enable (name?) */
+       CR_W_PS1000     = 0x00010000,   /* 1000 mbit */
+     /* CR_W_RXBURSTMASK= 0x00000e00, Im unsure about this */
+       CR_W_RXMODEMASK = 0x000000e0,
+       CR_W_PROM       = 0x00000080,   /* promiscuous mode */
+       CR_W_AB         = 0x00000040,   /* accept broadcast */
+       CR_W_AM         = 0x00000020,   /* accept mutlicast */
+       CR_W_ARP        = 0x00000008,   /* receive runt pkt */
+       CR_W_ALP        = 0x00000004,   /* receive long pkt */
+       CR_W_SEP        = 0x00000002,   /* receive error pkt */
+       CR_W_RXEN       = 0x00000001,   /* rx enable (unicast?) (name?) */
+
+       CR_R_TXSTOP     = 0x04000000,   /* tx stopped (name?) */
+       CR_R_FD         = 0x00100000,   /* full duplex detected */
+       CR_R_PS10       = 0x00080000,   /* 10 mbit detected */
+       CR_R_RXSTOP     = 0x00008000,   /* rx stopped (name?) */
+};
+
+/* The Tulip Rx and Tx buffer descriptors. */
+struct fealnx_desc {
+       s32 status;
+       s32 control;
+       u32 buffer;
+       u32 next_desc;
+       struct fealnx_desc *next_desc_logical;
+       struct sk_buff *skbuff;
+       u32 reserved1;
+       u32 reserved2;
+};
+
+/* Bits in network_desc.status */
+enum rx_desc_status_bits {
+       RXOWN = 0x80000000,     /* own bit */
+       FLNGMASK = 0x0fff0000,  /* frame length */
+       FLNGShift = 16,
+       MARSTATUS = 0x00004000, /* multicast address received */
+       BARSTATUS = 0x00002000, /* broadcast address received */
+       PHYSTATUS = 0x00001000, /* physical address received */
+       RXFSD = 0x00000800,     /* first descriptor */
+       RXLSD = 0x00000400,     /* last descriptor */
+       ErrorSummary = 0x80,    /* error summary */
+       RUNTPKT = 0x40,         /* runt packet received */
+       LONGPKT = 0x20,         /* long packet received */
+       FAE = 0x10,             /* frame align error */
+       CRC = 0x08,             /* crc error */
+       RXER = 0x04,            /* receive error */
+};
+
+enum rx_desc_control_bits {
+       RXIC = 0x00800000,      /* interrupt control */
+       RBSShift = 0,
+};
+
+enum tx_desc_status_bits {
+       TXOWN = 0x80000000,     /* own bit */
+       JABTO = 0x00004000,     /* jabber timeout */
+       CSL = 0x00002000,       /* carrier sense lost */
+       LC = 0x00001000,        /* late collision */
+       EC = 0x00000800,        /* excessive collision */
+       UDF = 0x00000400,       /* fifo underflow */
+       DFR = 0x00000200,       /* deferred */
+       HF = 0x00000100,        /* heartbeat fail */
+       NCRMask = 0x000000ff,   /* collision retry count */
+       NCRShift = 0,
+};
+
+enum tx_desc_control_bits {
+       TXIC = 0x80000000,      /* interrupt control */
+       ETIControl = 0x40000000,        /* early transmit interrupt */
+       TXLD = 0x20000000,      /* last descriptor */
+       TXFD = 0x10000000,      /* first descriptor */
+       CRCEnable = 0x08000000, /* crc control */
+       PADEnable = 0x04000000, /* padding control */
+       RetryTxLC = 0x02000000, /* retry late collision */
+       PKTSMask = 0x3ff800,    /* packet size bit21-11 */
+       PKTSShift = 11,
+       TBSMask = 0x000007ff,   /* transmit buffer bit 10-0 */
+       TBSShift = 0,
+};
+
+/* BootROM/EEPROM/MII Management Register */
+#define MASK_MIIR_MII_READ       0x00000000
+#define MASK_MIIR_MII_WRITE      0x00000008
+#define MASK_MIIR_MII_MDO        0x00000004
+#define MASK_MIIR_MII_MDI        0x00000002
+#define MASK_MIIR_MII_MDC        0x00000001
+
+/* ST+OP+PHYAD+REGAD+TA */
+#define OP_READ             0x6000     /* ST:01+OP:10+PHYAD+REGAD+TA:Z0 */
+#define OP_WRITE            0x5002     /* ST:01+OP:01+PHYAD+REGAD+TA:10 */
+
+/* ------------------------------------------------------------------------- */
+/*      Constants for Myson PHY                                              */
+/* ------------------------------------------------------------------------- */
+#define MysonPHYID      0xd0000302
+/* 89-7-27 add, (begin) */
+#define MysonPHYID0     0x0302
+#define StatusRegister  18
+#define SPEED100        0x0400 // bit10
+#define FULLMODE        0x0800 // bit11
+/* 89-7-27 add, (end) */
+
+/* ------------------------------------------------------------------------- */
+/*      Constants for Seeq 80225 PHY                                         */
+/* ------------------------------------------------------------------------- */
+#define SeeqPHYID0      0x0016
+
+#define MIIRegister18   18
+#define SPD_DET_100     0x80
+#define DPLX_DET_FULL   0x40
+
+/* ------------------------------------------------------------------------- */
+/*      Constants for Ahdoc 101 PHY                                          */
+/* ------------------------------------------------------------------------- */
+#define AhdocPHYID0     0x0022
+
+#define DiagnosticReg   18
+#define DPLX_FULL       0x0800
+#define Speed_100       0x0400
+
+/* 89/6/13 add, */
+/* -------------------------------------------------------------------------- */
+/*      Constants                                                             */
+/* -------------------------------------------------------------------------- */
+#define MarvellPHYID0           0x0141
+#define LevelOnePHYID0         0x0013
+
+#define MII1000BaseTControlReg  9
+#define MII1000BaseTStatusReg   10
+#define SpecificReg            17
+
+/* for 1000BaseT Control Register */
+#define PHYAbletoPerform1000FullDuplex  0x0200
+#define PHYAbletoPerform1000HalfDuplex  0x0100
+#define PHY1000AbilityMask              0x300
+
+// for phy specific status register, marvell phy.
+#define SpeedMask       0x0c000
+#define Speed_1000M     0x08000
+#define Speed_100M      0x4000
+#define Speed_10M       0
+#define Full_Duplex     0x2000
+
+// 89/12/29 add, for phy specific status register, levelone phy, (begin)
+#define LXT1000_100M    0x08000
+#define LXT1000_1000M   0x0c000
+#define LXT1000_Full    0x200
+// 89/12/29 add, for phy specific status register, levelone phy, (end)
+
+/* for 3-in-1 case, BMCRSR register */
+#define LinkIsUp2      0x00040000
+
+/* for PHY */
+#define LinkIsUp        0x0004
+
+
+struct netdev_private {
+       /* Descriptor rings first for alignment. */
+       struct fealnx_desc *rx_ring;
+       struct fealnx_desc *tx_ring;
+
+       dma_addr_t rx_ring_dma;
+       dma_addr_t tx_ring_dma;
+
+       spinlock_t lock;
+
+       /* Media monitoring timer. */
+       struct timer_list timer;
+
+       /* Reset timer */
+       struct timer_list reset_timer;
+       int reset_timer_armed;
+       unsigned long crvalue_sv;
+       unsigned long imrvalue_sv;
+
+       /* Frequently used values: keep some adjacent for cache effect. */
+       int flags;
+       struct pci_dev *pci_dev;
+       unsigned long crvalue;
+       unsigned long bcrvalue;
+       unsigned long imrvalue;
+       struct fealnx_desc *cur_rx;
+       struct fealnx_desc *lack_rxbuf;
+       int really_rx_count;
+       struct fealnx_desc *cur_tx;
+       struct fealnx_desc *cur_tx_copy;
+       int really_tx_count;
+       int free_tx_count;
+       unsigned int rx_buf_sz; /* Based on MTU+slack. */
+
+       /* These values are keep track of the transceiver/media in use. */
+       unsigned int linkok;
+       unsigned int line_speed;
+       unsigned int duplexmode;
+       unsigned int default_port:4;    /* Last dev->if_port value. */
+       unsigned int PHYType;
+
+       /* MII transceiver section. */
+       int mii_cnt;            /* MII device addresses. */
+       unsigned char phys[2];  /* MII device addresses. */
+       struct mii_if_info mii;
+       void __iomem *mem;
+};
+
+
+static int mdio_read(struct net_device *dev, int phy_id, int location);
+static void mdio_write(struct net_device *dev, int phy_id, int location, int value);
+static int netdev_open(struct net_device *dev);
+static void getlinktype(struct net_device *dev);
+static void getlinkstatus(struct net_device *dev);
+static void netdev_timer(struct timer_list *t);
+static void reset_timer(struct timer_list *t);
+static void fealnx_tx_timeout(struct net_device *dev, unsigned int txqueue);
+static void init_ring(struct net_device *dev);
+static netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev);
+static irqreturn_t intr_handler(int irq, void *dev_instance);
+static int netdev_rx(struct net_device *dev);
+static void set_rx_mode(struct net_device *dev);
+static void __set_rx_mode(struct net_device *dev);
+static struct net_device_stats *get_stats(struct net_device *dev);
+static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd);
+static const struct ethtool_ops netdev_ethtool_ops;
+static int netdev_close(struct net_device *dev);
+static void reset_rx_descriptors(struct net_device *dev);
+static void reset_tx_descriptors(struct net_device *dev);
+
+static void stop_nic_rx(void __iomem *ioaddr, long crvalue)
+{
+       int delay = 0x1000;
+       iowrite32(crvalue & ~(CR_W_RXEN), ioaddr + TCRRCR);
+       while (--delay) {
+               if ( (ioread32(ioaddr + TCRRCR) & CR_R_RXSTOP) == CR_R_RXSTOP)
+                       break;
+       }
+}
+
+
+static void stop_nic_rxtx(void __iomem *ioaddr, long crvalue)
+{
+       int delay = 0x1000;
+       iowrite32(crvalue & ~(CR_W_RXEN+CR_W_TXEN), ioaddr + TCRRCR);
+       while (--delay) {
+               if ( (ioread32(ioaddr + TCRRCR) & (CR_R_RXSTOP+CR_R_TXSTOP))
+                                           == (CR_R_RXSTOP+CR_R_TXSTOP) )
+                       break;
+       }
+}
+
+static const struct net_device_ops netdev_ops = {
+       .ndo_open               = netdev_open,
+       .ndo_stop               = netdev_close,
+       .ndo_start_xmit         = start_tx,
+       .ndo_get_stats          = get_stats,
+       .ndo_set_rx_mode        = set_rx_mode,
+       .ndo_eth_ioctl          = mii_ioctl,
+       .ndo_tx_timeout         = fealnx_tx_timeout,
+       .ndo_set_mac_address    = eth_mac_addr,
+       .ndo_validate_addr      = eth_validate_addr,
+};
+
+static int fealnx_init_one(struct pci_dev *pdev,
+                          const struct pci_device_id *ent)
+{
+       struct netdev_private *np;
+       int i, option, err, irq;
+       static int card_idx = -1;
+       char boardname[12];
+       void __iomem *ioaddr;
+       unsigned long len;
+       unsigned int chip_id = ent->driver_data;
+       struct net_device *dev;
+       void *ring_space;
+       dma_addr_t ring_dma;
+       u8 addr[ETH_ALEN];
+#ifdef USE_IO_OPS
+       int bar = 0;
+#else
+       int bar = 1;
+#endif
+
+       card_idx++;
+       sprintf(boardname, "fealnx%d", card_idx);
+
+       option = card_idx < MAX_UNITS ? options[card_idx] : 0;
+
+       i = pci_enable_device(pdev);
+       if (i) return i;
+       pci_set_master(pdev);
+
+       len = pci_resource_len(pdev, bar);
+       if (len < MIN_REGION_SIZE) {
+               dev_err(&pdev->dev,
+                          "region size %ld too small, aborting\n", len);
+               return -ENODEV;
+       }
+
+       i = pci_request_regions(pdev, boardname);
+       if (i)
+               return i;
+
+       irq = pdev->irq;
+
+       ioaddr = pci_iomap(pdev, bar, len);
+       if (!ioaddr) {
+               err = -ENOMEM;
+               goto err_out_res;
+       }
+
+       dev = alloc_etherdev(sizeof(struct netdev_private));
+       if (!dev) {
+               err = -ENOMEM;
+               goto err_out_unmap;
+       }
+       SET_NETDEV_DEV(dev, &pdev->dev);
+
+       /* read ethernet id */
+       for (i = 0; i < 6; ++i)
+               addr[i] = ioread8(ioaddr + PAR0 + i);
+       eth_hw_addr_set(dev, addr);
+
+       /* Reset the chip to erase previous misconfiguration. */
+       iowrite32(0x00000001, ioaddr + BCR);
+
+       /* Make certain the descriptor lists are aligned. */
+       np = netdev_priv(dev);
+       np->mem = ioaddr;
+       spin_lock_init(&np->lock);
+       np->pci_dev = pdev;
+       np->flags = skel_netdrv_tbl[chip_id].flags;
+       pci_set_drvdata(pdev, dev);
+       np->mii.dev = dev;
+       np->mii.mdio_read = mdio_read;
+       np->mii.mdio_write = mdio_write;
+       np->mii.phy_id_mask = 0x1f;
+       np->mii.reg_num_mask = 0x1f;
+
+       ring_space = dma_alloc_coherent(&pdev->dev, RX_TOTAL_SIZE, &ring_dma,
+                                       GFP_KERNEL);
+       if (!ring_space) {
+               err = -ENOMEM;
+               goto err_out_free_dev;
+       }
+       np->rx_ring = ring_space;
+       np->rx_ring_dma = ring_dma;
+
+       ring_space = dma_alloc_coherent(&pdev->dev, TX_TOTAL_SIZE, &ring_dma,
+                                       GFP_KERNEL);
+       if (!ring_space) {
+               err = -ENOMEM;
+               goto err_out_free_rx;
+       }
+       np->tx_ring = ring_space;
+       np->tx_ring_dma = ring_dma;
+
+       /* find the connected MII xcvrs */
+       if (np->flags == HAS_MII_XCVR) {
+               int phy, phy_idx = 0;
+
+               for (phy = 1; phy < 32 && phy_idx < ARRAY_SIZE(np->phys);
+                              phy++) {
+                       int mii_status = mdio_read(dev, phy, 1);
+
+                       if (mii_status != 0xffff && mii_status != 0x0000) {
+                               np->phys[phy_idx++] = phy;
+                               dev_info(&pdev->dev,
+                                      "MII PHY found at address %d, status "
+                                      "0x%4.4x.\n", phy, mii_status);
+                               /* get phy type */
+                               {
+                                       unsigned int data;
+
+                                       data = mdio_read(dev, np->phys[0], 2);
+                                       if (data == SeeqPHYID0)
+                                               np->PHYType = SeeqPHY;
+                                       else if (data == AhdocPHYID0)
+                                               np->PHYType = AhdocPHY;
+                                       else if (data == MarvellPHYID0)
+                                               np->PHYType = MarvellPHY;
+                                       else if (data == MysonPHYID0)
+                                               np->PHYType = Myson981;
+                                       else if (data == LevelOnePHYID0)
+                                               np->PHYType = LevelOnePHY;
+                                       else
+                                               np->PHYType = OtherPHY;
+                               }
+                       }
+               }
+
+               np->mii_cnt = phy_idx;
+               if (phy_idx == 0)
+                       dev_warn(&pdev->dev,
+                               "MII PHY not found -- this device may "
+                              "not operate correctly.\n");
+       } else {
+               np->phys[0] = 32;
+/* 89/6/23 add, (begin) */
+               /* get phy type */
+               if (ioread32(ioaddr + PHYIDENTIFIER) == MysonPHYID)
+                       np->PHYType = MysonPHY;
+               else
+                       np->PHYType = OtherPHY;
+       }
+       np->mii.phy_id = np->phys[0];
+
+       if (dev->mem_start)
+               option = dev->mem_start;
+
+       /* The lower four bits are the media type. */
+       if (option > 0) {
+               if (option & 0x200)
+                       np->mii.full_duplex = 1;
+               np->default_port = option & 15;
+       }
+
+       if (card_idx < MAX_UNITS && full_duplex[card_idx] > 0)
+               np->mii.full_duplex = full_duplex[card_idx];
+
+       if (np->mii.full_duplex) {
+               dev_info(&pdev->dev, "Media type forced to Full Duplex.\n");
+/* 89/6/13 add, (begin) */
+//      if (np->PHYType==MarvellPHY)
+               if ((np->PHYType == MarvellPHY) || (np->PHYType == LevelOnePHY)) {
+                       unsigned int data;
+
+                       data = mdio_read(dev, np->phys[0], 9);
+                       data = (data & 0xfcff) | 0x0200;
+                       mdio_write(dev, np->phys[0], 9, data);
+               }
+/* 89/6/13 add, (end) */
+               if (np->flags == HAS_MII_XCVR)
+                       mdio_write(dev, np->phys[0], MII_ADVERTISE, ADVERTISE_FULL);
+               else
+                       iowrite32(ADVERTISE_FULL, ioaddr + ANARANLPAR);
+               np->mii.force_media = 1;
+       }
+
+       dev->netdev_ops = &netdev_ops;
+       dev->ethtool_ops = &netdev_ethtool_ops;
+       dev->watchdog_timeo = TX_TIMEOUT;
+
+       err = register_netdev(dev);
+       if (err)
+               goto err_out_free_tx;
+
+       printk(KERN_INFO "%s: %s at %p, %pM, IRQ %d.\n",
+              dev->name, skel_netdrv_tbl[chip_id].chip_name, ioaddr,
+              dev->dev_addr, irq);
+
+       return 0;
+
+err_out_free_tx:
+       dma_free_coherent(&pdev->dev, TX_TOTAL_SIZE, np->tx_ring,
+                         np->tx_ring_dma);
+err_out_free_rx:
+       dma_free_coherent(&pdev->dev, RX_TOTAL_SIZE, np->rx_ring,
+                         np->rx_ring_dma);
+err_out_free_dev:
+       free_netdev(dev);
+err_out_unmap:
+       pci_iounmap(pdev, ioaddr);
+err_out_res:
+       pci_release_regions(pdev);
+       return err;
+}
+
+
+static void fealnx_remove_one(struct pci_dev *pdev)
+{
+       struct net_device *dev = pci_get_drvdata(pdev);
+
+       if (dev) {
+               struct netdev_private *np = netdev_priv(dev);
+
+               dma_free_coherent(&pdev->dev, TX_TOTAL_SIZE, np->tx_ring,
+                                 np->tx_ring_dma);
+               dma_free_coherent(&pdev->dev, RX_TOTAL_SIZE, np->rx_ring,
+                                 np->rx_ring_dma);
+               unregister_netdev(dev);
+               pci_iounmap(pdev, np->mem);
+               free_netdev(dev);
+               pci_release_regions(pdev);
+       } else
+               printk(KERN_ERR "fealnx: remove for unknown device\n");
+}
+
+
+static ulong m80x_send_cmd_to_phy(void __iomem *miiport, int opcode, int phyad, int regad)
+{
+       ulong miir;
+       int i;
+       unsigned int mask, data;
+
+       /* enable MII output */
+       miir = (ulong) ioread32(miiport);
+       miir &= 0xfffffff0;
+
+       miir |= MASK_MIIR_MII_WRITE + MASK_MIIR_MII_MDO;
+
+       /* send 32 1's preamble */
+       for (i = 0; i < 32; i++) {
+               /* low MDC; MDO is already high (miir) */
+               miir &= ~MASK_MIIR_MII_MDC;
+               iowrite32(miir, miiport);
+
+               /* high MDC */
+               miir |= MASK_MIIR_MII_MDC;
+               iowrite32(miir, miiport);
+       }
+
+       /* calculate ST+OP+PHYAD+REGAD+TA */
+       data = opcode | (phyad << 7) | (regad << 2);
+
+       /* sent out */
+       mask = 0x8000;
+       while (mask) {
+               /* low MDC, prepare MDO */
+               miir &= ~(MASK_MIIR_MII_MDC + MASK_MIIR_MII_MDO);
+               if (mask & data)
+                       miir |= MASK_MIIR_MII_MDO;
+
+               iowrite32(miir, miiport);
+               /* high MDC */
+               miir |= MASK_MIIR_MII_MDC;
+               iowrite32(miir, miiport);
+               udelay(30);
+
+               /* next */
+               mask >>= 1;
+               if (mask == 0x2 && opcode == OP_READ)
+                       miir &= ~MASK_MIIR_MII_WRITE;
+       }
+       return miir;
+}
+
+
+static int mdio_read(struct net_device *dev, int phyad, int regad)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem *miiport = np->mem + MANAGEMENT;
+       ulong miir;
+       unsigned int mask, data;
+
+       miir = m80x_send_cmd_to_phy(miiport, OP_READ, phyad, regad);
+
+       /* read data */
+       mask = 0x8000;
+       data = 0;
+       while (mask) {
+               /* low MDC */
+               miir &= ~MASK_MIIR_MII_MDC;
+               iowrite32(miir, miiport);
+
+               /* read MDI */
+               miir = ioread32(miiport);
+               if (miir & MASK_MIIR_MII_MDI)
+                       data |= mask;
+
+               /* high MDC, and wait */
+               miir |= MASK_MIIR_MII_MDC;
+               iowrite32(miir, miiport);
+               udelay(30);
+
+               /* next */
+               mask >>= 1;
+       }
+
+       /* low MDC */
+       miir &= ~MASK_MIIR_MII_MDC;
+       iowrite32(miir, miiport);
+
+       return data & 0xffff;
+}
+
+
+static void mdio_write(struct net_device *dev, int phyad, int regad, int data)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem *miiport = np->mem + MANAGEMENT;
+       ulong miir;
+       unsigned int mask;
+
+       miir = m80x_send_cmd_to_phy(miiport, OP_WRITE, phyad, regad);
+
+       /* write data */
+       mask = 0x8000;
+       while (mask) {
+               /* low MDC, prepare MDO */
+               miir &= ~(MASK_MIIR_MII_MDC + MASK_MIIR_MII_MDO);
+               if (mask & data)
+                       miir |= MASK_MIIR_MII_MDO;
+               iowrite32(miir, miiport);
+
+               /* high MDC */
+               miir |= MASK_MIIR_MII_MDC;
+               iowrite32(miir, miiport);
+
+               /* next */
+               mask >>= 1;
+       }
+
+       /* low MDC */
+       miir &= ~MASK_MIIR_MII_MDC;
+       iowrite32(miir, miiport);
+}
+
+
+static int netdev_open(struct net_device *dev)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem *ioaddr = np->mem;
+       const int irq = np->pci_dev->irq;
+       int rc, i;
+
+       iowrite32(0x00000001, ioaddr + BCR);    /* Reset */
+
+       rc = request_irq(irq, intr_handler, IRQF_SHARED, dev->name, dev);
+       if (rc)
+               return -EAGAIN;
+
+       for (i = 0; i < 3; i++)
+               iowrite16(((const unsigned short *)dev->dev_addr)[i],
+                               ioaddr + PAR0 + i*2);
+
+       init_ring(dev);
+
+       iowrite32(np->rx_ring_dma, ioaddr + RXLBA);
+       iowrite32(np->tx_ring_dma, ioaddr + TXLBA);
+
+       /* Initialize other registers. */
+       /* Configure the PCI bus bursts and FIFO thresholds.
+          486: Set 8 longword burst.
+          586: no burst limit.
+          Burst length 5:3
+          0 0 0   1
+          0 0 1   4
+          0 1 0   8
+          0 1 1   16
+          1 0 0   32
+          1 0 1   64
+          1 1 0   128
+          1 1 1   256
+          Wait the specified 50 PCI cycles after a reset by initializing
+          Tx and Rx queues and the address filter list.
+          FIXME (Ueimor): optimistic for alpha + posted writes ? */
+
+       np->bcrvalue = 0x10;    /* little-endian, 8 burst length */
+#ifdef __BIG_ENDIAN
+       np->bcrvalue |= 0x04;   /* big-endian */
+#endif
+
+#if defined(__i386__) && !defined(MODULE) && !defined(CONFIG_UML)
+       if (boot_cpu_data.x86 <= 4)
+               np->crvalue = 0xa00;
+       else
+#endif
+               np->crvalue = 0xe00;    /* rx 128 burst length */
+
+
+// 89/12/29 add,
+// 90/1/16 modify,
+//   np->imrvalue=FBE|TUNF|CNTOVF|RBU|TI|RI;
+       np->imrvalue = TUNF | CNTOVF | RBU | TI | RI;
+       if (np->pci_dev->device == 0x891) {
+               np->bcrvalue |= 0x200;  /* set PROG bit */
+               np->crvalue |= CR_W_ENH;        /* set enhanced bit */
+               np->imrvalue |= ETI;
+       }
+       iowrite32(np->bcrvalue, ioaddr + BCR);
+
+       if (dev->if_port == 0)
+               dev->if_port = np->default_port;
+
+       iowrite32(0, ioaddr + RXPDR);
+// 89/9/1 modify,
+//   np->crvalue = 0x00e40001;    /* tx store and forward, tx/rx enable */
+       np->crvalue |= 0x00e40001;      /* tx store and forward, tx/rx enable */
+       np->mii.full_duplex = np->mii.force_media;
+       getlinkstatus(dev);
+       if (np->linkok)
+               getlinktype(dev);
+       __set_rx_mode(dev);
+
+       netif_start_queue(dev);
+
+       /* Clear and Enable interrupts by setting the interrupt mask. */
+       iowrite32(FBE | TUNF | CNTOVF | RBU | TI | RI, ioaddr + ISR);
+       iowrite32(np->imrvalue, ioaddr + IMR);
+
+       if (debug)
+               printk(KERN_DEBUG "%s: Done netdev_open().\n", dev->name);
+
+       /* Set the timer to check for link beat. */
+       timer_setup(&np->timer, netdev_timer, 0);
+       np->timer.expires = RUN_AT(3 * HZ);
+
+       /* timer handler */
+       add_timer(&np->timer);
+
+       timer_setup(&np->reset_timer, reset_timer, 0);
+       np->reset_timer_armed = 0;
+       return rc;
+}
+
+
+static void getlinkstatus(struct net_device *dev)
+/* function: Routine will read MII Status Register to get link status.       */
+/* input   : dev... pointer to the adapter block.                            */
+/* output  : none.                                                           */
+{
+       struct netdev_private *np = netdev_priv(dev);
+       unsigned int i, DelayTime = 0x1000;
+
+       np->linkok = 0;
+
+       if (np->PHYType == MysonPHY) {
+               for (i = 0; i < DelayTime; ++i) {
+                       if (ioread32(np->mem + BMCRSR) & LinkIsUp2) {
+                               np->linkok = 1;
+                               return;
+                       }
+                       udelay(100);
+               }
+       } else {
+               for (i = 0; i < DelayTime; ++i) {
+                       if (mdio_read(dev, np->phys[0], MII_BMSR) & BMSR_LSTATUS) {
+                               np->linkok = 1;
+                               return;
+                       }
+                       udelay(100);
+               }
+       }
+}
+
+
+static void getlinktype(struct net_device *dev)
+{
+       struct netdev_private *np = netdev_priv(dev);
+
+       if (np->PHYType == MysonPHY) {  /* 3-in-1 case */
+               if (ioread32(np->mem + TCRRCR) & CR_R_FD)
+                       np->duplexmode = 2;     /* full duplex */
+               else
+                       np->duplexmode = 1;     /* half duplex */
+               if (ioread32(np->mem + TCRRCR) & CR_R_PS10)
+                       np->line_speed = 1;     /* 10M */
+               else
+                       np->line_speed = 2;     /* 100M */
+       } else {
+               if (np->PHYType == SeeqPHY) {   /* this PHY is SEEQ 80225 */
+                       unsigned int data;
+
+                       data = mdio_read(dev, np->phys[0], MIIRegister18);
+                       if (data & SPD_DET_100)
+                               np->line_speed = 2;     /* 100M */
+                       else
+                               np->line_speed = 1;     /* 10M */
+                       if (data & DPLX_DET_FULL)
+                               np->duplexmode = 2;     /* full duplex mode */
+                       else
+                               np->duplexmode = 1;     /* half duplex mode */
+               } else if (np->PHYType == AhdocPHY) {
+                       unsigned int data;
+
+                       data = mdio_read(dev, np->phys[0], DiagnosticReg);
+                       if (data & Speed_100)
+                               np->line_speed = 2;     /* 100M */
+                       else
+                               np->line_speed = 1;     /* 10M */
+                       if (data & DPLX_FULL)
+                               np->duplexmode = 2;     /* full duplex mode */
+                       else
+                               np->duplexmode = 1;     /* half duplex mode */
+               }
+/* 89/6/13 add, (begin) */
+               else if (np->PHYType == MarvellPHY) {
+                       unsigned int data;
+
+                       data = mdio_read(dev, np->phys[0], SpecificReg);
+                       if (data & Full_Duplex)
+                               np->duplexmode = 2;     /* full duplex mode */
+                       else
+                               np->duplexmode = 1;     /* half duplex mode */
+                       data &= SpeedMask;
+                       if (data == Speed_1000M)
+                               np->line_speed = 3;     /* 1000M */
+                       else if (data == Speed_100M)
+                               np->line_speed = 2;     /* 100M */
+                       else
+                               np->line_speed = 1;     /* 10M */
+               }
+/* 89/6/13 add, (end) */
+/* 89/7/27 add, (begin) */
+               else if (np->PHYType == Myson981) {
+                       unsigned int data;
+
+                       data = mdio_read(dev, np->phys[0], StatusRegister);
+
+                       if (data & SPEED100)
+                               np->line_speed = 2;
+                       else
+                               np->line_speed = 1;
+
+                       if (data & FULLMODE)
+                               np->duplexmode = 2;
+                       else
+                               np->duplexmode = 1;
+               }
+/* 89/7/27 add, (end) */
+/* 89/12/29 add */
+               else if (np->PHYType == LevelOnePHY) {
+                       unsigned int data;
+
+                       data = mdio_read(dev, np->phys[0], SpecificReg);
+                       if (data & LXT1000_Full)
+                               np->duplexmode = 2;     /* full duplex mode */
+                       else
+                               np->duplexmode = 1;     /* half duplex mode */
+                       data &= SpeedMask;
+                       if (data == LXT1000_1000M)
+                               np->line_speed = 3;     /* 1000M */
+                       else if (data == LXT1000_100M)
+                               np->line_speed = 2;     /* 100M */
+                       else
+                               np->line_speed = 1;     /* 10M */
+               }
+               np->crvalue &= (~CR_W_PS10) & (~CR_W_FD) & (~CR_W_PS1000);
+               if (np->line_speed == 1)
+                       np->crvalue |= CR_W_PS10;
+               else if (np->line_speed == 3)
+                       np->crvalue |= CR_W_PS1000;
+               if (np->duplexmode == 2)
+                       np->crvalue |= CR_W_FD;
+       }
+}
+
+
+/* Take lock before calling this */
+static void allocate_rx_buffers(struct net_device *dev)
+{
+       struct netdev_private *np = netdev_priv(dev);
+
+       /*  allocate skb for rx buffers */
+       while (np->really_rx_count != RX_RING_SIZE) {
+               struct sk_buff *skb;
+
+               skb = netdev_alloc_skb(dev, np->rx_buf_sz);
+               if (skb == NULL)
+                       break;  /* Better luck next round. */
+
+               while (np->lack_rxbuf->skbuff)
+                       np->lack_rxbuf = np->lack_rxbuf->next_desc_logical;
+
+               np->lack_rxbuf->skbuff = skb;
+               np->lack_rxbuf->buffer = dma_map_single(&np->pci_dev->dev,
+                                                       skb->data,
+                                                       np->rx_buf_sz,
+                                                       DMA_FROM_DEVICE);
+               np->lack_rxbuf->status = RXOWN;
+               ++np->really_rx_count;
+       }
+}
+
+
+static void netdev_timer(struct timer_list *t)
+{
+       struct netdev_private *np = from_timer(np, t, timer);
+       struct net_device *dev = np->mii.dev;
+       void __iomem *ioaddr = np->mem;
+       int old_crvalue = np->crvalue;
+       unsigned int old_linkok = np->linkok;
+       unsigned long flags;
+
+       if (debug)
+               printk(KERN_DEBUG "%s: Media selection timer tick, status %8.8x "
+                      "config %8.8x.\n", dev->name, ioread32(ioaddr + ISR),
+                      ioread32(ioaddr + TCRRCR));
+
+       spin_lock_irqsave(&np->lock, flags);
+
+       if (np->flags == HAS_MII_XCVR) {
+               getlinkstatus(dev);
+               if ((old_linkok == 0) && (np->linkok == 1)) {   /* we need to detect the media type again */
+                       getlinktype(dev);
+                       if (np->crvalue != old_crvalue) {
+                               stop_nic_rxtx(ioaddr, np->crvalue);
+                               iowrite32(np->crvalue, ioaddr + TCRRCR);
+                       }
+               }
+       }
+
+       allocate_rx_buffers(dev);
+
+       spin_unlock_irqrestore(&np->lock, flags);
+
+       np->timer.expires = RUN_AT(10 * HZ);
+       add_timer(&np->timer);
+}
+
+
+/* Take lock before calling */
+/* Reset chip and disable rx, tx and interrupts */
+static void reset_and_disable_rxtx(struct net_device *dev)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem *ioaddr = np->mem;
+       int delay=51;
+
+       /* Reset the chip's Tx and Rx processes. */
+       stop_nic_rxtx(ioaddr, 0);
+
+       /* Disable interrupts by clearing the interrupt mask. */
+       iowrite32(0, ioaddr + IMR);
+
+       /* Reset the chip to erase previous misconfiguration. */
+       iowrite32(0x00000001, ioaddr + BCR);
+
+       /* Ueimor: wait for 50 PCI cycles (and flush posted writes btw).
+          We surely wait too long (address+data phase). Who cares? */
+       while (--delay) {
+               ioread32(ioaddr + BCR);
+               rmb();
+       }
+}
+
+
+/* Take lock before calling */
+/* Restore chip after reset */
+static void enable_rxtx(struct net_device *dev)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem *ioaddr = np->mem;
+
+       reset_rx_descriptors(dev);
+
+       iowrite32(np->tx_ring_dma + ((char*)np->cur_tx - (char*)np->tx_ring),
+               ioaddr + TXLBA);
+       iowrite32(np->rx_ring_dma + ((char*)np->cur_rx - (char*)np->rx_ring),
+               ioaddr + RXLBA);
+
+       iowrite32(np->bcrvalue, ioaddr + BCR);
+
+       iowrite32(0, ioaddr + RXPDR);
+       __set_rx_mode(dev); /* changes np->crvalue, writes it into TCRRCR */
+
+       /* Clear and Enable interrupts by setting the interrupt mask. */
+       iowrite32(FBE | TUNF | CNTOVF | RBU | TI | RI, ioaddr + ISR);
+       iowrite32(np->imrvalue, ioaddr + IMR);
+
+       iowrite32(0, ioaddr + TXPDR);
+}
+
+
+static void reset_timer(struct timer_list *t)
+{
+       struct netdev_private *np = from_timer(np, t, reset_timer);
+       struct net_device *dev = np->mii.dev;
+       unsigned long flags;
+
+       printk(KERN_WARNING "%s: resetting tx and rx machinery\n", dev->name);
+
+       spin_lock_irqsave(&np->lock, flags);
+       np->crvalue = np->crvalue_sv;
+       np->imrvalue = np->imrvalue_sv;
+
+       reset_and_disable_rxtx(dev);
+       /* works for me without this:
+       reset_tx_descriptors(dev); */
+       enable_rxtx(dev);
+       netif_start_queue(dev); /* FIXME: or netif_wake_queue(dev); ? */
+
+       np->reset_timer_armed = 0;
+
+       spin_unlock_irqrestore(&np->lock, flags);
+}
+
+
+static void fealnx_tx_timeout(struct net_device *dev, unsigned int txqueue)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem *ioaddr = np->mem;
+       unsigned long flags;
+       int i;
+
+       printk(KERN_WARNING
+              "%s: Transmit timed out, status %8.8x, resetting...\n",
+              dev->name, ioread32(ioaddr + ISR));
+
+       {
+               printk(KERN_DEBUG "  Rx ring %p: ", np->rx_ring);
+               for (i = 0; i < RX_RING_SIZE; i++)
+                       printk(KERN_CONT " %8.8x",
+                              (unsigned int) np->rx_ring[i].status);
+               printk(KERN_CONT "\n");
+               printk(KERN_DEBUG "  Tx ring %p: ", np->tx_ring);
+               for (i = 0; i < TX_RING_SIZE; i++)
+                       printk(KERN_CONT " %4.4x", np->tx_ring[i].status);
+               printk(KERN_CONT "\n");
+       }
+
+       spin_lock_irqsave(&np->lock, flags);
+
+       reset_and_disable_rxtx(dev);
+       reset_tx_descriptors(dev);
+       enable_rxtx(dev);
+
+       spin_unlock_irqrestore(&np->lock, flags);
+
+       netif_trans_update(dev); /* prevent tx timeout */
+       dev->stats.tx_errors++;
+       netif_wake_queue(dev); /* or .._start_.. ?? */
+}
+
+
+/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
+static void init_ring(struct net_device *dev)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       int i;
+
+       /* initialize rx variables */
+       np->rx_buf_sz = (dev->mtu <= 1500 ? PKT_BUF_SZ : dev->mtu + 32);
+       np->cur_rx = &np->rx_ring[0];
+       np->lack_rxbuf = np->rx_ring;
+       np->really_rx_count = 0;
+
+       /* initial rx descriptors. */
+       for (i = 0; i < RX_RING_SIZE; i++) {
+               np->rx_ring[i].status = 0;
+               np->rx_ring[i].control = np->rx_buf_sz << RBSShift;
+               np->rx_ring[i].next_desc = np->rx_ring_dma +
+                       (i + 1)*sizeof(struct fealnx_desc);
+               np->rx_ring[i].next_desc_logical = &np->rx_ring[i + 1];
+               np->rx_ring[i].skbuff = NULL;
+       }
+
+       /* for the last rx descriptor */
+       np->rx_ring[i - 1].next_desc = np->rx_ring_dma;
+       np->rx_ring[i - 1].next_desc_logical = np->rx_ring;
+
+       /* allocate skb for rx buffers */
+       for (i = 0; i < RX_RING_SIZE; i++) {
+               struct sk_buff *skb = netdev_alloc_skb(dev, np->rx_buf_sz);
+
+               if (skb == NULL) {
+                       np->lack_rxbuf = &np->rx_ring[i];
+                       break;
+               }
+
+               ++np->really_rx_count;
+               np->rx_ring[i].skbuff = skb;
+               np->rx_ring[i].buffer = dma_map_single(&np->pci_dev->dev,
+                                                      skb->data,
+                                                      np->rx_buf_sz,
+                                                      DMA_FROM_DEVICE);
+               np->rx_ring[i].status = RXOWN;
+               np->rx_ring[i].control |= RXIC;
+       }
+
+       /* initialize tx variables */
+       np->cur_tx = &np->tx_ring[0];
+       np->cur_tx_copy = &np->tx_ring[0];
+       np->really_tx_count = 0;
+       np->free_tx_count = TX_RING_SIZE;
+
+       for (i = 0; i < TX_RING_SIZE; i++) {
+               np->tx_ring[i].status = 0;
+               /* do we need np->tx_ring[i].control = XXX; ?? */
+               np->tx_ring[i].next_desc = np->tx_ring_dma +
+                       (i + 1)*sizeof(struct fealnx_desc);
+               np->tx_ring[i].next_desc_logical = &np->tx_ring[i + 1];
+               np->tx_ring[i].skbuff = NULL;
+       }
+
+       /* for the last tx descriptor */
+       np->tx_ring[i - 1].next_desc = np->tx_ring_dma;
+       np->tx_ring[i - 1].next_desc_logical = &np->tx_ring[0];
+}
+
+
+static netdev_tx_t start_tx(struct sk_buff *skb, struct net_device *dev)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       unsigned long flags;
+
+       spin_lock_irqsave(&np->lock, flags);
+
+       np->cur_tx_copy->skbuff = skb;
+
+#define one_buffer
+#define BPT 1022
+#if defined(one_buffer)
+       np->cur_tx_copy->buffer = dma_map_single(&np->pci_dev->dev, skb->data,
+                                                skb->len, DMA_TO_DEVICE);
+       np->cur_tx_copy->control = TXIC | TXLD | TXFD | CRCEnable | PADEnable;
+       np->cur_tx_copy->control |= (skb->len << PKTSShift);    /* pkt size */
+       np->cur_tx_copy->control |= (skb->len << TBSShift);     /* buffer size */
+// 89/12/29 add,
+       if (np->pci_dev->device == 0x891)
+               np->cur_tx_copy->control |= ETIControl | RetryTxLC;
+       np->cur_tx_copy->status = TXOWN;
+       np->cur_tx_copy = np->cur_tx_copy->next_desc_logical;
+       --np->free_tx_count;
+#elif defined(two_buffer)
+       if (skb->len > BPT) {
+               struct fealnx_desc *next;
+
+               /* for the first descriptor */
+               np->cur_tx_copy->buffer = dma_map_single(&np->pci_dev->dev,
+                                                        skb->data, BPT,
+                                                        DMA_TO_DEVICE);
+               np->cur_tx_copy->control = TXIC | TXFD | CRCEnable | PADEnable;
+               np->cur_tx_copy->control |= (skb->len << PKTSShift);    /* pkt size */
+               np->cur_tx_copy->control |= (BPT << TBSShift);  /* buffer size */
+
+               /* for the last descriptor */
+               next = np->cur_tx_copy->next_desc_logical;
+               next->skbuff = skb;
+               next->control = TXIC | TXLD | CRCEnable | PADEnable;
+               next->control |= (skb->len << PKTSShift);       /* pkt size */
+               next->control |= ((skb->len - BPT) << TBSShift);        /* buf size */
+// 89/12/29 add,
+               if (np->pci_dev->device == 0x891)
+                       np->cur_tx_copy->control |= ETIControl | RetryTxLC;
+               next->buffer = dma_map_single(&ep->pci_dev->dev,
+                                             skb->data + BPT, skb->len - BPT,
+                                             DMA_TO_DEVICE);
+
+               next->status = TXOWN;
+               np->cur_tx_copy->status = TXOWN;
+
+               np->cur_tx_copy = next->next_desc_logical;
+               np->free_tx_count -= 2;
+       } else {
+               np->cur_tx_copy->buffer = dma_map_single(&np->pci_dev->dev,
+                                                        skb->data, skb->len,
+                                                        DMA_TO_DEVICE);
+               np->cur_tx_copy->control = TXIC | TXLD | TXFD | CRCEnable | PADEnable;
+               np->cur_tx_copy->control |= (skb->len << PKTSShift);    /* pkt size */
+               np->cur_tx_copy->control |= (skb->len << TBSShift);     /* buffer size */
+// 89/12/29 add,
+               if (np->pci_dev->device == 0x891)
+                       np->cur_tx_copy->control |= ETIControl | RetryTxLC;
+               np->cur_tx_copy->status = TXOWN;
+               np->cur_tx_copy = np->cur_tx_copy->next_desc_logical;
+               --np->free_tx_count;
+       }
+#endif
+
+       if (np->free_tx_count < 2)
+               netif_stop_queue(dev);
+       ++np->really_tx_count;
+       iowrite32(0, np->mem + TXPDR);
+
+       spin_unlock_irqrestore(&np->lock, flags);
+       return NETDEV_TX_OK;
+}
+
+
+/* Take lock before calling */
+/* Chip probably hosed tx ring. Clean up. */
+static void reset_tx_descriptors(struct net_device *dev)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       struct fealnx_desc *cur;
+       int i;
+
+       /* initialize tx variables */
+       np->cur_tx = &np->tx_ring[0];
+       np->cur_tx_copy = &np->tx_ring[0];
+       np->really_tx_count = 0;
+       np->free_tx_count = TX_RING_SIZE;
+
+       for (i = 0; i < TX_RING_SIZE; i++) {
+               cur = &np->tx_ring[i];
+               if (cur->skbuff) {
+                       dma_unmap_single(&np->pci_dev->dev, cur->buffer,
+                                        cur->skbuff->len, DMA_TO_DEVICE);
+                       dev_kfree_skb_any(cur->skbuff);
+                       cur->skbuff = NULL;
+               }
+               cur->status = 0;
+               cur->control = 0;       /* needed? */
+               /* probably not needed. We do it for purely paranoid reasons */
+               cur->next_desc = np->tx_ring_dma +
+                       (i + 1)*sizeof(struct fealnx_desc);
+               cur->next_desc_logical = &np->tx_ring[i + 1];
+       }
+       /* for the last tx descriptor */
+       np->tx_ring[TX_RING_SIZE - 1].next_desc = np->tx_ring_dma;
+       np->tx_ring[TX_RING_SIZE - 1].next_desc_logical = &np->tx_ring[0];
+}
+
+
+/* Take lock and stop rx before calling this */
+static void reset_rx_descriptors(struct net_device *dev)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       struct fealnx_desc *cur = np->cur_rx;
+       int i;
+
+       allocate_rx_buffers(dev);
+
+       for (i = 0; i < RX_RING_SIZE; i++) {
+               if (cur->skbuff)
+                       cur->status = RXOWN;
+               cur = cur->next_desc_logical;
+       }
+
+       iowrite32(np->rx_ring_dma + ((char*)np->cur_rx - (char*)np->rx_ring),
+               np->mem + RXLBA);
+}
+
+
+/* The interrupt handler does all of the Rx thread work and cleans up
+   after the Tx thread. */
+static irqreturn_t intr_handler(int irq, void *dev_instance)
+{
+       struct net_device *dev = (struct net_device *) dev_instance;
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem *ioaddr = np->mem;
+       long boguscnt = max_interrupt_work;
+       unsigned int num_tx = 0;
+       int handled = 0;
+
+       spin_lock(&np->lock);
+
+       iowrite32(0, ioaddr + IMR);
+
+       do {
+               u32 intr_status = ioread32(ioaddr + ISR);
+
+               /* Acknowledge all of the current interrupt sources ASAP. */
+               iowrite32(intr_status, ioaddr + ISR);
+
+               if (debug)
+                       printk(KERN_DEBUG "%s: Interrupt, status %4.4x.\n", dev->name,
+                              intr_status);
+
+               if (!(intr_status & np->imrvalue))
+                       break;
+
+               handled = 1;
+
+// 90/1/16 delete,
+//
+//      if (intr_status & FBE)
+//      {   /* fatal error */
+//          stop_nic_tx(ioaddr, 0);
+//          stop_nic_rx(ioaddr, 0);
+//          break;
+//      };
+
+               if (intr_status & TUNF)
+                       iowrite32(0, ioaddr + TXPDR);
+
+               if (intr_status & CNTOVF) {
+                       /* missed pkts */
+                       dev->stats.rx_missed_errors +=
+                               ioread32(ioaddr + TALLY) & 0x7fff;
+
+                       /* crc error */
+                       dev->stats.rx_crc_errors +=
+                           (ioread32(ioaddr + TALLY) & 0x7fff0000) >> 16;
+               }
+
+               if (intr_status & (RI | RBU)) {
+                       if (intr_status & RI)
+                               netdev_rx(dev);
+                       else {
+                               stop_nic_rx(ioaddr, np->crvalue);
+                               reset_rx_descriptors(dev);
+                               iowrite32(np->crvalue, ioaddr + TCRRCR);
+                       }
+               }
+
+               while (np->really_tx_count) {
+                       long tx_status = np->cur_tx->status;
+                       long tx_control = np->cur_tx->control;
+
+                       if (!(tx_control & TXLD)) {     /* this pkt is combined by two tx descriptors */
+                               struct fealnx_desc *next;
+
+                               next = np->cur_tx->next_desc_logical;
+                               tx_status = next->status;
+                               tx_control = next->control;
+                       }
+
+                       if (tx_status & TXOWN)
+                               break;
+
+                       if (!(np->crvalue & CR_W_ENH)) {
+                               if (tx_status & (CSL | LC | EC | UDF | HF)) {
+                                       dev->stats.tx_errors++;
+                                       if (tx_status & EC)
+                                               dev->stats.tx_aborted_errors++;
+                                       if (tx_status & CSL)
+                                               dev->stats.tx_carrier_errors++;
+                                       if (tx_status & LC)
+                                               dev->stats.tx_window_errors++;
+                                       if (tx_status & UDF)
+                                               dev->stats.tx_fifo_errors++;
+                                       if ((tx_status & HF) && np->mii.full_duplex == 0)
+                                               dev->stats.tx_heartbeat_errors++;
+
+                               } else {
+                                       dev->stats.tx_bytes +=
+                                           ((tx_control & PKTSMask) >> PKTSShift);
+
+                                       dev->stats.collisions +=
+                                           ((tx_status & NCRMask) >> NCRShift);
+                                       dev->stats.tx_packets++;
+                               }
+                       } else {
+                               dev->stats.tx_bytes +=
+                                   ((tx_control & PKTSMask) >> PKTSShift);
+                               dev->stats.tx_packets++;
+                       }
+
+                       /* Free the original skb. */
+                       dma_unmap_single(&np->pci_dev->dev,
+                                        np->cur_tx->buffer,
+                                        np->cur_tx->skbuff->len,
+                                        DMA_TO_DEVICE);
+                       dev_consume_skb_irq(np->cur_tx->skbuff);
+                       np->cur_tx->skbuff = NULL;
+                       --np->really_tx_count;
+                       if (np->cur_tx->control & TXLD) {
+                               np->cur_tx = np->cur_tx->next_desc_logical;
+                               ++np->free_tx_count;
+                       } else {
+                               np->cur_tx = np->cur_tx->next_desc_logical;
+                               np->cur_tx = np->cur_tx->next_desc_logical;
+                               np->free_tx_count += 2;
+                       }
+                       num_tx++;
+               }               /* end of for loop */
+
+               if (num_tx && np->free_tx_count >= 2)
+                       netif_wake_queue(dev);
+
+               /* read transmit status for enhanced mode only */
+               if (np->crvalue & CR_W_ENH) {
+                       long data;
+
+                       data = ioread32(ioaddr + TSR);
+                       dev->stats.tx_errors += (data & 0xff000000) >> 24;
+                       dev->stats.tx_aborted_errors +=
+                               (data & 0xff000000) >> 24;
+                       dev->stats.tx_window_errors +=
+                               (data & 0x00ff0000) >> 16;
+                       dev->stats.collisions += (data & 0x0000ffff);
+               }
+
+               if (--boguscnt < 0) {
+                       printk(KERN_WARNING "%s: Too much work at interrupt, "
+                              "status=0x%4.4x.\n", dev->name, intr_status);
+                       if (!np->reset_timer_armed) {
+                               np->reset_timer_armed = 1;
+                               np->reset_timer.expires = RUN_AT(HZ/2);
+                               add_timer(&np->reset_timer);
+                               stop_nic_rxtx(ioaddr, 0);
+                               netif_stop_queue(dev);
+                               /* or netif_tx_disable(dev); ?? */
+                               /* Prevent other paths from enabling tx,rx,intrs */
+                               np->crvalue_sv = np->crvalue;
+                               np->imrvalue_sv = np->imrvalue;
+                               np->crvalue &= ~(CR_W_TXEN | CR_W_RXEN); /* or simply = 0? */
+                               np->imrvalue = 0;
+                       }
+
+                       break;
+               }
+       } while (1);
+
+       /* read the tally counters */
+       /* missed pkts */
+       dev->stats.rx_missed_errors += ioread32(ioaddr + TALLY) & 0x7fff;
+
+       /* crc error */
+       dev->stats.rx_crc_errors +=
+               (ioread32(ioaddr + TALLY) & 0x7fff0000) >> 16;
+
+       if (debug)
+               printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n",
+                      dev->name, ioread32(ioaddr + ISR));
+
+       iowrite32(np->imrvalue, ioaddr + IMR);
+
+       spin_unlock(&np->lock);
+
+       return IRQ_RETVAL(handled);
+}
+
+
+/* This routine is logically part of the interrupt handler, but separated
+   for clarity and better register allocation. */
+static int netdev_rx(struct net_device *dev)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem *ioaddr = np->mem;
+
+       /* If EOP is set on the next entry, it's a new packet. Send it up. */
+       while (!(np->cur_rx->status & RXOWN) && np->cur_rx->skbuff) {
+               s32 rx_status = np->cur_rx->status;
+
+               if (np->really_rx_count == 0)
+                       break;
+
+               if (debug)
+                       printk(KERN_DEBUG "  netdev_rx() status was %8.8x.\n", rx_status);
+
+               if ((!((rx_status & RXFSD) && (rx_status & RXLSD))) ||
+                   (rx_status & ErrorSummary)) {
+                       if (rx_status & ErrorSummary) { /* there was a fatal error */
+                               if (debug)
+                                       printk(KERN_DEBUG
+                                              "%s: Receive error, Rx status %8.8x.\n",
+                                              dev->name, rx_status);
+
+                               dev->stats.rx_errors++; /* end of a packet. */
+                               if (rx_status & (LONGPKT | RUNTPKT))
+                                       dev->stats.rx_length_errors++;
+                               if (rx_status & RXER)
+                                       dev->stats.rx_frame_errors++;
+                               if (rx_status & CRC)
+                                       dev->stats.rx_crc_errors++;
+                       } else {
+                               int need_to_reset = 0;
+                               int desno = 0;
+
+                               if (rx_status & RXFSD) {        /* this pkt is too long, over one rx buffer */
+                                       struct fealnx_desc *cur;
+
+                                       /* check this packet is received completely? */
+                                       cur = np->cur_rx;
+                                       while (desno <= np->really_rx_count) {
+                                               ++desno;
+                                               if ((!(cur->status & RXOWN)) &&
+                                                   (cur->status & RXLSD))
+                                                       break;
+                                               /* goto next rx descriptor */
+                                               cur = cur->next_desc_logical;
+                                       }
+                                       if (desno > np->really_rx_count)
+                                               need_to_reset = 1;
+                               } else  /* RXLSD did not find, something error */
+                                       need_to_reset = 1;
+
+                               if (need_to_reset == 0) {
+                                       int i;
+
+                                       dev->stats.rx_length_errors++;
+
+                                       /* free all rx descriptors related this long pkt */
+                                       for (i = 0; i < desno; ++i) {
+                                               if (!np->cur_rx->skbuff) {
+                                                       printk(KERN_DEBUG
+                                                               "%s: I'm scared\n", dev->name);
+                                                       break;
+                                               }
+                                               np->cur_rx->status = RXOWN;
+                                               np->cur_rx = np->cur_rx->next_desc_logical;
+                                       }
+                                       continue;
+                               } else {        /* rx error, need to reset this chip */
+                                       stop_nic_rx(ioaddr, np->crvalue);
+                                       reset_rx_descriptors(dev);
+                                       iowrite32(np->crvalue, ioaddr + TCRRCR);
+                               }
+                               break;  /* exit the while loop */
+                       }
+               } else {        /* this received pkt is ok */
+
+                       struct sk_buff *skb;
+                       /* Omit the four octet CRC from the length. */
+                       short pkt_len = ((rx_status & FLNGMASK) >> FLNGShift) - 4;
+
+#ifndef final_version
+                       if (debug)
+                               printk(KERN_DEBUG "  netdev_rx() normal Rx pkt length %d"
+                                      " status %x.\n", pkt_len, rx_status);
+#endif
+
+                       /* Check if the packet is long enough to accept without copying
+                          to a minimally-sized skbuff. */
+                       if (pkt_len < rx_copybreak &&
+                           (skb = netdev_alloc_skb(dev, pkt_len + 2)) != NULL) {
+                               skb_reserve(skb, 2);    /* 16 byte align the IP header */
+                               dma_sync_single_for_cpu(&np->pci_dev->dev,
+                                                       np->cur_rx->buffer,
+                                                       np->rx_buf_sz,
+                                                       DMA_FROM_DEVICE);
+                               /* Call copy + cksum if available. */
+
+#if ! defined(__alpha__)
+                               skb_copy_to_linear_data(skb,
+                                       np->cur_rx->skbuff->data, pkt_len);
+                               skb_put(skb, pkt_len);
+#else
+                               skb_put_data(skb, np->cur_rx->skbuff->data,
+                                            pkt_len);
+#endif
+                               dma_sync_single_for_device(&np->pci_dev->dev,
+                                                          np->cur_rx->buffer,
+                                                          np->rx_buf_sz,
+                                                          DMA_FROM_DEVICE);
+                       } else {
+                               dma_unmap_single(&np->pci_dev->dev,
+                                                np->cur_rx->buffer,
+                                                np->rx_buf_sz,
+                                                DMA_FROM_DEVICE);
+                               skb_put(skb = np->cur_rx->skbuff, pkt_len);
+                               np->cur_rx->skbuff = NULL;
+                               --np->really_rx_count;
+                       }
+                       skb->protocol = eth_type_trans(skb, dev);
+                       netif_rx(skb);
+                       dev->stats.rx_packets++;
+                       dev->stats.rx_bytes += pkt_len;
+               }
+
+               np->cur_rx = np->cur_rx->next_desc_logical;
+       }                       /* end of while loop */
+
+       /*  allocate skb for rx buffers */
+       allocate_rx_buffers(dev);
+
+       return 0;
+}
+
+
+static struct net_device_stats *get_stats(struct net_device *dev)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem *ioaddr = np->mem;
+
+       /* The chip only need report frame silently dropped. */
+       if (netif_running(dev)) {
+               dev->stats.rx_missed_errors +=
+                       ioread32(ioaddr + TALLY) & 0x7fff;
+               dev->stats.rx_crc_errors +=
+                       (ioread32(ioaddr + TALLY) & 0x7fff0000) >> 16;
+       }
+
+       return &dev->stats;
+}
+
+
+/* for dev->set_multicast_list */
+static void set_rx_mode(struct net_device *dev)
+{
+       spinlock_t *lp = &((struct netdev_private *)netdev_priv(dev))->lock;
+       unsigned long flags;
+       spin_lock_irqsave(lp, flags);
+       __set_rx_mode(dev);
+       spin_unlock_irqrestore(lp, flags);
+}
+
+
+/* Take lock before calling */
+static void __set_rx_mode(struct net_device *dev)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem *ioaddr = np->mem;
+       u32 mc_filter[2];       /* Multicast hash filter */
+       u32 rx_mode;
+
+       if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */
+               memset(mc_filter, 0xff, sizeof(mc_filter));
+               rx_mode = CR_W_PROM | CR_W_AB | CR_W_AM;
+       } else if ((netdev_mc_count(dev) > multicast_filter_limit) ||
+                  (dev->flags & IFF_ALLMULTI)) {
+               /* Too many to match, or accept all multicasts. */
+               memset(mc_filter, 0xff, sizeof(mc_filter));
+               rx_mode = CR_W_AB | CR_W_AM;
+       } else {
+               struct netdev_hw_addr *ha;
+
+               memset(mc_filter, 0, sizeof(mc_filter));
+               netdev_for_each_mc_addr(ha, dev) {
+                       unsigned int bit;
+                       bit = (ether_crc(ETH_ALEN, ha->addr) >> 26) ^ 0x3F;
+                       mc_filter[bit >> 5] |= (1 << bit);
+               }
+               rx_mode = CR_W_AB | CR_W_AM;
+       }
+
+       stop_nic_rxtx(ioaddr, np->crvalue);
+
+       iowrite32(mc_filter[0], ioaddr + MAR0);
+       iowrite32(mc_filter[1], ioaddr + MAR1);
+       np->crvalue &= ~CR_W_RXMODEMASK;
+       np->crvalue |= rx_mode;
+       iowrite32(np->crvalue, ioaddr + TCRRCR);
+}
+
+static void netdev_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
+{
+       struct netdev_private *np = netdev_priv(dev);
+
+       strscpy(info->driver, DRV_NAME, sizeof(info->driver));
+       strscpy(info->bus_info, pci_name(np->pci_dev), sizeof(info->bus_info));
+}
+
+static int netdev_get_link_ksettings(struct net_device *dev,
+                                    struct ethtool_link_ksettings *cmd)
+{
+       struct netdev_private *np = netdev_priv(dev);
+
+       spin_lock_irq(&np->lock);
+       mii_ethtool_get_link_ksettings(&np->mii, cmd);
+       spin_unlock_irq(&np->lock);
+
+       return 0;
+}
+
+static int netdev_set_link_ksettings(struct net_device *dev,
+                                    const struct ethtool_link_ksettings *cmd)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       int rc;
+
+       spin_lock_irq(&np->lock);
+       rc = mii_ethtool_set_link_ksettings(&np->mii, cmd);
+       spin_unlock_irq(&np->lock);
+
+       return rc;
+}
+
+static int netdev_nway_reset(struct net_device *dev)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       return mii_nway_restart(&np->mii);
+}
+
+static u32 netdev_get_link(struct net_device *dev)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       return mii_link_ok(&np->mii);
+}
+
+static u32 netdev_get_msglevel(struct net_device *dev)
+{
+       return debug;
+}
+
+static void netdev_set_msglevel(struct net_device *dev, u32 value)
+{
+       debug = value;
+}
+
+static const struct ethtool_ops netdev_ethtool_ops = {
+       .get_drvinfo            = netdev_get_drvinfo,
+       .nway_reset             = netdev_nway_reset,
+       .get_link               = netdev_get_link,
+       .get_msglevel           = netdev_get_msglevel,
+       .set_msglevel           = netdev_set_msglevel,
+       .get_link_ksettings     = netdev_get_link_ksettings,
+       .set_link_ksettings     = netdev_set_link_ksettings,
+};
+
+static int mii_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       int rc;
+
+       if (!netif_running(dev))
+               return -EINVAL;
+
+       spin_lock_irq(&np->lock);
+       rc = generic_mii_ioctl(&np->mii, if_mii(rq), cmd, NULL);
+       spin_unlock_irq(&np->lock);
+
+       return rc;
+}
+
+
+static int netdev_close(struct net_device *dev)
+{
+       struct netdev_private *np = netdev_priv(dev);
+       void __iomem *ioaddr = np->mem;
+       int i;
+
+       netif_stop_queue(dev);
+
+       /* Disable interrupts by clearing the interrupt mask. */
+       iowrite32(0x0000, ioaddr + IMR);
+
+       /* Stop the chip's Tx and Rx processes. */
+       stop_nic_rxtx(ioaddr, 0);
+
+       del_timer_sync(&np->timer);
+       del_timer_sync(&np->reset_timer);
+
+       free_irq(np->pci_dev->irq, dev);
+
+       /* Free all the skbuffs in the Rx queue. */
+       for (i = 0; i < RX_RING_SIZE; i++) {
+               struct sk_buff *skb = np->rx_ring[i].skbuff;
+
+               np->rx_ring[i].status = 0;
+               if (skb) {
+                       dma_unmap_single(&np->pci_dev->dev,
+                                        np->rx_ring[i].buffer, np->rx_buf_sz,
+                                        DMA_FROM_DEVICE);
+                       dev_kfree_skb(skb);
+                       np->rx_ring[i].skbuff = NULL;
+               }
+       }
+
+       for (i = 0; i < TX_RING_SIZE; i++) {
+               struct sk_buff *skb = np->tx_ring[i].skbuff;
+
+               if (skb) {
+                       dma_unmap_single(&np->pci_dev->dev,
+                                        np->tx_ring[i].buffer, skb->len,
+                                        DMA_TO_DEVICE);
+                       dev_kfree_skb(skb);
+                       np->tx_ring[i].skbuff = NULL;
+               }
+       }
+
+       return 0;
+}
+
+static const struct pci_device_id fealnx_pci_tbl[] = {
+       {0x1516, 0x0800, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+       {0x1516, 0x0803, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1},
+       {0x1516, 0x0891, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2},
+       {} /* terminate list */
+};
+MODULE_DEVICE_TABLE(pci, fealnx_pci_tbl);
+
+
+static struct pci_driver fealnx_driver = {
+       .name           = "fealnx",
+       .id_table       = fealnx_pci_tbl,
+       .probe          = fealnx_init_one,
+       .remove         = fealnx_remove_one,
+};
+
+module_pci_driver(fealnx_driver);
index c557dfc..396e555 100644 (file)
@@ -1411,7 +1411,7 @@ ice_add_dscp_pfc_tlv(struct ice_lldp_org_tlv *tlv, struct ice_dcbx_cfg *dcbcfg)
        tlv->ouisubtype = htonl(ouisubtype);
 
        buf[0] = dcbcfg->pfc.pfccap & 0xF;
-       buf[1] = dcbcfg->pfc.pfcena & 0xF;
+       buf[1] = dcbcfg->pfc.pfcena;
 }
 
 /**
index b360bd8..f86e814 100644 (file)
@@ -4331,6 +4331,8 @@ ice_get_module_eeprom(struct net_device *netdev,
                 * SFP modules only ever use page 0.
                 */
                if (page == 0 || !(data[0x2] & 0x4)) {
+                       u32 copy_len;
+
                        /* If i2c bus is busy due to slow page change or
                         * link management access, call can fail. This is normal.
                         * So we retry this a few times.
@@ -4354,8 +4356,8 @@ ice_get_module_eeprom(struct net_device *netdev,
                        }
 
                        /* Make sure we have enough room for the new block */
-                       if ((i + SFF_READ_BLOCK_SIZE) < ee->len)
-                               memcpy(data + i, value, SFF_READ_BLOCK_SIZE);
+                       copy_len = min_t(u32, SFF_READ_BLOCK_SIZE, ee->len - i);
+                       memcpy(data + i, value, copy_len);
                }
        }
        return 0;
index 7814754..0f52ea3 100644 (file)
@@ -2126,7 +2126,7 @@ int ice_vsi_cfg_xdp_txqs(struct ice_vsi *vsi)
        ice_for_each_rxq(vsi, i)
                ice_tx_xsk_pool(vsi, i);
 
-       return ret;
+       return 0;
 }
 
 /**
@@ -2693,12 +2693,14 @@ ice_vsi_cfg_def(struct ice_vsi *vsi, struct ice_vsi_cfg_params *params)
                return ret;
 
        /* allocate memory for Tx/Rx ring stat pointers */
-       if (ice_vsi_alloc_stat_arrays(vsi))
+       ret = ice_vsi_alloc_stat_arrays(vsi);
+       if (ret)
                goto unroll_vsi_alloc;
 
        ice_alloc_fd_res(vsi);
 
-       if (ice_vsi_get_qs(vsi)) {
+       ret = ice_vsi_get_qs(vsi);
+       if (ret) {
                dev_err(dev, "Failed to allocate queues. vsi->idx = %d\n",
                        vsi->idx);
                goto unroll_vsi_alloc_stat;
@@ -2811,6 +2813,7 @@ ice_vsi_cfg_def(struct ice_vsi *vsi, struct ice_vsi_cfg_params *params)
                break;
        default:
                /* clean up the resources and exit */
+               ret = -EINVAL;
                goto unroll_vsi_init;
        }
 
@@ -3508,10 +3511,10 @@ int ice_vsi_rebuild(struct ice_vsi *vsi, u32 vsi_flags)
                if (vsi_flags & ICE_VSI_FLAG_INIT) {
                        ret = -EIO;
                        goto err_vsi_cfg_tc_lan;
-               } else {
-                       kfree(coalesce);
-                       return ice_schedule_reset(pf, ICE_RESET_PFR);
                }
+
+               kfree(coalesce);
+               return ice_schedule_reset(pf, ICE_RESET_PFR);
        }
 
        ice_vsi_realloc_stat_arrays(vsi, prev_txq, prev_rxq);
@@ -3759,7 +3762,7 @@ int ice_vsi_cfg_tc(struct ice_vsi *vsi, u8 ena_tc)
        dev = ice_pf_to_dev(pf);
        if (vsi->tc_cfg.ena_tc == ena_tc &&
            vsi->mqprio_qopt.mode != TC_MQPRIO_MODE_CHANNEL)
-               return ret;
+               return 0;
 
        ice_for_each_traffic_class(i) {
                /* build bitmap of enabled TCs */
index 6b48cbc..76f29a5 100644 (file)
@@ -1455,8 +1455,8 @@ ice_parse_cls_flower(struct net_device *filter_dev, struct ice_vsi *vsi,
                if (match.mask->vlan_priority) {
                        fltr->flags |= ICE_TC_FLWR_FIELD_VLAN_PRIO;
                        headers->vlan_hdr.vlan_prio =
-                               cpu_to_be16((match.key->vlan_priority <<
-                                            VLAN_PRIO_SHIFT) & VLAN_PRIO_MASK);
+                               be16_encode_bits(match.key->vlan_priority,
+                                                VLAN_PRIO_MASK);
                }
 
                if (match.mask->vlan_tpid)
@@ -1489,8 +1489,8 @@ ice_parse_cls_flower(struct net_device *filter_dev, struct ice_vsi *vsi,
                if (match.mask->vlan_priority) {
                        fltr->flags |= ICE_TC_FLWR_FIELD_CVLAN_PRIO;
                        headers->cvlan_hdr.vlan_prio =
-                               cpu_to_be16((match.key->vlan_priority <<
-                                            VLAN_PRIO_SHIFT) & VLAN_PRIO_MASK);
+                               be16_encode_bits(match.key->vlan_priority,
+                                                VLAN_PRIO_MASK);
                }
        }
 
index 389663a..ef721ca 100644 (file)
@@ -884,6 +884,9 @@ int rvu_cpt_lf_teardown(struct rvu *rvu, u16 pcifunc, int blkaddr, int lf,
 int rvu_cpt_ctx_flush(struct rvu *rvu, u16 pcifunc);
 int rvu_cpt_init(struct rvu *rvu);
 
+#define NDC_AF_BANK_MASK       GENMASK_ULL(7, 0)
+#define NDC_AF_BANK_LINE_MASK  GENMASK_ULL(31, 16)
+
 /* CN10K RVU */
 int rvu_set_channels_base(struct rvu *rvu);
 void rvu_program_channels(struct rvu *rvu);
@@ -902,6 +905,8 @@ static inline void rvu_dbg_init(struct rvu *rvu) {}
 static inline void rvu_dbg_exit(struct rvu *rvu) {}
 #endif
 
+int rvu_ndc_fix_locked_cacheline(struct rvu *rvu, int blkaddr);
+
 /* RVU Switch */
 void rvu_switch_enable(struct rvu *rvu);
 void rvu_switch_disable(struct rvu *rvu);
index fa280eb..26cfa50 100644 (file)
@@ -198,9 +198,6 @@ enum cpt_eng_type {
        CPT_IE_TYPE = 3,
 };
 
-#define NDC_MAX_BANK(rvu, blk_addr) (rvu_read64(rvu, \
-                                               blk_addr, NDC_AF_CONST) & 0xFF)
-
 #define rvu_dbg_NULL NULL
 #define rvu_dbg_open_NULL NULL
 
@@ -1448,6 +1445,7 @@ static int ndc_blk_hits_miss_stats(struct seq_file *s, int idx, int blk_addr)
        struct nix_hw *nix_hw;
        struct rvu *rvu;
        int bank, max_bank;
+       u64 ndc_af_const;
 
        if (blk_addr == BLKADDR_NDC_NPA0) {
                rvu = s->private;
@@ -1456,7 +1454,8 @@ static int ndc_blk_hits_miss_stats(struct seq_file *s, int idx, int blk_addr)
                rvu = nix_hw->rvu;
        }
 
-       max_bank = NDC_MAX_BANK(rvu, blk_addr);
+       ndc_af_const = rvu_read64(rvu, blk_addr, NDC_AF_CONST);
+       max_bank = FIELD_GET(NDC_AF_BANK_MASK, ndc_af_const);
        for (bank = 0; bank < max_bank; bank++) {
                seq_printf(s, "BANK:%d\n", bank);
                seq_printf(s, "\tHits:\t%lld\n",
index 26e639e..4ad707e 100644 (file)
@@ -790,6 +790,7 @@ static int nix_aq_enqueue_wait(struct rvu *rvu, struct rvu_block *block,
        struct nix_aq_res_s *result;
        int timeout = 1000;
        u64 reg, head;
+       int ret;
 
        result = (struct nix_aq_res_s *)aq->res->base;
 
@@ -813,9 +814,22 @@ static int nix_aq_enqueue_wait(struct rvu *rvu, struct rvu_block *block,
                        return -EBUSY;
        }
 
-       if (result->compcode != NIX_AQ_COMP_GOOD)
+       if (result->compcode != NIX_AQ_COMP_GOOD) {
                /* TODO: Replace this with some error code */
+               if (result->compcode == NIX_AQ_COMP_CTX_FAULT ||
+                   result->compcode == NIX_AQ_COMP_LOCKERR ||
+                   result->compcode == NIX_AQ_COMP_CTX_POISON) {
+                       ret = rvu_ndc_fix_locked_cacheline(rvu, BLKADDR_NDC_NIX0_RX);
+                       ret |= rvu_ndc_fix_locked_cacheline(rvu, BLKADDR_NDC_NIX0_TX);
+                       ret |= rvu_ndc_fix_locked_cacheline(rvu, BLKADDR_NDC_NIX1_RX);
+                       ret |= rvu_ndc_fix_locked_cacheline(rvu, BLKADDR_NDC_NIX1_TX);
+                       if (ret)
+                               dev_err(rvu->dev,
+                                       "%s: Not able to unlock cachelines\n", __func__);
+               }
+
                return -EBUSY;
+       }
 
        return 0;
 }
index 70bd036..4f5ca5a 100644 (file)
@@ -4,7 +4,7 @@
  * Copyright (C) 2018 Marvell.
  *
  */
-
+#include <linux/bitfield.h>
 #include <linux/module.h>
 #include <linux/pci.h>
 
@@ -42,9 +42,18 @@ static int npa_aq_enqueue_wait(struct rvu *rvu, struct rvu_block *block,
                        return -EBUSY;
        }
 
-       if (result->compcode != NPA_AQ_COMP_GOOD)
+       if (result->compcode != NPA_AQ_COMP_GOOD) {
                /* TODO: Replace this with some error code */
+               if (result->compcode == NPA_AQ_COMP_CTX_FAULT ||
+                   result->compcode == NPA_AQ_COMP_LOCKERR ||
+                   result->compcode == NPA_AQ_COMP_CTX_POISON) {
+                       if (rvu_ndc_fix_locked_cacheline(rvu, BLKADDR_NDC_NPA0))
+                               dev_err(rvu->dev,
+                                       "%s: Not able to unlock cachelines\n", __func__);
+               }
+
                return -EBUSY;
+       }
 
        return 0;
 }
@@ -545,3 +554,48 @@ void rvu_npa_lf_teardown(struct rvu *rvu, u16 pcifunc, int npalf)
 
        npa_ctx_free(rvu, pfvf);
 }
+
+/* Due to an Hardware errata, in some corner cases, AQ context lock
+ * operations can result in a NDC way getting into an illegal state
+ * of not valid but locked.
+ *
+ * This API solves the problem by clearing the lock bit of the NDC block.
+ * The operation needs to be done for each line of all the NDC banks.
+ */
+int rvu_ndc_fix_locked_cacheline(struct rvu *rvu, int blkaddr)
+{
+       int bank, max_bank, line, max_line, err;
+       u64 reg, ndc_af_const;
+
+       /* Set the ENABLE bit(63) to '0' */
+       reg = rvu_read64(rvu, blkaddr, NDC_AF_CAMS_RD_INTERVAL);
+       rvu_write64(rvu, blkaddr, NDC_AF_CAMS_RD_INTERVAL, reg & GENMASK_ULL(62, 0));
+
+       /* Poll until the BUSY bits(47:32) are set to '0' */
+       err = rvu_poll_reg(rvu, blkaddr, NDC_AF_CAMS_RD_INTERVAL, GENMASK_ULL(47, 32), true);
+       if (err) {
+               dev_err(rvu->dev, "Timed out while polling for NDC CAM busy bits.\n");
+               return err;
+       }
+
+       ndc_af_const = rvu_read64(rvu, blkaddr, NDC_AF_CONST);
+       max_bank = FIELD_GET(NDC_AF_BANK_MASK, ndc_af_const);
+       max_line = FIELD_GET(NDC_AF_BANK_LINE_MASK, ndc_af_const);
+       for (bank = 0; bank < max_bank; bank++) {
+               for (line = 0; line < max_line; line++) {
+                       /* Check if 'cache line valid bit(63)' is not set
+                        * but 'cache line lock bit(60)' is set and on
+                        * success, reset the lock bit(60).
+                        */
+                       reg = rvu_read64(rvu, blkaddr,
+                                        NDC_AF_BANKX_LINEX_METADATA(bank, line));
+                       if (!(reg & BIT_ULL(63)) && (reg & BIT_ULL(60))) {
+                               rvu_write64(rvu, blkaddr,
+                                           NDC_AF_BANKX_LINEX_METADATA(bank, line),
+                                           reg & ~BIT_ULL(60));
+                       }
+               }
+       }
+
+       return 0;
+}
index 1729b22..7007f0b 100644 (file)
 #define NDC_AF_INTR_ENA_W1S            (0x00068)
 #define NDC_AF_INTR_ENA_W1C            (0x00070)
 #define NDC_AF_ACTIVE_PC               (0x00078)
+#define NDC_AF_CAMS_RD_INTERVAL                (0x00080)
 #define NDC_AF_BP_TEST_ENABLE          (0x001F8)
 #define NDC_AF_BP_TEST(a)              (0x00200 | (a) << 3)
 #define NDC_AF_BLK_RST                 (0x002F0)
                (0x00F00 | (a) << 5 | (b) << 4)
 #define NDC_AF_BANKX_HIT_PC(a)         (0x01000 | (a) << 3)
 #define NDC_AF_BANKX_MISS_PC(a)                (0x01100 | (a) << 3)
+#define NDC_AF_BANKX_LINEX_METADATA(a, b) \
+               (0x10000 | (a) << 12 | (b) << 3)
 
 /* LBK */
 #define LBK_CONST                      (0x10ull)
index 14be6ea..3cb4362 100644 (file)
@@ -616,7 +616,8 @@ static int mtk_mac_finish(struct phylink_config *config, unsigned int mode,
        mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id));
        mcr_new = mcr_cur;
        mcr_new |= MAC_MCR_IPG_CFG | MAC_MCR_FORCE_MODE |
-                  MAC_MCR_BACKOFF_EN | MAC_MCR_BACKPR_EN | MAC_MCR_FORCE_LINK;
+                  MAC_MCR_BACKOFF_EN | MAC_MCR_BACKPR_EN | MAC_MCR_FORCE_LINK |
+                  MAC_MCR_RX_FIFO_CLR_DIS;
 
        /* Only update control register when needed! */
        if (mcr_new != mcr_cur)
index afc9d52..b65de17 100644 (file)
 #define MAC_MCR_FORCE_MODE     BIT(15)
 #define MAC_MCR_TX_EN          BIT(14)
 #define MAC_MCR_RX_EN          BIT(13)
+#define MAC_MCR_RX_FIFO_CLR_DIS        BIT(12)
 #define MAC_MCR_BACKOFF_EN     BIT(9)
 #define MAC_MCR_BACKPR_EN      BIT(8)
 #define MAC_MCR_FORCE_RX_FC    BIT(5)
index a9aec90..7d66fe7 100644 (file)
@@ -194,7 +194,7 @@ int lan966x_police_port_del(struct lan966x_port *port,
                return -EINVAL;
        }
 
-       err = lan966x_police_del(port, port->tc.police_id);
+       err = lan966x_police_del(port, POL_IDX_PORT + port->chip_port);
        if (err) {
                NL_SET_ERR_MSG_MOD(extack,
                                   "Failed to add policer to port");
index 871a3e6..2d76366 100644 (file)
@@ -249,6 +249,21 @@ static int sparx5_dcb_ieee_dscp_setdel(struct net_device *dev,
        return 0;
 }
 
+static int sparx5_dcb_ieee_delapp(struct net_device *dev, struct dcb_app *app)
+{
+       int err;
+
+       if (app->selector == IEEE_8021QAZ_APP_SEL_DSCP)
+               err = sparx5_dcb_ieee_dscp_setdel(dev, app, dcb_ieee_delapp);
+       else
+               err = dcb_ieee_delapp(dev, app);
+
+       if (err < 0)
+               return err;
+
+       return sparx5_dcb_app_update(dev);
+}
+
 static int sparx5_dcb_ieee_setapp(struct net_device *dev, struct dcb_app *app)
 {
        struct dcb_app app_itr;
@@ -264,7 +279,7 @@ static int sparx5_dcb_ieee_setapp(struct net_device *dev, struct dcb_app *app)
        if (prio) {
                app_itr = *app;
                app_itr.priority = prio;
-               dcb_ieee_delapp(dev, &app_itr);
+               sparx5_dcb_ieee_delapp(dev, &app_itr);
        }
 
        if (app->selector == IEEE_8021QAZ_APP_SEL_DSCP)
@@ -281,21 +296,6 @@ out:
        return err;
 }
 
-static int sparx5_dcb_ieee_delapp(struct net_device *dev, struct dcb_app *app)
-{
-       int err;
-
-       if (app->selector == IEEE_8021QAZ_APP_SEL_DSCP)
-               err = sparx5_dcb_ieee_dscp_setdel(dev, app, dcb_ieee_delapp);
-       else
-               err = dcb_ieee_delapp(dev, app);
-
-       if (err < 0)
-               return err;
-
-       return sparx5_dcb_app_update(dev);
-}
-
 static int sparx5_dcb_setapptrust(struct net_device *dev, u8 *selectors,
                                  int nselectors)
 {
index 59fb058..0cc026b 100644 (file)
@@ -324,14 +324,15 @@ netdev_tx_t nfp_nfd3_tx(struct sk_buff *skb, struct net_device *netdev)
 
        /* Do not reorder - tso may adjust pkt cnt, vlan may override fields */
        nfp_nfd3_tx_tso(r_vec, txbuf, txd, skb, md_bytes);
-       nfp_nfd3_tx_csum(dp, r_vec, txbuf, txd, skb);
+       if (ipsec)
+               nfp_nfd3_ipsec_tx(txd, skb);
+       else
+               nfp_nfd3_tx_csum(dp, r_vec, txbuf, txd, skb);
        if (skb_vlan_tag_present(skb) && dp->ctrl & NFP_NET_CFG_CTRL_TXVLAN) {
                txd->flags |= NFD3_DESC_TX_VLAN;
                txd->vlan = cpu_to_le16(skb_vlan_tag_get(skb));
        }
 
-       if (ipsec)
-               nfp_nfd3_ipsec_tx(txd, skb);
        /* Gather DMA */
        if (nr_frags > 0) {
                __le64 second_half;
index e90f8c9..5108769 100644 (file)
 void nfp_nfd3_ipsec_tx(struct nfp_nfd3_tx_desc *txd, struct sk_buff *skb)
 {
        struct xfrm_state *x = xfrm_input_state(skb);
+       struct xfrm_offload *xo = xfrm_offload(skb);
+       struct iphdr *iph = ip_hdr(skb);
+       int l4_proto;
 
        if (x->xso.dev && (x->xso.dev->features & NETIF_F_HW_ESP_TX_CSUM)) {
-               txd->flags |= NFD3_DESC_TX_CSUM | NFD3_DESC_TX_IP4_CSUM |
-                             NFD3_DESC_TX_TCP_CSUM | NFD3_DESC_TX_UDP_CSUM;
+               txd->flags |= NFD3_DESC_TX_CSUM;
+
+               if (iph->version == 4)
+                       txd->flags |= NFD3_DESC_TX_IP4_CSUM;
+
+               if (x->props.mode == XFRM_MODE_TRANSPORT)
+                       l4_proto = xo->proto;
+               else if (x->props.mode == XFRM_MODE_TUNNEL)
+                       l4_proto = xo->inner_ipproto;
+               else
+                       return;
+
+               switch (l4_proto) {
+               case IPPROTO_UDP:
+                       txd->flags |= NFD3_DESC_TX_UDP_CSUM;
+                       return;
+               case IPPROTO_TCP:
+                       txd->flags |= NFD3_DESC_TX_TCP_CSUM;
+                       return;
+               }
        }
 }
index d60c0e9..33b6d74 100644 (file)
@@ -387,7 +387,8 @@ netdev_tx_t nfp_nfdk_tx(struct sk_buff *skb, struct net_device *netdev)
        if (!skb_is_gso(skb)) {
                real_len = skb->len;
                /* Metadata desc */
-               metadata = nfp_nfdk_tx_csum(dp, r_vec, 1, skb, metadata);
+               if (!ipsec)
+                       metadata = nfp_nfdk_tx_csum(dp, r_vec, 1, skb, metadata);
                txd->raw = cpu_to_le64(metadata);
                txd++;
        } else {
@@ -395,7 +396,8 @@ netdev_tx_t nfp_nfdk_tx(struct sk_buff *skb, struct net_device *netdev)
                (txd + 1)->raw = nfp_nfdk_tx_tso(r_vec, txbuf, skb);
                real_len = txbuf->real_len;
                /* Metadata desc */
-               metadata = nfp_nfdk_tx_csum(dp, r_vec, txbuf->pkt_cnt, skb, metadata);
+               if (!ipsec)
+                       metadata = nfp_nfdk_tx_csum(dp, r_vec, txbuf->pkt_cnt, skb, metadata);
                txd->raw = cpu_to_le64(metadata);
                txd += 2;
                txbuf++;
index 58d8f59..cec199f 100644 (file)
@@ -9,9 +9,13 @@
 u64 nfp_nfdk_ipsec_tx(u64 flags, struct sk_buff *skb)
 {
        struct xfrm_state *x = xfrm_input_state(skb);
+       struct iphdr *iph = ip_hdr(skb);
 
-       if (x->xso.dev && (x->xso.dev->features & NETIF_F_HW_ESP_TX_CSUM))
-               flags |= NFDK_DESC_TX_L3_CSUM | NFDK_DESC_TX_L4_CSUM;
+       if (x->xso.dev && (x->xso.dev->features & NETIF_F_HW_ESP_TX_CSUM)) {
+               if (iph->version == 4)
+                       flags |= NFDK_DESC_TX_L3_CSUM;
+               flags |= NFDK_DESC_TX_L4_CSUM;
+       }
 
        return flags;
 }
index 81b7ca0..62f0bf9 100644 (file)
@@ -38,6 +38,7 @@
 #include <net/tls.h>
 #include <net/vxlan.h>
 #include <net/xdp_sock_drv.h>
+#include <net/xfrm.h>
 
 #include "nfpcore/nfp_dev.h"
 #include "nfpcore/nfp_nsp.h"
@@ -1897,6 +1898,9 @@ nfp_net_features_check(struct sk_buff *skb, struct net_device *dev,
                        features &= ~NETIF_F_GSO_MASK;
        }
 
+       if (xfrm_offload(skb))
+               return features;
+
        /* VXLAN/GRE check */
        switch (vlan_get_protocol(skb)) {
        case htons(ETH_P_IP):
index e4902a7..8f543c3 100644 (file)
@@ -1170,6 +1170,7 @@ static int stmmac_init_phy(struct net_device *dev)
 
                phylink_ethtool_get_wol(priv->phylink, &wol);
                device_set_wakeup_capable(priv->device, !!wol.supported);
+               device_set_wakeup_enable(priv->device, !!wol.wolopts);
        }
 
        return ret;
index e1a569b..0b0c6c0 100644 (file)
@@ -1913,6 +1913,8 @@ static int ca8210_skb_tx(
         * packet
         */
        mac_len = ieee802154_hdr_peek_addrs(skb, &header);
+       if (mac_len < 0)
+               return mac_len;
 
        secspec.security_level = header.sec.level;
        secspec.key_id_mode = header.sec.key_id_mode;
index ccecee2..0b88635 100644 (file)
@@ -342,6 +342,37 @@ static int lan88xx_config_aneg(struct phy_device *phydev)
        return genphy_config_aneg(phydev);
 }
 
+static void lan88xx_link_change_notify(struct phy_device *phydev)
+{
+       int temp;
+
+       /* At forced 100 F/H mode, chip may fail to set mode correctly
+        * when cable is switched between long(~50+m) and short one.
+        * As workaround, set to 10 before setting to 100
+        * at forced 100 F/H mode.
+        */
+       if (!phydev->autoneg && phydev->speed == 100) {
+               /* disable phy interrupt */
+               temp = phy_read(phydev, LAN88XX_INT_MASK);
+               temp &= ~LAN88XX_INT_MASK_MDINTPIN_EN_;
+               phy_write(phydev, LAN88XX_INT_MASK, temp);
+
+               temp = phy_read(phydev, MII_BMCR);
+               temp &= ~(BMCR_SPEED100 | BMCR_SPEED1000);
+               phy_write(phydev, MII_BMCR, temp); /* set to 10 first */
+               temp |= BMCR_SPEED100;
+               phy_write(phydev, MII_BMCR, temp); /* set to 100 later */
+
+               /* clear pending interrupt generated while workaround */
+               temp = phy_read(phydev, LAN88XX_INT_STS);
+
+               /* enable phy interrupt back */
+               temp = phy_read(phydev, LAN88XX_INT_MASK);
+               temp |= LAN88XX_INT_MASK_MDINTPIN_EN_;
+               phy_write(phydev, LAN88XX_INT_MASK, temp);
+       }
+}
+
 static struct phy_driver microchip_phy_driver[] = {
 {
        .phy_id         = 0x0007c132,
@@ -359,6 +390,7 @@ static struct phy_driver microchip_phy_driver[] = {
 
        .config_init    = lan88xx_config_init,
        .config_aneg    = lan88xx_config_aneg,
+       .link_change_notify = lan88xx_link_change_notify,
 
        .config_intr    = lan88xx_phy_config_intr,
        .handle_interrupt = lan88xx_handle_interrupt,
index 3f8a64f..1785f1c 100644 (file)
@@ -3098,8 +3098,6 @@ static int phy_probe(struct device *dev)
        if (phydrv->flags & PHY_IS_INTERNAL)
                phydev->is_internal = true;
 
-       mutex_lock(&phydev->lock);
-
        /* Deassert the reset signal */
        phy_device_reset(phydev, 0);
 
@@ -3146,7 +3144,7 @@ static int phy_probe(struct device *dev)
         */
        err = genphy_c45_read_eee_adv(phydev, phydev->advertising_eee);
        if (err)
-               return err;
+               goto out;
 
        /* There is no "enabled" flag. If PHY is advertising, assume it is
         * kind of enabled.
@@ -3188,12 +3186,10 @@ static int phy_probe(struct device *dev)
        phydev->state = PHY_READY;
 
 out:
-       /* Assert the reset signal */
+       /* Re-assert the reset signal on error */
        if (err)
                phy_device_reset(phydev, 1);
 
-       mutex_unlock(&phydev->lock);
-
        return err;
 }
 
@@ -3203,9 +3199,7 @@ static int phy_remove(struct device *dev)
 
        cancel_delayed_work_sync(&phydev->state_queue);
 
-       mutex_lock(&phydev->lock);
        phydev->state = PHY_DOWN;
-       mutex_unlock(&phydev->lock);
 
        sfp_bus_del_upstream(phydev->sfp_bus);
        phydev->sfp_bus = NULL;
index ac7481c..00d9eff 100644 (file)
@@ -44,7 +44,6 @@ static struct smsc_hw_stat smsc_hw_stats[] = {
 };
 
 struct smsc_phy_priv {
-       u16 intmask;
        bool energy_enable;
 };
 
@@ -57,7 +56,6 @@ static int smsc_phy_ack_interrupt(struct phy_device *phydev)
 
 static int smsc_phy_config_intr(struct phy_device *phydev)
 {
-       struct smsc_phy_priv *priv = phydev->priv;
        int rc;
 
        if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
@@ -65,14 +63,9 @@ static int smsc_phy_config_intr(struct phy_device *phydev)
                if (rc)
                        return rc;
 
-               priv->intmask = MII_LAN83C185_ISF_INT4 | MII_LAN83C185_ISF_INT6;
-               if (priv->energy_enable)
-                       priv->intmask |= MII_LAN83C185_ISF_INT7;
-
-               rc = phy_write(phydev, MII_LAN83C185_IM, priv->intmask);
+               rc = phy_write(phydev, MII_LAN83C185_IM,
+                              MII_LAN83C185_ISF_INT_PHYLIB_EVENTS);
        } else {
-               priv->intmask = 0;
-
                rc = phy_write(phydev, MII_LAN83C185_IM, 0);
                if (rc)
                        return rc;
@@ -85,7 +78,6 @@ static int smsc_phy_config_intr(struct phy_device *phydev)
 
 static irqreturn_t smsc_phy_handle_interrupt(struct phy_device *phydev)
 {
-       struct smsc_phy_priv *priv = phydev->priv;
        int irq_status;
 
        irq_status = phy_read(phydev, MII_LAN83C185_ISF);
@@ -96,7 +88,7 @@ static irqreturn_t smsc_phy_handle_interrupt(struct phy_device *phydev)
                return IRQ_NONE;
        }
 
-       if (!(irq_status & priv->intmask))
+       if (!(irq_status & MII_LAN83C185_ISF_INT_PHYLIB_EVENTS))
                return IRQ_NONE;
 
        phy_trigger_machine(phydev);
index c896393..cd4083e 100644 (file)
@@ -665,6 +665,11 @@ static const struct usb_device_id mbim_devs[] = {
          .driver_info = (unsigned long)&cdc_mbim_info_avoid_altsetting_toggle,
        },
 
+       /* Telit FE990 */
+       { USB_DEVICE_AND_INTERFACE_INFO(0x1bc7, 0x1081, USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE),
+         .driver_info = (unsigned long)&cdc_mbim_info_avoid_altsetting_toggle,
+       },
+
        /* default entry */
        { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE),
          .driver_info = (unsigned long)&cdc_mbim_info_zlp,
index f18ab8e..0684888 100644 (file)
@@ -2115,33 +2115,8 @@ static void lan78xx_remove_mdio(struct lan78xx_net *dev)
 static void lan78xx_link_status_change(struct net_device *net)
 {
        struct phy_device *phydev = net->phydev;
-       int temp;
-
-       /* At forced 100 F/H mode, chip may fail to set mode correctly
-        * when cable is switched between long(~50+m) and short one.
-        * As workaround, set to 10 before setting to 100
-        * at forced 100 F/H mode.
-        */
-       if (!phydev->autoneg && (phydev->speed == 100)) {
-               /* disable phy interrupt */
-               temp = phy_read(phydev, LAN88XX_INT_MASK);
-               temp &= ~LAN88XX_INT_MASK_MDINTPIN_EN_;
-               phy_write(phydev, LAN88XX_INT_MASK, temp);
 
-               temp = phy_read(phydev, MII_BMCR);
-               temp &= ~(BMCR_SPEED100 | BMCR_SPEED1000);
-               phy_write(phydev, MII_BMCR, temp); /* set to 10 first */
-               temp |= BMCR_SPEED100;
-               phy_write(phydev, MII_BMCR, temp); /* set to 100 later */
-
-               /* clear pending interrupt generated while workaround */
-               temp = phy_read(phydev, LAN88XX_INT_STS);
-
-               /* enable phy interrupt back */
-               temp = phy_read(phydev, LAN88XX_INT_MASK);
-               temp |= LAN88XX_INT_MASK_MDINTPIN_EN_;
-               phy_write(phydev, LAN88XX_INT_MASK, temp);
-       }
+       phy_print_status(phydev);
 }
 
 static int irq_map(struct irq_domain *d, unsigned int irq,
index a808d71..571e37e 100644 (file)
@@ -1364,6 +1364,7 @@ static const struct usb_device_id products[] = {
        {QMI_QUIRK_SET_DTR(0x1bc7, 0x1057, 2)}, /* Telit FN980 */
        {QMI_QUIRK_SET_DTR(0x1bc7, 0x1060, 2)}, /* Telit LN920 */
        {QMI_QUIRK_SET_DTR(0x1bc7, 0x1070, 2)}, /* Telit FN990 */
+       {QMI_QUIRK_SET_DTR(0x1bc7, 0x1080, 2)}, /* Telit FE990 */
        {QMI_FIXED_INTF(0x1bc7, 0x1100, 3)},    /* Telit ME910 */
        {QMI_FIXED_INTF(0x1bc7, 0x1101, 3)},    /* Telit ME910 dual modem */
        {QMI_FIXED_INTF(0x1bc7, 0x1200, 5)},    /* Telit LE920 */
index 2d53e0f..1e0f229 100644 (file)
@@ -247,6 +247,9 @@ static void fdp_nci_i2c_read_device_properties(struct device *dev,
                                           len, sizeof(**fw_vsc_cfg),
                                           GFP_KERNEL);
 
+               if (!*fw_vsc_cfg)
+                       goto alloc_err;
+
                r = device_property_read_u8_array(dev, FDP_DP_FW_VSC_CFG_NAME,
                                                  *fw_vsc_cfg, len);
 
@@ -260,6 +263,7 @@ vsc_read_err:
                *fw_vsc_cfg = NULL;
        }
 
+alloc_err:
        dev_dbg(dev, "Clock type: %d, clock frequency: %d, VSC: %s",
                *clock_type, *clock_freq, *fw_vsc_cfg != NULL ? "yes" : "no");
 }
index 82d0e41..faa108b 100644 (file)
@@ -17,6 +17,13 @@ static inline bool nf_tproxy_sk_is_transparent(struct sock *sk)
        return false;
 }
 
+static inline void nf_tproxy_twsk_deschedule_put(struct inet_timewait_sock *tw)
+{
+       local_bh_disable();
+       inet_twsk_deschedule_put(tw);
+       local_bh_enable();
+}
+
 /* assign a socket to the skb -- consumes sk */
 static inline void nf_tproxy_assign_sock(struct sk_buff *skb, struct sock *sk)
 {
index 19ebbef..5041c35 100644 (file)
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/* SPDX-License-Identifier: (GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause */
 /* Do not edit directly, auto-generated from: */
 /*     Documentation/netlink/specs/fou.yaml */
 /* YNL-GEN uapi header */
index 5883914..8c4e3e5 100644 (file)
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/* SPDX-License-Identifier: (GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause */
 /* Do not edit directly, auto-generated from: */
 /*     Documentation/netlink/specs/netdev.yaml */
 /* YNL-GEN uapi header */
index fa22ec7..7378074 100644 (file)
@@ -4569,6 +4569,7 @@ static int btf_datasec_resolve(struct btf_verifier_env *env,
        struct btf *btf = env->btf;
        u16 i;
 
+       env->resolve_mode = RESOLVE_TBD;
        for_each_vsi_from(i, v->next_member, v->t, vsi) {
                u32 var_type_id = vsi->type, type_id, type_size = 0;
                const struct btf_type *var_type = btf_type_by_id(env->btf,
index 6f3d654..f81b243 100644 (file)
@@ -97,8 +97,11 @@ reset:
 struct xdp_page_head {
        struct xdp_buff orig_ctx;
        struct xdp_buff ctx;
-       struct xdp_frame frm;
-       u8 data[];
+       union {
+               /* ::data_hard_start starts here */
+               DECLARE_FLEX_ARRAY(struct xdp_frame, frame);
+               DECLARE_FLEX_ARRAY(u8, data);
+       };
 };
 
 struct xdp_test_data {
@@ -113,6 +116,10 @@ struct xdp_test_data {
        u32 frame_cnt;
 };
 
+/* tools/testing/selftests/bpf/prog_tests/xdp_do_redirect.c:%MAX_PKT_SIZE
+ * must be updated accordingly this gets changed, otherwise BPF selftests
+ * will fail.
+ */
 #define TEST_XDP_FRAME_SIZE (PAGE_SIZE - sizeof(struct xdp_page_head))
 #define TEST_XDP_MAX_BATCH 256
 
@@ -132,8 +139,8 @@ static void xdp_test_run_init_page(struct page *page, void *arg)
        headroom -= meta_len;
 
        new_ctx = &head->ctx;
-       frm = &head->frm;
-       data = &head->data;
+       frm = head->frame;
+       data = head->data;
        memcpy(data + headroom, orig_ctx->data_meta, frm_len);
 
        xdp_init_buff(new_ctx, TEST_XDP_FRAME_SIZE, &xdp->rxq);
@@ -223,7 +230,7 @@ static void reset_ctx(struct xdp_page_head *head)
        head->ctx.data = head->orig_ctx.data;
        head->ctx.data_meta = head->orig_ctx.data_meta;
        head->ctx.data_end = head->orig_ctx.data_end;
-       xdp_update_frame_from_buff(&head->ctx, &head->frm);
+       xdp_update_frame_from_buff(&head->ctx, head->frame);
 }
 
 static int xdp_recv_frames(struct xdp_frame **frames, int nframes,
@@ -285,7 +292,7 @@ static int xdp_test_run_batch(struct xdp_test_data *xdp, struct bpf_prog *prog,
                head = phys_to_virt(page_to_phys(page));
                reset_ctx(head);
                ctx = &head->ctx;
-               frm = &head->frm;
+               frm = head->frame;
                xdp->frame_cnt++;
 
                act = bpf_prog_run_xdp(prog, ctx);
index ebc202f..bf61ea4 100644 (file)
@@ -134,6 +134,9 @@ static int cfusbl_device_notify(struct notifier_block *me, unsigned long what,
        struct usb_device *usbdev;
        int res;
 
+       if (what == NETDEV_UNREGISTER && dev->reg_state >= NETREG_UNREGISTERED)
+               return 0;
+
        /* Check whether we have a NCM device, and find its VID/PID. */
        if (!(dev->dev.parent && dev->dev.parent->driver &&
              strcmp(dev->dev.parent->driver->name, "cdc_ncm") == 0))
index 48812ec..9e10802 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: BSD-3-Clause
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
 /* Do not edit directly, auto-generated from: */
 /*     Documentation/netlink/specs/netdev.yaml */
 /* YNL-GEN kernel source */
index b16dc7e..2c5fc7d 100644 (file)
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: BSD-3-Clause */
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
 /* Do not edit directly, auto-generated from: */
 /*     Documentation/netlink/specs/netdev.yaml */
 /* YNL-GEN kernel header */
index eb7d33b..1a31815 100644 (file)
@@ -517,18 +517,16 @@ static void *kmalloc_reserve(unsigned int *size, gfp_t flags, int node,
 #ifdef HAVE_SKB_SMALL_HEAD_CACHE
        if (obj_size <= SKB_SMALL_HEAD_CACHE_SIZE &&
            !(flags & KMALLOC_NOT_NORMAL_BITS)) {
-
-               /* skb_small_head_cache has non power of two size,
-                * likely forcing SLUB to use order-3 pages.
-                * We deliberately attempt a NOMEMALLOC allocation only.
-                */
                obj = kmem_cache_alloc_node(skb_small_head_cache,
                                flags | __GFP_NOMEMALLOC | __GFP_NOWARN,
                                node);
-               if (obj) {
-                       *size = SKB_SMALL_HEAD_CACHE_SIZE;
+               *size = SKB_SMALL_HEAD_CACHE_SIZE;
+               if (obj || !(gfp_pfmemalloc_allowed(flags)))
                        goto out;
-               }
+               /* Try again but now we are using pfmemalloc reserves */
+               ret_pfmemalloc = true;
+               obj = kmem_cache_alloc_node(skb_small_head_cache, flags, node);
+               goto out;
        }
 #endif
        *size = obj_size = kmalloc_size_roundup(obj_size);
@@ -2082,6 +2080,7 @@ struct sk_buff *skb_realloc_headroom(struct sk_buff *skb, unsigned int headroom)
 }
 EXPORT_SYMBOL(skb_realloc_headroom);
 
+/* Note: We plan to rework this in linux-6.4 */
 int __skb_unclone_keeptruesize(struct sk_buff *skb, gfp_t pri)
 {
        unsigned int saved_end_offset, saved_truesize;
@@ -2100,6 +2099,22 @@ int __skb_unclone_keeptruesize(struct sk_buff *skb, gfp_t pri)
        if (likely(skb_end_offset(skb) == saved_end_offset))
                return 0;
 
+#ifdef HAVE_SKB_SMALL_HEAD_CACHE
+       /* We can not change skb->end if the original or new value
+        * is SKB_SMALL_HEAD_HEADROOM, as it might break skb_kfree_head().
+        */
+       if (saved_end_offset == SKB_SMALL_HEAD_HEADROOM ||
+           skb_end_offset(skb) == SKB_SMALL_HEAD_HEADROOM) {
+               /* We think this path should not be taken.
+                * Add a temporary trace to warn us just in case.
+                */
+               pr_err_once("__skb_unclone_keeptruesize() skb_end_offset() %u -> %u\n",
+                           saved_end_offset, skb_end_offset(skb));
+               WARN_ON_ONCE(1);
+               return 0;
+       }
+#endif
+
        shinfo = skb_shinfo(skb);
 
        /* We are about to change back skb->end,
index 341c565..c258887 100644 (file)
@@ -2818,7 +2818,8 @@ static void sk_enter_memory_pressure(struct sock *sk)
 static void sk_leave_memory_pressure(struct sock *sk)
 {
        if (sk->sk_prot->leave_memory_pressure) {
-               sk->sk_prot->leave_memory_pressure(sk);
+               INDIRECT_CALL_INET_1(sk->sk_prot->leave_memory_pressure,
+                                    tcp_leave_memory_pressure, sk);
        } else {
                unsigned long *memory_pressure = sk->sk_prot->memory_pressure;
 
index 2215f57..d8f4379 100644 (file)
@@ -1412,7 +1412,7 @@ static int nl802154_trigger_scan(struct sk_buff *skb, struct genl_info *info)
                return -EOPNOTSUPP;
        }
 
-       if (!nla_get_u8(info->attrs[NL802154_ATTR_SCAN_TYPE])) {
+       if (!info->attrs[NL802154_ATTR_SCAN_TYPE]) {
                NL_SET_ERR_MSG(info->extack, "Malformed request, missing scan type");
                return -EINVAL;
        }
index 6c3820f..5c14fe0 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: BSD-3-Clause
+// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
 /* Do not edit directly, auto-generated from: */
 /*     Documentation/netlink/specs/fou.yaml */
 /* YNL-GEN kernel source */
index b7a6812..58b1e1e 100644 (file)
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: BSD-3-Clause */
+/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
 /* Do not edit directly, auto-generated from: */
 /*     Documentation/netlink/specs/fou.yaml */
 /* YNL-GEN kernel header */
index b22b2c7..69e3317 100644 (file)
@@ -38,7 +38,7 @@ nf_tproxy_handle_time_wait4(struct net *net, struct sk_buff *skb,
                                            hp->source, lport ? lport : hp->dest,
                                            skb->dev, NF_TPROXY_LOOKUP_LISTENER);
                if (sk2) {
-                       inet_twsk_deschedule_put(inet_twsk(sk));
+                       nf_tproxy_twsk_deschedule_put(inet_twsk(sk));
                        sk = sk2;
                }
        }
index cf26d65..ebf9175 100644 (file)
@@ -186,6 +186,9 @@ static int tcp_bpf_recvmsg_parser(struct sock *sk,
        if (unlikely(flags & MSG_ERRQUEUE))
                return inet_recv_error(sk, msg, len, addr_len);
 
+       if (!len)
+               return 0;
+
        psock = sk_psock_get(sk);
        if (unlikely(!psock))
                return tcp_recvmsg(sk, msg, len, flags, addr_len);
@@ -244,6 +247,9 @@ static int tcp_bpf_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
        if (unlikely(flags & MSG_ERRQUEUE))
                return inet_recv_error(sk, msg, len, addr_len);
 
+       if (!len)
+               return 0;
+
        psock = sk_psock_get(sk);
        if (unlikely(!psock))
                return tcp_recvmsg(sk, msg, len, flags, addr_len);
index e5dc91d..0735d82 100644 (file)
@@ -68,6 +68,9 @@ static int udp_bpf_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,
        if (unlikely(flags & MSG_ERRQUEUE))
                return inet_recv_error(sk, msg, len, addr_len);
 
+       if (!len)
+               return 0;
+
        psock = sk_psock_get(sk);
        if (unlikely(!psock))
                return sk_udp_recvmsg(sk, msg, len, flags, addr_len);
index 47447f0..bee45df 100644 (file)
@@ -477,6 +477,7 @@ int ila_xlat_nl_cmd_get_mapping(struct sk_buff *skb, struct genl_info *info)
 
        rcu_read_lock();
 
+       ret = -ESRCH;
        ila = ila_lookup_by_params(&xp, ilan);
        if (ila) {
                ret = ila_dump_info(ila,
index 929502e..52f828b 100644 (file)
@@ -63,7 +63,7 @@ nf_tproxy_handle_time_wait6(struct sk_buff *skb, int tproto, int thoff,
                                            lport ? lport : hp->dest,
                                            skb->dev, NF_TPROXY_LOOKUP_LISTENER);
                if (sk2) {
-                       inet_twsk_deschedule_put(inet_twsk(sk));
+                       nf_tproxy_twsk_deschedule_put(inet_twsk(sk));
                        sk = sk2;
                }
        }
index 7250082..c6a6a60 100644 (file)
@@ -96,8 +96,8 @@ static DEFINE_MUTEX(nf_conntrack_mutex);
 #define GC_SCAN_MAX_DURATION   msecs_to_jiffies(10)
 #define GC_SCAN_EXPIRED_MAX    (64000u / HZ)
 
-#define MIN_CHAINLEN   8u
-#define MAX_CHAINLEN   (32u - MIN_CHAINLEN)
+#define MIN_CHAINLEN   50u
+#define MAX_CHAINLEN   (80u - MIN_CHAINLEN)
 
 static struct conntrack_gc_work conntrack_gc_work;
 
index c11dff9..bfc3aaa 100644 (file)
@@ -328,11 +328,12 @@ nla_put_failure:
 }
 
 #ifdef CONFIG_NF_CONNTRACK_MARK
-static int ctnetlink_dump_mark(struct sk_buff *skb, const struct nf_conn *ct)
+static int ctnetlink_dump_mark(struct sk_buff *skb, const struct nf_conn *ct,
+                              bool dump)
 {
        u32 mark = READ_ONCE(ct->mark);
 
-       if (!mark)
+       if (!mark && !dump)
                return 0;
 
        if (nla_put_be32(skb, CTA_MARK, htonl(mark)))
@@ -343,7 +344,7 @@ nla_put_failure:
        return -1;
 }
 #else
-#define ctnetlink_dump_mark(a, b) (0)
+#define ctnetlink_dump_mark(a, b, c) (0)
 #endif
 
 #ifdef CONFIG_NF_CONNTRACK_SECMARK
@@ -548,7 +549,7 @@ static int ctnetlink_dump_extinfo(struct sk_buff *skb,
 static int ctnetlink_dump_info(struct sk_buff *skb, struct nf_conn *ct)
 {
        if (ctnetlink_dump_status(skb, ct) < 0 ||
-           ctnetlink_dump_mark(skb, ct) < 0 ||
+           ctnetlink_dump_mark(skb, ct, true) < 0 ||
            ctnetlink_dump_secctx(skb, ct) < 0 ||
            ctnetlink_dump_id(skb, ct) < 0 ||
            ctnetlink_dump_use(skb, ct) < 0 ||
@@ -831,8 +832,7 @@ ctnetlink_conntrack_event(unsigned int events, const struct nf_ct_event *item)
        }
 
 #ifdef CONFIG_NF_CONNTRACK_MARK
-       if (events & (1 << IPCT_MARK) &&
-           ctnetlink_dump_mark(skb, ct) < 0)
+       if (ctnetlink_dump_mark(skb, ct, events & (1 << IPCT_MARK)))
                goto nla_put_failure;
 #endif
        nlmsg_end(skb, nlh);
@@ -2735,7 +2735,7 @@ static int __ctnetlink_glue_build(struct sk_buff *skb, struct nf_conn *ct)
                goto nla_put_failure;
 
 #ifdef CONFIG_NF_CONNTRACK_MARK
-       if (ctnetlink_dump_mark(skb, ct) < 0)
+       if (ctnetlink_dump_mark(skb, ct, true) < 0)
                goto nla_put_failure;
 #endif
        if (ctnetlink_dump_labels(skb, ct) < 0)
index 7f2bda6..8e6d7ea 100644 (file)
@@ -105,11 +105,15 @@ static void nft_last_destroy(const struct nft_ctx *ctx,
 static int nft_last_clone(struct nft_expr *dst, const struct nft_expr *src)
 {
        struct nft_last_priv *priv_dst = nft_expr_priv(dst);
+       struct nft_last_priv *priv_src = nft_expr_priv(src);
 
        priv_dst->last = kzalloc(sizeof(*priv_dst->last), GFP_ATOMIC);
        if (!priv_dst->last)
                return -ENOMEM;
 
+       priv_dst->last->set = priv_src->last->set;
+       priv_dst->last->jiffies = priv_src->last->jiffies;
+
        return 0;
 }
 
index 123578e..3ba12a7 100644 (file)
@@ -236,12 +236,16 @@ static void nft_quota_destroy(const struct nft_ctx *ctx,
 static int nft_quota_clone(struct nft_expr *dst, const struct nft_expr *src)
 {
        struct nft_quota *priv_dst = nft_expr_priv(dst);
+       struct nft_quota *priv_src = nft_expr_priv(src);
+
+       priv_dst->quota = priv_src->quota;
+       priv_dst->flags = priv_src->flags;
 
        priv_dst->consumed = kmalloc(sizeof(*priv_dst->consumed), GFP_ATOMIC);
        if (!priv_dst->consumed)
                return -ENOMEM;
 
-       atomic64_set(priv_dst->consumed, 0);
+       *priv_dst->consumed = *priv_src->consumed;
 
        return 0;
 }
index 348bf56..b9264e7 100644 (file)
@@ -1446,8 +1446,8 @@ static int nfc_se_io(struct nfc_dev *dev, u32 se_idx,
        return rc;
 
 error:
-       kfree(cb_context);
        device_unlock(&dev->dev);
+       kfree(cb_context);
        return rc;
 }
 
index 8dabfb5..0d7aee8 100644 (file)
@@ -158,6 +158,9 @@ static int tcf_connmark_init(struct net *net, struct nlattr *nla,
                nparms->zone = parm->zone;
 
                ret = 0;
+       } else {
+               err = ret;
+               goto out_free;
        }
 
        err = tcf_action_check_ctrlact(parm->action, tp, &goto_ch, extack);
index e960a46..475fe22 100644 (file)
@@ -2200,8 +2200,9 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
                fnew->flags = nla_get_u32(tb[TCA_FLOWER_FLAGS]);
 
                if (!tc_flags_valid(fnew->flags)) {
+                       kfree(fnew);
                        err = -EINVAL;
-                       goto errout;
+                       goto errout_tb;
                }
        }
 
@@ -2226,8 +2227,10 @@ static int fl_change(struct net *net, struct sk_buff *in_skb,
                }
                spin_unlock(&tp->lock);
 
-               if (err)
-                       goto errout;
+               if (err) {
+                       kfree(fnew);
+                       goto errout_tb;
+               }
        }
        fnew->handle = handle;
 
@@ -2337,7 +2340,6 @@ errout_mask:
        fl_mask_put(head, fnew->mask);
 errout_idr:
        idr_remove(&head->handle_idr, fnew->handle);
-errout:
        __fl_put(fnew);
 errout_tb:
        kfree(tb);
index a4cccdf..ff6dd86 100644 (file)
@@ -2657,16 +2657,14 @@ static int smc_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
 {
        struct sock *sk = sock->sk;
        struct smc_sock *smc;
-       int rc = -EPIPE;
+       int rc;
 
        smc = smc_sk(sk);
        lock_sock(sk);
-       if ((sk->sk_state != SMC_ACTIVE) &&
-           (sk->sk_state != SMC_APPCLOSEWAIT1) &&
-           (sk->sk_state != SMC_INIT))
-               goto out;
 
+       /* SMC does not support connect with fastopen */
        if (msg->msg_flags & MSG_FASTOPEN) {
+               /* not connected yet, fallback */
                if (sk->sk_state == SMC_INIT && !smc->connect_nonblock) {
                        rc = smc_switch_to_fallback(smc, SMC_CLC_DECL_OPTUNSUPP);
                        if (rc)
@@ -2675,6 +2673,11 @@ static int smc_sendmsg(struct socket *sock, struct msghdr *msg, size_t len)
                        rc = -EINVAL;
                        goto out;
                }
+       } else if ((sk->sk_state != SMC_ACTIVE) &&
+                  (sk->sk_state != SMC_APPCLOSEWAIT1) &&
+                  (sk->sk_state != SMC_INIT)) {
+               rc = -EPIPE;
+               goto out;
        }
 
        if (smc->use_fallback) {
index 6bae8ce..9c92c0e 100644 (file)
@@ -450,7 +450,9 @@ static struct file_system_type sock_fs_type = {
  *
  *     Returns the &file bound with @sock, implicitly storing it
  *     in sock->file. If dname is %NULL, sets to "".
- *     On failure the return is a ERR pointer (see linux/err.h).
+ *
+ *     On failure @sock is released, and an ERR pointer is returned.
+ *
  *     This function uses GFP_KERNEL internally.
  */
 
@@ -1638,7 +1640,6 @@ static struct socket *__sys_socket_create(int family, int type, int protocol)
 struct file *__sys_socket_file(int family, int type, int protocol)
 {
        struct socket *sock;
-       struct file *file;
        int flags;
 
        sock = __sys_socket_create(family, type, protocol);
@@ -1649,11 +1650,7 @@ struct file *__sys_socket_file(int family, int type, int protocol)
        if (SOCK_NONBLOCK != O_NONBLOCK && (flags & SOCK_NONBLOCK))
                flags = (flags & ~SOCK_NONBLOCK) | O_NONBLOCK;
 
-       file = sock_alloc_file(sock, flags, NULL);
-       if (IS_ERR(file))
-               sock_release(sock);
-
-       return file;
+       return sock_alloc_file(sock, flags, NULL);
 }
 
 int __sys_socket(int family, int type, int protocol)
index 6c59378..a7cc4f9 100644 (file)
@@ -508,6 +508,8 @@ handle_error:
                        zc_pfrag.offset = iter_offset.offset;
                        zc_pfrag.size = copy;
                        tls_append_frag(record, &zc_pfrag, copy);
+
+                       iter_offset.offset += copy;
                } else if (copy) {
                        copy = min_t(size_t, copy, pfrag->size - pfrag->offset);
 
index 3735cb0..b32c112 100644 (file)
@@ -405,13 +405,11 @@ static int do_tls_getsockopt_conf(struct sock *sk, char __user *optval,
                        rc = -EINVAL;
                        goto out;
                }
-               lock_sock(sk);
                memcpy(crypto_info_aes_gcm_128->iv,
                       cctx->iv + TLS_CIPHER_AES_GCM_128_SALT_SIZE,
                       TLS_CIPHER_AES_GCM_128_IV_SIZE);
                memcpy(crypto_info_aes_gcm_128->rec_seq, cctx->rec_seq,
                       TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE);
-               release_sock(sk);
                if (copy_to_user(optval,
                                 crypto_info_aes_gcm_128,
                                 sizeof(*crypto_info_aes_gcm_128)))
@@ -429,13 +427,11 @@ static int do_tls_getsockopt_conf(struct sock *sk, char __user *optval,
                        rc = -EINVAL;
                        goto out;
                }
-               lock_sock(sk);
                memcpy(crypto_info_aes_gcm_256->iv,
                       cctx->iv + TLS_CIPHER_AES_GCM_256_SALT_SIZE,
                       TLS_CIPHER_AES_GCM_256_IV_SIZE);
                memcpy(crypto_info_aes_gcm_256->rec_seq, cctx->rec_seq,
                       TLS_CIPHER_AES_GCM_256_REC_SEQ_SIZE);
-               release_sock(sk);
                if (copy_to_user(optval,
                                 crypto_info_aes_gcm_256,
                                 sizeof(*crypto_info_aes_gcm_256)))
@@ -451,13 +447,11 @@ static int do_tls_getsockopt_conf(struct sock *sk, char __user *optval,
                        rc = -EINVAL;
                        goto out;
                }
-               lock_sock(sk);
                memcpy(aes_ccm_128->iv,
                       cctx->iv + TLS_CIPHER_AES_CCM_128_SALT_SIZE,
                       TLS_CIPHER_AES_CCM_128_IV_SIZE);
                memcpy(aes_ccm_128->rec_seq, cctx->rec_seq,
                       TLS_CIPHER_AES_CCM_128_REC_SEQ_SIZE);
-               release_sock(sk);
                if (copy_to_user(optval, aes_ccm_128, sizeof(*aes_ccm_128)))
                        rc = -EFAULT;
                break;
@@ -472,13 +466,11 @@ static int do_tls_getsockopt_conf(struct sock *sk, char __user *optval,
                        rc = -EINVAL;
                        goto out;
                }
-               lock_sock(sk);
                memcpy(chacha20_poly1305->iv,
                       cctx->iv + TLS_CIPHER_CHACHA20_POLY1305_SALT_SIZE,
                       TLS_CIPHER_CHACHA20_POLY1305_IV_SIZE);
                memcpy(chacha20_poly1305->rec_seq, cctx->rec_seq,
                       TLS_CIPHER_CHACHA20_POLY1305_REC_SEQ_SIZE);
-               release_sock(sk);
                if (copy_to_user(optval, chacha20_poly1305,
                                sizeof(*chacha20_poly1305)))
                        rc = -EFAULT;
@@ -493,13 +485,11 @@ static int do_tls_getsockopt_conf(struct sock *sk, char __user *optval,
                        rc = -EINVAL;
                        goto out;
                }
-               lock_sock(sk);
                memcpy(sm4_gcm_info->iv,
                       cctx->iv + TLS_CIPHER_SM4_GCM_SALT_SIZE,
                       TLS_CIPHER_SM4_GCM_IV_SIZE);
                memcpy(sm4_gcm_info->rec_seq, cctx->rec_seq,
                       TLS_CIPHER_SM4_GCM_REC_SEQ_SIZE);
-               release_sock(sk);
                if (copy_to_user(optval, sm4_gcm_info, sizeof(*sm4_gcm_info)))
                        rc = -EFAULT;
                break;
@@ -513,13 +503,11 @@ static int do_tls_getsockopt_conf(struct sock *sk, char __user *optval,
                        rc = -EINVAL;
                        goto out;
                }
-               lock_sock(sk);
                memcpy(sm4_ccm_info->iv,
                       cctx->iv + TLS_CIPHER_SM4_CCM_SALT_SIZE,
                       TLS_CIPHER_SM4_CCM_IV_SIZE);
                memcpy(sm4_ccm_info->rec_seq, cctx->rec_seq,
                       TLS_CIPHER_SM4_CCM_REC_SEQ_SIZE);
-               release_sock(sk);
                if (copy_to_user(optval, sm4_ccm_info, sizeof(*sm4_ccm_info)))
                        rc = -EFAULT;
                break;
@@ -535,13 +523,11 @@ static int do_tls_getsockopt_conf(struct sock *sk, char __user *optval,
                        rc = -EINVAL;
                        goto out;
                }
-               lock_sock(sk);
                memcpy(crypto_info_aria_gcm_128->iv,
                       cctx->iv + TLS_CIPHER_ARIA_GCM_128_SALT_SIZE,
                       TLS_CIPHER_ARIA_GCM_128_IV_SIZE);
                memcpy(crypto_info_aria_gcm_128->rec_seq, cctx->rec_seq,
                       TLS_CIPHER_ARIA_GCM_128_REC_SEQ_SIZE);
-               release_sock(sk);
                if (copy_to_user(optval,
                                 crypto_info_aria_gcm_128,
                                 sizeof(*crypto_info_aria_gcm_128)))
@@ -559,13 +545,11 @@ static int do_tls_getsockopt_conf(struct sock *sk, char __user *optval,
                        rc = -EINVAL;
                        goto out;
                }
-               lock_sock(sk);
                memcpy(crypto_info_aria_gcm_256->iv,
                       cctx->iv + TLS_CIPHER_ARIA_GCM_256_SALT_SIZE,
                       TLS_CIPHER_ARIA_GCM_256_IV_SIZE);
                memcpy(crypto_info_aria_gcm_256->rec_seq, cctx->rec_seq,
                       TLS_CIPHER_ARIA_GCM_256_REC_SEQ_SIZE);
-               release_sock(sk);
                if (copy_to_user(optval,
                                 crypto_info_aria_gcm_256,
                                 sizeof(*crypto_info_aria_gcm_256)))
@@ -614,11 +598,9 @@ static int do_tls_getsockopt_no_pad(struct sock *sk, char __user *optval,
        if (len < sizeof(value))
                return -EINVAL;
 
-       lock_sock(sk);
        value = -EINVAL;
        if (ctx->rx_conf == TLS_SW || ctx->rx_conf == TLS_HW)
                value = ctx->rx_no_pad;
-       release_sock(sk);
        if (value < 0)
                return value;
 
@@ -635,6 +617,8 @@ static int do_tls_getsockopt(struct sock *sk, int optname,
 {
        int rc = 0;
 
+       lock_sock(sk);
+
        switch (optname) {
        case TLS_TX:
        case TLS_RX:
@@ -651,6 +635,9 @@ static int do_tls_getsockopt(struct sock *sk, int optname,
                rc = -ENOPROTOOPT;
                break;
        }
+
+       release_sock(sk);
+
        return rc;
 }
 
index 782d370..635b8bf 100644 (file)
@@ -956,7 +956,9 @@ int tls_sw_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
                               MSG_CMSG_COMPAT))
                return -EOPNOTSUPP;
 
-       mutex_lock(&tls_ctx->tx_lock);
+       ret = mutex_lock_interruptible(&tls_ctx->tx_lock);
+       if (ret)
+               return ret;
        lock_sock(sk);
 
        if (unlikely(msg->msg_controllen)) {
@@ -1290,7 +1292,9 @@ int tls_sw_sendpage(struct sock *sk, struct page *page,
                      MSG_SENDPAGE_NOTLAST | MSG_SENDPAGE_NOPOLICY))
                return -EOPNOTSUPP;
 
-       mutex_lock(&tls_ctx->tx_lock);
+       ret = mutex_lock_interruptible(&tls_ctx->tx_lock);
+       if (ret)
+               return ret;
        lock_sock(sk);
        ret = tls_sw_do_sendpage(sk, page, offset, size, flags);
        release_sock(sk);
@@ -2127,7 +2131,7 @@ recv_end:
                else
                        err = process_rx_list(ctx, msg, &control, 0,
                                              async_copy_bytes, is_peek);
-               decrypted = max(err, 0);
+               decrypted += max(err, 0);
        }
 
        copied += decrypted;
@@ -2435,11 +2439,19 @@ static void tx_work_handler(struct work_struct *work)
 
        if (!test_and_clear_bit(BIT_TX_SCHEDULED, &ctx->tx_bitmask))
                return;
-       mutex_lock(&tls_ctx->tx_lock);
-       lock_sock(sk);
-       tls_tx_records(sk, -1);
-       release_sock(sk);
-       mutex_unlock(&tls_ctx->tx_lock);
+
+       if (mutex_trylock(&tls_ctx->tx_lock)) {
+               lock_sock(sk);
+               tls_tx_records(sk, -1);
+               release_sock(sk);
+               mutex_unlock(&tls_ctx->tx_lock);
+       } else if (!test_and_set_bit(BIT_TX_SCHEDULED, &ctx->tx_bitmask)) {
+               /* Someone is holding the tx_lock, they will likely run Tx
+                * and cancel the work on their way out of the lock section.
+                * Schedule a long delay just in case.
+                */
+               schedule_delayed_work(&ctx->tx_work.work, msecs_to_jiffies(10));
+       }
 }
 
 static bool tls_is_tx_ready(struct tls_sw_context_tx *ctx)
index 347122c..0b0f18e 100644 (file)
@@ -2105,7 +2105,8 @@ out:
 #define UNIX_SKB_FRAGS_SZ (PAGE_SIZE << get_order(32768))
 
 #if IS_ENABLED(CONFIG_AF_UNIX_OOB)
-static int queue_oob(struct socket *sock, struct msghdr *msg, struct sock *other)
+static int queue_oob(struct socket *sock, struct msghdr *msg, struct sock *other,
+                    struct scm_cookie *scm, bool fds_sent)
 {
        struct unix_sock *ousk = unix_sk(other);
        struct sk_buff *skb;
@@ -2116,6 +2117,11 @@ static int queue_oob(struct socket *sock, struct msghdr *msg, struct sock *other
        if (!skb)
                return err;
 
+       err = unix_scm_to_skb(scm, skb, !fds_sent);
+       if (err < 0) {
+               kfree_skb(skb);
+               return err;
+       }
        skb_put(skb, 1);
        err = skb_copy_datagram_from_iter(skb, 0, &msg->msg_iter, 1);
 
@@ -2243,7 +2249,7 @@ static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg,
 
 #if IS_ENABLED(CONFIG_AF_UNIX_OOB)
        if (msg->msg_flags & MSG_OOB) {
-               err = queue_oob(sock, msg, other);
+               err = queue_oob(sock, msg, other, &scm, fds_sent);
                if (err)
                        goto out_err;
                sent++;
index e9bf155..2f9d827 100644 (file)
@@ -54,6 +54,9 @@ static int unix_bpf_recvmsg(struct sock *sk, struct msghdr *msg,
        struct sk_psock *psock;
        int copied;
 
+       if (!len)
+               return 0;
+
        psock = sk_psock_get(sk);
        if (unlikely(!psock))
                return __unix_recvmsg(sk, msg, len, flags);
index 5883914..8c4e3e5 100644 (file)
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/* SPDX-License-Identifier: (GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause */
 /* Do not edit directly, auto-generated from: */
 /*     Documentation/netlink/specs/netdev.yaml */
 /* YNL-GEN uapi header */
index db410b7..ffaa803 100755 (executable)
@@ -1,5 +1,5 @@
 #!/usr/bin/env python3
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
 
 import argparse
 import json
index 3c73f59..4b3797f 100644 (file)
@@ -1,7 +1,8 @@
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
 
-from .nlspec import SpecAttr, SpecAttrSet, SpecFamily, SpecOperation
+from .nlspec import SpecAttr, SpecAttrSet, SpecEnumEntry, SpecEnumSet, \
+    SpecFamily, SpecOperation
 from .ynl import YnlFamily
 
-__all__ = ["SpecAttr", "SpecAttrSet", "SpecFamily", "SpecOperation",
-           "YnlFamily"]
+__all__ = ["SpecAttr", "SpecAttrSet", "SpecEnumEntry", "SpecEnumSet",
+           "SpecFamily", "SpecOperation", "YnlFamily"]
index 71da568..a34d088 100644 (file)
@@ -1,4 +1,4 @@
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
 
 import collections
 import importlib
@@ -57,6 +57,94 @@ class SpecElement:
         pass
 
 
+class SpecEnumEntry(SpecElement):
+    """ Entry within an enum declared in the Netlink spec.
+
+    Attributes:
+        doc         documentation string
+        enum_set    back reference to the enum
+        value       numerical value of this enum (use accessors in most situations!)
+
+    Methods:
+        raw_value   raw value, i.e. the id in the enum, unlike user value which is a mask for flags
+        user_value   user value, same as raw value for enums, for flags it's the mask
+    """
+    def __init__(self, enum_set, yaml, prev, value_start):
+        if isinstance(yaml, str):
+            yaml = {'name': yaml}
+        super().__init__(enum_set.family, yaml)
+
+        self.doc = yaml.get('doc', '')
+        self.enum_set = enum_set
+
+        if 'value' in yaml:
+            self.value = yaml['value']
+        elif prev:
+            self.value = prev.value + 1
+        else:
+            self.value = value_start
+
+    def has_doc(self):
+        return bool(self.doc)
+
+    def raw_value(self):
+        return self.value
+
+    def user_value(self):
+        if self.enum_set['type'] == 'flags':
+            return 1 << self.value
+        else:
+            return self.value
+
+
+class SpecEnumSet(SpecElement):
+    """ Enum type
+
+    Represents an enumeration (list of numerical constants)
+    as declared in the "definitions" section of the spec.
+
+    Attributes:
+        type            enum or flags
+        entries         entries by name
+        entries_by_val  entries by value
+    Methods:
+        get_mask      for flags compute the mask of all defined values
+    """
+    def __init__(self, family, yaml):
+        super().__init__(family, yaml)
+
+        self.type = yaml['type']
+
+        prev_entry = None
+        value_start = self.yaml.get('value-start', 0)
+        self.entries = dict()
+        self.entries_by_val = dict()
+        for entry in self.yaml['entries']:
+            e = self.new_entry(entry, prev_entry, value_start)
+            self.entries[e.name] = e
+            self.entries_by_val[e.raw_value()] = e
+            prev_entry = e
+
+    def new_entry(self, entry, prev_entry, value_start):
+        return SpecEnumEntry(self, entry, prev_entry, value_start)
+
+    def has_doc(self):
+        if 'doc' in self.yaml:
+            return True
+        for entry in self.entries.values():
+            if entry.has_doc():
+                return True
+        return False
+
+    def get_mask(self):
+        mask = 0
+        idx = self.yaml.get('value-start', 0)
+        for _ in self.entries.values():
+            mask |= 1 << idx
+            idx += 1
+        return mask
+
+
 class SpecAttr(SpecElement):
     """ Single Netlink atttribute type
 
@@ -95,15 +183,22 @@ class SpecAttrSet(SpecElement):
         self.attrs = collections.OrderedDict()
         self.attrs_by_val = collections.OrderedDict()
 
-        val = 0
-        for elem in self.yaml['attributes']:
-            if 'value' in elem:
-                val = elem['value']
+        if self.subset_of is None:
+            val = 1
+            for elem in self.yaml['attributes']:
+                if 'value' in elem:
+                    val = elem['value']
 
-            attr = self.new_attr(elem, val)
-            self.attrs[attr.name] = attr
-            self.attrs_by_val[attr.value] = attr
-            val += 1
+                attr = self.new_attr(elem, val)
+                self.attrs[attr.name] = attr
+                self.attrs_by_val[attr.value] = attr
+                val += 1
+        else:
+            real_set = family.attr_sets[self.subset_of]
+            for elem in self.yaml['attributes']:
+                attr = real_set[elem['name']]
+                self.attrs[attr.name] = attr
+                self.attrs_by_val[attr.value] = attr
 
     def new_attr(self, elem, value):
         return SpecAttr(self.family, self, elem, value)
@@ -186,6 +281,7 @@ class SpecFamily(SpecElement):
         msgs       dict of all messages (index by name)
         msgs_by_value  dict of all messages (indexed by name)
         ops        dict of all valid requests / responses
+        consts     dict of all constants/enums
     """
     def __init__(self, spec_path, schema_path=None):
         with open(spec_path, "r") as stream:
@@ -215,6 +311,7 @@ class SpecFamily(SpecElement):
         self.req_by_value = collections.OrderedDict()
         self.rsp_by_value = collections.OrderedDict()
         self.ops = collections.OrderedDict()
+        self.consts = collections.OrderedDict()
 
         last_exception = None
         while len(self._resolution_list) > 0:
@@ -235,6 +332,9 @@ class SpecFamily(SpecElement):
             if len(resolved) == 0:
                 raise last_exception
 
+    def new_enum(self, elem):
+        return SpecEnumSet(self, elem)
+
     def new_attr_set(self, elem):
         return SpecAttrSet(self, elem)
 
@@ -245,7 +345,7 @@ class SpecFamily(SpecElement):
         self._resolution_list.append(elem)
 
     def _dictify_ops_unified(self):
-        val = 0
+        val = 1
         for elem in self.yaml['operations']['list']:
             if 'value' in elem:
                 val = elem['value']
@@ -256,7 +356,7 @@ class SpecFamily(SpecElement):
             self.msgs[op.name] = op
 
     def _dictify_ops_directional(self):
-        req_val = rsp_val = 0
+        req_val = rsp_val = 1
         for elem in self.yaml['operations']['list']:
             if 'notify' in elem:
                 if 'value' in elem:
@@ -289,6 +389,12 @@ class SpecFamily(SpecElement):
     def resolve(self):
         self.resolve_up(super())
 
+        for elem in self.yaml['definitions']:
+            if elem['type'] == 'enum' or elem['type'] == 'flags':
+                self.consts[elem['name']] = self.new_enum(elem)
+            else:
+                self.consts[elem['name']] = elem
+
         for elem in self.yaml['attribute-sets']:
             attr_set = self.new_attr_set(elem)
             self.attr_sets[elem['name']] = attr_set
index 1c7411e..90764a8 100644 (file)
@@ -1,4 +1,4 @@
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
 
 import functools
 import os
@@ -303,11 +303,6 @@ class YnlFamily(SpecFamily):
         self.sock.setsockopt(Netlink.SOL_NETLINK, Netlink.NETLINK_CAP_ACK, 1)
         self.sock.setsockopt(Netlink.SOL_NETLINK, Netlink.NETLINK_EXT_ACK, 1)
 
-        self._types = dict()
-
-        for elem in self.yaml.get('definitions', []):
-            self._types[elem['name']] = elem
-
         self.async_msg_ids = set()
         self.async_msg_queue = []
 
@@ -353,13 +348,13 @@ class YnlFamily(SpecFamily):
 
     def _decode_enum(self, rsp, attr_spec):
         raw = rsp[attr_spec['name']]
-        enum = self._types[attr_spec['enum']]
+        enum = self.consts[attr_spec['enum']]
         i = attr_spec.get('value-start', 0)
         if 'enum-as-flags' in attr_spec and attr_spec['enum-as-flags']:
             value = set()
             while raw:
                 if raw & 1:
-                    value.add(enum['entries'][i])
+                    value.add(enum.entries_by_val[i].name)
                 raw >>= 1
                 i += 1
         else:
index 274e9c5..1bcc535 100755 (executable)
@@ -1,11 +1,12 @@
 #!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
 
 import argparse
 import collections
 import os
 import yaml
 
-from lib import SpecFamily, SpecAttrSet, SpecAttr, SpecOperation
+from lib import SpecFamily, SpecAttrSet, SpecAttr, SpecOperation, SpecEnumSet, SpecEnumEntry
 
 
 def c_upper(name):
@@ -566,97 +567,37 @@ class Struct:
         self.inherited = [c_lower(x) for x in sorted(self._inherited)]
 
 
-class EnumEntry:
+class EnumEntry(SpecEnumEntry):
     def __init__(self, enum_set, yaml, prev, value_start):
-        if isinstance(yaml, str):
-            self.name = yaml
-            yaml = {}
-            self.doc = ''
-        else:
-            self.name = yaml['name']
-            self.doc = yaml.get('doc', '')
-
-        self.yaml = yaml
-        self.enum_set = enum_set
-        self.c_name = c_upper(enum_set.value_pfx + self.name)
-
-        if 'value' in yaml:
-            self.value = yaml['value']
-            if prev:
-                self.value_change = (self.value != prev.value + 1)
-        elif prev:
-            self.value_change = False
-            self.value = prev.value + 1
+        super().__init__(enum_set, yaml, prev, value_start)
+
+        if prev:
+            self.value_change = (self.value != prev.value + 1)
         else:
-            self.value = value_start
             self.value_change = (self.value != 0)
-
         self.value_change = self.value_change or self.enum_set['type'] == 'flags'
 
-    def __getitem__(self, key):
-        return self.yaml[key]
-
-    def __contains__(self, key):
-        return key in self.yaml
-
-    def has_doc(self):
-        return bool(self.doc)
+        # Added by resolve:
+        self.c_name = None
+        delattr(self, "c_name")
 
-    # raw value, i.e. the id in the enum, unlike user value which is a mask for flags
-    def raw_value(self):
-        return self.value
+    def resolve(self):
+        self.resolve_up(super())
 
-    # user value, same as raw value for enums, for flags it's the mask
-    def user_value(self):
-        if self.enum_set['type'] == 'flags':
-            return 1 << self.value
-        else:
-            return self.value
+        self.c_name = c_upper(self.enum_set.value_pfx + self.name)
 
 
-class EnumSet:
+class EnumSet(SpecEnumSet):
     def __init__(self, family, yaml):
-        self.yaml = yaml
-        self.family = family
-
         self.render_name = c_lower(family.name + '-' + yaml['name'])
         self.enum_name = 'enum ' + self.render_name
 
         self.value_pfx = yaml.get('name-prefix', f"{family.name}-{yaml['name']}-")
 
-        self.type = yaml['type']
-
-        prev_entry = None
-        value_start = self.yaml.get('value-start', 0)
-        self.entries = {}
-        self.entry_list = []
-        for entry in self.yaml['entries']:
-            e = EnumEntry(self, entry, prev_entry, value_start)
-            self.entries[e.name] = e
-            self.entry_list.append(e)
-            prev_entry = e
-
-    def __getitem__(self, key):
-        return self.yaml[key]
-
-    def __contains__(self, key):
-        return key in self.yaml
-
-    def has_doc(self):
-        if 'doc' in self.yaml:
-            return True
-        for entry in self.entry_list:
-            if entry.has_doc():
-                return True
-        return False
+        super().__init__(family, yaml)
 
-    def get_mask(self):
-        mask = 0
-        idx = self.yaml.get('value-start', 0)
-        for _ in self.entry_list:
-            mask |= 1 << idx
-            idx += 1
-        return mask
+    def new_entry(self, entry, prev_entry, value_start):
+        return EnumEntry(self, entry, prev_entry, value_start)
 
 
 class AttrSet(SpecAttrSet):
@@ -791,8 +732,6 @@ class Family(SpecFamily):
 
         self.mcgrps = self.yaml.get('mcast-groups', {'list': []})
 
-        self.consts = dict()
-
         self.hooks = dict()
         for when in ['pre', 'post']:
             self.hooks[when] = dict()
@@ -819,6 +758,9 @@ class Family(SpecFamily):
         if self.kernel_policy == 'global':
             self._load_global_policy()
 
+    def new_enum(self, elem):
+        return EnumSet(self, elem)
+
     def new_attr_set(self, elem):
         return AttrSet(self, elem)
 
@@ -836,12 +778,6 @@ class Family(SpecFamily):
                 }
 
     def _dictify(self):
-        for elem in self.yaml['definitions']:
-            if elem['type'] == 'enum' or elem['type'] == 'flags':
-                self.consts[elem['name']] = EnumSet(self, elem)
-            else:
-                self.consts[elem['name']] = elem
-
         ntf = []
         for msg in self.msgs.values():
             if 'notify' in msg:
@@ -1979,7 +1915,7 @@ def render_uapi(family, cw):
                 if 'doc' in enum:
                     doc = ' - ' + enum['doc']
                 cw.write_doc_line(enum.enum_name + doc)
-                for entry in enum.entry_list:
+                for entry in enum.entries.values():
                     if entry.has_doc():
                         doc = '@' + entry.c_name + ': ' + entry['doc']
                         cw.write_doc_line(doc)
@@ -1987,7 +1923,7 @@ def render_uapi(family, cw):
 
             uapi_enum_start(family, cw, const, 'name')
             name_pfx = const.get('name-prefix', f"{family.name}-{const['name']}-")
-            for entry in enum.entry_list:
+            for entry in enum.entries.values():
                 suffix = ','
                 if entry.value_change:
                     suffix = f" = {entry.user_value()}" + suffix
@@ -2044,14 +1980,17 @@ def render_uapi(family, cw):
     max_value = f"({cnt_name} - 1)"
 
     uapi_enum_start(family, cw, family['operations'], 'enum-name')
+    val = 0
     for op in family.msgs.values():
         if separate_ntf and ('notify' in op or 'event' in op):
             continue
 
         suffix = ','
-        if 'value' in op:
-            suffix = f" = {op['value']},"
+        if op.value != val:
+            suffix = f" = {op.value},"
+            val = op.value
         cw.p(op.enum_name + suffix)
+        val += 1
     cw.nl()
     cw.p(cnt_name + ('' if max_by_define else ','))
     if not max_by_define:
@@ -2124,12 +2063,12 @@ def main():
 
     _, spec_kernel = find_kernel_root(args.spec)
     if args.mode == 'uapi':
-        cw.p('/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */')
+        cw.p('/* SPDX-License-Identifier: (GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause */')
     else:
         if args.header:
-            cw.p('/* SPDX-License-Identifier: BSD-3-Clause */')
+            cw.p('/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */')
         else:
-            cw.p('// SPDX-License-Identifier: BSD-3-Clause')
+            cw.p('// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause')
     cw.p("/* Do not edit directly, auto-generated from: */")
     cw.p(f"/*\t{spec_kernel} */")
     cw.p(f"/* YNL-GEN {args.mode} {'header' if args.header else 'source'} */")
index 43989ae..74f5de1 100755 (executable)
@@ -1,5 +1,5 @@
 #!/bin/bash
-# SPDX-License-Identifier: BSD-3-Clause
+# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
 
 TOOL=$(dirname $(realpath $0))/ynl-gen-c.py
 
index cbb600b..210d643 100644 (file)
@@ -879,6 +879,34 @@ static struct btf_raw_test raw_tests[] = {
        .btf_load_err = true,
        .err_str = "Invalid elem",
 },
+{
+       .descr = "var after datasec, ptr followed by modifier",
+       .raw_types = {
+               /* .bss section */                              /* [1] */
+               BTF_TYPE_ENC(NAME_TBD, BTF_INFO_ENC(BTF_KIND_DATASEC, 0, 2),
+                       sizeof(void*)+4),
+               BTF_VAR_SECINFO_ENC(4, 0, sizeof(void*)),
+               BTF_VAR_SECINFO_ENC(6, sizeof(void*), 4),
+               /* int */                                       /* [2] */
+               BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),
+               /* int* */                                      /* [3] */
+               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_PTR, 0, 0), 2),
+               BTF_VAR_ENC(NAME_TBD, 3, 0),                    /* [4] */
+               /* const int */                                 /* [5] */
+               BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_CONST, 0, 0), 2),
+               BTF_VAR_ENC(NAME_TBD, 5, 0),                    /* [6] */
+               BTF_END_RAW,
+       },
+       .str_sec = "\0a\0b\0c\0",
+       .str_sec_size = sizeof("\0a\0b\0c\0"),
+       .map_type = BPF_MAP_TYPE_ARRAY,
+       .map_name = ".bss",
+       .key_size = sizeof(int),
+       .value_size = sizeof(void*)+4,
+       .key_type_id = 0,
+       .value_type_id = 1,
+       .max_entries = 1,
+},
 /* Test member exceeds the size of struct.
  *
  * struct A {
index 2666c84..7271a18 100644 (file)
@@ -65,12 +65,13 @@ static int attach_tc_prog(struct bpf_tc_hook *hook, int fd)
 }
 
 /* The maximum permissible size is: PAGE_SIZE - sizeof(struct xdp_page_head) -
- * sizeof(struct skb_shared_info) - XDP_PACKET_HEADROOM = 3368 bytes
+ * SKB_DATA_ALIGN(sizeof(struct skb_shared_info)) - XDP_PACKET_HEADROOM =
+ * 3408 bytes for 64-byte cacheline and 3216 for 256-byte one.
  */
 #if defined(__s390x__)
-#define MAX_PKT_SIZE 3176
+#define MAX_PKT_SIZE 3216
 #else
-#define MAX_PKT_SIZE 3368
+#define MAX_PKT_SIZE 3408
 #endif
 static void test_max_pkt_size(int fd)
 {
index 924ecb3..dd40d9f 100755 (executable)
@@ -404,6 +404,8 @@ EOF
        echo SERVER-$family | ip netns exec "$ns1" timeout 5 socat -u STDIN TCP-LISTEN:2000 &
        sc_s=$!
 
+       sleep 1
+
        result=$(ip netns exec "$ns0" timeout 1 socat TCP:$daddr:2000 STDOUT)
 
        if [ "$result" = "SERVER-inet" ];then