* [PATCH net-next 5/9] atm: remove SVC socket support and the signaling daemon interface
From: Jakub Kicinski @ 2026-06-13 20:10 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, 3chas3, mitch,
linux-atm-general, dwmw2, Jakub Kicinski
In-Reply-To: <20260613201032.77274-1-kuba@kernel.org>
ATM switched virtual circuits (SVCs) are set up and torn down by a
user-space signaling daemon (atmsigd) which the kernel talks to over
a dedicated "sigd" socket: the kernel marshals Q.2931-style requests
(as_connect, as_listen, as_accept, as_close, ...) to the daemon and
applies the results to PF_ATMSVC sockets. This is the machinery behind
classical SVC use and was the foundation for LANE / MPOA, all of which
have been removed.
DSL deployments do not use any of this. PPPoATM and BR2684 run over
permanent virtual circuits (PF_ATMPVC) with a statically configured
VPI/VCI; no atmsigd, no Q.2931. Neither remaining ATM driver
(solos-pci, the USB DSL modems) is reachable through the SVC path.
Remove the SVC socket family and the signaling interface:
- delete net/atm/svc.c, net/atm/signaling.c and signaling.h
- drop atmsvc_init()/atmsvc_exit() and the PF_ATMSVC registration and
module alias
- drop the ATMSIGD_CTRL ioctl (sigd_attach) and the /proc/net/atm/svc
file
- fold the SVC branch out of atm_change_qos(); all sockets are PVCs now
The obsolete ATM_SETSC ioctl stub is left in place (it already just
warns and returns 0), as is the struct atm_vcc SVC bookkeeping shared
with the queueing layer.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
net/atm/Makefile | 2 +-
include/linux/atmdev.h | 38 +--
net/atm/common.h | 5 -
net/atm/signaling.h | 31 --
net/atm/br2684.c | 3 +-
net/atm/common.c | 35 +--
net/atm/ioctl.c | 32 --
net/atm/pppoatm.c | 3 +-
net/atm/proc.c | 56 ----
net/atm/resources.c | 1 -
net/atm/signaling.c | 297 ------------------
net/atm/svc.c | 694 -----------------------------------------
12 files changed, 12 insertions(+), 1185 deletions(-)
delete mode 100644 net/atm/signaling.h
delete mode 100644 net/atm/signaling.c
delete mode 100644 net/atm/svc.c
diff --git a/net/atm/Makefile b/net/atm/Makefile
index 5ed48d50df35..0a14ea7d4683 100644
--- a/net/atm/Makefile
+++ b/net/atm/Makefile
@@ -3,7 +3,7 @@
# Makefile for the ATM Protocol Families.
#
-atm-y := pvc.o signaling.o svc.o ioctl.o common.o atm_misc.o raw.o resources.o atm_sysfs.o
+atm-y := pvc.o ioctl.o common.o atm_misc.o raw.o resources.o atm_sysfs.o
obj-$(CONFIG_ATM) += atm.o
obj-$(CONFIG_ATM_BR2684) += br2684.o
diff --git a/include/linux/atmdev.h b/include/linux/atmdev.h
index 7abbd23fada6..59477676063c 100644
--- a/include/linux/atmdev.h
+++ b/include/linux/atmdev.h
@@ -51,33 +51,11 @@ enum {
driver, cleared by anybody. */
ATM_VF_PARTIAL, /* resources are bound to PVC (partial PVC
setup), controlled by socket layer */
- ATM_VF_REGIS, /* registered with demon, controlled by SVC
- socket layer */
- ATM_VF_BOUND, /* local SAP is set, controlled by SVC socket
- layer */
- ATM_VF_RELEASED, /* demon has indicated/requested release,
- controlled by SVC socket layer */
ATM_VF_HASQOS, /* QOS parameters have been set */
- ATM_VF_LISTEN, /* socket is used for listening */
- ATM_VF_META, /* SVC socket isn't used for normal data
- traffic and doesn't depend on signaling
- to be available */
- ATM_VF_SESSION, /* VCC is p2mp session control descriptor */
- ATM_VF_HASSAP, /* SAP has been set */
- ATM_VF_CLOSE, /* asynchronous close - treat like VF_RELEASED*/
- ATM_VF_WAITING, /* waiting for reply from sigd */
- ATM_VF_IS_CLIP, /* in use by CLIP protocol */
+ ATM_VF_CLOSE, /* asynchronous close - VC is being torn down */
};
-#define ATM_VF2VS(flags) \
- (test_bit(ATM_VF_READY,&(flags)) ? ATM_VS_CONNECTED : \
- test_bit(ATM_VF_RELEASED,&(flags)) ? ATM_VS_CLOSING : \
- test_bit(ATM_VF_LISTEN,&(flags)) ? ATM_VS_LISTEN : \
- test_bit(ATM_VF_REGIS,&(flags)) ? ATM_VS_INUSE : \
- test_bit(ATM_VF_BOUND,&(flags)) ? ATM_VS_BOUND : ATM_VS_IDLE)
-
-
enum {
ATM_DF_REMOVED, /* device was removed from atm_devs list */
};
@@ -100,7 +78,6 @@ struct atm_vcc {
unsigned long atm_options; /* ATM layer options */
struct atm_dev *dev; /* device back pointer */
struct atm_qos qos; /* QOS */
- struct atm_sap sap; /* SAP */
void (*release_cb)(struct atm_vcc *vcc); /* release_sock callback */
void (*push)(struct atm_vcc *vcc,struct sk_buff *skb);
void (*pop)(struct atm_vcc *vcc,struct sk_buff *skb); /* optional */
@@ -109,16 +86,8 @@ struct atm_vcc {
void *proto_data; /* per-protocol data */
struct k_atm_aal_stats *stats; /* pointer to AAL stats group */
struct module *owner; /* owner of ->push function */
- /* SVC part --- may move later ------------------------------------- */
- short itf; /* interface number */
- struct sockaddr_atmsvc local;
- struct sockaddr_atmsvc remote;
- /* Multipoint part ------------------------------------------------- */
- struct atm_vcc *session; /* session VCC descriptor */
- /* Other stuff ----------------------------------------------------- */
- void *user_back; /* user backlink - not touched by */
- /* native ATM stack. Currently used */
- /* by CLIP and sch_atm. */
+ void *user_back; /* user backlink - not touched by the */
+ /* native ATM stack, used by sch_atm */
};
static inline struct atm_vcc *atm_sk(struct sock *sk)
@@ -151,7 +120,6 @@ struct atm_dev {
char signal; /* signal status (ATM_PHY_SIG_*) */
int link_rate; /* link rate (default: OC3) */
refcount_t refcnt; /* reference count */
- spinlock_t lock; /* protect internal members */
#ifdef CONFIG_PROC_FS
struct proc_dir_entry *proc_entry; /* proc entry */
char *proc_name; /* proc entry name */
diff --git a/net/atm/common.h b/net/atm/common.h
index ae4502abf028..11cb51dd7dbb 100644
--- a/net/atm/common.h
+++ b/net/atm/common.h
@@ -28,8 +28,6 @@ void vcc_process_recv_queue(struct atm_vcc *vcc);
int atmpvc_init(void);
void atmpvc_exit(void);
-int atmsvc_init(void);
-void atmsvc_exit(void);
int atm_sysfs_init(void);
void atm_sysfs_exit(void);
@@ -48,9 +46,6 @@ static inline void atm_proc_exit(void)
}
#endif /* CONFIG_PROC_FS */
-/* SVC */
-int svc_change_qos(struct atm_vcc *vcc,struct atm_qos *qos);
-
void atm_dev_release_vccs(struct atm_dev *dev);
#endif
diff --git a/net/atm/signaling.h b/net/atm/signaling.h
deleted file mode 100644
index 2df8220f7ab5..000000000000
--- a/net/atm/signaling.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/* net/atm/signaling.h - ATM signaling */
-
-/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
-
-
-#ifndef NET_ATM_SIGNALING_H
-#define NET_ATM_SIGNALING_H
-
-#include <linux/atm.h>
-#include <linux/atmdev.h>
-#include <linux/atmsvc.h>
-
-
-extern struct atm_vcc *sigd; /* needed in svc_release */
-
-
-/*
- * sigd_enq is a wrapper for sigd_enq2, covering the more common cases, and
- * avoiding huge lists of null values.
- */
-
-void sigd_enq2(struct atm_vcc *vcc,enum atmsvc_msg_type type,
- struct atm_vcc *listen_vcc,const struct sockaddr_atmpvc *pvc,
- const struct sockaddr_atmsvc *svc,const struct atm_qos *qos,int reply);
-void sigd_enq(struct atm_vcc *vcc,enum atmsvc_msg_type type,
- struct atm_vcc *listen_vcc,const struct sockaddr_atmpvc *pvc,
- const struct sockaddr_atmsvc *svc);
-int sigd_attach(struct atm_vcc *vcc);
-
-#endif
diff --git a/net/atm/br2684.c b/net/atm/br2684.c
index 6580d67c3456..05712c28386a 100644
--- a/net/atm/br2684.c
+++ b/net/atm/br2684.c
@@ -311,8 +311,7 @@ static netdev_tx_t br2684_start_xmit(struct sk_buff *skb,
bh_lock_sock(sk_atm(atmvcc));
- if (test_bit(ATM_VF_RELEASED, &atmvcc->flags) ||
- test_bit(ATM_VF_CLOSE, &atmvcc->flags) ||
+ if (test_bit(ATM_VF_CLOSE, &atmvcc->flags) ||
!test_bit(ATM_VF_READY, &atmvcc->flags)) {
dev->stats.tx_dropped++;
dev_kfree_skb(skb);
diff --git a/net/atm/common.c b/net/atm/common.c
index 7d5b7c39b80b..650814d0a56c 100644
--- a/net/atm/common.c
+++ b/net/atm/common.c
@@ -30,7 +30,6 @@
#include "resources.h" /* atm_find_dev */
#include "common.h" /* prototypes */
#include "protocols.h" /* atm_init_<transport> */
-#include "signaling.h" /* for WAITING and sigd_attach */
struct hlist_head vcc_hash[VCC_HTABLE_SIZE];
EXPORT_SYMBOL(vcc_hash);
@@ -154,8 +153,6 @@ int vcc_create(struct net *net, struct socket *sock, int protocol, int family, i
vcc = atm_sk(sk);
vcc->dev = NULL;
- memset(&vcc->local, 0, sizeof(struct sockaddr_atmsvc));
- memset(&vcc->remote, 0, sizeof(struct sockaddr_atmsvc));
vcc->qos.txtp.max_sdu = 1 << 16; /* for meta VCs */
refcount_set(&sk->sk_wmem_alloc, SK_WMEM_ALLOC_BIAS);
atomic_set(&sk->sk_rmem_alloc, 0);
@@ -216,7 +213,6 @@ void vcc_release_async(struct atm_vcc *vcc, int reply)
set_bit(ATM_VF_CLOSE, &vcc->flags);
sk->sk_shutdown |= RCV_SHUTDOWN;
sk->sk_err = -reply;
- clear_bit(ATM_VF_WAITING, &vcc->flags);
sk->sk_state_change(sk);
}
EXPORT_SYMBOL(vcc_release_async);
@@ -527,8 +523,7 @@ int vcc_recvmsg(struct socket *sock, struct msghdr *msg, size_t size,
return -EOPNOTSUPP;
vcc = ATM_SD(sock);
- if (test_bit(ATM_VF_RELEASED, &vcc->flags) ||
- test_bit(ATM_VF_CLOSE, &vcc->flags) ||
+ if (test_bit(ATM_VF_CLOSE, &vcc->flags) ||
!test_bit(ATM_VF_READY, &vcc->flags))
return 0;
@@ -575,8 +570,7 @@ int vcc_sendmsg(struct socket *sock, struct msghdr *m, size_t size)
goto out;
}
vcc = ATM_SD(sock);
- if (test_bit(ATM_VF_RELEASED, &vcc->flags) ||
- test_bit(ATM_VF_CLOSE, &vcc->flags) ||
+ if (test_bit(ATM_VF_CLOSE, &vcc->flags) ||
!test_bit(ATM_VF_READY, &vcc->flags)) {
error = -EPIPE;
send_sig(SIGPIPE, current, 0);
@@ -604,8 +598,7 @@ int vcc_sendmsg(struct socket *sock, struct msghdr *m, size_t size)
error = -ERESTARTSYS;
break;
}
- if (test_bit(ATM_VF_RELEASED, &vcc->flags) ||
- test_bit(ATM_VF_CLOSE, &vcc->flags) ||
+ if (test_bit(ATM_VF_CLOSE, &vcc->flags) ||
!test_bit(ATM_VF_READY, &vcc->flags)) {
error = -EPIPE;
send_sig(SIGPIPE, current, 0);
@@ -665,8 +658,7 @@ __poll_t vcc_poll(struct file *file, struct socket *sock, poll_table *wait)
if (sk->sk_err)
mask = EPOLLERR;
- if (test_bit(ATM_VF_RELEASED, &vcc->flags) ||
- test_bit(ATM_VF_CLOSE, &vcc->flags))
+ if (test_bit(ATM_VF_CLOSE, &vcc->flags))
mask |= EPOLLHUP;
/* readable? */
@@ -674,10 +666,6 @@ __poll_t vcc_poll(struct file *file, struct socket *sock, poll_table *wait)
mask |= EPOLLIN | EPOLLRDNORM;
/* writable? */
- if (sock->state == SS_CONNECTING &&
- test_bit(ATM_VF_WAITING, &vcc->flags))
- return mask;
-
if (vcc->qos.txtp.traffic_class != ATM_NONE &&
vcc_writable(sk))
mask |= EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND;
@@ -704,9 +692,7 @@ static int atm_change_qos(struct atm_vcc *vcc, struct atm_qos *qos)
return error;
if (!vcc->dev->ops->change_qos)
return -EOPNOTSUPP;
- if (sk_atm(vcc)->sk_family == AF_ATMPVC)
- return vcc->dev->ops->change_qos(vcc, qos, ATM_MF_SET);
- return svc_change_qos(vcc, qos);
+ return vcc->dev->ops->change_qos(vcc, qos, ATM_MF_SET);
}
static int check_tp(const struct atm_trafprm *tp)
@@ -854,15 +840,10 @@ static int __init atm_init(void)
pr_err("atmpvc_init() failed with %d\n", error);
goto out_unregister_vcc_proto;
}
- error = atmsvc_init();
- if (error < 0) {
- pr_err("atmsvc_init() failed with %d\n", error);
- goto out_atmpvc_exit;
- }
error = atm_proc_init();
if (error < 0) {
pr_err("atm_proc_init() failed with %d\n", error);
- goto out_atmsvc_exit;
+ goto out_atmpvc_exit;
}
error = atm_sysfs_init();
if (error < 0) {
@@ -873,8 +854,6 @@ static int __init atm_init(void)
return error;
out_atmproc_exit:
atm_proc_exit();
-out_atmsvc_exit:
- atmsvc_exit();
out_atmpvc_exit:
atmpvc_exit();
out_unregister_vcc_proto:
@@ -886,7 +865,6 @@ static void __exit atm_exit(void)
{
atm_proc_exit();
atm_sysfs_exit();
- atmsvc_exit();
atmpvc_exit();
proto_unregister(&vcc_proto);
}
@@ -898,4 +876,3 @@ module_exit(atm_exit);
MODULE_DESCRIPTION("Asynchronous Transfer Mode (ATM) networking core");
MODULE_LICENSE("GPL");
MODULE_ALIAS_NETPROTO(PF_ATMPVC);
-MODULE_ALIAS_NETPROTO(PF_ATMSVC);
diff --git a/net/atm/ioctl.c b/net/atm/ioctl.c
index 97f20cd051ed..11f6d236f5d6 100644
--- a/net/atm/ioctl.c
+++ b/net/atm/ioctl.c
@@ -13,13 +13,11 @@
#include <linux/atmdev.h>
#include <linux/atmarp.h> /* manifest constants */
#include <linux/capability.h>
-#include <linux/atmsvc.h>
#include <linux/mutex.h>
#include <asm/ioctls.h>
#include <net/compat.h>
#include "resources.h"
-#include "signaling.h" /* for WAITING and sigd_attach */
#include "common.h"
@@ -86,36 +84,6 @@ static int do_vcc_ioctl(struct socket *sock, unsigned int cmd,
current->comm, task_pid_nr(current));
error = 0;
goto done;
- case ATMSIGD_CTRL:
- if (!capable(CAP_NET_ADMIN)) {
- error = -EPERM;
- goto done;
- }
- /*
- * The user/kernel protocol for exchanging signalling
- * info uses kernel pointers as opaque references,
- * so the holder of the file descriptor can scribble
- * on the kernel... so we should make sure that we
- * have the same privileges that /proc/kcore needs
- */
- if (!capable(CAP_SYS_RAWIO)) {
- error = -EPERM;
- goto done;
- }
-#ifdef CONFIG_COMPAT
- /* WTF? I don't even want to _think_ about making this
- work for 32-bit userspace. TBH I don't really want
- to think about it at all. dwmw2. */
- if (compat) {
- net_warn_ratelimited("32-bit task cannot be atmsigd\n");
- error = -EINVAL;
- goto done;
- }
-#endif
- error = sigd_attach(vcc);
- if (!error)
- sock->state = SS_CONNECTED;
- goto done;
case ATM_SETBACKEND:
case ATM_NEWBACKENDIF:
{
diff --git a/net/atm/pppoatm.c b/net/atm/pppoatm.c
index e3c422dc533a..6da52d12df68 100644
--- a/net/atm/pppoatm.c
+++ b/net/atm/pppoatm.c
@@ -308,8 +308,7 @@ static int pppoatm_send(struct ppp_channel *chan, struct sk_buff *skb)
test_and_set_bit(BLOCKED, &pvcc->blocked);
goto nospace;
}
- if (test_bit(ATM_VF_RELEASED, &vcc->flags) ||
- test_bit(ATM_VF_CLOSE, &vcc->flags) ||
+ if (test_bit(ATM_VF_CLOSE, &vcc->flags) ||
!test_bit(ATM_VF_READY, &vcc->flags)) {
bh_unlock_sock(sk_atm(vcc));
kfree_skb(skb);
diff --git a/net/atm/proc.c b/net/atm/proc.c
index b650da764a23..8f20b49b9c02 100644
--- a/net/atm/proc.c
+++ b/net/atm/proc.c
@@ -29,7 +29,6 @@
#include <linux/atomic.h>
#include "resources.h"
#include "common.h" /* atm_proc_init prototype */
-#include "signaling.h" /* to get sigd - ugly too */
static ssize_t proc_dev_atm_read(struct file *file, char __user *buf,
size_t count, loff_t *pos);
@@ -156,13 +155,6 @@ static void pvc_info(struct seq_file *seq, struct atm_vcc *vcc)
seq_putc(seq, '\n');
}
-static const char *vcc_state(struct atm_vcc *vcc)
-{
- static const char *const map[] = { ATM_VS2TXT_MAP };
-
- return map[ATM_VF2VS(vcc->flags)];
-}
-
static void vcc_info(struct seq_file *seq, struct atm_vcc *vcc)
{
struct sock *sk = sk_atm(vcc);
@@ -177,9 +169,6 @@ static void vcc_info(struct seq_file *seq, struct atm_vcc *vcc)
case AF_ATMPVC:
seq_printf(seq, "PVC");
break;
- case AF_ATMSVC:
- seq_printf(seq, "SVC");
- break;
default:
seq_printf(seq, "%3d", sk->sk_family);
}
@@ -190,26 +179,6 @@ static void vcc_info(struct seq_file *seq, struct atm_vcc *vcc)
refcount_read(&sk->sk_refcnt));
}
-static void svc_info(struct seq_file *seq, struct atm_vcc *vcc)
-{
- if (!vcc->dev)
- seq_printf(seq, sizeof(void *) == 4 ?
- "N/A@%pK%10s" : "N/A@%pK%2s", vcc, "");
- else
- seq_printf(seq, "%3d %3d %5d ",
- vcc->dev->number, vcc->vpi, vcc->vci);
- seq_printf(seq, "%-10s ", vcc_state(vcc));
- seq_printf(seq, "%s%s", vcc->remote.sas_addr.pub,
- *vcc->remote.sas_addr.pub && *vcc->remote.sas_addr.prv ? "+" : "");
- if (*vcc->remote.sas_addr.prv) {
- int i;
-
- for (i = 0; i < ATM_ESA_LEN; i++)
- seq_printf(seq, "%02x", vcc->remote.sas_addr.prv[i]);
- }
- seq_putc(seq, '\n');
-}
-
static int atm_dev_seq_show(struct seq_file *seq, void *v)
{
static char atm_dev_banner[] =
@@ -278,29 +247,6 @@ static const struct seq_operations vcc_seq_ops = {
.show = vcc_seq_show,
};
-static int svc_seq_show(struct seq_file *seq, void *v)
-{
- static const char atm_svc_banner[] =
- "Itf VPI VCI State Remote\n";
-
- if (v == SEQ_START_TOKEN)
- seq_puts(seq, atm_svc_banner);
- else {
- struct vcc_state *state = seq->private;
- struct atm_vcc *vcc = atm_sk(state->sk);
-
- svc_info(seq, vcc);
- }
- return 0;
-}
-
-static const struct seq_operations svc_seq_ops = {
- .start = vcc_seq_start,
- .next = vcc_seq_next,
- .stop = vcc_seq_stop,
- .show = svc_seq_show,
-};
-
static ssize_t proc_dev_atm_read(struct file *file, char __user *buf,
size_t count, loff_t *pos)
{
@@ -376,8 +322,6 @@ int __init atm_proc_init(void)
proc_create_seq("devices", 0444, atm_proc_root, &atm_dev_seq_ops);
proc_create_seq_private("pvc", 0444, atm_proc_root, &pvc_seq_ops,
sizeof(struct vcc_state), (void *)(uintptr_t)PF_ATMPVC);
- proc_create_seq_private("svc", 0444, atm_proc_root, &svc_seq_ops,
- sizeof(struct vcc_state), (void *)(uintptr_t)PF_ATMSVC);
proc_create_seq_private("vc", 0444, atm_proc_root, &vcc_seq_ops,
sizeof(struct vcc_state), NULL);
return 0;
diff --git a/net/atm/resources.c b/net/atm/resources.c
index a08f65aacf75..3fa4b0df63a8 100644
--- a/net/atm/resources.c
+++ b/net/atm/resources.c
@@ -40,7 +40,6 @@ static struct atm_dev *__alloc_atm_dev(const char *type)
dev->type = type;
dev->signal = ATM_PHY_SIG_UNKNOWN;
dev->link_rate = ATM_OC3_PCR;
- spin_lock_init(&dev->lock);
return dev;
}
diff --git a/net/atm/signaling.c b/net/atm/signaling.c
deleted file mode 100644
index b991d937205a..000000000000
--- a/net/atm/signaling.c
+++ /dev/null
@@ -1,297 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/* net/atm/signaling.c - ATM signaling */
-
-/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__
-
-#include <linux/errno.h> /* error codes */
-#include <linux/kernel.h> /* printk */
-#include <linux/skbuff.h>
-#include <linux/wait.h>
-#include <linux/sched.h> /* jiffies and HZ */
-#include <linux/atm.h> /* ATM stuff */
-#include <linux/atmsap.h>
-#include <linux/atmsvc.h>
-#include <linux/atmdev.h>
-#include <linux/bitops.h>
-#include <linux/slab.h>
-
-#include "resources.h"
-#include "signaling.h"
-
-struct atm_vcc *sigd = NULL;
-
-/*
- * find_get_vcc - validate and get a reference to a vcc pointer
- * @vcc: the vcc pointer to validate
- *
- * This function validates that @vcc points to a registered VCC in vcc_hash.
- * If found, it increments the socket reference count and returns the vcc.
- * The caller must call sock_put(sk_atm(vcc)) when done.
- *
- * Returns the vcc pointer if valid, NULL otherwise.
- */
-static struct atm_vcc *find_get_vcc(struct atm_vcc *vcc)
-{
- int i;
-
- read_lock(&vcc_sklist_lock);
- for (i = 0; i < VCC_HTABLE_SIZE; i++) {
- struct sock *s;
-
- sk_for_each(s, &vcc_hash[i]) {
- if (atm_sk(s) == vcc) {
- sock_hold(s);
- read_unlock(&vcc_sklist_lock);
- return vcc;
- }
- }
- }
- read_unlock(&vcc_sklist_lock);
- return NULL;
-}
-
-static void sigd_put_skb(struct sk_buff *skb)
-{
- if (!sigd) {
- pr_debug("atmsvc: no signaling daemon\n");
- kfree_skb(skb);
- return;
- }
- atm_force_charge(sigd, skb->truesize);
- skb_queue_tail(&sk_atm(sigd)->sk_receive_queue, skb);
- sk_atm(sigd)->sk_data_ready(sk_atm(sigd));
-}
-
-static void modify_qos(struct atm_vcc *vcc, struct atmsvc_msg *msg)
-{
- struct sk_buff *skb;
-
- if (test_bit(ATM_VF_RELEASED, &vcc->flags) ||
- !test_bit(ATM_VF_READY, &vcc->flags))
- return;
- msg->type = as_error;
- if (!vcc->dev->ops->change_qos)
- msg->reply = -EOPNOTSUPP;
- else {
- /* should lock VCC */
- msg->reply = vcc->dev->ops->change_qos(vcc, &msg->qos,
- msg->reply);
- if (!msg->reply)
- msg->type = as_okay;
- }
- /*
- * Should probably just turn around the old skb. But then, the buffer
- * space accounting needs to follow the change too. Maybe later.
- */
- while (!(skb = alloc_skb(sizeof(struct atmsvc_msg), GFP_KERNEL)))
- schedule();
- *(struct atmsvc_msg *)skb_put(skb, sizeof(struct atmsvc_msg)) = *msg;
- sigd_put_skb(skb);
-}
-
-static int sigd_send(struct atm_vcc *vcc, struct sk_buff *skb)
-{
- struct atmsvc_msg *msg;
- struct atm_vcc *session_vcc;
- struct sock *sk;
-
- msg = (struct atmsvc_msg *) skb->data;
- WARN_ON(refcount_sub_and_test(skb->truesize, &sk_atm(vcc)->sk_wmem_alloc));
-
- vcc = find_get_vcc(*(struct atm_vcc **)&msg->vcc);
- if (!vcc) {
- pr_debug("invalid vcc pointer in msg\n");
- dev_kfree_skb(skb);
- return -EINVAL;
- }
-
- pr_debug("%d (0x%lx)\n", (int)msg->type, (unsigned long)vcc);
- sk = sk_atm(vcc);
-
- switch (msg->type) {
- case as_okay:
- sk->sk_err = -msg->reply;
- clear_bit(ATM_VF_WAITING, &vcc->flags);
- if (!*vcc->local.sas_addr.prv && !*vcc->local.sas_addr.pub) {
- vcc->local.sas_family = AF_ATMSVC;
- memcpy(vcc->local.sas_addr.prv,
- msg->local.sas_addr.prv, ATM_ESA_LEN);
- memcpy(vcc->local.sas_addr.pub,
- msg->local.sas_addr.pub, ATM_E164_LEN + 1);
- }
- session_vcc = vcc->session ? vcc->session : vcc;
- if (session_vcc->vpi || session_vcc->vci)
- break;
- session_vcc->itf = msg->pvc.sap_addr.itf;
- session_vcc->vpi = msg->pvc.sap_addr.vpi;
- session_vcc->vci = msg->pvc.sap_addr.vci;
- if (session_vcc->vpi || session_vcc->vci)
- session_vcc->qos = msg->qos;
- break;
- case as_error:
- clear_bit(ATM_VF_REGIS, &vcc->flags);
- clear_bit(ATM_VF_READY, &vcc->flags);
- sk->sk_err = -msg->reply;
- clear_bit(ATM_VF_WAITING, &vcc->flags);
- break;
- case as_indicate:
- /* Release the reference from msg->vcc, we'll use msg->listen_vcc instead */
- sock_put(sk);
-
- vcc = find_get_vcc(*(struct atm_vcc **)&msg->listen_vcc);
- if (!vcc) {
- pr_debug("invalid listen_vcc pointer in msg\n");
- dev_kfree_skb(skb);
- return -EINVAL;
- }
-
- sk = sk_atm(vcc);
- pr_debug("as_indicate!!!\n");
- lock_sock(sk);
- if (sk_acceptq_is_full(sk)) {
- sigd_enq(NULL, as_reject, vcc, NULL, NULL);
- dev_kfree_skb(skb);
- goto as_indicate_complete;
- }
- sk_acceptq_added(sk);
- skb_queue_tail(&sk->sk_receive_queue, skb);
- pr_debug("waking sk_sleep(sk) 0x%p\n", sk_sleep(sk));
- sk->sk_state_change(sk);
-as_indicate_complete:
- release_sock(sk);
- /* Paired with find_get_vcc(msg->listen_vcc) above */
- sock_put(sk);
- return 0;
- case as_close:
- set_bit(ATM_VF_RELEASED, &vcc->flags);
- vcc_release_async(vcc, msg->reply);
- goto out;
- case as_modify:
- modify_qos(vcc, msg);
- break;
- case as_addparty:
- case as_dropparty:
- WRITE_ONCE(sk->sk_err_soft, -msg->reply);
- /* < 0 failure, otherwise ep_ref */
- clear_bit(ATM_VF_WAITING, &vcc->flags);
- break;
- default:
- pr_alert("bad message type %d\n", (int)msg->type);
- dev_kfree_skb(skb);
- /* Paired with find_get_vcc(msg->vcc) above */
- sock_put(sk);
- return -EINVAL;
- }
- sk->sk_state_change(sk);
-out:
- dev_kfree_skb(skb);
- /* Paired with find_get_vcc(msg->vcc) above */
- sock_put(sk);
- return 0;
-}
-
-void sigd_enq2(struct atm_vcc *vcc, enum atmsvc_msg_type type,
- struct atm_vcc *listen_vcc, const struct sockaddr_atmpvc *pvc,
- const struct sockaddr_atmsvc *svc, const struct atm_qos *qos,
- int reply)
-{
- struct sk_buff *skb;
- struct atmsvc_msg *msg;
- static unsigned int session = 0;
-
- pr_debug("%d (0x%p)\n", (int)type, vcc);
- while (!(skb = alloc_skb(sizeof(struct atmsvc_msg), GFP_KERNEL)))
- schedule();
- msg = skb_put_zero(skb, sizeof(struct atmsvc_msg));
- msg->type = type;
- *(struct atm_vcc **) &msg->vcc = vcc;
- *(struct atm_vcc **) &msg->listen_vcc = listen_vcc;
- msg->reply = reply;
- if (qos)
- msg->qos = *qos;
- if (vcc)
- msg->sap = vcc->sap;
- if (svc)
- msg->svc = *svc;
- if (vcc)
- msg->local = vcc->local;
- if (pvc)
- msg->pvc = *pvc;
- if (vcc) {
- if (type == as_connect && test_bit(ATM_VF_SESSION, &vcc->flags))
- msg->session = ++session;
- /* every new pmp connect gets the next session number */
- }
- sigd_put_skb(skb);
- if (vcc)
- set_bit(ATM_VF_REGIS, &vcc->flags);
-}
-
-void sigd_enq(struct atm_vcc *vcc, enum atmsvc_msg_type type,
- struct atm_vcc *listen_vcc, const struct sockaddr_atmpvc *pvc,
- const struct sockaddr_atmsvc *svc)
-{
- sigd_enq2(vcc, type, listen_vcc, pvc, svc, vcc ? &vcc->qos : NULL, 0);
- /* other ISP applications may use "reply" */
-}
-
-static void purge_vcc(struct atm_vcc *vcc)
-{
- if (sk_atm(vcc)->sk_family == PF_ATMSVC &&
- !test_bit(ATM_VF_META, &vcc->flags)) {
- set_bit(ATM_VF_RELEASED, &vcc->flags);
- clear_bit(ATM_VF_REGIS, &vcc->flags);
- vcc_release_async(vcc, -EUNATCH);
- }
-}
-
-static void sigd_close(struct atm_vcc *vcc)
-{
- struct sock *s;
- int i;
-
- pr_debug("\n");
- sigd = NULL;
- if (skb_peek(&sk_atm(vcc)->sk_receive_queue))
- pr_err("closing with requests pending\n");
- skb_queue_purge(&sk_atm(vcc)->sk_receive_queue);
-
- read_lock(&vcc_sklist_lock);
- for (i = 0; i < VCC_HTABLE_SIZE; ++i) {
- struct hlist_head *head = &vcc_hash[i];
-
- sk_for_each(s, head) {
- vcc = atm_sk(s);
-
- purge_vcc(vcc);
- }
- }
- read_unlock(&vcc_sklist_lock);
-}
-
-static const struct atmdev_ops sigd_dev_ops = {
- .close = sigd_close,
- .send = sigd_send
-};
-
-static struct atm_dev sigd_dev = {
- .ops = &sigd_dev_ops,
- .type = "sig",
- .number = 999,
- .lock = __SPIN_LOCK_UNLOCKED(sigd_dev.lock)
-};
-
-int sigd_attach(struct atm_vcc *vcc)
-{
- if (sigd)
- return -EADDRINUSE;
- pr_debug("\n");
- sigd = vcc;
- vcc->dev = &sigd_dev;
- vcc_insert_socket(sk_atm(vcc));
- set_bit(ATM_VF_META, &vcc->flags);
- set_bit(ATM_VF_READY, &vcc->flags);
- return 0;
-}
diff --git a/net/atm/svc.c b/net/atm/svc.c
deleted file mode 100644
index 270e95154a2b..000000000000
--- a/net/atm/svc.c
+++ /dev/null
@@ -1,694 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/* net/atm/svc.c - ATM SVC sockets */
-
-/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
-
-#define pr_fmt(fmt) KBUILD_MODNAME ":%s: " fmt, __func__
-
-#include <linux/string.h>
-#include <linux/net.h> /* struct socket, struct proto_ops */
-#include <linux/errno.h> /* error codes */
-#include <linux/kernel.h> /* printk */
-#include <linux/skbuff.h>
-#include <linux/wait.h>
-#include <linux/sched/signal.h>
-#include <linux/fcntl.h> /* O_NONBLOCK */
-#include <linux/init.h>
-#include <linux/atm.h> /* ATM stuff */
-#include <linux/atmsap.h>
-#include <linux/atmsvc.h>
-#include <linux/atmdev.h>
-#include <linux/bitops.h>
-#include <net/sock.h> /* for sock_no_* */
-#include <linux/uaccess.h>
-#include <linux/uio.h>
-#include <linux/export.h>
-
-#include "resources.h"
-#include "common.h" /* common for PVCs and SVCs */
-#include "signaling.h"
-
-#ifdef CONFIG_COMPAT
-/* It actually takes struct sockaddr_atmsvc, not struct atm_iobuf */
-#define COMPAT_ATM_ADDPARTY _IOW('a', ATMIOC_SPECIAL + 4, struct compat_atm_iobuf)
-#endif
-
-static int svc_create(struct net *net, struct socket *sock, int protocol,
- int kern);
-
-/*
- * Note: since all this is still nicely synchronized with the signaling demon,
- * there's no need to protect sleep loops with clis. If signaling is
- * moved into the kernel, that would change.
- */
-
-
-static int svc_shutdown(struct socket *sock, int how)
-{
- return 0;
-}
-
-static void svc_disconnect(struct atm_vcc *vcc)
-{
- DEFINE_WAIT(wait);
- struct sk_buff *skb;
- struct sock *sk = sk_atm(vcc);
-
- pr_debug("%p\n", vcc);
- if (test_bit(ATM_VF_REGIS, &vcc->flags)) {
- sigd_enq(vcc, as_close, NULL, NULL, NULL);
- for (;;) {
- prepare_to_wait(sk_sleep(sk), &wait, TASK_UNINTERRUPTIBLE);
- if (test_bit(ATM_VF_RELEASED, &vcc->flags) || !sigd)
- break;
- schedule();
- }
- finish_wait(sk_sleep(sk), &wait);
- }
- /* beware - socket is still in use by atmsigd until the last
- as_indicate has been answered */
- while ((skb = skb_dequeue(&sk->sk_receive_queue)) != NULL) {
- atm_return(vcc, skb->truesize);
- pr_debug("LISTEN REL\n");
- sigd_enq2(NULL, as_reject, vcc, NULL, NULL, &vcc->qos, 0);
- dev_kfree_skb(skb);
- }
- clear_bit(ATM_VF_REGIS, &vcc->flags);
- /* ... may retry later */
-}
-
-static int svc_release(struct socket *sock)
-{
- struct sock *sk = sock->sk;
- struct atm_vcc *vcc;
-
- if (sk) {
- vcc = ATM_SD(sock);
- pr_debug("%p\n", vcc);
- clear_bit(ATM_VF_READY, &vcc->flags);
- /*
- * VCC pointer is used as a reference,
- * so we must not free it (thereby subjecting it to re-use)
- * before all pending connections are closed
- */
- svc_disconnect(vcc);
- vcc_release(sock);
- }
- return 0;
-}
-
-static int svc_bind(struct socket *sock, struct sockaddr_unsized *sockaddr,
- int sockaddr_len)
-{
- DEFINE_WAIT(wait);
- struct sock *sk = sock->sk;
- struct sockaddr_atmsvc *addr;
- struct atm_vcc *vcc;
- int error;
-
- if (sockaddr_len != sizeof(struct sockaddr_atmsvc))
- return -EINVAL;
- lock_sock(sk);
- if (sock->state == SS_CONNECTED) {
- error = -EISCONN;
- goto out;
- }
- if (sock->state != SS_UNCONNECTED) {
- error = -EINVAL;
- goto out;
- }
- vcc = ATM_SD(sock);
- addr = (struct sockaddr_atmsvc *) sockaddr;
- if (addr->sas_family != AF_ATMSVC) {
- error = -EAFNOSUPPORT;
- goto out;
- }
- clear_bit(ATM_VF_BOUND, &vcc->flags);
- /* failing rebind will kill old binding */
- /* @@@ check memory (de)allocation on rebind */
- if (!test_bit(ATM_VF_HASQOS, &vcc->flags)) {
- error = -EBADFD;
- goto out;
- }
- vcc->local = *addr;
- set_bit(ATM_VF_WAITING, &vcc->flags);
- sigd_enq(vcc, as_bind, NULL, NULL, &vcc->local);
- for (;;) {
- prepare_to_wait(sk_sleep(sk), &wait, TASK_UNINTERRUPTIBLE);
- if (!test_bit(ATM_VF_WAITING, &vcc->flags) || !sigd)
- break;
- schedule();
- }
- finish_wait(sk_sleep(sk), &wait);
- clear_bit(ATM_VF_REGIS, &vcc->flags); /* doesn't count */
- if (!sigd) {
- error = -EUNATCH;
- goto out;
- }
- if (!sk->sk_err)
- set_bit(ATM_VF_BOUND, &vcc->flags);
- error = -sk->sk_err;
-out:
- release_sock(sk);
- return error;
-}
-
-static int svc_connect(struct socket *sock, struct sockaddr_unsized *sockaddr,
- int sockaddr_len, int flags)
-{
- DEFINE_WAIT(wait);
- struct sock *sk = sock->sk;
- struct sockaddr_atmsvc *addr;
- struct atm_vcc *vcc = ATM_SD(sock);
- int error;
-
- pr_debug("%p\n", vcc);
- lock_sock(sk);
- if (sockaddr_len != sizeof(struct sockaddr_atmsvc)) {
- error = -EINVAL;
- goto out;
- }
-
- switch (sock->state) {
- default:
- error = -EINVAL;
- goto out;
- case SS_CONNECTED:
- error = -EISCONN;
- goto out;
- case SS_CONNECTING:
- if (test_bit(ATM_VF_WAITING, &vcc->flags)) {
- error = -EALREADY;
- goto out;
- }
- sock->state = SS_UNCONNECTED;
- if (sk->sk_err) {
- error = -sk->sk_err;
- goto out;
- }
- break;
- case SS_UNCONNECTED:
- addr = (struct sockaddr_atmsvc *) sockaddr;
- if (addr->sas_family != AF_ATMSVC) {
- error = -EAFNOSUPPORT;
- goto out;
- }
- if (!test_bit(ATM_VF_HASQOS, &vcc->flags)) {
- error = -EBADFD;
- goto out;
- }
- if (vcc->qos.txtp.traffic_class == ATM_ANYCLASS ||
- vcc->qos.rxtp.traffic_class == ATM_ANYCLASS) {
- error = -EINVAL;
- goto out;
- }
- if (!vcc->qos.txtp.traffic_class &&
- !vcc->qos.rxtp.traffic_class) {
- error = -EINVAL;
- goto out;
- }
- vcc->remote = *addr;
- set_bit(ATM_VF_WAITING, &vcc->flags);
- sigd_enq(vcc, as_connect, NULL, NULL, &vcc->remote);
- if (flags & O_NONBLOCK) {
- sock->state = SS_CONNECTING;
- error = -EINPROGRESS;
- goto out;
- }
- error = 0;
- prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
- while (test_bit(ATM_VF_WAITING, &vcc->flags) && sigd) {
- schedule();
- if (!signal_pending(current)) {
- prepare_to_wait(sk_sleep(sk), &wait,
- TASK_INTERRUPTIBLE);
- continue;
- }
- pr_debug("*ABORT*\n");
- /*
- * This is tricky:
- * Kernel ---close--> Demon
- * Kernel <--close--- Demon
- * or
- * Kernel ---close--> Demon
- * Kernel <--error--- Demon
- * or
- * Kernel ---close--> Demon
- * Kernel <--okay---- Demon
- * Kernel <--close--- Demon
- */
- sigd_enq(vcc, as_close, NULL, NULL, NULL);
- while (test_bit(ATM_VF_WAITING, &vcc->flags) && sigd) {
- prepare_to_wait(sk_sleep(sk), &wait,
- TASK_INTERRUPTIBLE);
- schedule();
- }
- if (!sk->sk_err)
- while (!test_bit(ATM_VF_RELEASED, &vcc->flags) &&
- sigd) {
- prepare_to_wait(sk_sleep(sk), &wait,
- TASK_INTERRUPTIBLE);
- schedule();
- }
- clear_bit(ATM_VF_REGIS, &vcc->flags);
- clear_bit(ATM_VF_RELEASED, &vcc->flags);
- clear_bit(ATM_VF_CLOSE, &vcc->flags);
- /* we're gone now but may connect later */
- error = -EINTR;
- break;
- }
- finish_wait(sk_sleep(sk), &wait);
- if (error)
- goto out;
- if (!sigd) {
- error = -EUNATCH;
- goto out;
- }
- if (sk->sk_err) {
- error = -sk->sk_err;
- goto out;
- }
- }
-
- vcc->qos.txtp.max_pcr = SELECT_TOP_PCR(vcc->qos.txtp);
- vcc->qos.txtp.pcr = 0;
- vcc->qos.txtp.min_pcr = 0;
-
- error = vcc_connect(sock, vcc->itf, vcc->vpi, vcc->vci);
- if (!error)
- sock->state = SS_CONNECTED;
- else
- (void)svc_disconnect(vcc);
-out:
- release_sock(sk);
- return error;
-}
-
-static int svc_listen(struct socket *sock, int backlog)
-{
- DEFINE_WAIT(wait);
- struct sock *sk = sock->sk;
- struct atm_vcc *vcc = ATM_SD(sock);
- int error;
-
- pr_debug("%p\n", vcc);
- lock_sock(sk);
- /* let server handle listen on unbound sockets */
- if (test_bit(ATM_VF_SESSION, &vcc->flags)) {
- error = -EINVAL;
- goto out;
- }
- if (test_bit(ATM_VF_LISTEN, &vcc->flags)) {
- error = -EADDRINUSE;
- goto out;
- }
- set_bit(ATM_VF_WAITING, &vcc->flags);
- sigd_enq(vcc, as_listen, NULL, NULL, &vcc->local);
- for (;;) {
- prepare_to_wait(sk_sleep(sk), &wait, TASK_UNINTERRUPTIBLE);
- if (!test_bit(ATM_VF_WAITING, &vcc->flags) || !sigd)
- break;
- schedule();
- }
- finish_wait(sk_sleep(sk), &wait);
- if (!sigd) {
- error = -EUNATCH;
- goto out;
- }
- set_bit(ATM_VF_LISTEN, &vcc->flags);
- vcc_insert_socket(sk);
- sk->sk_max_ack_backlog = backlog > 0 ? backlog : ATM_BACKLOG_DEFAULT;
- error = -sk->sk_err;
-out:
- release_sock(sk);
- return error;
-}
-
-static int svc_accept(struct socket *sock, struct socket *newsock,
- struct proto_accept_arg *arg)
-{
- struct sock *sk = sock->sk;
- struct sk_buff *skb;
- struct atmsvc_msg *msg;
- struct atm_vcc *old_vcc = ATM_SD(sock);
- struct atm_vcc *new_vcc;
- int error;
-
- lock_sock(sk);
-
- error = svc_create(sock_net(sk), newsock, 0, arg->kern);
- if (error)
- goto out;
-
- new_vcc = ATM_SD(newsock);
-
- pr_debug("%p -> %p\n", old_vcc, new_vcc);
- while (1) {
- DEFINE_WAIT(wait);
-
- prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
- while (!(skb = skb_dequeue(&sk->sk_receive_queue)) &&
- sigd) {
- if (test_bit(ATM_VF_RELEASED, &old_vcc->flags))
- break;
- if (test_bit(ATM_VF_CLOSE, &old_vcc->flags)) {
- error = -sk->sk_err;
- break;
- }
- if (arg->flags & O_NONBLOCK) {
- error = -EAGAIN;
- break;
- }
- release_sock(sk);
- schedule();
- lock_sock(sk);
- if (signal_pending(current)) {
- error = -ERESTARTSYS;
- break;
- }
- prepare_to_wait(sk_sleep(sk), &wait,
- TASK_INTERRUPTIBLE);
- }
- finish_wait(sk_sleep(sk), &wait);
- if (error)
- goto out;
- if (!skb) {
- error = -EUNATCH;
- goto out;
- }
- msg = (struct atmsvc_msg *)skb->data;
- new_vcc->qos = msg->qos;
- set_bit(ATM_VF_HASQOS, &new_vcc->flags);
- new_vcc->remote = msg->svc;
- new_vcc->local = msg->local;
- new_vcc->sap = msg->sap;
- error = vcc_connect(newsock, msg->pvc.sap_addr.itf,
- msg->pvc.sap_addr.vpi,
- msg->pvc.sap_addr.vci);
- dev_kfree_skb(skb);
- sk_acceptq_removed(sk);
- if (error) {
- sigd_enq2(NULL, as_reject, old_vcc, NULL, NULL,
- &old_vcc->qos, error);
- error = error == -EAGAIN ? -EBUSY : error;
- goto out;
- }
- /* wait should be short, so we ignore the non-blocking flag */
- set_bit(ATM_VF_WAITING, &new_vcc->flags);
- sigd_enq(new_vcc, as_accept, old_vcc, NULL, NULL);
- for (;;) {
- prepare_to_wait(sk_sleep(sk_atm(new_vcc)), &wait,
- TASK_UNINTERRUPTIBLE);
- if (!test_bit(ATM_VF_WAITING, &new_vcc->flags) || !sigd)
- break;
- release_sock(sk);
- schedule();
- lock_sock(sk);
- }
- finish_wait(sk_sleep(sk_atm(new_vcc)), &wait);
- if (!sigd) {
- error = -EUNATCH;
- goto out;
- }
- if (!sk_atm(new_vcc)->sk_err)
- break;
- if (sk_atm(new_vcc)->sk_err != ERESTARTSYS) {
- error = -sk_atm(new_vcc)->sk_err;
- goto out;
- }
- }
- newsock->state = SS_CONNECTED;
-out:
- release_sock(sk);
- return error;
-}
-
-static int svc_getname(struct socket *sock, struct sockaddr *sockaddr,
- int peer)
-{
- struct sockaddr_atmsvc *addr;
-
- addr = (struct sockaddr_atmsvc *) sockaddr;
- memcpy(addr, peer ? &ATM_SD(sock)->remote : &ATM_SD(sock)->local,
- sizeof(struct sockaddr_atmsvc));
- return sizeof(struct sockaddr_atmsvc);
-}
-
-int svc_change_qos(struct atm_vcc *vcc, struct atm_qos *qos)
-{
- struct sock *sk = sk_atm(vcc);
- DEFINE_WAIT(wait);
-
- set_bit(ATM_VF_WAITING, &vcc->flags);
- sigd_enq2(vcc, as_modify, NULL, NULL, &vcc->local, qos, 0);
- for (;;) {
- prepare_to_wait(sk_sleep(sk), &wait, TASK_UNINTERRUPTIBLE);
- if (!test_bit(ATM_VF_WAITING, &vcc->flags) ||
- test_bit(ATM_VF_RELEASED, &vcc->flags) || !sigd) {
- break;
- }
- schedule();
- }
- finish_wait(sk_sleep(sk), &wait);
- if (!sigd)
- return -EUNATCH;
- return -sk->sk_err;
-}
-
-static int svc_setsockopt(struct socket *sock, int level, int optname,
- sockptr_t optval, unsigned int optlen)
-{
- struct sock *sk = sock->sk;
- struct atm_vcc *vcc = ATM_SD(sock);
- int value, error = 0;
-
- lock_sock(sk);
- switch (optname) {
- case SO_ATMSAP:
- if (level != SOL_ATM || optlen != sizeof(struct atm_sap)) {
- error = -EINVAL;
- goto out;
- }
- if (copy_from_sockptr(&vcc->sap, optval, optlen)) {
- error = -EFAULT;
- goto out;
- }
- set_bit(ATM_VF_HASSAP, &vcc->flags);
- break;
- case SO_MULTIPOINT:
- if (level != SOL_ATM || optlen != sizeof(int)) {
- error = -EINVAL;
- goto out;
- }
- if (copy_from_sockptr(&value, optval, sizeof(int))) {
- error = -EFAULT;
- goto out;
- }
- if (value == 1)
- set_bit(ATM_VF_SESSION, &vcc->flags);
- else if (value == 0)
- clear_bit(ATM_VF_SESSION, &vcc->flags);
- else
- error = -EINVAL;
- break;
- default:
- error = vcc_setsockopt(sock, level, optname, optval, optlen);
- }
-
-out:
- release_sock(sk);
- return error;
-}
-
-static int svc_getsockopt(struct socket *sock, int level, int optname,
- sockopt_t *opt)
-{
- struct sock *sk = sock->sk;
- int error = 0, len;
-
- lock_sock(sk);
- if (!__SO_LEVEL_MATCH(optname, level) || optname != SO_ATMSAP) {
- error = vcc_getsockopt(sock, level, optname, opt);
- goto out;
- }
- len = opt->optlen;
- if (len != sizeof(struct atm_sap)) {
- error = -EINVAL;
- goto out;
- }
- if (copy_to_iter(&ATM_SD(sock)->sap, sizeof(struct atm_sap),
- &opt->iter_out) != sizeof(struct atm_sap)) {
- error = -EFAULT;
- goto out;
- }
-out:
- release_sock(sk);
- return error;
-}
-
-static int svc_addparty(struct socket *sock, struct sockaddr *sockaddr,
- int sockaddr_len, int flags)
-{
- DEFINE_WAIT(wait);
- struct sock *sk = sock->sk;
- struct atm_vcc *vcc = ATM_SD(sock);
- int error;
-
- lock_sock(sk);
- set_bit(ATM_VF_WAITING, &vcc->flags);
- sigd_enq(vcc, as_addparty, NULL, NULL,
- (struct sockaddr_atmsvc *) sockaddr);
- if (flags & O_NONBLOCK) {
- error = -EINPROGRESS;
- goto out;
- }
- pr_debug("added wait queue\n");
- for (;;) {
- prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
- if (!test_bit(ATM_VF_WAITING, &vcc->flags) || !sigd)
- break;
- schedule();
- }
- finish_wait(sk_sleep(sk), &wait);
- error = -xchg(&sk->sk_err_soft, 0);
-out:
- release_sock(sk);
- return error;
-}
-
-static int svc_dropparty(struct socket *sock, int ep_ref)
-{
- DEFINE_WAIT(wait);
- struct sock *sk = sock->sk;
- struct atm_vcc *vcc = ATM_SD(sock);
- int error;
-
- lock_sock(sk);
- set_bit(ATM_VF_WAITING, &vcc->flags);
- sigd_enq2(vcc, as_dropparty, NULL, NULL, NULL, NULL, ep_ref);
- for (;;) {
- prepare_to_wait(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE);
- if (!test_bit(ATM_VF_WAITING, &vcc->flags) || !sigd)
- break;
- schedule();
- }
- finish_wait(sk_sleep(sk), &wait);
- if (!sigd) {
- error = -EUNATCH;
- goto out;
- }
- error = -xchg(&sk->sk_err_soft, 0);
-out:
- release_sock(sk);
- return error;
-}
-
-static int svc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
-{
- int error, ep_ref;
- struct sockaddr_atmsvc sa;
- struct atm_vcc *vcc = ATM_SD(sock);
-
- switch (cmd) {
- case ATM_ADDPARTY:
- if (!test_bit(ATM_VF_SESSION, &vcc->flags))
- return -EINVAL;
- if (copy_from_user(&sa, (void __user *) arg, sizeof(sa)))
- return -EFAULT;
- error = svc_addparty(sock, (struct sockaddr *)&sa, sizeof(sa),
- 0);
- break;
- case ATM_DROPPARTY:
- if (!test_bit(ATM_VF_SESSION, &vcc->flags))
- return -EINVAL;
- if (copy_from_user(&ep_ref, (void __user *) arg, sizeof(int)))
- return -EFAULT;
- error = svc_dropparty(sock, ep_ref);
- break;
- default:
- error = vcc_ioctl(sock, cmd, arg);
- }
-
- return error;
-}
-
-#ifdef CONFIG_COMPAT
-static int svc_compat_ioctl(struct socket *sock, unsigned int cmd,
- unsigned long arg)
-{
- /* The definition of ATM_ADDPARTY uses the size of struct atm_iobuf.
- But actually it takes a struct sockaddr_atmsvc, which doesn't need
- compat handling. So all we have to do is fix up cmd... */
- if (cmd == COMPAT_ATM_ADDPARTY)
- cmd = ATM_ADDPARTY;
-
- if (cmd == ATM_ADDPARTY || cmd == ATM_DROPPARTY)
- return svc_ioctl(sock, cmd, arg);
- else
- return vcc_compat_ioctl(sock, cmd, arg);
-}
-#endif /* CONFIG_COMPAT */
-
-static const struct proto_ops svc_proto_ops = {
- .family = PF_ATMSVC,
- .owner = THIS_MODULE,
-
- .release = svc_release,
- .bind = svc_bind,
- .connect = svc_connect,
- .socketpair = sock_no_socketpair,
- .accept = svc_accept,
- .getname = svc_getname,
- .poll = vcc_poll,
- .ioctl = svc_ioctl,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = svc_compat_ioctl,
-#endif
- .gettstamp = sock_gettstamp,
- .listen = svc_listen,
- .shutdown = svc_shutdown,
- .setsockopt = svc_setsockopt,
- .getsockopt_iter = svc_getsockopt,
- .sendmsg = vcc_sendmsg,
- .recvmsg = vcc_recvmsg,
- .mmap = sock_no_mmap,
-};
-
-
-static int svc_create(struct net *net, struct socket *sock, int protocol,
- int kern)
-{
- int error;
-
- if (!net_eq(net, &init_net))
- return -EAFNOSUPPORT;
-
- sock->ops = &svc_proto_ops;
- error = vcc_create(net, sock, protocol, AF_ATMSVC, kern);
- if (error)
- return error;
- ATM_SD(sock)->local.sas_family = AF_ATMSVC;
- ATM_SD(sock)->remote.sas_family = AF_ATMSVC;
- return 0;
-}
-
-static const struct net_proto_family svc_family_ops = {
- .family = PF_ATMSVC,
- .create = svc_create,
- .owner = THIS_MODULE,
-};
-
-
-/*
- * Initialize the ATM SVC protocol family
- */
-
-int __init atmsvc_init(void)
-{
- return sock_register(&svc_family_ops);
-}
-
-void atmsvc_exit(void)
-{
- sock_unregister(PF_ATMSVC);
-}
--
2.54.0
^ permalink raw reply related
* [PATCH net-next 4/9] atm: remove the local ATM (NSAP) address registry
From: Jakub Kicinski @ 2026-06-13 20:10 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, 3chas3, mitch,
linux-atm-general, dwmw2, Jakub Kicinski
In-Reply-To: <20260613201032.77274-1-kuba@kernel.org>
net/atm/addr.c maintained the per-device lists of local NSAP addresses
(dev->local) and ILMI-learned LECS addresses (dev->lecs). These exist
solely to serve SVC signaling: the lists are populated through the
ATM_{ADD,DEL,RST}ADDR / ATM_{ADD,DEL,GET}LECSADDR ioctls used by the
atmsigd / ILMI daemons, and consumed when registering addresses with the
signaling daemon. The LECS list belonged to LAN Emulation, which has
been removed.
With no SVC users in a DSL-only configuration these lists are always
empty, so drop the registry entirely:
- remove the ADDR/LECSADDR/RSTADDR ioctls
- drop the now-always-empty "atmaddress" sysfs attribute
- remove the dev->local / dev->lecs lists, structs and enums
- delete net/atm/addr.c and net/atm/addr.h
The device ESI ("MAC" address) and its ATM_{G,S}ETESI ioctls and
"address" sysfs attribute are retained - the USB DSL modems populate
the ESI.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
net/atm/Makefile | 2 +-
include/linux/atmdev.h | 9 ---
net/atm/addr.h | 21 ------
net/atm/addr.c | 162 -----------------------------------------
net/atm/atm_sysfs.c | 25 -------
net/atm/common.c | 1 -
net/atm/ioctl.c | 12 ---
net/atm/resources.c | 48 ------------
net/atm/svc.c | 1 -
9 files changed, 1 insertion(+), 280 deletions(-)
delete mode 100644 net/atm/addr.h
delete mode 100644 net/atm/addr.c
diff --git a/net/atm/Makefile b/net/atm/Makefile
index 484a1b1552cc..5ed48d50df35 100644
--- a/net/atm/Makefile
+++ b/net/atm/Makefile
@@ -3,7 +3,7 @@
# Makefile for the ATM Protocol Families.
#
-atm-y := addr.o pvc.o signaling.o svc.o ioctl.o common.o atm_misc.o raw.o resources.o atm_sysfs.o
+atm-y := pvc.o signaling.o svc.o ioctl.o common.o atm_misc.o raw.o resources.o atm_sysfs.o
obj-$(CONFIG_ATM) += atm.o
obj-$(CONFIG_ATM_BR2684) += br2684.o
diff --git a/include/linux/atmdev.h b/include/linux/atmdev.h
index 71c5bf6950e3..7abbd23fada6 100644
--- a/include/linux/atmdev.h
+++ b/include/linux/atmdev.h
@@ -136,13 +136,6 @@ static inline struct sock *sk_atm(struct atm_vcc *vcc)
return (struct sock *)vcc;
}
-struct atm_dev_addr {
- struct sockaddr_atmsvc addr; /* ATM address */
- struct list_head entry; /* next address */
-};
-
-enum atm_addr_type_t { ATM_ADDR_LOCAL, ATM_ADDR_LECS };
-
struct atm_dev {
const struct atmdev_ops *ops; /* device operations; NULL if unused */
const struct atmphy_ops *phy; /* PHY operations, may be undefined */
@@ -152,8 +145,6 @@ struct atm_dev {
void *dev_data; /* per-device data */
void *phy_data; /* private PHY data */
unsigned long flags; /* device flags (ATM_DF_*) */
- struct list_head local; /* local ATM addresses */
- struct list_head lecs; /* LECS ATM addresses learned via ILMI */
unsigned char esi[ESI_LEN]; /* ESI ("MAC" addr) */
struct atm_cirange ci_range; /* VPI/VCI range */
struct k_atm_dev_stats stats; /* statistics */
diff --git a/net/atm/addr.h b/net/atm/addr.h
deleted file mode 100644
index da3f848411a0..000000000000
--- a/net/atm/addr.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/* SPDX-License-Identifier: GPL-2.0 */
-/* net/atm/addr.h - Local ATM address registry */
-
-/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
-
-
-#ifndef NET_ATM_ADDR_H
-#define NET_ATM_ADDR_H
-
-#include <linux/atm.h>
-#include <linux/atmdev.h>
-
-void atm_reset_addr(struct atm_dev *dev, enum atm_addr_type_t type);
-int atm_add_addr(struct atm_dev *dev, const struct sockaddr_atmsvc *addr,
- enum atm_addr_type_t type);
-int atm_del_addr(struct atm_dev *dev, const struct sockaddr_atmsvc *addr,
- enum atm_addr_type_t type);
-int atm_get_addr(struct atm_dev *dev, struct sockaddr_atmsvc __user *buf,
- size_t size, enum atm_addr_type_t type);
-
-#endif
diff --git a/net/atm/addr.c b/net/atm/addr.c
deleted file mode 100644
index 938f360ae230..000000000000
--- a/net/atm/addr.c
+++ /dev/null
@@ -1,162 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/* net/atm/addr.c - Local ATM address registry */
-
-/* Written 1995-2000 by Werner Almesberger, EPFL LRC/ICA */
-
-#include <linux/atm.h>
-#include <linux/atmdev.h>
-#include <linux/slab.h>
-#include <linux/uaccess.h>
-
-#include "signaling.h"
-#include "addr.h"
-
-static int check_addr(const struct sockaddr_atmsvc *addr)
-{
- int i;
-
- if (addr->sas_family != AF_ATMSVC)
- return -EAFNOSUPPORT;
- if (!*addr->sas_addr.pub)
- return *addr->sas_addr.prv ? 0 : -EINVAL;
- for (i = 1; i < ATM_E164_LEN + 1; i++) /* make sure it's \0-terminated */
- if (!addr->sas_addr.pub[i])
- return 0;
- return -EINVAL;
-}
-
-static int identical(const struct sockaddr_atmsvc *a, const struct sockaddr_atmsvc *b)
-{
- if (*a->sas_addr.prv)
- if (memcmp(a->sas_addr.prv, b->sas_addr.prv, ATM_ESA_LEN))
- return 0;
- if (!*a->sas_addr.pub)
- return !*b->sas_addr.pub;
- if (!*b->sas_addr.pub)
- return 0;
- return !strcmp(a->sas_addr.pub, b->sas_addr.pub);
-}
-
-static void notify_sigd(const struct atm_dev *dev)
-{
- struct sockaddr_atmpvc pvc;
-
- pvc.sap_addr.itf = dev->number;
- sigd_enq(NULL, as_itf_notify, NULL, &pvc, NULL);
-}
-
-void atm_reset_addr(struct atm_dev *dev, enum atm_addr_type_t atype)
-{
- unsigned long flags;
- struct atm_dev_addr *this, *p;
- struct list_head *head;
-
- spin_lock_irqsave(&dev->lock, flags);
- if (atype == ATM_ADDR_LECS)
- head = &dev->lecs;
- else
- head = &dev->local;
- list_for_each_entry_safe(this, p, head, entry) {
- list_del(&this->entry);
- kfree(this);
- }
- spin_unlock_irqrestore(&dev->lock, flags);
- if (head == &dev->local)
- notify_sigd(dev);
-}
-
-int atm_add_addr(struct atm_dev *dev, const struct sockaddr_atmsvc *addr,
- enum atm_addr_type_t atype)
-{
- unsigned long flags;
- struct atm_dev_addr *this;
- struct list_head *head;
- int error;
-
- error = check_addr(addr);
- if (error)
- return error;
- spin_lock_irqsave(&dev->lock, flags);
- if (atype == ATM_ADDR_LECS)
- head = &dev->lecs;
- else
- head = &dev->local;
- list_for_each_entry(this, head, entry) {
- if (identical(&this->addr, addr)) {
- spin_unlock_irqrestore(&dev->lock, flags);
- return -EEXIST;
- }
- }
- this = kmalloc_obj(struct atm_dev_addr, GFP_ATOMIC);
- if (!this) {
- spin_unlock_irqrestore(&dev->lock, flags);
- return -ENOMEM;
- }
- this->addr = *addr;
- list_add(&this->entry, head);
- spin_unlock_irqrestore(&dev->lock, flags);
- if (head == &dev->local)
- notify_sigd(dev);
- return 0;
-}
-
-int atm_del_addr(struct atm_dev *dev, const struct sockaddr_atmsvc *addr,
- enum atm_addr_type_t atype)
-{
- unsigned long flags;
- struct atm_dev_addr *this;
- struct list_head *head;
- int error;
-
- error = check_addr(addr);
- if (error)
- return error;
- spin_lock_irqsave(&dev->lock, flags);
- if (atype == ATM_ADDR_LECS)
- head = &dev->lecs;
- else
- head = &dev->local;
- list_for_each_entry(this, head, entry) {
- if (identical(&this->addr, addr)) {
- list_del(&this->entry);
- spin_unlock_irqrestore(&dev->lock, flags);
- kfree(this);
- if (head == &dev->local)
- notify_sigd(dev);
- return 0;
- }
- }
- spin_unlock_irqrestore(&dev->lock, flags);
- return -ENOENT;
-}
-
-int atm_get_addr(struct atm_dev *dev, struct sockaddr_atmsvc __user * buf,
- size_t size, enum atm_addr_type_t atype)
-{
- unsigned long flags;
- struct atm_dev_addr *this;
- struct list_head *head;
- int total = 0, error;
- struct sockaddr_atmsvc *tmp_buf, *tmp_bufp;
-
- spin_lock_irqsave(&dev->lock, flags);
- if (atype == ATM_ADDR_LECS)
- head = &dev->lecs;
- else
- head = &dev->local;
- list_for_each_entry(this, head, entry)
- total += sizeof(struct sockaddr_atmsvc);
- tmp_buf = tmp_bufp = kmalloc(total, GFP_ATOMIC);
- if (!tmp_buf) {
- spin_unlock_irqrestore(&dev->lock, flags);
- return -ENOMEM;
- }
- list_for_each_entry(this, head, entry)
- memcpy(tmp_bufp++, &this->addr, sizeof(struct sockaddr_atmsvc));
- spin_unlock_irqrestore(&dev->lock, flags);
- error = total > size ? -E2BIG : total;
- if (copy_to_user(buf, tmp_buf, total < size ? total : size))
- error = -EFAULT;
- kfree(tmp_buf);
- return error;
-}
diff --git a/net/atm/atm_sysfs.c b/net/atm/atm_sysfs.c
index 54e7fb1a4ee5..0676a9c333ff 100644
--- a/net/atm/atm_sysfs.c
+++ b/net/atm/atm_sysfs.c
@@ -27,29 +27,6 @@ static ssize_t address_show(struct device *cdev,
return scnprintf(buf, PAGE_SIZE, "%pM\n", adev->esi);
}
-static ssize_t atmaddress_show(struct device *cdev,
- struct device_attribute *attr, char *buf)
-{
- unsigned long flags;
- struct atm_dev *adev = to_atm_dev(cdev);
- struct atm_dev_addr *aaddr;
- int count = 0;
-
- spin_lock_irqsave(&adev->lock, flags);
- list_for_each_entry(aaddr, &adev->local, entry) {
- count += scnprintf(buf + count, PAGE_SIZE - count,
- "%1phN.%2phN.%10phN.%6phN.%1phN\n",
- &aaddr->addr.sas_addr.prv[0],
- &aaddr->addr.sas_addr.prv[1],
- &aaddr->addr.sas_addr.prv[3],
- &aaddr->addr.sas_addr.prv[13],
- &aaddr->addr.sas_addr.prv[19]);
- }
- spin_unlock_irqrestore(&adev->lock, flags);
-
- return count;
-}
-
static ssize_t atmindex_show(struct device *cdev,
struct device_attribute *attr, char *buf)
{
@@ -91,14 +68,12 @@ static ssize_t link_rate_show(struct device *cdev,
}
static DEVICE_ATTR_RO(address);
-static DEVICE_ATTR_RO(atmaddress);
static DEVICE_ATTR_RO(atmindex);
static DEVICE_ATTR_RO(carrier);
static DEVICE_ATTR_RO(type);
static DEVICE_ATTR_RO(link_rate);
static struct device_attribute *atm_attrs[] = {
- &dev_attr_atmaddress,
&dev_attr_address,
&dev_attr_atmindex,
&dev_attr_carrier,
diff --git a/net/atm/common.c b/net/atm/common.c
index c6e87fc9bbfc..7d5b7c39b80b 100644
--- a/net/atm/common.c
+++ b/net/atm/common.c
@@ -30,7 +30,6 @@
#include "resources.h" /* atm_find_dev */
#include "common.h" /* prototypes */
#include "protocols.h" /* atm_init_<transport> */
-#include "addr.h" /* address registry */
#include "signaling.h" /* for WAITING and sigd_attach */
struct hlist_head vcc_hash[VCC_HTABLE_SIZE];
diff --git a/net/atm/ioctl.c b/net/atm/ioctl.c
index 4f2d185bf2f0..97f20cd051ed 100644
--- a/net/atm/ioctl.c
+++ b/net/atm/ioctl.c
@@ -220,10 +220,6 @@ int vcc_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
#define ATM_GETNAMES32 _IOW('a', ATMIOC_ITF+3, struct compat_atm_iobuf)
#define ATM_GETTYPE32 _IOW('a', ATMIOC_ITF+4, struct compat_atmif_sioc)
#define ATM_GETESI32 _IOW('a', ATMIOC_ITF+5, struct compat_atmif_sioc)
-#define ATM_GETADDR32 _IOW('a', ATMIOC_ITF+6, struct compat_atmif_sioc)
-#define ATM_RSTADDR32 _IOW('a', ATMIOC_ITF+7, struct compat_atmif_sioc)
-#define ATM_ADDADDR32 _IOW('a', ATMIOC_ITF+8, struct compat_atmif_sioc)
-#define ATM_DELADDR32 _IOW('a', ATMIOC_ITF+9, struct compat_atmif_sioc)
#define ATM_GETCIRANGE32 _IOW('a', ATMIOC_ITF+10, struct compat_atmif_sioc)
#define ATM_SETCIRANGE32 _IOW('a', ATMIOC_ITF+11, struct compat_atmif_sioc)
#define ATM_SETESI32 _IOW('a', ATMIOC_ITF+12, struct compat_atmif_sioc)
@@ -242,10 +238,6 @@ static struct {
{ ATM_GETNAMES32, ATM_GETNAMES },
{ ATM_GETTYPE32, ATM_GETTYPE },
{ ATM_GETESI32, ATM_GETESI },
- { ATM_GETADDR32, ATM_GETADDR },
- { ATM_RSTADDR32, ATM_RSTADDR },
- { ATM_ADDADDR32, ATM_ADDADDR },
- { ATM_DELADDR32, ATM_DELADDR },
{ ATM_GETCIRANGE32, ATM_GETCIRANGE },
{ ATM_SETCIRANGE32, ATM_SETCIRANGE },
{ ATM_SETESI32, ATM_SETESI },
@@ -305,10 +297,6 @@ static int do_atm_ioctl(struct socket *sock, unsigned int cmd32,
case ATM_GETLINKRATE:
case ATM_GETTYPE:
case ATM_GETESI:
- case ATM_GETADDR:
- case ATM_RSTADDR:
- case ATM_ADDADDR:
- case ATM_DELADDR:
case ATM_GETCIRANGE:
case ATM_SETCIRANGE:
case ATM_SETESI:
diff --git a/net/atm/resources.c b/net/atm/resources.c
index 7aac25e917b4..a08f65aacf75 100644
--- a/net/atm/resources.c
+++ b/net/atm/resources.c
@@ -25,7 +25,6 @@
#include "common.h"
#include "resources.h"
-#include "addr.h"
LIST_HEAD(atm_devs);
@@ -42,8 +41,6 @@ static struct atm_dev *__alloc_atm_dev(const char *type)
dev->signal = ATM_PHY_SIG_UNKNOWN;
dev->link_rate = ATM_OC3_PCR;
spin_lock_init(&dev->lock);
- INIT_LIST_HEAD(&dev->local);
- INIT_LIST_HEAD(&dev->lecs);
return dev;
}
@@ -306,51 +303,6 @@ int atm_dev_ioctl(unsigned int cmd, void __user *buf, int __user *sioc_len,
goto done;
}
break;
- case ATM_RSTADDR:
- if (!capable(CAP_NET_ADMIN)) {
- error = -EPERM;
- goto done;
- }
- atm_reset_addr(dev, ATM_ADDR_LOCAL);
- break;
- case ATM_ADDADDR:
- case ATM_DELADDR:
- case ATM_ADDLECSADDR:
- case ATM_DELLECSADDR:
- {
- struct sockaddr_atmsvc addr;
-
- if (!capable(CAP_NET_ADMIN)) {
- error = -EPERM;
- goto done;
- }
-
- if (copy_from_user(&addr, buf, sizeof(addr))) {
- error = -EFAULT;
- goto done;
- }
- if (cmd == ATM_ADDADDR || cmd == ATM_ADDLECSADDR)
- error = atm_add_addr(dev, &addr,
- (cmd == ATM_ADDADDR ?
- ATM_ADDR_LOCAL : ATM_ADDR_LECS));
- else
- error = atm_del_addr(dev, &addr,
- (cmd == ATM_DELADDR ?
- ATM_ADDR_LOCAL : ATM_ADDR_LECS));
- goto done;
- }
- case ATM_GETADDR:
- case ATM_GETLECSADDR:
- error = atm_get_addr(dev, buf, len,
- (cmd == ATM_GETADDR ?
- ATM_ADDR_LOCAL : ATM_ADDR_LECS));
- if (error < 0)
- goto done;
- size = error;
- /* may return 0, but later on size == 0 means "don't
- write the length" */
- error = put_user(size, sioc_len) ? -EFAULT : 0;
- goto done;
case ATM_SETLOOP:
if (__ATM_LM_XTRMT((int) (unsigned long) buf) &&
__ATM_LM_XTLOC((int) (unsigned long) buf) >
diff --git a/net/atm/svc.c b/net/atm/svc.c
index 7c5559f50a99..270e95154a2b 100644
--- a/net/atm/svc.c
+++ b/net/atm/svc.c
@@ -27,7 +27,6 @@
#include "resources.h"
#include "common.h" /* common for PVCs and SVCs */
#include "signaling.h"
-#include "addr.h"
#ifdef CONFIG_COMPAT
/* It actually takes struct sockaddr_atmsvc, not struct atm_iobuf */
--
2.54.0
^ permalink raw reply related
* [PATCH net-next 3/9] atm: remove dead SONET PHY ioctls
From: Jakub Kicinski @ 2026-06-13 20:10 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, 3chas3, mitch,
linux-atm-general, dwmw2, Jakub Kicinski
In-Reply-To: <20260613201032.77274-1-kuba@kernel.org>
The SONET_* ioctls are SONET/SDH PHY controls that atm_dev_ioctl() and
the compat path only ever forwarded to the driver's ->ioctl() handler.
The PHY drivers that implemented them (the S/UNI library and the framers
on the removed PCI/SBUS adapters) are gone, and neither surviving driver
services them: solos-pci has no ->ioctl, and usbatm handles only
ATM_QUERYLOOP. They now uniformly return an error regardless.
Drop the SONET compat passthrough and the SONET cases in atm_dev_ioctl(),
along with the now-unused linux/sonet.h includes. The SONET_* uAPI
definitions are untouched.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
net/atm/ioctl.c | 13 -------------
net/atm/resources.c | 5 -----
2 files changed, 18 deletions(-)
diff --git a/net/atm/ioctl.c b/net/atm/ioctl.c
index 0f3f9ad8301f..4f2d185bf2f0 100644
--- a/net/atm/ioctl.c
+++ b/net/atm/ioctl.c
@@ -13,7 +13,6 @@
#include <linux/atmdev.h>
#include <linux/atmarp.h> /* manifest constants */
#include <linux/capability.h>
-#include <linux/sonet.h> /* for ioctls */
#include <linux/atmsvc.h>
#include <linux/mutex.h>
#include <asm/ioctls.h>
@@ -290,18 +289,6 @@ static int do_atm_ioctl(struct socket *sock, unsigned int cmd32,
int i;
unsigned int cmd = 0;
- switch (cmd32) {
- case SONET_GETSTAT:
- case SONET_GETSTATZ:
- case SONET_GETDIAG:
- case SONET_SETDIAG:
- case SONET_CLRDIAG:
- case SONET_SETFRAMING:
- case SONET_GETFRAMING:
- case SONET_GETFRSENSE:
- return do_atmif_sioc(sock, cmd32, arg);
- }
-
for (i = 0; i < NR_ATM_IOCTL; i++) {
if (cmd32 == atm_ioctl_map[i].cmd32) {
cmd = atm_ioctl_map[i].cmd;
diff --git a/net/atm/resources.c b/net/atm/resources.c
index 939452a610c0..7aac25e917b4 100644
--- a/net/atm/resources.c
+++ b/net/atm/resources.c
@@ -13,7 +13,6 @@
#include <linux/ctype.h>
#include <linux/string.h>
#include <linux/atmdev.h>
-#include <linux/sonet.h>
#include <linux/kernel.h> /* for barrier */
#include <linux/module.h>
#include <linux/bitops.h>
@@ -361,10 +360,6 @@ int atm_dev_ioctl(unsigned int cmd, void __user *buf, int __user *sioc_len,
}
fallthrough;
case ATM_SETCIRANGE:
- case SONET_GETSTATZ:
- case SONET_SETDIAG:
- case SONET_CLRDIAG:
- case SONET_SETFRAMING:
if (!capable(CAP_NET_ADMIN)) {
error = -EPERM;
goto done;
--
2.54.0
^ permalink raw reply related
* [PATCH net-next 2/9] atm: remove the unused send_oam / push_oam callbacks
From: Jakub Kicinski @ 2026-06-13 20:10 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, 3chas3, mitch,
linux-atm-general, dwmw2, Jakub Kicinski
In-Reply-To: <20260613201032.77274-1-kuba@kernel.org>
The atmdev_ops::send_oam device operation and the atm_vcc::push_oam
callback were the kernel's interface for raw F4/F5 OAM cell exchange.
Nothing assigns them a non-NULL value and nothing ever invokes them:
the core only ever initialises push_oam to NULL (in vcc_create() and the
AAL init helpers) and the Solos driver only lists send_oam = NULL for
documentation. The drivers that actually drove OAM through these hooks
were removed along with the legacy ATM adapters.
Drop both callbacks and the NULL initialisers.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
include/linux/atmdev.h | 8 --------
drivers/atm/solos-pci.c | 1 -
net/atm/common.c | 1 -
net/atm/raw.c | 2 --
4 files changed, 12 deletions(-)
diff --git a/include/linux/atmdev.h b/include/linux/atmdev.h
index 82a32526df64..71c5bf6950e3 100644
--- a/include/linux/atmdev.h
+++ b/include/linux/atmdev.h
@@ -104,7 +104,6 @@ struct atm_vcc {
void (*release_cb)(struct atm_vcc *vcc); /* release_sock callback */
void (*push)(struct atm_vcc *vcc,struct sk_buff *skb);
void (*pop)(struct atm_vcc *vcc,struct sk_buff *skb); /* optional */
- int (*push_oam)(struct atm_vcc *vcc,void *cell);
int (*send)(struct atm_vcc *vcc,struct sk_buff *skb);
void *dev_data; /* per-device data */
void *proto_data; /* per-protocol data */
@@ -170,12 +169,6 @@ struct atm_dev {
struct list_head dev_list; /* linkage */
};
-
-/* OF: send_Oam Flags */
-
-#define ATM_OF_IMMED 1 /* Attempt immediate delivery */
-#define ATM_OF_INRATE 2 /* Attempt in-rate delivery */
-
struct atmdev_ops { /* only send is required */
void (*dev_close)(struct atm_dev *dev);
int (*open)(struct atm_vcc *vcc);
@@ -188,7 +181,6 @@ struct atmdev_ops { /* only send is required */
int (*pre_send)(struct atm_vcc *vcc, struct sk_buff *skb);
int (*send)(struct atm_vcc *vcc,struct sk_buff *skb);
int (*send_bh)(struct atm_vcc *vcc, struct sk_buff *skb);
- int (*send_oam)(struct atm_vcc *vcc,void *cell,int flags);
void (*phy_put)(struct atm_dev *dev,unsigned char value,
unsigned long addr);
unsigned char (*phy_get)(struct atm_dev *dev,unsigned long addr);
diff --git a/drivers/atm/solos-pci.c b/drivers/atm/solos-pci.c
index bcb1353877e4..4ad170a858ee 100644
--- a/drivers/atm/solos-pci.c
+++ b/drivers/atm/solos-pci.c
@@ -1180,7 +1180,6 @@ static const struct atmdev_ops fpga_ops = {
.close = pclose,
.ioctl = NULL,
.send = psend,
- .send_oam = NULL,
.phy_put = NULL,
.phy_get = NULL,
.change_qos = NULL,
diff --git a/net/atm/common.c b/net/atm/common.c
index 913f7e32ce41..c6e87fc9bbfc 100644
--- a/net/atm/common.c
+++ b/net/atm/common.c
@@ -163,7 +163,6 @@ int vcc_create(struct net *net, struct socket *sock, int protocol, int family, i
vcc->push = NULL;
vcc->pop = NULL;
vcc->owner = NULL;
- vcc->push_oam = NULL;
vcc->release_cb = NULL;
vcc->vpi = vcc->vci = 0; /* no VCI/VPI yet */
vcc->atm_options = vcc->aal_options = 0;
diff --git a/net/atm/raw.c b/net/atm/raw.c
index 0d36aeb3671b..1d6ac7b0c4e5 100644
--- a/net/atm/raw.c
+++ b/net/atm/raw.c
@@ -63,7 +63,6 @@ int atm_init_aal0(struct atm_vcc *vcc)
{
vcc->push = atm_push_raw;
vcc->pop = atm_pop_raw;
- vcc->push_oam = NULL;
vcc->send = atm_send_aal0;
return 0;
}
@@ -72,7 +71,6 @@ int atm_init_aal5(struct atm_vcc *vcc)
{
vcc->push = atm_push_raw;
vcc->pop = atm_pop_raw;
- vcc->push_oam = NULL;
if (vcc->dev->ops->send_bh)
vcc->send = vcc->dev->ops->send_bh;
else
--
2.54.0
^ permalink raw reply related
* [PATCH net-next 1/9] atm: remove AAL3/4 transport support
From: Jakub Kicinski @ 2026-06-13 20:10 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, 3chas3, mitch,
linux-atm-general, dwmw2, Jakub Kicinski
In-Reply-To: <20260613201032.77274-1-kuba@kernel.org>
AAL3/4 is an obsolete connection-oriented ATM adaptation layer that has
seen no real use since the SMDS-era hardware it was designed for (90s?).
We are only maintaining ATM support in-tree to keep PPPoATM running,
and PPPoATM runs over AAL5.
Drop the "raw" AAL3/4 transport (atm_init_aal34()) and the ATM_AAL34
cases in the connect and traffic-parameter paths. A vcc_connect() with
qos.aal == ATM_AAL34 now fails with -EPROTOTYPE.
uAPI cleanup is performed later, separately.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
---
net/atm/protocols.h | 1 -
net/atm/common.c | 7 -------
net/atm/raw.c | 12 ------------
3 files changed, 20 deletions(-)
diff --git a/net/atm/protocols.h b/net/atm/protocols.h
index 18d4d008bac3..30158efb5e1a 100644
--- a/net/atm/protocols.h
+++ b/net/atm/protocols.h
@@ -8,7 +8,6 @@
#define NET_ATM_PROTOCOLS_H
int atm_init_aal0(struct atm_vcc *vcc); /* "raw" AAL0 */
-int atm_init_aal34(struct atm_vcc *vcc);/* "raw" AAL3/4 transport */
int atm_init_aal5(struct atm_vcc *vcc); /* "raw" AAL5 transport */
#endif
diff --git a/net/atm/common.c b/net/atm/common.c
index 60132de4eebe..913f7e32ce41 100644
--- a/net/atm/common.c
+++ b/net/atm/common.c
@@ -293,9 +293,6 @@ static int adjust_tp(struct atm_trafprm *tp, unsigned char aal)
case ATM_AAL0:
max_sdu = ATM_CELL_SIZE-1;
break;
- case ATM_AAL34:
- max_sdu = ATM_MAX_AAL34_PDU;
- break;
default:
pr_warn("AAL problems ... (%d)\n", aal);
fallthrough;
@@ -411,10 +408,6 @@ static int __vcc_connect(struct atm_vcc *vcc, struct atm_dev *dev, short vpi,
error = atm_init_aal0(vcc);
vcc->stats = &dev->stats.aal0;
break;
- case ATM_AAL34:
- error = atm_init_aal34(vcc);
- vcc->stats = &dev->stats.aal34;
- break;
case ATM_NO_AAL:
/* ATM_AAL5 is also used in the "0 for default" case */
vcc->qos.aal = ATM_AAL5;
diff --git a/net/atm/raw.c b/net/atm/raw.c
index 1e6511ec842c..0d36aeb3671b 100644
--- a/net/atm/raw.c
+++ b/net/atm/raw.c
@@ -68,18 +68,6 @@ int atm_init_aal0(struct atm_vcc *vcc)
return 0;
}
-int atm_init_aal34(struct atm_vcc *vcc)
-{
- vcc->push = atm_push_raw;
- vcc->pop = atm_pop_raw;
- vcc->push_oam = NULL;
- if (vcc->dev->ops->send_bh)
- vcc->send = vcc->dev->ops->send_bh;
- else
- vcc->send = vcc->dev->ops->send;
- return 0;
-}
-
int atm_init_aal5(struct atm_vcc *vcc)
{
vcc->push = atm_push_raw;
--
2.54.0
^ permalink raw reply related
* [PATCH net-next 0/9] atm: remove more dead code
From: Jakub Kicinski @ 2026-06-13 20:10 UTC (permalink / raw)
To: davem
Cc: netdev, edumazet, pabeni, andrew+netdev, horms, 3chas3, mitch,
linux-atm-general, dwmw2, Jakub Kicinski
Commit 6deb53595092 ("net: remove unused ATM protocols and legacy
ATM device drivers") removed a good chunk of old ATM drivers.
Our goal going forward is to limit the ATM support to PPPoATM
used in ADSL deployments.
A recent burst of AI generated fixes for net/atm/signaling.c and
net/atm/svc.c made me look closer at the remaining code. PPPoATM runs
over permanent virtual circuits (PF_ATMPVC) with a statically
configured VPI/VCI. We can drop switched virtual circuits (SVCs)
and user-space signaling (atmsigd) support. While digging around
I noticed a few more obviously dead pieces of code.
Annoyingly, I have applied one "fix" to QoS config which will
now make net conflict with this series :/
Jakub Kicinski (9):
atm: remove AAL3/4 transport support
atm: remove the unused send_oam / push_oam callbacks
atm: remove dead SONET PHY ioctls
atm: remove the local ATM (NSAP) address registry
atm: remove SVC socket support and the signaling daemon interface
atm: remove the unused change_qos device operation
atm: remove the unused pre_send and send_bh device operations
atm: remove unused ATM PHY operations
atm: remove orphaned uAPI for deleted drivers, protocols and SVCs
net/atm/Makefile | 2 +-
include/linux/atm_tcp.h | 24 --
include/linux/atmdev.h | 70 +--
include/uapi/linux/atm_eni.h | 24 --
include/uapi/linux/atm_he.h | 21 -
include/uapi/linux/atm_idt77105.h | 29 --
include/uapi/linux/atm_nicstar.h | 54 ---
include/uapi/linux/atm_tcp.h | 62 ---
include/uapi/linux/atm_zatm.h | 47 --
include/uapi/linux/atmarp.h | 42 --
include/uapi/linux/atmclip.h | 22 -
include/uapi/linux/atmdev.h | 18 -
include/uapi/linux/atmlec.h | 92 ----
include/uapi/linux/atmmpc.h | 127 ------
include/uapi/linux/atmsvc.h | 56 ---
net/atm/addr.h | 21 -
net/atm/common.h | 5 -
net/atm/protocols.h | 1 -
net/atm/signaling.h | 31 --
drivers/atm/solos-pci.c | 4 -
net/atm/addr.c | 162 -------
net/atm/atm_sysfs.c | 25 --
net/atm/br2684.c | 3 +-
net/atm/common.c | 73 +---
net/atm/ioctl.c | 58 ---
net/atm/pppoatm.c | 3 +-
net/atm/proc.c | 56 ---
net/atm/raw.c | 21 +-
net/atm/resources.c | 54 ---
net/atm/signaling.c | 297 -------------
net/atm/svc.c | 695 ------------------------------
31 files changed, 14 insertions(+), 2185 deletions(-)
delete mode 100644 include/linux/atm_tcp.h
delete mode 100644 include/uapi/linux/atm_eni.h
delete mode 100644 include/uapi/linux/atm_he.h
delete mode 100644 include/uapi/linux/atm_idt77105.h
delete mode 100644 include/uapi/linux/atm_nicstar.h
delete mode 100644 include/uapi/linux/atm_tcp.h
delete mode 100644 include/uapi/linux/atm_zatm.h
delete mode 100644 include/uapi/linux/atmarp.h
delete mode 100644 include/uapi/linux/atmclip.h
delete mode 100644 include/uapi/linux/atmlec.h
delete mode 100644 include/uapi/linux/atmmpc.h
delete mode 100644 include/uapi/linux/atmsvc.h
delete mode 100644 net/atm/addr.h
delete mode 100644 net/atm/signaling.h
delete mode 100644 net/atm/addr.c
delete mode 100644 net/atm/signaling.c
delete mode 100644 net/atm/svc.c
--
2.54.0
^ permalink raw reply
* Re: [PATCH bpf v5 1/2] bpf: Run generic devmap egress prog on private skb
From: Jakub Kicinski @ 2026-06-13 20:03 UTC (permalink / raw)
To: Alexei Starovoitov
Cc: Sun Jian, bpf, Network Development, LKML,
open list:KERNEL SELFTEST FRAMEWORK, Alexei Starovoitov,
Daniel Borkmann, Andrii Nakryiko, Martin KaFai Lau,
David S. Miller, Jesper Dangaard Brouer, John Fastabend,
Stanislav Fomichev, Shuah Khan, Jiayuan Chen,
Toke Høiland-Jørgensen, Menglong Dong, Emil Tsalapatis
In-Reply-To: <CAADnVQJyOR0hppTmhbTRS8Sp5ci-L0fmQ6V+R9eiJDop_95rVA@mail.gmail.com>
On Sat, 13 Jun 2026 10:53:35 -0700 Alexei Starovoitov wrote:
> On Sat, Jun 13, 2026 at 10:25 AM Jakub Kicinski <kuba@kernel.org> wrote:
> >
> > On Fri, 12 Jun 2026 19:40:31 +0800 Sun Jian wrote:
> > > Suggested-by: Jakub Kicinski <kuba@kernel.org>
> >
> > I did not suggest this
>
> ohh. I didn't follow discussion closely.
> Do you want me to revert the whole set or just remove that line?
It's alright, I was just being grumpy.
Maybe we can amend Chris's prompts to catch these?
Noobs tend to add Suggested-by for people who simply commented
on patches.
^ permalink raw reply
* Re: [PATCH net] virtio-net: fix len check in receive_big()
From: Xiang Mei @ 2026-06-13 19:58 UTC (permalink / raw)
To: David Laight
Cc: mst, jasowang, xuanzhuo, eperezma, andrew+netdev, davem, edumazet,
kuba, pabeni, netdev, virtualization, linux-kernel,
minhquangbui99, bestswngs
In-Reply-To: <20260611104836.469610b0@pumpkin>
On Thu, Jun 11, 2026 at 2:48 AM David Laight
<david.laight.linux@gmail.com> wrote:
>
> On Wed, 10 Jun 2026 15:16:06 -0700
> Xiang Mei <xmei5@asu.edu> wrote:
>
> > receive_big() bounds the device-announced length by
> > (big_packets_num_skbfrags + 1) * PAGE_SIZE. That is still too loose:
> > add_recvbuf_big() sets sg[1] to start at offset
> > sizeof(struct padded_vnet_hdr) into the first page, so the chain
> > actually carries hdr_len + (PAGE_SIZE - sizeof(padded_vnet_hdr)) +
> > big_packets_num_skbfrags * PAGE_SIZE bytes -- 20 bytes less than the
> > check allows for the common hdr_len == 12 case.
> >
> > A malicious virtio backend can announce a len in that gap. page_to_skb()
> > then walks one frag past the page chain, storing a NULL page->private
> > into skb_shinfo()->frags[MAX_SKB_FRAGS], which is both an out-of-bounds
> > write past the static frag array and a NULL frag handed up the rx path.
> >
> > Bound len by the size add_recvbuf_big() actually advertised.
> >
> > Fixes: 0c716703965f ("virtio-net: fix received length check in big packets")
> > Reported-by: Weiming Shi <bestswngs@gmail.com>
> > Signed-off-by: Xiang Mei <xmei5@asu.edu>
> > ---
> > drivers/net/virtio_net.c | 8 +++++---
> > 1 file changed, 5 insertions(+), 3 deletions(-)
> >
> > diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
> > index f4adcfee7a80..afe73eda1491 100644
> > --- a/drivers/net/virtio_net.c
> > +++ b/drivers/net/virtio_net.c
> > @@ -1999,15 +1999,17 @@ static struct sk_buff *receive_big(struct net_device *dev,
> > struct virtnet_rq_stats *stats)
> > {
> > struct page *page = buf;
> > + unsigned long max_len;
> > struct sk_buff *skb;
> >
> > /* Make sure that len does not exceed the size allocated in
> > * add_recvbuf_big.
> > */
> > - if (unlikely(len > (vi->big_packets_num_skbfrags + 1) * PAGE_SIZE)) {
> > + max_len = vi->hdr_len + (PAGE_SIZE - sizeof(struct padded_vnet_hdr)) +
> > + vi->big_packets_num_skbfrags * PAGE_SIZE;
>
> That looks like a constant (for the vi).
Thanks, agreed it's a per-device constant, so the expression never
changes after setup.
> Probably worth saving rather than recalculating all the time.
>
I thought about caching it in a vi->big_packets_max_len field computed
once after virtnet_set_big_packets(). My only concern is that the
saving is marginal here: receive_big() is only reached when
mergeable_rx_bufs is off (the "else if (vi->big_packets)" branch). Any
backend that negotiates VIRTIO_NET_F_MRG_RXBUF takes the
receive_mergeable() path for all RX, so receive_big() is never hit
there at all. And the bound has exactly one consumer.
So it's trading a 4-byte field (probably free in existing padding)
against one cache-hit load plus a shift, and two adds, on a path
that's effectively cold on modern setups. I mainly work on security
issues, so I would like to listen to your idea.
Xiang
> -- David
>
> > + if (unlikely(len > max_len)) {
> > pr_debug("%s: rx error: len %u exceeds allocated size %lu\n",
> > - dev->name, len,
> > - (vi->big_packets_num_skbfrags + 1) * PAGE_SIZE);
> > + dev->name, len, max_len);
> > goto err;
> > }
> >
>
^ permalink raw reply
* Re: [PATCH v1 net-next 1/1] tcp: Replace min_tso_segs() with tso_segs() CC callback for TCP Prague
From: kernel test robot @ 2026-06-13 19:54 UTC (permalink / raw)
To: chia-yu.chang, andrii, eddyz87, horms, dsahern, bpf, netdev,
pabeni, jhs, kuba, stephen, davem, edumazet, andrew+netdev,
donald.hunter, kuniyu, ij, ncardwell, koen.de_schepper, g.white,
ingemar.s.johansson, mirja.kuehlewind, cheshire, rs.ietf,
Jason_Livingood, vidhi_goel
Cc: oe-kbuild-all, Chia-Yu Chang
In-Reply-To: <20260613080831.236636-1-chia-yu.chang@nokia-bell-labs.com>
Hi,
kernel test robot noticed the following build errors:
[auto build test ERROR on net-next/main]
url: https://github.com/intel-lab-lkp/linux/commits/chia-yu-chang-nokia-bell-labs-com/tcp-Replace-min_tso_segs-with-tso_segs-CC-callback-for-TCP-Prague/20260613-160954
base: net-next/main
patch link: https://lore.kernel.org/r/20260613080831.236636-1-chia-yu.chang%40nokia-bell-labs.com
patch subject: [PATCH v1 net-next 1/1] tcp: Replace min_tso_segs() with tso_segs() CC callback for TCP Prague
config: x86_64-randconfig-075 (https://download.01.org/0day-ci/archive/20260614/202606140311.15fhp3QN-lkp@intel.com/config)
compiler: gcc-13 (Debian 13.3.0-16) 13.3.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260614/202606140311.15fhp3QN-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202606140311.15fhp3QN-lkp@intel.com/
All errors (new ones prefixed by >>, old ones prefixed by <<):
>> ERROR: modpost: "tcp_tso_autosize" [net/ipv4/tcp_bbr.ko] undefined!
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Re: [PATCH net-next 1/8] dt-bindings: net: realtek,rtl9301-mdio: Add RTL83xx series
From: Krzysztof Kozlowski @ 2026-06-13 19:07 UTC (permalink / raw)
To: Markus Stockhausen
Cc: andrew, hkallweit1, linux, davem, edumazet, kuba, pabeni, netdev,
chris.packham, daniel, robh, krzk+dt, conor+dt, devicetree
In-Reply-To: <20260613112946.1071411-2-markus.stockhausen@gmx.de>
On Sat, Jun 13, 2026 at 01:29:39PM +0200, Markus Stockhausen wrote:
- realtek,rtl9302b-mdio
> @@ -24,6 +34,8 @@ properties:
> - realtek,rtl9313-mdio
> - const: realtek,rtl9311-mdio
> - enum:
> + - realtek,rtl8380-mdio
> + - realtek,rtl8391-mdio
> - realtek,rtl9301-mdio
I don't have that context in my recent next, but anyway looks correct.
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH net-next v5 4/4] net: dsa: initial support for MT7628 embedded switch
From: Daniel Golle @ 2026-06-13 18:52 UTC (permalink / raw)
To: Joris Vaisvila
Cc: netdev, horms, pabeni, kuba, edumazet, davem, olteanv,
Andrew Lunn, devicetree, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Arınç ÜNAL, Landen Chao,
DENG Qingfang, Sean Wang
In-Reply-To: <20260613181845.111877-5-joey@tinyisr.com>
On Sat, Jun 13, 2026 at 09:18:45PM +0300, Joris Vaisvila wrote:
> Add support for the MT7628 embedded switch.
>
> The switch has 5 built-in 100Mbps user ports (ports 0-4) and one 1Gbps
> port that is internally attached to the SoCs CPU MAC and serves as the
> CPU port.
>
> The switch hardware has a very limited 16 entry VLAN table. Configuring
> VLANs is the only way to control switch forwarding. Currently 6 entries
> are used by tag_8021q to isolate the ports. Double tag feature is
> enabled to force the switch to append the VLAN tag even if the incoming
> packet is already tagged, this simulates VLAN-unaware functionality and
> simplifies the tagger implementation.
>
> Signed-off-by: Joris Vaisvila <joey@tinyisr.com>
> [...]
> diff --git a/drivers/net/dsa/mt7628.c b/drivers/net/dsa/mt7628.c
> new file mode 100644
> index 000000000000..cedf063ad749
> --- /dev/null
> +++ b/drivers/net/dsa/mt7628.c
> [...]
> +
> +static const struct regmap_config mt7628_esw_regmap_cfg = {
> + .name = "mt7628-esw",
> + .reg_bits = 32,
> + .val_bits = 32,
> + .reg_stride = 4,
> + .fast_io = true,
> + .reg_format_endian = REGMAP_ENDIAN_LITTLE,
> + .val_format_endian = REGMAP_ENDIAN_LITTLE,
> +};
> +
> +struct mt7628_vlan {
> + bool active;
> + u8 members;
> + u8 untag;
> + u16 vid;
> +};
> +
> +struct mt7628_esw {
> + void __iomem *base;
Why even keep *base here if actual access all happens via the
regmap created for it?
Other than that lgtm
Reviewed-by: Daniel Golle <daniel@makrotopia.org>
^ permalink raw reply
* Re: [PATCH net-next v5 3/4] net: dsa: initial MT7628 tagging driver
From: Daniel Golle @ 2026-06-13 18:49 UTC (permalink / raw)
To: Joris Vaisvila
Cc: netdev, horms, pabeni, kuba, edumazet, davem, olteanv,
Andrew Lunn, devicetree, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Arınç ÜNAL, Landen Chao,
DENG Qingfang, Sean Wang
In-Reply-To: <20260613181845.111877-4-joey@tinyisr.com>
On Sat, Jun 13, 2026 at 09:18:44PM +0300, Joris Vaisvila wrote:
> Add support for the MT7628 embedded switch's tag.
>
> The MT7628 tag is merged with the VLAN TPID field when a VLAN is
> appended by the switch hardware. It is not installed if the VLAN tag is
> already there on ingress. Due to this hardware quirk the tag cannot be
> trusted for port 0 if we don't know that the VLAN was added by the
> hardware. As a workaround for this the switch is configured to always
> append the port PVID tag even if the incoming packet is already tagged.
> The tagging driver can then trust that the tag is always accurate and
> the whole VLAN tag can be removed on ingress as it's only metadata for
> the tagger.
>
> On egress the MT7628 tag allows precise TX, but the correct VLAN tag
> from tag_8021q is still appended or the switch will not forward the
> packet.
>
> Signed-off-by: Joris Vaisvila <joey@tinyisr.com>
> Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Daniel Golle <daniel@makrotopia.org>
^ permalink raw reply
* Re: [PATCH net-next v5 2/4] net: phy: mediatek: add phy driver for MT7628 built-in Fast Ethernet PHYs
From: Daniel Golle @ 2026-06-13 18:49 UTC (permalink / raw)
To: Joris Vaisvila
Cc: netdev, horms, pabeni, kuba, edumazet, davem, olteanv,
Andrew Lunn, devicetree, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Arınç ÜNAL, Landen Chao,
DENG Qingfang, Sean Wang
In-Reply-To: <20260613181845.111877-3-joey@tinyisr.com>
On Sat, Jun 13, 2026 at 09:18:43PM +0300, Joris Vaisvila wrote:
> The Fast Ethernet PHYs present in the MT7628 SoCs require an
> undocumented bit to be set before they can establish 100mbps links.
>
> This commit adds the Kconfig option MEDIATEK_FE_SOC_PHY and the
> corresponding driver mtk-fe-soc.c.
>
> Signed-off-by: Joris Vaisvila <joey@tinyisr.com>
> Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Reviewed-by: Daniel Golle <daniel@makrotopia.org>
> [...]
> diff --git a/drivers/net/phy/mediatek/mtk-fe-soc.c b/drivers/net/phy/mediatek/mtk-fe-soc.c
> new file mode 100644
> index 000000000000..9eb4960bcaad
> --- /dev/null
> +++ b/drivers/net/phy/mediatek/mtk-fe-soc.c
> @@ -0,0 +1,50 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Driver for MT7628 Embedded Switch internal Fast Ethernet PHYs
> + */
> +#include <linux/module.h>
> +#include <linux/phy.h>
> +
> +#define MTK_FPHY_ID_MT7628 0x03a29410
> +#define MTK_EXT_PAGE_ACCESS 0x1f
> +
> +static int mt7628_phy_read_page(struct phy_device *phydev)
> +{
> + return __phy_read(phydev, MTK_EXT_PAGE_ACCESS);
> +}
> +
> +static int mt7628_phy_write_page(struct phy_device *phydev, int page)
> +{
> + return __phy_write(phydev, MTK_EXT_PAGE_ACCESS, page);
> +}
> +
> +static int mt7628_phy_config_init(struct phy_device *phydev)
> +{
> + /*
> + * This undocumented bit is required for the PHYs to be able to
> + * establish 100mbps links.
> + */
> + return phy_modify_paged(phydev, 0x8000, 30, BIT(13), BIT(13));
As there is only that single use I don't think it justifies introducing
a phy_set_bits_paged helper just for that, but that's my opinion...
^ permalink raw reply
* [PATCH v1 bpf-next 2/2] selftests/bpf: Add tests for bpf_redirect_peer with BPF_F_INGRESS
From: Jordan Rife @ 2026-06-13 18:34 UTC (permalink / raw)
To: bpf
Cc: Jordan Rife, netdev, Alexei Starovoitov, Daniel Borkmann,
Andrii Nakryiko, Martin KaFai Lau, Stanislav Fomichev
In-Reply-To: <20260613183424.1198073-1-jordan@jrife.io>
Extend redirect tests to cover bpf_redirect_peer(BPF_F_INGRESS). SRC
redirects to DST using bpf_redirect_peer(BPF_F_INGRESS) then traffic is
hairpinned into DST using bpf_redirect(BPF_F_INGRESS).
Signed-off-by: Jordan Rife <jordan@jrife.io>
---
.../selftests/bpf/prog_tests/tc_redirect.c | 68 +++++++++++++++++++
.../selftests/bpf/progs/test_tc_peer.c | 22 ++++++
2 files changed, 90 insertions(+)
diff --git a/tools/testing/selftests/bpf/prog_tests/tc_redirect.c b/tools/testing/selftests/bpf/prog_tests/tc_redirect.c
index 64fbda082309..af8968b89ad7 100644
--- a/tools/testing/selftests/bpf/prog_tests/tc_redirect.c
+++ b/tools/testing/selftests/bpf/prog_tests/tc_redirect.c
@@ -192,6 +192,8 @@ static int create_netkit(int mode, char *prim, char *peer)
req.n.nlmsg_len += sizeof(struct ifinfomsg);
addattr_l(&req.n, sizeof(req), IFLA_IFNAME, peer, strlen(peer));
addattr_nest_end(&req.n, peer_info);
+ addattr32(&req.n, sizeof(req), IFLA_NETKIT_SCRUB,
+ NETKIT_SCRUB_NONE);
addattr_nest_end(&req.n, data);
addattr_nest_end(&req.n, linkinfo);
@@ -405,6 +407,24 @@ static int netns_load_bpf(const struct bpf_program *src_prog,
return -1;
}
+static struct bpf_link *netns_attach_nk(const char *ns, int ifindex,
+ struct bpf_program *prog)
+{
+ LIBBPF_OPTS(bpf_netkit_opts, optl);
+ struct nstoken *nstoken = NULL;
+ struct bpf_link *link = NULL;
+
+ nstoken = open_netns(ns);
+ if (!ASSERT_OK_PTR(nstoken, "setns"))
+ goto cleanup;
+
+ link = bpf_program__attach_netkit(prog, ifindex, &optl);
+cleanup:
+ if (nstoken)
+ close_netns(nstoken);
+ return link;
+}
+
static void test_tcp(int family, const char *addr, __u16 port)
{
int listen_fd = -1, accept_fd = -1, client_fd = -1;
@@ -1082,6 +1102,53 @@ static void test_tc_redirect_peer(struct netns_setup_result *setup_result)
close_netns(nstoken);
}
+static void test_tc_redirect_peer_ing(struct netns_setup_result *setup_result)
+{
+ struct test_tc_peer *skel;
+ struct nstoken *nstoken;
+ int err;
+
+ nstoken = open_netns(NS_FWD);
+ if (!ASSERT_OK_PTR(nstoken, "setns fwd"))
+ return;
+
+ skel = test_tc_peer__open();
+ if (!ASSERT_OK_PTR(skel, "test_tc_peer__open"))
+ goto done;
+
+ skel->rodata->IFINDEX_SRC = setup_result->ifindex_src_fwd;
+ skel->rodata->IFINDEX_DST = setup_result->ifindex_dst_fwd;
+ ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc_src_ing,
+ BPF_NETKIT_PRIMARY), 0, "src_prog_attach_type");
+ ASSERT_EQ(bpf_program__set_expected_attach_type(skel->progs.tc_dst_ing,
+ BPF_NETKIT_PRIMARY), 0, "dst_prog_attach_type");
+
+ err = test_tc_peer__load(skel);
+ if (!ASSERT_OK(err, "test_tc_peer__load"))
+ goto done;
+
+ skel->links.tc_src_ing = netns_attach_nk(NS_SRC,
+ setup_result->ifindex_src,
+ skel->progs.tc_src_ing);
+ if (!ASSERT_OK_PTR(skel->links.tc_src_ing, "attach_src"))
+ goto done;
+ skel->links.tc_dst_ing = netns_attach_nk(NS_DST,
+ setup_result->ifindex_dst,
+ skel->progs.tc_dst_ing);
+ if (!ASSERT_OK_PTR(skel->links.tc_dst_ing, "attach_dst"))
+ goto done;
+
+ if (!ASSERT_OK(set_forwarding(false), "disable forwarding"))
+ goto done;
+
+ test_connectivity();
+
+done:
+ if (skel)
+ test_tc_peer__destroy(skel);
+ close_netns(nstoken);
+}
+
static int tun_open(char *name)
{
struct ifreq ifr;
@@ -1280,6 +1347,7 @@ static void *test_tc_redirect_run_tests(void *arg)
RUN_TEST(tc_redirect_peer, MODE_VETH);
RUN_TEST(tc_redirect_peer, MODE_NETKIT);
+ RUN_TEST(tc_redirect_peer_ing, MODE_NETKIT);
RUN_TEST(tc_redirect_peer_l3, MODE_VETH);
RUN_TEST(tc_redirect_peer_l3, MODE_NETKIT);
RUN_TEST(tc_redirect_neigh, MODE_VETH);
diff --git a/tools/testing/selftests/bpf/progs/test_tc_peer.c b/tools/testing/selftests/bpf/progs/test_tc_peer.c
index 365eacb5dc34..1f2345f05fcd 100644
--- a/tools/testing/selftests/bpf/progs/test_tc_peer.c
+++ b/tools/testing/selftests/bpf/progs/test_tc_peer.c
@@ -34,6 +34,28 @@ int tc_src(struct __sk_buff *skb)
return bpf_redirect_peer(IFINDEX_DST, 0);
}
+SEC("tc")
+int tc_dst_ing(struct __sk_buff *skb)
+{
+ if (!skb->mark) {
+ skb->mark = 0x1;
+ return bpf_redirect_peer(IFINDEX_SRC, BPF_F_INGRESS);
+ }
+
+ return bpf_redirect(IFINDEX_DST, 0);
+}
+
+SEC("tc")
+int tc_src_ing(struct __sk_buff *skb)
+{
+ if (!skb->mark) {
+ skb->mark = 0x1;
+ return bpf_redirect_peer(IFINDEX_DST, BPF_F_INGRESS);
+ }
+
+ return bpf_redirect(IFINDEX_SRC, 0);
+}
+
SEC("tc")
int tc_dst_l3(struct __sk_buff *skb)
{
--
2.43.0
^ permalink raw reply related
* [PATCH v1 bpf-next 1/2] bpf: Support BPF_F_INGRESS with bpf_redirect_peer
From: Jordan Rife @ 2026-06-13 18:34 UTC (permalink / raw)
To: bpf
Cc: Jordan Rife, netdev, Alexei Starovoitov, Daniel Borkmann,
Andrii Nakryiko, Martin KaFai Lau, Stanislav Fomichev
In-Reply-To: <20260613183424.1198073-1-jordan@jrife.io>
We have several use cases where a pod injects traffic into the datapath
of another so that the traffic appears to have originated from that
pod. One such use case is a synthetic flow generator which injects
synthetic traffic into a pod's datapath to enable dynamic probing and
debugging. Another is a transparent proxy where connections originating
from one pod are redirected towards another which proxies that
connection. The new connection is bound to the IP of the original pod
using IP_TRANSPARENT and its traffic is injected into that pod's
datapath and handled as if it had originated there. This can be used for
mTLS, etc.
We use bpf_redirect(BPF_F_INGRESS) to direct traffic leaving the proxy,
flow generator, etc. towards the target pod, ensuring that eBPF programs
that are meant to intercept traffic leaving that pod are executed.
However, this doesn't work with netkit.
With netkit, an ingress redirection from proxy to workload skips eBPF
programs that are meant to intercept traffic leaving the pod, since they
reside on the netkit peer device. One workaround is to attach the
same program to both the netkit peer device and the TCX ingress hook for
the netkit pair's primary interface, but
a) This seems hacky and we need to be careful not to run the same
program twice for the same skb in cases where we want to pass that
traffic to the host stack.
b) We're trying to keep the proxy redirection / traffic injection
systems as modular and separated from Cilium as possible, the system
that manages netkit setup and core eBPF programming.
It would be handy if instead we could redirect traffic directly from
one netkit peer device to another. This patch proposes an extension
to bpf_redirect_peer to allow us to do just that.
With this patch, the BPF_F_INGRESS flag tells bpf_redirect_peer to emit
the skb in the egress direction of the target interface's peer device
While the main use case is netkit, I suppose you could also use this
mode with veth as well if, e.g., there were some eBPF programs attached
to that side of the veth pair that needed to intercept traffic.
+---------------------------------------------------------------------+
| +-------------------------+ 6. bpf_redirect_neigh(eth0) |
| | pod (10.244.0.10) | ------------------------ |
| | | | | |
| | +--------+ | | +---------+ | |
| | 1. packet -->| | | | | | | |
| | leaves ^ | netkit |<===========|======| netkit | | |
| | | | peer |=======(eBPF)=====>| primary | | |
| | | | | | | | | | |
| | | +--------+ | | +---------+ | |
| | | | | 2. bpf_redirect v |
| +-----------|-------------+ |___________________ +-------|
| | | | eth0 |
| | 5. bpf_redirect_peer(BPF_F_INGRESS) | +-------|
| |________________________ | |
| +-------------------------+ | | |
| | proxy (10.244.0.11) | | | |
| | IP_TRANSPARENT | | | |
| | +--------+ | | +---------+ | |
| | 3. packet <--| | | | | |<-- |
| | enters | netkit |<===========|======| netkit | |
| | [proxy] | peer |=======(eBPF)=====>| primary | |
| | 4. packet -->| | | | | |
| | leaves +--------+ | +---------+ |
| | sip=10.244.0.10 | |
| +-------------------------+ |
+---------------------------------------------------------------------+
Using the proxy use case as an example, in step 5 we would redirect
traffic leaving the proxy towards the pod's peer device using
bpf_redirect_peer(BPF_F_INGRESS).
As a bonus, since the skb doesn't have to go through the backlog queue
it can take full advantage of netkit's performance benefits. I set up a
test where outgoing iperf3 traffic is injected into the datapath of
another pod using either bpf_redirect_peer(BPF_F_INGRESS) or
bpf_redirect(BPF_F_INGRESS). I used Cilium's eBPF host routing mode
which skips the host stack and uses BPF redirect helpers to do all the
routing.
(net.ipv4.tcp_congestion_control=cubic,mtu=1500,100GiB link,Cilium
eBPF host routing mode)
BASELINE [bpf_redirect(BPF_F_INGRESS)]
1. [iperf pod] ==bpf_redirect([pod b], BPF_F_INGRESS)==> [pod b]
2. [pod b] ==bpf_redirect_neigh([eth0])==> eth0
3. eth0 ==over network==> [host b]
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-60.00 sec 231 GBytes 33.0 Gbits/sec 12060 sender
[ 5] 0.00-60.00 sec 230 GBytes 33.0 Gbits/sec receiver
TEST [bpf_redirect_peer(BPF_F_INGRESS)]
1. [iperf pod] ==bpf_redirect_peer([pod b], BPF_F_INGRESS)==> [pod b]
2. [pod b] ==bpf_redirect_neigh([eth0])==> eth0
3. eth0 ==over network==> [host b]
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-60.00 sec 272 GBytes 38.9 Gbits/sec 0 sender
[ 5] 0.00-60.00 sec 272 GBytes 38.9 Gbits/sec receiver
In this test, using bpf_redirect_peer(BPF_F_INGRESS) for the hop from
[iperf pod] to [pod b] led to ~18% more throughput compared to
bpf_redirect(BPF_F_INGRESS).
Signed-off-by: Jordan Rife <jordan@jrife.io>
---
include/uapi/linux/bpf.h | 16 +++++++++-------
net/core/filter.c | 14 ++++++++------
tools/include/uapi/linux/bpf.h | 16 +++++++++-------
3 files changed, 26 insertions(+), 20 deletions(-)
diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
index 11dd610fa5fa..dd0f2c3aea58 100644
--- a/include/uapi/linux/bpf.h
+++ b/include/uapi/linux/bpf.h
@@ -5074,17 +5074,19 @@ union bpf_attr {
* Description
* Redirect the packet to another net device of index *ifindex*.
* This helper is somewhat similar to **bpf_redirect**\ (), except
- * that the redirection happens to the *ifindex*' peer device and
- * the netns switch takes place from ingress to ingress without
- * going through the CPU's backlog queue.
+ * that the redirection happens to the *ifindex*' peer device. If
+ * *flags* is 0, the netns switch takes place from ingress to
+ * ingress without going through the CPU's backlog queue. If the
+ * **BPF_F_INGRESS** flag is provided then redirection happens in
+ * the egress direction of the peer device.
*
* *skb*\ **->mark** and *skb*\ **->tstamp** are not cleared during
* the netns switch.
*
- * The *flags* argument is reserved and must be 0. The helper is
- * currently only supported for tc BPF program types at the
- * ingress hook and for veth and netkit target device types. The
- * peer device must reside in a different network namespace.
+ * If the *flags* argument is 0, the helper is currently only
+ * supported for tc BPF program types at the ingress hook and for
+ * veth and netkit target device types. The peer device must reside
+ * in a different network namespace.
* Return
* The helper returns **TC_ACT_REDIRECT** on success or
* **TC_ACT_SHOT** on error.
diff --git a/net/core/filter.c b/net/core/filter.c
index 9590877b0714..c24fdf744f75 100644
--- a/net/core/filter.c
+++ b/net/core/filter.c
@@ -2529,16 +2529,18 @@ int skb_do_redirect(struct sk_buff *skb)
if (unlikely(!dev))
goto out_drop;
if (flags & BPF_F_PEER) {
- if (unlikely(!skb_at_tc_ingress(skb)))
- goto out_drop;
dev = skb_get_peer_dev(dev);
if (unlikely(!dev ||
!(dev->flags & IFF_UP) ||
net_eq(net, dev_net(dev))))
goto out_drop;
- skb->dev = dev;
- dev_sw_netstats_rx_add(dev, skb->len);
skb_scrub_packet(skb, false);
+ if (flags & BPF_F_INGRESS)
+ return __bpf_redirect(skb, dev, 0);
+ if (unlikely(!skb_at_tc_ingress(skb)))
+ goto out_drop;
+ dev_sw_netstats_rx_add(dev, skb->len);
+ skb->dev = dev;
return -EAGAIN;
}
return flags & BPF_F_NEIGH ?
@@ -2575,10 +2577,10 @@ BPF_CALL_2(bpf_redirect_peer, u32, ifindex, u64, flags)
{
struct bpf_redirect_info *ri = bpf_net_ctx_get_ri();
- if (unlikely(flags))
+ if (unlikely(flags & ~BPF_F_INGRESS))
return TC_ACT_SHOT;
- ri->flags = BPF_F_PEER;
+ ri->flags = BPF_F_PEER | flags;
ri->tgt_index = ifindex;
return TC_ACT_REDIRECT;
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index 11dd610fa5fa..dd0f2c3aea58 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -5074,17 +5074,19 @@ union bpf_attr {
* Description
* Redirect the packet to another net device of index *ifindex*.
* This helper is somewhat similar to **bpf_redirect**\ (), except
- * that the redirection happens to the *ifindex*' peer device and
- * the netns switch takes place from ingress to ingress without
- * going through the CPU's backlog queue.
+ * that the redirection happens to the *ifindex*' peer device. If
+ * *flags* is 0, the netns switch takes place from ingress to
+ * ingress without going through the CPU's backlog queue. If the
+ * **BPF_F_INGRESS** flag is provided then redirection happens in
+ * the egress direction of the peer device.
*
* *skb*\ **->mark** and *skb*\ **->tstamp** are not cleared during
* the netns switch.
*
- * The *flags* argument is reserved and must be 0. The helper is
- * currently only supported for tc BPF program types at the
- * ingress hook and for veth and netkit target device types. The
- * peer device must reside in a different network namespace.
+ * If the *flags* argument is 0, the helper is currently only
+ * supported for tc BPF program types at the ingress hook and for
+ * veth and netkit target device types. The peer device must reside
+ * in a different network namespace.
* Return
* The helper returns **TC_ACT_REDIRECT** on success or
* **TC_ACT_SHOT** on error.
--
2.43.0
^ permalink raw reply related
* [PATCH v1 bpf-next 0/2] bpf: bpf_redirect_peer egress redirection
From: Jordan Rife @ 2026-06-13 18:34 UTC (permalink / raw)
To: bpf
Cc: Jordan Rife, netdev, Alexei Starovoitov, Daniel Borkmann,
Andrii Nakryiko, Martin KaFai Lau, Stanislav Fomichev
We have several use cases where a pod injects traffic into the datapath
of another so that the traffic appears to have originated from that
pod. One such use case is a synthetic flow generator which injects
synthetic traffic into a pod's datapath to enable dynamic probing and
debugging. Another is a transparent proxy where connections originating
from one pod are redirected towards another which proxies that
connection. The new connection is bound to the IP of the original pod
using IP_TRANSPARENT and its traffic is injected into that pod's
datapath and handled as if it had originated there. This can be used for
mTLS, etc.
We use bpf_redirect(BPF_F_INGRESS) to direct traffic leaving the proxy,
flow generator, etc. towards the target pod, ensuring that eBPF programs
that are meant to intercept traffic leaving that pod are executed.
However, this doesn't work with netkit.
With netkit, an ingress redirection from proxy to workload skips eBPF
programs that are meant to intercept traffic leaving the pod, since they
reside on the netkit peer device. One workaround is to attach the
same program to both the netkit peer device and the TCX ingress hook for
the netkit pair's primary interface, but
a) This seems hacky and we need to be careful not to run the same
program twice for the same skb in cases where we want to pass that
traffic to the host stack.
b) We're trying to keep the proxy redirection / traffic injection
systems as modular and separated from Cilium as possible, the system
that manages netkit setup and core eBPF programming.
It would be handy if instead we could redirect traffic directly from
one netkit peer device to another. This patch proposes an extension
to bpf_redirect_peer to allow us to do just that.
With this patch, the BPF_F_INGRESS flag tells bpf_redirect_peer to emit
the skb in the egress direction of the target interface's peer device
While the main use case is netkit, I suppose you could also use this
mode with veth as well if, e.g., there were some eBPF programs attached
to that side of the veth pair that needed to intercept traffic.
+---------------------------------------------------------------------+
| +-------------------------+ 6. bpf_redirect_neigh(eth0) |
| | pod (10.244.0.10) | ------------------------ |
| | | | | |
| | +--------+ | | +---------+ | |
| | 1. packet -->| | | | | | | |
| | leaves ^ | netkit |<===========|======| netkit | | |
| | | | peer |=======(eBPF)=====>| primary | | |
| | | | | | | | | | |
| | | +--------+ | | +---------+ | |
| | | | | 2. bpf_redirect v |
| +-----------|-------------+ |___________________ +-------|
| | | | eth0 |
| | 5. bpf_redirect_peer(BPF_F_INGRESS) | +-------|
| |________________________ | |
| +-------------------------+ | | |
| | proxy (10.244.0.11) | | | |
| | IP_TRANSPARENT | | | |
| | +--------+ | | +---------+ | |
| | 3. packet <--| | | | | |<-- |
| | enters | netkit |<===========|======| netkit | |
| | [proxy] | peer |=======(eBPF)=====>| primary | |
| | 4. packet -->| | | | | |
| | leaves +--------+ | +---------+ |
| | sip=10.244.0.10 | |
| +-------------------------+ |
+---------------------------------------------------------------------+
Using the proxy use case as an example, in step 5 we would redirect
traffic leaving the proxy towards the pod's peer device using
bpf_redirect_peer(BPF_F_INGRESS).
As a bonus, since the skb doesn't have to go through the backlog queue
it can take full advantage of netkit's performance benefits. I set up a
test where outgoing iperf3 traffic is injected into the datapath of
another pod using either bpf_redirect_peer(BPF_F_INGRESS) or
bpf_redirect(BPF_F_INGRESS). I used Cilium's eBPF host routing mode
which skips the host stack and uses BPF redirect helpers to do all the
routing.
(net.ipv4.tcp_congestion_control=cubic,mtu=1500,100GiB link,Cilium
eBPF host routing mode)
BASELINE [bpf_redirect(BPF_F_INGRESS)]
1. [iperf pod] ==bpf_redirect([pod b], BPF_F_INGRESS)==> [pod b]
2. [pod b] ==bpf_redirect_neigh([eth0])==> eth0
3. eth0 ==over network==> [host b]
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-60.00 sec 231 GBytes 33.0 Gbits/sec 12060 sender
[ 5] 0.00-60.00 sec 230 GBytes 33.0 Gbits/sec receiver
TEST [bpf_redirect_peer(BPF_F_INGRESS)]
1. [iperf pod] ==bpf_redirect_peer([pod b], BPF_F_INGRESS)==> [pod b]
2. [pod b] ==bpf_redirect_neigh([eth0])==> eth0
3. eth0 ==over network==> [host b]
[ ID] Interval Transfer Bitrate Retr
[ 5] 0.00-60.00 sec 272 GBytes 38.9 Gbits/sec 0 sender
[ 5] 0.00-60.00 sec 272 GBytes 38.9 Gbits/sec receiver
In this test, using bpf_redirect_peer(BPF_F_INGRESS) for the hop from
[iperf pod] to [pod b] led to ~18% more throughput compared to
bpf_redirect(BPF_F_INGRESS).
Note: I wasn't sure about the flag name. I can see where BPF_F_INGRESS
might be confusing, since technically it's an egress redirection
from the perspective of the peer device's namespace. But, I didn't
want to add a BPF_F_EGRESS flag just for this and convinced myself
it makes sense, because from the perspective of the caller the skb
will be flowing towards the current namespace.
Jordan Rife (2):
bpf: Support BPF_F_INGRESS with bpf_redirect_peer
selftests/bpf: Add tests for bpf_redirect_peer with BPF_F_INGRESS
include/uapi/linux/bpf.h | 16 +++--
net/core/filter.c | 14 ++--
tools/include/uapi/linux/bpf.h | 16 +++--
.../selftests/bpf/prog_tests/tc_redirect.c | 68 +++++++++++++++++++
.../selftests/bpf/progs/test_tc_peer.c | 22 ++++++
5 files changed, 116 insertions(+), 20 deletions(-)
--
2.43.0
^ permalink raw reply
* Re: [PATCH net-next v2 0/2] net: isolate SKB data area allocations
From: Jakub Kicinski @ 2026-06-13 18:33 UTC (permalink / raw)
To: Pedro Falcato
Cc: Vlastimil Babka, Harry Yoo, Andrew Morton, David S. Miller,
Eric Dumazet, Paolo Abeni, linux-hardening, linux-mm, netdev,
linux-kernel, Hao Li, Christoph Lameter, David Rientjes,
Roman Gushchin, Simon Horman, Jason Xing, Kuniyuki Iwashima,
Kees Cook
In-Reply-To: <20260611124642.345400-1-pfalcato@suse.de>
On Thu, 11 Jun 2026 13:46:40 +0100 Pedro Falcato wrote:
> Subject: [PATCH net-next v2 0/2] net: isolate SKB data area allocations
This doesn't apply to net-next, does patch 2 not apply to mm?
If neither tree can take both - maybe MM can take the first patch by
itself and we will queue patch 2 after the changes propagate during
the merge window?
^ permalink raw reply
* Re: [PATCH net-next] net: sparx5: change ndo_set_rx_mode_async return type to int
From: Jakub Kicinski @ 2026-06-13 18:29 UTC (permalink / raw)
To: Robert Marko
Cc: andrew+netdev, davem, edumazet, pabeni, Steen.Hegelund,
daniel.machon, UNGLinuxDriver, sdf.kernel, netdev,
linux-arm-kernel, linux-kernel, luka.perkov
In-Reply-To: <20260611101151.426050-1-robert.marko@sartura.hr>
On Thu, 11 Jun 2026 12:11:13 +0200 Robert Marko wrote:
> Commit ("net: add retry mechanism to ndo_set_rx_mode_async") changed the
> ndo_set_rx_mode_async return type to int, however it did not update the
> SparX-5 driver.
>
> So, simply update the sparx5_set_rx_mode return type to int, propagate
> return from __hw_addr_sync_dev or simply return 0.
>
> Fixes: d90b85c23b3d ("net: add retry mechanism to ndo_set_rx_mode_async")
This commit does not exist, as I said in:
https://lore.kernel.org/all/20260507091012.7eeb17f5@kernel.org/
the first two patches of that series were _not_ applied.
--
pw-bot: reject
^ permalink raw reply
* Re: [PATCH net-next 1/3] net: busy-poll: introduce sk_tx_busy_loop()
From: Jakub Kicinski @ 2026-06-13 18:21 UTC (permalink / raw)
To: menglong8.dong
Cc: jasowang, mst, xuanzhuo, eperezma, andrew+netdev, davem, edumazet,
pabeni, magnus.karlsson, maciej.fijalkowski, sdf, horms, ast,
daniel, hawk, john.fastabend, bjorn, kerneljasonxing, netdev,
virtualization, linux-kernel, bpf
In-Reply-To: <20260611071242.2485058-2-dongml2@chinatelecom.cn>
On Thu, 11 Jun 2026 15:12:40 +0800 menglong8.dong@gmail.com wrote:
> For now, we use sk_busy_loop() for both rx and tx path. The sk_busy_loop()
> will call napi_busy_loop() for the specified napi_id. However, some
> nic drivers have tx napi, such as virtio-net. In this case, sk_busy_loop()
> doesn't work, as it can only schedule the NAPI for the rx queue.
>
> Therefore, introduce sk_tx_busy_loop() for the nic drivers that support tx
> napi, which will schedule the tx napi if available.
First, I thought the only difference with Tx NAPI is that it can't be
busy polled. So if you want to poll an instance don't register it as
a Tx one instead of adding all this "tx polling" stuff in the core?
Second, can this problem happen for any other NIC or is it purely
an artifact of virtio's delayed Tx completion handling?
Third, this series does not apply.
^ permalink raw reply
* [PATCH net-next v5 4/4] net: dsa: initial support for MT7628 embedded switch
From: Joris Vaisvila @ 2026-06-13 18:18 UTC (permalink / raw)
To: netdev
Cc: horms, pabeni, kuba, edumazet, davem, olteanv, Andrew Lunn,
devicetree, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Arınç ÜNAL, Landen Chao, DENG Qingfang, Sean Wang,
Daniel Golle, Joris Vaisvila
In-Reply-To: <20260613181845.111877-1-joey@tinyisr.com>
Add support for the MT7628 embedded switch.
The switch has 5 built-in 100Mbps user ports (ports 0-4) and one 1Gbps
port that is internally attached to the SoCs CPU MAC and serves as the
CPU port.
The switch hardware has a very limited 16 entry VLAN table. Configuring
VLANs is the only way to control switch forwarding. Currently 6 entries
are used by tag_8021q to isolate the ports. Double tag feature is
enabled to force the switch to append the VLAN tag even if the incoming
packet is already tagged, this simulates VLAN-unaware functionality and
simplifies the tagger implementation.
Signed-off-by: Joris Vaisvila <joey@tinyisr.com>
---
drivers/net/dsa/Kconfig | 8 +
drivers/net/dsa/Makefile | 1 +
drivers/net/dsa/mt7628.c | 649 +++++++++++++++++++++++++++++++++++++++
3 files changed, 658 insertions(+)
create mode 100644 drivers/net/dsa/mt7628.c
diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig
index 4ab567c5bbaf..daa1d3d4ba60 100644
--- a/drivers/net/dsa/Kconfig
+++ b/drivers/net/dsa/Kconfig
@@ -63,6 +63,14 @@ config NET_DSA_MT7530_MMIO
are directly mapped into the SoCs register space rather than being
accessible via MDIO.
+config NET_DSA_MT7628
+ tristate "MediaTek MT7628 Embedded Ethernet switch support"
+ select NET_DSA_TAG_MT7628
+ select MEDIATEK_FE_SOC_PHY
+ help
+ This enables support for the built-in Ethernet switch found
+ in the MT7628 SoC.
+
config NET_DSA_MV88E6060
tristate "Marvell 88E6060 ethernet switch chip support"
select NET_DSA_TAG_TRAILER
diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile
index d2975badffc0..6ceb78a755d7 100644
--- a/drivers/net/dsa/Makefile
+++ b/drivers/net/dsa/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_NET_DSA_KS8995) += ks8995.o
obj-$(CONFIG_NET_DSA_MT7530) += mt7530.o
obj-$(CONFIG_NET_DSA_MT7530_MDIO) += mt7530-mdio.o
obj-$(CONFIG_NET_DSA_MT7530_MMIO) += mt7530-mmio.o
+obj-$(CONFIG_NET_DSA_MT7628) += mt7628.o
obj-$(CONFIG_NET_DSA_MV88E6060) += mv88e6060.o
obj-$(CONFIG_NET_DSA_RZN1_A5PSW) += rzn1_a5psw.o
obj-$(CONFIG_NET_DSA_SMSC_LAN9303) += lan9303-core.o
diff --git a/drivers/net/dsa/mt7628.c b/drivers/net/dsa/mt7628.c
new file mode 100644
index 000000000000..cedf063ad749
--- /dev/null
+++ b/drivers/net/dsa/mt7628.c
@@ -0,0 +1,649 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Mediatek MT7628 Embedded Switch (ESW) DSA driver
+ * Copyright (C) 2026 Joris Vaisvila <joey@tinyisr.com>
+ *
+ * Portions derived from OpenWRT esw_rt3050 driver:
+ * Copyright (C) 2009-2015 John Crispin <blogic@openwrt.org>
+ * Copyright (C) 2009-2015 Felix Fietkau <nbd@nbd.name>
+ * Copyright (C) 2013-2015 Michael Lee <igvtee@gmail.com>
+ * Copyright (C) 2016 Vittorio Gambaletta <openwrt@vittgam.net>
+ */
+
+#include <linux/platform_device.h>
+#include <linux/etherdevice.h>
+#include <linux/netdevice.h>
+#include <linux/dsa/8021q.h>
+#include <linux/if_bridge.h>
+#include <linux/module.h>
+#include <linux/mdio.h>
+#include <linux/of.h>
+#include <linux/of_mdio.h>
+#include <linux/of_net.h>
+#include <linux/kernel.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <net/dsa.h>
+
+#define MT7628_ESW_REG_IMR 0x04
+#define MT7628_ESW_REG_FCT0 0x08
+#define MT7628_ESW_REG_PFC1 0x14
+#define MT7628_ESW_REG_PVIDC(port) (0x40 + 4 * ((port) / 2))
+#define MT7628_ESW_REG_VLANI(vlan) (0x50 + 4 * ((vlan) / 2))
+#define MT7628_ESW_REG_VMSC(vlan) (0x70 + 4 * ((vlan) / 4))
+#define MT7628_ESW_REG_VUB(vlan) (0x100 + 4 * ((vlan) / 4))
+#define MT7628_ESW_REG_SOCPC 0x8c
+#define MT7628_ESW_REG_POC0 0x90
+#define MT7628_ESW_REG_POC2 0x98
+#define MT7628_ESW_REG_SGC 0x9c
+#define MT7628_ESW_REG_PCR0 0xc0
+#define MT7628_ESW_REG_PCR1 0xc4
+#define MT7628_ESW_REG_FPA2 0xc8
+#define MT7628_ESW_REG_FCT2 0xcc
+#define MT7628_ESW_REG_SGC2 0xe4
+
+#define MT7628_ESW_FCT0_DROP_SET_TH GENMASK(7, 0)
+#define MT7628_ESW_FCT0_DROP_RLS_TH GENMASK(15, 8)
+#define MT7628_ESW_FCT0_FC_SET_TH GENMASK(23, 16)
+#define MT7628_ESW_FCT0_FC_RLS_TH GENMASK(31, 24)
+
+#define MT7628_ESW_PFC1_EN_VLAN GENMASK(22, 16)
+
+#define MT7628_ESW_PVID_S 12
+#define MT7628_ESW_PVID_M GENMASK(11, 0)
+#define MT7628_ESW_PVID_SHIFT(port) \
+ (MT7628_ESW_PVID_S * ((port) % 2))
+#define MT7628_ESW_PVID_MASK(port) \
+ (MT7628_ESW_PVID_M << MT7628_ESW_PVID_SHIFT(port))
+#define MT7628_ESW_PVID_PREP(port, pvid) \
+ (((pvid) & MT7628_ESW_PVID_M) << MT7628_ESW_PVID_SHIFT(port))
+
+#define MT7628_ESW_VID_S 12
+#define MT7628_ESW_VID_M GENMASK(11, 0)
+#define MT7628_ESW_VID_SHIFT(vlan) \
+ (MT7628_ESW_VID_S * ((vlan) % 2))
+#define MT7628_ESW_VID_MASK(vlan) \
+ (MT7628_ESW_VID_M << MT7628_ESW_VID_SHIFT(vlan))
+#define MT7628_ESW_VID_PREP(vlan, vid) \
+ (((vid) & MT7628_ESW_VID_M) << MT7628_ESW_VID_SHIFT(vlan))
+
+#define MT7628_ESW_VMSC_S 8
+#define MT7628_ESW_VMSC_M GENMASK(7, 0)
+#define MT7628_ESW_VMSC_SHIFT(vlan) \
+ (MT7628_ESW_VMSC_S * ((vlan) % 4))
+#define MT7628_ESW_VMSC_MASK(vlan) \
+ (MT7628_ESW_VMSC_M << MT7628_ESW_VMSC_SHIFT(vlan))
+#define MT7628_ESW_VMSC_PREP(vlan, vmsc) \
+ (((vmsc) & MT7628_ESW_VMSC_M) << MT7628_ESW_VMSC_SHIFT(vlan))
+
+#define MT7628_ESW_VUB_S 7
+#define MT7628_ESW_VUB_M GENMASK(6, 0)
+#define MT7628_ESW_VUB_SHIFT(vlan) \
+ (MT7628_ESW_VUB_S * ((vlan) % 4))
+#define MT7628_ESW_VUB_MASK(vlan) \
+ (MT7628_ESW_VUB_M << MT7628_ESW_VUB_SHIFT(vlan))
+#define MT7628_ESW_VUB_PREP(vlan, vub) \
+ (((vub) & MT7628_ESW_VUB_M) << MT7628_ESW_VUB_SHIFT(vlan))
+
+#define MT7628_ESW_SOCPC_CRC_PADDING BIT(25)
+#define MT7628_ESW_SOCPC_DISBC2CPU GENMASK(22, 16)
+#define MT7628_ESW_SOCPC_DISMC2CPU GENMASK(14, 8)
+#define MT7628_ESW_SOCPC_DISUN2CPU GENMASK(6, 0)
+
+#define MT7628_ESW_POC0_PORT_DISABLE GENMASK(29, 23)
+
+#define MT7628_ESW_POC2_PER_VLAN_UNTAG_EN BIT(15)
+
+#define MT7628_ESW_SGC_AGING_INTERVAL GENMASK(3, 0)
+#define MT7628_ESW_BC_STORM_PROT GENMASK(5, 4)
+#define MT7628_ESW_PKT_MAX_LEN GENMASK(7, 6)
+#define MT7628_ESW_DIS_PKT_ABORT BIT(8)
+#define MT7628_ESW_ADDRESS_HASH_ALG GENMASK(10, 9)
+#define MT7628_ESW_DISABLE_TX_BACKOFF BIT(11)
+#define MT7628_ESW_BP_JAM_CNT GENMASK(15, 12)
+#define MT7628_ESW_DISMIIPORT_WASTX GENMASK(17, 16)
+#define MT7628_ESW_BP_MODE GENMASK(19, 18)
+#define MT7628_ESW_BISH_DIS BIT(20)
+#define MT7628_ESW_BISH_TH GENMASK(22, 21)
+#define MT7628_ESW_LED_FLASH_TIME GENMASK(24, 23)
+#define MT7628_ESW_RMC_RULE GENMASK(26, 25)
+#define MT7628_ESW_IP_MULT_RULE GENMASK(28, 27)
+#define MT7628_ESW_LEN_ERR_CHK BIT(29)
+#define MT7628_ESW_BKOFF_ALG BIT(30)
+
+#define MT7628_ESW_PCR0_WT_NWAY_DATA GENMASK(31, 16)
+#define MT7628_ESW_PCR0_RD_PHY_CMD BIT(14)
+#define MT7628_ESW_PCR0_WT_PHY_CMD BIT(13)
+#define MT7628_ESW_PCR0_CPU_PHY_REG GENMASK(12, 8)
+#define MT7628_ESW_PCR0_CPU_PHY_ADDR GENMASK(4, 0)
+
+#define MT7628_ESW_PCR1_RD_DATA GENMASK(31, 16)
+#define MT7628_ESW_PCR1_RD_DONE BIT(1)
+#define MT7628_ESW_PCR1_WT_DONE BIT(0)
+
+#define MT7628_ESW_FPA2_AP_EN BIT(29)
+#define MT7628_ESW_FPA2_EXT_PHY_ADDR_BASE GENMASK(28, 24)
+#define MT7628_ESW_FPA2_FORCE_RGMII_LINK1 BIT(13)
+#define MT7628_ESW_FPA2_FORCE_RGMII_EN1 BIT(11)
+
+#define MT7628_ESW_FCT2_MUST_DROP_RLS_TH GENMASK(17, 13)
+#define MT7628_ESW_FCT2_MUST_DROP_SET_TH GENMASK(12, 8)
+#define MT7628_ESW_FCT2_MC_PER_PORT_TH GENMASK(5, 0)
+
+#define MT7628_ESW_SGC2_SPECIAL_TAG_EN BIT(23)
+#define MT7628_ESW_SGC2_TX_CPU_TPID_BIT_MAP GENMASK(22, 16)
+#define MT7628_ESW_SGC2_DOUBLE_TAG_EN GENMASK(6, 0)
+
+#define MT7628_ESW_PORTS_NOCPU GENMASK(5, 0)
+#define MT7628_ESW_PORTS_CPU BIT(6)
+#define MT7628_ESW_PORTS_ALL GENMASK(6, 0)
+
+#define MT7628_ESW_NUM_PORTS 7
+#define MT7628_NUM_VLANS 16
+
+static const struct regmap_config mt7628_esw_regmap_cfg = {
+ .name = "mt7628-esw",
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .fast_io = true,
+ .reg_format_endian = REGMAP_ENDIAN_LITTLE,
+ .val_format_endian = REGMAP_ENDIAN_LITTLE,
+};
+
+struct mt7628_vlan {
+ bool active;
+ u8 members;
+ u8 untag;
+ u16 vid;
+};
+
+struct mt7628_esw {
+ void __iomem *base;
+ struct reset_control *rst_ephy;
+ struct reset_control *rst_esw;
+ struct regmap *regmap;
+ struct dsa_switch *ds;
+ u16 tag_8021q_pvid[MT7628_ESW_NUM_PORTS];
+ struct mt7628_vlan vlans[MT7628_NUM_VLANS];
+ struct device *dev;
+};
+
+static int mt7628_mii_read(struct mii_bus *bus, int port, int regnum)
+{
+ struct mt7628_esw *esw = bus->priv;
+ int ret;
+ u32 val;
+
+ /*
+ * RD_DONE bit is read to clear. Read PCR1 once to acknowledge any
+ * stale completion indicator before starting a new transaction.
+ */
+ ret = regmap_read(esw->regmap, MT7628_ESW_REG_PCR1, &val);
+ if (ret)
+ goto out;
+
+ ret = regmap_write(esw->regmap, MT7628_ESW_REG_PCR0,
+ FIELD_PREP(MT7628_ESW_PCR0_CPU_PHY_REG,
+ regnum) |
+ FIELD_PREP(MT7628_ESW_PCR0_CPU_PHY_ADDR,
+ port) | MT7628_ESW_PCR0_RD_PHY_CMD);
+ if (ret)
+ goto out;
+
+ ret = regmap_read_poll_timeout(esw->regmap, MT7628_ESW_REG_PCR1, val,
+ (val & MT7628_ESW_PCR1_RD_DONE), 10,
+ 5000);
+ if (ret)
+ goto out;
+
+ return FIELD_GET(MT7628_ESW_PCR1_RD_DATA, val);
+
+out:
+ dev_err(&bus->dev, "read failed. MDIO timeout?\n");
+ return ret;
+}
+
+static int mt7628_mii_write(struct mii_bus *bus, int port, int regnum, u16 dat)
+{
+ struct mt7628_esw *esw = bus->priv;
+ u32 val;
+ int ret;
+
+ /*
+ * WT_DONE bit is read to clear. Read PCR1 once to acknowledge any
+ * stale completion indicator before starting a new transaction.
+ */
+ ret = regmap_read(esw->regmap, MT7628_ESW_REG_PCR1, &val);
+ if (ret)
+ goto out;
+
+ ret = regmap_write(esw->regmap, MT7628_ESW_REG_PCR0,
+ FIELD_PREP(MT7628_ESW_PCR0_WT_NWAY_DATA, dat) |
+ FIELD_PREP(MT7628_ESW_PCR0_CPU_PHY_REG,
+ regnum) |
+ FIELD_PREP(MT7628_ESW_PCR0_CPU_PHY_ADDR,
+ port) | MT7628_ESW_PCR0_WT_PHY_CMD);
+ if (ret)
+ goto out;
+
+ ret = regmap_read_poll_timeout(esw->regmap, MT7628_ESW_REG_PCR1, val,
+ (val & MT7628_ESW_PCR1_WT_DONE), 10,
+ 5000);
+ if (ret)
+ goto out;
+
+ return 0;
+
+out:
+ dev_err(&bus->dev, "write failed. MDIO timeout?\n");
+ return ret;
+}
+
+static int mt7628_setup_internal_mdio(struct dsa_switch *ds)
+{
+ struct mt7628_esw *esw = ds->priv;
+ struct device *dev = ds->dev;
+ struct mii_bus *bus;
+
+ bus = devm_mdiobus_alloc(dev);
+ if (!bus)
+ return -ENOMEM;
+
+ bus->name = "MT7628 internal MDIO bus";
+ snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(dev));
+ bus->priv = esw;
+ bus->read = mt7628_mii_read;
+ bus->write = mt7628_mii_write;
+ bus->parent = dev;
+
+ ds->user_mii_bus = bus;
+ bus->phy_mask = ~ds->phys_mii_mask;
+
+ return devm_mdiobus_register(dev, bus);
+}
+
+static void mt7628_switch_init(struct dsa_switch *ds)
+{
+ struct mt7628_esw *esw = ds->priv;
+
+ regmap_write(esw->regmap, MT7628_ESW_REG_FCT0,
+ FIELD_PREP(MT7628_ESW_FCT0_DROP_SET_TH, 0x50) |
+ FIELD_PREP(MT7628_ESW_FCT0_DROP_RLS_TH, 0x78) |
+ FIELD_PREP(MT7628_ESW_FCT0_FC_SET_TH, 0xa0) |
+ FIELD_PREP(MT7628_ESW_FCT0_FC_RLS_TH, 0xc8));
+
+ regmap_write(esw->regmap, MT7628_ESW_REG_FCT2,
+ FIELD_PREP(MT7628_ESW_FCT2_MC_PER_PORT_TH, 0xc) |
+ FIELD_PREP(MT7628_ESW_FCT2_MUST_DROP_SET_TH, 0x10) |
+ FIELD_PREP(MT7628_ESW_FCT2_MUST_DROP_RLS_TH, 0x12));
+
+ /*
+ * general switch configuration:
+ * 300s aging interval
+ * broadcast storm prevention disabled
+ * max packet length 1536 bytes
+ * disable collision 16 packet abort and late collision abort
+ * use xor48 for address hashing
+ * disable tx backoff
+ * 10 packet back pressure jam
+ * disable was_transmit
+ * jam until BP condition released
+ * 30ms LED flash
+ * rmc tb fault to all ports
+ * unmatched IGMP as broadcast
+ */
+ regmap_write(esw->regmap, MT7628_ESW_REG_SGC,
+ FIELD_PREP(MT7628_ESW_SGC_AGING_INTERVAL, 1) |
+ FIELD_PREP(MT7628_ESW_BC_STORM_PROT, 0) |
+ FIELD_PREP(MT7628_ESW_PKT_MAX_LEN, 0) |
+ MT7628_ESW_DIS_PKT_ABORT |
+ FIELD_PREP(MT7628_ESW_ADDRESS_HASH_ALG, 1) |
+ MT7628_ESW_DISABLE_TX_BACKOFF |
+ FIELD_PREP(MT7628_ESW_BP_JAM_CNT, 10) |
+ FIELD_PREP(MT7628_ESW_DISMIIPORT_WASTX, 0) |
+ FIELD_PREP(MT7628_ESW_BP_MODE, 0b10) |
+ FIELD_PREP(MT7628_ESW_LED_FLASH_TIME, 0) |
+ FIELD_PREP(MT7628_ESW_RMC_RULE, 0) |
+ FIELD_PREP(MT7628_ESW_IP_MULT_RULE, 0));
+
+ regmap_write(esw->regmap, MT7628_ESW_REG_SOCPC,
+ MT7628_ESW_SOCPC_CRC_PADDING |
+ FIELD_PREP(MT7628_ESW_SOCPC_DISUN2CPU,
+ MT7628_ESW_PORTS_CPU) |
+ FIELD_PREP(MT7628_ESW_SOCPC_DISMC2CPU,
+ MT7628_ESW_PORTS_CPU) |
+ FIELD_PREP(MT7628_ESW_SOCPC_DISBC2CPU,
+ MT7628_ESW_PORTS_CPU));
+
+ regmap_set_bits(esw->regmap, MT7628_ESW_REG_FPA2,
+ MT7628_ESW_FPA2_FORCE_RGMII_EN1 |
+ MT7628_ESW_FPA2_FORCE_RGMII_LINK1 |
+ MT7628_ESW_FPA2_AP_EN);
+
+ regmap_update_bits(esw->regmap, MT7628_ESW_REG_FPA2,
+ MT7628_ESW_FPA2_EXT_PHY_ADDR_BASE,
+ FIELD_PREP(MT7628_ESW_FPA2_EXT_PHY_ADDR_BASE, 31));
+
+ /* disable all interrupts */
+ regmap_write(esw->regmap, MT7628_ESW_REG_IMR, 0);
+
+ /* enable MT7628 DSA tag on CPU port */
+ regmap_write(esw->regmap, MT7628_ESW_REG_SGC2,
+ MT7628_ESW_SGC2_SPECIAL_TAG_EN |
+ FIELD_PREP(MT7628_ESW_SGC2_TX_CPU_TPID_BIT_MAP,
+ MT7628_ESW_PORTS_CPU));
+
+ /*
+ * Double tag feature allows switch to always append the port PVID VLAN tag
+ * regardless of if the incoming packet already has a VLAN tag.
+ * This is enabled to simulate VLAN unawareness.
+ */
+ regmap_set_bits(esw->regmap, MT7628_ESW_REG_SGC2,
+ FIELD_PREP(MT7628_ESW_SGC2_DOUBLE_TAG_EN,
+ MT7628_ESW_PORTS_NOCPU));
+
+ regmap_set_bits(esw->regmap, MT7628_ESW_REG_POC2,
+ MT7628_ESW_POC2_PER_VLAN_UNTAG_EN);
+
+ regmap_update_bits(esw->regmap, MT7628_ESW_REG_PFC1,
+ MT7628_ESW_PFC1_EN_VLAN,
+ FIELD_PREP(MT7628_ESW_PFC1_EN_VLAN,
+ MT7628_ESW_PORTS_ALL));
+}
+
+static void mt7628_esw_set_pvid(struct mt7628_esw *esw, unsigned int port,
+ unsigned int pvid)
+{
+ regmap_update_bits(esw->regmap, MT7628_ESW_REG_PVIDC(port),
+ MT7628_ESW_PVID_MASK(port),
+ MT7628_ESW_PVID_PREP(port, pvid));
+}
+
+static void mt7628_esw_set_vlan_id(struct mt7628_esw *esw, unsigned int vlan,
+ unsigned int vid)
+{
+ regmap_update_bits(esw->regmap, MT7628_ESW_REG_VLANI(vlan),
+ MT7628_ESW_VID_MASK(vlan),
+ MT7628_ESW_VID_PREP(vlan, vid));
+}
+
+static void mt7628_esw_set_vmsc(struct mt7628_esw *esw, unsigned int vlan,
+ unsigned int msc)
+{
+ regmap_update_bits(esw->regmap, MT7628_ESW_REG_VMSC(vlan),
+ MT7628_ESW_VMSC_MASK(vlan),
+ MT7628_ESW_VMSC_PREP(vlan, msc));
+}
+
+static void mt7628_esw_set_vub(struct mt7628_esw *esw, unsigned int vlan,
+ unsigned int vub)
+{
+ regmap_update_bits(esw->regmap, MT7628_ESW_REG_VUB(vlan),
+ MT7628_ESW_VUB_MASK(vlan),
+ MT7628_ESW_VUB_PREP(vlan, vub));
+}
+
+static void mt7628_vlan_sync(struct dsa_switch *ds)
+{
+ struct mt7628_esw *esw = ds->priv;
+ int i;
+
+ for (i = 0; i < MT7628_NUM_VLANS; i++) {
+ struct mt7628_vlan *vlan = &esw->vlans[i];
+
+ mt7628_esw_set_vmsc(esw, i, vlan->members);
+ mt7628_esw_set_vlan_id(esw, i, vlan->vid);
+ mt7628_esw_set_vub(esw, i, vlan->untag);
+ }
+
+ for (i = 0; i < ds->num_ports; i++)
+ mt7628_esw_set_pvid(esw, i, esw->tag_8021q_pvid[i]);
+}
+
+static int mt7628_setup(struct dsa_switch *ds)
+{
+ struct mt7628_esw *esw = ds->priv;
+ int ret;
+
+ ret = reset_control_reset(esw->rst_esw);
+ if (ret)
+ return ret;
+ usleep_range(1000, 2000);
+
+ ret = reset_control_reset(esw->rst_ephy);
+ if (ret)
+ return ret;
+ usleep_range(1000, 2000);
+ /*
+ * all MMIO reads hang if esw is not out of reset
+ * ephy needs extra time to get out of reset or it ends up misconfigured
+ */
+
+ mt7628_switch_init(ds);
+
+ ret = mt7628_setup_internal_mdio(ds);
+ if (ret)
+ return ret;
+
+ rtnl_lock();
+ ret = dsa_tag_8021q_register(ds, htons(ETH_P_8021Q));
+ rtnl_unlock();
+
+ return ret;
+}
+
+static int mt7628_port_enable(struct dsa_switch *ds, int port,
+ struct phy_device *phy)
+{
+ struct mt7628_esw *esw = ds->priv;
+
+ regmap_clear_bits(esw->regmap, MT7628_ESW_REG_POC0,
+ FIELD_PREP(MT7628_ESW_POC0_PORT_DISABLE, BIT(port)));
+ return 0;
+}
+
+static void mt7628_port_disable(struct dsa_switch *ds, int port)
+{
+ struct mt7628_esw *esw = ds->priv;
+
+ regmap_set_bits(esw->regmap, MT7628_ESW_REG_POC0,
+ FIELD_PREP(MT7628_ESW_POC0_PORT_DISABLE, BIT(port)));
+}
+
+static enum dsa_tag_protocol
+mt7628_get_tag_proto(struct dsa_switch *ds, int port, enum dsa_tag_protocol mp)
+{
+ return DSA_TAG_PROTO_MT7628;
+}
+
+static void mt7628_phylink_get_caps(struct dsa_switch *ds, int port,
+ struct phylink_config *config)
+{
+ switch (port) {
+ case 6:
+ config->mac_capabilities |= MAC_1000;
+ fallthrough;
+ case 0 ... 4:
+ config->mac_capabilities |= MAC_100 | MAC_10;
+ __set_bit(PHY_INTERFACE_MODE_INTERNAL,
+ config->supported_interfaces);
+ break;
+ default:
+ break; /* port 5 does not exist on MT7628 */
+ }
+}
+
+static int mt7628_dsa_8021q_vlan_add(struct dsa_switch *ds, int port,
+ u16 vid, u16 flags)
+{
+ struct mt7628_esw *esw = ds->priv;
+ struct mt7628_vlan *vlan = NULL;
+ int i;
+
+ for (i = 0; i < MT7628_NUM_VLANS; i++) {
+ struct mt7628_vlan *check_vlan = &esw->vlans[i];
+
+ if (!check_vlan->active && !vlan)
+ vlan = check_vlan;
+
+ if (check_vlan->active && check_vlan->vid == vid) {
+ vlan = check_vlan;
+ break;
+ }
+ }
+
+ if (!vlan)
+ return -ENOSPC;
+
+ vlan->vid = vid;
+ vlan->active = true;
+ vlan->members |= BIT(port);
+
+ if (flags & BRIDGE_VLAN_INFO_PVID)
+ esw->tag_8021q_pvid[port] = vid;
+
+ if (flags & BRIDGE_VLAN_INFO_UNTAGGED)
+ vlan->untag |= BIT(port);
+
+ mt7628_vlan_sync(ds);
+ return 0;
+}
+
+static int mt7628_dsa_8021q_vlan_del(struct dsa_switch *ds, int port, u16 vid)
+{
+ struct mt7628_esw *esw = ds->priv;
+ struct mt7628_vlan *vlan = NULL;
+ int i;
+
+ for (i = 0; i < MT7628_NUM_VLANS; i++) {
+ struct mt7628_vlan *check_vlan = &esw->vlans[i];
+
+ if (!check_vlan->active || check_vlan->vid != vid)
+ continue;
+ vlan = check_vlan;
+ break;
+ }
+ if (!vlan)
+ return -ENOENT;
+
+ if (esw->tag_8021q_pvid[port] == vid)
+ esw->tag_8021q_pvid[port] = 0;
+
+ vlan->members &= ~BIT(port);
+ vlan->untag &= ~BIT(port);
+
+ if (!vlan->members) {
+ vlan->active = false;
+ vlan->vid = 0;
+ }
+
+ mt7628_vlan_sync(ds);
+ return 0;
+}
+
+static void mt7628_teardown(struct dsa_switch *ds)
+{
+ rtnl_lock();
+ dsa_tag_8021q_unregister(ds);
+ rtnl_unlock();
+}
+
+static const struct dsa_switch_ops mt7628_switch_ops = {
+ .get_tag_protocol = mt7628_get_tag_proto,
+ .setup = mt7628_setup,
+ .teardown = mt7628_teardown,
+ .port_enable = mt7628_port_enable,
+ .port_disable = mt7628_port_disable,
+ .phylink_get_caps = mt7628_phylink_get_caps,
+ .tag_8021q_vlan_add = mt7628_dsa_8021q_vlan_add,
+ .tag_8021q_vlan_del = mt7628_dsa_8021q_vlan_del,
+};
+
+static int mt7628_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mt7628_esw *esw;
+ struct dsa_switch *ds;
+
+ ds = devm_kzalloc(&pdev->dev, sizeof(*ds), GFP_KERNEL);
+ if (!ds)
+ return -ENOMEM;
+
+ esw = devm_kzalloc(&pdev->dev, sizeof(*esw), GFP_KERNEL);
+ if (!esw)
+ return -ENOMEM;
+
+ esw->base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(esw->base))
+ return PTR_ERR(esw->base);
+
+ esw->regmap = devm_regmap_init_mmio(&pdev->dev, esw->base,
+ &mt7628_esw_regmap_cfg);
+ if (IS_ERR(esw->regmap))
+ return PTR_ERR(esw->regmap);
+
+ esw->rst_ephy = devm_reset_control_get_exclusive(&pdev->dev, "ephy");
+ if (IS_ERR(esw->rst_ephy))
+ return dev_err_probe(dev, PTR_ERR(esw->rst_ephy),
+ "failed to get EPHY reset\n");
+
+ esw->rst_esw = devm_reset_control_get_exclusive(&pdev->dev, "esw");
+ if (IS_ERR(esw->rst_esw))
+ return dev_err_probe(dev, PTR_ERR(esw->rst_esw),
+ "failed to get ESW reset\n");
+
+ ds->dev = dev;
+ ds->num_ports = MT7628_ESW_NUM_PORTS;
+ ds->ops = &mt7628_switch_ops;
+ ds->priv = esw;
+ esw->ds = ds;
+ esw->dev = dev;
+ dev_set_drvdata(dev, esw);
+
+ return dsa_register_switch(ds);
+}
+
+static void mt7628_remove(struct platform_device *pdev)
+{
+ struct mt7628_esw *esw = platform_get_drvdata(pdev);
+
+ if (!esw)
+ return;
+
+ dsa_unregister_switch(esw->ds);
+}
+
+static void mt7628_shutdown(struct platform_device *pdev)
+{
+ struct mt7628_esw *esw = platform_get_drvdata(pdev);
+
+ if (!esw)
+ return;
+
+ dsa_switch_shutdown(esw->ds);
+ dev_set_drvdata(&pdev->dev, NULL);
+}
+
+static const struct of_device_id mt7628_of_match[] = {
+ { .compatible = "mediatek,mt7628-esw" },
+ {}
+};
+
+MODULE_DEVICE_TABLE(of, mt7628_of_match);
+
+static struct platform_driver mt7628_driver = {
+ .driver = {
+ .name = "mt7628-esw",
+ .of_match_table = mt7628_of_match,
+ },
+ .probe = mt7628_probe,
+ .remove = mt7628_remove,
+ .shutdown = mt7628_shutdown,
+};
+
+module_platform_driver(mt7628_driver);
+
+MODULE_AUTHOR("Joris Vaisvila <joey@tinyisr.com>");
+MODULE_DESCRIPTION("Driver for Mediatek MT7628 embedded switch");
+MODULE_LICENSE("GPL");
--
2.54.0
^ permalink raw reply related
* [PATCH net-next v5 3/4] net: dsa: initial MT7628 tagging driver
From: Joris Vaisvila @ 2026-06-13 18:18 UTC (permalink / raw)
To: netdev
Cc: horms, pabeni, kuba, edumazet, davem, olteanv, Andrew Lunn,
devicetree, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Arınç ÜNAL, Landen Chao, DENG Qingfang, Sean Wang,
Daniel Golle, Joris Vaisvila
In-Reply-To: <20260613181845.111877-1-joey@tinyisr.com>
Add support for the MT7628 embedded switch's tag.
The MT7628 tag is merged with the VLAN TPID field when a VLAN is
appended by the switch hardware. It is not installed if the VLAN tag is
already there on ingress. Due to this hardware quirk the tag cannot be
trusted for port 0 if we don't know that the VLAN was added by the
hardware. As a workaround for this the switch is configured to always
append the port PVID tag even if the incoming packet is already tagged.
The tagging driver can then trust that the tag is always accurate and
the whole VLAN tag can be removed on ingress as it's only metadata for
the tagger.
On egress the MT7628 tag allows precise TX, but the correct VLAN tag
from tag_8021q is still appended or the switch will not forward the
packet.
Signed-off-by: Joris Vaisvila <joey@tinyisr.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
---
include/net/dsa.h | 2 +
net/dsa/Kconfig | 6 +++
net/dsa/Makefile | 1 +
net/dsa/tag_mt7628.c | 89 ++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 98 insertions(+)
create mode 100644 net/dsa/tag_mt7628.c
diff --git a/include/net/dsa.h b/include/net/dsa.h
index 8c16ef23cc10..913d1f71e3db 100644
--- a/include/net/dsa.h
+++ b/include/net/dsa.h
@@ -59,6 +59,7 @@ struct tc_action;
#define DSA_TAG_PROTO_MXL_GSW1XX_VALUE 31
#define DSA_TAG_PROTO_MXL862_VALUE 32
#define DSA_TAG_PROTO_NETC_VALUE 33
+#define DSA_TAG_PROTO_MT7628_VALUE 34
enum dsa_tag_protocol {
DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE,
@@ -95,6 +96,7 @@ enum dsa_tag_protocol {
DSA_TAG_PROTO_MXL_GSW1XX = DSA_TAG_PROTO_MXL_GSW1XX_VALUE,
DSA_TAG_PROTO_MXL862 = DSA_TAG_PROTO_MXL862_VALUE,
DSA_TAG_PROTO_NETC = DSA_TAG_PROTO_NETC_VALUE,
+ DSA_TAG_PROTO_MT7628 = DSA_TAG_PROTO_MT7628_VALUE,
};
struct dsa_switch;
diff --git a/net/dsa/Kconfig b/net/dsa/Kconfig
index d5e725b90d78..23b4b74004ed 100644
--- a/net/dsa/Kconfig
+++ b/net/dsa/Kconfig
@@ -98,6 +98,12 @@ config NET_DSA_TAG_EDSA
Say Y or M if you want to enable support for tagging frames for the
Marvell switches which use EtherType DSA headers.
+config NET_DSA_TAG_MT7628
+ tristate "Tag driver for the MT7628 embedded switch"
+ help
+ Say Y or M if you want to enable support for tagging frames for the
+ switch embedded in the MT7628 SoC.
+
config NET_DSA_TAG_MTK
tristate "Tag driver for Mediatek switches"
help
diff --git a/net/dsa/Makefile b/net/dsa/Makefile
index b8c2667cd14a..d15bcf5c68f0 100644
--- a/net/dsa/Makefile
+++ b/net/dsa/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_NET_DSA_TAG_GSWIP) += tag_gswip.o
obj-$(CONFIG_NET_DSA_TAG_HELLCREEK) += tag_hellcreek.o
obj-$(CONFIG_NET_DSA_TAG_KSZ) += tag_ksz.o
obj-$(CONFIG_NET_DSA_TAG_LAN9303) += tag_lan9303.o
+obj-$(CONFIG_NET_DSA_TAG_MT7628) += tag_mt7628.o
obj-$(CONFIG_NET_DSA_TAG_MTK) += tag_mtk.o
obj-$(CONFIG_NET_DSA_TAG_MXL_862XX) += tag_mxl862xx.o
obj-$(CONFIG_NET_DSA_TAG_MXL_GSW1XX) += tag_mxl-gsw1xx.o
diff --git a/net/dsa/tag_mt7628.c b/net/dsa/tag_mt7628.c
new file mode 100644
index 000000000000..f0e346595f30
--- /dev/null
+++ b/net/dsa/tag_mt7628.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2026, Joris Vaisvila <joey@tinyisr.com>
+ * MT7628 switch tag support
+ */
+
+#include <linux/etherdevice.h>
+#include <linux/dsa/8021q.h>
+#include <net/dsa.h>
+
+#include "tag.h"
+
+/*
+ * The MT7628 tag is encoded in the VLAN TPID field.
+ * On TX the lower 6 bits encode the destination port bitmask.
+ * On RX the lower 3 bits encode the source port number.
+ *
+ * The switch hardware will not modify the TPID of an incoming packet if it is
+ * already VLAN tagged. To work around this the switch is configured to always
+ * append a tag_8021q standalone VLAN tag for each port. That means we can
+ * safely strip the outer VLAN tag after parsing it.
+ *
+ * A VLAN tag is constructed on egress to target the standalone VLAN and
+ * destination port.
+ */
+
+#define MT7628_TAG_NAME "mt7628"
+
+#define MT7628_TAG_TX_PORT GENMASK(5, 0)
+#define MT7628_TAG_RX_PORT GENMASK(2, 0)
+#define MT7628_TAG_LEN 4
+
+static struct sk_buff *mt7628_tag_xmit(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ struct dsa_port *dp;
+ u16 xmit_vlan;
+ __be16 *tag;
+
+ dp = dsa_user_to_port(dev);
+ xmit_vlan = dsa_tag_8021q_standalone_vid(dp);
+
+ skb_push(skb, MT7628_TAG_LEN);
+ dsa_alloc_etype_header(skb, MT7628_TAG_LEN);
+
+ tag = dsa_etype_header_pos_tx(skb);
+
+ tag[0] = htons(ETH_P_8021Q |
+ FIELD_PREP(MT7628_TAG_TX_PORT,
+ dsa_xmit_port_mask(skb, dev)));
+ tag[1] = htons(xmit_vlan);
+
+ return skb;
+}
+
+static struct sk_buff *mt7628_tag_rcv(struct sk_buff *skb,
+ struct net_device *dev)
+{
+ __be16 *phdr;
+
+ if (unlikely(!pskb_may_pull(skb, MT7628_TAG_LEN)))
+ return NULL;
+
+ phdr = dsa_etype_header_pos_rx(skb);
+ skb->dev =
+ dsa_conduit_find_user(dev, 0,
+ FIELD_GET(MT7628_TAG_RX_PORT, ntohs(*phdr)));
+ if (!skb->dev)
+ return NULL;
+
+ skb_pull_rcsum(skb, MT7628_TAG_LEN);
+ dsa_strip_etype_header(skb, MT7628_TAG_LEN);
+ dsa_default_offload_fwd_mark(skb);
+ return skb;
+}
+
+static const struct dsa_device_ops mt7628_tag_ops = {
+ .name = MT7628_TAG_NAME,
+ .proto = DSA_TAG_PROTO_MT7628,
+ .xmit = mt7628_tag_xmit,
+ .rcv = mt7628_tag_rcv,
+ .needed_headroom = MT7628_TAG_LEN,
+};
+
+module_dsa_tag_driver(mt7628_tag_ops);
+
+MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_MT7628, MT7628_TAG_NAME);
+MODULE_DESCRIPTION("DSA tag driver for MT7628 switch");
+MODULE_LICENSE("GPL");
--
2.54.0
^ permalink raw reply related
* [PATCH net-next v5 2/4] net: phy: mediatek: add phy driver for MT7628 built-in Fast Ethernet PHYs
From: Joris Vaisvila @ 2026-06-13 18:18 UTC (permalink / raw)
To: netdev
Cc: horms, pabeni, kuba, edumazet, davem, olteanv, Andrew Lunn,
devicetree, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Arınç ÜNAL, Landen Chao, DENG Qingfang, Sean Wang,
Daniel Golle, Joris Vaisvila
In-Reply-To: <20260613181845.111877-1-joey@tinyisr.com>
The Fast Ethernet PHYs present in the MT7628 SoCs require an
undocumented bit to be set before they can establish 100mbps links.
This commit adds the Kconfig option MEDIATEK_FE_SOC_PHY and the
corresponding driver mtk-fe-soc.c.
Signed-off-by: Joris Vaisvila <joey@tinyisr.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/phy/mediatek/Kconfig | 10 +++++-
drivers/net/phy/mediatek/Makefile | 1 +
drivers/net/phy/mediatek/mtk-fe-soc.c | 50 +++++++++++++++++++++++++++
3 files changed, 60 insertions(+), 1 deletion(-)
create mode 100644 drivers/net/phy/mediatek/mtk-fe-soc.c
diff --git a/drivers/net/phy/mediatek/Kconfig b/drivers/net/phy/mediatek/Kconfig
index bb7dc876271e..b6a51f38c358 100644
--- a/drivers/net/phy/mediatek/Kconfig
+++ b/drivers/net/phy/mediatek/Kconfig
@@ -21,8 +21,16 @@ config MEDIATEK_GE_PHY
common operations with MediaTek SoC built-in Gigabit
Ethernet PHYs.
+config MEDIATEK_FE_SOC_PHY
+ tristate "MediaTek SoC Fast Ethernet PHYs"
+ help
+ Support for MediaTek MT7628 built-in Fast Ethernet PHYs.
+ This driver only sets an initialization bit required for the PHY
+ to establish 100 Mbps links. All other PHY operations are handled
+ by the kernel's generic PHY code.
+
config MEDIATEK_GE_SOC_PHY
- tristate "MediaTek SoC Ethernet PHYs"
+ tristate "MediaTek SoC Gigabit Ethernet PHYs"
depends on ARM64 || COMPILE_TEST
depends on ARCH_AIROHA || (ARCH_MEDIATEK && NVMEM_MTK_EFUSE) || \
COMPILE_TEST
diff --git a/drivers/net/phy/mediatek/Makefile b/drivers/net/phy/mediatek/Makefile
index ac57ecc799fc..6f9cacf7f906 100644
--- a/drivers/net/phy/mediatek/Makefile
+++ b/drivers/net/phy/mediatek/Makefile
@@ -1,5 +1,6 @@
# SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_MEDIATEK_2P5GE_PHY) += mtk-2p5ge.o
+obj-$(CONFIG_MEDIATEK_FE_SOC_PHY) += mtk-fe-soc.o
obj-$(CONFIG_MEDIATEK_GE_PHY) += mtk-ge.o
obj-$(CONFIG_MEDIATEK_GE_SOC_PHY) += mtk-ge-soc.o
obj-$(CONFIG_MTK_NET_PHYLIB) += mtk-phy-lib.o
diff --git a/drivers/net/phy/mediatek/mtk-fe-soc.c b/drivers/net/phy/mediatek/mtk-fe-soc.c
new file mode 100644
index 000000000000..9eb4960bcaad
--- /dev/null
+++ b/drivers/net/phy/mediatek/mtk-fe-soc.c
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for MT7628 Embedded Switch internal Fast Ethernet PHYs
+ */
+#include <linux/module.h>
+#include <linux/phy.h>
+
+#define MTK_FPHY_ID_MT7628 0x03a29410
+#define MTK_EXT_PAGE_ACCESS 0x1f
+
+static int mt7628_phy_read_page(struct phy_device *phydev)
+{
+ return __phy_read(phydev, MTK_EXT_PAGE_ACCESS);
+}
+
+static int mt7628_phy_write_page(struct phy_device *phydev, int page)
+{
+ return __phy_write(phydev, MTK_EXT_PAGE_ACCESS, page);
+}
+
+static int mt7628_phy_config_init(struct phy_device *phydev)
+{
+ /*
+ * This undocumented bit is required for the PHYs to be able to
+ * establish 100mbps links.
+ */
+ return phy_modify_paged(phydev, 0x8000, 30, BIT(13), BIT(13));
+}
+
+static struct phy_driver mtk_soc_fe_phy_driver[] = {
+ {
+ PHY_ID_MATCH_EXACT(MTK_FPHY_ID_MT7628),
+ .name = "MediaTek MT7628 PHY",
+ .config_init = mt7628_phy_config_init,
+ .read_page = mt7628_phy_read_page,
+ .write_page = mt7628_phy_write_page,
+ },
+};
+
+module_phy_driver(mtk_soc_fe_phy_driver);
+static const struct mdio_device_id __maybe_unused mtk_soc_fe_phy_tbl[] = {
+ { PHY_ID_MATCH_EXACT(MTK_FPHY_ID_MT7628) },
+ { }
+};
+
+MODULE_DESCRIPTION("MediaTek SoC Fast Ethernet PHY driver");
+MODULE_AUTHOR("Joris Vaisvila <joey@tinyisr.com>");
+MODULE_LICENSE("GPL");
+
+MODULE_DEVICE_TABLE(mdio, mtk_soc_fe_phy_tbl);
--
2.54.0
^ permalink raw reply related
* [PATCH net-next v5 1/4] dt-bindings: net: dsa: add MT7628 ESW
From: Joris Vaisvila @ 2026-06-13 18:18 UTC (permalink / raw)
To: netdev
Cc: horms, pabeni, kuba, edumazet, davem, olteanv, Andrew Lunn,
devicetree, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Arınç ÜNAL, Landen Chao, DENG Qingfang, Sean Wang,
Daniel Golle, Joris Vaisvila, Krzysztof Kozlowski
In-Reply-To: <20260613181845.111877-1-joey@tinyisr.com>
Add device tree bindings for the MediaTek MT7628 embedded Ethernet
Switch.
The Switch provides 5 external user ports and 1 internal CPU port, with
integrated 10/100 PHYs and fixed port to PHY mapping.
The CPU port is internally connected and uses port index 6.
Signed-off-by: Joris Vaisvila <joey@tinyisr.com>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
---
.../bindings/net/dsa/mediatek,mt7628-esw.yaml | 96 +++++++++++++++++++
1 file changed, 96 insertions(+)
create mode 100644 Documentation/devicetree/bindings/net/dsa/mediatek,mt7628-esw.yaml
diff --git a/Documentation/devicetree/bindings/net/dsa/mediatek,mt7628-esw.yaml b/Documentation/devicetree/bindings/net/dsa/mediatek,mt7628-esw.yaml
new file mode 100644
index 000000000000..e0e7ffef6648
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/dsa/mediatek,mt7628-esw.yaml
@@ -0,0 +1,96 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/net/dsa/mediatek,mt7628-esw.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Mediatek MT7628 Embedded Ethernet Switch
+
+maintainers:
+ - Joris Vaisvila <joey@tinyisr.com>
+
+description:
+ The MT7628 SoC's built-in Ethernet Switch has five user ports and one
+ internally connected CPU port. The user ports are all connected to the SoC's
+ integrated Fast Ethernet PHYs. The switch registers are directly mapped in
+ the SoC's memory.
+
+allOf:
+ - $ref: dsa.yaml#/$defs/ethernet-ports
+
+properties:
+ compatible:
+ const: mediatek,mt7628-esw
+
+ reg:
+ maxItems: 1
+
+ resets:
+ items:
+ - description: internal switch block reset
+ - description: internal phy package reset
+
+ reset-names:
+ items:
+ - const: esw
+ - const: ephy
+
+required:
+ - compatible
+ - reg
+ - resets
+ - reset-names
+ - ethernet-ports
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ switch@10110000 {
+ compatible = "mediatek,mt7628-esw";
+ reg = <0x10110000 0x8000>;
+
+ resets = <&sysc 23>, <&sysc 24>;
+ reset-names = "esw", "ephy";
+
+ ethernet-ports {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ethernet-port@0 {
+ reg = <0>;
+ phy-mode = "internal";
+ };
+
+ ethernet-port@1 {
+ reg = <1>;
+ phy-mode = "internal";
+ };
+
+ ethernet-port@2 {
+ reg = <2>;
+ phy-mode = "internal";
+ };
+
+ ethernet-port@3 {
+ reg = <3>;
+ phy-mode = "internal";
+ };
+
+ ethernet-port@4 {
+ reg = <4>;
+ phy-mode = "internal";
+ };
+
+ ethernet-port@6 {
+ reg = <6>;
+ phy-mode = "internal";
+ ethernet = <ðernet>;
+
+ fixed-link {
+ speed = <1000>;
+ full-duplex;
+ };
+ };
+ };
+ };
--
2.54.0
^ permalink raw reply related
* [PATCH net-next v5 0/4] net: dsa: mt7628 embedded switch initial support
From: Joris Vaisvila @ 2026-06-13 18:18 UTC (permalink / raw)
To: netdev
Cc: horms, pabeni, kuba, edumazet, davem, olteanv, Andrew Lunn,
devicetree, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Arınç ÜNAL, Landen Chao, DENG Qingfang, Sean Wang,
Daniel Golle, Joris Vaisvila
This patch series adds initial support for the MediaTek MT7628 Embedded
Switch.
The driver implements the basic functionality required to operate the
switch using DSA. The hardware provides five internal Fast Ethernet user
ports and one Gigabit port connected internally to the CPU MAC.
Bridge offloading is not yet supported.
Tested on an MT7628NN-based board.
Changes since v4:
mt7628 dsa driver:
- fixed mdiobus allocation tied to platform device while
being initialized in DSA switch setup
mt7628 phy driver:
- replaced phy_write() with phy_modify() when setting PHY init
bit (no functional change)
mt7628 dt binding:
- moved unevaluatedProperties after required block
- removed blank line between compatible and reg in example
Link: https://lore.kernel.org/netdev/20260608192948.289745-1-joey@tinyisr.com/t/#u
Changes since v3:
- rebased on latest net-next
mt7628 dsa driver:
- simplified vlan_add hardware vlan slot search
- fixed vlan_del not removing vid from port pvid
- separated mii_read/mii_write error handling from return
value parsing. Updated RD_DONE/WT_DONE bit checking
with clearer logic and a comment.
- moved NET_DSA_MT7628 after NET_DSA_MT7530 in Kconfig
- added missing reset return value checks in probe
- fixed mt7628_switch_ops missing const specifier
- removed mdio node parsing from of, as there is nothing
to configure
mt7628 dt binding:
- updated description to be more clear about port count
- dropped optional mdio subnode. the switch does not
expose an external MDIO bus and all integrated PHY
access is handled by the driver.
- removed unused switch0 label in example
Link: https://lore.kernel.org/netdev/20260428185510.261521-1-joey@tinyisr.com/t/#u
Changes since v2:
- fix binding issues found in review
- fix ignored dsa_tag_8021q_register return value
- add switch teardown to clean up tag_8021q
- fix ordering issue where mdio probe fail would leak tag_8021q
Link: https://lore.kernel.org/netdev/20260330184017.766200-1-joey@tinyisr.com/t/#u
Changes since v1:
- changed port 6 phy-mode to internal
- cleaned up tag_mt7628 rcv function and mask defines
- fixed sorting error in drivers/net/dsa/ Kconfig and Makefile
- fixed sorting error in net/dsa/ Kconfig and Makefile
- fixed mt7628_mii_read/write return values on error
Link: https://lore.kernel.org/netdev/20260326204413.3317584-1-joey@tinyisr.com/t/#u
Thanks,
Joris
Joris Vaisvila (4):
dt-bindings: net: dsa: add MT7628 ESW
net: phy: mediatek: add phy driver for MT7628 built-in Fast Ethernet
PHYs
net: dsa: initial MT7628 tagging driver
net: dsa: initial support for MT7628 embedded switch
.../bindings/net/dsa/mediatek,mt7628-esw.yaml | 96 +++
drivers/net/dsa/Kconfig | 8 +
drivers/net/dsa/Makefile | 1 +
drivers/net/dsa/mt7628.c | 649 ++++++++++++++++++
drivers/net/phy/mediatek/Kconfig | 10 +-
drivers/net/phy/mediatek/Makefile | 1 +
drivers/net/phy/mediatek/mtk-fe-soc.c | 50 ++
include/net/dsa.h | 2 +
net/dsa/Kconfig | 6 +
net/dsa/Makefile | 1 +
net/dsa/tag_mt7628.c | 89 +++
11 files changed, 912 insertions(+), 1 deletion(-)
create mode 100644 Documentation/devicetree/bindings/net/dsa/mediatek,mt7628-esw.yaml
create mode 100644 drivers/net/dsa/mt7628.c
create mode 100644 drivers/net/phy/mediatek/mtk-fe-soc.c
create mode 100644 net/dsa/tag_mt7628.c
--
2.54.0
^ permalink raw reply
* Re: [RFC PATCH bpf-next 0/5] tcp: opportunistic loopback splice for BPF-paired sockets
From: Jakub Kicinski @ 2026-06-13 17:57 UTC (permalink / raw)
To: Alexei Starovoitov
Cc: Cong Wang, Network Development, bpf, John Fastabend,
Jakub Sitnicki, Jiayuan Chen, Hemanth Malla, zijianzhang
In-Reply-To: <CAADnVQ+KTNKkf_Tc-RZR-g8wEfJU4qWcOPnjDbA2=PEtZsYnYg@mail.gmail.com>
On Fri, 12 Jun 2026 09:01:43 -0700 Alexei Starovoitov wrote:
> Just saying that the code is free nowadays, so whether it's 1k lines
> or 10 lines is irrelevant for the discussion.
>
> As far as the idea goes, I think, it would be interesting in pre-AI era,
> but today splice and friends are a prime target for bugs and more bugs.
> skmsg and tcp_bpf are reeling from unfixed bugs too,
> so my take is that we should not add any new features to skmsg
> and instead deprecate what is already there.
100% agreed. There are so many unfixed skmsg bugs it's hard to know
were to start :( Kernel "intelligence" to help unoptimized applications
is particularly unappealing right now.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox