]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
6.2-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 10 Mar 2023 11:26:00 +0000 (12:26 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 10 Mar 2023 11:26:00 +0000 (12:26 +0100)
added patches:
drm-display-dp_mst-add-drm_atomic_get_old_mst_topology_state.patch
eth-fealnx-bring-back-this-old-driver.patch
net-tls-avoid-hanging-tasks-on-the-tx_lock.patch
vdpa-ifcvf-alloc-the-mgmt_dev-before-the-adapter.patch
vdpa-ifcvf-allocate-the-adapter-in-dev_add.patch
vdpa-ifcvf-decouple-config-dev-irq-requester-and-vectors-allocator-from-the-adapter.patch
vdpa-ifcvf-decouple-config-irq-releaser-from-the-adapter.patch
vdpa-ifcvf-decouple-config-space-ops-from-the-adapter.patch
vdpa-ifcvf-decouple-hw-features-manipulators-from-the-adapter.patch
vdpa-ifcvf-decouple-vq-irq-releasers-from-the-adapter.patch
vdpa-ifcvf-decouple-vq-irq-requester-from-the-adapter.patch
vdpa-ifcvf-ifcvf_request_irq-works-on-ifcvf_hw.patch
vdpa-ifcvf-manage-ifcvf_hw-in-the-mgmt_dev.patch
x86-resctl-fix-scheduler-confusion-with-current.patch

15 files changed:
queue-6.2/drm-display-dp_mst-add-drm_atomic_get_old_mst_topology_state.patch [new file with mode: 0644]
queue-6.2/eth-fealnx-bring-back-this-old-driver.patch [new file with mode: 0644]
queue-6.2/net-tls-avoid-hanging-tasks-on-the-tx_lock.patch [new file with mode: 0644]
queue-6.2/series
queue-6.2/vdpa-ifcvf-alloc-the-mgmt_dev-before-the-adapter.patch [new file with mode: 0644]
queue-6.2/vdpa-ifcvf-allocate-the-adapter-in-dev_add.patch [new file with mode: 0644]
queue-6.2/vdpa-ifcvf-decouple-config-dev-irq-requester-and-vectors-allocator-from-the-adapter.patch [new file with mode: 0644]
queue-6.2/vdpa-ifcvf-decouple-config-irq-releaser-from-the-adapter.patch [new file with mode: 0644]
queue-6.2/vdpa-ifcvf-decouple-config-space-ops-from-the-adapter.patch [new file with mode: 0644]
queue-6.2/vdpa-ifcvf-decouple-hw-features-manipulators-from-the-adapter.patch [new file with mode: 0644]
queue-6.2/vdpa-ifcvf-decouple-vq-irq-releasers-from-the-adapter.patch [new file with mode: 0644]
queue-6.2/vdpa-ifcvf-decouple-vq-irq-requester-from-the-adapter.patch [new file with mode: 0644]
queue-6.2/vdpa-ifcvf-ifcvf_request_irq-works-on-ifcvf_hw.patch [new file with mode: 0644]
queue-6.2/vdpa-ifcvf-manage-ifcvf_hw-in-the-mgmt_dev.patch [new file with mode: 0644]
queue-6.2/x86-resctl-fix-scheduler-confusion-with-current.patch [new file with mode: 0644]

diff --git a/queue-6.2/drm-display-dp_mst-add-drm_atomic_get_old_mst_topology_state.patch b/queue-6.2/drm-display-dp_mst-add-drm_atomic_get_old_mst_topology_state.patch
new file mode 100644 (file)
index 0000000..e0fb12e
--- /dev/null
@@ -0,0 +1,111 @@
+From 9ffdb67af0ee625ae127711845532f670cc6a4e7 Mon Sep 17 00:00:00 2001
+From: Imre Deak <imre.deak@intel.com>
+Date: Mon, 6 Feb 2023 13:48:55 +0200
+Subject: drm/display/dp_mst: Add drm_atomic_get_old_mst_topology_state()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Imre Deak <imre.deak@intel.com>
+
+commit 9ffdb67af0ee625ae127711845532f670cc6a4e7 upstream.
+
+Add a function to get the old MST topology state, required by a
+follow-up i915 patch.
+
+While at it clarify the code comment of
+drm_atomic_get_new_mst_topology_state() and add _new prefix
+to the new state pointer to remind about its difference from the old
+state.
+
+v2: Use old_/new_ prefixes for the state pointers. (Ville)
+
+Cc: Lyude Paul <lyude@redhat.com>
+Cc: Ville Syrjälä <ville.syrjala@linux.intel.com>
+Cc: stable@vger.kernel.org # 6.1
+Cc: dri-devel@lists.freedesktop.org
+Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
+Reviewed-by: Lyude Paul <lyude@redhat.com>
+Acked-by: Lyude Paul <lyude@redhat.com>
+Acked-by: Daniel Vetter <daniel@ffwll.ch>
+Acked-by: Wayne Lin <wayne.lin@amd.com>
+Acked-by: Jani Nikula <jani.nikula@intel.com>
+Signed-off-by: Imre Deak <imre.deak@intel.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20230206114856.2665066-3-imre.deak@intel.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/gpu/drm/display/drm_dp_mst_topology.c |   33 ++++++++++++++++++++++----
+ include/drm/display/drm_dp_mst_helper.h       |    3 ++
+ 2 files changed, 32 insertions(+), 4 deletions(-)
+
+--- a/drivers/gpu/drm/display/drm_dp_mst_topology.c
++++ b/drivers/gpu/drm/display/drm_dp_mst_topology.c
+@@ -5355,27 +5355,52 @@ struct drm_dp_mst_topology_state *drm_at
+ EXPORT_SYMBOL(drm_atomic_get_mst_topology_state);
+ /**
++ * drm_atomic_get_old_mst_topology_state: get old MST topology state in atomic state, if any
++ * @state: global atomic state
++ * @mgr: MST topology manager, also the private object in this case
++ *
++ * This function wraps drm_atomic_get_old_private_obj_state() passing in the MST atomic
++ * state vtable so that the private object state returned is that of a MST
++ * topology object.
++ *
++ * Returns:
++ *
++ * The old MST topology state, or NULL if there's no topology state for this MST mgr
++ * in the global atomic state
++ */
++struct drm_dp_mst_topology_state *
++drm_atomic_get_old_mst_topology_state(struct drm_atomic_state *state,
++                                    struct drm_dp_mst_topology_mgr *mgr)
++{
++      struct drm_private_state *old_priv_state =
++              drm_atomic_get_old_private_obj_state(state, &mgr->base);
++
++      return old_priv_state ? to_dp_mst_topology_state(old_priv_state) : NULL;
++}
++EXPORT_SYMBOL(drm_atomic_get_old_mst_topology_state);
++
++/**
+  * drm_atomic_get_new_mst_topology_state: get new MST topology state in atomic state, if any
+  * @state: global atomic state
+  * @mgr: MST topology manager, also the private object in this case
+  *
+- * This function wraps drm_atomic_get_priv_obj_state() passing in the MST atomic
++ * This function wraps drm_atomic_get_new_private_obj_state() passing in the MST atomic
+  * state vtable so that the private object state returned is that of a MST
+  * topology object.
+  *
+  * Returns:
+  *
+- * The MST topology state, or NULL if there's no topology state for this MST mgr
++ * The new MST topology state, or NULL if there's no topology state for this MST mgr
+  * in the global atomic state
+  */
+ struct drm_dp_mst_topology_state *
+ drm_atomic_get_new_mst_topology_state(struct drm_atomic_state *state,
+                                     struct drm_dp_mst_topology_mgr *mgr)
+ {
+-      struct drm_private_state *priv_state =
++      struct drm_private_state *new_priv_state =
+               drm_atomic_get_new_private_obj_state(state, &mgr->base);
+-      return priv_state ? to_dp_mst_topology_state(priv_state) : NULL;
++      return new_priv_state ? to_dp_mst_topology_state(new_priv_state) : NULL;
+ }
+ EXPORT_SYMBOL(drm_atomic_get_new_mst_topology_state);
+--- a/include/drm/display/drm_dp_mst_helper.h
++++ b/include/drm/display/drm_dp_mst_helper.h
+@@ -867,6 +867,9 @@ struct drm_dp_mst_topology_state *
+ drm_atomic_get_mst_topology_state(struct drm_atomic_state *state,
+                                 struct drm_dp_mst_topology_mgr *mgr);
+ struct drm_dp_mst_topology_state *
++drm_atomic_get_old_mst_topology_state(struct drm_atomic_state *state,
++                                    struct drm_dp_mst_topology_mgr *mgr);
++struct drm_dp_mst_topology_state *
+ drm_atomic_get_new_mst_topology_state(struct drm_atomic_state *state,
+                                     struct drm_dp_mst_topology_mgr *mgr);
+ struct drm_dp_mst_atomic_payload *
diff --git a/queue-6.2/eth-fealnx-bring-back-this-old-driver.patch b/queue-6.2/eth-fealnx-bring-back-this-old-driver.patch
new file mode 100644 (file)
index 0000000..4c7b35b
--- /dev/null
@@ -0,0 +1,2038 @@
+From 8f14820801042c221bb9fe51643a2585cac5dec2 Mon Sep 17 00:00:00 2001
+From: Jakub Kicinski <kuba@kernel.org>
+Date: Tue, 7 Mar 2023 09:19:30 -0800
+Subject: eth: fealnx: bring back this old driver
+
+From: Jakub Kicinski <kuba@kernel.org>
+
+commit 8f14820801042c221bb9fe51643a2585cac5dec2 upstream.
+
+This reverts commit d5e2d038dbece821f1af57acbeded3aa9a1832c1.
+
+We have a report of this chip being used on a
+
+  SURECOM EP-320X-S 100/10M Ethernet PCI Adapter
+
+which could still have been purchased in some parts
+of the world 3 years ago.
+
+Cc: stable@vger.kernel.org
+Link: https://bugzilla.kernel.org/show_bug.cgi?id=217151
+Fixes: d5e2d038dbec ("eth: fealnx: delete the driver for Myson MTD-800")
+Link: https://lore.kernel.org/r/20230307171930.4008454-1-kuba@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ arch/mips/configs/mtx1_defconfig      |    1 
+ arch/powerpc/configs/ppc6xx_defconfig |    1 
+ drivers/net/ethernet/Kconfig          |   10 
+ drivers/net/ethernet/Makefile         |    1 
+ drivers/net/ethernet/fealnx.c         | 1953 ++++++++++++++++++++++++++++++++++
+ 5 files changed, 1966 insertions(+)
+ create mode 100644 drivers/net/ethernet/fealnx.c
+
+--- a/arch/mips/configs/mtx1_defconfig
++++ b/arch/mips/configs/mtx1_defconfig
+@@ -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
+--- a/arch/powerpc/configs/ppc6xx_defconfig
++++ b/arch/powerpc/configs/ppc6xx_defconfig
+@@ -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
+--- a/drivers/net/ethernet/Kconfig
++++ b/drivers/net/ethernet/Kconfig
+@@ -132,6 +132,16 @@ source "drivers/net/ethernet/mscc/Kconfi
+ 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"
+--- a/drivers/net/ethernet/Makefile
++++ b/drivers/net/ethernet/Makefile
+@@ -64,6 +64,7 @@ obj-$(CONFIG_NET_VENDOR_MICROCHIP) += mi
+ 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/
+--- /dev/null
++++ b/drivers/net/ethernet/fealnx.c
+@@ -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);
diff --git a/queue-6.2/net-tls-avoid-hanging-tasks-on-the-tx_lock.patch b/queue-6.2/net-tls-avoid-hanging-tasks-on-the-tx_lock.patch
new file mode 100644 (file)
index 0000000..bdf3fce
--- /dev/null
@@ -0,0 +1,78 @@
+From f3221361dc85d4de22586ce8441ec2c67b454f5d Mon Sep 17 00:00:00 2001
+From: Jakub Kicinski <kuba@kernel.org>
+Date: Tue, 28 Feb 2023 16:28:57 -0800
+Subject: net: tls: avoid hanging tasks on the tx_lock
+
+From: Jakub Kicinski <kuba@kernel.org>
+
+commit f3221361dc85d4de22586ce8441ec2c67b454f5d upstream.
+
+syzbot sent a hung task report and Eric explains that adversarial
+receiver may keep RWIN at 0 for a long time, so we are not guaranteed
+to make forward progress. Thread which took tx_lock and went to sleep
+may not release tx_lock for hours. Use interruptible sleep where
+possible and reschedule the work if it can't take the lock.
+
+Testing: existing selftest passes
+
+Reported-by: syzbot+9c0268252b8ef967c62e@syzkaller.appspotmail.com
+Fixes: 79ffe6087e91 ("net/tls: add a TX lock")
+Link: https://lore.kernel.org/all/000000000000e412e905f5b46201@google.com/
+Cc: stable@vger.kernel.org # wait 4 weeks
+Reviewed-by: Eric Dumazet <edumazet@google.com>
+Link: https://lore.kernel.org/r/20230301002857.2101894-1-kuba@kernel.org
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ net/tls/tls_sw.c |   26 +++++++++++++++++++-------
+ 1 file changed, 19 insertions(+), 7 deletions(-)
+
+--- a/net/tls/tls_sw.c
++++ b/net/tls/tls_sw.c
+@@ -941,7 +941,9 @@ int tls_sw_sendmsg(struct sock *sk, stru
+                              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)) {
+@@ -1275,7 +1277,9 @@ int tls_sw_sendpage(struct sock *sk, str
+                     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);
+@@ -2416,11 +2420,19 @@ static void tx_work_handler(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 4b0a89a42192345220ac00633b3d1eda6025efc8..48c9b74fbe29a07a6f8a1ff94d6224d4986ba544 100644 (file)
@@ -185,3 +185,17 @@ phy-rockchip-typec-fix-unsigned-comparison-with-less.patch
 rdma-cma-distinguish-between-sockaddr_in-and-sockadd.patch
 soundwire-cadence-remove-wasted-space-in-response_bu.patch
 soundwire-cadence-drain-the-rx-fifo-after-an-io-time.patch
+eth-fealnx-bring-back-this-old-driver.patch
+net-tls-avoid-hanging-tasks-on-the-tx_lock.patch
+x86-resctl-fix-scheduler-confusion-with-current.patch
+vdpa-ifcvf-decouple-hw-features-manipulators-from-the-adapter.patch
+vdpa-ifcvf-decouple-config-space-ops-from-the-adapter.patch
+vdpa-ifcvf-alloc-the-mgmt_dev-before-the-adapter.patch
+vdpa-ifcvf-decouple-vq-irq-releasers-from-the-adapter.patch
+vdpa-ifcvf-decouple-config-irq-releaser-from-the-adapter.patch
+vdpa-ifcvf-decouple-vq-irq-requester-from-the-adapter.patch
+vdpa-ifcvf-decouple-config-dev-irq-requester-and-vectors-allocator-from-the-adapter.patch
+vdpa-ifcvf-ifcvf_request_irq-works-on-ifcvf_hw.patch
+vdpa-ifcvf-manage-ifcvf_hw-in-the-mgmt_dev.patch
+vdpa-ifcvf-allocate-the-adapter-in-dev_add.patch
+drm-display-dp_mst-add-drm_atomic_get_old_mst_topology_state.patch
diff --git a/queue-6.2/vdpa-ifcvf-alloc-the-mgmt_dev-before-the-adapter.patch b/queue-6.2/vdpa-ifcvf-alloc-the-mgmt_dev-before-the-adapter.patch
new file mode 100644 (file)
index 0000000..016c672
--- /dev/null
@@ -0,0 +1,92 @@
+From 66e3970b16d1e960afbece65739a3628273633f1 Mon Sep 17 00:00:00 2001
+From: Zhu Lingshan <lingshan.zhu@intel.com>
+Date: Fri, 25 Nov 2022 22:57:15 +0800
+Subject: vDPA/ifcvf: alloc the mgmt_dev before the adapter
+
+From: Zhu Lingshan <lingshan.zhu@intel.com>
+
+commit 66e3970b16d1e960afbece65739a3628273633f1 upstream.
+
+This commit reverses the order of allocating the
+management device and the adapter. So that it would
+be possible to move the allocation of the adapter
+to dev_add().
+
+Signed-off-by: Zhu Lingshan <lingshan.zhu@intel.com>
+Cc: stable@vger.kernel.org
+Message-Id: <20221125145724.1129962-4-lingshan.zhu@intel.com>
+Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/vdpa/ifcvf/ifcvf_main.c |   31 ++++++++++++++-----------------
+ 1 file changed, 14 insertions(+), 17 deletions(-)
+
+--- a/drivers/vdpa/ifcvf/ifcvf_main.c
++++ b/drivers/vdpa/ifcvf/ifcvf_main.c
+@@ -831,22 +831,30 @@ static int ifcvf_probe(struct pci_dev *p
+       }
+       pci_set_master(pdev);
++      ifcvf_mgmt_dev = kzalloc(sizeof(struct ifcvf_vdpa_mgmt_dev), GFP_KERNEL);
++      if (!ifcvf_mgmt_dev) {
++              IFCVF_ERR(pdev, "Failed to alloc memory for the vDPA management device\n");
++              return -ENOMEM;
++      }
+       adapter = vdpa_alloc_device(struct ifcvf_adapter, vdpa,
+                                   dev, &ifc_vdpa_ops, 1, 1, NULL, false);
+       if (IS_ERR(adapter)) {
+               IFCVF_ERR(pdev, "Failed to allocate vDPA structure");
+-              return PTR_ERR(adapter);
++              ret = PTR_ERR(adapter);
++              goto err;
+       }
++      adapter->pdev = pdev;
++      adapter->vdpa.dma_dev = &pdev->dev;
++      adapter->vdpa.mdev = &ifcvf_mgmt_dev->mdev;
++      ifcvf_mgmt_dev->adapter = adapter;
++
+       vf = &adapter->vf;
+       vf->dev_type = get_dev_type(pdev);
+       vf->base = pcim_iomap_table(pdev);
+       vf->pdev = pdev;
+-      adapter->pdev = pdev;
+-      adapter->vdpa.dma_dev = &pdev->dev;
+-
+       ret = ifcvf_init_hw(vf, pdev);
+       if (ret) {
+               IFCVF_ERR(pdev, "Failed to init IFCVF hw\n");
+@@ -859,16 +867,6 @@ static int ifcvf_probe(struct pci_dev *p
+       vf->hw_features = ifcvf_get_hw_features(vf);
+       vf->config_size = ifcvf_get_config_size(vf);
+-      ifcvf_mgmt_dev = kzalloc(sizeof(struct ifcvf_vdpa_mgmt_dev), GFP_KERNEL);
+-      if (!ifcvf_mgmt_dev) {
+-              IFCVF_ERR(pdev, "Failed to alloc memory for the vDPA management device\n");
+-              return -ENOMEM;
+-      }
+-
+-      ifcvf_mgmt_dev->mdev.ops = &ifcvf_vdpa_mgmt_dev_ops;
+-      ifcvf_mgmt_dev->mdev.device = dev;
+-      ifcvf_mgmt_dev->adapter = adapter;
+-
+       dev_type = get_dev_type(pdev);
+       switch (dev_type) {
+       case VIRTIO_ID_NET:
+@@ -883,12 +881,11 @@ static int ifcvf_probe(struct pci_dev *p
+               goto err;
+       }
++      ifcvf_mgmt_dev->mdev.ops = &ifcvf_vdpa_mgmt_dev_ops;
++      ifcvf_mgmt_dev->mdev.device = dev;
+       ifcvf_mgmt_dev->mdev.max_supported_vqs = vf->nr_vring;
+       ifcvf_mgmt_dev->mdev.supported_features = vf->hw_features;
+-      adapter->vdpa.mdev = &ifcvf_mgmt_dev->mdev;
+-
+-
+       ret = vdpa_mgmtdev_register(&ifcvf_mgmt_dev->mdev);
+       if (ret) {
+               IFCVF_ERR(pdev,
diff --git a/queue-6.2/vdpa-ifcvf-allocate-the-adapter-in-dev_add.patch b/queue-6.2/vdpa-ifcvf-allocate-the-adapter-in-dev_add.patch
new file mode 100644 (file)
index 0000000..b382058
--- /dev/null
@@ -0,0 +1,93 @@
+From 93139037b582134deb1ed894bbc4bc1d34ff35e7 Mon Sep 17 00:00:00 2001
+From: Zhu Lingshan <lingshan.zhu@intel.com>
+Date: Fri, 25 Nov 2022 22:57:22 +0800
+Subject: vDPA/ifcvf: allocate the adapter in dev_add()
+
+From: Zhu Lingshan <lingshan.zhu@intel.com>
+
+commit 93139037b582134deb1ed894bbc4bc1d34ff35e7 upstream.
+
+The adapter is the container of the vdpa_device,
+this commits allocate the adapter in dev_add()
+rather than in probe(). So that the vdpa_device()
+could be re-created when the userspace creates
+the vdpa device, and free-ed in dev_del()
+
+Signed-off-by: Zhu Lingshan <lingshan.zhu@intel.com>
+Cc: stable@vger.kernel.org
+Message-Id: <20221125145724.1129962-11-lingshan.zhu@intel.com>
+Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/vdpa/ifcvf/ifcvf_main.c |   34 +++++++++++++---------------------
+ 1 file changed, 13 insertions(+), 21 deletions(-)
+
+--- a/drivers/vdpa/ifcvf/ifcvf_main.c
++++ b/drivers/vdpa/ifcvf/ifcvf_main.c
+@@ -746,12 +746,20 @@ static int ifcvf_vdpa_dev_add(struct vdp
+       int ret;
+       ifcvf_mgmt_dev = container_of(mdev, struct ifcvf_vdpa_mgmt_dev, mdev);
+-      if (!ifcvf_mgmt_dev->adapter)
+-              return -EOPNOTSUPP;
++      vf = &ifcvf_mgmt_dev->vf;
++      pdev = vf->pdev;
++      adapter = vdpa_alloc_device(struct ifcvf_adapter, vdpa,
++                                  &pdev->dev, &ifc_vdpa_ops, 1, 1, NULL, false);
++      if (IS_ERR(adapter)) {
++              IFCVF_ERR(pdev, "Failed to allocate vDPA structure");
++              return PTR_ERR(adapter);
++      }
+-      adapter = ifcvf_mgmt_dev->adapter;
+-      vf = adapter->vf;
+-      pdev = adapter->pdev;
++      ifcvf_mgmt_dev->adapter = adapter;
++      adapter->pdev = pdev;
++      adapter->vdpa.dma_dev = &pdev->dev;
++      adapter->vdpa.mdev = mdev;
++      adapter->vf = vf;
+       vdpa_dev = &adapter->vdpa;
+       if (name)
+@@ -769,7 +777,6 @@ static int ifcvf_vdpa_dev_add(struct vdp
+       return 0;
+ }
+-
+ static void ifcvf_vdpa_dev_del(struct vdpa_mgmt_dev *mdev, struct vdpa_device *dev)
+ {
+       struct ifcvf_vdpa_mgmt_dev *ifcvf_mgmt_dev;
+@@ -788,7 +795,6 @@ static int ifcvf_probe(struct pci_dev *p
+ {
+       struct ifcvf_vdpa_mgmt_dev *ifcvf_mgmt_dev;
+       struct device *dev = &pdev->dev;
+-      struct ifcvf_adapter *adapter;
+       struct ifcvf_hw *vf;
+       u32 dev_type;
+       int ret, i;
+@@ -825,24 +831,10 @@ static int ifcvf_probe(struct pci_dev *p
+               return -ENOMEM;
+       }
+-      adapter = vdpa_alloc_device(struct ifcvf_adapter, vdpa,
+-                                  dev, &ifc_vdpa_ops, 1, 1, NULL, false);
+-      if (IS_ERR(adapter)) {
+-              IFCVF_ERR(pdev, "Failed to allocate vDPA structure");
+-              ret = PTR_ERR(adapter);
+-              goto err;
+-      }
+-
+-      adapter->pdev = pdev;
+-      adapter->vdpa.dma_dev = &pdev->dev;
+-      adapter->vdpa.mdev = &ifcvf_mgmt_dev->mdev;
+-      ifcvf_mgmt_dev->adapter = adapter;
+-
+       vf = &ifcvf_mgmt_dev->vf;
+       vf->dev_type = get_dev_type(pdev);
+       vf->base = pcim_iomap_table(pdev);
+       vf->pdev = pdev;
+-      adapter->vf = vf;
+       ret = ifcvf_init_hw(vf, pdev);
+       if (ret) {
diff --git a/queue-6.2/vdpa-ifcvf-decouple-config-dev-irq-requester-and-vectors-allocator-from-the-adapter.patch b/queue-6.2/vdpa-ifcvf-decouple-config-dev-irq-requester-and-vectors-allocator-from-the-adapter.patch
new file mode 100644 (file)
index 0000000..4767029
--- /dev/null
@@ -0,0 +1,91 @@
+From a70d833e696e538a0feff5e539086c74a90ddf90 Mon Sep 17 00:00:00 2001
+From: Zhu Lingshan <lingshan.zhu@intel.com>
+Date: Fri, 25 Nov 2022 22:57:19 +0800
+Subject: vDPA/ifcvf: decouple config/dev IRQ requester and vectors allocator from the adapter
+
+From: Zhu Lingshan <lingshan.zhu@intel.com>
+
+commit a70d833e696e538a0feff5e539086c74a90ddf90 upstream.
+
+This commit decouples the config irq requester, the device
+shared irq requester and the MSI vectors allocator from
+the adapter. So they can be safely invoked since probe
+before the adapter is allocated.
+
+Signed-off-by: Zhu Lingshan <lingshan.zhu@intel.com>
+Cc: stable@vger.kernel.org
+Message-Id: <20221125145724.1129962-8-lingshan.zhu@intel.com>
+Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/vdpa/ifcvf/ifcvf_main.c |   21 +++++++++------------
+ 1 file changed, 9 insertions(+), 12 deletions(-)
+
+--- a/drivers/vdpa/ifcvf/ifcvf_main.c
++++ b/drivers/vdpa/ifcvf/ifcvf_main.c
+@@ -132,10 +132,9 @@ static void ifcvf_free_irq(struct ifcvf_
+  * It returns the number of allocated vectors, negative
+  * return value when fails.
+  */
+-static int ifcvf_alloc_vectors(struct ifcvf_adapter *adapter)
++static int ifcvf_alloc_vectors(struct ifcvf_hw *vf)
+ {
+-      struct pci_dev *pdev = adapter->pdev;
+-      struct ifcvf_hw *vf = &adapter->vf;
++      struct pci_dev *pdev = vf->pdev;
+       int max_intr, ret;
+       /* all queues and config interrupt  */
+@@ -222,10 +221,9 @@ err:
+       return -EFAULT;
+ }
+-static int ifcvf_request_dev_irq(struct ifcvf_adapter *adapter)
++static int ifcvf_request_dev_irq(struct ifcvf_hw *vf)
+ {
+-      struct pci_dev *pdev = adapter->pdev;
+-      struct ifcvf_hw *vf = &adapter->vf;
++      struct pci_dev *pdev = vf->pdev;
+       int i, vector, ret, irq;
+       vector = 0;
+@@ -276,10 +274,9 @@ static int ifcvf_request_vq_irq(struct i
+       return ret;
+ }
+-static int ifcvf_request_config_irq(struct ifcvf_adapter *adapter)
++static int ifcvf_request_config_irq(struct ifcvf_hw *vf)
+ {
+-      struct pci_dev *pdev = adapter->pdev;
+-      struct ifcvf_hw *vf = &adapter->vf;
++      struct pci_dev *pdev = vf->pdev;
+       int config_vector, ret;
+       if (vf->msix_vector_status == MSIX_VECTOR_PER_VQ_AND_CONFIG)
+@@ -322,7 +319,7 @@ static int ifcvf_request_irq(struct ifcv
+       struct ifcvf_hw *vf = &adapter->vf;
+       int nvectors, ret, max_intr;
+-      nvectors = ifcvf_alloc_vectors(adapter);
++      nvectors = ifcvf_alloc_vectors(vf);
+       if (nvectors <= 0)
+               return -EFAULT;
+@@ -333,7 +330,7 @@ static int ifcvf_request_irq(struct ifcv
+       if (nvectors == 1) {
+               vf->msix_vector_status = MSIX_VECTOR_DEV_SHARED;
+-              ret = ifcvf_request_dev_irq(adapter);
++              ret = ifcvf_request_dev_irq(vf);
+               return ret;
+       }
+@@ -342,7 +339,7 @@ static int ifcvf_request_irq(struct ifcv
+       if (ret)
+               return ret;
+-      ret = ifcvf_request_config_irq(adapter);
++      ret = ifcvf_request_config_irq(vf);
+       if (ret)
+               return ret;
diff --git a/queue-6.2/vdpa-ifcvf-decouple-config-irq-releaser-from-the-adapter.patch b/queue-6.2/vdpa-ifcvf-decouple-config-irq-releaser-from-the-adapter.patch
new file mode 100644 (file)
index 0000000..70c1ddd
--- /dev/null
@@ -0,0 +1,99 @@
+From 23dac55cec3afdbc1b4eaed1c79f2cee00477f8b Mon Sep 17 00:00:00 2001
+From: Zhu Lingshan <lingshan.zhu@intel.com>
+Date: Fri, 25 Nov 2022 22:57:17 +0800
+Subject: vDPA/ifcvf: decouple config IRQ releaser from the adapter
+
+From: Zhu Lingshan <lingshan.zhu@intel.com>
+
+commit 23dac55cec3afdbc1b4eaed1c79f2cee00477f8b upstream.
+
+This commit decouples config IRQ releaser from the adapter,
+so that it could be invoked once probe or in err handlers.
+ifcvf_free_irq() works on ifcvf_hw in this commit
+
+Signed-off-by: Zhu Lingshan <lingshan.zhu@intel.com>
+Cc: stable@vger.kernel.org
+Message-Id: <20221125145724.1129962-6-lingshan.zhu@intel.com>
+Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/vdpa/ifcvf/ifcvf_main.c |   22 ++++++++++------------
+ 1 file changed, 10 insertions(+), 12 deletions(-)
+
+--- a/drivers/vdpa/ifcvf/ifcvf_main.c
++++ b/drivers/vdpa/ifcvf/ifcvf_main.c
+@@ -101,10 +101,9 @@ static void ifcvf_free_vq_irq(struct ifc
+               ifcvf_free_vqs_reused_irq(vf);
+ }
+-static void ifcvf_free_config_irq(struct ifcvf_adapter *adapter)
++static void ifcvf_free_config_irq(struct ifcvf_hw *vf)
+ {
+-      struct pci_dev *pdev = adapter->pdev;
+-      struct ifcvf_hw *vf = &adapter->vf;
++      struct pci_dev *pdev = vf->pdev;
+       if (vf->config_irq == -EINVAL)
+               return;
+@@ -119,13 +118,12 @@ static void ifcvf_free_config_irq(struct
+       }
+ }
+-static void ifcvf_free_irq(struct ifcvf_adapter *adapter)
++static void ifcvf_free_irq(struct ifcvf_hw *vf)
+ {
+-      struct pci_dev *pdev = adapter->pdev;
+-      struct ifcvf_hw *vf = &adapter->vf;
++      struct pci_dev *pdev = vf->pdev;
+       ifcvf_free_vq_irq(vf);
+-      ifcvf_free_config_irq(adapter);
++      ifcvf_free_config_irq(vf);
+       ifcvf_free_irq_vectors(pdev);
+ }
+@@ -187,7 +185,7 @@ static int ifcvf_request_per_vq_irq(stru
+       return 0;
+ err:
+-      ifcvf_free_irq(adapter);
++      ifcvf_free_irq(vf);
+       return -EFAULT;
+ }
+@@ -221,7 +219,7 @@ static int ifcvf_request_vqs_reused_irq(
+       return 0;
+ err:
+-      ifcvf_free_irq(adapter);
++      ifcvf_free_irq(vf);
+       return -EFAULT;
+ }
+@@ -262,7 +260,7 @@ static int ifcvf_request_dev_irq(struct
+       return 0;
+ err:
+-      ifcvf_free_irq(adapter);
++      ifcvf_free_irq(vf);
+       return -EFAULT;
+@@ -317,7 +315,7 @@ static int ifcvf_request_config_irq(stru
+       return 0;
+ err:
+-      ifcvf_free_irq(adapter);
++      ifcvf_free_irq(vf);
+       return -EFAULT;
+ }
+@@ -508,7 +506,7 @@ static int ifcvf_vdpa_reset(struct vdpa_
+       if (status_old & VIRTIO_CONFIG_S_DRIVER_OK) {
+               ifcvf_stop_datapath(adapter);
+-              ifcvf_free_irq(adapter);
++              ifcvf_free_irq(vf);
+       }
+       ifcvf_reset_vring(adapter);
diff --git a/queue-6.2/vdpa-ifcvf-decouple-config-space-ops-from-the-adapter.patch b/queue-6.2/vdpa-ifcvf-decouple-config-space-ops-from-the-adapter.patch
new file mode 100644 (file)
index 0000000..19ac5c0
--- /dev/null
@@ -0,0 +1,97 @@
+From af8eb69a62b73a2ce5f91575453534ac07f06eb4 Mon Sep 17 00:00:00 2001
+From: Zhu Lingshan <lingshan.zhu@intel.com>
+Date: Fri, 25 Nov 2022 22:57:14 +0800
+Subject: vDPA/ifcvf: decouple config space ops from the adapter
+
+From: Zhu Lingshan <lingshan.zhu@intel.com>
+
+commit af8eb69a62b73a2ce5f91575453534ac07f06eb4 upstream.
+
+This commit decopules the config space ops from the
+adapter layer, so these functions can be invoked
+once the device is probed.
+
+Signed-off-by: Zhu Lingshan <lingshan.zhu@intel.com>
+Cc: stable@vger.kernel.org
+Message-Id: <20221125145724.1129962-3-lingshan.zhu@intel.com>
+Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/vdpa/ifcvf/ifcvf_base.c |   21 +++++----------------
+ 1 file changed, 5 insertions(+), 16 deletions(-)
+
+--- a/drivers/vdpa/ifcvf/ifcvf_base.c
++++ b/drivers/vdpa/ifcvf/ifcvf_base.c
+@@ -10,11 +10,6 @@
+ #include "ifcvf_base.h"
+-struct ifcvf_adapter *vf_to_adapter(struct ifcvf_hw *hw)
+-{
+-      return container_of(hw, struct ifcvf_adapter, vf);
+-}
+-
+ u16 ifcvf_set_vq_vector(struct ifcvf_hw *hw, u16 qid, int vector)
+ {
+       struct virtio_pci_common_cfg __iomem *cfg = hw->common_cfg;
+@@ -37,8 +32,6 @@ u16 ifcvf_set_config_vector(struct ifcvf
+ static void __iomem *get_cap_addr(struct ifcvf_hw *hw,
+                                 struct virtio_pci_cap *cap)
+ {
+-      struct ifcvf_adapter *ifcvf;
+-      struct pci_dev *pdev;
+       u32 length, offset;
+       u8 bar;
+@@ -46,17 +39,14 @@ static void __iomem *get_cap_addr(struct
+       offset = le32_to_cpu(cap->offset);
+       bar = cap->bar;
+-      ifcvf= vf_to_adapter(hw);
+-      pdev = ifcvf->pdev;
+-
+       if (bar >= IFCVF_PCI_MAX_RESOURCE) {
+-              IFCVF_DBG(pdev,
++              IFCVF_DBG(hw->pdev,
+                         "Invalid bar number %u to get capabilities\n", bar);
+               return NULL;
+       }
+-      if (offset + length > pci_resource_len(pdev, bar)) {
+-              IFCVF_DBG(pdev,
++      if (offset + length > pci_resource_len(hw->pdev, bar)) {
++              IFCVF_DBG(hw->pdev,
+                         "offset(%u) + len(%u) overflows bar%u's capability\n",
+                         offset, length, bar);
+               return NULL;
+@@ -92,6 +82,7 @@ int ifcvf_init_hw(struct ifcvf_hw *hw, s
+               IFCVF_ERR(pdev, "Failed to read PCI capability list\n");
+               return -EIO;
+       }
++      hw->pdev = pdev;
+       while (pos) {
+               ret = ifcvf_read_config_range(pdev, (u32 *)&cap,
+@@ -230,13 +221,11 @@ int ifcvf_verify_min_features(struct ifc
+ u32 ifcvf_get_config_size(struct ifcvf_hw *hw)
+ {
+-      struct ifcvf_adapter *adapter;
+       u32 net_config_size = sizeof(struct virtio_net_config);
+       u32 blk_config_size = sizeof(struct virtio_blk_config);
+       u32 cap_size = hw->cap_dev_config_size;
+       u32 config_size;
+-      adapter = vf_to_adapter(hw);
+       /* If the onboard device config space size is greater than
+        * the size of struct virtio_net/blk_config, only the spec
+        * implementing contents size is returned, this is very
+@@ -251,7 +240,7 @@ u32 ifcvf_get_config_size(struct ifcvf_h
+               break;
+       default:
+               config_size = 0;
+-              IFCVF_ERR(adapter->pdev, "VIRTIO ID %u not supported\n", hw->dev_type);
++              IFCVF_ERR(hw->pdev, "VIRTIO ID %u not supported\n", hw->dev_type);
+       }
+       return config_size;
diff --git a/queue-6.2/vdpa-ifcvf-decouple-hw-features-manipulators-from-the-adapter.patch b/queue-6.2/vdpa-ifcvf-decouple-hw-features-manipulators-from-the-adapter.patch
new file mode 100644 (file)
index 0000000..b1ab3cd
--- /dev/null
@@ -0,0 +1,76 @@
+From d59f633dd05940739b5f46f5d4403cafb91d2742 Mon Sep 17 00:00:00 2001
+From: Zhu Lingshan <lingshan.zhu@intel.com>
+Date: Fri, 25 Nov 2022 22:57:13 +0800
+Subject: vDPA/ifcvf: decouple hw features manipulators from the adapter
+
+From: Zhu Lingshan <lingshan.zhu@intel.com>
+
+commit d59f633dd05940739b5f46f5d4403cafb91d2742 upstream.
+
+This commit gets rid of ifcvf_adapter in hw features related
+functions in ifcvf_base. Then these functions are more rubust
+and de-coupling from the ifcvf_adapter layer. So these
+functions could be invoded once the device is probed, even
+before the adapter is allocaed.
+
+Signed-off-by: Zhu Lingshan <lingshan.zhu@intel.com>
+Cc: stable@vger.kernel.org
+Message-Id: <20221125145724.1129962-2-lingshan.zhu@intel.com>
+Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/vdpa/ifcvf/ifcvf_base.c |    9 ++-------
+ drivers/vdpa/ifcvf/ifcvf_base.h |    1 +
+ drivers/vdpa/ifcvf/ifcvf_main.c |    1 +
+ 3 files changed, 4 insertions(+), 7 deletions(-)
+
+--- a/drivers/vdpa/ifcvf/ifcvf_base.c
++++ b/drivers/vdpa/ifcvf/ifcvf_base.c
+@@ -220,10 +220,8 @@ u64 ifcvf_get_features(struct ifcvf_hw *
+ int ifcvf_verify_min_features(struct ifcvf_hw *hw, u64 features)
+ {
+-      struct ifcvf_adapter *ifcvf = vf_to_adapter(hw);
+-
+       if (!(features & BIT_ULL(VIRTIO_F_ACCESS_PLATFORM)) && features) {
+-              IFCVF_ERR(ifcvf->pdev, "VIRTIO_F_ACCESS_PLATFORM is not negotiated\n");
++              IFCVF_ERR(hw->pdev, "VIRTIO_F_ACCESS_PLATFORM is not negotiated\n");
+               return -EINVAL;
+       }
+@@ -301,14 +299,11 @@ static void ifcvf_set_features(struct if
+ static int ifcvf_config_features(struct ifcvf_hw *hw)
+ {
+-      struct ifcvf_adapter *ifcvf;
+-
+-      ifcvf = vf_to_adapter(hw);
+       ifcvf_set_features(hw, hw->req_features);
+       ifcvf_add_status(hw, VIRTIO_CONFIG_S_FEATURES_OK);
+       if (!(ifcvf_get_status(hw) & VIRTIO_CONFIG_S_FEATURES_OK)) {
+-              IFCVF_ERR(ifcvf->pdev, "Failed to set FEATURES_OK status\n");
++              IFCVF_ERR(hw->pdev, "Failed to set FEATURES_OK status\n");
+               return -EIO;
+       }
+--- a/drivers/vdpa/ifcvf/ifcvf_base.h
++++ b/drivers/vdpa/ifcvf/ifcvf_base.h
+@@ -89,6 +89,7 @@ struct ifcvf_hw {
+       u16 nr_vring;
+       /* VIRTIO_PCI_CAP_DEVICE_CFG size */
+       u32 cap_dev_config_size;
++      struct pci_dev *pdev;
+ };
+ struct ifcvf_adapter {
+--- a/drivers/vdpa/ifcvf/ifcvf_main.c
++++ b/drivers/vdpa/ifcvf/ifcvf_main.c
+@@ -842,6 +842,7 @@ static int ifcvf_probe(struct pci_dev *p
+       vf = &adapter->vf;
+       vf->dev_type = get_dev_type(pdev);
+       vf->base = pcim_iomap_table(pdev);
++      vf->pdev = pdev;
+       adapter->pdev = pdev;
+       adapter->vdpa.dma_dev = &pdev->dev;
diff --git a/queue-6.2/vdpa-ifcvf-decouple-vq-irq-releasers-from-the-adapter.patch b/queue-6.2/vdpa-ifcvf-decouple-vq-irq-releasers-from-the-adapter.patch
new file mode 100644 (file)
index 0000000..e9d81c7
--- /dev/null
@@ -0,0 +1,79 @@
+From 004cbcabab46d9346e2524c4eedd71ea57fe4f3c Mon Sep 17 00:00:00 2001
+From: Zhu Lingshan <lingshan.zhu@intel.com>
+Date: Fri, 25 Nov 2022 22:57:16 +0800
+Subject: vDPA/ifcvf: decouple vq IRQ releasers from the adapter
+
+From: Zhu Lingshan <lingshan.zhu@intel.com>
+
+commit 004cbcabab46d9346e2524c4eedd71ea57fe4f3c upstream.
+
+This commit decouples the IRQ releasers from the
+adapter, so that these functions could be
+safely invoked once probe
+
+Signed-off-by: Zhu Lingshan <lingshan.zhu@intel.com>
+Cc: stable@vger.kernel.org
+Message-Id: <20221125145724.1129962-5-lingshan.zhu@intel.com>
+Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/vdpa/ifcvf/ifcvf_main.c |   21 +++++++++------------
+ 1 file changed, 9 insertions(+), 12 deletions(-)
+
+--- a/drivers/vdpa/ifcvf/ifcvf_main.c
++++ b/drivers/vdpa/ifcvf/ifcvf_main.c
+@@ -69,10 +69,9 @@ static void ifcvf_free_irq_vectors(void
+       pci_free_irq_vectors(data);
+ }
+-static void ifcvf_free_per_vq_irq(struct ifcvf_adapter *adapter)
++static void ifcvf_free_per_vq_irq(struct ifcvf_hw *vf)
+ {
+-      struct pci_dev *pdev = adapter->pdev;
+-      struct ifcvf_hw *vf = &adapter->vf;
++      struct pci_dev *pdev = vf->pdev;
+       int i;
+       for (i = 0; i < vf->nr_vring; i++) {
+@@ -83,10 +82,9 @@ static void ifcvf_free_per_vq_irq(struct
+       }
+ }
+-static void ifcvf_free_vqs_reused_irq(struct ifcvf_adapter *adapter)
++static void ifcvf_free_vqs_reused_irq(struct ifcvf_hw *vf)
+ {
+-      struct pci_dev *pdev = adapter->pdev;
+-      struct ifcvf_hw *vf = &adapter->vf;
++      struct pci_dev *pdev = vf->pdev;
+       if (vf->vqs_reused_irq != -EINVAL) {
+               devm_free_irq(&pdev->dev, vf->vqs_reused_irq, vf);
+@@ -95,14 +93,12 @@ static void ifcvf_free_vqs_reused_irq(st
+ }
+-static void ifcvf_free_vq_irq(struct ifcvf_adapter *adapter)
++static void ifcvf_free_vq_irq(struct ifcvf_hw *vf)
+ {
+-      struct ifcvf_hw *vf = &adapter->vf;
+-
+       if (vf->msix_vector_status == MSIX_VECTOR_PER_VQ_AND_CONFIG)
+-              ifcvf_free_per_vq_irq(adapter);
++              ifcvf_free_per_vq_irq(vf);
+       else
+-              ifcvf_free_vqs_reused_irq(adapter);
++              ifcvf_free_vqs_reused_irq(vf);
+ }
+ static void ifcvf_free_config_irq(struct ifcvf_adapter *adapter)
+@@ -126,8 +122,9 @@ static void ifcvf_free_config_irq(struct
+ static void ifcvf_free_irq(struct ifcvf_adapter *adapter)
+ {
+       struct pci_dev *pdev = adapter->pdev;
++      struct ifcvf_hw *vf = &adapter->vf;
+-      ifcvf_free_vq_irq(adapter);
++      ifcvf_free_vq_irq(vf);
+       ifcvf_free_config_irq(adapter);
+       ifcvf_free_irq_vectors(pdev);
+ }
diff --git a/queue-6.2/vdpa-ifcvf-decouple-vq-irq-requester-from-the-adapter.patch b/queue-6.2/vdpa-ifcvf-decouple-vq-irq-requester-from-the-adapter.patch
new file mode 100644 (file)
index 0000000..fbd6a3a
--- /dev/null
@@ -0,0 +1,77 @@
+From f9a9ffb2e4dbde81090416fc51662441c2a7b73b Mon Sep 17 00:00:00 2001
+From: Zhu Lingshan <lingshan.zhu@intel.com>
+Date: Fri, 25 Nov 2022 22:57:18 +0800
+Subject: vDPA/ifcvf: decouple vq irq requester from the adapter
+
+From: Zhu Lingshan <lingshan.zhu@intel.com>
+
+commit f9a9ffb2e4dbde81090416fc51662441c2a7b73b upstream.
+
+This commit decouples the vq irq requester from the adapter,
+so that these functions can be invoked since probe.
+
+Signed-off-by: Zhu Lingshan <lingshan.zhu@intel.com>
+Cc: stable@vger.kernel.org
+Message-Id: <20221125145724.1129962-7-lingshan.zhu@intel.com>
+Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/vdpa/ifcvf/ifcvf_main.c |   19 ++++++++-----------
+ 1 file changed, 8 insertions(+), 11 deletions(-)
+
+--- a/drivers/vdpa/ifcvf/ifcvf_main.c
++++ b/drivers/vdpa/ifcvf/ifcvf_main.c
+@@ -155,10 +155,9 @@ static int ifcvf_alloc_vectors(struct if
+       return ret;
+ }
+-static int ifcvf_request_per_vq_irq(struct ifcvf_adapter *adapter)
++static int ifcvf_request_per_vq_irq(struct ifcvf_hw *vf)
+ {
+-      struct pci_dev *pdev = adapter->pdev;
+-      struct ifcvf_hw *vf = &adapter->vf;
++      struct pci_dev *pdev = vf->pdev;
+       int i, vector, ret, irq;
+       vf->vqs_reused_irq = -EINVAL;
+@@ -190,10 +189,9 @@ err:
+       return -EFAULT;
+ }
+-static int ifcvf_request_vqs_reused_irq(struct ifcvf_adapter *adapter)
++static int ifcvf_request_vqs_reused_irq(struct ifcvf_hw *vf)
+ {
+-      struct pci_dev *pdev = adapter->pdev;
+-      struct ifcvf_hw *vf = &adapter->vf;
++      struct pci_dev *pdev = vf->pdev;
+       int i, vector, ret, irq;
+       vector = 0;
+@@ -266,15 +264,14 @@ err:
+ }
+-static int ifcvf_request_vq_irq(struct ifcvf_adapter *adapter)
++static int ifcvf_request_vq_irq(struct ifcvf_hw *vf)
+ {
+-      struct ifcvf_hw *vf = &adapter->vf;
+       int ret;
+       if (vf->msix_vector_status == MSIX_VECTOR_PER_VQ_AND_CONFIG)
+-              ret = ifcvf_request_per_vq_irq(adapter);
++              ret = ifcvf_request_per_vq_irq(vf);
+       else
+-              ret = ifcvf_request_vqs_reused_irq(adapter);
++              ret = ifcvf_request_vqs_reused_irq(vf);
+       return ret;
+ }
+@@ -341,7 +338,7 @@ static int ifcvf_request_irq(struct ifcv
+               return ret;
+       }
+-      ret = ifcvf_request_vq_irq(adapter);
++      ret = ifcvf_request_vq_irq(vf);
+       if (ret)
+               return ret;
diff --git a/queue-6.2/vdpa-ifcvf-ifcvf_request_irq-works-on-ifcvf_hw.patch b/queue-6.2/vdpa-ifcvf-ifcvf_request_irq-works-on-ifcvf_hw.patch
new file mode 100644 (file)
index 0000000..dfac8f3
--- /dev/null
@@ -0,0 +1,44 @@
+From 7cfd36b7e8be6bdaeb5af0f9729871b732a7a3c8 Mon Sep 17 00:00:00 2001
+From: Zhu Lingshan <lingshan.zhu@intel.com>
+Date: Fri, 25 Nov 2022 22:57:20 +0800
+Subject: vDPA/ifcvf: ifcvf_request_irq works on ifcvf_hw
+
+From: Zhu Lingshan <lingshan.zhu@intel.com>
+
+commit 7cfd36b7e8be6bdaeb5af0f9729871b732a7a3c8 upstream.
+
+All ifcvf_request_irq's callees are refactored
+to work on ifcvf_hw, so it should be decoupled
+from the adapter as well
+
+Signed-off-by: Zhu Lingshan <lingshan.zhu@intel.com>
+Cc: stable@vger.kernel.org
+Message-Id: <20221125145724.1129962-9-lingshan.zhu@intel.com>
+Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/vdpa/ifcvf/ifcvf_main.c |    5 ++---
+ 1 file changed, 2 insertions(+), 3 deletions(-)
+
+--- a/drivers/vdpa/ifcvf/ifcvf_main.c
++++ b/drivers/vdpa/ifcvf/ifcvf_main.c
+@@ -314,9 +314,8 @@ err:
+       return -EFAULT;
+ }
+-static int ifcvf_request_irq(struct ifcvf_adapter *adapter)
++static int ifcvf_request_irq(struct ifcvf_hw *vf)
+ {
+-      struct ifcvf_hw *vf = &adapter->vf;
+       int nvectors, ret, max_intr;
+       nvectors = ifcvf_alloc_vectors(vf);
+@@ -468,7 +467,7 @@ static void ifcvf_vdpa_set_status(struct
+       if ((status & VIRTIO_CONFIG_S_DRIVER_OK) &&
+           !(status_old & VIRTIO_CONFIG_S_DRIVER_OK)) {
+-              ret = ifcvf_request_irq(adapter);
++              ret = ifcvf_request_irq(vf);
+               if (ret) {
+                       status = ifcvf_get_status(vf);
+                       status |= VIRTIO_CONFIG_S_FAILED;
diff --git a/queue-6.2/vdpa-ifcvf-manage-ifcvf_hw-in-the-mgmt_dev.patch b/queue-6.2/vdpa-ifcvf-manage-ifcvf_hw-in-the-mgmt_dev.patch
new file mode 100644 (file)
index 0000000..826a166
--- /dev/null
@@ -0,0 +1,85 @@
+From 6a3b2f179b49f2c6452ecc37b4778a43848b454c Mon Sep 17 00:00:00 2001
+From: Zhu Lingshan <lingshan.zhu@intel.com>
+Date: Fri, 25 Nov 2022 22:57:21 +0800
+Subject: vDPA/ifcvf: manage ifcvf_hw in the mgmt_dev
+
+From: Zhu Lingshan <lingshan.zhu@intel.com>
+
+commit 6a3b2f179b49f2c6452ecc37b4778a43848b454c upstream.
+
+This commit allocates the hw structure in the
+management device structure. So the hardware
+can be initialized once the management device
+is allocated in probe.
+
+Signed-off-by: Zhu Lingshan <lingshan.zhu@intel.com>
+Cc: stable@vger.kernel.org
+Message-Id: <20221125145724.1129962-10-lingshan.zhu@intel.com>
+Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/vdpa/ifcvf/ifcvf_base.h |    5 +++--
+ drivers/vdpa/ifcvf/ifcvf_main.c |    7 ++++---
+ 2 files changed, 7 insertions(+), 5 deletions(-)
+
+--- a/drivers/vdpa/ifcvf/ifcvf_base.h
++++ b/drivers/vdpa/ifcvf/ifcvf_base.h
+@@ -39,7 +39,7 @@
+ #define IFCVF_INFO(pdev, fmt, ...)    dev_info(&pdev->dev, fmt, ##__VA_ARGS__)
+ #define ifcvf_private_to_vf(adapter) \
+-      (&((struct ifcvf_adapter *)adapter)->vf)
++      (((struct ifcvf_adapter *)adapter)->vf)
+ /* all vqs and config interrupt has its own vector */
+ #define MSIX_VECTOR_PER_VQ_AND_CONFIG         1
+@@ -95,7 +95,7 @@ struct ifcvf_hw {
+ struct ifcvf_adapter {
+       struct vdpa_device vdpa;
+       struct pci_dev *pdev;
+-      struct ifcvf_hw vf;
++      struct ifcvf_hw *vf;
+ };
+ struct ifcvf_vring_lm_cfg {
+@@ -110,6 +110,7 @@ struct ifcvf_lm_cfg {
+ struct ifcvf_vdpa_mgmt_dev {
+       struct vdpa_mgmt_dev mdev;
++      struct ifcvf_hw vf;
+       struct ifcvf_adapter *adapter;
+       struct pci_dev *pdev;
+ };
+--- a/drivers/vdpa/ifcvf/ifcvf_main.c
++++ b/drivers/vdpa/ifcvf/ifcvf_main.c
+@@ -402,7 +402,7 @@ static struct ifcvf_hw *vdpa_to_vf(struc
+ {
+       struct ifcvf_adapter *adapter = vdpa_to_adapter(vdpa_dev);
+-      return &adapter->vf;
++      return adapter->vf;
+ }
+ static u64 ifcvf_vdpa_get_device_features(struct vdpa_device *vdpa_dev)
+@@ -750,7 +750,7 @@ static int ifcvf_vdpa_dev_add(struct vdp
+               return -EOPNOTSUPP;
+       adapter = ifcvf_mgmt_dev->adapter;
+-      vf = &adapter->vf;
++      vf = adapter->vf;
+       pdev = adapter->pdev;
+       vdpa_dev = &adapter->vdpa;
+@@ -838,10 +838,11 @@ static int ifcvf_probe(struct pci_dev *p
+       adapter->vdpa.mdev = &ifcvf_mgmt_dev->mdev;
+       ifcvf_mgmt_dev->adapter = adapter;
+-      vf = &adapter->vf;
++      vf = &ifcvf_mgmt_dev->vf;
+       vf->dev_type = get_dev_type(pdev);
+       vf->base = pcim_iomap_table(pdev);
+       vf->pdev = pdev;
++      adapter->vf = vf;
+       ret = ifcvf_init_hw(vf, pdev);
+       if (ret) {
diff --git a/queue-6.2/x86-resctl-fix-scheduler-confusion-with-current.patch b/queue-6.2/x86-resctl-fix-scheduler-confusion-with-current.patch
new file mode 100644 (file)
index 0000000..d941aff
--- /dev/null
@@ -0,0 +1,178 @@
+From 7fef099702527c3b2c5234a2ea6a24411485a13a Mon Sep 17 00:00:00 2001
+From: Linus Torvalds <torvalds@linux-foundation.org>
+Date: Tue, 7 Mar 2023 13:06:29 -0800
+Subject: x86/resctl: fix scheduler confusion with 'current'
+
+From: Linus Torvalds <torvalds@linux-foundation.org>
+
+commit 7fef099702527c3b2c5234a2ea6a24411485a13a upstream.
+
+The implementation of 'current' on x86 is very intentionally special: it
+is a very common thing to look up, and it uses 'this_cpu_read_stable()'
+to get the current thread pointer efficiently from per-cpu storage.
+
+And the keyword in there is 'stable': the current thread pointer never
+changes as far as a single thread is concerned.  Even if when a thread
+is preempted, or moved to another CPU, or even across an explicit call
+'schedule()' that thread will still have the same value for 'current'.
+
+It is, after all, the kernel base pointer to thread-local storage.
+That's why it's stable to begin with, but it's also why it's important
+enough that we have that special 'this_cpu_read_stable()' access for it.
+
+So this is all done very intentionally to allow the compiler to treat
+'current' as a value that never visibly changes, so that the compiler
+can do CSE and combine multiple different 'current' accesses into one.
+
+However, there is obviously one very special situation when the
+currently running thread does actually change: inside the scheduler
+itself.
+
+So the scheduler code paths are special, and do not have a 'current'
+thread at all.  Instead there are _two_ threads: the previous and the
+next thread - typically called 'prev' and 'next' (or prev_p/next_p)
+internally.
+
+So this is all actually quite straightforward and simple, and not all
+that complicated.
+
+Except for when you then have special code that is run in scheduler
+context, that code then has to be aware that 'current' isn't really a
+valid thing.  Did you mean 'prev'? Did you mean 'next'?
+
+In fact, even if then look at the code, and you use 'current' after the
+new value has been assigned to the percpu variable, we have explicitly
+told the compiler that 'current' is magical and always stable.  So the
+compiler is quite free to use an older (or newer) value of 'current',
+and the actual assignment to the percpu storage is not relevant even if
+it might look that way.
+
+Which is exactly what happened in the resctl code, that blithely used
+'current' in '__resctrl_sched_in()' when it really wanted the new
+process state (as implied by the name: we're scheduling 'into' that new
+resctl state).  And clang would end up just using the old thread pointer
+value at least in some configurations.
+
+This could have happened with gcc too, and purely depends on random
+compiler details.  Clang just seems to have been more aggressive about
+moving the read of the per-cpu current_task pointer around.
+
+The fix is trivial: just make the resctl code adhere to the scheduler
+rules of using the prev/next thread pointer explicitly, instead of using
+'current' in a situation where it just wasn't valid.
+
+That same code is then also used outside of the scheduler context (when
+a thread resctl state is explicitly changed), and then we will just pass
+in 'current' as that pointer, of course.  There is no ambiguity in that
+case.
+
+The fix may be trivial, but noticing and figuring out what went wrong
+was not.  The credit for that goes to Stephane Eranian.
+
+Reported-by: Stephane Eranian <eranian@google.com>
+Link: https://lore.kernel.org/lkml/20230303231133.1486085-1-eranian@google.com/
+Link: https://lore.kernel.org/lkml/alpine.LFD.2.01.0908011214330.3304@localhost.localdomain/
+Reviewed-by: Nick Desaulniers <ndesaulniers@google.com>
+Tested-by: Tony Luck <tony.luck@intel.com>
+Tested-by: Stephane Eranian <eranian@google.com>
+Tested-by: Babu Moger <babu.moger@amd.com>
+Cc: stable@kernel.org
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ arch/x86/include/asm/resctrl.h         |   12 ++++++------
+ arch/x86/kernel/cpu/resctrl/rdtgroup.c |    4 ++--
+ arch/x86/kernel/process_32.c           |    2 +-
+ arch/x86/kernel/process_64.c           |    2 +-
+ 4 files changed, 10 insertions(+), 10 deletions(-)
+
+--- a/arch/x86/include/asm/resctrl.h
++++ b/arch/x86/include/asm/resctrl.h
+@@ -49,7 +49,7 @@ DECLARE_STATIC_KEY_FALSE(rdt_mon_enable_
+  *   simple as possible.
+  * Must be called with preemption disabled.
+  */
+-static void __resctrl_sched_in(void)
++static inline void __resctrl_sched_in(struct task_struct *tsk)
+ {
+       struct resctrl_pqr_state *state = this_cpu_ptr(&pqr_state);
+       u32 closid = state->default_closid;
+@@ -61,13 +61,13 @@ static void __resctrl_sched_in(void)
+        * Else use the closid/rmid assigned to this cpu.
+        */
+       if (static_branch_likely(&rdt_alloc_enable_key)) {
+-              tmp = READ_ONCE(current->closid);
++              tmp = READ_ONCE(tsk->closid);
+               if (tmp)
+                       closid = tmp;
+       }
+       if (static_branch_likely(&rdt_mon_enable_key)) {
+-              tmp = READ_ONCE(current->rmid);
++              tmp = READ_ONCE(tsk->rmid);
+               if (tmp)
+                       rmid = tmp;
+       }
+@@ -88,17 +88,17 @@ static inline unsigned int resctrl_arch_
+       return val * scale;
+ }
+-static inline void resctrl_sched_in(void)
++static inline void resctrl_sched_in(struct task_struct *tsk)
+ {
+       if (static_branch_likely(&rdt_enable_key))
+-              __resctrl_sched_in();
++              __resctrl_sched_in(tsk);
+ }
+ void resctrl_cpu_detect(struct cpuinfo_x86 *c);
+ #else
+-static inline void resctrl_sched_in(void) {}
++static inline void resctrl_sched_in(struct task_struct *tsk) {}
+ static inline void resctrl_cpu_detect(struct cpuinfo_x86 *c) {}
+ #endif /* CONFIG_X86_CPU_RESCTRL */
+--- a/arch/x86/kernel/cpu/resctrl/rdtgroup.c
++++ b/arch/x86/kernel/cpu/resctrl/rdtgroup.c
+@@ -314,7 +314,7 @@ static void update_cpu_closid_rmid(void
+        * executing task might have its own closid selected. Just reuse
+        * the context switch code.
+        */
+-      resctrl_sched_in();
++      resctrl_sched_in(current);
+ }
+ /*
+@@ -535,7 +535,7 @@ static void _update_task_closid_rmid(voi
+        * Otherwise, the MSR is updated when the task is scheduled in.
+        */
+       if (task == current)
+-              resctrl_sched_in();
++              resctrl_sched_in(task);
+ }
+ static void update_task_closid_rmid(struct task_struct *t)
+--- a/arch/x86/kernel/process_32.c
++++ b/arch/x86/kernel/process_32.c
+@@ -212,7 +212,7 @@ __switch_to(struct task_struct *prev_p,
+       switch_fpu_finish();
+       /* Load the Intel cache allocation PQR MSR. */
+-      resctrl_sched_in();
++      resctrl_sched_in(next_p);
+       return prev_p;
+ }
+--- a/arch/x86/kernel/process_64.c
++++ b/arch/x86/kernel/process_64.c
+@@ -656,7 +656,7 @@ __switch_to(struct task_struct *prev_p,
+       }
+       /* Load the Intel cache allocation PQR MSR. */
+-      resctrl_sched_in();
++      resctrl_sched_in(next_p);
+       return prev_p;
+ }