Netdev List
 help / color / mirror / Atom feed
* Re: [PATCH v6 3/3] dts: s32g: Add GPR syscon region
From: Jared Kangas @ 2026-04-22 16:25 UTC (permalink / raw)
  To: Dan Carpenter
  Cc: Chester Lin, Matthias Brugger, Ghennadi Procopciuc,
	NXP S32 Linux Team, Frank Li, Sascha Hauer,
	Pengutronix Kernel Team, Fabio Estevam, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, linux-arm-kernel, imx,
	devicetree, linux-kernel, linaro-s32, netdev
In-Reply-To: <aeZYQp9b5aoV7Ihv@stanley.mountain>

On Mon, Apr 20, 2026 at 07:45:54PM +0300, Dan Carpenter wrote:
> On Mon, Apr 20, 2026 at 09:04:00AM -0700, Jared Kangas wrote:
> > Fixing Dan's address based on mailmap update, sorry for the noise.
> > 
> > On Fri, Apr 17, 2026 at 02:36:25PM -0700, Jared Kangas wrote:
> > > Hi Dan,
> > > 
> > > [snip]
> > > 
> > > I gave this a test on an S32G-VNP-RDB3 and didn't see any issues on the
> > > dwmac-s32 side, but this appears to trigger a panic when reading the new
> > > debugfs regmap/*/registers file for the syscon node:
> > > 
> > > [snip]
> 
> Oh, ugh...  I didn't realize that this wasn't merged.  I don't have a
> way to test this any more.  The simplest fix would be to do change the
> 0x3000 to 0x100.  The GPR63 register is at 0xFC.
> 
> 		reg = <0x4007c000 0x100>;
> 
> That's probably the best fix as well.  The later register areas would
> be their own syscons.

Tried that out and it looks good to me. With the write routed through
syscon:

    # xxd -g4 /proc/device-tree/soc@0/syscon@4007c000/reg
    00000000: 4007c000 00000100                    @.......
    # cat /sys/kernel/debug/regmap/dummy-syscon@0x000000004007c000/registers
    00: 00000000
    04: 00000002
    08: 000000e7
    0c: 00000001
    10: ffffffff
    14: 1fffffff
    18: 00007fff
    1c: 00000000
    20: 00000000
    ...
    f4: 00000000
    f8: 00000000
    fc: 00000000

No more crashes and 04's value lines up with the S32_PHY_INTF_SEL_RGMII
(0x2) write, so if you're able to post a revision, feel free to add my
T-b:

Tested-by: Jared Kangas <jkangas@redhat.com>


^ permalink raw reply

* Re: [BUG] rxrpc: Client connection leak and BUG() call during kernel IO thread exit
From: Anderson Nascimento @ 2026-04-22 16:25 UTC (permalink / raw)
  To: David Howells
  Cc: netdev, Marc Dionne, Jakub Kicinski, David S. Miller,
	Eric Dumazet, Paolo Abeni, linux-kernel, Jeffrey Altman,
	Simon Horman
In-Reply-To: <2593154.1776874118@warthog.procyon.org.uk>

Hi David,

On Wed, Apr 22, 2026 at 1:08 PM David Howells <dhowells@redhat.com> wrote:
>
> Do you by any chance have a reproducer program for this?
>

Sorry, I mixed things up and ended up sending a different reproducer.
I do have it, I will test it now and send here in a few minutes.

> David
>


-- 
Anderson Nascimento
Allele Security Intelligence
https://www.allelesecurity.com

^ permalink raw reply

* Re: [PATCH v1 1/2] vfio: add callback to get tph info for dma-buf
From: Jason Gunthorpe @ 2026-04-22 16:29 UTC (permalink / raw)
  To: Alex Williamson
  Cc: Zhiping Zhang, Stanislav Fomichev, Keith Busch, Leon Romanovsky,
	Bjorn Helgaas, linux-rdma, linux-pci, netdev, dri-devel,
	Yochai Cohen, Yishai Hadas
In-Reply-To: <20260422092327.3f629ad6@shazbot.org>

On Wed, Apr 22, 2026 at 09:23:27AM -0600, Alex Williamson wrote:
> In general though, I'm really hoping that someone interested in
> enabling TPH as an interface through vfio actually decides to take
> resource targeting and revocation seriously.  There's no validation of
> the steering tag here relative to what the user has access to and no
> mechanism to revoke those tags if access changes.  In fact, there's not
> even a proposed mechanism allowing the user to derive valid steering
> tags.  Does the user implicitly know the value and the kernel just
> allows it because... yolo? 

This is the steering tag that remote devices will send *INTO* the VFIO
device.

IMHO it is entirely appropriate that the driver controlling the device
decide what tags are sent into it and when, so that's the VFIO
userspace.

There is no concept of access here since the entire device is captured
by VFIO.

If the VFIO device catastrophically malfunctions when receiving
certain steering tags then it is incompatible with VFIO and we should
at least block this new API..

The only requirement is that the device limit the TPH to only the
function that is perceiving them. If a device is really broken and
doesn't meet that then it should be blocked off and it is probably not
safe to be used with VMs at all.

Jason

^ permalink raw reply

* Re: [PATCH AUTOSEL 7.0-6.18] net: mana: hardening: Validate adapter_mtu from MANA_QUERY_DEV_CONFIG
From: Erni Sri Satya Vennela @ 2026-04-22 16:31 UTC (permalink / raw)
  To: Sasha Levin
  Cc: patches, stable, Jakub Kicinski, kys, haiyangz, wei.liu, decui,
	longli, andrew+netdev, davem, edumazet, pabeni, linux-hyperv,
	netdev, linux-kernel
In-Reply-To: <20260420132314.1023554-44-sashal@kernel.org>

On Mon, Apr 20, 2026 at 09:17:18AM -0400, Sasha Levin wrote:
> From: Erni Sri Satya Vennela <ernis@linux.microsoft.com>
> 
> [ Upstream commit d7709812e13d06132ddae3d21540472ea5cb11c5 ]
> 
> As a part of MANA hardening for CVM, validate the adapter_mtu value
> returned from the MANA_QUERY_DEV_CONFIG HWC command.
> 
> The adapter_mtu value is used to compute ndev->max_mtu via:
> gc->adapter_mtu - ETH_HLEN. If hardware returns a bogus adapter_mtu
> smaller than ETH_HLEN (e.g. 0), the unsigned subtraction wraps to a
> huge value, silently allowing oversized MTU settings.
> 
> Add a validation check to reject adapter_mtu values below
> ETH_MIN_MTU + ETH_HLEN, returning -EPROTO to fail the device
> configuration early with a clear error message.
> 
> Signed-off-by: Erni Sri Satya Vennela <ernis@linux.microsoft.com>
> Link: https://patch.msgid.link/20260326173101.2010514-1-ernis@linux.microsoft.com
> Signed-off-by: Jakub Kicinski <kuba@kernel.org>
> Signed-off-by: Sasha Levin <sashal@kernel.org>
> ---
> 
> FOR backporting:
> - Fixes a concrete integer underflow bug (adapter_mtu - ETH_HLEN wraps
>   to ~4GB)
> - Small, surgical fix (6 lines of logic)
> - Obviously correct bounds check
> - No regression risk
> - Accepted by netdev maintainer
> - Author is regular driver contributor
> - Affects widely-used Azure MANA driver
> - Security-relevant in CVM environments
> 
> 2.53.0

Thanks Sasha, this is good for stable.

This should also carry a Fixes tag:

Fixes: 80f6215b450e ("net: mana: Add support for jumbo frame")

The code was introduced in v6.4-rc1, so the backport applies
to 6.6.y and later stable trees.

Acked-by: Erni Sri Satya Vennela ernis@linux.microsoft.com

^ permalink raw reply

* Re: [PATCH net v3 3/8] xsk: fix use-after-free of xs->skb in xsk_build_skb() free_err path
From: Stanislav Fomichev @ 2026-04-22 16:31 UTC (permalink / raw)
  To: Jason Xing; +Cc: bpf, netdev, Jason Xing
In-Reply-To: <20260422033650.68457-4-kerneljasonxing@gmail.com>

> From: Jason Xing <kernelxing@tencent.com>
> 
> When xsk_build_skb() processes multi-buffer packets in copy mode, the
> first descriptor stores data into the skb linear area without adding
> any frags, so nr_frags stays at 0. The caller then sets xs->skb = skb
> to accumulate subsequent descriptors.
> 
> If a continuation descriptor fails (e.g. alloc_page returns NULL with
> -EAGAIN), we jump to free_err where the condition:
> 
>   if (skb && !skb_shinfo(skb)->nr_frags)
>       kfree_skb(skb);
> 
> evaluates to true because nr_frags is still 0 (the first descriptor
> used the linear area, not frags). This frees the skb while xs->skb
> still points to it, creating a dangling pointer. On the next transmit
> attempt or socket close, xs->skb is dereferenced, causing a
> use-after-free or double-free.
> 
> Fix by using a !xs->skb check to handle first frag situation, ensuring
> we only free skbs that were freshly allocated in this call
> (xs->skb is NULL) and never free an in-progress multi-buffer skb that
> the caller still references.
> 
> Closes: https://lore.kernel.org/all/20260415082654.21026-4-kerneljasonxing@gmail.com/
> Fixes: 6b9c129c2f93 ("xsk: remove @first_frag from xsk_build_skb()")
> Signed-off-by: Jason Xing <kernelxing@tencent.com>
> ---
>  net/xdp/xsk.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c
> index 6521604f8d42..d23d1b14b8b4 100644
> --- a/net/xdp/xsk.c
> +++ b/net/xdp/xsk.c
> @@ -889,7 +889,7 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs,
>  	return skb;
>  
>  free_err:
> -	if (skb && !skb_shinfo(skb)->nr_frags)
> +	if (skb && !xs->skb)
>  		kfree_skb(skb);
>  
>  	if (err == -EOVERFLOW) {
> --
> 2.41.3
>

Acked-by: Stanislav Fomichev <sdf@fomichev.me>

^ permalink raw reply

* Re: [PATCH net v3 4/8] xsk: prevent CQ desync when freeing half-built skbs in xsk_build_skb()
From: Stanislav Fomichev @ 2026-04-22 16:31 UTC (permalink / raw)
  To: Jason Xing; +Cc: bpf, netdev, Jason Xing
In-Reply-To: <20260422033650.68457-5-kerneljasonxing@gmail.com>

> From: Jason Xing <kernelxing@tencent.com>
> 
> Once xsk_skb_init_misc() has been called on an skb, its destructor is
> set to xsk_destruct_skb(), which submits the descriptor address(es) to
> the completion queue and advances the CQ producer. If such an skb is
> subsequently freed via kfree_skb() along an error path - before the
> skb has ever been handed to the driver - the destructor still runs and
> submits a bogus, half-initialized address to the CQ.
> 
> Postpone the init phase when we believe the allocation of first frag is
> successfully completed. Before this init, skb can be safely freed by
> kfree_skb().
> 
> Closes: https://lore.kernel.org/all/20260419045822.843BFC2BCAF@smtp.kernel.org/
> Fixes: c30d084960cf ("xsk: avoid overwriting skb fields for multi-buffer traffic")
> Signed-off-by: Jason Xing <kernelxing@tencent.com>
> ---
>  net/xdp/xsk.c | 5 ++---
>  1 file changed, 2 insertions(+), 3 deletions(-)
> 
> diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c
> index d23d1b14b8b4..88ec6d2cbbcf 100644
> --- a/net/xdp/xsk.c
> +++ b/net/xdp/xsk.c
> @@ -739,8 +739,6 @@ static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs,
>  			return ERR_PTR(err);
>  
>  		skb_reserve(skb, hr);
> -
> -		xsk_skb_init_misc(skb, xs, desc->addr);
>  		if (desc->options & XDP_TX_METADATA) {
>  			err = xsk_skb_metadata(skb, buffer, desc, pool, hr);
>  			if (unlikely(err))
> @@ -834,7 +832,6 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs,
>  			if (unlikely(err))
>  				goto free_err;
>  
> -			xsk_skb_init_misc(skb, xs, desc->addr);
>  			if (desc->options & XDP_TX_METADATA) {
>  				err = xsk_skb_metadata(skb, buffer, desc,
>  						       xs->pool, hr);
> @@ -884,6 +881,8 @@ static struct sk_buff *xsk_build_skb(struct xdp_sock *xs,
>  		}
>  	}
>  
> +	if (!xs->skb)
> +		xsk_skb_init_misc(skb, xs, desc->addr);
>  	xsk_inc_num_desc(skb);
>  
>  	return skb;
> --
> 2.41.3
>

Acked-by: Stanislav Fomichev <sdf@fomichev.me>

^ permalink raw reply

* Re: [PATCH net v3 5/8] xsk: avoid skb leak in XDP_TX_METADATA case
From: Stanislav Fomichev @ 2026-04-22 16:31 UTC (permalink / raw)
  To: Jason Xing; +Cc: bpf, netdev, Jason Xing
In-Reply-To: <20260422033650.68457-6-kerneljasonxing@gmail.com>

> From: Jason Xing <kernelxing@tencent.com>
> 
> Fix it by explicitly adding kfree_skb() before returning back to its
> caller.
> 
> How to reproduce it in virtio_net:
> 1. the current skb is the first one (which means no frag and xs->skb is
>    NULL) and users enable metadata feature.
> 2. xsk_skb_metadata() returns a error code.
> 3. the caller xsk_build_skb() clears skb by using 'skb = NULL;'.
> 4. there is no chance to free this skb anymore.
> 
> Closes: https://lore.kernel.org/all/20260415085204.3F87AC19424@smtp.kernel.org/
> Fixes: 30c3055f9c0d ("xsk: wrap generic metadata handling onto separate function")
> Signed-off-by: Jason Xing <kernelxing@tencent.com>
> ---
>  net/xdp/xsk.c | 4 +++-
>  1 file changed, 3 insertions(+), 1 deletion(-)
> 
> diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c
> index 88ec6d2cbbcf..c49b58199d2f 100644
> --- a/net/xdp/xsk.c
> +++ b/net/xdp/xsk.c
> @@ -741,8 +741,10 @@ static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs,
>  		skb_reserve(skb, hr);
>  		if (desc->options & XDP_TX_METADATA) {
>  			err = xsk_skb_metadata(skb, buffer, desc, pool, hr);
> -			if (unlikely(err))
> +			if (unlikely(err)) {
> +				kfree_skb(skb);
>  				return ERR_PTR(err);
> +			}
>  		}
>  	} else {
>  		struct xsk_addrs *xsk_addr;
> --
> 2.41.3
>

Acked-by: Stanislav Fomichev <sdf@fomichev.me>

^ permalink raw reply

* Re: [PATCH net v3 8/8] xsk: don't support AF_XDP on 32-bit architectures
From: Stanislav Fomichev @ 2026-04-22 16:31 UTC (permalink / raw)
  To: Jason Xing; +Cc: bpf, netdev, Jason Xing
In-Reply-To: <20260422033650.68457-9-kerneljasonxing@gmail.com>

> From: Jason Xing <kernelxing@tencent.com>
> 
> In copy mode TX, xsk_skb_destructor_set_addr() stores the 64-bit
> descriptor address into skb_shinfo(skb)->destructor_arg (void *) via a
> uintptr_t cast:
> 
>     skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t)addr | 0x1UL);
> 
> On 32-bit architectures uintptr_t is 32 bits, so the upper 32 bits of
> the descriptor address are silently dropped. In XDP_ZEROCOPY unaligned
> mode the chunk offset is encoded in bits 48-63 of the descriptor
> address (XSK_UNALIGNED_BUF_OFFSET_SHIFT = 48), meaning the offset is
> lost entirely. The completion queue then returns a truncated address to
> userspace, making buffer recycling impossible.
> 
> Since we hear no one is using AF_XDP on 32-bit arch, we decided to
> strictly stop supporting it at compile time.
> 
> Closes: https://lore.kernel.org/all/20260419045824.D9E5EC2BCAF@smtp.kernel.org/
> Fixes: 0ebc27a4c67d ("xsk: avoid data corruption on cq descriptor number")
> Suggested-by: Stanislav Fomichev <sdf@fomichev.me>
> Signed-off-by: Jason Xing <kernelxing@tencent.com>
> ---
>  net/xdp/Kconfig | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/net/xdp/Kconfig b/net/xdp/Kconfig
> index 71af2febe72a..819aa5795f50 100644
> --- a/net/xdp/Kconfig
> +++ b/net/xdp/Kconfig
> @@ -1,7 +1,7 @@
>  # SPDX-License-Identifier: GPL-2.0-only
>  config XDP_SOCKETS
>  	bool "XDP sockets"
> -	depends on BPF_SYSCALL
> +	depends on BPF_SYSCALL && 64BIT
>  	default n
>  	help
>  	  XDP sockets allows a channel between XDP programs and
> --
> 2.41.3
>

Acked-by: Stanislav Fomichev <sdf@fomichev.me>

^ permalink raw reply

* Re: [PATCH net v3 6/8] xsk: free the skb when hitting the upper bound MAX_SKB_FRAGS
From: Stanislav Fomichev @ 2026-04-22 16:31 UTC (permalink / raw)
  To: Jason Xing; +Cc: bpf, netdev, Jason Xing
In-Reply-To: <20260422033650.68457-7-kerneljasonxing@gmail.com>

> From: Jason Xing <kernelxing@tencent.com>
> 
> Fix it by explicitly adding kfree_skb() before returning back to its
> caller.
> 
> How to reproduce it in virtio_net:
> 1. the current skb is the first one (which means xs->skb is NULL) and
>    hit the limit MAX_SKB_FRAGS.
> 2. xsk_build_skb_zerocopy() returns -EOVERFLOW.
> 3. the caller xsk_build_skb() clears skb by using 'skb = NULL;'. This
>    is why bug can be triggered.
> 4. there is no chance to free this skb anymore.
> 
> Note that if in this case the xs->skb is not NULL, xsk_build_skb() will
> call xsk_drop_skb(xs->skb) to do the right thing.
> 
> Fixes: cf24f5a5feea ("xsk: add support for AF_XDP multi-buffer on Tx path")
> Signed-off-by: Jason Xing <kernelxing@tencent.com>
> ---
>  net/xdp/xsk.c | 5 ++++-
>  1 file changed, 4 insertions(+), 1 deletion(-)
> 
> diff --git a/net/xdp/xsk.c b/net/xdp/xsk.c
> index c49b58199d2f..5e6326e076ab 100644
> --- a/net/xdp/xsk.c
> +++ b/net/xdp/xsk.c
> @@ -776,8 +776,11 @@ static struct sk_buff *xsk_build_skb_zerocopy(struct xdp_sock *xs,
>  	addr = buffer - pool->addrs;
>  
>  	for (copied = 0, i = skb_shinfo(skb)->nr_frags; copied < len; i++) {
> -		if (unlikely(i >= MAX_SKB_FRAGS))
> +		if (unlikely(i >= MAX_SKB_FRAGS)) {
> +			if (!xs->skb)
> +				kfree_skb(skb);
>  			return ERR_PTR(-EOVERFLOW);
> +		}
>  
>  		page = pool->umem->pgs[addr >> PAGE_SHIFT];
>  		get_page(page);
> --
> 2.41.3
>

Acked-by: Stanislav Fomichev <sdf@fomichev.me>

^ permalink raw reply

* Re: [BUG] rxrpc: Client connection leak and BUG() call during kernel IO thread exit
From: Anderson Nascimento @ 2026-04-22 16:37 UTC (permalink / raw)
  To: David Howells
  Cc: netdev, Marc Dionne, Jakub Kicinski, David S. Miller,
	Eric Dumazet, Paolo Abeni, linux-kernel, Jeffrey Altman,
	Simon Horman
In-Reply-To: <CAPhRvkyCQQM1cv+zQgZjbQDhQ2YVxJ26PG9UrkA6B7uTCguhPQ@mail.gmail.com>

On Wed, Apr 22, 2026 at 1:25 PM Anderson Nascimento
<anderson@allelesecurity.com> wrote:
>
> Hi David,
>
> On Wed, Apr 22, 2026 at 1:08 PM David Howells <dhowells@redhat.com> wrote:
> >
> > Do you by any chance have a reproducer program for this?
> >
>
> Sorry, I mixed things up and ended up sending a different reproducer.
> I do have it, I will test it now and send here in a few minutes.

You can find the reproducers below. I run them simultaneously in a
bash while loop on two different SSH shells. I can trigger running
only one server and one client, but when I discovered the bug I was
running 2 servers and 2 clients simultaneously. You can try both, and
here it doesn't take long to trigger it. My virtual machine is
configured to have 4 cores.

while true; do ./server; done
while true; do ./client; done

server.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <linux/rxrpc.h>
#include <pthread.h>

#define RXRPC_ADD_CHARGE_ACCEPT(control, ctrllen) \
do { \
struct cmsghdr *__cmsg; \
__cmsg = (void *)(control) + (ctrllen); \
__cmsg->cmsg_len = CMSG_LEN(0); \
__cmsg->cmsg_level = SOL_RXRPC; \
__cmsg->cmsg_type = RXRPC_CHARGE_ACCEPT; \
(ctrllen) += __cmsg->cmsg_len; \
\
} while (0)

int sk;

void *__close(void *a){

close(sk);

return NULL;
}

int main(void){
struct sockaddr_rxrpc sockaddr_rxrpc_server;
struct msghdr msg;
struct iovec iov;
char buffer_msg_control[4096];
size_t control_len = 0;
pthread_t th[2];

memset(&sockaddr_rxrpc_server,'\0',sizeof(sockaddr_rxrpc_server));
memset(&buffer_msg_control,'\0',sizeof(buffer_msg_control));
memset(&msg,'\0',sizeof(msg));
memset(&iov,'\0',sizeof(iov));

sk = socket(AF_RXRPC, SOCK_DGRAM, PF_INET);

sockaddr_rxrpc_server.srx_family = AF_RXRPC;
sockaddr_rxrpc_server.srx_service = 1234;
sockaddr_rxrpc_server.transport_type = SOCK_DGRAM;
sockaddr_rxrpc_server.transport_len =
sizeof(sockaddr_rxrpc_server.transport.sin);

sockaddr_rxrpc_server.transport.family = AF_INET;
        sockaddr_rxrpc_server.transport.sin.sin_port = htons(7000);

bind(sk, (struct sockaddr *)&sockaddr_rxrpc_server,
sizeof(sockaddr_rxrpc_server));

listen(sk,10);

RXRPC_ADD_CHARGE_ACCEPT(buffer_msg_control, control_len);

msg.msg_control = buffer_msg_control;
msg.msg_controllen = control_len;

sendmsg(sk, &msg, 0);

pthread_create(&th[0],NULL,&__close,NULL);

pthread_join(th[0],NULL);

return 0;
}

client.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <linux/rxrpc.h>

static const unsigned char local_addr[4] = { 127, 0, 0, 1 };

#define RXRPC_ADD_CALLID(control, ctrllen, id) \
do { \
struct cmsghdr *__cmsg; \
__cmsg = (void *)(control) + (ctrllen); \
__cmsg->cmsg_len = CMSG_LEN(sizeof(unsigned long)); \
__cmsg->cmsg_level = SOL_RXRPC; \
__cmsg->cmsg_type = RXRPC_USER_CALL_ID; \
*(unsigned long *)CMSG_DATA(__cmsg) = (id); \
(ctrllen) += __cmsg->cmsg_len; \
\
} while (0)

int main(void){
struct msghdr msg;
struct sockaddr_rxrpc sockaddr_rxrpc_local;
struct sockaddr_rxrpc sockaddr_rxrpc_client;
char buffer_msg_control[4096];
size_t control_len;
int sk;

memset(&sockaddr_rxrpc_local,'\0',sizeof(sockaddr_rxrpc_local));
memset(&sockaddr_rxrpc_client,'\0',sizeof(sockaddr_rxrpc_client));
memset(&buffer_msg_control,'\0',sizeof(buffer_msg_control));
memset(&msg,'\0',sizeof(msg));

sk = socket(AF_RXRPC, SOCK_DGRAM, PF_INET);

sockaddr_rxrpc_local.srx_family = AF_RXRPC;
sockaddr_rxrpc_local.srx_service = 0;
sockaddr_rxrpc_local.transport_type = SOCK_DGRAM;
sockaddr_rxrpc_local.transport_len = sizeof(sockaddr_rxrpc_local.transport.sin);

sockaddr_rxrpc_local.transport.family = AF_INET;
sockaddr_rxrpc_local.transport.sin.sin_port = htons(7001);

memcpy(&sockaddr_rxrpc_local.transport.sin.sin_addr, &local_addr, 4);

bind(sk, (struct sockaddr *)&sockaddr_rxrpc_local,
sizeof(sockaddr_rxrpc_local));

sockaddr_rxrpc_client.srx_family = AF_RXRPC;
sockaddr_rxrpc_client.srx_service = 1234;
sockaddr_rxrpc_client.transport_type = SOCK_DGRAM;
sockaddr_rxrpc_client.transport_len =
sizeof(sockaddr_rxrpc_client.transport.sin);
sockaddr_rxrpc_client.transport.family = AF_INET;
sockaddr_rxrpc_client.transport.sin.sin_port = htons(7000);

memcpy(&sockaddr_rxrpc_client.transport.sin.sin_addr, &local_addr, 4);

connect(sk, (struct sockaddr *)&sockaddr_rxrpc_client,
sizeof(sockaddr_rxrpc_client));

control_len = 0;
RXRPC_ADD_CALLID(buffer_msg_control, control_len, 0x1234);

msg.msg_control = buffer_msg_control;
msg.msg_controllen = control_len;

sendmsg(sk, &msg, 0);

return 0;
}

The report I have just triggered.

[  473.601077] rxrpc: AF_RXRPC: Leaked client conn 00000000bf02a6a7 {1}
[  473.601115] ------------[ cut here ]------------
[  473.601117] kernel BUG at net/rxrpc/conn_client.c:64!
[  473.601169] Oops: invalid opcode: 0000 [#1] SMP NOPTI
[  473.601180] CPU: 0 UID: 0 PID: 1107239 Comm: krxrpcio/7001 Not
tainted 6.18.13-200.fc43.x86_64 #1 PREEMPT(lazy)
[  473.601193] Hardware name: VMware, Inc. VMware Virtual
Platform/440BX Desktop Reference Platform, BIOS 6.00 11/12/2020
[  473.601205] RIP: 0010:rxrpc_purge_client_connections+0x58/0xa0 [rxrpc]
[  473.601261] Code: 28 01 00 00 00 74 25 31 c0 48 8d 74 24 0c 48 89
cf 89 44 24 0c 48 89 0c 24 e8 d4 ec c2 c1 48 89 c6 48 85 c0 0f 85 49
dd 01 00 <0f> 0b 31 f6 48 89 cf 48 89 0c 24 e8 c8 aa c4 c1 48 8b 0c 24
85 c0
[  473.601280] RSP: 0018:ffffc900159cfdd8 EFLAGS: 00010246
[  473.601288] RAX: 0000000000000000 RBX: ffff88810a6b4800 RCX: 0000000000000000
[  473.601297] RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff88810a6b4920
[  473.601305] RBP: ffff888123398000 R08: ffffc900159cfdb8 R09: ffff88810a6b4928
[  473.601313] R10: 0000000000000018 R11: 0000000040000000 R12: ffff88810a9cda00
[  473.601322] R13: ffff88810a6b4800 R14: ffffc900159cfe70 R15: ffff88812d0c2800
[  473.601330] FS:  0000000000000000(0000) GS:ffff8882af626000(0000)
knlGS:0000000000000000
[  473.601339] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  473.601347] CR2: 00007faf20630030 CR3: 000000000382e002 CR4: 00000000003706f0
[  473.601380] Call Trace:
[  473.601387]  <TASK>
[  473.601393]  rxrpc_destroy_local+0xc9/0xe0 [rxrpc]
[  473.601443]  rxrpc_io_thread+0x65d/0x750 [rxrpc]
[  473.601487]  ? __pfx_rxrpc_io_thread+0x10/0x10 [rxrpc]
[  473.601527]  kthread+0xfc/0x240
[  473.601536]  ? __pfx_kthread+0x10/0x10
[  473.601542]  ret_from_fork+0xf4/0x110
[  473.601550]  ? __pfx_kthread+0x10/0x10
[  473.601558]  ret_from_fork_asm+0x1a/0x30
[  473.601574]  </TASK>
[  473.601578] Modules linked in: vsock_diag fcrypt pcbc rxrpc
ip6_udp_tunnel krb5 udp_tunnel rfkill nft_fib_inet nft_fib_ipv4
nft_fib_ipv6 nft_fib nft_reject_inet nf_reject_ipv4 nf_reject_ipv6
nft_reject nft_ct nft_chain_nat nf_nat nf_conntrack nf_defrag_ipv6
nf_defrag_ipv4 nf_tables intel_rapl_msr intel_rapl_common
intel_uncore_frequency_common intel_pmc_core pmt_telemetry
pmt_discovery pmt_class intel_pmc_ssram_telemetry intel_vsec rapl
vmw_balloon sunrpc qrtr vmxnet3 i2c_piix4 i2c_smbus binfmt_misc joydev
loop dm_multipath nfnetlink zram lz4hc_compress lz4_compress
vmw_vsock_vmci_transport vsock vmw_vmci xfs nvme nvme_core
nvme_keyring polyval_clmulni ghash_clmulni_intel nvme_auth vmwgfx hkdf
drm_ttm_helper ata_generic ttm pata_acpi serio_raw scsi_dh_rdac
scsi_dh_emc scsi_dh_alua i2c_dev fuse
[  473.601690] ---[ end trace 0000000000000000 ]---
[  473.601698] RIP: 0010:rxrpc_purge_client_connections+0x58/0xa0 [rxrpc]
[  473.601794] Code: 28 01 00 00 00 74 25 31 c0 48 8d 74 24 0c 48 89
cf 89 44 24 0c 48 89 0c 24 e8 d4 ec c2 c1 48 89 c6 48 85 c0 0f 85 49
dd 01 00 <0f> 0b 31 f6 48 89 cf 48 89 0c 24 e8 c8 aa c4 c1 48 8b 0c 24
85 c0
[  473.601813] RSP: 0018:ffffc900159cfdd8 EFLAGS: 00010246
[  473.601820] RAX: 0000000000000000 RBX: ffff88810a6b4800 RCX: 0000000000000000
[  473.601829] RDX: 0000000000000000 RSI: 0000000000000000 RDI: ffff88810a6b4920
[  473.601837] RBP: ffff888123398000 R08: ffffc900159cfdb8 R09: ffff88810a6b4928
[  473.601845] R10: 0000000000000018 R11: 0000000040000000 R12: ffff88810a9cda00
[  473.602211] R13: ffff88810a6b4800 R14: ffffc900159cfe70 R15: ffff88812d0c2800
[  473.602599] FS:  0000000000000000(0000) GS:ffff8882af626000(0000)
knlGS:0000000000000000
[  473.603301] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  473.604869] CR2: 00007faf20630030 CR3: 000000000382e002 CR4: 00000000003706f0

Best regards,
-- 
Anderson Nascimento
Allele Security Intelligence
https://www.allelesecurity.com

^ permalink raw reply

* Re: [PATCH net v3 8/8] xsk: don't support AF_XDP on 32-bit architectures
From: Jason Xing @ 2026-04-22 16:37 UTC (permalink / raw)
  To: Alexander Lobakin
  Cc: davem, edumazet, kuba, pabeni, bjorn, magnus.karlsson,
	maciej.fijalkowski, jonathan.lemon, sdf, ast, daniel, hawk,
	john.fastabend, bpf, netdev, Jason Xing
In-Reply-To: <2e0e0e18-cb75-4638-9a12-5906de6a8308@intel.com>

On Thu, Apr 23, 2026 at 12:10 AM Alexander Lobakin
<aleksander.lobakin@intel.com> wrote:
>
> From: Jason Xing <kerneljasonxing@gmail.com>
> Date: Wed, 22 Apr 2026 11:36:50 +0800
>
> > From: Jason Xing <kernelxing@tencent.com>
> >
> > In copy mode TX, xsk_skb_destructor_set_addr() stores the 64-bit
> > descriptor address into skb_shinfo(skb)->destructor_arg (void *) via a
> > uintptr_t cast:
> >
> >     skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t)addr | 0x1UL);
> >
> > On 32-bit architectures uintptr_t is 32 bits, so the upper 32 bits of
> > the descriptor address are silently dropped. In XDP_ZEROCOPY unaligned
> > mode the chunk offset is encoded in bits 48-63 of the descriptor
> > address (XSK_UNALIGNED_BUF_OFFSET_SHIFT = 48), meaning the offset is
> > lost entirely. The completion queue then returns a truncated address to
> > userspace, making buffer recycling impossible.
>
> What if we relax the restriction a bit? For example, refuse to configure

As to the bug itself, yes, It only affects the unaligned mode.

I wonder if we can support this after someone requires us to support
32-bit arch and use it in the real world, then we can use the previous
patch to complete the full support (which doesn't harm the path on
64-bit arch).

The code looks like this based on your suggestion. Just for the record.
diff --git a/net/xdp/xdp_umem.c b/net/xdp/xdp_umem.c
index 58da2f4f4397..03417b04592f 100644
--- a/net/xdp/xdp_umem.c
+++ b/net/xdp/xdp_umem.c
@@ -177,6 +177,9 @@ static int xdp_umem_reg(struct xdp_umem *umem,
struct xdp_umem_reg *mr)
if (mr->flags & ~XDP_UMEM_FLAGS_VALID)
return -EINVAL;
+ if (!IS_ENABLED(CONFIG_64BIT) && unaligned_chunks)
+ return -EOPNOTSUPP;
+
if (!unaligned_chunks && !is_power_of_2(chunk_size))
return -EINVAL;

Actually I'm fine with either of them. Right now I'm not so sure which
direction this patch should take :)

Thanks,
Jason

> an XSk socket in unaligned mode if on a 32-bit arch? Or add a check
> under CONFIG_32_BIT like it was done in Page Pool:
>
> skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t)addr | 0x1UL);
>
> #ifdef CONFIG_32BIT
> if (((uintptr_t)skb_shinfo(skb)->destructor_arg) & ~0x1UL) != addr)
>         // WARN_ONCE or whatever + error path
> #endif
>
> I never used XSk on a 32-bit arch, but back when I was working on 32-bit
> MIPS 1G routers, I wanted to add native XSk support to the Eth driver.
> Sure, just for fun, now that we have cheap AArch64 and other 64-bit
> embedded chips, 32-bit embedded networking SoCs are almost dead, but
> OTOH, as you can see, other subsystems like PP still try to support 32 bit.
> Especially given that this issue applies to only to the skb XSk path,
> not native in-driver implementations.
>
> >
> > Since we hear no one is using AF_XDP on 32-bit arch, we decided to
> > strictly stop supporting it at compile time.
> >
> > Closes: https://lore.kernel.org/all/20260419045824.D9E5EC2BCAF@smtp.kernel.org/
> > Fixes: 0ebc27a4c67d ("xsk: avoid data corruption on cq descriptor number")
> > Suggested-by: Stanislav Fomichev <sdf@fomichev.me>
> > Signed-off-by: Jason Xing <kernelxing@tencent.com>
> > ---
> >  net/xdp/Kconfig | 2 +-
> >  1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > diff --git a/net/xdp/Kconfig b/net/xdp/Kconfig
> > index 71af2febe72a..819aa5795f50 100644
> > --- a/net/xdp/Kconfig
> > +++ b/net/xdp/Kconfig
> > @@ -1,7 +1,7 @@
> >  # SPDX-License-Identifier: GPL-2.0-only
> >  config XDP_SOCKETS
> >       bool "XDP sockets"
> > -     depends on BPF_SYSCALL
> > +     depends on BPF_SYSCALL && 64BIT
> >       default n
> >       help
> >         XDP sockets allows a channel between XDP programs and
>
> Thanks,
> Olek

^ permalink raw reply related

* Re: [PATCH net-next v7 0/5] TLS read_sock performance scalability
From: Chuck Lever @ 2026-04-22 16:41 UTC (permalink / raw)
  To: john.fastabend, Jakub Kicinski, Sabrina Dubroca
  Cc: netdev, kernel-tls-handshake, Chuck Lever, Hannes Reinecke,
	Alistair Francis
In-Reply-To: <20260328-tls-read-sock-v7-0-15678415dfc1@oracle.com>


On Sat, Mar 28, 2026, at 11:17 AM, Chuck Lever wrote:
> I'd like to encourage in-kernel kTLS consumers (i.e., NFS and
> NVMe/TCP) to coalesce on the use of read_sock. When I suggested
> this to Hannes, he reported a number of nagging performance
> scalability issues with read_sock. This series is an attempt to
> run these issues down and get them fixed before we convert the
> above sock_recvmsg consumers over to read_sock.
>
> Batch async decryption and its submit/deliver scaffolding were
> dropped from this series because async_capable is always false
> for TLS 1.3, which NFS and NVMe/TCP both require. Async crypto
> support for TLS 1.3 is a prerequisite for revisiting that work.
>
> ---
> Changes since v6:
> - Rebased on net-next, v5's 1/6 was merged upstream
>
> Changes since v5:
> - Patch 6: Set released = true when sk_flush_backlog() returns
>   true, so tls_strp_msg_load() knows the socket lock was
>   released (Sabrina)
> - Patch 6: Drop Fixes tag; submit bug fix separately via net
>   if warranted (Sabrina)
> - Patch 6: Note redundant flush on cold path in commit message
>   (Sabrina)
>
> Changes since v4:
> - Drop batch async decryption and submit/deliver restructure:
>   async_capable is always false for TLS 1.3, so the new code
>   was unreachable for NFS and NVMe/TCP
> - Purge async_hold directly in tls_decrypt_async_wait() and drop
>   the tls_decrypt_async_drain() wrapper
> - Merge tls_strp_check_rcv_quiet() into tls_strp_check_rcv() with
>   a bool wake parameter; fix lost wakeup on the recvmsg exit path
>
> Changes since v3:
> - Clarify why tls_decrypt_async_drain() is separate from _wait()
> - Fold tls_err_abort() into tls_rx_one_record(), drop tls_rx_decrypt_record()
> - Move backlog flush into tls_rx_rec_wait() so all RX paths benefit
>
> Changes since v2:
> - Fix short read self tests
>
> Changes since v1:
> - Add C11 reference
> - Extend data_ready reduction to recvmsg and splice
> - Restructure read_sock and recvmsg using shared helpers
>
> ---
> Chuck Lever (5):
>       tls: Abort the connection on decrypt failure
>       tls: Fix dangling skb pointer in tls_sw_read_sock()
>       tls: Factor tls_strp_msg_release() from tls_strp_msg_done()
>       tls: Suppress spurious saved_data_ready on all receive paths
>       tls: Flush backlog before waiting for a new record
>
>  net/tls/tls.h      |  4 ++--
>  net/tls/tls_main.c |  2 +-
>  net/tls/tls_strp.c | 42 +++++++++++++++++++++++++++++++-----------
>  net/tls/tls_sw.c   | 50 ++++++++++++++++++++++++++++++--------------------
>  4 files changed, 64 insertions(+), 34 deletions(-)
> ---
> base-commit: ced629dc8e5c51ff2b5d847adeeb1035cd655d58
> change-id: 20260317-tls-read-sock-a0022c9df265

I see that this series is currently not in v7.1. What is left to do?


-- 
Chuck Lever

^ permalink raw reply

* Re: [PATCH net 0/6] net/ncsi: harden packet parsing against malformed BMC replies
From: Paul Fertser @ 2026-04-22 16:44 UTC (permalink / raw)
  To: Michael Bommarito
  Cc: Samuel Mendoza-Jonas, netdev, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, linux-kernel
In-Reply-To: <20260422160342.1975093-1-michael.bommarito@gmail.com>

Hello Michael,

On Wed, Apr 22, 2026 at 12:03:36PM -0400, Michael Bommarito wrote:
> NC-SI treats the management controller as privileged, but the Linux
...
> The threat model here is a compromised BMC or management-channel MITM
> on the NC-SI link.

The subject of the cover letter and the quoted fragment suggest that
you have a wrong impression of where NC-SI links exist and what they
carry, let me try to clarify.

On motherboards with BMC (the management controller) there often is a
way for the BMC (dedicated SoC these days) to talk to the
host-controlled NIC via NC-SI which is basically RMII (normally used
to talk to Ethernet PHY but here it's used to talk to a whole big NIC)
on hardware level plus special kind of frames sent in-band for
(partial) control and monitoring of the NIC. And regular frames are
transmitted over the same set of signals, there's no dedicated channel
for any kind of management inside NC-SI.

The code your patches modify always runs only on the BMC itself, the
packets parsed are generated by a NIC directly.

So if anything, the threat model here is compromised NIC
firmware. MITMing sounds unlikely as that would require tricky
hardware modifications and if you can do that it's easier to put a
modified NIC instead.

The idea to not trust anything coming from a NIC too much is good in
general but please take the correct context into account when
reasoning about the patches.

^ permalink raw reply

* Re: [PATCH iwl-net v1] igc: set tx buffer type for SMD frames
From: Simon Horman @ 2026-04-22 16:46 UTC (permalink / raw)
  To: Kohei Enju
  Cc: intel-wired-lan, netdev, Tony Nguyen, Przemek Kitszel,
	Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Choong Yong Liang, Vladimir Oltean,
	Vinicius Costa Gomes, Chwee-Lin Choong
In-Reply-To: <20260417193223.291093-1-kohei@enjuk.jp>

On Sat, Apr 18, 2026 at 04:31:47AM +0900, Kohei Enju wrote:
> Sashiko pointed out that igc_fpe_init_smd_frame() initializes
> igc_tx_buffer fields for an SMD skb, but does not set the buffer type:
> https://sashiko.dev/#/patchset/20260415025226.114115-1-kohei%40enjuk.jp
> 
> Since igc_tx_buffer entries are reused, a stale XDP or XSK type can
> remain and make TX completion use the wrong cleanup path.
> 
> Set the buffer type to IGC_TX_BUFFER_TYPE_SKB.
> 
> Fixes: 5422570c0010 ("igc: add support for frame preemption verification")
> Signed-off-by: Kohei Enju <kohei@enjuk.jp>

Reviewed-by: Simon Horman <horms@kernel.org>


^ permalink raw reply

* Re: [PATCH net v3 8/8] xsk: don't support AF_XDP on 32-bit architectures
From: Maciej Fijalkowski @ 2026-04-22 16:58 UTC (permalink / raw)
  To: Jason Xing
  Cc: Alexander Lobakin, davem, edumazet, kuba, pabeni, bjorn,
	magnus.karlsson, jonathan.lemon, sdf, ast, daniel, hawk,
	john.fastabend, bpf, netdev, Jason Xing
In-Reply-To: <CAL+tcoDTXEsehprDHeO7P1pcqwu_US3Li-dm_YyGDAwu2aYZvw@mail.gmail.com>

On Thu, Apr 23, 2026 at 12:37:07AM +0800, Jason Xing wrote:
> On Thu, Apr 23, 2026 at 12:10 AM Alexander Lobakin
> <aleksander.lobakin@intel.com> wrote:
> >
> > From: Jason Xing <kerneljasonxing@gmail.com>
> > Date: Wed, 22 Apr 2026 11:36:50 +0800
> >
> > > From: Jason Xing <kernelxing@tencent.com>
> > >
> > > In copy mode TX, xsk_skb_destructor_set_addr() stores the 64-bit
> > > descriptor address into skb_shinfo(skb)->destructor_arg (void *) via a
> > > uintptr_t cast:
> > >
> > >     skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t)addr | 0x1UL);
> > >
> > > On 32-bit architectures uintptr_t is 32 bits, so the upper 32 bits of
> > > the descriptor address are silently dropped. In XDP_ZEROCOPY unaligned
> > > mode the chunk offset is encoded in bits 48-63 of the descriptor
> > > address (XSK_UNALIGNED_BUF_OFFSET_SHIFT = 48), meaning the offset is
> > > lost entirely. The completion queue then returns a truncated address to
> > > userspace, making buffer recycling impossible.
> >
> > What if we relax the restriction a bit? For example, refuse to configure
> 
> As to the bug itself, yes, It only affects the unaligned mode.
> 
> I wonder if we can support this after someone requires us to support
> 32-bit arch and use it in the real world, then we can use the previous
> patch to complete the full support (which doesn't harm the path on
> 64-bit arch).
> 
> The code looks like this based on your suggestion. Just for the record.
> diff --git a/net/xdp/xdp_umem.c b/net/xdp/xdp_umem.c
> index 58da2f4f4397..03417b04592f 100644
> --- a/net/xdp/xdp_umem.c
> +++ b/net/xdp/xdp_umem.c
> @@ -177,6 +177,9 @@ static int xdp_umem_reg(struct xdp_umem *umem,
> struct xdp_umem_reg *mr)
> if (mr->flags & ~XDP_UMEM_FLAGS_VALID)
> return -EINVAL;
> + if (!IS_ENABLED(CONFIG_64BIT) && unaligned_chunks)
> + return -EOPNOTSUPP;
> +
> if (!unaligned_chunks && !is_power_of_2(chunk_size))
> return -EINVAL;
> 
> Actually I'm fine with either of them. Right now I'm not so sure which
> direction this patch should take :)

Abstracting from discussion, that is not a patch we would want against
-net. We should not forbid 32bit on stable kernels, maybe there is someone
in the 'basement' using 32bit xsk on stable kernel. Plus the fixes tag vs
patch's content looks weird ;)

> 
> Thanks,
> Jason
> 
> > an XSk socket in unaligned mode if on a 32-bit arch? Or add a check
> > under CONFIG_32_BIT like it was done in Page Pool:
> >
> > skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t)addr | 0x1UL);
> >
> > #ifdef CONFIG_32BIT
> > if (((uintptr_t)skb_shinfo(skb)->destructor_arg) & ~0x1UL) != addr)
> >         // WARN_ONCE or whatever + error path
> > #endif
> >
> > I never used XSk on a 32-bit arch, but back when I was working on 32-bit
> > MIPS 1G routers, I wanted to add native XSk support to the Eth driver.
> > Sure, just for fun, now that we have cheap AArch64 and other 64-bit
> > embedded chips, 32-bit embedded networking SoCs are almost dead, but
> > OTOH, as you can see, other subsystems like PP still try to support 32 bit.
> > Especially given that this issue applies to only to the skb XSk path,
> > not native in-driver implementations.
> >
> > >
> > > Since we hear no one is using AF_XDP on 32-bit arch, we decided to
> > > strictly stop supporting it at compile time.
> > >
> > > Closes: https://lore.kernel.org/all/20260419045824.D9E5EC2BCAF@smtp.kernel.org/
> > > Fixes: 0ebc27a4c67d ("xsk: avoid data corruption on cq descriptor number")
> > > Suggested-by: Stanislav Fomichev <sdf@fomichev.me>
> > > Signed-off-by: Jason Xing <kernelxing@tencent.com>
> > > ---
> > >  net/xdp/Kconfig | 2 +-
> > >  1 file changed, 1 insertion(+), 1 deletion(-)
> > >
> > > diff --git a/net/xdp/Kconfig b/net/xdp/Kconfig
> > > index 71af2febe72a..819aa5795f50 100644
> > > --- a/net/xdp/Kconfig
> > > +++ b/net/xdp/Kconfig
> > > @@ -1,7 +1,7 @@
> > >  # SPDX-License-Identifier: GPL-2.0-only
> > >  config XDP_SOCKETS
> > >       bool "XDP sockets"
> > > -     depends on BPF_SYSCALL
> > > +     depends on BPF_SYSCALL && 64BIT
> > >       default n
> > >       help
> > >         XDP sockets allows a channel between XDP programs and
> >
> > Thanks,
> > Olek

^ permalink raw reply

* Re: [PATCH net v3 8/8] xsk: don't support AF_XDP on 32-bit architectures
From: Alexander Lobakin @ 2026-04-22 17:00 UTC (permalink / raw)
  To: Jason Xing
  Cc: davem, edumazet, kuba, pabeni, bjorn, magnus.karlsson,
	maciej.fijalkowski, jonathan.lemon, sdf, ast, daniel, hawk,
	john.fastabend, bpf, netdev, Jason Xing
In-Reply-To: <CAL+tcoDTXEsehprDHeO7P1pcqwu_US3Li-dm_YyGDAwu2aYZvw@mail.gmail.com>

From: Jason Xing <kerneljasonxing@gmail.com>
Date: Thu, 23 Apr 2026 00:37:07 +0800

> On Thu, Apr 23, 2026 at 12:10 AM Alexander Lobakin
> <aleksander.lobakin@intel.com> wrote:
>>
>> From: Jason Xing <kerneljasonxing@gmail.com>
>> Date: Wed, 22 Apr 2026 11:36:50 +0800
>>
>>> From: Jason Xing <kernelxing@tencent.com>
>>>
>>> In copy mode TX, xsk_skb_destructor_set_addr() stores the 64-bit
>>> descriptor address into skb_shinfo(skb)->destructor_arg (void *) via a
>>> uintptr_t cast:
>>>
>>>     skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t)addr | 0x1UL);
>>>
>>> On 32-bit architectures uintptr_t is 32 bits, so the upper 32 bits of
>>> the descriptor address are silently dropped. In XDP_ZEROCOPY unaligned
>>> mode the chunk offset is encoded in bits 48-63 of the descriptor
>>> address (XSK_UNALIGNED_BUF_OFFSET_SHIFT = 48), meaning the offset is
>>> lost entirely. The completion queue then returns a truncated address to
>>> userspace, making buffer recycling impossible.
>>
>> What if we relax the restriction a bit? For example, refuse to configure
> 
> As to the bug itself, yes, It only affects the unaligned mode.
> 
> I wonder if we can support this after someone requires us to support
> 32-bit arch and use it in the real world, then we can use the previous
> patch to complete the full support (which doesn't harm the path on
> 64-bit arch).
> 
> The code looks like this based on your suggestion. Just for the record.
> diff --git a/net/xdp/xdp_umem.c b/net/xdp/xdp_umem.c
> index 58da2f4f4397..03417b04592f 100644
> --- a/net/xdp/xdp_umem.c
> +++ b/net/xdp/xdp_umem.c
> @@ -177,6 +177,9 @@ static int xdp_umem_reg(struct xdp_umem *umem,
> struct xdp_umem_reg *mr)
> if (mr->flags & ~XDP_UMEM_FLAGS_VALID)
> return -EINVAL;
> + if (!IS_ENABLED(CONFIG_64BIT) && unaligned_chunks)
> + return -EOPNOTSUPP;

I'm fine with this one, as well as with the check I proposed. This one
would happen during the configuration so it won't affect the hotpath,
my check is just inspired by what Page Pool does when
`sizeof(dma_addr_t) > sizeof(unsigned long)` (rare case but perfectly
valid on some architectures).

> +
> if (!unaligned_chunks && !is_power_of_2(chunk_size))
> return -EINVAL;
> 
> Actually I'm fine with either of them. Right now I'm not so sure which
> direction this patch should take :)

Up to you and other reviewers, but I really do think cutting off 32-bit
arches entirely due to a bug which can happen only under certain
conditions (skb XSk mode + unaligned mode) is too aggressive.

> 
> Thanks,
> Jason
> 
>> an XSk socket in unaligned mode if on a 32-bit arch? Or add a check
>> under CONFIG_32_BIT like it was done in Page Pool:
>>
>> skb_shinfo(skb)->destructor_arg = (void *)((uintptr_t)addr | 0x1UL);
>>
>> #ifdef CONFIG_32BIT
>> if (((uintptr_t)skb_shinfo(skb)->destructor_arg) & ~0x1UL) != addr)
>>         // WARN_ONCE or whatever + error path
>> #endif
>>
>> I never used XSk on a 32-bit arch, but back when I was working on 32-bit
>> MIPS 1G routers, I wanted to add native XSk support to the Eth driver.
>> Sure, just for fun, now that we have cheap AArch64 and other 64-bit
>> embedded chips, 32-bit embedded networking SoCs are almost dead, but
>> OTOH, as you can see, other subsystems like PP still try to support 32 bit.
>> Especially given that this issue applies to only to the skb XSk path,
>> not native in-driver implementations.
>>
>>>
>>> Since we hear no one is using AF_XDP on 32-bit arch, we decided to
>>> strictly stop supporting it at compile time.
Thanks,
Olek

^ permalink raw reply

* Re: [PATCH v2] mptcp: do not drop partial packets
From: Matthieu Baerts @ 2026-04-22 17:03 UTC (permalink / raw)
  To: Shardul Bankar, pabeni, martineau
  Cc: geliang, davem, edumazet, kuba, horms, netdev, mptcp,
	linux-kernel, janak, kalpan.jani, Shardul Bankar
In-Reply-To: <20260422143931.43281-1-shardul.b@mpiricsoftware.com>

Hi Shardul,

On 22/04/2026 16:39, Shardul Bankar wrote:
> When a packet arrives with map_seq < ack_seq < end_seq, the beginning
> of the packet has already been acknowledged but the end contains new
> data.  Currently the entire packet is dropped as "old data," forcing
> the sender to retransmit.
> 
> Instead, skip the already-acked bytes by adjusting the skb offset and
> enqueue only the new portion.  Update bytes_received and ack_seq to
> reflect the new data consumed.

Thank you for the v2.

I didn't review it (yet), but just to let you know that there are some
rules on Netdev [1] (that usually also applied on MPTCP side), and an
important one is:

 - don't repost your patches within one 24h period

Each version generates a lot of emails that are sent and need to be
triaged. With the high volume, it is then harder for us to follow, plus
a lot of shared resources are used, etc.

One last thing, because this patch is not an urgent fix, do you mind
sending new versions only the to MPTCP ML: to a restricted number of
people for the first versions, there is enough traffic on Netdev.

[1] https://docs.kernel.org/process/maintainer-netdev.html

> A previous attempt at this fix (commit 1d2ce718811a ("mptcp: do not
> drop partial packets"), reverted in commit bf39160c4218 ("Revert

Note: these two commits should not be mentioned here, they have only
been applied to the MPTCP tree, but not upstreamed. Instead, please use
lore links, e.g.

https://lore.kernel.org/c9b426a4e163aa3c4fe8b80c79f1a610f47ae7d8.1763075056.git.pabeni@redhat.com

> "mptcp: do not drop partial packets"")) also added a zero-window
> check and changed rcv_wnd_sent initialization, which caused test
> regressions.  This version addresses only the partial packet handling
> without modifying receive window accounting.
> 
> Fixes: ab174ad8ef76 ("mptcp: move ooo skbs into msk out of order queue.")
> Closes: https://github.com/multipath-tcp/mptcp_net-next/issues/600
> Signed-off-by: Shardul Bankar <shardul.b@mpiricsoftware.com>

Checkpatch is complaining that the author of the patch is not the same
as the one who sent the patch; You probably need to run this command to
fix it:

  git commit --amend --reset-author

(when the patch is sent, `git format-patch` will add a second From: tag)

Cheers,
Matt
-- 
Sponsored by the NGI0 Core fund.


^ permalink raw reply

* Re: [PATCH net v3 1/2] bnge: fix initial HWRM sequence
From: Simon Horman @ 2026-04-22 17:06 UTC (permalink / raw)
  To: vikas.gupta
  Cc: 'Simon Horman', davem, edumazet, kuba, pabeni,
	andrew+netdev, netdev, linux-kernel, vsrama-krishna.nemani,
	bhargava.marreddy, rajashekar.hudumula, ajit.khaparde,
	dharmender.garg, rahul-rg.gupta
In-Reply-To: <20260418023438.1597876-2-vikas.gupta@broadcom.com>

From: 'Simon Horman' <horms@kernel.org>

This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
Full review at: https://sashiko.dev
---
bnge: fix initial HWRM sequence

Firmware may not advertize correct resources if backing store is not
enabled before resource information is queried.
Fix the initial sequence of HWRMs so that driver gets capabilities
and resource information correctly.

> diff --git a/drivers/net/ethernet/broadcom/bnge/bnge_core.c b/drivers/net/ethernet/broadcom/bnge/bnge_core.c
> index 1c14c5fe8d613..68b74eb2c3a29 100644
> --- a/drivers/net/ethernet/broadcom/bnge/bnge_core.c
> +++ b/drivers/net/ethernet/broadcom/bnge/bnge_core.c
[ ... ]
> @@ -133,23 +140,28 @@ static int bnge_fw_register_dev(struct bnge_dev *bd)
>  
>  	bnge_hwrm_fw_set_time(bd);
>  
> -	rc =  bnge_hwrm_func_drv_rgtr(bd);
> +	/* Get the resources and configuration from firmware */
> +	rc = bnge_func_qcaps(bd);
>  	if (rc) {
> -		dev_err(bd->dev, "Failed to rgtr with firmware rc: %d\n", rc);
> +		dev_err(bd->dev, "Failed querying caps rc: %d\n", rc);
>  		return rc;
>  	}
>  
>  	rc = bnge_alloc_ctx_mem(bd);
>  	if (rc) {
>  		dev_err(bd->dev, "Failed to allocate ctx mem rc: %d\n", rc);
> -		goto err_func_unrgtr;
> +		goto err_free_ctx_mem;
>  	}
>  
> -	/* Get the resources and configuration from firmware */
> -	rc = bnge_func_qcaps(bd);
> +	rc = bnge_hwrm_func_drv_rgtr(bd);
>  	if (rc) {
> -		dev_err(bd->dev, "Failed initial configuration rc: %d\n", rc);
> -		rc = -ENODEV;
> +		dev_err(bd->dev, "Failed to rgtr with firmware rc: %d\n", rc);
> +		goto err_free_ctx_mem;

Does this regression leave the hardware with active DMA pointers to freed
memory?

If bnge_alloc_ctx_mem() allocates host DMA memory and passes the addresses
to the firmware via bnge_hwrm_func_backing_store(), and then
bnge_hwrm_func_drv_rgtr() fails, the code jumps directly to err_free_ctx_mem.

> +	}
> +
> +	rc = bnge_func_qrcaps_qcfg(bd);
> +	if (rc) {
> +		dev_err(bd->dev, "Failed querying resources rc: %d\n", rc);
>  		goto err_func_unrgtr;
>  	}
>  
> @@ -158,7 +170,9 @@ static int bnge_fw_register_dev(struct bnge_dev *bd)
>  	return 0;
>  
>  err_func_unrgtr:
> -	bnge_fw_unregister_dev(bd);
> +	bnge_hwrm_func_drv_unrgtr(bd);
> +err_free_ctx_mem:
> +	bnge_free_ctx_mem(bd);

Since the err_func_unrgtr label is bypassed, is the firmware ever told to
drop the backing store configuration before the host frees the memory in
bnge_free_ctx_mem()?

In the pre-patch sequence, the driver was registered first, so any failure
subsequently triggered a driver unregister, which implicitly told the
firmware to drop the backing store configuration. With the new order, does
the firmware maintain active DMA pointers to the freed host memory?

^ permalink raw reply

* Re: [PATCH net 0/6] net/ncsi: harden packet parsing against malformed BMC replies
From: Michael Bommarito @ 2026-04-22 17:06 UTC (permalink / raw)
  To: Paul Fertser
  Cc: Samuel Mendoza-Jonas, netdev, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, linux-kernel
In-Reply-To: <aej66UTWzQ74dven@home.paul.comp>

On Wed, Apr 22, 2026 at 12:44 PM Paul Fertser <fercerpav@gmail.com> wrote:
> The code your patches modify always runs only on the BMC itself, the
> packets parsed are generated by a NIC directly.
>
> So if anything, the threat model here is compromised NIC
> firmware. MITMing sounds unlikely as that would require tricky
> hardware modifications and if you can do that it's easier to put a
> modified NIC instead.

Thanks for the correction, I definitely got that backwards!

Should I submit a v2 with the corrected notes or wait for any other
feedback / review on patch substance (assuming you think it's actually
worth merging)?

Thanks,
Mike Bommarito

^ permalink raw reply

* Re: [PATCH] net: dsa: realtek: rtl8365mb: add support for RTL8367SB
From: Breno Leitao @ 2026-04-22 17:12 UTC (permalink / raw)
  To: Mieczyslaw Nalewaj; +Cc: netdev@vger.kernel.org
In-Reply-To: <f5ac420f-0087-4af0-bb43-b9a5b6228fbd@yahoo.com>

On Wed, Apr 22, 2026 at 05:58:30PM +0200, Mieczyslaw Nalewaj wrote:
> Add chip info entry for the Realtek RTL8367SB switch. This device has
> chip ID 0x6367 and version 0x0010. It exposes two external interfaces:
> port 6 supports SGMII and HSGMII, while port 7 supports MII, TMII,
> RMII and RGMII. Use the existing 8365MB-VC jam table for initialization.

This should target net-next, which is currently closed. Additionally, it
appears the maintainers were not properly copied on this patch?!

https://lore.kernel.org/all/20260412142250.131bf997@kernel.org/

pw-bot: cr

^ permalink raw reply

* Re: [net-next v38] mctp pcc: Implement MCTP over PCC Transport
From: Adam Young @ 2026-04-22 17:17 UTC (permalink / raw)
  To: Jakub Kicinski, admiyo
  Cc: jk, matt, andrew+netdev, davem, edumazet, pabeni, netdev,
	linux-kernel, sudeep.holla, Jonathan.Cameron, lihuisong
In-Reply-To: <20260410032441.1844450-1-kuba@kernel.org>


On 4/9/26 23:24, Jakub Kicinski wrote:
> This is an AI-generated review of your patch. The human sending this
> email has considered the AI review valid, or at least plausible.
> ---
> mctp pcc: Implement MCTP over PCC Transport
>
> This commit implements a network driver for the Management Component
> Transport Protocol (MCTP) over the Platform Communication Channel (PCC).
>
> Link: https://www.dmtf.org/sites/default/files/standards/documents/DSP0292_1.0.0WIP50.pdf
> Link: https://uefi.org/htmlspecs/ACPI_Spec_6_4_html/14_Platform_Communications_Channel/Platform_Comm_Channel.html
>
>> diff --git a/drivers/net/mctp/mctp-pcc.c b/drivers/net/mctp/mctp-pcc.c
>> new file mode 100644
>> --- /dev/null
>> +++ b/drivers/net/mctp/mctp-pcc.c
> [ ... ]
>
>> +static void mctp_pcc_client_rx_callback(struct mbox_client *cl, void *mssg)
>> +{
> [ ... ]
>
>> +	// The message must at least have the PCC command indicating it is an MCTP
>> +	// message followed by the MCTP header, or we have a malformed message.
>> +	if (pcc_header.length < sizeof(pcc_header.command) + sizeof(struct mctp_hdr)) {
>> +		dev_dstats_rx_dropped(mctp_pcc_ndev->ndev);
>> +		return;
>> +	}
>> +
>> +	size = pcc_header.length - sizeof(pcc_header.command) + sizeof(pcc_header);
> Can this size calculation wrap around? Because pcc_header.length is read from
> shared memory, a maliciously large value (e.g. 0xFFFFFFF8) could wrap around
> the unsigned 32-bit addition, resulting in a small positive size.
>
> This small positive size would then bypass the check below, and eventually
> fail in skb_pull(), leaving the malformed skb to be passed to the network
> stack.

This seems legit.  The logic to check the size should ensure that the 
value pulled out of the shared buffer is the size of the buffer, minus 
the header.


>
>> +	// If the reported size is larger than the shared memory, something is wrong
>> +	// and the best we can do is treat it as corrupted data.
>> +	if (size > inbox->chan->shmem_size) {
>> +		dev_dstats_rx_dropped(mctp_pcc_ndev->ndev);
>> +		return;
>> +	}
> [ ... ]
>
>> +static netdev_tx_t mctp_pcc_tx(struct sk_buff *skb, struct net_device *ndev)
>> +{
> [ ... ]
>
>> +	pcc_header = skb_push(skb, sizeof(*pcc_header));
>> +	pcc_header->signature = PCC_SIGNATURE | mpnd->outbox.index;
>> +	pcc_header->flags = PCC_CMD_COMPLETION_NOTIFY;
>> +	memcpy(&pcc_header->command, MCTP_SIGNATURE, MCTP_SIGNATURE_LENGTH);
>> +	pcc_header->length = len + MCTP_SIGNATURE_LENGTH;
> Are these assignments writing native endian values into shared memory?
>
> The ACPI Platform Communications Channel specification defines the shared
> memory region as strictly little-endian. Writing without cpu_to_le32() will
> cause big endian architectures to write byte-swapped metadata, resulting in
> dropped packets.

This looks legit, and it reflects some of the early version of my patch 
where I was doing similar transforms.

The target for this code is only ARM little endian systems, but I think 
it is more correct to handle the wider cases in case that decision changes.


>
>> +
>> +	rc = mbox_send_message(mpnd->outbox.chan->mchan, skb);
>> +	if (rc < 0) {
> [ ... ]
>
>> +static void mctp_pcc_tx_prepare(struct mbox_client *cl, void *mssg)
>> +{
> [ ... ]
>
>> +	if (skb->len > outbox->chan->shmem_size) {
>> +		dev_dstats_tx_dropped(mctp_pcc_ndev->ndev);
>> +		return;
>> +	}
> If the skb is oversized, this skips the copy and returns, but does it abort
> the transmission?
>
> Since mctp_pcc_tx() already submitted the message and tx_prepare() returns
> void, the hardware doorbell will still be rung. The remote endpoint would
> process the unmodified shared memory, replaying the previous packet's data.
This looks legit.  Once we are in tx_prepare the message is going to be 
sent. All checks like this need to be done in tx
>
>> +	memcpy_toio(outbox->chan->shmem,  skb->data, skb->len);
> Is it possible to read out of bounds here if the skb is fragmented?
>
> The skb->data pointer only points to the linear portion of the packet, while
> skb->len represents the total packet length including page fragments.
> skb_cow_head() does not linearize the packet, so a call to skb_linearize()
> might be needed before copying.

Legit.

While we don't have fragmentation enabled right now, it could very well 
be enabled in the future, and this should be addressed.


>
>> +}
>> +
>> +static void mctp_pcc_tx_done(struct mbox_client *c, void *mssg, int r)
>> +{
>> +	struct mctp_pcc_ndev *mctp_pcc_ndev;
>> +	struct sk_buff *skb = mssg;
>> +
>> +	mctp_pcc_ndev = container_of(c, struct mctp_pcc_ndev, outbox.client);
>> +	dev_dstats_tx_add(mctp_pcc_ndev->ndev, skb->len);
> Could this lead to sequence counter corruption and deadlock?
>
> mctp_pcc_tx() runs in process or softirq context and calls
> dev_dstats_tx_dropped(). mctp_pcc_tx_done() runs in the controller's hardirq
> context and calls dev_dstats_tx_add().
>
> The generic dev_dstats_* macros use u64_stats_update_begin(), which does not
> disable interrupts. If a hardware interrupt fires while the softirq path is
> inside u64_stats_update_begin(), the hardirq path will re-enter the lock.

I think this is legit. mctp_pcc_tx_done is triggered by the interupt 
handler (it is the registered call back). While I do prefer to register 
the packet as being sent at this point, I think that latest it can be 
done is after the packet is written to the shared region in 
mctp_pcc_tx_prepare.


>
>> +	dev_consume_skb_any(skb);
>> +	netif_wake_queue(mctp_pcc_ndev->ndev);
>> +}
> [ ... ]
>
>> +static int mctp_pcc_ndo_stop(struct net_device *ndev)
>> +{
>> +	struct mctp_pcc_ndev *mctp_pcc_ndev;
>> +	unsigned int count, idx;
>> +	struct mbox_chan *chan;
>> +	struct sk_buff *skb;
>> +
>> +	mctp_pcc_ndev = netdev_priv(ndev);
>> +	chan = mctp_pcc_ndev->outbox.chan->mchan;
>> +
>> +	scoped_guard(spinlock_irqsave, &chan->lock) {
>> +		chan->active_req = NULL;
> Does this bypass the mailbox API and cause a memory leak?
>
> By explicitly setting active_req to NULL without freeing the in-flight skb,
> the skb is leaked.
>
> Additionally, when the hardware completion interrupt fires, mbox_chan_txdone()
> will pass NULL as the mssg parameter to mctp_pcc_tx_done(). Since
> mctp_pcc_tx_done() dereferences skb->len, this will result in a NULL pointer
> dereference.

Legit.  I was thinking that the packet pointed to by chan->active_req 
was still in the ring buffer, but it has already been removed.


>
>> +		while (chan->msg_count > 0) {
>> +			count = chan->msg_count;
>> +			idx = chan->msg_free;
>> +			if (idx >= count)
>> +				idx -= count;
>> +			else
>> +				idx += MBOX_TX_QUEUE_LEN - count;
> Is this index calculation accurate?
>
> Since msg_free is incremented and msg_count is decremented in each iteration
> below, the calculated idx jumps by 2. This skips alternate skbs, leaking
> them, and reads uninitialized memory, which is then passed to
> dev_consume_skb_any(), causing a use-after-free or double free.

I don't think this is legit.  I've written code to test this algorithm, 
and it works as written.  This logic comes from the 
drivers/mailbox/mailbox.c function msg_submit.

  idx does not jump by two each iteration.

>
>> +			skb = chan->msg_data[idx];
>> +			dev_dstats_tx_dropped(ndev);
>> +			dev_consume_skb_any(skb);
>> +			chan->msg_count--;
>> +			chan->msg_free++;
>> +		}
>> +	}
> [ ... ]
>
>> +static int mctp_pcc_driver_add(struct acpi_device *acpi_dev)
>> +{
>> +	struct mctp_pcc_lookup_context context = {0};
>> +	struct mctp_pcc_ndev *mctp_pcc_ndev;
> [ ... ]
>
>> +	status = acpi_walk_resources(dev_handle, "_CRS", lookup_pcct_indices,
>> +				     &context);
>> +	if (!ACPI_SUCCESS(status)) {
>> +		dev_err(dev, "FAILED to lookup PCC indexes from CRS\n");
>> +		return -EINVAL;
>> +	}
> What happens if the _CRS table contains fewer than two PCC_DWORD_TYPE
> resources?
>
> If context.index is not verified to be at least 2 here, context.inbox_index
> and context.outbox_index might remain uninitialized or at their default zero
> values. This could cause the driver to blindly request PCC channel 0, which
> may belong to another hardware subsystem.

If that is the case then things are truely corrupted on the system.  So, 
yeah, this is legit, but a sign of things much worse happening.

It is easy enough to check that we get 2 values in the context from walk 
resources.


>
>> +
>> +	snprintf(name, sizeof(name), "mctppcc%d", context.inbox_index);
>> +	ndev = alloc_netdev(sizeof(*mctp_pcc_ndev), name, NET_NAME_PREDICTABLE,
>> +			    mctp_pcc_setup);

^ permalink raw reply

* Re: [PATCH iwl-net v1] ice: fix UAF/NULL deref when VSI rebuild and XDP attach race
From: Simon Horman @ 2026-04-22 18:00 UTC (permalink / raw)
  To: Kohei Enju
  Cc: intel-wired-lan, netdev, Tony Nguyen, Przemek Kitszel,
	Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Wojciech Drewek, Jacob Keller, Larysa Zaremba,
	Maciej Fijalkowski
In-Reply-To: <20260418090137.411506-1-kohei@enjuk.jp>

On Sat, Apr 18, 2026 at 09:01:15AM +0000, Kohei Enju wrote:
> ice_xdp_setup_prog() unconditionally hot-swaps xdp_prog when
> ICE_VSI_REBUILD_PENDING is set. In the attach path, this can publish a
> new rx_ring->xdp_prog before rx_ring->xdp_ring becomes valid while the
> rebuild is pending. As a result, ice_clean_rx_irq() may dereference
> rx_ring->xdp_ring too early.
> 
> With high-volume RX packets, running these commands in parallel
> triggered a KASAN splat [1].
>  # ethtool --reset $DEV irq dma filter offload
>  # ip link set dev $DEV xdp {obj $OBJ sec xdp,off}
> 
> Fix this by rejecting XDP attach while rebuild is pending.
> Keep XDP detach allowed in this window. Detach clears rx_ring->xdp_prog,
> so the RX path will not attempt to access rx_ring->xdp_ring.
> 
> [1]
> BUG: KASAN: slab-use-after-free in ice_napi_poll+0x3921/0x41a0
> Read of size 2 at addr ffff88812475b880 by task ksoftirqd/1/23
> [...]
> Call Trace:
>  <TASK>
>  ice_napi_poll+0x3921/0x41a0
>  __napi_poll+0x98/0x520
>  net_rx_action+0x8f2/0xfa0
>  handle_softirqs+0x1cb/0x7f0
> [...]
>  </TASK>
> 
> Allocated by task 7246:
>  ice_prepare_xdp_rings+0x3de/0x12d0
>  ice_xdp+0x61c/0xef0
>  dev_xdp_install+0x3c4/0x840
>  dev_xdp_attach+0x50a/0x10a0
>  dev_change_xdp_fd+0x175/0x210
> [...]
> 
> Freed by task 7251:
>  __rcu_free_sheaf_prepare+0x5f/0x230
>  rcu_free_sheaf+0x1a/0xf0
>  rcu_core+0x567/0x1d80
>  handle_softirqs+0x1cb/0x7f0
> 
> Fixes: 2504b8405768 ("ice: protect XDP configuration with a mutex")
> Signed-off-by: Kohei Enju <kohei@enjuk.jp>

Reviewed-by: Simon Horman <horms@kernel.org>

Sashiko has provided some feedback on this patch.
However, I believe the issues it raises are not introduced
by this patch and should not block progress of it.
I'd like to ask if you could take a look over that feedback
and see if any follow-up is appropriate.

Thanks!

^ permalink raw reply

* [PATCH net v2 02/15] drivers: net: 3com: 3c515: Remove this driver
From: Andrew Lunn @ 2026-04-22 18:01 UTC (permalink / raw)
  To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan
  Cc: Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
	Daniel Palmer, linux-kernel, netdev, linux-doc, Andrew Lunn
In-Reply-To: <20260422-v7-0-0-net-next-driver-removal-v1-v2-0-08a5b59784d5@lunn.ch>

The 3c515 was written by Donald Becker between 1997-1998. It is an ISA
device, so unlikely to be used with modern kernels.

Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
 drivers/net/ethernet/3com/3c515.c  | 1566 ------------------------------------
 drivers/net/ethernet/3com/Kconfig  |   11 -
 drivers/net/ethernet/3com/Makefile |    1 -
 3 files changed, 1578 deletions(-)

diff --git a/drivers/net/ethernet/3com/3c515.c b/drivers/net/ethernet/3com/3c515.c
deleted file mode 100644
index 2227c83a4862..000000000000
--- a/drivers/net/ethernet/3com/3c515.c
+++ /dev/null
@@ -1,1566 +0,0 @@
-/*
-	Written 1997-1998 by Donald Becker.
-
-	This software may be used and distributed according to the terms
-	of the GNU General Public License, incorporated herein by reference.
-
-	This driver is for the 3Com ISA EtherLink XL "Corkscrew" 3c515 ethercard.
-
-	The author may be reached as becker@scyld.com, or C/O
-	Scyld Computing Corporation
-	410 Severn Ave., Suite 210
-	Annapolis MD 21403
-
-
-	2000/2/2- Added support for kernel-level ISAPnP
-		by Stephen Frost <sfrost@snowman.net> and Alessandro Zummo
-	Cleaned up for 2.3.x/softnet by Jeff Garzik and Alan Cox.
-
-	2001/11/17 - Added ethtool support (jgarzik)
-
-	2002/10/28 - Locking updates for 2.5 (alan@lxorguk.ukuu.org.uk)
-
-*/
-
-#define DRV_NAME		"3c515"
-
-#define CORKSCREW 1
-
-/* "Knobs" that adjust features and parameters. */
-/* Set the copy breakpoint for the copy-only-tiny-frames scheme.
-   Setting to > 1512 effectively disables this feature. */
-static int rx_copybreak = 200;
-
-/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
-static int max_interrupt_work = 20;
-
-/* Enable the automatic media selection code -- usually set. */
-#define AUTOMEDIA 1
-
-/* Allow the use of fragment bus master transfers instead of only
-   programmed-I/O for Vortex cards.  Full-bus-master transfers are always
-   enabled by default on Boomerang cards.  If VORTEX_BUS_MASTER is defined,
-   the feature may be turned on using 'options'. */
-#define VORTEX_BUS_MASTER
-
-/* A few values that may be tweaked. */
-/* Keep the ring sizes a power of two for efficiency. */
-#define TX_RING_SIZE	16
-#define RX_RING_SIZE	16
-#define PKT_BUF_SZ		1536	/* Size of each temporary Rx buffer. */
-
-#include <linux/module.h>
-#include <linux/isapnp.h>
-#include <linux/kernel.h>
-#include <linux/netdevice.h>
-#include <linux/string.h>
-#include <linux/errno.h>
-#include <linux/in.h>
-#include <linux/ioport.h>
-#include <linux/skbuff.h>
-#include <linux/etherdevice.h>
-#include <linux/interrupt.h>
-#include <linux/timer.h>
-#include <linux/ethtool.h>
-#include <linux/bitops.h>
-#include <linux/uaccess.h>
-
-#include <net/Space.h>
-
-#include <asm/io.h>
-#include <asm/dma.h>
-
-#define NEW_MULTICAST
-#include <linux/delay.h>
-
-#define MAX_UNITS 8
-
-MODULE_AUTHOR("Donald Becker <becker@scyld.com>");
-MODULE_DESCRIPTION("3Com 3c515 Corkscrew driver");
-MODULE_LICENSE("GPL");
-
-/* "Knobs" for adjusting internal parameters. */
-/* Put out somewhat more debugging messages. (0 - no msg, 1 minimal msgs). */
-#define DRIVER_DEBUG 1
-/* Some values here only for performance evaluation and path-coverage
-   debugging. */
-static int rx_nocopy, rx_copy, queued_packet;
-
-/* Number of times to check to see if the Tx FIFO has space, used in some
-   limited cases. */
-#define WAIT_TX_AVAIL 200
-
-/* Operational parameter that usually are not changed. */
-#define TX_TIMEOUT  ((4*HZ)/10)	/* Time in jiffies before concluding Tx hung */
-
-/* The size here is somewhat misleading: the Corkscrew also uses the ISA
-   aliased registers at <base>+0x400.
-   */
-#define CORKSCREW_TOTAL_SIZE 0x20
-
-#ifdef DRIVER_DEBUG
-static int corkscrew_debug = DRIVER_DEBUG;
-#else
-static int corkscrew_debug = 1;
-#endif
-
-#define CORKSCREW_ID 10
-
-/*
-				Theory of Operation
-
-I. Board Compatibility
-
-This device driver is designed for the 3Com 3c515 ISA Fast EtherLink XL,
-3Com's ISA bus adapter for Fast Ethernet.  Due to the unique I/O port layout,
-it's not practical to integrate this driver with the other EtherLink drivers.
-
-II. Board-specific settings
-
-The Corkscrew has an EEPROM for configuration, but no special settings are
-needed for Linux.
-
-III. Driver operation
-
-The 3c515 series use an interface that's very similar to the 3c900 "Boomerang"
-PCI cards, with the bus master interface extensively modified to work with
-the ISA bus.
-
-The card is capable of full-bus-master transfers with separate
-lists of transmit and receive descriptors, similar to the AMD LANCE/PCnet,
-DEC Tulip and Intel Speedo3.
-
-This driver uses a "RX_COPYBREAK" scheme rather than a fixed intermediate
-receive buffer.  This scheme allocates full-sized skbuffs as receive
-buffers.  The value RX_COPYBREAK is used as the copying breakpoint: it is
-chosen to trade-off the memory wasted by passing the full-sized skbuff to
-the queue layer for all frames vs. the copying cost of copying a frame to a
-correctly-sized skbuff.
-
-
-IIIC. Synchronization
-The driver runs as two independent, single-threaded flows of control.  One
-is the send-packet routine, which enforces single-threaded use by the netif
-layer.  The other thread is the interrupt handler, which is single
-threaded by the hardware and other software.
-
-IV. Notes
-
-Thanks to Terry Murphy of 3Com for providing documentation and a development
-board.
-
-The names "Vortex", "Boomerang" and "Corkscrew" are the internal 3Com
-project names.  I use these names to eliminate confusion -- 3Com product
-numbers and names are very similar and often confused.
-
-The new chips support both ethernet (1.5K) and FDDI (4.5K) frame sizes!
-This driver only supports ethernet frames because of the recent MTU limit
-of 1.5K, but the changes to support 4.5K are minimal.
-*/
-
-/* Operational definitions.
-   These are not used by other compilation units and thus are not
-   exported in a ".h" file.
-
-   First the windows.  There are eight register windows, with the command
-   and status registers available in each.
-   */
-#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD)
-#define EL3_CMD 0x0e
-#define EL3_STATUS 0x0e
-
-/* The top five bits written to EL3_CMD are a command, the lower
-   11 bits are the parameter, if applicable.
-   Note that 11 parameters bits was fine for ethernet, but the new chips
-   can handle FDDI length frames (~4500 octets) and now parameters count
-   32-bit 'Dwords' rather than octets. */
-
-enum corkscrew_cmd {
-	TotalReset = 0 << 11, SelectWindow = 1 << 11, StartCoax = 2 << 11,
-	RxDisable = 3 << 11, RxEnable = 4 << 11, RxReset = 5 << 11,
-	UpStall = 6 << 11, UpUnstall = (6 << 11) + 1, DownStall = (6 << 11) + 2,
-	DownUnstall = (6 << 11) + 3, RxDiscard = 8 << 11, TxEnable = 9 << 11,
-	TxDisable = 10 << 11, TxReset = 11 << 11, FakeIntr = 12 << 11,
-	AckIntr = 13 << 11, SetIntrEnb = 14 << 11, SetStatusEnb = 15 << 11,
-	SetRxFilter = 16 << 11, SetRxThreshold = 17 << 11,
-	SetTxThreshold = 18 << 11, SetTxStart = 19 << 11, StartDMAUp = 20 << 11,
-	StartDMADown = (20 << 11) + 1, StatsEnable = 21 << 11,
-	StatsDisable = 22 << 11, StopCoax = 23 << 11,
-};
-
-/* The SetRxFilter command accepts the following classes: */
-enum RxFilter {
-	RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8
-};
-
-/* Bits in the general status register. */
-enum corkscrew_status {
-	IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004,
-	TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020,
-	IntReq = 0x0040, StatsFull = 0x0080,
-	DMADone = 1 << 8, DownComplete = 1 << 9, UpComplete = 1 << 10,
-	DMAInProgress = 1 << 11,	/* DMA controller is still busy. */
-	CmdInProgress = 1 << 12,	/* EL3_CMD is still busy. */
-};
-
-/* Register window 1 offsets, the window used in normal operation.
-   On the Corkscrew this window is always mapped at offsets 0x10-0x1f. */
-enum Window1 {
-	TX_FIFO = 0x10, RX_FIFO = 0x10, RxErrors = 0x14,
-	RxStatus = 0x18, Timer = 0x1A, TxStatus = 0x1B,
-	TxFree = 0x1C,		/* Remaining free bytes in Tx buffer. */
-};
-enum Window0 {
-	Wn0IRQ = 0x08,
-#if defined(CORKSCREW)
-	Wn0EepromCmd = 0x200A,	/* Corkscrew EEPROM command register. */
-	Wn0EepromData = 0x200C,	/* Corkscrew EEPROM results register. */
-#else
-	Wn0EepromCmd = 10,	/* Window 0: EEPROM command register. */
-	Wn0EepromData = 12,	/* Window 0: EEPROM results register. */
-#endif
-};
-enum Win0_EEPROM_bits {
-	EEPROM_Read = 0x80, EEPROM_WRITE = 0x40, EEPROM_ERASE = 0xC0,
-	EEPROM_EWENB = 0x30,	/* Enable erasing/writing for 10 msec. */
-	EEPROM_EWDIS = 0x00,	/* Disable EWENB before 10 msec timeout. */
-};
-
-/* EEPROM locations. */
-enum eeprom_offset {
-	PhysAddr01 = 0, PhysAddr23 = 1, PhysAddr45 = 2, ModelID = 3,
-	EtherLink3ID = 7,
-};
-
-enum Window3 {			/* Window 3: MAC/config bits. */
-	Wn3_Config = 0, Wn3_MAC_Ctrl = 6, Wn3_Options = 8,
-};
-enum wn3_config {
-	Ram_size = 7,
-	Ram_width = 8,
-	Ram_speed = 0x30,
-	Rom_size = 0xc0,
-	Ram_split_shift = 16,
-	Ram_split = 3 << Ram_split_shift,
-	Xcvr_shift = 20,
-	Xcvr = 7 << Xcvr_shift,
-	Autoselect = 0x1000000,
-};
-
-enum Window4 {
-	Wn4_NetDiag = 6, Wn4_Media = 10,	/* Window 4: Xcvr/media bits. */
-};
-enum Win4_Media_bits {
-	Media_SQE = 0x0008,	/* Enable SQE error counting for AUI. */
-	Media_10TP = 0x00C0,	/* Enable link beat and jabber for 10baseT. */
-	Media_Lnk = 0x0080,	/* Enable just link beat for 100TX/100FX. */
-	Media_LnkBeat = 0x0800,
-};
-enum Window7 {			/* Window 7: Bus Master control. */
-	Wn7_MasterAddr = 0, Wn7_MasterLen = 6, Wn7_MasterStatus = 12,
-};
-
-/* Boomerang-style bus master control registers.  Note ISA aliases! */
-enum MasterCtrl {
-	PktStatus = 0x400, DownListPtr = 0x404, FragAddr = 0x408, FragLen =
-	    0x40c,
-	TxFreeThreshold = 0x40f, UpPktStatus = 0x410, UpListPtr = 0x418,
-};
-
-/* The Rx and Tx descriptor lists.
-   Caution Alpha hackers: these types are 32 bits!  Note also the 8 byte
-   alignment contraint on tx_ring[] and rx_ring[]. */
-struct boom_rx_desc {
-	u32 next;
-	s32 status;
-	u32 addr;
-	s32 length;
-};
-
-/* Values for the Rx status entry. */
-enum rx_desc_status {
-	RxDComplete = 0x00008000, RxDError = 0x4000,
-	/* See boomerang_rx() for actual error bits */
-};
-
-struct boom_tx_desc {
-	u32 next;
-	s32 status;
-	u32 addr;
-	s32 length;
-};
-
-struct corkscrew_private {
-	const char *product_name;
-	struct list_head list;
-	struct net_device *our_dev;
-	/* The Rx and Tx rings are here to keep them quad-word-aligned. */
-	struct boom_rx_desc rx_ring[RX_RING_SIZE];
-	struct boom_tx_desc tx_ring[TX_RING_SIZE];
-	/* The addresses of transmit- and receive-in-place skbuffs. */
-	struct sk_buff *rx_skbuff[RX_RING_SIZE];
-	struct sk_buff *tx_skbuff[TX_RING_SIZE];
-	unsigned int cur_rx, cur_tx;	/* The next free ring entry */
-	unsigned int dirty_rx, dirty_tx;/* The ring entries to be free()ed. */
-	struct sk_buff *tx_skb;	/* Packet being eaten by bus master ctrl.  */
-	struct timer_list timer;	/* Media selection timer. */
-	int capabilities	;	/* Adapter capabilities word. */
-	int options;			/* User-settable misc. driver options. */
-	int last_rx_packets;		/* For media autoselection. */
-	unsigned int available_media:8,	/* From Wn3_Options */
-		media_override:3,	/* Passed-in media type. */
-		default_media:3,	/* Read from the EEPROM. */
-		full_duplex:1, autoselect:1, bus_master:1,	/* Vortex can only do a fragment bus-m. */
-		full_bus_master_tx:1, full_bus_master_rx:1,	/* Boomerang  */
-		tx_full:1;
-	spinlock_t lock;
-	struct device *dev;
-};
-
-/* The action to take with a media selection timer tick.
-   Note that we deviate from the 3Com order by checking 10base2 before AUI.
- */
-enum xcvr_types {
-	XCVR_10baseT = 0, XCVR_AUI, XCVR_10baseTOnly, XCVR_10base2, XCVR_100baseTx,
-	XCVR_100baseFx, XCVR_MII = 6, XCVR_Default = 8,
-};
-
-static struct media_table {
-	char *name;
-	unsigned int media_bits:16,	/* Bits to set in Wn4_Media register. */
-		mask:8,			/* The transceiver-present bit in Wn3_Config. */
-		next:8;			/* The media type to try next. */
-	short wait;			/* Time before we check media status. */
-} media_tbl[] = {
-	{ "10baseT", Media_10TP, 0x08, XCVR_10base2, (14 * HZ) / 10 },
-	{ "10Mbs AUI", Media_SQE, 0x20, XCVR_Default, (1 * HZ) / 10},
-	{ "undefined", 0, 0x80, XCVR_10baseT, 10000},
-	{ "10base2", 0, 0x10, XCVR_AUI, (1 * HZ) / 10},
-	{ "100baseTX", Media_Lnk, 0x02, XCVR_100baseFx, (14 * HZ) / 10},
-	{ "100baseFX", Media_Lnk, 0x04, XCVR_MII, (14 * HZ) / 10},
-	{ "MII", 0, 0x40, XCVR_10baseT, 3 * HZ},
-	{ "undefined", 0, 0x01, XCVR_10baseT, 10000},
-	{ "Default", 0, 0xFF, XCVR_10baseT, 10000},
-};
-
-#ifdef __ISAPNP__
-static struct isapnp_device_id corkscrew_isapnp_adapters[] = {
-	{	ISAPNP_ANY_ID, ISAPNP_ANY_ID,
-		ISAPNP_VENDOR('T', 'C', 'M'), ISAPNP_FUNCTION(0x5051),
-		(long) "3Com Fast EtherLink ISA" },
-	{ }	/* terminate list */
-};
-
-MODULE_DEVICE_TABLE(isapnp, corkscrew_isapnp_adapters);
-
-static int nopnp;
-#endif /* __ISAPNP__ */
-
-static struct net_device *corkscrew_scan(int unit);
-static int corkscrew_setup(struct net_device *dev, int ioaddr,
-			    struct pnp_dev *idev, int card_number);
-static int corkscrew_open(struct net_device *dev);
-static void corkscrew_timer(struct timer_list *t);
-static netdev_tx_t corkscrew_start_xmit(struct sk_buff *skb,
-					struct net_device *dev);
-static int corkscrew_rx(struct net_device *dev);
-static void corkscrew_timeout(struct net_device *dev, unsigned int txqueue);
-static int boomerang_rx(struct net_device *dev);
-static irqreturn_t corkscrew_interrupt(int irq, void *dev_id);
-static int corkscrew_close(struct net_device *dev);
-static void update_stats(int addr, struct net_device *dev);
-static struct net_device_stats *corkscrew_get_stats(struct net_device *dev);
-static void set_rx_mode(struct net_device *dev);
-static const struct ethtool_ops netdev_ethtool_ops;
-
-
-/*
-   Unfortunately maximizing the shared code between the integrated and
-   module version of the driver results in a complicated set of initialization
-   procedures.
-   init_module() -- modules /  tc59x_init()  -- built-in
-		The wrappers for corkscrew_scan()
-   corkscrew_scan()  		 The common routine that scans for PCI and EISA cards
-   corkscrew_found_device() Allocate a device structure when we find a card.
-					Different versions exist for modules and built-in.
-   corkscrew_probe1()		Fill in the device structure -- this is separated
-					so that the modules code can put it in dev->init.
-*/
-/* This driver uses 'options' to pass the media type, full-duplex flag, etc. */
-/* Note: this is the only limit on the number of cards supported!! */
-static int options[MAX_UNITS] = { -1, -1, -1, -1, -1, -1, -1, -1, };
-
-#ifdef MODULE
-static int debug = -1;
-
-module_param(debug, int, 0);
-module_param_array(options, int, NULL, 0);
-module_param(rx_copybreak, int, 0);
-module_param(max_interrupt_work, int, 0);
-MODULE_PARM_DESC(debug, "3c515 debug level (0-6)");
-MODULE_PARM_DESC(options, "3c515: Bits 0-2: media type, bit 3: full duplex, bit 4: bus mastering");
-MODULE_PARM_DESC(rx_copybreak, "3c515 copy breakpoint for copy-only-tiny-frames");
-MODULE_PARM_DESC(max_interrupt_work, "3c515 maximum events handled per interrupt");
-
-/* A list of all installed Vortex devices, for removing the driver module. */
-/* we will need locking (and refcounting) if we ever use it for more */
-static LIST_HEAD(root_corkscrew_dev);
-
-static int corkscrew_init_module(void)
-{
-	int found = 0;
-	if (debug >= 0)
-		corkscrew_debug = debug;
-	while (corkscrew_scan(-1))
-		found++;
-	return found ? 0 : -ENODEV;
-}
-module_init(corkscrew_init_module);
-
-#else
-struct net_device *tc515_probe(int unit)
-{
-	struct net_device *dev = corkscrew_scan(unit);
-
-	if (!dev)
-		return ERR_PTR(-ENODEV);
-
-	return dev;
-}
-#endif				/* not MODULE */
-
-static int check_device(unsigned ioaddr)
-{
-	int timer;
-
-	if (!request_region(ioaddr, CORKSCREW_TOTAL_SIZE, "3c515"))
-		return 0;
-	/* Check the resource configuration for a matching ioaddr. */
-	if ((inw(ioaddr + 0x2002) & 0x1f0) != (ioaddr & 0x1f0)) {
-		release_region(ioaddr, CORKSCREW_TOTAL_SIZE);
-		return 0;
-	}
-	/* Verify by reading the device ID from the EEPROM. */
-	outw(EEPROM_Read + 7, ioaddr + Wn0EepromCmd);
-	/* Pause for at least 162 us. for the read to take place. */
-	for (timer = 4; timer >= 0; timer--) {
-		udelay(162);
-		if ((inw(ioaddr + Wn0EepromCmd) & 0x0200) == 0)
-			break;
-	}
-	if (inw(ioaddr + Wn0EepromData) != 0x6d50) {
-		release_region(ioaddr, CORKSCREW_TOTAL_SIZE);
-		return 0;
-	}
-	return 1;
-}
-
-static void cleanup_card(struct net_device *dev)
-{
-	struct corkscrew_private *vp = netdev_priv(dev);
-	list_del_init(&vp->list);
-	if (dev->dma)
-		free_dma(dev->dma);
-	outw(TotalReset, dev->base_addr + EL3_CMD);
-	release_region(dev->base_addr, CORKSCREW_TOTAL_SIZE);
-	if (vp->dev)
-		pnp_device_detach(to_pnp_dev(vp->dev));
-}
-
-static struct net_device *corkscrew_scan(int unit)
-{
-	struct net_device *dev;
-	static int cards_found = 0;
-	static int ioaddr;
-	int err;
-#ifdef __ISAPNP__
-	short i;
-	static int pnp_cards;
-#endif
-
-	dev = alloc_etherdev(sizeof(struct corkscrew_private));
-	if (!dev)
-		return ERR_PTR(-ENOMEM);
-
-	if (unit >= 0) {
-		sprintf(dev->name, "eth%d", unit);
-		netdev_boot_setup_check(dev);
-	}
-
-#ifdef __ISAPNP__
-	if(nopnp == 1)
-		goto no_pnp;
-	for(i=0; corkscrew_isapnp_adapters[i].vendor != 0; i++) {
-		struct pnp_dev *idev = NULL;
-		int irq;
-		while((idev = pnp_find_dev(NULL,
-					   corkscrew_isapnp_adapters[i].vendor,
-					   corkscrew_isapnp_adapters[i].function,
-					   idev))) {
-
-			if (pnp_device_attach(idev) < 0)
-				continue;
-			if (pnp_activate_dev(idev) < 0) {
-				pr_warn("pnp activate failed (out of resources?)\n");
-				pnp_device_detach(idev);
-				continue;
-			}
-			if (!pnp_port_valid(idev, 0) || !pnp_irq_valid(idev, 0)) {
-				pnp_device_detach(idev);
-				continue;
-			}
-			ioaddr = pnp_port_start(idev, 0);
-			irq = pnp_irq(idev, 0);
-			if (!check_device(ioaddr)) {
-				pnp_device_detach(idev);
-				continue;
-			}
-			if(corkscrew_debug)
-				pr_debug("ISAPNP reports %s at i/o 0x%x, irq %d\n",
-					(char*) corkscrew_isapnp_adapters[i].driver_data, ioaddr, irq);
-			pr_info("3c515 Resource configuration register %#4.4x, DCR %4.4x.\n",
-		     		inl(ioaddr + 0x2002), inw(ioaddr + 0x2000));
-			/* irq = inw(ioaddr + 0x2002) & 15; */ /* Use the irq from isapnp */
-			SET_NETDEV_DEV(dev, &idev->dev);
-			pnp_cards++;
-			err = corkscrew_setup(dev, ioaddr, idev, cards_found++);
-			if (!err)
-				return dev;
-			cleanup_card(dev);
-		}
-	}
-no_pnp:
-#endif /* __ISAPNP__ */
-
-	/* Check all locations on the ISA bus -- evil! */
-	for (ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x20) {
-		if (!check_device(ioaddr))
-			continue;
-
-		pr_info("3c515 Resource configuration register %#4.4x, DCR %4.4x.\n",
-		     inl(ioaddr + 0x2002), inw(ioaddr + 0x2000));
-		err = corkscrew_setup(dev, ioaddr, NULL, cards_found++);
-		if (!err)
-			return dev;
-		cleanup_card(dev);
-	}
-	free_netdev(dev);
-	return NULL;
-}
-
-
-static const struct net_device_ops netdev_ops = {
-	.ndo_open		= corkscrew_open,
-	.ndo_stop		= corkscrew_close,
-	.ndo_start_xmit		= corkscrew_start_xmit,
-	.ndo_tx_timeout		= corkscrew_timeout,
-	.ndo_get_stats		= corkscrew_get_stats,
-	.ndo_set_rx_mode	= set_rx_mode,
-	.ndo_set_mac_address 	= eth_mac_addr,
-	.ndo_validate_addr	= eth_validate_addr,
-};
-
-
-static int corkscrew_setup(struct net_device *dev, int ioaddr,
-			    struct pnp_dev *idev, int card_number)
-{
-	struct corkscrew_private *vp = netdev_priv(dev);
-	unsigned int eeprom[0x40], checksum = 0;	/* EEPROM contents */
-	__be16 addr[ETH_ALEN / 2];
-	int i;
-	int irq;
-
-#ifdef __ISAPNP__
-	if (idev) {
-		irq = pnp_irq(idev, 0);
-		vp->dev = &idev->dev;
-	} else {
-		irq = inw(ioaddr + 0x2002) & 15;
-	}
-#else
-	irq = inw(ioaddr + 0x2002) & 15;
-#endif
-
-	dev->base_addr = ioaddr;
-	dev->irq = irq;
-	dev->dma = inw(ioaddr + 0x2000) & 7;
-	vp->product_name = "3c515";
-	vp->options = dev->mem_start;
-	vp->our_dev = dev;
-
-	if (!vp->options) {
-		 if (card_number >= MAX_UNITS)
-			vp->options = -1;
-		else
-			vp->options = options[card_number];
-	}
-
-	if (vp->options >= 0) {
-		vp->media_override = vp->options & 7;
-		if (vp->media_override == 2)
-			vp->media_override = 0;
-		vp->full_duplex = (vp->options & 8) ? 1 : 0;
-		vp->bus_master = (vp->options & 16) ? 1 : 0;
-	} else {
-		vp->media_override = 7;
-		vp->full_duplex = 0;
-		vp->bus_master = 0;
-	}
-#ifdef MODULE
-	list_add(&vp->list, &root_corkscrew_dev);
-#endif
-
-	pr_info("%s: 3Com %s at %#3x,", dev->name, vp->product_name, ioaddr);
-
-	spin_lock_init(&vp->lock);
-
-	timer_setup(&vp->timer, corkscrew_timer, 0);
-
-	/* Read the station address from the EEPROM. */
-	EL3WINDOW(0);
-	for (i = 0; i < 0x18; i++) {
-		int timer;
-		outw(EEPROM_Read + i, ioaddr + Wn0EepromCmd);
-		/* Pause for at least 162 us. for the read to take place. */
-		for (timer = 4; timer >= 0; timer--) {
-			udelay(162);
-			if ((inw(ioaddr + Wn0EepromCmd) & 0x0200) == 0)
-				break;
-		}
-		eeprom[i] = inw(ioaddr + Wn0EepromData);
-		checksum ^= eeprom[i];
-		if (i < 3)
-			addr[i] = htons(eeprom[i]);
-	}
-	eth_hw_addr_set(dev, (u8 *)addr);
-	checksum = (checksum ^ (checksum >> 8)) & 0xff;
-	if (checksum != 0x00)
-		pr_cont(" ***INVALID CHECKSUM %4.4x*** ", checksum);
-	pr_cont(" %pM", dev->dev_addr);
-	if (eeprom[16] == 0x11c7) {	/* Corkscrew */
-		if (request_dma(dev->dma, "3c515")) {
-			pr_cont(", DMA %d allocation failed", dev->dma);
-			dev->dma = 0;
-		} else
-			pr_cont(", DMA %d", dev->dma);
-	}
-	pr_cont(", IRQ %d\n", dev->irq);
-	/* Tell them about an invalid IRQ. */
-	if (corkscrew_debug && (dev->irq <= 0 || dev->irq > 15))
-		pr_warn(" *** Warning: this IRQ is unlikely to work! ***\n");
-
-	{
-		static const char * const ram_split[] = {
-			"5:3", "3:1", "1:1", "3:5"
-		};
-		__u32 config;
-		EL3WINDOW(3);
-		vp->available_media = inw(ioaddr + Wn3_Options);
-		config = inl(ioaddr + Wn3_Config);
-		if (corkscrew_debug > 1)
-			pr_info("  Internal config register is %4.4x, transceivers %#x.\n",
-				config, inw(ioaddr + Wn3_Options));
-		pr_info("  %dK %s-wide RAM %s Rx:Tx split, %s%s interface.\n",
-			8 << config & Ram_size,
-			config & Ram_width ? "word" : "byte",
-			ram_split[(config & Ram_split) >> Ram_split_shift],
-			config & Autoselect ? "autoselect/" : "",
-			media_tbl[(config & Xcvr) >> Xcvr_shift].name);
-		vp->default_media = (config & Xcvr) >> Xcvr_shift;
-		vp->autoselect = config & Autoselect ? 1 : 0;
-		dev->if_port = vp->default_media;
-	}
-	if (vp->media_override != 7) {
-		pr_info("  Media override to transceiver type %d (%s).\n",
-		       vp->media_override,
-		       media_tbl[vp->media_override].name);
-		dev->if_port = vp->media_override;
-	}
-
-	vp->capabilities = eeprom[16];
-	vp->full_bus_master_tx = (vp->capabilities & 0x20) ? 1 : 0;
-	/* Rx is broken at 10mbps, so we always disable it. */
-	/* vp->full_bus_master_rx = 0; */
-	vp->full_bus_master_rx = (vp->capabilities & 0x20) ? 1 : 0;
-
-	/* The 3c51x-specific entries in the device structure. */
-	dev->netdev_ops = &netdev_ops;
-	dev->watchdog_timeo = (400 * HZ) / 1000;
-	dev->ethtool_ops = &netdev_ethtool_ops;
-
-	return register_netdev(dev);
-}
-
-
-static int corkscrew_open(struct net_device *dev)
-{
-	int ioaddr = dev->base_addr;
-	struct corkscrew_private *vp = netdev_priv(dev);
-	bool armtimer = false;
-	__u32 config;
-	int i;
-
-	/* Before initializing select the active media port. */
-	EL3WINDOW(3);
-	if (vp->full_duplex)
-		outb(0x20, ioaddr + Wn3_MAC_Ctrl);	/* Set the full-duplex bit. */
-	config = inl(ioaddr + Wn3_Config);
-
-	if (vp->media_override != 7) {
-		if (corkscrew_debug > 1)
-			pr_info("%s: Media override to transceiver %d (%s).\n",
-				dev->name, vp->media_override,
-				media_tbl[vp->media_override].name);
-		dev->if_port = vp->media_override;
-	} else if (vp->autoselect) {
-		/* Find first available media type, starting with 100baseTx. */
-		dev->if_port = 4;
-		while (!(vp->available_media & media_tbl[dev->if_port].mask))
-			dev->if_port = media_tbl[dev->if_port].next;
-
-		if (corkscrew_debug > 1)
-			pr_debug("%s: Initial media type %s.\n",
-			       dev->name, media_tbl[dev->if_port].name);
-		armtimer = true;
-	} else
-		dev->if_port = vp->default_media;
-
-	config = (config & ~Xcvr) | (dev->if_port << Xcvr_shift);
-	outl(config, ioaddr + Wn3_Config);
-
-	if (corkscrew_debug > 1) {
-		pr_debug("%s: corkscrew_open() InternalConfig %8.8x.\n",
-		       dev->name, config);
-	}
-
-	outw(TxReset, ioaddr + EL3_CMD);
-	for (i = 20; i >= 0; i--)
-		if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress))
-			break;
-
-	outw(RxReset, ioaddr + EL3_CMD);
-	/* Wait a few ticks for the RxReset command to complete. */
-	for (i = 20; i >= 0; i--)
-		if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress))
-			break;
-
-	outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
-
-	/* Use the now-standard shared IRQ implementation. */
-	if (vp->capabilities == 0x11c7) {
-		/* Corkscrew: Cannot share ISA resources. */
-		if (dev->irq == 0 ||
-		    dev->dma == 0 ||
-		    request_irq(dev->irq, corkscrew_interrupt, 0,
-				vp->product_name, dev))
-			return -EAGAIN;
-		enable_dma(dev->dma);
-		set_dma_mode(dev->dma, DMA_MODE_CASCADE);
-	} else if (request_irq(dev->irq, corkscrew_interrupt, IRQF_SHARED,
-			       vp->product_name, dev)) {
-		return -EAGAIN;
-	}
-
-	if (armtimer)
-		mod_timer(&vp->timer, jiffies + media_tbl[dev->if_port].wait);
-
-	if (corkscrew_debug > 1) {
-		EL3WINDOW(4);
-		pr_debug("%s: corkscrew_open() irq %d media status %4.4x.\n",
-		       dev->name, dev->irq, inw(ioaddr + Wn4_Media));
-	}
-
-	/* Set the station address and mask in window 2 each time opened. */
-	EL3WINDOW(2);
-	for (i = 0; i < 6; i++)
-		outb(dev->dev_addr[i], ioaddr + i);
-	for (; i < 12; i += 2)
-		outw(0, ioaddr + i);
-
-	if (dev->if_port == 3)
-		/* Start the thinnet transceiver. We should really wait 50ms... */
-		outw(StartCoax, ioaddr + EL3_CMD);
-	EL3WINDOW(4);
-	outw((inw(ioaddr + Wn4_Media) & ~(Media_10TP | Media_SQE)) |
-	     media_tbl[dev->if_port].media_bits, ioaddr + Wn4_Media);
-
-	/* Switch to the stats window, and clear all stats by reading. */
-	outw(StatsDisable, ioaddr + EL3_CMD);
-	EL3WINDOW(6);
-	for (i = 0; i < 10; i++)
-		inb(ioaddr + i);
-	inw(ioaddr + 10);
-	inw(ioaddr + 12);
-	/* New: On the Vortex we must also clear the BadSSD counter. */
-	EL3WINDOW(4);
-	inb(ioaddr + 12);
-	/* ..and on the Boomerang we enable the extra statistics bits. */
-	outw(0x0040, ioaddr + Wn4_NetDiag);
-
-	/* Switch to register set 7 for normal use. */
-	EL3WINDOW(7);
-
-	if (vp->full_bus_master_rx) {	/* Boomerang bus master. */
-		vp->cur_rx = vp->dirty_rx = 0;
-		if (corkscrew_debug > 2)
-			pr_debug("%s:  Filling in the Rx ring.\n", dev->name);
-		for (i = 0; i < RX_RING_SIZE; i++) {
-			struct sk_buff *skb;
-			if (i < (RX_RING_SIZE - 1))
-				vp->rx_ring[i].next =
-				    isa_virt_to_bus(&vp->rx_ring[i + 1]);
-			else
-				vp->rx_ring[i].next = 0;
-			vp->rx_ring[i].status = 0;	/* Clear complete bit. */
-			vp->rx_ring[i].length = PKT_BUF_SZ | 0x80000000;
-			skb = netdev_alloc_skb(dev, PKT_BUF_SZ);
-			vp->rx_skbuff[i] = skb;
-			if (skb == NULL)
-				break;	/* Bad news!  */
-			skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
-			vp->rx_ring[i].addr = isa_virt_to_bus(skb->data);
-		}
-		if (i != 0)
-			vp->rx_ring[i - 1].next =
-				isa_virt_to_bus(&vp->rx_ring[0]);	/* Wrap the ring. */
-		outl(isa_virt_to_bus(&vp->rx_ring[0]), ioaddr + UpListPtr);
-	}
-	if (vp->full_bus_master_tx) {	/* Boomerang bus master Tx. */
-		vp->cur_tx = vp->dirty_tx = 0;
-		outb(PKT_BUF_SZ >> 8, ioaddr + TxFreeThreshold);	/* Room for a packet. */
-		/* Clear the Tx ring. */
-		for (i = 0; i < TX_RING_SIZE; i++)
-			vp->tx_skbuff[i] = NULL;
-		outl(0, ioaddr + DownListPtr);
-	}
-	/* Set receiver mode: presumably accept b-case and phys addr only. */
-	set_rx_mode(dev);
-	outw(StatsEnable, ioaddr + EL3_CMD);	/* Turn on statistics. */
-
-	netif_start_queue(dev);
-
-	outw(RxEnable, ioaddr + EL3_CMD);	/* Enable the receiver. */
-	outw(TxEnable, ioaddr + EL3_CMD);	/* Enable transmitter. */
-	/* Allow status bits to be seen. */
-	outw(SetStatusEnb | AdapterFailure | IntReq | StatsFull |
-	     (vp->full_bus_master_tx ? DownComplete : TxAvailable) |
-	     (vp->full_bus_master_rx ? UpComplete : RxComplete) |
-	     (vp->bus_master ? DMADone : 0), ioaddr + EL3_CMD);
-	/* Ack all pending events, and set active indicator mask. */
-	outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
-	     ioaddr + EL3_CMD);
-	outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull
-	     | (vp->bus_master ? DMADone : 0) | UpComplete | DownComplete,
-	     ioaddr + EL3_CMD);
-
-	return 0;
-}
-
-static void corkscrew_timer(struct timer_list *t)
-{
-#ifdef AUTOMEDIA
-	struct corkscrew_private *vp = timer_container_of(vp, t, timer);
-	struct net_device *dev = vp->our_dev;
-	int ioaddr = dev->base_addr;
-	unsigned long flags;
-	int ok = 0;
-
-	if (corkscrew_debug > 1)
-		pr_debug("%s: Media selection timer tick happened, %s.\n",
-		       dev->name, media_tbl[dev->if_port].name);
-
-	spin_lock_irqsave(&vp->lock, flags);
-
-	{
-		int old_window = inw(ioaddr + EL3_CMD) >> 13;
-		int media_status;
-		EL3WINDOW(4);
-		media_status = inw(ioaddr + Wn4_Media);
-		switch (dev->if_port) {
-		case 0:
-		case 4:
-		case 5:	/* 10baseT, 100baseTX, 100baseFX  */
-			if (media_status & Media_LnkBeat) {
-				ok = 1;
-				if (corkscrew_debug > 1)
-					pr_debug("%s: Media %s has link beat, %x.\n",
-						dev->name,
-						media_tbl[dev->if_port].name,
-						media_status);
-			} else if (corkscrew_debug > 1)
-				pr_debug("%s: Media %s is has no link beat, %x.\n",
-					dev->name,
-					media_tbl[dev->if_port].name,
-					media_status);
-
-			break;
-		default:	/* Other media types handled by Tx timeouts. */
-			if (corkscrew_debug > 1)
-				pr_debug("%s: Media %s is has no indication, %x.\n",
-					dev->name,
-					media_tbl[dev->if_port].name,
-					media_status);
-			ok = 1;
-		}
-		if (!ok) {
-			__u32 config;
-
-			do {
-				dev->if_port =
-				    media_tbl[dev->if_port].next;
-			}
-			while (!(vp->available_media & media_tbl[dev->if_port].mask));
-
-			if (dev->if_port == 8) {	/* Go back to default. */
-				dev->if_port = vp->default_media;
-				if (corkscrew_debug > 1)
-					pr_debug("%s: Media selection failing, using default %s port.\n",
-						dev->name,
-						media_tbl[dev->if_port].name);
-			} else {
-				if (corkscrew_debug > 1)
-					pr_debug("%s: Media selection failed, now trying %s port.\n",
-						dev->name,
-						media_tbl[dev->if_port].name);
-				vp->timer.expires = jiffies + media_tbl[dev->if_port].wait;
-				add_timer(&vp->timer);
-			}
-			outw((media_status & ~(Media_10TP | Media_SQE)) |
-			     media_tbl[dev->if_port].media_bits,
-			     ioaddr + Wn4_Media);
-
-			EL3WINDOW(3);
-			config = inl(ioaddr + Wn3_Config);
-			config = (config & ~Xcvr) | (dev->if_port << Xcvr_shift);
-			outl(config, ioaddr + Wn3_Config);
-
-			outw(dev->if_port == 3 ? StartCoax : StopCoax,
-			     ioaddr + EL3_CMD);
-		}
-		EL3WINDOW(old_window);
-	}
-
-	spin_unlock_irqrestore(&vp->lock, flags);
-	if (corkscrew_debug > 1)
-		pr_debug("%s: Media selection timer finished, %s.\n",
-		       dev->name, media_tbl[dev->if_port].name);
-
-#endif				/* AUTOMEDIA */
-}
-
-static void corkscrew_timeout(struct net_device *dev, unsigned int txqueue)
-{
-	int i;
-	struct corkscrew_private *vp = netdev_priv(dev);
-	int ioaddr = dev->base_addr;
-
-	pr_warn("%s: transmit timed out, tx_status %2.2x status %4.4x\n",
-		dev->name, inb(ioaddr + TxStatus),
-		inw(ioaddr + EL3_STATUS));
-	/* Slight code bloat to be user friendly. */
-	if ((inb(ioaddr + TxStatus) & 0x88) == 0x88)
-		pr_warn("%s: Transmitter encountered 16 collisions -- network cable problem?\n",
-			dev->name);
-#ifndef final_version
-	pr_debug("  Flags; bus-master %d, full %d; dirty %d current %d.\n",
-	       vp->full_bus_master_tx, vp->tx_full, vp->dirty_tx,
-	       vp->cur_tx);
-	pr_debug("  Down list %8.8x vs. %p.\n", inl(ioaddr + DownListPtr),
-	       &vp->tx_ring[0]);
-	for (i = 0; i < TX_RING_SIZE; i++) {
-		pr_debug("  %d: %p  length %8.8x status %8.8x\n", i,
-		       &vp->tx_ring[i],
-		       vp->tx_ring[i].length, vp->tx_ring[i].status);
-	}
-#endif
-	/* Issue TX_RESET and TX_START commands. */
-	outw(TxReset, ioaddr + EL3_CMD);
-	for (i = 20; i >= 0; i--)
-		if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress))
-			break;
-	outw(TxEnable, ioaddr + EL3_CMD);
-	netif_trans_update(dev); /* prevent tx timeout */
-	dev->stats.tx_errors++;
-	dev->stats.tx_dropped++;
-	netif_wake_queue(dev);
-}
-
-static netdev_tx_t corkscrew_start_xmit(struct sk_buff *skb,
-					struct net_device *dev)
-{
-	struct corkscrew_private *vp = netdev_priv(dev);
-	int ioaddr = dev->base_addr;
-
-	/* Block a timer-based transmit from overlapping. */
-
-	netif_stop_queue(dev);
-
-	if (vp->full_bus_master_tx) {	/* BOOMERANG bus-master */
-		/* Calculate the next Tx descriptor entry. */
-		int entry = vp->cur_tx % TX_RING_SIZE;
-		struct boom_tx_desc *prev_entry;
-		unsigned long flags;
-		int i;
-
-		if (vp->tx_full)	/* No room to transmit with */
-			return NETDEV_TX_BUSY;
-		if (vp->cur_tx != 0)
-			prev_entry = &vp->tx_ring[(vp->cur_tx - 1) % TX_RING_SIZE];
-		else
-			prev_entry = NULL;
-		if (corkscrew_debug > 3)
-			pr_debug("%s: Trying to send a packet, Tx index %d.\n",
-				dev->name, vp->cur_tx);
-		/* vp->tx_full = 1; */
-		vp->tx_skbuff[entry] = skb;
-		vp->tx_ring[entry].next = 0;
-		vp->tx_ring[entry].addr = isa_virt_to_bus(skb->data);
-		vp->tx_ring[entry].length = skb->len | 0x80000000;
-		vp->tx_ring[entry].status = skb->len | 0x80000000;
-
-		spin_lock_irqsave(&vp->lock, flags);
-		outw(DownStall, ioaddr + EL3_CMD);
-		/* Wait for the stall to complete. */
-		for (i = 20; i >= 0; i--)
-			if ((inw(ioaddr + EL3_STATUS) & CmdInProgress) == 0)
-				break;
-		if (prev_entry)
-			prev_entry->next = isa_virt_to_bus(&vp->tx_ring[entry]);
-		if (inl(ioaddr + DownListPtr) == 0) {
-			outl(isa_virt_to_bus(&vp->tx_ring[entry]),
-			     ioaddr + DownListPtr);
-			queued_packet++;
-		}
-		outw(DownUnstall, ioaddr + EL3_CMD);
-		spin_unlock_irqrestore(&vp->lock, flags);
-
-		vp->cur_tx++;
-		if (vp->cur_tx - vp->dirty_tx > TX_RING_SIZE - 1)
-			vp->tx_full = 1;
-		else {		/* Clear previous interrupt enable. */
-			if (prev_entry)
-				prev_entry->status &= ~0x80000000;
-			netif_wake_queue(dev);
-		}
-		return NETDEV_TX_OK;
-	}
-	/* Put out the doubleword header... */
-	outl(skb->len, ioaddr + TX_FIFO);
-	dev->stats.tx_bytes += skb->len;
-#ifdef VORTEX_BUS_MASTER
-	if (vp->bus_master) {
-		/* Set the bus-master controller to transfer the packet. */
-		outl(isa_virt_to_bus(skb->data), ioaddr + Wn7_MasterAddr);
-		outw((skb->len + 3) & ~3, ioaddr + Wn7_MasterLen);
-		vp->tx_skb = skb;
-		outw(StartDMADown, ioaddr + EL3_CMD);
-		/* queue will be woken at the DMADone interrupt. */
-	} else {
-		/* ... and the packet rounded to a doubleword. */
-		outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
-		dev_kfree_skb(skb);
-		if (inw(ioaddr + TxFree) > 1536) {
-			netif_wake_queue(dev);
-		} else
-			/* Interrupt us when the FIFO has room for max-sized packet. */
-			outw(SetTxThreshold + (1536 >> 2),
-			     ioaddr + EL3_CMD);
-	}
-#else
-	/* ... and the packet rounded to a doubleword. */
-	outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
-	dev_kfree_skb(skb);
-	if (inw(ioaddr + TxFree) > 1536) {
-		netif_wake_queue(dev);
-	} else
-		/* Interrupt us when the FIFO has room for max-sized packet. */
-		outw(SetTxThreshold + (1536 >> 2), ioaddr + EL3_CMD);
-#endif				/* bus master */
-
-
-	/* Clear the Tx status stack. */
-	{
-		short tx_status;
-		int i = 4;
-
-		while (--i > 0 && (tx_status = inb(ioaddr + TxStatus)) > 0) {
-			if (tx_status & 0x3C) {	/* A Tx-disabling error occurred.  */
-				if (corkscrew_debug > 2)
-					pr_debug("%s: Tx error, status %2.2x.\n",
-						dev->name, tx_status);
-				if (tx_status & 0x04)
-					dev->stats.tx_fifo_errors++;
-				if (tx_status & 0x38)
-					dev->stats.tx_aborted_errors++;
-				if (tx_status & 0x30) {
-					int j;
-					outw(TxReset, ioaddr + EL3_CMD);
-					for (j = 20; j >= 0; j--)
-						if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress))
-							break;
-				}
-				outw(TxEnable, ioaddr + EL3_CMD);
-			}
-			outb(0x00, ioaddr + TxStatus);	/* Pop the status stack. */
-		}
-	}
-	return NETDEV_TX_OK;
-}
-
-/* The interrupt handler does all of the Rx thread work and cleans up
-   after the Tx thread. */
-
-static irqreturn_t corkscrew_interrupt(int irq, void *dev_id)
-{
-	/* Use the now-standard shared IRQ implementation. */
-	struct net_device *dev = dev_id;
-	struct corkscrew_private *lp = netdev_priv(dev);
-	int ioaddr, status;
-	int latency;
-	int i = max_interrupt_work;
-
-	ioaddr = dev->base_addr;
-	latency = inb(ioaddr + Timer);
-
-	spin_lock(&lp->lock);
-
-	status = inw(ioaddr + EL3_STATUS);
-
-	if (corkscrew_debug > 4)
-		pr_debug("%s: interrupt, status %4.4x, timer %d.\n",
-			dev->name, status, latency);
-	if ((status & 0xE000) != 0xE000) {
-		static int donedidthis;
-		/* Some interrupt controllers store a bogus interrupt from boot-time.
-		   Ignore a single early interrupt, but don't hang the machine for
-		   other interrupt problems. */
-		if (donedidthis++ > 100) {
-			pr_err("%s: Bogus interrupt, bailing. Status %4.4x, start=%d.\n",
-				   dev->name, status, netif_running(dev));
-			free_irq(dev->irq, dev);
-			dev->irq = -1;
-		}
-	}
-
-	do {
-		if (corkscrew_debug > 5)
-			pr_debug("%s: In interrupt loop, status %4.4x.\n",
-			       dev->name, status);
-		if (status & RxComplete)
-			corkscrew_rx(dev);
-
-		if (status & TxAvailable) {
-			if (corkscrew_debug > 5)
-				pr_debug("	TX room bit was handled.\n");
-			/* There's room in the FIFO for a full-sized packet. */
-			outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
-			netif_wake_queue(dev);
-		}
-		if (status & DownComplete) {
-			unsigned int dirty_tx = lp->dirty_tx;
-
-			while (lp->cur_tx - dirty_tx > 0) {
-				int entry = dirty_tx % TX_RING_SIZE;
-				if (inl(ioaddr + DownListPtr) == isa_virt_to_bus(&lp->tx_ring[entry]))
-					break;	/* It still hasn't been processed. */
-				if (lp->tx_skbuff[entry]) {
-					dev_consume_skb_irq(lp->tx_skbuff[entry]);
-					lp->tx_skbuff[entry] = NULL;
-				}
-				dirty_tx++;
-			}
-			lp->dirty_tx = dirty_tx;
-			outw(AckIntr | DownComplete, ioaddr + EL3_CMD);
-			if (lp->tx_full && (lp->cur_tx - dirty_tx <= TX_RING_SIZE - 1)) {
-				lp->tx_full = 0;
-				netif_wake_queue(dev);
-			}
-		}
-#ifdef VORTEX_BUS_MASTER
-		if (status & DMADone) {
-			outw(0x1000, ioaddr + Wn7_MasterStatus);	/* Ack the event. */
-			dev_consume_skb_irq(lp->tx_skb);	/* Release the transferred buffer */
-			netif_wake_queue(dev);
-		}
-#endif
-		if (status & UpComplete) {
-			boomerang_rx(dev);
-			outw(AckIntr | UpComplete, ioaddr + EL3_CMD);
-		}
-		if (status & (AdapterFailure | RxEarly | StatsFull)) {
-			/* Handle all uncommon interrupts at once. */
-			if (status & RxEarly) {	/* Rx early is unused. */
-				corkscrew_rx(dev);
-				outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
-			}
-			if (status & StatsFull) {	/* Empty statistics. */
-				static int DoneDidThat;
-				if (corkscrew_debug > 4)
-					pr_debug("%s: Updating stats.\n", dev->name);
-				update_stats(ioaddr, dev);
-				/* DEBUG HACK: Disable statistics as an interrupt source. */
-				/* This occurs when we have the wrong media type! */
-				if (DoneDidThat == 0 && inw(ioaddr + EL3_STATUS) & StatsFull) {
-					int win, reg;
-					pr_notice("%s: Updating stats failed, disabling stats as an interrupt source.\n",
-						dev->name);
-					for (win = 0; win < 8; win++) {
-						EL3WINDOW(win);
-						pr_notice("Vortex window %d:", win);
-						for (reg = 0; reg < 16; reg++)
-							pr_cont(" %2.2x", inb(ioaddr + reg));
-						pr_cont("\n");
-					}
-					EL3WINDOW(7);
-					outw(SetIntrEnb | TxAvailable |
-					     RxComplete | AdapterFailure |
-					     UpComplete | DownComplete |
-					     TxComplete, ioaddr + EL3_CMD);
-					DoneDidThat++;
-				}
-			}
-			if (status & AdapterFailure) {
-				/* Adapter failure requires Rx reset and reinit. */
-				outw(RxReset, ioaddr + EL3_CMD);
-				/* Set the Rx filter to the current state. */
-				set_rx_mode(dev);
-				outw(RxEnable, ioaddr + EL3_CMD);	/* Re-enable the receiver. */
-				outw(AckIntr | AdapterFailure,
-				     ioaddr + EL3_CMD);
-			}
-		}
-
-		if (--i < 0) {
-			pr_err("%s: Too much work in interrupt, status %4.4x. Disabling functions (%4.4x).\n",
-				dev->name, status, SetStatusEnb | ((~status) & 0x7FE));
-			/* Disable all pending interrupts. */
-			outw(SetStatusEnb | ((~status) & 0x7FE), ioaddr + EL3_CMD);
-			outw(AckIntr | 0x7FF, ioaddr + EL3_CMD);
-			break;
-		}
-		/* Acknowledge the IRQ. */
-		outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
-
-	} while ((status = inw(ioaddr + EL3_STATUS)) & (IntLatch | RxComplete));
-
-	spin_unlock(&lp->lock);
-
-	if (corkscrew_debug > 4)
-		pr_debug("%s: exiting interrupt, status %4.4x.\n", dev->name, status);
-	return IRQ_HANDLED;
-}
-
-static int corkscrew_rx(struct net_device *dev)
-{
-	int ioaddr = dev->base_addr;
-	int i;
-	short rx_status;
-
-	if (corkscrew_debug > 5)
-		pr_debug("   In rx_packet(), status %4.4x, rx_status %4.4x.\n",
-		     inw(ioaddr + EL3_STATUS), inw(ioaddr + RxStatus));
-	while ((rx_status = inw(ioaddr + RxStatus)) > 0) {
-		if (rx_status & 0x4000) {	/* Error, update stats. */
-			unsigned char rx_error = inb(ioaddr + RxErrors);
-			if (corkscrew_debug > 2)
-				pr_debug(" Rx error: status %2.2x.\n",
-				       rx_error);
-			dev->stats.rx_errors++;
-			if (rx_error & 0x01)
-				dev->stats.rx_over_errors++;
-			if (rx_error & 0x02)
-				dev->stats.rx_length_errors++;
-			if (rx_error & 0x04)
-				dev->stats.rx_frame_errors++;
-			if (rx_error & 0x08)
-				dev->stats.rx_crc_errors++;
-			if (rx_error & 0x10)
-				dev->stats.rx_length_errors++;
-		} else {
-			/* The packet length: up to 4.5K!. */
-			short pkt_len = rx_status & 0x1fff;
-			struct sk_buff *skb;
-
-			skb = netdev_alloc_skb(dev, pkt_len + 5 + 2);
-			if (corkscrew_debug > 4)
-				pr_debug("Receiving packet size %d status %4.4x.\n",
-				     pkt_len, rx_status);
-			if (skb != NULL) {
-				skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
-				/* 'skb_put()' points to the start of sk_buff data area. */
-				insl(ioaddr + RX_FIFO,
-				     skb_put(skb, pkt_len),
-				     (pkt_len + 3) >> 2);
-				outw(RxDiscard, ioaddr + EL3_CMD);	/* Pop top Rx packet. */
-				skb->protocol = eth_type_trans(skb, dev);
-				netif_rx(skb);
-				dev->stats.rx_packets++;
-				dev->stats.rx_bytes += pkt_len;
-				/* Wait a limited time to go to next packet. */
-				for (i = 200; i >= 0; i--)
-					if (! (inw(ioaddr + EL3_STATUS) & CmdInProgress))
-						break;
-				continue;
-			} else if (corkscrew_debug)
-				pr_debug("%s: Couldn't allocate a sk_buff of size %d.\n", dev->name, pkt_len);
-		}
-		outw(RxDiscard, ioaddr + EL3_CMD);
-		dev->stats.rx_dropped++;
-		/* Wait a limited time to skip this packet. */
-		for (i = 200; i >= 0; i--)
-			if (!(inw(ioaddr + EL3_STATUS) & CmdInProgress))
-				break;
-	}
-	return 0;
-}
-
-static int boomerang_rx(struct net_device *dev)
-{
-	struct corkscrew_private *vp = netdev_priv(dev);
-	int entry = vp->cur_rx % RX_RING_SIZE;
-	int ioaddr = dev->base_addr;
-	int rx_status;
-
-	if (corkscrew_debug > 5)
-		pr_debug("   In boomerang_rx(), status %4.4x, rx_status %4.4x.\n",
-			inw(ioaddr + EL3_STATUS), inw(ioaddr + RxStatus));
-	while ((rx_status = vp->rx_ring[entry].status) & RxDComplete) {
-		if (rx_status & RxDError) {	/* Error, update stats. */
-			unsigned char rx_error = rx_status >> 16;
-			if (corkscrew_debug > 2)
-				pr_debug(" Rx error: status %2.2x.\n",
-				       rx_error);
-			dev->stats.rx_errors++;
-			if (rx_error & 0x01)
-				dev->stats.rx_over_errors++;
-			if (rx_error & 0x02)
-				dev->stats.rx_length_errors++;
-			if (rx_error & 0x04)
-				dev->stats.rx_frame_errors++;
-			if (rx_error & 0x08)
-				dev->stats.rx_crc_errors++;
-			if (rx_error & 0x10)
-				dev->stats.rx_length_errors++;
-		} else {
-			/* The packet length: up to 4.5K!. */
-			short pkt_len = rx_status & 0x1fff;
-			struct sk_buff *skb;
-
-			dev->stats.rx_bytes += pkt_len;
-			if (corkscrew_debug > 4)
-				pr_debug("Receiving packet size %d status %4.4x.\n",
-				     pkt_len, rx_status);
-
-			/* Check if the packet is long enough to just accept without
-			   copying to a properly sized skbuff. */
-			if (pkt_len < rx_copybreak &&
-			    (skb = netdev_alloc_skb(dev, pkt_len + 4)) != NULL) {
-				skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
-				/* 'skb_put()' points to the start of sk_buff data area. */
-				skb_put_data(skb,
-					     isa_bus_to_virt(vp->rx_ring[entry].addr),
-					     pkt_len);
-				rx_copy++;
-			} else {
-				void *temp;
-				/* Pass up the skbuff already on the Rx ring. */
-				skb = vp->rx_skbuff[entry];
-				vp->rx_skbuff[entry] = NULL;
-				temp = skb_put(skb, pkt_len);
-				/* Remove this checking code for final release. */
-				if (isa_bus_to_virt(vp->rx_ring[entry].addr) != temp)
-					pr_warn("%s: Warning -- the skbuff addresses do not match in boomerang_rx: %p vs. %p / %p\n",
-						dev->name,
-						isa_bus_to_virt(vp->rx_ring[entry].addr),
-						skb->head, temp);
-				rx_nocopy++;
-			}
-			skb->protocol = eth_type_trans(skb, dev);
-			netif_rx(skb);
-			dev->stats.rx_packets++;
-		}
-		entry = (++vp->cur_rx) % RX_RING_SIZE;
-	}
-	/* Refill the Rx ring buffers. */
-	for (; vp->cur_rx - vp->dirty_rx > 0; vp->dirty_rx++) {
-		struct sk_buff *skb;
-		entry = vp->dirty_rx % RX_RING_SIZE;
-		if (vp->rx_skbuff[entry] == NULL) {
-			skb = netdev_alloc_skb(dev, PKT_BUF_SZ);
-			if (skb == NULL)
-				break;	/* Bad news!  */
-			skb_reserve(skb, 2);	/* Align IP on 16 byte boundaries */
-			vp->rx_ring[entry].addr = isa_virt_to_bus(skb->data);
-			vp->rx_skbuff[entry] = skb;
-		}
-		vp->rx_ring[entry].status = 0;	/* Clear complete bit. */
-	}
-	return 0;
-}
-
-static int corkscrew_close(struct net_device *dev)
-{
-	struct corkscrew_private *vp = netdev_priv(dev);
-	int ioaddr = dev->base_addr;
-	int i;
-
-	netif_stop_queue(dev);
-
-	if (corkscrew_debug > 1) {
-		pr_debug("%s: corkscrew_close() status %4.4x, Tx status %2.2x.\n",
-		     dev->name, inw(ioaddr + EL3_STATUS),
-		     inb(ioaddr + TxStatus));
-		pr_debug("%s: corkscrew close stats: rx_nocopy %d rx_copy %d tx_queued %d.\n",
-			dev->name, rx_nocopy, rx_copy, queued_packet);
-	}
-
-	timer_delete_sync(&vp->timer);
-
-	/* Turn off statistics ASAP.  We update lp->stats below. */
-	outw(StatsDisable, ioaddr + EL3_CMD);
-
-	/* Disable the receiver and transmitter. */
-	outw(RxDisable, ioaddr + EL3_CMD);
-	outw(TxDisable, ioaddr + EL3_CMD);
-
-	if (dev->if_port == XCVR_10base2)
-		/* Turn off thinnet power.  Green! */
-		outw(StopCoax, ioaddr + EL3_CMD);
-
-	free_irq(dev->irq, dev);
-
-	outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD);
-
-	update_stats(ioaddr, dev);
-	if (vp->full_bus_master_rx) {	/* Free Boomerang bus master Rx buffers. */
-		outl(0, ioaddr + UpListPtr);
-		for (i = 0; i < RX_RING_SIZE; i++)
-			if (vp->rx_skbuff[i]) {
-				dev_kfree_skb(vp->rx_skbuff[i]);
-				vp->rx_skbuff[i] = NULL;
-			}
-	}
-	if (vp->full_bus_master_tx) {	/* Free Boomerang bus master Tx buffers. */
-		outl(0, ioaddr + DownListPtr);
-		for (i = 0; i < TX_RING_SIZE; i++)
-			if (vp->tx_skbuff[i]) {
-				dev_kfree_skb(vp->tx_skbuff[i]);
-				vp->tx_skbuff[i] = NULL;
-			}
-	}
-
-	return 0;
-}
-
-static struct net_device_stats *corkscrew_get_stats(struct net_device *dev)
-{
-	struct corkscrew_private *vp = netdev_priv(dev);
-	unsigned long flags;
-
-	if (netif_running(dev)) {
-		spin_lock_irqsave(&vp->lock, flags);
-		update_stats(dev->base_addr, dev);
-		spin_unlock_irqrestore(&vp->lock, flags);
-	}
-	return &dev->stats;
-}
-
-/*  Update statistics.
-	Unlike with the EL3 we need not worry about interrupts changing
-	the window setting from underneath us, but we must still guard
-	against a race condition with a StatsUpdate interrupt updating the
-	table.  This is done by checking that the ASM (!) code generated uses
-	atomic updates with '+='.
-	*/
-static void update_stats(int ioaddr, struct net_device *dev)
-{
-	/* Unlike the 3c5x9 we need not turn off stats updates while reading. */
-	/* Switch to the stats window, and read everything. */
-	EL3WINDOW(6);
-	dev->stats.tx_carrier_errors += inb(ioaddr + 0);
-	dev->stats.tx_heartbeat_errors += inb(ioaddr + 1);
-	/* Multiple collisions. */ inb(ioaddr + 2);
-	dev->stats.collisions += inb(ioaddr + 3);
-	dev->stats.tx_window_errors += inb(ioaddr + 4);
-	dev->stats.rx_fifo_errors += inb(ioaddr + 5);
-	dev->stats.tx_packets += inb(ioaddr + 6);
-	dev->stats.tx_packets += (inb(ioaddr + 9) & 0x30) << 4;
-						/* Rx packets   */ inb(ioaddr + 7);
-						/* Must read to clear */
-	/* Tx deferrals */ inb(ioaddr + 8);
-	/* Don't bother with register 9, an extension of registers 6&7.
-	   If we do use the 6&7 values the atomic update assumption above
-	   is invalid. */
-	inw(ioaddr + 10);	/* Total Rx and Tx octets. */
-	inw(ioaddr + 12);
-	/* New: On the Vortex we must also clear the BadSSD counter. */
-	EL3WINDOW(4);
-	inb(ioaddr + 12);
-
-	/* We change back to window 7 (not 1) with the Vortex. */
-	EL3WINDOW(7);
-}
-
-/* This new version of set_rx_mode() supports v1.4 kernels.
-   The Vortex chip has no documented multicast filter, so the only
-   multicast setting is to receive all multicast frames.  At least
-   the chip has a very clean way to set the mode, unlike many others. */
-static void set_rx_mode(struct net_device *dev)
-{
-	int ioaddr = dev->base_addr;
-	unsigned short new_mode;
-
-	if (dev->flags & IFF_PROMISC) {
-		if (corkscrew_debug > 3)
-			pr_debug("%s: Setting promiscuous mode.\n",
-			       dev->name);
-		new_mode = SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm;
-	} else if (!netdev_mc_empty(dev) || dev->flags & IFF_ALLMULTI) {
-		new_mode = SetRxFilter | RxStation | RxMulticast | RxBroadcast;
-	} else
-		new_mode = SetRxFilter | RxStation | RxBroadcast;
-
-	outw(new_mode, ioaddr + EL3_CMD);
-}
-
-static void netdev_get_drvinfo(struct net_device *dev,
-			       struct ethtool_drvinfo *info)
-{
-	strscpy(info->driver, DRV_NAME, sizeof(info->driver));
-	snprintf(info->bus_info, sizeof(info->bus_info), "ISA 0x%lx",
-		 dev->base_addr);
-}
-
-static u32 netdev_get_msglevel(struct net_device *dev)
-{
-	return corkscrew_debug;
-}
-
-static void netdev_set_msglevel(struct net_device *dev, u32 level)
-{
-	corkscrew_debug = level;
-}
-
-static const struct ethtool_ops netdev_ethtool_ops = {
-	.get_drvinfo		= netdev_get_drvinfo,
-	.get_msglevel		= netdev_get_msglevel,
-	.set_msglevel		= netdev_set_msglevel,
-};
-
-#ifdef MODULE
-static void __exit corkscrew_exit_module(void)
-{
-	while (!list_empty(&root_corkscrew_dev)) {
-		struct net_device *dev;
-		struct corkscrew_private *vp;
-
-		vp = list_entry(root_corkscrew_dev.next,
-				struct corkscrew_private, list);
-		dev = vp->our_dev;
-		unregister_netdev(dev);
-		cleanup_card(dev);
-		free_netdev(dev);
-	}
-}
-module_exit(corkscrew_exit_module);
-#endif				/* MODULE */
diff --git a/drivers/net/ethernet/3com/Kconfig b/drivers/net/ethernet/3com/Kconfig
index c05a1b63c1c9..3fd3202d9776 100644
--- a/drivers/net/ethernet/3com/Kconfig
+++ b/drivers/net/ethernet/3com/Kconfig
@@ -17,17 +17,6 @@ config NET_VENDOR_3COM
 
 if NET_VENDOR_3COM
 
