Messages in this thread | | | From | Tobias Waldekranz <> | Subject | Re: [PATCH net-next] net: mdio: Add netlink interface | Date | Tue, 07 Mar 2023 13:26:30 +0100 |
| |
On mån, mar 06, 2023 at 15:45, Sean Anderson <sean.anderson@seco.com> wrote: > This adds a netlink interface to make reads/writes to mdio buses. This > makes it easier to debug devices. This is especially useful when there > is a PCS involved (and the ethtool reads are faked), when there is no > MAC associated with a PHY, or when the MDIO device is not a PHY. > > The closest existing in-kernel interfaces are the SIOCG/SMIIREG ioctls, but > they have several drawbacks: > > 1. "Write register" is not always exactly that. The kernel will try to > be extra helpful and do things behind the scenes if it detects a > write to the reset bit of a PHY for example. > > 2. Only one op per syscall. This means that is impossible to implement > many operations in a safe manner. Something as simple as a > read/mask/write cycle can race against an in-kernel driver. > > 3. Addressing is awkward since you don't address the MDIO bus > directly, rather "the MDIO bus to which this netdev's PHY is > connected". This makes it hard to talk to devices on buses to which > no PHYs are connected, the typical case being Ethernet switches. > > To address these shortcomings, this adds a GENL interface with which a user > can interact with an MDIO bus directly. The user sends a program that > mdio-netlink executes, possibly emitting data back to the user. I.e. it > implements a very simple VM. Read/mask/write operations could be > implemented by dedicated commands, but when you start looking at more > advanced things like reading out the VLAN database of a switch you need > state and branching. > > To prevent userspace phy drivers, writes are disabled by default, and can > only be enabled by editing the source. This is the same strategy used by > regmap for debugfs writes. Unfortunately, this disallows several useful > features, including > > - Register writes (obviously) > - C45-over-C22 > - Atomic access to paged registers > - Better MDIO emulation for e.g. QEMU > > However, the read-only interface remains broadly useful for debugging. > Users who want to use the above features can re-enable them by defining > MDIO_NETLINK_ALLOW_WRITE and recompiling their kernel.
What about taking a page from the BPF playbook and require all loaded programs (MDIO_GENL_XFERs) to be licensed under GPL? That would mean that the userspace program that generated it would also have to be GPLed.
My view has always been that a vendor looking to build a userspace SDK won't be deterred by this limitation. They can easily build mdio-netlink.ko from mdio-tools and use that to drive it, or (more likely) they already have their own implementation that they are stuck with for legacy reasons. In other words: we are only punishing legitimate users (mdio-tools being one of them, IMO).
Perhaps with this approach we could have our cake and eat it too.
> Signed-off-by: Sean Anderson <sean.anderson@seco.com> > --- > This driver was written by Tobias Waldekranz. It is adapted from the > version he released with mdio-tools [1]. This was last discussed 2.5 > years ago [2], and I have incorperated his cover letter into this commit > message. The discussion mainly centered around the write capability > allowing for userspace phy drivers. Although it comes with reduced > functionality, I hope this new approach satisfies Andrew. I have also > made several minor changes for style and to stay abrest of changing > APIs. > > Tobias, I've taken the liberty of adding some copyright notices > attributing this to you.
Fine by me :)
> [1] https://github.com/wkz/mdio-tools > [2] https://lore.kernel.org/netdev/C42DZQLTPHM5.2THDSRK84BI3T@wkz-x280/ > > drivers/net/mdio/Kconfig | 8 + > drivers/net/mdio/Makefile | 1 + > drivers/net/mdio/mdio-netlink.c | 464 ++++++++++++++++++++++++++++++ > include/uapi/linux/mdio-netlink.h | 61 ++++ > 4 files changed, 534 insertions(+) > create mode 100644 drivers/net/mdio/mdio-netlink.c > create mode 100644 include/uapi/linux/mdio-netlink.h > > diff --git a/drivers/net/mdio/Kconfig b/drivers/net/mdio/Kconfig > index 90309980686e..8a01978e5b51 100644 > --- a/drivers/net/mdio/Kconfig > +++ b/drivers/net/mdio/Kconfig > @@ -43,6 +43,14 @@ config ACPI_MDIO > > if MDIO_BUS > > +config MDIO_NETLINK > + tristate "Netlink interface for MDIO buses" > + help > + Enable a netlink interface to allow reading MDIO buses from > + userspace. A small virtual machine allows implementing complex > + operations, such as conditional reads or polling. All operations > + submitted in the same program are evaluated atomically. > + > config MDIO_DEVRES > tristate > > diff --git a/drivers/net/mdio/Makefile b/drivers/net/mdio/Makefile > index 7d4cb4c11e4e..5583d5b8d174 100644 > --- a/drivers/net/mdio/Makefile > +++ b/drivers/net/mdio/Makefile > @@ -4,6 +4,7 @@ > obj-$(CONFIG_ACPI_MDIO) += acpi_mdio.o > obj-$(CONFIG_FWNODE_MDIO) += fwnode_mdio.o > obj-$(CONFIG_OF_MDIO) += of_mdio.o > +obj-$(CONFIG_MDIO_NETLINK) += mdio-netlink.o > > obj-$(CONFIG_MDIO_ASPEED) += mdio-aspeed.o > obj-$(CONFIG_MDIO_BCM_IPROC) += mdio-bcm-iproc.o > diff --git a/drivers/net/mdio/mdio-netlink.c b/drivers/net/mdio/mdio-netlink.c > new file mode 100644 > index 000000000000..3e32d1a9bab3 > --- /dev/null > +++ b/drivers/net/mdio/mdio-netlink.c > @@ -0,0 +1,464 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Copyright (C) 2022-23 Sean Anderson <sean.anderson@seco.com> > + * Copyright (C) 2020-22 Tobias Waldekranz <tobias@waldekranz.com> > + */ > + > +#include <linux/init.h> > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/netlink.h> > +#include <linux/phy.h> > +#include <net/genetlink.h> > +#include <net/netlink.h> > +#include <uapi/linux/mdio-netlink.h> > + > +struct mdio_nl_xfer { > + struct genl_info *info; > + struct sk_buff *msg; > + void *hdr; > + struct nlattr *data; > + > + struct mii_bus *mdio; > + int timeout_ms; > + > + int prog_len; > + struct mdio_nl_insn *prog; > +}; > + > +static int mdio_nl_open(struct mdio_nl_xfer *xfer); > +static int mdio_nl_close(struct mdio_nl_xfer *xfer, bool last, int xerr); > + > +static int mdio_nl_flush(struct mdio_nl_xfer *xfer) > +{ > + int err; > + > + err = mdio_nl_close(xfer, false, 0); > + if (err) > + return err; > + > + return mdio_nl_open(xfer); > +} > + > +static int mdio_nl_emit(struct mdio_nl_xfer *xfer, u32 datum) > +{ > + int err = 0; > + > + if (!nla_put_nohdr(xfer->msg, sizeof(datum), &datum)) > + return 0; > + > + err = mdio_nl_flush(xfer); > + if (err) > + return err; > + > + return nla_put_nohdr(xfer->msg, sizeof(datum), &datum); > +} > + > +static inline u16 *__arg_r(u32 arg, u16 *regs) > +{ > + WARN_ON_ONCE(arg >> 16 != MDIO_NL_ARG_REG); > + > + return ®s[arg & 0x7]; > +} > + > +static inline u16 __arg_i(u32 arg) > +{ > + WARN_ON_ONCE(arg >> 16 != MDIO_NL_ARG_IMM); > + > + return arg & 0xffff; > +} > + > +static inline u16 __arg_ri(u32 arg, u16 *regs) > +{ > + switch ((enum mdio_nl_argmode)(arg >> 16)) { > + case MDIO_NL_ARG_IMM: > + return arg & 0xffff; > + case MDIO_NL_ARG_REG: > + return regs[arg & 7]; > + default: > + WARN_ON_ONCE(1); > + return 0; > + } > +} > + > +/* To prevent out-of-tree drivers from being implemented through this > + * interface, disallow writes by default. This does disallow read-only uses, > + * such as c45-over-c22 or reading phys with pages. However, with a such a > + * flexible interface, we must use a big hammer. People who want to use this > + * will need to modify the source code directly. > + */ > +#undef MDIO_NETLINK_ALLOW_WRITE > + > +static int mdio_nl_eval(struct mdio_nl_xfer *xfer) > +{ > + struct mdio_nl_insn *insn; > + unsigned long timeout; > + u16 regs[8] = { 0 }; > + int pc, ret = 0; > + int phy_id, reg, prtad, devad, val; > + > + timeout = jiffies + msecs_to_jiffies(xfer->timeout_ms); > + > + mutex_lock(&xfer->mdio->mdio_lock); > + > + for (insn = xfer->prog, pc = 0; > + pc < xfer->prog_len; > + insn = &xfer->prog[++pc]) { > + if (time_after(jiffies, timeout)) { > + ret = -ETIMEDOUT; > + break; > + } > + > + switch ((enum mdio_nl_op)insn->op) { > + case MDIO_NL_OP_READ: > + phy_id = __arg_ri(insn->arg0, regs); > + prtad = mdio_phy_id_prtad(phy_id); > + devad = mdio_phy_id_devad(phy_id); > + reg = __arg_ri(insn->arg1, regs); > + > + if (mdio_phy_id_is_c45(phy_id)) > + ret = __mdiobus_c45_read(xfer->mdio, prtad, > + devad, reg); > + else > + ret = __mdiobus_read(xfer->mdio, phy_id, reg); > + > + if (ret < 0) > + goto exit; > + *__arg_r(insn->arg2, regs) = ret; > + ret = 0; > + break; > + > + case MDIO_NL_OP_WRITE: > + phy_id = __arg_ri(insn->arg0, regs); > + prtad = mdio_phy_id_prtad(phy_id); > + devad = mdio_phy_id_devad(phy_id); > + reg = __arg_ri(insn->arg1, regs); > + val = __arg_ri(insn->arg2, regs); > + > +#ifdef MDIO_NETLINK_ALLOW_WRITE > + add_taint(TAINT_USER, LOCKDEP_STILL_OK); > + if (mdio_phy_id_is_c45(phy_id)) > + ret = __mdiobus_c45_write(xfer->mdio, prtad, > + devad, reg, val > + else > + ret = __mdiobus_write(xfer->mdio, dev, reg, > + val); > +#else > + ret = -EPERM; > +#endif > + if (ret < 0) > + goto exit; > + ret = 0; > + break; > + > + case MDIO_NL_OP_AND: > + *__arg_r(insn->arg2, regs) = > + __arg_ri(insn->arg0, regs) & > + __arg_ri(insn->arg1, regs); > + break; > + > + case MDIO_NL_OP_OR: > + *__arg_r(insn->arg2, regs) = > + __arg_ri(insn->arg0, regs) | > + __arg_ri(insn->arg1, regs); > + break; > + > + case MDIO_NL_OP_ADD: > + *__arg_r(insn->arg2, regs) = > + __arg_ri(insn->arg0, regs) + > + __arg_ri(insn->arg1, regs); > + break; > + > + case MDIO_NL_OP_JEQ: > + if (__arg_ri(insn->arg0, regs) == > + __arg_ri(insn->arg1, regs)) > + pc += (s16)__arg_i(insn->arg2); > + break; > + > + case MDIO_NL_OP_JNE: > + if (__arg_ri(insn->arg0, regs) != > + __arg_ri(insn->arg1, regs)) > + pc += (s16)__arg_i(insn->arg2); > + break; > + > + case MDIO_NL_OP_EMIT: > + ret = mdio_nl_emit(xfer, __arg_ri(insn->arg0, regs)); > + if (ret < 0) > + goto exit; > + ret = 0; > + break; > + > + case MDIO_NL_OP_UNSPEC: > + default: > + ret = -EINVAL; > + goto exit; > + } > + } > +exit: > + mutex_unlock(&xfer->mdio->mdio_lock); > + return ret; > +} > + > +struct mdio_nl_op_proto { > + u8 arg0; > + u8 arg1; > + u8 arg2; > +}; > + > +static const struct mdio_nl_op_proto mdio_nl_op_protos[MDIO_NL_OP_MAX + 1] = { > + [MDIO_NL_OP_READ] = { > + .arg0 = BIT(MDIO_NL_ARG_REG) | BIT(MDIO_NL_ARG_IMM), > + .arg1 = BIT(MDIO_NL_ARG_REG) | BIT(MDIO_NL_ARG_IMM), > + .arg2 = BIT(MDIO_NL_ARG_REG), > + }, > + [MDIO_NL_OP_WRITE] = { > + .arg0 = BIT(MDIO_NL_ARG_REG) | BIT(MDIO_NL_ARG_IMM), > + .arg1 = BIT(MDIO_NL_ARG_REG) | BIT(MDIO_NL_ARG_IMM), > + .arg2 = BIT(MDIO_NL_ARG_REG) | BIT(MDIO_NL_ARG_IMM), > + }, > + [MDIO_NL_OP_AND] = { > + .arg0 = BIT(MDIO_NL_ARG_REG) | BIT(MDIO_NL_ARG_IMM), > + .arg1 = BIT(MDIO_NL_ARG_REG) | BIT(MDIO_NL_ARG_IMM), > + .arg2 = BIT(MDIO_NL_ARG_REG), > + }, > + [MDIO_NL_OP_OR] = { > + .arg0 = BIT(MDIO_NL_ARG_REG) | BIT(MDIO_NL_ARG_IMM), > + .arg1 = BIT(MDIO_NL_ARG_REG) | BIT(MDIO_NL_ARG_IMM), > + .arg2 = BIT(MDIO_NL_ARG_REG), > + }, > + [MDIO_NL_OP_ADD] = { > + .arg0 = BIT(MDIO_NL_ARG_REG) | BIT(MDIO_NL_ARG_IMM), > + .arg1 = BIT(MDIO_NL_ARG_REG) | BIT(MDIO_NL_ARG_IMM), > + .arg2 = BIT(MDIO_NL_ARG_REG), > + }, > + [MDIO_NL_OP_JEQ] = { > + .arg0 = BIT(MDIO_NL_ARG_REG) | BIT(MDIO_NL_ARG_IMM), > + .arg1 = BIT(MDIO_NL_ARG_REG) | BIT(MDIO_NL_ARG_IMM), > + .arg2 = BIT(MDIO_NL_ARG_IMM), > + }, > + [MDIO_NL_OP_JNE] = { > + .arg0 = BIT(MDIO_NL_ARG_REG) | BIT(MDIO_NL_ARG_IMM), > + .arg1 = BIT(MDIO_NL_ARG_REG) | BIT(MDIO_NL_ARG_IMM), > + .arg2 = BIT(MDIO_NL_ARG_IMM), > + }, > + [MDIO_NL_OP_EMIT] = { > + .arg0 = BIT(MDIO_NL_ARG_REG) | BIT(MDIO_NL_ARG_IMM), > + .arg1 = BIT(MDIO_NL_ARG_NONE), > + .arg2 = BIT(MDIO_NL_ARG_NONE), > + }, > +}; > + > +static int mdio_nl_validate_insn(const struct nlattr *attr, > + struct netlink_ext_ack *extack, > + const struct mdio_nl_insn *insn) > +{ > + const struct mdio_nl_op_proto *proto; > + > + if (insn->op > MDIO_NL_OP_MAX) { > + NL_SET_ERR_MSG_ATTR(extack, attr, "Illegal instruction"); > + return -EINVAL; > + } > + > + proto = &mdio_nl_op_protos[insn->op]; > + > + if (!(BIT(insn->arg0 >> 16) & proto->arg0)) { > + NL_SET_ERR_MSG_ATTR(extack, attr, "Argument 0 invalid"); > + return -EINVAL; > + } > + > + if (!(BIT(insn->arg1 >> 16) & proto->arg1)) { > + NL_SET_ERR_MSG_ATTR(extack, attr, "Argument 1 invalid"); > + return -EINVAL; > + } > + > + if (!(BIT(insn->arg2 >> 16) & proto->arg2)) { > + NL_SET_ERR_MSG_ATTR(extack, attr, "Argument 2 invalid"); > + return -EINVAL; > + } > + > + return 0; > +} > + > +static int mdio_nl_validate_prog(const struct nlattr *attr, > + struct netlink_ext_ack *extack) > +{ > + const struct mdio_nl_insn *prog = nla_data(attr); > + int len = nla_len(attr); > + int i, err = 0; > + > + if (len % sizeof(*prog)) { > + NL_SET_ERR_MSG_ATTR(extack, attr, "Unaligned instruction"); > + return -EINVAL; > + } > + > + len /= sizeof(*prog); > + for (i = 0; i < len; i++) { > + err = mdio_nl_validate_insn(attr, extack, &prog[i]); > + if (err) > + break; > + } > + > + return err; > +} > + > +static const struct nla_policy mdio_nl_policy[MDIO_NLA_MAX + 1] = { > + [MDIO_NLA_UNSPEC] = { .type = NLA_UNSPEC, }, > + [MDIO_NLA_BUS_ID] = { .type = NLA_STRING, .len = MII_BUS_ID_SIZE }, > + [MDIO_NLA_TIMEOUT] = NLA_POLICY_MAX(NLA_U16, 10 * MSEC_PER_SEC), > + [MDIO_NLA_PROG] = NLA_POLICY_VALIDATE_FN(NLA_BINARY, > + mdio_nl_validate_prog, > + 0x1000), > + [MDIO_NLA_DATA] = { .type = NLA_NESTED }, > + [MDIO_NLA_ERROR] = { .type = NLA_S32, }, > +}; > + > +static struct genl_family mdio_nl_family; > + > +static int mdio_nl_open(struct mdio_nl_xfer *xfer) > +{ > + int err; > + > + xfer->msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); > + if (!xfer->msg) { > + err = -ENOMEM; > + goto err; > + } > + > + xfer->hdr = genlmsg_put(xfer->msg, xfer->info->snd_portid, > + xfer->info->snd_seq, &mdio_nl_family, > + NLM_F_ACK | NLM_F_MULTI, MDIO_GENL_XFER); > + if (!xfer->hdr) { > + err = -EMSGSIZE; > + goto err_free; > + } > + > + xfer->data = nla_nest_start(xfer->msg, MDIO_NLA_DATA); > + if (!xfer->data) { > + err = -EMSGSIZE; > + goto err_free; > + } > + > + return 0; > + > +err_free: > + nlmsg_free(xfer->msg); > +err: > + return err; > +} > + > +static int mdio_nl_close(struct mdio_nl_xfer *xfer, bool last, int xerr) > +{ > + struct nlmsghdr *end; > + int err; > + > + nla_nest_end(xfer->msg, xfer->data); > + > + if (xerr && nla_put_s32(xfer->msg, MDIO_NLA_ERROR, xerr)) { > + err = mdio_nl_flush(xfer); > + if (err) > + goto err_free; > + > + if (nla_put_s32(xfer->msg, MDIO_NLA_ERROR, xerr)) { > + err = -EMSGSIZE; > + goto err_free; > + } > + } > + > + genlmsg_end(xfer->msg, xfer->hdr); > + > + if (last) { > + end = nlmsg_put(xfer->msg, xfer->info->snd_portid, > + xfer->info->snd_seq, NLMSG_DONE, 0, > + NLM_F_ACK | NLM_F_MULTI); > + if (!end) { > + err = mdio_nl_flush(xfer); > + if (err) > + goto err_free; > + > + end = nlmsg_put(xfer->msg, xfer->info->snd_portid, > + xfer->info->snd_seq, NLMSG_DONE, 0, > + NLM_F_ACK | NLM_F_MULTI); > + if (!end) { > + err = -EMSGSIZE; > + goto err_free; > + } > + } > + } > + > + return genlmsg_unicast(genl_info_net(xfer->info), xfer->msg, > + xfer->info->snd_portid); > + > +err_free: > + nlmsg_free(xfer->msg); > + return err; > +} > + > +static int mdio_nl_cmd_xfer(struct sk_buff *skb, struct genl_info *info) > +{ > + struct mdio_nl_xfer xfer; > + int err; > + > + if (!info->attrs[MDIO_NLA_BUS_ID] || > + !info->attrs[MDIO_NLA_PROG] || > + info->attrs[MDIO_NLA_DATA] || > + info->attrs[MDIO_NLA_ERROR]) > + return -EINVAL; > + > + xfer.mdio = mdio_find_bus(nla_data(info->attrs[MDIO_NLA_BUS_ID])); > + if (!xfer.mdio) > + return -ENODEV; > + > + if (info->attrs[MDIO_NLA_TIMEOUT]) > + xfer.timeout_ms = nla_get_u32(info->attrs[MDIO_NLA_TIMEOUT]); > + else > + xfer.timeout_ms = 100; > + > + xfer.info = info; > + xfer.prog_len = nla_len(info->attrs[MDIO_NLA_PROG]) / sizeof(*xfer.prog); > + xfer.prog = nla_data(info->attrs[MDIO_NLA_PROG]); > + > + err = mdio_nl_open(&xfer); > + if (err) > + return err; > + > + err = mdio_nl_eval(&xfer); > + > + err = mdio_nl_close(&xfer, true, err); > + return err; > +} > + > +static const struct genl_ops mdio_nl_ops[] = { > + { > + .cmd = MDIO_GENL_XFER, > + .doit = mdio_nl_cmd_xfer, > + .flags = GENL_ADMIN_PERM, > + }, > +}; > + > +static struct genl_family mdio_nl_family = { > + .name = "mdio", > + .version = 1, > + .maxattr = MDIO_NLA_MAX, > + .netnsok = false, > + .module = THIS_MODULE, > + .ops = mdio_nl_ops, > + .n_ops = ARRAY_SIZE(mdio_nl_ops), > + .policy = mdio_nl_policy, > +}; > + > +static int __init mdio_nl_init(void) > +{ > + return genl_register_family(&mdio_nl_family); > +} > + > +static void __exit mdio_nl_exit(void) > +{ > + genl_unregister_family(&mdio_nl_family); > +} > + > +MODULE_AUTHOR("Tobias Waldekranz <tobias@waldekranz.com>"); > +MODULE_DESCRIPTION("MDIO Netlink Interface"); > +MODULE_LICENSE("GPL"); > + > +module_init(mdio_nl_init); > +module_exit(mdio_nl_exit); > diff --git a/include/uapi/linux/mdio-netlink.h b/include/uapi/linux/mdio-netlink.h > new file mode 100644 > index 000000000000..bebd3b45c882 > --- /dev/null > +++ b/include/uapi/linux/mdio-netlink.h > @@ -0,0 +1,61 @@ > +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ > +/* > + * Copyright (C) 2020-22 Tobias Waldekranz <tobias@waldekranz.com> > + */ > + > +#ifndef _UAPI_LINUX_MDIO_NETLINK_H > +#define _UAPI_LINUX_MDIO_NETLINK_H > + > +#include <linux/types.h> > + > +enum { > + MDIO_GENL_UNSPEC, > + MDIO_GENL_XFER, > + > + __MDIO_GENL_MAX, > + MDIO_GENL_MAX = __MDIO_GENL_MAX - 1 > +}; > + > +enum { > + MDIO_NLA_UNSPEC, > + MDIO_NLA_BUS_ID, /* string */ > + MDIO_NLA_TIMEOUT, /* u32 */ > + MDIO_NLA_PROG, /* struct mdio_nl_insn[] */ > + MDIO_NLA_DATA, /* nest */ > + MDIO_NLA_ERROR, /* s32 */ > + > + __MDIO_NLA_MAX, > + MDIO_NLA_MAX = __MDIO_NLA_MAX - 1 > +}; > + > +enum mdio_nl_op { > + MDIO_NL_OP_UNSPEC, > + MDIO_NL_OP_READ, /* read dev(RI), port(RI), dst(R) */ > + MDIO_NL_OP_WRITE, /* write dev(RI), port(RI), src(RI) */ > + MDIO_NL_OP_AND, /* and a(RI), b(RI), dst(R) */ > + MDIO_NL_OP_OR, /* or a(RI), b(RI), dst(R) */ > + MDIO_NL_OP_ADD, /* add a(RI), b(RI), dst(R) */ > + MDIO_NL_OP_JEQ, /* jeq a(RI), b(RI), jmp(I) */ > + MDIO_NL_OP_JNE, /* jeq a(RI), b(RI), jmp(I) */ > + MDIO_NL_OP_EMIT, /* emit src(RI) */ > + > + __MDIO_NL_OP_MAX, > + MDIO_NL_OP_MAX = __MDIO_NL_OP_MAX - 1 > +}; > + > +enum mdio_nl_argmode { > + MDIO_NL_ARG_NONE, > + MDIO_NL_ARG_REG, > + MDIO_NL_ARG_IMM, > + MDIO_NL_ARG_RESERVED > +}; > + > +struct mdio_nl_insn { > + __u64 op:8; > + __u64 reserved:2; > + __u64 arg0:18; > + __u64 arg1:18; > + __u64 arg2:18; > +}; > + > +#endif /* _UAPI_LINUX_MDIO_NETLINK_H */ > -- > 2.35.1.1320.gc452695387.dirty
| |