-config 3C515
-	tristate "3c515 ISA \"Fast EtherLink\""
-	depends on ISA && ISA_DMA_API && !PPC32
-	select NETDEV_LEGACY_INIT
-	help
-	  If you have a 3Com ISA EtherLink XL "Corkscrew" 3c515 Fast Ethernet
-	  network card, say Y here.
-
-	  To compile this driver as a module, choose M here. The module
-	  will be called 3c515.
-
 config PCMCIA_3C574
 	tristate "3Com 3c574 PCMCIA support"
 	depends on PCMCIA && HAS_IOPORT
diff --git a/drivers/net/ethernet/3com/Makefile b/drivers/net/ethernet/3com/Makefile
index f7623fa2d441..babfd93d5d53 100644
--- a/drivers/net/ethernet/3com/Makefile
+++ b/drivers/net/ethernet/3com/Makefile
@@ -3,7 +3,6 @@
 # Makefile for the 3Com Ethernet device drivers
 #
 
-obj-$(CONFIG_3C515) += 3c515.o
 obj-$(CONFIG_PCMCIA_3C589) += 3c589_cs.o
 obj-$(CONFIG_PCMCIA_3C574) += 3c574_cs.o
 obj-$(CONFIG_VORTEX) += 3c59x.o

-- 
2.53.0


^ permalink raw reply related

* [PATCH net v2 01/15] drivers: net: 3com: 3c509: Remove this driver
From: Andrew Lunn @ 2026-04-22 18:01 UTC (permalink / raw)
  To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan
  Cc: Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
	Daniel Palmer, linux-kernel, netdev, linux-doc, Andrew Lunn
In-Reply-To: <20260422-v7-0-0-net-next-driver-removal-v1-v2-0-08a5b59784d5@lunn.ch>

The 3c509 was written by Donald Becker between 1993-2000. It is an ISA
device, so unlikely to be used with modern kernels.

Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
v2
Delete Documentation as well
---
 .../device_drivers/ethernet/3com/3c509.rst         |  249 ----
 drivers/net/ethernet/3com/3c509.c                  | 1448 --------------------
 drivers/net/ethernet/3com/Kconfig                  |   14 -
 drivers/net/ethernet/3com/Makefile                 |    1 -
 4 files changed, 1712 deletions(-)

diff --git a/Documentation/networking/device_drivers/ethernet/3com/3c509.rst b/Documentation/networking/device_drivers/ethernet/3com/3c509.rst
deleted file mode 100644
index 47f706bacdd9..000000000000
--- a/Documentation/networking/device_drivers/ethernet/3com/3c509.rst
+++ /dev/null
@@ -1,249 +0,0 @@
-.. SPDX-License-Identifier: GPL-2.0
-
-=============================================================================
-Linux and the 3Com EtherLink III Series Ethercards (driver v1.18c and higher)
-=============================================================================
-
-This file contains the instructions and caveats for v1.18c and higher versions
-of the 3c509 driver. You should not use the driver without reading this file.
-
-release 1.0
-
-28 February 2002
-
-Current maintainer (corrections to):
-  David Ruggiero <jdr@farfalle.com>
-
-Introduction
-============
-
-The following are notes and information on using the 3Com EtherLink III series
-ethercards in Linux. These cards are commonly known by the most widely-used
-card's 3Com model number, 3c509. They are all 10mb/s ISA-bus cards and shouldn't
-be (but sometimes are) confused with the similarly-numbered PCI-bus "3c905"
-(aka "Vortex" or "Boomerang") series.  Kernel support for the 3c509 family is
-provided by the module 3c509.c, which has code to support all of the following
-models:
-
- - 3c509 (original ISA card)
- - 3c509B (later revision of the ISA card; supports full-duplex)
- - 3c589 (PCMCIA)
- - 3c589B (later revision of the 3c589; supports full-duplex)
- - 3c579 (EISA)
-
-Large portions of this documentation were heavily borrowed from the guide
-written the original author of the 3c509 driver, Donald Becker. The master
-copy of that document, which contains notes on older versions of the driver,
-currently resides on Scyld web server: http://www.scyld.com/.
-
-
-Special Driver Features
-=======================
-
-Overriding card settings
-
-The driver allows boot- or load-time overriding of the card's detected IOADDR,
-IRQ, and transceiver settings, although this capability shouldn't generally be
-needed except to enable full-duplex mode (see below). An example of the syntax
-for LILO parameters for doing this::
-
-    ether=10,0x310,3,0x3c509,eth0
-
-This configures the first found 3c509 card for IRQ 10, base I/O 0x310, and
-transceiver type 3 (10base2). The flag "0x3c509" must be set to avoid conflicts
-with other card types when overriding the I/O address. When the driver is
-loaded as a module, only the IRQ may be overridden. For example,
-setting two cards to IRQ10 and IRQ11 is done by using the irq module
-option::
-
-   options 3c509 irq=10,11
-
-
-Full-duplex mode
-================
-
-The v1.18c driver added support for the 3c509B's full-duplex capabilities.
-In order to enable and successfully use full-duplex mode, three conditions
-must be met:
-
-(a) You must have a Etherlink III card model whose hardware supports full-
-duplex operations. Currently, the only members of the 3c509 family that are
-positively known to support full-duplex are the 3c509B (ISA bus) and 3c589B
-(PCMCIA) cards. Cards without the "B" model designation do *not* support
-full-duplex mode; these include the original 3c509 (no "B"), the original
-3c589, the 3c529 (MCA bus), and the 3c579 (EISA bus).
-
-(b) You must be using your card's 10baseT transceiver (i.e., the RJ-45
-connector), not its AUI (thick-net) or 10base2 (thin-net/coax) interfaces.
-AUI and 10base2 network cabling is physically incapable of full-duplex
-operation.
-
-(c) Most importantly, your 3c509B must be connected to a link partner that is
-itself full-duplex capable. This is almost certainly one of two things: a full-
-duplex-capable  Ethernet switch (*not* a hub), or a full-duplex-capable NIC on
-another system that's connected directly to the 3c509B via a crossover cable.
-
-Full-duplex mode can be enabled using 'ethtool'.
-
-.. warning::
-
-  Extremely important caution concerning full-duplex mode
-
-  Understand that the 3c509B's hardware's full-duplex support is much more
-  limited than that provide by more modern network interface cards. Although
-  at the physical layer of the network it fully supports full-duplex operation,
-  the card was designed before the current Ethernet auto-negotiation (N-way)
-  spec was written. This means that the 3c509B family ***cannot and will not
-  auto-negotiate a full-duplex connection with its link partner under any
-  circumstances, no matter how it is initialized***. If the full-duplex mode
-  of the 3c509B is enabled, its link partner will very likely need to be
-  independently _forced_ into full-duplex mode as well; otherwise various nasty
-  failures will occur - at the very least, you'll see massive numbers of packet
-  collisions. This is one of very rare circumstances where disabling auto-
-  negotiation and forcing the duplex mode of a network interface card or switch
-  would ever be necessary or desirable.
-
-
-Available Transceiver Types
-===========================
-
-For versions of the driver v1.18c and above, the available transceiver types are:
-
-== =========================================================================
-0  transceiver type from EEPROM config (normally 10baseT); force half-duplex
-1  AUI (thick-net / DB15 connector)
-2  (undefined)
-3  10base2 (thin-net == coax / BNC connector)
-4  10baseT (RJ-45 connector); force half-duplex mode
-8  transceiver type and duplex mode taken from card's EEPROM config settings
-12 10baseT (RJ-45 connector); force full-duplex mode
-== =========================================================================
-
-Prior to driver version 1.18c, only transceiver codes 0-4 were supported. Note
-that the new transceiver codes 8 and 12 are the *only* ones that will enable
-full-duplex mode, no matter what the card's detected EEPROM settings might be.
-This insured that merely upgrading the driver from an earlier version would
-never automatically enable full-duplex mode in an existing installation;
-it must always be explicitly enabled via one of these code in order to be
-activated.
-
-The transceiver type can be changed using 'ethtool'.
-
-
-Interpretation of error messages and common problems
-----------------------------------------------------
-
-Error Messages
-^^^^^^^^^^^^^^
-
-eth0: Infinite loop in interrupt, status 2011.
-These are "mostly harmless" message indicating that the driver had too much
-work during that interrupt cycle. With a status of 0x2011 you are receiving
-packets faster than they can be removed from the card. This should be rare
-or impossible in normal operation. Possible causes of this error report are:
-
-   - a "green" mode enabled that slows the processor down when there is no
-     keyboard activity.
-
-   - some other device or device driver hogging the bus or disabling interrupts.
-     Check /proc/interrupts for excessive interrupt counts. The timer tick
-     interrupt should always be incrementing faster than the others.
-
-No received packets
-^^^^^^^^^^^^^^^^^^^
-
-If a 3c509, 3c562 or 3c589 can successfully transmit packets, but never
-receives packets (as reported by /proc/net/dev or 'ifconfig') you likely
-have an interrupt line problem. Check /proc/interrupts to verify that the
-card is actually generating interrupts. If the interrupt count is not
-increasing you likely have a physical conflict with two devices trying to
-use the same ISA IRQ line. The common conflict is with a sound card on IRQ10
-or IRQ5, and the easiest solution is to move the 3c509 to a different
-interrupt line. If the device is receiving packets but 'ping' doesn't work,
-you have a routing problem.
-
-Tx Carrier Errors Reported in /proc/net/dev
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-
-If an EtherLink III appears to transmit packets, but the "Tx carrier errors"
-field in /proc/net/dev increments as quickly as the Tx packet count, you
-likely have an unterminated network or the incorrect media transceiver selected.
-
-3c509B card is not detected on machines with an ISA PnP BIOS.
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-While the updated driver works with most PnP BIOS programs, it does not work
-with all. This can be fixed by disabling PnP support using the 3Com-supplied
-setup program.
-
-3c509 card is not detected on overclocked machines
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-Increase the delay time in id_read_eeprom() from the current value, 500,
-to an absurdly high value, such as 5000.
-
-
-Decoding Status and Error Messages
-----------------------------------
-
-
-The bits in the main status register are:
-
-=====	======================================
-value 	description
-=====	======================================
-0x01 	Interrupt latch
-0x02 	Tx overrun, or Rx underrun
-0x04 	Tx complete
-0x08 	Tx FIFO room available
-0x10 	A complete Rx packet has arrived
-0x20 	A Rx packet has started to arrive
-0x40 	The driver has requested an interrupt
-0x80 	Statistics counter nearly full
-=====	======================================
-
-The bits in the transmit (Tx) status word are:
-
-=====	============================================
-value	description
-=====	============================================
-0x02	Out-of-window collision.
-0x04	Status stack overflow (normally impossible).
-0x08	16 collisions.
-0x10	Tx underrun (not enough PCI bus bandwidth).
-0x20	Tx jabber.
-0x40	Tx interrupt requested.
-0x80	Status is valid (this should always be set).
-=====	============================================
-
-
-When a transmit error occurs the driver produces a status message such as::
-
-   eth0: Transmit error, Tx status register 82
-
-The two values typically seen here are:
-
-0x82
-^^^^
-
-Out of window collision. This typically occurs when some other Ethernet
-host is incorrectly set to full duplex on a half duplex network.
-
-0x88
-^^^^
-
-16 collisions. This typically occurs when the network is exceptionally busy
-or when another host doesn't correctly back off after a collision. If this
-error is mixed with 0x82 errors it is the result of a host incorrectly set
-to full duplex (see above).
-
-Both of these errors are the result of network problems that should be
-corrected. They do not represent driver malfunction.
-
-
-Revision history (this file)
-============================
-
-28Feb02 v1.0  DR   New; major portions based on Becker original 3c509 docs
-
diff --git a/drivers/net/ethernet/3com/3c509.c b/drivers/net/ethernet/3com/3c509.c
deleted file mode 100644
index fb68339e1511..000000000000
--- a/drivers/net/ethernet/3com/3c509.c
+++ /dev/null
@@ -1,1448 +0,0 @@
-/* 3c509.c: A 3c509 EtherLink3 ethernet driver for linux. */
-/*
-	Written 1993-2000 by Donald Becker.
-
-	Copyright 1994-2000 by Donald Becker.
-	Copyright 1993 United States Government as represented by the
-	Director, National Security Agency.	 This software may be used and
-	distributed according to the terms of the GNU General Public License,
-	incorporated herein by reference.
-
-	This driver is for the 3Com EtherLinkIII series.
-
-	The author may be reached as becker@scyld.com, or C/O
-	Scyld Computing Corporation
-	410 Severn Ave., Suite 210
-	Annapolis MD 21403
-
-	Known limitations:
-	Because of the way 3c509 ISA detection works it's difficult to predict
-	a priori which of several ISA-mode cards will be detected first.
-
-	This driver does not use predictive interrupt mode, resulting in higher
-	packet latency but lower overhead.  If interrupts are disabled for an
-	unusually long time it could also result in missed packets, but in
-	practice this rarely happens.
-
-
-	FIXES:
-		Alan Cox:       Removed the 'Unexpected interrupt' bug.
-		Michael Meskes:	Upgraded to Donald Becker's version 1.07.
-		Alan Cox:	Increased the eeprom delay. Regardless of
-				what the docs say some people definitely
-				get problems with lower (but in card spec)
-				delays
-		v1.10 4/21/97 Fixed module code so that multiple cards may be detected,
-				other cleanups.  -djb
-		Andrea Arcangeli:	Upgraded to Donald Becker's version 1.12.
-		Rick Payne:	Fixed SMP race condition
-		v1.13 9/8/97 Made 'max_interrupt_work' an insmod-settable variable -djb
-		v1.14 10/15/97 Avoided waiting..discard message for fast machines -djb
-		v1.15 1/31/98 Faster recovery for Tx errors. -djb
-		v1.16 2/3/98 Different ID port handling to avoid sound cards. -djb
-		v1.18 12Mar2001 Andrew Morton
-			- Avoid bogus detect of 3c590's (Andrzej Krzysztofowicz)
-			- Reviewed against 1.18 from scyld.com
-		v1.18a 17Nov2001 Jeff Garzik <jgarzik@pobox.com>
-			- ethtool support
-		v1.18b 1Mar2002 Zwane Mwaikambo <zwane@commfireservices.com>
-			- Power Management support
-		v1.18c 1Mar2002 David Ruggiero <jdr@farfalle.com>
-			- Full duplex support
-		v1.19  16Oct2002 Zwane Mwaikambo <zwane@linuxpower.ca>
-			- Additional ethtool features
-		v1.19a 28Oct2002 Davud Ruggiero <jdr@farfalle.com>
-			- Increase *read_eeprom udelay to workaround oops with 2 cards.
-		v1.19b 08Nov2002 Marc Zyngier <maz@wild-wind.fr.eu.org>
-			- Introduce driver model for EISA cards.
-		v1.20  04Feb2008 Ondrej Zary <linux@rainbow-software.org>
-			- convert to isa_driver and pnp_driver and some cleanups
-*/
-
-#define DRV_NAME	"3c509"
-
-/* A few values that may be tweaked. */
-
-/* Time in jiffies before concluding the transmitter is hung. */
-#define TX_TIMEOUT  (400*HZ/1000)
-
-#include <linux/module.h>
-#include <linux/isa.h>
-#include <linux/pnp.h>
-#include <linux/string.h>
-#include <linux/interrupt.h>
-#include <linux/errno.h>
-#include <linux/in.h>
-#include <linux/ioport.h>
-#include <linux/init.h>
-#include <linux/netdevice.h>
-#include <linux/etherdevice.h>
-#include <linux/pm.h>
-#include <linux/skbuff.h>
-#include <linux/delay.h>	/* for udelay() */
-#include <linux/spinlock.h>
-#include <linux/ethtool.h>
-#include <linux/device.h>
-#include <linux/eisa.h>
-#include <linux/bitops.h>
-
-#include <linux/uaccess.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-
-#ifdef EL3_DEBUG
-static int el3_debug = EL3_DEBUG;
-#else
-static int el3_debug = 2;
-#endif
-
-/* Used to do a global count of all the cards in the system.  Must be
- * a global variable so that the eisa probe routines can increment
- * it */
-static int el3_cards = 0;
-#define EL3_MAX_CARDS 8
-
-/* To minimize the size of the driver source I only define operating
-   constants if they are used several times.  You'll need the manual
-   anyway if you want to understand driver details. */
-/* Offsets from base I/O address. */
-#define EL3_DATA 0x00
-#define EL3_CMD 0x0e
-#define EL3_STATUS 0x0e
-#define	EEPROM_READ 0x80
-
-#define EL3_IO_EXTENT	16
-
-#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD)
-
-
-/* The top five bits written to EL3_CMD are a command, the lower
-   11 bits are the parameter, if applicable. */
-enum c509cmd {
-	TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11,
-	RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, RxDiscard = 8<<11,
-	TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11,
-	FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11,
-	SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11,
-	SetTxThreshold = 18<<11, SetTxStart = 19<<11, StatsEnable = 21<<11,
-	StatsDisable = 22<<11, StopCoax = 23<<11, PowerUp = 27<<11,
-	PowerDown = 28<<11, PowerAuto = 29<<11};
-
-enum c509status {
-	IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004,
-	TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020,
-	IntReq = 0x0040, StatsFull = 0x0080, CmdBusy = 0x1000, };
-
-/* The SetRxFilter command accepts the following classes: */
-enum RxFilter {
-	RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8 };
-
-/* Register window 1 offsets, the window used in normal operation. */
-#define TX_FIFO		0x00
-#define RX_FIFO		0x00
-#define RX_STATUS 	0x08
-#define TX_STATUS 	0x0B
-#define TX_FREE		0x0C		/* Remaining free bytes in Tx buffer. */
-
-#define WN0_CONF_CTRL	0x04		/* Window 0: Configuration control register */
-#define WN0_ADDR_CONF	0x06		/* Window 0: Address configuration register */
-#define WN0_IRQ		0x08		/* Window 0: Set IRQ line in bits 12-15. */
-#define WN4_MEDIA	0x0A		/* Window 4: Various transcvr/media bits. */
-#define	MEDIA_TP	0x00C0		/* Enable link beat and jabber for 10baseT. */
-#define WN4_NETDIAG	0x06		/* Window 4: Net diagnostic */
-#define FD_ENABLE	0x8000		/* Enable full-duplex ("external loopback") */
-
-/*
- * Must be a power of two (we use a binary and in the
- * circular queue)
- */
-#define SKB_QUEUE_SIZE	64
-
-enum el3_cardtype { EL3_ISA, EL3_PNP, EL3_EISA };
-
-struct el3_private {
-	spinlock_t lock;
-	/* skb send-queue */
-	int head, size;
-	struct sk_buff *queue[SKB_QUEUE_SIZE];
-	enum el3_cardtype type;
-};
-static int id_port;
-static int current_tag;
-static struct net_device *el3_devs[EL3_MAX_CARDS];
-
-/* Parameters that may be passed into the module. */
-static int debug = -1;
-static int irq[] = {-1, -1, -1, -1, -1, -1, -1, -1};
-/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
-static int max_interrupt_work = 10;
-#ifdef CONFIG_PNP
-static int nopnp;
-#endif
-
-static int el3_common_init(struct net_device *dev);
-static void el3_common_remove(struct net_device *dev);
-static ushort id_read_eeprom(int index);
-static ushort read_eeprom(int ioaddr, int index);
-static int el3_open(struct net_device *dev);
-static netdev_tx_t el3_start_xmit(struct sk_buff *skb, struct net_device *dev);
-static irqreturn_t el3_interrupt(int irq, void *dev_id);
-static void update_stats(struct net_device *dev);
-static struct net_device_stats *el3_get_stats(struct net_device *dev);
-static int el3_rx(struct net_device *dev);
-static int el3_close(struct net_device *dev);
-static void set_multicast_list(struct net_device *dev);
-static void el3_tx_timeout (struct net_device *dev, unsigned int txqueue);
-static void el3_down(struct net_device *dev);
-static void el3_up(struct net_device *dev);
-static const struct ethtool_ops ethtool_ops;
-#ifdef CONFIG_PM
-static int el3_suspend(struct device *, pm_message_t);
-static int el3_resume(struct device *);
-#else
-#define el3_suspend NULL
-#define el3_resume NULL
-#endif
-
-
-/* generic device remove for all device types */
-static int el3_device_remove (struct device *device);
-#ifdef CONFIG_NET_POLL_CONTROLLER
-static void el3_poll_controller(struct net_device *dev);
-#endif
-
-/* Return 0 on success, 1 on error, 2 when found already detected PnP card */
-static int el3_isa_id_sequence(__be16 *phys_addr)
-{
-	short lrs_state = 0xff;
-	int i;
-
-	/* ISA boards are detected by sending the ID sequence to the
-	   ID_PORT.  We find cards past the first by setting the 'current_tag'
-	   on cards as they are found.  Cards with their tag set will not
-	   respond to subsequent ID sequences. */
-
-	outb(0x00, id_port);
-	outb(0x00, id_port);
-	for (i = 0; i < 255; i++) {
-		outb(lrs_state, id_port);
-		lrs_state <<= 1;
-		lrs_state = lrs_state & 0x100 ? lrs_state ^ 0xcf : lrs_state;
-	}
-	/* For the first probe, clear all board's tag registers. */
-	if (current_tag == 0)
-		outb(0xd0, id_port);
-	else			/* Otherwise kill off already-found boards. */
-		outb(0xd8, id_port);
-	if (id_read_eeprom(7) != 0x6d50)
-		return 1;
-	/* Read in EEPROM data, which does contention-select.
-	   Only the lowest address board will stay "on-line".
-	   3Com got the byte order backwards. */
-	for (i = 0; i < 3; i++)
-		phys_addr[i] = htons(id_read_eeprom(i));
-#ifdef CONFIG_PNP
-	if (!nopnp) {
-		/* The ISA PnP 3c509 cards respond to the ID sequence too.
-		   This check is needed in order not to register them twice. */
-		for (i = 0; i < el3_cards; i++) {
-			struct el3_private *lp = netdev_priv(el3_devs[i]);
-			if (lp->type == EL3_PNP &&
-			    ether_addr_equal((u8 *)phys_addr, el3_devs[i]->dev_addr)) {
-				if (el3_debug > 3)
-					pr_debug("3c509 with address %02x %02x %02x %02x %02x %02x was found by ISAPnP\n",
-						phys_addr[0] & 0xff, phys_addr[0] >> 8,
-						phys_addr[1] & 0xff, phys_addr[1] >> 8,
-						phys_addr[2] & 0xff, phys_addr[2] >> 8);
-				/* Set the adaptor tag so that the next card can be found. */
-				outb(0xd0 + ++current_tag, id_port);
-				return 2;
-			}
-		}
-	}
-#endif /* CONFIG_PNP */
-	return 0;
-
-}
-
-static void el3_dev_fill(struct net_device *dev, __be16 *phys_addr, int ioaddr,
-			 int irq, int if_port, enum el3_cardtype type)
-{
-	struct el3_private *lp = netdev_priv(dev);
-
-	eth_hw_addr_set(dev, (u8 *)phys_addr);
-	dev->base_addr = ioaddr;
-	dev->irq = irq;
-	dev->if_port = if_port;
-	lp->type = type;
-}
-
-static int el3_isa_match(struct device *pdev, unsigned int ndev)
-{
-	struct net_device *dev;
-	int ioaddr, isa_irq, if_port, err;
-	unsigned int iobase;
-	__be16 phys_addr[3];
-
-	while ((err = el3_isa_id_sequence(phys_addr)) == 2)
-		;	/* Skip to next card when PnP card found */
-	if (err == 1)
-		return 0;
-
-	iobase = id_read_eeprom(8);
-	if_port = iobase >> 14;
-	ioaddr = 0x200 + ((iobase & 0x1f) << 4);
-	if (irq[el3_cards] > 1 && irq[el3_cards] < 16)
-		isa_irq = irq[el3_cards];
-	else
-		isa_irq = id_read_eeprom(9) >> 12;
-
-	dev = alloc_etherdev(sizeof(struct el3_private));
-	if (!dev)
-		return -ENOMEM;
-
-	SET_NETDEV_DEV(dev, pdev);
-
-	if (!request_region(ioaddr, EL3_IO_EXTENT, "3c509-isa")) {
-		free_netdev(dev);
-		return 0;
-	}
-
-	/* Set the adaptor tag so that the next card can be found. */
-	outb(0xd0 + ++current_tag, id_port);
-
-	/* Activate the adaptor at the EEPROM location. */
-	outb((ioaddr >> 4) | 0xe0, id_port);
-
-	EL3WINDOW(0);
-	if (inw(ioaddr) != 0x6d50) {
-		free_netdev(dev);
-		return 0;
-	}
-
-	/* Free the interrupt so that some other card can use it. */
-	outw(0x0f00, ioaddr + WN0_IRQ);
-
-	el3_dev_fill(dev, phys_addr, ioaddr, isa_irq, if_port, EL3_ISA);
-	dev_set_drvdata(pdev, dev);
-	if (el3_common_init(dev)) {
-		free_netdev(dev);
-		return 0;
-	}
-
-	el3_devs[el3_cards++] = dev;
-	return 1;
-}
-
-static void el3_isa_remove(struct device *pdev,
-				    unsigned int ndev)
-{
-	el3_device_remove(pdev);
-	dev_set_drvdata(pdev, NULL);
-}
-
-#ifdef CONFIG_PM
-static int el3_isa_suspend(struct device *dev, unsigned int n,
-			   pm_message_t state)
-{
-	current_tag = 0;
-	return el3_suspend(dev, state);
-}
-
-static int el3_isa_resume(struct device *dev, unsigned int n)
-{
-	struct net_device *ndev = dev_get_drvdata(dev);
-	int ioaddr = ndev->base_addr, err;
-	__be16 phys_addr[3];
-
-	while ((err = el3_isa_id_sequence(phys_addr)) == 2)
-		;	/* Skip to next card when PnP card found */
-	if (err == 1)
-		return 0;
-	/* Set the adaptor tag so that the next card can be found. */
-	outb(0xd0 + ++current_tag, id_port);
-	/* Enable the card */
-	outb((ioaddr >> 4) | 0xe0, id_port);
-	EL3WINDOW(0);
-	if (inw(ioaddr) != 0x6d50)
-		return 1;
-	/* Free the interrupt so that some other card can use it. */
-	outw(0x0f00, ioaddr + WN0_IRQ);
-	return el3_resume(dev);
-}
-#endif
-
-static struct isa_driver el3_isa_driver = {
-	.match		= el3_isa_match,
-	.remove		= el3_isa_remove,
-#ifdef CONFIG_PM
-	.suspend	= el3_isa_suspend,
-	.resume		= el3_isa_resume,
-#endif
-	.driver		= {
-		.name	= "3c509"
-	},
-};
-static int isa_registered;
-
-#ifdef CONFIG_PNP
-static const struct pnp_device_id el3_pnp_ids[] = {
-	{ .id = "TCM5090" }, /* 3Com Etherlink III (TP) */
-	{ .id = "TCM5091" }, /* 3Com Etherlink III */
-	{ .id = "TCM5094" }, /* 3Com Etherlink III (combo) */
-	{ .id = "TCM5095" }, /* 3Com Etherlink III (TPO) */
-	{ .id = "TCM5098" }, /* 3Com Etherlink III (TPC) */
-	{ .id = "PNP80f7" }, /* 3Com Etherlink III compatible */
-	{ .id = "PNP80f8" }, /* 3Com Etherlink III compatible */
-	{ .id = "" }
-};
-MODULE_DEVICE_TABLE(pnp, el3_pnp_ids);
-
-static int el3_pnp_probe(struct pnp_dev *pdev, const struct pnp_device_id *id)
-{
-	short i;
-	int ioaddr, irq, if_port;
-	__be16 phys_addr[3];
-	struct net_device *dev = NULL;
-	int err;
-
-	ioaddr = pnp_port_start(pdev, 0);
-	if (!request_region(ioaddr, EL3_IO_EXTENT, "3c509-pnp"))
-		return -EBUSY;
-	irq = pnp_irq(pdev, 0);
-	EL3WINDOW(0);
-	for (i = 0; i < 3; i++)
-		phys_addr[i] = htons(read_eeprom(ioaddr, i));
-	if_port = read_eeprom(ioaddr, 8) >> 14;
-	dev = alloc_etherdev(sizeof(struct el3_private));
-	if (!dev) {
-		release_region(ioaddr, EL3_IO_EXTENT);
-		return -ENOMEM;
-	}
-	SET_NETDEV_DEV(dev, &pdev->dev);
-
-	el3_dev_fill(dev, phys_addr, ioaddr, irq, if_port, EL3_PNP);
-	pnp_set_drvdata(pdev, dev);
-	err = el3_common_init(dev);
-
-	if (err) {
-		pnp_set_drvdata(pdev, NULL);
-		free_netdev(dev);
-		return err;
-	}
-
-	el3_devs[el3_cards++] = dev;
-	return 0;
-}
-
-static void el3_pnp_remove(struct pnp_dev *pdev)
-{
-	el3_common_remove(pnp_get_drvdata(pdev));
-	pnp_set_drvdata(pdev, NULL);
-}
-
-#ifdef CONFIG_PM
-static int el3_pnp_suspend(struct pnp_dev *pdev, pm_message_t state)
-{
-	return el3_suspend(&pdev->dev, state);
-}
-
-static int el3_pnp_resume(struct pnp_dev *pdev)
-{
-	return el3_resume(&pdev->dev);
-}
-#endif
-
-static struct pnp_driver el3_pnp_driver = {
-	.name		= "3c509",
-	.id_table	= el3_pnp_ids,
-	.probe		= el3_pnp_probe,
-	.remove		= el3_pnp_remove,
-#ifdef CONFIG_PM
-	.suspend	= el3_pnp_suspend,
-	.resume		= el3_pnp_resume,
-#endif
-};
-static int pnp_registered;
-#endif /* CONFIG_PNP */
-
-#ifdef CONFIG_EISA
-static const struct eisa_device_id el3_eisa_ids[] = {
-		{ "TCM5090" },
-		{ "TCM5091" },
-		{ "TCM5092" },
-		{ "TCM5093" },
-		{ "TCM5094" },
-		{ "TCM5095" },
-		{ "TCM5098" },
-		{ "" }
-};
-MODULE_DEVICE_TABLE(eisa, el3_eisa_ids);
-
-static int el3_eisa_probe (struct device *device);
-
-static struct eisa_driver el3_eisa_driver = {
-		.id_table = el3_eisa_ids,
-		.driver   = {
-				.name    = "3c579",
-				.probe   = el3_eisa_probe,
-				.remove  = el3_device_remove,
-				.suspend = el3_suspend,
-				.resume  = el3_resume,
-		}
-};
-static int eisa_registered;
-#endif
-
-static const struct net_device_ops netdev_ops = {
-	.ndo_open 		= el3_open,
-	.ndo_stop	 	= el3_close,
-	.ndo_start_xmit 	= el3_start_xmit,
-	.ndo_get_stats 		= el3_get_stats,
-	.ndo_set_rx_mode	= set_multicast_list,
-	.ndo_tx_timeout 	= el3_tx_timeout,
-	.ndo_set_mac_address 	= eth_mac_addr,
-	.ndo_validate_addr	= eth_validate_addr,
-#ifdef CONFIG_NET_POLL_CONTROLLER
-	.ndo_poll_controller	= el3_poll_controller,
-#endif
-};
-
-static int el3_common_init(struct net_device *dev)
-{
-	struct el3_private *lp = netdev_priv(dev);
-	int err;
-	static const char * const if_names[] = {
-		"10baseT", "AUI", "undefined", "BNC"
-	};
-
-	spin_lock_init(&lp->lock);
-
-	if (dev->mem_start & 0x05) { /* xcvr codes 1/3/4/12 */
-		dev->if_port = (dev->mem_start & 0x0f);
-	} else { /* xcvr codes 0/8 */
-		/* use eeprom value, but save user's full-duplex selection */
-		dev->if_port |= (dev->mem_start & 0x08);
-	}
-
-	/* The EL3-specific entries in the device structure. */
-	dev->netdev_ops = &netdev_ops;
-	dev->watchdog_timeo = TX_TIMEOUT;
-	dev->ethtool_ops = &ethtool_ops;
-
-	err = register_netdev(dev);
-	if (err) {
-		pr_err("Failed to register 3c5x9 at %#3.3lx, IRQ %d.\n",
-			dev->base_addr, dev->irq);
-		release_region(dev->base_addr, EL3_IO_EXTENT);
-		return err;
-	}
-
-	pr_info("%s: 3c5x9 found at %#3.3lx, %s port, address %pM, IRQ %d.\n",
-	       dev->name, dev->base_addr, if_names[(dev->if_port & 0x03)],
-	       dev->dev_addr, dev->irq);
-
-	return 0;
-
-}
-
-static void el3_common_remove (struct net_device *dev)
-{
-	unregister_netdev (dev);
-	release_region(dev->base_addr, EL3_IO_EXTENT);
-	free_netdev (dev);
-}
-
-#ifdef CONFIG_EISA
-static int el3_eisa_probe(struct device *device)
-{
-	short i;
-	int ioaddr, irq, if_port;
-	__be16 phys_addr[3];
-	struct net_device *dev = NULL;
-	struct eisa_device *edev;
-	int err;
-
-	/* Yeepee, The driver framework is calling us ! */
-	edev = to_eisa_device (device);
-	ioaddr = edev->base_addr;
-
-	if (!request_region(ioaddr, EL3_IO_EXTENT, "3c579-eisa"))
-		return -EBUSY;
-
-	/* Change the register set to the configuration window 0. */
-	outw(SelectWindow | 0, ioaddr + 0xC80 + EL3_CMD);
-
-	irq = inw(ioaddr + WN0_IRQ) >> 12;
-	if_port = inw(ioaddr + 6)>>14;
-	for (i = 0; i < 3; i++)
-		phys_addr[i] = htons(read_eeprom(ioaddr, i));
-
-	/* Restore the "Product ID" to the EEPROM read register. */
-	read_eeprom(ioaddr, 3);
-
-	dev = alloc_etherdev(sizeof (struct el3_private));
-	if (dev == NULL) {
-		release_region(ioaddr, EL3_IO_EXTENT);
-		return -ENOMEM;
-	}
-
-	SET_NETDEV_DEV(dev, device);
-
-	el3_dev_fill(dev, phys_addr, ioaddr, irq, if_port, EL3_EISA);
-	eisa_set_drvdata (edev, dev);
-	err = el3_common_init(dev);
-
-	if (err) {
-		eisa_set_drvdata (edev, NULL);
-		free_netdev(dev);
-		return err;
-	}
-
-	el3_devs[el3_cards++] = dev;
-	return 0;
-}
-#endif
-
-/* This remove works for all device types.
- *
- * The net dev must be stored in the driver data field */
-static int el3_device_remove(struct device *device)
-{
-	struct net_device *dev;
-
-	dev = dev_get_drvdata(device);
-
-	el3_common_remove (dev);
-	return 0;
-}
-
-/* Read a word from the EEPROM using the regular EEPROM access register.
-   Assume that we are in register window zero.
- */
-static ushort read_eeprom(int ioaddr, int index)
-{
-	outw(EEPROM_READ + index, ioaddr + 10);
-	/* Pause for at least 162 us. for the read to take place.
-	   Some chips seem to require much longer */
-	mdelay(2);
-	return inw(ioaddr + 12);
-}
-
-/* Read a word from the EEPROM when in the ISA ID probe state. */
-static ushort id_read_eeprom(int index)
-{
-	int bit, word = 0;
-
-	/* Issue read command, and pause for at least 162 us. for it to complete.
-	   Assume extra-fast 16Mhz bus. */
-	outb(EEPROM_READ + index, id_port);
-
-	/* Pause for at least 162 us. for the read to take place. */
-	/* Some chips seem to require much longer */
-	mdelay(4);
-
-	for (bit = 15; bit >= 0; bit--)
-		word = (word << 1) + (inb(id_port) & 0x01);
-
-	if (el3_debug > 3)
-		pr_debug("  3c509 EEPROM word %d %#4.4x.\n", index, word);
-
-	return word;
-}
-
-
-static int
-el3_open(struct net_device *dev)
-{
-	int ioaddr = dev->base_addr;
-	int i;
-
-	outw(TxReset, ioaddr + EL3_CMD);
-	outw(RxReset, ioaddr + EL3_CMD);
-	outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
-
-	i = request_irq(dev->irq, el3_interrupt, 0, dev->name, dev);
-	if (i)
-		return i;
-
-	EL3WINDOW(0);
-	if (el3_debug > 3)
-		pr_debug("%s: Opening, IRQ %d	 status@%x %4.4x.\n", dev->name,
-			   dev->irq, ioaddr + EL3_STATUS, inw(ioaddr + EL3_STATUS));
-
-	el3_up(dev);
-
-	if (el3_debug > 3)
-		pr_debug("%s: Opened 3c509  IRQ %d  status %4.4x.\n",
-			   dev->name, dev->irq, inw(ioaddr + EL3_STATUS));
-
-	return 0;
-}
-
-static void
-el3_tx_timeout (struct net_device *dev, unsigned int txqueue)
-{
-	int ioaddr = dev->base_addr;
-
-	/* Transmitter timeout, serious problems. */
-	pr_warn("%s: transmit timed out, Tx_status %2.2x status %4.4x Tx FIFO room %d\n",
-		dev->name, inb(ioaddr + TX_STATUS), inw(ioaddr + EL3_STATUS),
-		inw(ioaddr + TX_FREE));
-	dev->stats.tx_errors++;
-	netif_trans_update(dev); /* prevent tx timeout */
-	/* Issue TX_RESET and TX_START commands. */
-	outw(TxReset, ioaddr + EL3_CMD);
-	outw(TxEnable, ioaddr + EL3_CMD);
-	netif_wake_queue(dev);
-}
-
-
-static netdev_tx_t
-el3_start_xmit(struct sk_buff *skb, struct net_device *dev)
-{
-	struct el3_private *lp = netdev_priv(dev);
-	int ioaddr = dev->base_addr;
-	unsigned long flags;
-
-	netif_stop_queue (dev);
-
-	dev->stats.tx_bytes += skb->len;
-
-	if (el3_debug > 4) {
-		pr_debug("%s: el3_start_xmit(length = %u) called, status %4.4x.\n",
-			   dev->name, skb->len, inw(ioaddr + EL3_STATUS));
-	}
-	/*
-	 *	We lock the driver against other processors. Note
-	 *	we don't need to lock versus the IRQ as we suspended
-	 *	that. This means that we lose the ability to take
-	 *	an RX during a TX upload. That sucks a bit with SMP
-	 *	on an original 3c509 (2K buffer)
-	 *
-	 *	Using disable_irq stops us crapping on other
-	 *	time sensitive devices.
-	 */
-
-	spin_lock_irqsave(&lp->lock, flags);
-
-	/* Put out the doubleword header... */
-	outw(skb->len, ioaddr + TX_FIFO);
-	outw(0x00, ioaddr + TX_FIFO);
-	/* ... and the packet rounded to a doubleword. */
-	outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
-
-	if (inw(ioaddr + TX_FREE) > 1536)
-		netif_start_queue(dev);
-	else
-		/* Interrupt us when the FIFO has room for max-sized packet. */
-		outw(SetTxThreshold + 1536, ioaddr + EL3_CMD);
-
-	spin_unlock_irqrestore(&lp->lock, flags);
-
-	dev_consume_skb_any (skb);
-
-	/* Clear the Tx status stack. */
-	{
-		short tx_status;
-		int i = 4;
-
-		while (--i > 0	&&	(tx_status = inb(ioaddr + TX_STATUS)) > 0) {
-			if (tx_status & 0x38) dev->stats.tx_aborted_errors++;
-			if (tx_status & 0x30) outw(TxReset, ioaddr + EL3_CMD);
-			if (tx_status & 0x3C) outw(TxEnable, ioaddr + EL3_CMD);
-			outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */
-		}
-	}
-	return NETDEV_TX_OK;
-}
-
-/* The EL3 interrupt handler. */
-static irqreturn_t
-el3_interrupt(int irq, void *dev_id)
-{
-	struct net_device *dev = dev_id;
-	struct el3_private *lp;
-	int ioaddr, status;
-	int i = max_interrupt_work;
-
-	lp = netdev_priv(dev);
-	spin_lock(&lp->lock);
-
-	ioaddr = dev->base_addr;
-
-	if (el3_debug > 4) {
-		status = inw(ioaddr + EL3_STATUS);
-		pr_debug("%s: interrupt, status %4.4x.\n", dev->name, status);
-	}
-
-	while ((status = inw(ioaddr + EL3_STATUS)) &
-		   (IntLatch | RxComplete | StatsFull)) {
-
-		if (status & RxComplete)
-			el3_rx(dev);
-
-		if (status & TxAvailable) {
-			if (el3_debug > 5)
-				pr_debug("	TX room bit was handled.\n");
-			/* There's room in the FIFO for a full-sized packet. */
-			outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
-			netif_wake_queue (dev);
-		}
-		if (status & (AdapterFailure | RxEarly | StatsFull | TxComplete)) {
-			/* Handle all uncommon interrupts. */
-			if (status & StatsFull)				/* Empty statistics. */
-				update_stats(dev);
-			if (status & RxEarly) {				/* Rx early is unused. */
-				el3_rx(dev);
-				outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
-			}
-			if (status & TxComplete) {			/* Really Tx error. */
-				short tx_status;
-				int i = 4;
-
-				while (--i>0 && (tx_status = inb(ioaddr + TX_STATUS)) > 0) {
-					if (tx_status & 0x38) dev->stats.tx_aborted_errors++;
-					if (tx_status & 0x30) outw(TxReset, ioaddr + EL3_CMD);
-					if (tx_status & 0x3C) outw(TxEnable, ioaddr + EL3_CMD);
-					outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */
-				}
-			}
-			if (status & AdapterFailure) {
-				/* Adapter failure requires Rx reset and reinit. */
-				outw(RxReset, ioaddr + EL3_CMD);
-				/* Set the Rx filter to the current state. */
-				outw(SetRxFilter | RxStation | RxBroadcast
-					 | (dev->flags & IFF_ALLMULTI ? RxMulticast : 0)
-					 | (dev->flags & IFF_PROMISC ? RxProm : 0),
-					 ioaddr + EL3_CMD);
-				outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */
-				outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD);
-			}
-		}
-
-		if (--i < 0) {
-			pr_err("%s: Infinite loop in interrupt, status %4.4x.\n",
-				   dev->name, status);
-			/* Clear all interrupts. */
-			outw(AckIntr | 0xFF, ioaddr + EL3_CMD);
-			break;
-		}
-		/* Acknowledge the IRQ. */
-		outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD); /* Ack IRQ */
-	}
-
-	if (el3_debug > 4) {
-		pr_debug("%s: exiting interrupt, status %4.4x.\n", dev->name,
-			   inw(ioaddr + EL3_STATUS));
-	}
-	spin_unlock(&lp->lock);
-	return IRQ_HANDLED;
-}
-
-
-#ifdef CONFIG_NET_POLL_CONTROLLER
-/*
- * Polling receive - used by netconsole and other diagnostic tools
- * to allow network i/o with interrupts disabled.
- */
-static void el3_poll_controller(struct net_device *dev)
-{
-	disable_irq(dev->irq);
-	el3_interrupt(dev->irq, dev);
-	enable_irq(dev->irq);
-}
-#endif
-
-static struct net_device_stats *
-el3_get_stats(struct net_device *dev)
-{
-	struct el3_private *lp = netdev_priv(dev);
-	unsigned long flags;
-
-	/*
-	 *	This is fast enough not to bother with disable IRQ
-	 *	stuff.
-	 */
-
-	spin_lock_irqsave(&lp->lock, flags);
-	update_stats(dev);
-	spin_unlock_irqrestore(&lp->lock, flags);
-	return &dev->stats;
-}
-
-/*  Update statistics.  We change to register window 6, so this should be run
-	single-threaded if the device is active. This is expected to be a rare
-	operation, and it's simpler for the rest of the driver to assume that
-	window 1 is always valid rather than use a special window-state variable.
-	*/
-static void update_stats(struct net_device *dev)
-{
-	int ioaddr = dev->base_addr;
-
-	if (el3_debug > 5)
-		pr_debug("   Updating the statistics.\n");
-	/* Turn off statistics updates while reading. */
-	outw(StatsDisable, ioaddr + EL3_CMD);
-	/* Switch to the stats window, and read everything. */
-	EL3WINDOW(6);
-	dev->stats.tx_carrier_errors 	+= inb(ioaddr + 0);
-	dev->stats.tx_heartbeat_errors	+= inb(ioaddr + 1);
-	/* Multiple collisions. */	   inb(ioaddr + 2);
-	dev->stats.collisions		+= inb(ioaddr + 3);
-	dev->stats.tx_window_errors	+= inb(ioaddr + 4);
-	dev->stats.rx_fifo_errors	+= inb(ioaddr + 5);
-	dev->stats.tx_packets		+= inb(ioaddr + 6);
-	/* Rx packets	*/		   inb(ioaddr + 7);
-	/* Tx deferrals */		   inb(ioaddr + 8);
-	inw(ioaddr + 10);	/* Total Rx and Tx octets. */
-	inw(ioaddr + 12);
-
-	/* Back to window 1, and turn statistics back on. */
-	EL3WINDOW(1);
-	outw(StatsEnable, ioaddr + EL3_CMD);
-}
-
-static int
-el3_rx(struct net_device *dev)
-{
-	int ioaddr = dev->base_addr;
-	short rx_status;
-
-	if (el3_debug > 5)
-		pr_debug("   In rx_packet(), status %4.4x, rx_status %4.4x.\n",
-			   inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS));
-	while ((rx_status = inw(ioaddr + RX_STATUS)) > 0) {
-		if (rx_status & 0x4000) { /* Error, update stats. */
-			short error = rx_status & 0x3800;
-
-			outw(RxDiscard, ioaddr + EL3_CMD);
-			dev->stats.rx_errors++;
-			switch (error) {
-			case 0x0000:		dev->stats.rx_over_errors++; break;
-			case 0x0800:		dev->stats.rx_length_errors++; break;
-			case 0x1000:		dev->stats.rx_frame_errors++; break;
-			case 0x1800:		dev->stats.rx_length_errors++; break;
-			case 0x2000:		dev->stats.rx_frame_errors++; break;
-			case 0x2800:		dev->stats.rx_crc_errors++; break;
-			}
-		} else {
-			short pkt_len = rx_status & 0x7ff;
-			struct sk_buff *skb;
-
-			skb = netdev_alloc_skb(dev, pkt_len + 5);
-			if (el3_debug > 4)
-				pr_debug("Receiving packet size %d status %4.4x.\n",
-					   pkt_len, rx_status);
-			if (skb != NULL) {
-				skb_reserve(skb, 2);     /* Align IP on 16 byte */
-
-				/* 'skb->data' points to the start of sk_buff data area. */
-				insl(ioaddr + RX_FIFO, skb_put(skb,pkt_len),
-					 (pkt_len + 3) >> 2);
-
-				outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
-				skb->protocol = eth_type_trans(skb,dev);
-				netif_rx(skb);
-				dev->stats.rx_bytes += pkt_len;
-				dev->stats.rx_packets++;
-				continue;
-			}
-			outw(RxDiscard, ioaddr + EL3_CMD);
-			dev->stats.rx_dropped++;
-			if (el3_debug)
-				pr_debug("%s: Couldn't allocate a sk_buff of size %d.\n",
-					   dev->name, pkt_len);
-		}
-		inw(ioaddr + EL3_STATUS); 				/* Delay. */
-		while (inw(ioaddr + EL3_STATUS) & 0x1000)
-			pr_debug("	Waiting for 3c509 to discard packet, status %x.\n",
-				   inw(ioaddr + EL3_STATUS) );
-	}
-
-	return 0;
-}
-
-/*
- *     Set or clear the multicast filter for this adaptor.
- */
-static void
-set_multicast_list(struct net_device *dev)
-{
-	unsigned long flags;
-	struct el3_private *lp = netdev_priv(dev);
-	int ioaddr = dev->base_addr;
-	int mc_count = netdev_mc_count(dev);
-
-	if (el3_debug > 1) {
-		static int old;
-		if (old != mc_count) {
-			old = mc_count;
-			pr_debug("%s: Setting Rx mode to %d addresses.\n",
-				 dev->name, mc_count);
-		}
-	}
-	spin_lock_irqsave(&lp->lock, flags);
-	if (dev->flags&IFF_PROMISC) {
-		outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm,
-			 ioaddr + EL3_CMD);
-	}
-	else if (mc_count || (dev->flags&IFF_ALLMULTI)) {
-		outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast, ioaddr + EL3_CMD);
-	}
-	else
-		outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
-	spin_unlock_irqrestore(&lp->lock, flags);
-}
-
-static int
-el3_close(struct net_device *dev)
-{
-	int ioaddr = dev->base_addr;
-	struct el3_private *lp = netdev_priv(dev);
-
-	if (el3_debug > 2)
-		pr_debug("%s: Shutting down ethercard.\n", dev->name);
-
-	el3_down(dev);
-
-	free_irq(dev->irq, dev);
-	/* Switching back to window 0 disables the IRQ. */
-	EL3WINDOW(0);
-	if (lp->type != EL3_EISA) {
-		/* But we explicitly zero the IRQ line select anyway. Don't do
-		 * it on EISA cards, it prevents the module from getting an
-		 * IRQ after unload+reload... */
-		outw(0x0f00, ioaddr + WN0_IRQ);
-	}
-
-	return 0;
-}
-
-static int
-el3_link_ok(struct net_device *dev)
-{
-	int ioaddr = dev->base_addr;
-	u16 tmp;
-
-	EL3WINDOW(4);
-	tmp = inw(ioaddr + WN4_MEDIA);
-	EL3WINDOW(1);
-	return tmp & (1<<11);
-}
-
-static void
-el3_netdev_get_ecmd(struct net_device *dev, struct ethtool_link_ksettings *cmd)
-{
-	u16 tmp;
-	int ioaddr = dev->base_addr;
-	u32 supported;
-
-	EL3WINDOW(0);
-	/* obtain current transceiver via WN4_MEDIA? */
-	tmp = inw(ioaddr + WN0_ADDR_CONF);
-	switch (tmp >> 14) {
-	case 0:
-		cmd->base.port = PORT_TP;
-		break;
-	case 1:
-		cmd->base.port = PORT_AUI;
-		break;
-	case 3:
-		cmd->base.port = PORT_BNC;
-		break;
-	default:
-		break;
-	}
-
-	cmd->base.duplex = DUPLEX_HALF;
-	supported = 0;
-	tmp = inw(ioaddr + WN0_CONF_CTRL);
-	if (tmp & (1<<13))
-		supported |= SUPPORTED_AUI;
-	if (tmp & (1<<12))
-		supported |= SUPPORTED_BNC;
-	if (tmp & (1<<9)) {
-		supported |= SUPPORTED_TP | SUPPORTED_10baseT_Half |
-				SUPPORTED_10baseT_Full;	/* hmm... */
-		EL3WINDOW(4);
-		tmp = inw(ioaddr + WN4_NETDIAG);
-		if (tmp & FD_ENABLE)
-			cmd->base.duplex = DUPLEX_FULL;
-	}
-
-	ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported,
-						supported);
-	cmd->base.speed = SPEED_10;
-	EL3WINDOW(1);
-}
-
-static int
-el3_netdev_set_ecmd(struct net_device *dev,
-		    const struct ethtool_link_ksettings *cmd)
-{
-	u16 tmp;
-	int ioaddr = dev->base_addr;
-
-	if (cmd->base.speed != SPEED_10)
-		return -EINVAL;
-	if ((cmd->base.duplex != DUPLEX_HALF) &&
-	    (cmd->base.duplex != DUPLEX_FULL))
-		return -EINVAL;
-
-	/* change XCVR type */
-	EL3WINDOW(0);
-	tmp = inw(ioaddr + WN0_ADDR_CONF);
-	switch (cmd->base.port) {
-	case PORT_TP:
-		tmp &= ~(3<<14);
-		dev->if_port = 0;
-		break;
-	case PORT_AUI:
-		tmp |= (1<<14);
-		dev->if_port = 1;
-		break;
-	case PORT_BNC:
-		tmp |= (3<<14);
-		dev->if_port = 3;
-		break;
-	default:
-		return -EINVAL;
-	}
-
-	outw(tmp, ioaddr + WN0_ADDR_CONF);
-	if (dev->if_port == 3) {
-		/* fire up the DC-DC convertor if BNC gets enabled */
-		tmp = inw(ioaddr + WN0_ADDR_CONF);
-		if (tmp & (3 << 14)) {
-			outw(StartCoax, ioaddr + EL3_CMD);
-			udelay(800);
-		} else
-			return -EIO;
-	}
-
-	EL3WINDOW(4);
-	tmp = inw(ioaddr + WN4_NETDIAG);
-	if (cmd->base.duplex == DUPLEX_FULL)
-		tmp |= FD_ENABLE;
-	else
-		tmp &= ~FD_ENABLE;
-	outw(tmp, ioaddr + WN4_NETDIAG);
-	EL3WINDOW(1);
-
-	return 0;
-}
-
-static void el3_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
-{
-	strscpy(info->driver, DRV_NAME, sizeof(info->driver));
-}
-
-static int el3_get_link_ksettings(struct net_device *dev,
-				  struct ethtool_link_ksettings *cmd)
-{
-	struct el3_private *lp = netdev_priv(dev);
-
-	spin_lock_irq(&lp->lock);
-	el3_netdev_get_ecmd(dev, cmd);
-	spin_unlock_irq(&lp->lock);
-	return 0;
-}
-
-static int el3_set_link_ksettings(struct net_device *dev,
-				  const struct ethtool_link_ksettings *cmd)
-{
-	struct el3_private *lp = netdev_priv(dev);
-	int ret;
-
-	spin_lock_irq(&lp->lock);
-	ret = el3_netdev_set_ecmd(dev, cmd);
-	spin_unlock_irq(&lp->lock);
-	return ret;
-}
-
-static u32 el3_get_link(struct net_device *dev)
-{
-	struct el3_private *lp = netdev_priv(dev);
-	u32 ret;
-
-	spin_lock_irq(&lp->lock);
-	ret = el3_link_ok(dev);
-	spin_unlock_irq(&lp->lock);
-	return ret;
-}
-
-static u32 el3_get_msglevel(struct net_device *dev)
-{
-	return el3_debug;
-}
-
-static void el3_set_msglevel(struct net_device *dev, u32 v)
-{
-	el3_debug = v;
-}
-
-static const struct ethtool_ops ethtool_ops = {
-	.get_drvinfo = el3_get_drvinfo,
-	.get_link = el3_get_link,
-	.get_msglevel = el3_get_msglevel,
-	.set_msglevel = el3_set_msglevel,
-	.get_link_ksettings = el3_get_link_ksettings,
-	.set_link_ksettings = el3_set_link_ksettings,
-};
-
-static void
-el3_down(struct net_device *dev)
-{
-	int ioaddr = dev->base_addr;
-
-	netif_stop_queue(dev);
-
-	/* Turn off statistics ASAP.  We update lp->stats below. */
-	outw(StatsDisable, ioaddr + EL3_CMD);
-
-	/* Disable the receiver and transmitter. */
-	outw(RxDisable, ioaddr + EL3_CMD);
-	outw(TxDisable, ioaddr + EL3_CMD);
-
-	if (dev->if_port == 3)
-		/* Turn off thinnet power.  Green! */
-		outw(StopCoax, ioaddr + EL3_CMD);
-	else if (dev->if_port == 0) {
-		/* Disable link beat and jabber, if_port may change here next open(). */
-		EL3WINDOW(4);
-		outw(inw(ioaddr + WN4_MEDIA) & ~MEDIA_TP, ioaddr + WN4_MEDIA);
-	}
-
-	outw(SetIntrEnb | 0x0000, ioaddr + EL3_CMD);
-
-	update_stats(dev);
-}
-
-static void
-el3_up(struct net_device *dev)
-{
-	int i, sw_info, net_diag;
-	int ioaddr = dev->base_addr;
-
-	/* Activating the board required and does no harm otherwise */
-	outw(0x0001, ioaddr + 4);
-
-	/* Set the IRQ line. */
-	outw((dev->irq << 12) | 0x0f00, ioaddr + WN0_IRQ);
-
-	/* Set the station address in window 2 each time opened. */
-	EL3WINDOW(2);
-
-	for (i = 0; i < 6; i++)
-		outb(dev->dev_addr[i], ioaddr + i);
-
-	if ((dev->if_port & 0x03) == 3) /* BNC interface */
-		/* Start the thinnet transceiver. We should really wait 50ms...*/
-		outw(StartCoax, ioaddr + EL3_CMD);
-	else if ((dev->if_port & 0x03) == 0) { /* 10baseT interface */
-		/* Combine secondary sw_info word (the adapter level) and primary
-			sw_info word (duplex setting plus other useless bits) */
-		EL3WINDOW(0);
-		sw_info = (read_eeprom(ioaddr, 0x14) & 0x400f) |
-			(read_eeprom(ioaddr, 0x0d) & 0xBff0);
-
-		EL3WINDOW(4);
-		net_diag = inw(ioaddr + WN4_NETDIAG);
-		net_diag = (net_diag | FD_ENABLE); /* temporarily assume full-duplex will be set */
-		pr_info("%s: ", dev->name);
-		switch (dev->if_port & 0x0c) {
-			case 12:
-				/* force full-duplex mode if 3c5x9b */
-				if (sw_info & 0x000f) {
-					pr_cont("Forcing 3c5x9b full-duplex mode");
-					break;
-				}
-				fallthrough;
-			case 8:
-				/* set full-duplex mode based on eeprom config setting */
-				if ((sw_info & 0x000f) && (sw_info & 0x8000)) {
-					pr_cont("Setting 3c5x9b full-duplex mode (from EEPROM configuration bit)");
-					break;
-				}
-				fallthrough;
-			default:
-				/* xcvr=(0 || 4) OR user has an old 3c5x9 non "B" model */
-				pr_cont("Setting 3c5x9/3c5x9B half-duplex mode");
-				net_diag = (net_diag & ~FD_ENABLE); /* disable full duplex */
-		}
-
-		outw(net_diag, ioaddr + WN4_NETDIAG);
-		pr_cont(" if_port: %d, sw_info: %4.4x\n", dev->if_port, sw_info);
-		if (el3_debug > 3)
-			pr_debug("%s: 3c5x9 net diag word is now: %4.4x.\n", dev->name, net_diag);
-		/* Enable link beat and jabber check. */
-		outw(inw(ioaddr + WN4_MEDIA) | MEDIA_TP, ioaddr + WN4_MEDIA);
-	}
-
-	/* Switch to the stats window, and clear all stats by reading. */
-	outw(StatsDisable, ioaddr + EL3_CMD);
-	EL3WINDOW(6);
-	for (i = 0; i < 9; i++)
-		inb(ioaddr + i);
-	inw(ioaddr + 10);
-	inw(ioaddr + 12);
-
-	/* Switch to register set 1 for normal use. */
-	EL3WINDOW(1);
-
-	/* Accept b-case and phys addr only. */
-	outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
-	outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
-
-	outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
-	outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
-	/* Allow status bits to be seen. */
-	outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD);
-	/* Ack all pending events, and set active indicator mask. */
-	outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
-		 ioaddr + EL3_CMD);
-	outw(SetIntrEnb | IntLatch|TxAvailable|TxComplete|RxComplete|StatsFull,
-		 ioaddr + EL3_CMD);
-
-	netif_start_queue(dev);
-}
-
-/* Power Management support functions */
-#ifdef CONFIG_PM
-
-static int
-el3_suspend(struct device *pdev, pm_message_t state)
-{
-	unsigned long flags;
-	struct net_device *dev;
-	struct el3_private *lp;
-	int ioaddr;
-
-	dev = dev_get_drvdata(pdev);
-	lp = netdev_priv(dev);
-	ioaddr = dev->base_addr;
-
-	spin_lock_irqsave(&lp->lock, flags);
-
-	if (netif_running(dev))
-		netif_device_detach(dev);
-
-	el3_down(dev);
-	outw(PowerDown, ioaddr + EL3_CMD);
-
-	spin_unlock_irqrestore(&lp->lock, flags);
-	return 0;
-}
-
-static int
-el3_resume(struct device *pdev)
-{
-	unsigned long flags;
-	struct net_device *dev;
-	struct el3_private *lp;
-	int ioaddr;
-
-	dev = dev_get_drvdata(pdev);
-	lp = netdev_priv(dev);
-	ioaddr = dev->base_addr;
-
-	spin_lock_irqsave(&lp->lock, flags);
-
-	outw(PowerUp, ioaddr + EL3_CMD);
-	EL3WINDOW(0);
-	el3_up(dev);
-
-	if (netif_running(dev))
-		netif_device_attach(dev);
-
-	spin_unlock_irqrestore(&lp->lock, flags);
-	return 0;
-}
-
-#endif /* CONFIG_PM */
-
-module_param(debug,int, 0);
-module_param_hw_array(irq, int, irq, NULL, 0);
-module_param(max_interrupt_work, int, 0);
-MODULE_PARM_DESC(debug, "debug level (0-6)");
-MODULE_PARM_DESC(irq, "IRQ number(s) (assigned)");
-MODULE_PARM_DESC(max_interrupt_work, "maximum events handled per interrupt");
-#ifdef CONFIG_PNP
-module_param(nopnp, int, 0);
-MODULE_PARM_DESC(nopnp, "disable ISA PnP support (0-1)");
-#endif	/* CONFIG_PNP */
-MODULE_DESCRIPTION("3Com Etherlink III (3c509, 3c509B, 3c529, 3c579) ethernet driver");
-MODULE_LICENSE("GPL");
-
-static int __init el3_init_module(void)
-{
-	int ret = 0;
-
-	if (debug >= 0)
-		el3_debug = debug;
-
-#ifdef CONFIG_PNP
-	if (!nopnp) {
-		ret = pnp_register_driver(&el3_pnp_driver);
-		if (!ret)
-			pnp_registered = 1;
-	}
-#endif
-	/* Select an open I/O location at 0x1*0 to do ISA contention select. */
-	/* Start with 0x110 to avoid some sound cards.*/
-	for (id_port = 0x110 ; id_port < 0x200; id_port += 0x10) {
-		if (!request_region(id_port, 1, "3c509-control"))
-			continue;
-		outb(0x00, id_port);
-		outb(0xff, id_port);
-		if (inb(id_port) & 0x01)
-			break;
-		else
-			release_region(id_port, 1);
-	}
-	if (id_port >= 0x200) {
-		id_port = 0;
-		pr_err("No I/O port available for 3c509 activation.\n");
-	} else {
-		ret = isa_register_driver(&el3_isa_driver, EL3_MAX_CARDS);
-		if (!ret)
-			isa_registered = 1;
-	}
-#ifdef CONFIG_EISA
-	ret = eisa_driver_register(&el3_eisa_driver);
-	if (!ret)
-		eisa_registered = 1;
-#endif
-
-#ifdef CONFIG_PNP
-	if (pnp_registered)
-		ret = 0;
-#endif
-	if (isa_registered)
-		ret = 0;
-#ifdef CONFIG_EISA
-	if (eisa_registered)
-		ret = 0;
-#endif
-	return ret;
-}
-
-static void __exit el3_cleanup_module(void)
-{
-#ifdef CONFIG_PNP
-	if (pnp_registered)
-		pnp_unregister_driver(&el3_pnp_driver);
-#endif
-	if (isa_registered)
-		isa_unregister_driver(&el3_isa_driver);
-	if (id_port)
-		release_region(id_port, 1);
-#ifdef CONFIG_EISA
-	if (eisa_registered)
-		eisa_driver_unregister(&el3_eisa_driver);
-#endif
-}
-
-module_init (el3_init_module);
-module_exit (el3_cleanup_module);
diff --git a/drivers/net/ethernet/3com/Kconfig b/drivers/net/ethernet/3com/Kconfig
index 1fbab79e2be4..c05a1b63c1c9 100644
--- a/drivers/net/ethernet/3com/Kconfig
+++ b/drivers/net/ethernet/3com/Kconfig
@@ -17,20 +17,6 @@ config NET_VENDOR_3COM
 
 if NET_VENDOR_3COM
 
-config EL3
-	tristate "3c509/3c579 \"EtherLink III\" support"
-	depends on (ISA || EISA)
-	help
-	  If you have a network (Ethernet) card belonging to the 3Com
-	  EtherLinkIII series, say Y here.
-
-	  If your card is not working you may need to use the DOS
-	  setup disk to disable Plug & Play mode, and to select the default
-	  media type.
-
-	  To compile this driver as a module, choose M here. The module
-	  will be called 3c509.
-
 config 3C515
 	tristate "3c515 ISA \"Fast EtherLink\""
 	depends on ISA && ISA_DMA_API && !PPC32
diff --git a/drivers/net/ethernet/3com/Makefile b/drivers/net/ethernet/3com/Makefile
index f8b73babc510..f7623fa2d441 100644
--- a/drivers/net/ethernet/3com/Makefile
+++ b/drivers/net/ethernet/3com/Makefile
@@ -3,7 +3,6 @@
 # Makefile for the 3Com Ethernet device drivers
 #
 
-obj-$(CONFIG_EL3) += 3c509.o
 obj-$(CONFIG_3C515) += 3c515.o
 obj-$(CONFIG_PCMCIA_3C589) += 3c589_cs.o
 obj-$(CONFIG_PCMCIA_3C574) += 3c574_cs.o

-- 
2.53.0


^ permalink raw reply related

* [PATCH net v2 00/15] Remove a number of ISA and PCMCIA Ethernet drivers
From: Andrew Lunn @ 2026-04-22 18:01 UTC (permalink / raw)
  To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Simon Horman, Jonathan Corbet, Shuah Khan
  Cc: Geert Uytterhoeven, Michael Fritscher, Byron Stanoszek,
	Daniel Palmer, linux-kernel, netdev, linux-doc, Andrew Lunn

These old drivers have not been much of a Maintenance burden until
recently. Now there are more newbies using AI and fuzzers finding
issues, resulting in more work for Maintainers. Fixing these old
drivers make little sense, if it is not clear they have users.

These mostly ISA and PCMCIA Ethernet devices, mostly from the last
century, a couple from 2001 or 2002. It seems unlikely they are still
used. However, remove them one patch at a time so they can be brought
back if somebody still has the hardware, runs modern kernels and wants
to take up the roll of driver Maintainer.

Signed-off-by: Andrew Lunn <andrew@lunn.ch>
---
Changes in v2:
- Drop removal of 3c59x and xirc2ps, MVME147
- Remove some documents which belong to the drivers being removed.
- Link to v1: https://lore.kernel.org/r/20260421-v7-0-0-net-next-driver-removal-v1-v1-0-69517c689d1f@lunn.ch

---
Andrew Lunn (15):
      drivers: net: 3com: 3c509: Remove this driver
      drivers: net: 3com: 3c515: Remove this driver
      drivers: net: 3com: 3c574: Remove this driver
      drivers: net: 3com: 3c589: Remove this driver
      drivers: net: amd: lance: Remove this driver
      drivers: net: amd: nmclan: Remove this driver
      drivers: net: smsc: smc9194: Remove this driver
      drivers: net: smsc: smc91c92: Remove this driver
      drivers: net: cirrus: cs89x0: Remove this driver
      drivers: net: cirrus: mac89x0: Remove this driver
      drivers: net: fujitsu: fmvj18x: Remove this driver
      drivers: net: 8390: AX88190: Remove this driver
      drivers: net: 8390: pcnet: Remove this driver
      drivers: net: 8390: ultra: Remove this driver
      drivers: net: 8390: wd80x3: Remove this driver

 .../device_drivers/ethernet/3com/3c509.rst         |  249 ---
 .../device_drivers/ethernet/cirrus/cs89x0.rst      |  647 ------
 .../device_drivers/ethernet/smsc/smc9.rst          |   48 -
 drivers/net/ethernet/3com/3c509.c                  | 1448 --------------
 drivers/net/ethernet/3com/3c515.c                  | 1566 ---------------
 drivers/net/ethernet/3com/3c574_cs.c               | 1164 -----------
 drivers/net/ethernet/3com/3c589_cs.c               |  974 ---------
 drivers/net/ethernet/3com/Kconfig                  |   45 -
 drivers/net/ethernet/3com/Makefile                 |    4 -
 drivers/net/ethernet/8390/Kconfig                  |   52 -
 drivers/net/ethernet/8390/Makefile                 |    4 -
 drivers/net/ethernet/8390/axnet_cs.c               | 1707 ----------------
 drivers/net/ethernet/8390/pcnet_cs.c               | 1717 ----------------
 drivers/net/ethernet/8390/smc-ultra.c              |  630 ------
 drivers/net/ethernet/8390/wd.c                     |  575 ------
 drivers/net/ethernet/Kconfig                       |    1 -
 drivers/net/ethernet/amd/Kconfig                   |   21 -
 drivers/net/ethernet/amd/Makefile                  |    2 -
 drivers/net/ethernet/amd/lance.c                   | 1317 -------------
 drivers/net/ethernet/amd/nmclan_cs.c               | 1508 --------------
 drivers/net/ethernet/cirrus/Kconfig                |   40 -
 drivers/net/ethernet/cirrus/Makefile               |    2 -
 drivers/net/ethernet/cirrus/cs89x0.c               | 1915 ------------------
 drivers/net/ethernet/cirrus/cs89x0.h               |  461 -----
 drivers/net/ethernet/cirrus/mac89x0.c              |  577 ------
 drivers/net/ethernet/fujitsu/Kconfig               |   30 -
 drivers/net/ethernet/fujitsu/Makefile              |    6 -
 drivers/net/ethernet/fujitsu/fmvj18x_cs.c          | 1176 -----------
 drivers/net/ethernet/smsc/Kconfig                  |   27 -
 drivers/net/ethernet/smsc/Makefile                 |    2 -
 drivers/net/ethernet/smsc/smc9194.c                | 1535 ---------------
 drivers/net/ethernet/smsc/smc91c92_cs.c            | 2059 --------------------
 32 files changed, 21509 deletions(-)
---
base-commit: 1f5ffc672165ff851063a5fd044b727ab2517ae3
change-id: 20260421-v7-0-0-net-next-driver-removal-v1-47c88c987b34

Best regards,
-- 
Andrew Lunn <andrew@lunn.ch>


^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox