]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
Merge ra.kernel.org:/pub/scm/linux/kernel/git/netdev/net
authorDavid S. Miller <davem@davemloft.net>
Fri, 17 Feb 2023 11:06:39 +0000 (11:06 +0000)
committerDavid S. Miller <davem@davemloft.net>
Fri, 17 Feb 2023 11:06:39 +0000 (11:06 +0000)
Some of the devlink bits were tricky, but I think I got it right.

Signed-off-by: David S. Miller <davem@davemloft.net>
23 files changed:
1  2 
MAINTAINERS
drivers/net/ethernet/broadcom/bnxt/bnxt.c
drivers/net/ethernet/intel/i40e/i40e_main.c
drivers/net/ethernet/intel/ice/ice_devlink.c
drivers/net/ethernet/intel/ice/ice_main.c
drivers/net/ethernet/intel/ice/ice_xsk.c
drivers/net/ethernet/intel/igb/igb_main.c
drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
drivers/net/ethernet/netronome/nfp/crypto/ipsec.c
drivers/net/ethernet/netronome/nfp/nfp_net_common.c
drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h
drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
drivers/net/ethernet/ti/am65-cpsw-nuss.c
drivers/net/ethernet/ti/am65-cpsw-nuss.h
include/linux/netdevice.h
include/net/sock.h
net/caif/caif_socket.c
net/core/dev.c
net/devlink/dev.c
net/devlink/leftover.c
net/ipv6/tcp_ipv6.c
net/socket.c
net/tipc/socket.c

diff --cc MAINTAINERS
Simple merge
index a25a68c69f2227c5e5ab6780552cf989e85b0036,374b7f10b549be3fcad1ef4ddc01d6d9277ef607..b2d96ae5668c7475a4d4fee3dd28068f539fa51b
@@@ -597,107 -597,6 +597,125 @@@ ice_construct_skb_zc(struct ice_rx_rin
        return skb;
  }
  
-  * ice_clean_xdp_irq_zc - AF_XDP ZC specific Tx cleaning routine
 +/**
-       if (tx_desc->cmd_type_offset_bsz &
-           cpu_to_le64(ICE_TX_DESC_DTYPE_DESC_DONE)) {
++ * ice_clean_xdp_tx_buf - Free and unmap XDP Tx buffer
++ * @xdp_ring: XDP Tx ring
++ * @tx_buf: Tx buffer to clean
++ */
++static void
++ice_clean_xdp_tx_buf(struct ice_tx_ring *xdp_ring, struct ice_tx_buf *tx_buf)
++{
++      page_frag_free(tx_buf->raw_buf);
++      xdp_ring->xdp_tx_active--;
++      dma_unmap_single(xdp_ring->dev, dma_unmap_addr(tx_buf, dma),
++                       dma_unmap_len(tx_buf, len), DMA_TO_DEVICE);
++      dma_unmap_len_set(tx_buf, len, 0);
++}
++
++/**
++ * ice_clean_xdp_irq_zc - produce AF_XDP descriptors to CQ
 + * @xdp_ring: XDP Tx ring
 + */
 +static void ice_clean_xdp_irq_zc(struct ice_tx_ring *xdp_ring)
 +{
 +      u16 ntc = xdp_ring->next_to_clean;
 +      struct ice_tx_desc *tx_desc;
 +      u16 cnt = xdp_ring->count;
 +      struct ice_tx_buf *tx_buf;
++      u16 completed_frames = 0;
 +      u16 xsk_frames = 0;
 +      u16 last_rs;
 +      int i;
 +
 +      last_rs = xdp_ring->next_to_use ? xdp_ring->next_to_use - 1 : cnt - 1;
 +      tx_desc = ICE_TX_DESC(xdp_ring, last_rs);
-                       xsk_frames = last_rs - ntc + 1;
++      if ((tx_desc->cmd_type_offset_bsz &
++          cpu_to_le64(ICE_TX_DESC_DTYPE_DESC_DONE))) {
 +              if (last_rs >= ntc)
-                       xsk_frames = last_rs + cnt - ntc + 1;
++                      completed_frames = last_rs - ntc + 1;
 +              else
-       if (!xsk_frames)
++                      completed_frames = last_rs + cnt - ntc + 1;
 +      }
 +
-       if (likely(!xdp_ring->xdp_tx_active))
++      if (!completed_frames)
 +              return;
 +
-       for (i = 0; i < xsk_frames; i++) {
++      if (likely(!xdp_ring->xdp_tx_active)) {
++              xsk_frames = completed_frames;
 +              goto skip;
++      }
 +
 +      ntc = xdp_ring->next_to_clean;
-               if (tx_buf->xdp) {
-                       xsk_buff_free(tx_buf->xdp);
-                       xdp_ring->xdp_tx_active--;
++      for (i = 0; i < completed_frames; i++) {
 +              tx_buf = &xdp_ring->tx_buf[ntc];
 +
-               if (ntc == cnt)
++              if (tx_buf->raw_buf) {
++                      ice_clean_xdp_tx_buf(xdp_ring, tx_buf);
++                      tx_buf->raw_buf = NULL;
 +              } else {
 +                      xsk_frames++;
 +              }
 +
 +              ntc++;
-       xdp_ring->next_to_clean += xsk_frames;
++              if (ntc >= xdp_ring->count)
 +                      ntc = 0;
 +      }
 +skip:
 +      tx_desc->cmd_type_offset_bsz = 0;
++      xdp_ring->next_to_clean += completed_frames;
 +      if (xdp_ring->next_to_clean >= cnt)
 +              xdp_ring->next_to_clean -= cnt;
 +      if (xsk_frames)
 +              xsk_tx_completed(xdp_ring->xsk_pool, xsk_frames);
 +}
 +
 +/**
 + * ice_xmit_xdp_tx_zc - AF_XDP ZC handler for XDP_TX
 + * @xdp: XDP buffer to xmit
 + * @xdp_ring: XDP ring to produce descriptor onto
 + *
 + * note that this function works directly on xdp_buff, no need to convert
 + * it to xdp_frame. xdp_buff pointer is stored to ice_tx_buf so that cleaning
 + * side will be able to xsk_buff_free() it.
 + *
 + * Returns ICE_XDP_TX for successfully produced desc, ICE_XDP_CONSUMED if there
 + * was not enough space on XDP ring
 + */
 +static int ice_xmit_xdp_tx_zc(struct xdp_buff *xdp,
 +                            struct ice_tx_ring *xdp_ring)
 +{
 +      u32 size = xdp->data_end - xdp->data;
 +      u32 ntu = xdp_ring->next_to_use;
 +      struct ice_tx_desc *tx_desc;
 +      struct ice_tx_buf *tx_buf;
 +      dma_addr_t dma;
 +
 +      if (ICE_DESC_UNUSED(xdp_ring) < ICE_RING_QUARTER(xdp_ring)) {
 +              ice_clean_xdp_irq_zc(xdp_ring);
 +              if (!ICE_DESC_UNUSED(xdp_ring)) {
 +                      xdp_ring->ring_stats->tx_stats.tx_busy++;
 +                      return ICE_XDP_CONSUMED;
 +              }
 +      }
 +
 +      dma = xsk_buff_xdp_get_dma(xdp);
 +      xsk_buff_raw_dma_sync_for_device(xdp_ring->xsk_pool, dma, size);
 +
 +      tx_buf = &xdp_ring->tx_buf[ntu];
 +      tx_buf->xdp = xdp;
 +      tx_desc = ICE_TX_DESC(xdp_ring, ntu);
 +      tx_desc->buf_addr = cpu_to_le64(dma);
 +      tx_desc->cmd_type_offset_bsz = ice_build_ctob(ICE_TX_DESC_CMD_EOP,
 +                                                    0, size, 0);
 +      xdp_ring->xdp_tx_active++;
 +
 +      if (++ntu == xdp_ring->count)
 +              ntu = 0;
 +      xdp_ring->next_to_use = ntu;
 +
 +      return ICE_XDP_TX;
 +}
 +
  /**
   * ice_run_xdp_zc - Executes an XDP program in zero-copy path
   * @rx_ring: Rx ring
index b8bc89fc2540485a0b1973cd664fb30848e42cd8,063cd371033a8824a5527db6caf06d35611d7ee0..c0dcce8ae4375dca52fb1b5ca211a7b8b2ed7ada
@@@ -487,10 -482,13 +492,13 @@@ static int nfp_net_xfrm_add_state(struc
        }
  
        /* Allocate saidx and commit the SA */
-       err = nfp_ipsec_cfg_cmd_issue(nn, NFP_IPSEC_CFG_MSSG_ADD_SA, saidx, &msg);
+       msg.cmd = NFP_IPSEC_CFG_MSSG_ADD_SA;
+       msg.sa_idx = saidx;
+       err = nfp_net_sched_mbox_amsg_work(nn, NFP_NET_CFG_MBOX_CMD_IPSEC, &msg,
+                                          sizeof(msg), nfp_net_ipsec_cfg);
        if (err) {
                xa_erase(&nn->xa_ipsec, saidx);
 -              nn_err(nn, "Failed to issue IPsec command err ret=%d\n", err);
 +              NL_SET_ERR_MSG_MOD(extack, "Failed to issue IPsec command");
                return err;
        }
  
Simple merge
Simple merge
Simple merge
diff --cc net/core/dev.c
Simple merge
index b40153fa26800cda521de53ffe59e54b266a4679,0000000000000000000000000000000000000000..bf1d6f1bcfc75955d53394187e1ed2ad37b256c8
mode 100644,000000..100644
--- /dev/null
@@@ -1,1348 -1,0 +1,1346 @@@
-       move_netdevice_notifier_net(curr_net, dest_net,
-                                   &devlink->netdevice_nb);
 +// SPDX-License-Identifier: GPL-2.0-or-later
 +/*
 + * Copyright (c) 2016 Mellanox Technologies. All rights reserved.
 + * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
 + */
 +
 +#include <net/genetlink.h>
 +#include <net/sock.h>
 +#include "devl_internal.h"
 +
 +struct devlink_info_req {
 +      struct sk_buff *msg;
 +      void (*version_cb)(const char *version_name,
 +                         enum devlink_info_version_type version_type,
 +                         void *version_cb_priv);
 +      void *version_cb_priv;
 +};
 +
 +struct devlink_reload_combination {
 +      enum devlink_reload_action action;
 +      enum devlink_reload_limit limit;
 +};
 +
 +static const struct devlink_reload_combination devlink_reload_invalid_combinations[] = {
 +      {
 +              /* can't reinitialize driver with no down time */
 +              .action = DEVLINK_RELOAD_ACTION_DRIVER_REINIT,
 +              .limit = DEVLINK_RELOAD_LIMIT_NO_RESET,
 +      },
 +};
 +
 +static bool
 +devlink_reload_combination_is_invalid(enum devlink_reload_action action,
 +                                    enum devlink_reload_limit limit)
 +{
 +      int i;
 +
 +      for (i = 0; i < ARRAY_SIZE(devlink_reload_invalid_combinations); i++)
 +              if (devlink_reload_invalid_combinations[i].action == action &&
 +                  devlink_reload_invalid_combinations[i].limit == limit)
 +                      return true;
 +      return false;
 +}
 +
 +static bool
 +devlink_reload_action_is_supported(struct devlink *devlink, enum devlink_reload_action action)
 +{
 +      return test_bit(action, &devlink->ops->reload_actions);
 +}
 +
 +static bool
 +devlink_reload_limit_is_supported(struct devlink *devlink, enum devlink_reload_limit limit)
 +{
 +      return test_bit(limit, &devlink->ops->reload_limits);
 +}
 +
 +static int devlink_reload_stat_put(struct sk_buff *msg,
 +                                 enum devlink_reload_limit limit, u32 value)
 +{
 +      struct nlattr *reload_stats_entry;
 +
 +      reload_stats_entry = nla_nest_start(msg, DEVLINK_ATTR_RELOAD_STATS_ENTRY);
 +      if (!reload_stats_entry)
 +              return -EMSGSIZE;
 +
 +      if (nla_put_u8(msg, DEVLINK_ATTR_RELOAD_STATS_LIMIT, limit) ||
 +          nla_put_u32(msg, DEVLINK_ATTR_RELOAD_STATS_VALUE, value))
 +              goto nla_put_failure;
 +      nla_nest_end(msg, reload_stats_entry);
 +      return 0;
 +
 +nla_put_failure:
 +      nla_nest_cancel(msg, reload_stats_entry);
 +      return -EMSGSIZE;
 +}
 +
 +static int
 +devlink_reload_stats_put(struct sk_buff *msg, struct devlink *devlink, bool is_remote)
 +{
 +      struct nlattr *reload_stats_attr, *act_info, *act_stats;
 +      int i, j, stat_idx;
 +      u32 value;
 +
 +      if (!is_remote)
 +              reload_stats_attr = nla_nest_start(msg, DEVLINK_ATTR_RELOAD_STATS);
 +      else
 +              reload_stats_attr = nla_nest_start(msg, DEVLINK_ATTR_REMOTE_RELOAD_STATS);
 +
 +      if (!reload_stats_attr)
 +              return -EMSGSIZE;
 +
 +      for (i = 0; i <= DEVLINK_RELOAD_ACTION_MAX; i++) {
 +              if ((!is_remote &&
 +                   !devlink_reload_action_is_supported(devlink, i)) ||
 +                  i == DEVLINK_RELOAD_ACTION_UNSPEC)
 +                      continue;
 +              act_info = nla_nest_start(msg, DEVLINK_ATTR_RELOAD_ACTION_INFO);
 +              if (!act_info)
 +                      goto nla_put_failure;
 +
 +              if (nla_put_u8(msg, DEVLINK_ATTR_RELOAD_ACTION, i))
 +                      goto action_info_nest_cancel;
 +              act_stats = nla_nest_start(msg, DEVLINK_ATTR_RELOAD_ACTION_STATS);
 +              if (!act_stats)
 +                      goto action_info_nest_cancel;
 +
 +              for (j = 0; j <= DEVLINK_RELOAD_LIMIT_MAX; j++) {
 +                      /* Remote stats are shown even if not locally supported.
 +                       * Stats of actions with unspecified limit are shown
 +                       * though drivers don't need to register unspecified
 +                       * limit.
 +                       */
 +                      if ((!is_remote && j != DEVLINK_RELOAD_LIMIT_UNSPEC &&
 +                           !devlink_reload_limit_is_supported(devlink, j)) ||
 +                          devlink_reload_combination_is_invalid(i, j))
 +                              continue;
 +
 +                      stat_idx = j * __DEVLINK_RELOAD_ACTION_MAX + i;
 +                      if (!is_remote)
 +                              value = devlink->stats.reload_stats[stat_idx];
 +                      else
 +                              value = devlink->stats.remote_reload_stats[stat_idx];
 +                      if (devlink_reload_stat_put(msg, j, value))
 +                              goto action_stats_nest_cancel;
 +              }
 +              nla_nest_end(msg, act_stats);
 +              nla_nest_end(msg, act_info);
 +      }
 +      nla_nest_end(msg, reload_stats_attr);
 +      return 0;
 +
 +action_stats_nest_cancel:
 +      nla_nest_cancel(msg, act_stats);
 +action_info_nest_cancel:
 +      nla_nest_cancel(msg, act_info);
 +nla_put_failure:
 +      nla_nest_cancel(msg, reload_stats_attr);
 +      return -EMSGSIZE;
 +}
 +
 +static int devlink_nl_fill(struct sk_buff *msg, struct devlink *devlink,
 +                         enum devlink_command cmd, u32 portid,
 +                         u32 seq, int flags)
 +{
 +      struct nlattr *dev_stats;
 +      void *hdr;
 +
 +      hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
 +      if (!hdr)
 +              return -EMSGSIZE;
 +
 +      if (devlink_nl_put_handle(msg, devlink))
 +              goto nla_put_failure;
 +      if (nla_put_u8(msg, DEVLINK_ATTR_RELOAD_FAILED, devlink->reload_failed))
 +              goto nla_put_failure;
 +
 +      dev_stats = nla_nest_start(msg, DEVLINK_ATTR_DEV_STATS);
 +      if (!dev_stats)
 +              goto nla_put_failure;
 +
 +      if (devlink_reload_stats_put(msg, devlink, false))
 +              goto dev_stats_nest_cancel;
 +      if (devlink_reload_stats_put(msg, devlink, true))
 +              goto dev_stats_nest_cancel;
 +
 +      nla_nest_end(msg, dev_stats);
 +      genlmsg_end(msg, hdr);
 +      return 0;
 +
 +dev_stats_nest_cancel:
 +      nla_nest_cancel(msg, dev_stats);
 +nla_put_failure:
 +      genlmsg_cancel(msg, hdr);
 +      return -EMSGSIZE;
 +}
 +
 +void devlink_notify(struct devlink *devlink, enum devlink_command cmd)
 +{
 +      struct sk_buff *msg;
 +      int err;
 +
 +      WARN_ON(cmd != DEVLINK_CMD_NEW && cmd != DEVLINK_CMD_DEL);
 +      WARN_ON(!xa_get_mark(&devlinks, devlink->index, DEVLINK_REGISTERED));
 +
 +      msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 +      if (!msg)
 +              return;
 +
 +      err = devlink_nl_fill(msg, devlink, cmd, 0, 0, 0);
 +      if (err) {
 +              nlmsg_free(msg);
 +              return;
 +      }
 +
 +      genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink),
 +                              msg, 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL);
 +}
 +
 +int devlink_nl_cmd_get_doit(struct sk_buff *skb, struct genl_info *info)
 +{
 +      struct devlink *devlink = info->user_ptr[0];
 +      struct sk_buff *msg;
 +      int err;
 +
 +      msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 +      if (!msg)
 +              return -ENOMEM;
 +
 +      err = devlink_nl_fill(msg, devlink, DEVLINK_CMD_NEW,
 +                            info->snd_portid, info->snd_seq, 0);
 +      if (err) {
 +              nlmsg_free(msg);
 +              return err;
 +      }
 +
 +      return genlmsg_reply(msg, info);
 +}
 +
 +static int
 +devlink_nl_cmd_get_dump_one(struct sk_buff *msg, struct devlink *devlink,
 +                          struct netlink_callback *cb)
 +{
 +      return devlink_nl_fill(msg, devlink, DEVLINK_CMD_NEW,
 +                             NETLINK_CB(cb->skb).portid,
 +                             cb->nlh->nlmsg_seq, NLM_F_MULTI);
 +}
 +
 +const struct devlink_cmd devl_cmd_get = {
 +      .dump_one               = devlink_nl_cmd_get_dump_one,
 +};
 +
 +static void devlink_reload_failed_set(struct devlink *devlink,
 +                                    bool reload_failed)
 +{
 +      if (devlink->reload_failed == reload_failed)
 +              return;
 +      devlink->reload_failed = reload_failed;
 +      devlink_notify(devlink, DEVLINK_CMD_NEW);
 +}
 +
 +bool devlink_is_reload_failed(const struct devlink *devlink)
 +{
 +      return devlink->reload_failed;
 +}
 +EXPORT_SYMBOL_GPL(devlink_is_reload_failed);
 +
 +static void
 +__devlink_reload_stats_update(struct devlink *devlink, u32 *reload_stats,
 +                            enum devlink_reload_limit limit, u32 actions_performed)
 +{
 +      unsigned long actions = actions_performed;
 +      int stat_idx;
 +      int action;
 +
 +      for_each_set_bit(action, &actions, __DEVLINK_RELOAD_ACTION_MAX) {
 +              stat_idx = limit * __DEVLINK_RELOAD_ACTION_MAX + action;
 +              reload_stats[stat_idx]++;
 +      }
 +      devlink_notify(devlink, DEVLINK_CMD_NEW);
 +}
 +
 +static void
 +devlink_reload_stats_update(struct devlink *devlink, enum devlink_reload_limit limit,
 +                          u32 actions_performed)
 +{
 +      __devlink_reload_stats_update(devlink, devlink->stats.reload_stats, limit,
 +                                    actions_performed);
 +}
 +
 +/**
 + *    devlink_remote_reload_actions_performed - Update devlink on reload actions
 + *      performed which are not a direct result of devlink reload call.
 + *
 + *    This should be called by a driver after performing reload actions in case it was not
 + *    a result of devlink reload call. For example fw_activate was performed as a result
 + *    of devlink reload triggered fw_activate on another host.
 + *    The motivation for this function is to keep data on reload actions performed on this
 + *    function whether it was done due to direct devlink reload call or not.
 + *
 + *    @devlink: devlink
 + *    @limit: reload limit
 + *    @actions_performed: bitmask of actions performed
 + */
 +void devlink_remote_reload_actions_performed(struct devlink *devlink,
 +                                           enum devlink_reload_limit limit,
 +                                           u32 actions_performed)
 +{
 +      if (WARN_ON(!actions_performed ||
 +                  actions_performed & BIT(DEVLINK_RELOAD_ACTION_UNSPEC) ||
 +                  actions_performed >= BIT(__DEVLINK_RELOAD_ACTION_MAX) ||
 +                  limit > DEVLINK_RELOAD_LIMIT_MAX))
 +              return;
 +
 +      __devlink_reload_stats_update(devlink, devlink->stats.remote_reload_stats, limit,
 +                                    actions_performed);
 +}
 +EXPORT_SYMBOL_GPL(devlink_remote_reload_actions_performed);
 +
 +static struct net *devlink_netns_get(struct sk_buff *skb,
 +                                   struct genl_info *info)
 +{
 +      struct nlattr *netns_pid_attr = info->attrs[DEVLINK_ATTR_NETNS_PID];
 +      struct nlattr *netns_fd_attr = info->attrs[DEVLINK_ATTR_NETNS_FD];
 +      struct nlattr *netns_id_attr = info->attrs[DEVLINK_ATTR_NETNS_ID];
 +      struct net *net;
 +
 +      if (!!netns_pid_attr + !!netns_fd_attr + !!netns_id_attr > 1) {
 +              NL_SET_ERR_MSG(info->extack, "multiple netns identifying attributes specified");
 +              return ERR_PTR(-EINVAL);
 +      }
 +
 +      if (netns_pid_attr) {
 +              net = get_net_ns_by_pid(nla_get_u32(netns_pid_attr));
 +      } else if (netns_fd_attr) {
 +              net = get_net_ns_by_fd(nla_get_u32(netns_fd_attr));
 +      } else if (netns_id_attr) {
 +              net = get_net_ns_by_id(sock_net(skb->sk),
 +                                     nla_get_u32(netns_id_attr));
 +              if (!net)
 +                      net = ERR_PTR(-EINVAL);
 +      } else {
 +              WARN_ON(1);
 +              net = ERR_PTR(-EINVAL);
 +      }
 +      if (IS_ERR(net)) {
 +              NL_SET_ERR_MSG(info->extack, "Unknown network namespace");
 +              return ERR_PTR(-EINVAL);
 +      }
 +      if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) {
 +              put_net(net);
 +              return ERR_PTR(-EPERM);
 +      }
 +      return net;
 +}
 +
 +static void devlink_reload_netns_change(struct devlink *devlink,
 +                                      struct net *curr_net,
 +                                      struct net *dest_net)
 +{
 +      /* Userspace needs to be notified about devlink objects
 +       * removed from original and entering new network namespace.
 +       * The rest of the devlink objects are re-created during
 +       * reload process so the notifications are generated separatelly.
 +       */
 +      devlink_notify_unregister(devlink);
 +      write_pnet(&devlink->_net, dest_net);
 +      devlink_notify_register(devlink);
 +}
 +
 +int devlink_reload(struct devlink *devlink, struct net *dest_net,
 +                 enum devlink_reload_action action,
 +                 enum devlink_reload_limit limit,
 +                 u32 *actions_performed, struct netlink_ext_ack *extack)
 +{
 +      u32 remote_reload_stats[DEVLINK_RELOAD_STATS_ARRAY_SIZE];
 +      struct net *curr_net;
 +      int err;
 +
 +      memcpy(remote_reload_stats, devlink->stats.remote_reload_stats,
 +             sizeof(remote_reload_stats));
 +
 +      err = devlink->ops->reload_down(devlink, !!dest_net, action, limit, extack);
 +      if (err)
 +              return err;
 +
 +      curr_net = devlink_net(devlink);
 +      if (dest_net && !net_eq(dest_net, curr_net))
 +              devlink_reload_netns_change(devlink, curr_net, dest_net);
 +
 +      if (action == DEVLINK_RELOAD_ACTION_DRIVER_REINIT)
 +              devlink_params_driverinit_load_new(devlink);
 +
 +      err = devlink->ops->reload_up(devlink, action, limit, actions_performed, extack);
 +      devlink_reload_failed_set(devlink, !!err);
 +      if (err)
 +              return err;
 +
 +      WARN_ON(!(*actions_performed & BIT(action)));
 +      /* Catch driver on updating the remote action within devlink reload */
 +      WARN_ON(memcmp(remote_reload_stats, devlink->stats.remote_reload_stats,
 +                     sizeof(remote_reload_stats)));
 +      devlink_reload_stats_update(devlink, limit, *actions_performed);
 +      return 0;
 +}
 +
 +static int
 +devlink_nl_reload_actions_performed_snd(struct devlink *devlink, u32 actions_performed,
 +                                      enum devlink_command cmd, struct genl_info *info)
 +{
 +      struct sk_buff *msg;
 +      void *hdr;
 +
 +      msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 +      if (!msg)
 +              return -ENOMEM;
 +
 +      hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq, &devlink_nl_family, 0, cmd);
 +      if (!hdr)
 +              goto free_msg;
 +
 +      if (devlink_nl_put_handle(msg, devlink))
 +              goto nla_put_failure;
 +
 +      if (nla_put_bitfield32(msg, DEVLINK_ATTR_RELOAD_ACTIONS_PERFORMED, actions_performed,
 +                             actions_performed))
 +              goto nla_put_failure;
 +      genlmsg_end(msg, hdr);
 +
 +      return genlmsg_reply(msg, info);
 +
 +nla_put_failure:
 +      genlmsg_cancel(msg, hdr);
 +free_msg:
 +      nlmsg_free(msg);
 +      return -EMSGSIZE;
 +}
 +
 +int devlink_nl_cmd_reload(struct sk_buff *skb, struct genl_info *info)
 +{
 +      struct devlink *devlink = info->user_ptr[0];
 +      enum devlink_reload_action action;
 +      enum devlink_reload_limit limit;
 +      struct net *dest_net = NULL;
 +      u32 actions_performed;
 +      int err;
 +
 +      err = devlink_resources_validate(devlink, NULL, info);
 +      if (err) {
 +              NL_SET_ERR_MSG(info->extack, "resources size validation failed");
 +              return err;
 +      }
 +
 +      if (info->attrs[DEVLINK_ATTR_RELOAD_ACTION])
 +              action = nla_get_u8(info->attrs[DEVLINK_ATTR_RELOAD_ACTION]);
 +      else
 +              action = DEVLINK_RELOAD_ACTION_DRIVER_REINIT;
 +
 +      if (!devlink_reload_action_is_supported(devlink, action)) {
 +              NL_SET_ERR_MSG(info->extack, "Requested reload action is not supported by the driver");
 +              return -EOPNOTSUPP;
 +      }
 +
 +      limit = DEVLINK_RELOAD_LIMIT_UNSPEC;
 +      if (info->attrs[DEVLINK_ATTR_RELOAD_LIMITS]) {
 +              struct nla_bitfield32 limits;
 +              u32 limits_selected;
 +
 +              limits = nla_get_bitfield32(info->attrs[DEVLINK_ATTR_RELOAD_LIMITS]);
 +              limits_selected = limits.value & limits.selector;
 +              if (!limits_selected) {
 +                      NL_SET_ERR_MSG(info->extack, "Invalid limit selected");
 +                      return -EINVAL;
 +              }
 +              for (limit = 0 ; limit <= DEVLINK_RELOAD_LIMIT_MAX ; limit++)
 +                      if (limits_selected & BIT(limit))
 +                              break;
 +              /* UAPI enables multiselection, but currently it is not used */
 +              if (limits_selected != BIT(limit)) {
 +                      NL_SET_ERR_MSG(info->extack, "Multiselection of limit is not supported");
 +                      return -EOPNOTSUPP;
 +              }
 +              if (!devlink_reload_limit_is_supported(devlink, limit)) {
 +                      NL_SET_ERR_MSG(info->extack, "Requested limit is not supported by the driver");
 +                      return -EOPNOTSUPP;
 +              }
 +              if (devlink_reload_combination_is_invalid(action, limit)) {
 +                      NL_SET_ERR_MSG(info->extack, "Requested limit is invalid for this action");
 +                      return -EINVAL;
 +              }
 +      }
 +      if (info->attrs[DEVLINK_ATTR_NETNS_PID] ||
 +          info->attrs[DEVLINK_ATTR_NETNS_FD] ||
 +          info->attrs[DEVLINK_ATTR_NETNS_ID]) {
 +              dest_net = devlink_netns_get(skb, info);
 +              if (IS_ERR(dest_net))
 +                      return PTR_ERR(dest_net);
 +              if (!net_eq(dest_net, devlink_net(devlink)) &&
 +                  action != DEVLINK_RELOAD_ACTION_DRIVER_REINIT) {
 +                      NL_SET_ERR_MSG_MOD(info->extack,
 +                                         "Changing namespace is only supported for reinit action");
 +                      return -EOPNOTSUPP;
 +              }
 +      }
 +
 +      err = devlink_reload(devlink, dest_net, action, limit, &actions_performed, info->extack);
 +
 +      if (dest_net)
 +              put_net(dest_net);
 +
 +      if (err)
 +              return err;
 +      /* For backward compatibility generate reply only if attributes used by user */
 +      if (!info->attrs[DEVLINK_ATTR_RELOAD_ACTION] && !info->attrs[DEVLINK_ATTR_RELOAD_LIMITS])
 +              return 0;
 +
 +      return devlink_nl_reload_actions_performed_snd(devlink, actions_performed,
 +                                                     DEVLINK_CMD_RELOAD, info);
 +}
 +
 +bool devlink_reload_actions_valid(const struct devlink_ops *ops)
 +{
 +      const struct devlink_reload_combination *comb;
 +      int i;
 +
 +      if (!devlink_reload_supported(ops)) {
 +              if (WARN_ON(ops->reload_actions))
 +                      return false;
 +              return true;
 +      }
 +
 +      if (WARN_ON(!ops->reload_actions ||
 +                  ops->reload_actions & BIT(DEVLINK_RELOAD_ACTION_UNSPEC) ||
 +                  ops->reload_actions >= BIT(__DEVLINK_RELOAD_ACTION_MAX)))
 +              return false;
 +
 +      if (WARN_ON(ops->reload_limits & BIT(DEVLINK_RELOAD_LIMIT_UNSPEC) ||
 +                  ops->reload_limits >= BIT(__DEVLINK_RELOAD_LIMIT_MAX)))
 +              return false;
 +
 +      for (i = 0; i < ARRAY_SIZE(devlink_reload_invalid_combinations); i++)  {
 +              comb = &devlink_reload_invalid_combinations[i];
 +              if (ops->reload_actions == BIT(comb->action) &&
 +                  ops->reload_limits == BIT(comb->limit))
 +                      return false;
 +      }
 +      return true;
 +}
 +
 +static int devlink_nl_eswitch_fill(struct sk_buff *msg, struct devlink *devlink,
 +                                 enum devlink_command cmd, u32 portid,
 +                                 u32 seq, int flags)
 +{
 +      const struct devlink_ops *ops = devlink->ops;
 +      enum devlink_eswitch_encap_mode encap_mode;
 +      u8 inline_mode;
 +      void *hdr;
 +      int err = 0;
 +      u16 mode;
 +
 +      hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
 +      if (!hdr)
 +              return -EMSGSIZE;
 +
 +      err = devlink_nl_put_handle(msg, devlink);
 +      if (err)
 +              goto nla_put_failure;
 +
 +      if (ops->eswitch_mode_get) {
 +              err = ops->eswitch_mode_get(devlink, &mode);
 +              if (err)
 +                      goto nla_put_failure;
 +              err = nla_put_u16(msg, DEVLINK_ATTR_ESWITCH_MODE, mode);
 +              if (err)
 +                      goto nla_put_failure;
 +      }
 +
 +      if (ops->eswitch_inline_mode_get) {
 +              err = ops->eswitch_inline_mode_get(devlink, &inline_mode);
 +              if (err)
 +                      goto nla_put_failure;
 +              err = nla_put_u8(msg, DEVLINK_ATTR_ESWITCH_INLINE_MODE,
 +                               inline_mode);
 +              if (err)
 +                      goto nla_put_failure;
 +      }
 +
 +      if (ops->eswitch_encap_mode_get) {
 +              err = ops->eswitch_encap_mode_get(devlink, &encap_mode);
 +              if (err)
 +                      goto nla_put_failure;
 +              err = nla_put_u8(msg, DEVLINK_ATTR_ESWITCH_ENCAP_MODE, encap_mode);
 +              if (err)
 +                      goto nla_put_failure;
 +      }
 +
 +      genlmsg_end(msg, hdr);
 +      return 0;
 +
 +nla_put_failure:
 +      genlmsg_cancel(msg, hdr);
 +      return err;
 +}
 +
 +int devlink_nl_cmd_eswitch_get_doit(struct sk_buff *skb, struct genl_info *info)
 +{
 +      struct devlink *devlink = info->user_ptr[0];
 +      struct sk_buff *msg;
 +      int err;
 +
 +      msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 +      if (!msg)
 +              return -ENOMEM;
 +
 +      err = devlink_nl_eswitch_fill(msg, devlink, DEVLINK_CMD_ESWITCH_GET,
 +                                    info->snd_portid, info->snd_seq, 0);
 +
 +      if (err) {
 +              nlmsg_free(msg);
 +              return err;
 +      }
 +
 +      return genlmsg_reply(msg, info);
 +}
 +
 +int devlink_nl_cmd_eswitch_set_doit(struct sk_buff *skb, struct genl_info *info)
 +{
 +      struct devlink *devlink = info->user_ptr[0];
 +      const struct devlink_ops *ops = devlink->ops;
 +      enum devlink_eswitch_encap_mode encap_mode;
 +      u8 inline_mode;
 +      int err = 0;
 +      u16 mode;
 +
 +      if (info->attrs[DEVLINK_ATTR_ESWITCH_MODE]) {
 +              if (!ops->eswitch_mode_set)
 +                      return -EOPNOTSUPP;
 +              mode = nla_get_u16(info->attrs[DEVLINK_ATTR_ESWITCH_MODE]);
 +              err = devlink_rate_nodes_check(devlink, mode, info->extack);
 +              if (err)
 +                      return err;
 +              err = ops->eswitch_mode_set(devlink, mode, info->extack);
 +              if (err)
 +                      return err;
 +      }
 +
 +      if (info->attrs[DEVLINK_ATTR_ESWITCH_INLINE_MODE]) {
 +              if (!ops->eswitch_inline_mode_set)
 +                      return -EOPNOTSUPP;
 +              inline_mode = nla_get_u8(info->attrs[DEVLINK_ATTR_ESWITCH_INLINE_MODE]);
 +              err = ops->eswitch_inline_mode_set(devlink, inline_mode,
 +                                                 info->extack);
 +              if (err)
 +                      return err;
 +      }
 +
 +      if (info->attrs[DEVLINK_ATTR_ESWITCH_ENCAP_MODE]) {
 +              if (!ops->eswitch_encap_mode_set)
 +                      return -EOPNOTSUPP;
 +              encap_mode = nla_get_u8(info->attrs[DEVLINK_ATTR_ESWITCH_ENCAP_MODE]);
 +              err = ops->eswitch_encap_mode_set(devlink, encap_mode,
 +                                                info->extack);
 +              if (err)
 +                      return err;
 +      }
 +
 +      return 0;
 +}
 +
 +int devlink_info_serial_number_put(struct devlink_info_req *req, const char *sn)
 +{
 +      if (!req->msg)
 +              return 0;
 +      return nla_put_string(req->msg, DEVLINK_ATTR_INFO_SERIAL_NUMBER, sn);
 +}
 +EXPORT_SYMBOL_GPL(devlink_info_serial_number_put);
 +
 +int devlink_info_board_serial_number_put(struct devlink_info_req *req,
 +                                       const char *bsn)
 +{
 +      if (!req->msg)
 +              return 0;
 +      return nla_put_string(req->msg, DEVLINK_ATTR_INFO_BOARD_SERIAL_NUMBER,
 +                            bsn);
 +}
 +EXPORT_SYMBOL_GPL(devlink_info_board_serial_number_put);
 +
 +static int devlink_info_version_put(struct devlink_info_req *req, int attr,
 +                                  const char *version_name,
 +                                  const char *version_value,
 +                                  enum devlink_info_version_type version_type)
 +{
 +      struct nlattr *nest;
 +      int err;
 +
 +      if (req->version_cb)
 +              req->version_cb(version_name, version_type,
 +                              req->version_cb_priv);
 +
 +      if (!req->msg)
 +              return 0;
 +
 +      nest = nla_nest_start_noflag(req->msg, attr);
 +      if (!nest)
 +              return -EMSGSIZE;
 +
 +      err = nla_put_string(req->msg, DEVLINK_ATTR_INFO_VERSION_NAME,
 +                           version_name);
 +      if (err)
 +              goto nla_put_failure;
 +
 +      err = nla_put_string(req->msg, DEVLINK_ATTR_INFO_VERSION_VALUE,
 +                           version_value);
 +      if (err)
 +              goto nla_put_failure;
 +
 +      nla_nest_end(req->msg, nest);
 +
 +      return 0;
 +
 +nla_put_failure:
 +      nla_nest_cancel(req->msg, nest);
 +      return err;
 +}
 +
 +int devlink_info_version_fixed_put(struct devlink_info_req *req,
 +                                 const char *version_name,
 +                                 const char *version_value)
 +{
 +      return devlink_info_version_put(req, DEVLINK_ATTR_INFO_VERSION_FIXED,
 +                                      version_name, version_value,
 +                                      DEVLINK_INFO_VERSION_TYPE_NONE);
 +}
 +EXPORT_SYMBOL_GPL(devlink_info_version_fixed_put);
 +
 +int devlink_info_version_stored_put(struct devlink_info_req *req,
 +                                  const char *version_name,
 +                                  const char *version_value)
 +{
 +      return devlink_info_version_put(req, DEVLINK_ATTR_INFO_VERSION_STORED,
 +                                      version_name, version_value,
 +                                      DEVLINK_INFO_VERSION_TYPE_NONE);
 +}
 +EXPORT_SYMBOL_GPL(devlink_info_version_stored_put);
 +
 +int devlink_info_version_stored_put_ext(struct devlink_info_req *req,
 +                                      const char *version_name,
 +                                      const char *version_value,
 +                                      enum devlink_info_version_type version_type)
 +{
 +      return devlink_info_version_put(req, DEVLINK_ATTR_INFO_VERSION_STORED,
 +                                      version_name, version_value,
 +                                      version_type);
 +}
 +EXPORT_SYMBOL_GPL(devlink_info_version_stored_put_ext);
 +
 +int devlink_info_version_running_put(struct devlink_info_req *req,
 +                                   const char *version_name,
 +                                   const char *version_value)
 +{
 +      return devlink_info_version_put(req, DEVLINK_ATTR_INFO_VERSION_RUNNING,
 +                                      version_name, version_value,
 +                                      DEVLINK_INFO_VERSION_TYPE_NONE);
 +}
 +EXPORT_SYMBOL_GPL(devlink_info_version_running_put);
 +
 +int devlink_info_version_running_put_ext(struct devlink_info_req *req,
 +                                       const char *version_name,
 +                                       const char *version_value,
 +                                       enum devlink_info_version_type version_type)
 +{
 +      return devlink_info_version_put(req, DEVLINK_ATTR_INFO_VERSION_RUNNING,
 +                                      version_name, version_value,
 +                                      version_type);
 +}
 +EXPORT_SYMBOL_GPL(devlink_info_version_running_put_ext);
 +
 +static int devlink_nl_driver_info_get(struct device_driver *drv,
 +                                    struct devlink_info_req *req)
 +{
 +      if (!drv)
 +              return 0;
 +
 +      if (drv->name[0])
 +              return nla_put_string(req->msg, DEVLINK_ATTR_INFO_DRIVER_NAME,
 +                                    drv->name);
 +
 +      return 0;
 +}
 +
 +static int
 +devlink_nl_info_fill(struct sk_buff *msg, struct devlink *devlink,
 +                   enum devlink_command cmd, u32 portid,
 +                   u32 seq, int flags, struct netlink_ext_ack *extack)
 +{
 +      struct device *dev = devlink_to_dev(devlink);
 +      struct devlink_info_req req = {};
 +      void *hdr;
 +      int err;
 +
 +      hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
 +      if (!hdr)
 +              return -EMSGSIZE;
 +
 +      err = -EMSGSIZE;
 +      if (devlink_nl_put_handle(msg, devlink))
 +              goto err_cancel_msg;
 +
 +      req.msg = msg;
 +      if (devlink->ops->info_get) {
 +              err = devlink->ops->info_get(devlink, &req, extack);
 +              if (err)
 +                      goto err_cancel_msg;
 +      }
 +
 +      err = devlink_nl_driver_info_get(dev->driver, &req);
 +      if (err)
 +              goto err_cancel_msg;
 +
 +      genlmsg_end(msg, hdr);
 +      return 0;
 +
 +err_cancel_msg:
 +      genlmsg_cancel(msg, hdr);
 +      return err;
 +}
 +
 +int devlink_nl_cmd_info_get_doit(struct sk_buff *skb, struct genl_info *info)
 +{
 +      struct devlink *devlink = info->user_ptr[0];
 +      struct sk_buff *msg;
 +      int err;
 +
 +      msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 +      if (!msg)
 +              return -ENOMEM;
 +
 +      err = devlink_nl_info_fill(msg, devlink, DEVLINK_CMD_INFO_GET,
 +                                 info->snd_portid, info->snd_seq, 0,
 +                                 info->extack);
 +      if (err) {
 +              nlmsg_free(msg);
 +              return err;
 +      }
 +
 +      return genlmsg_reply(msg, info);
 +}
 +
 +static int
 +devlink_nl_cmd_info_get_dump_one(struct sk_buff *msg, struct devlink *devlink,
 +                               struct netlink_callback *cb)
 +{
 +      int err;
 +
 +      err = devlink_nl_info_fill(msg, devlink, DEVLINK_CMD_INFO_GET,
 +                                 NETLINK_CB(cb->skb).portid,
 +                                 cb->nlh->nlmsg_seq, NLM_F_MULTI,
 +                                 cb->extack);
 +      if (err == -EOPNOTSUPP)
 +              err = 0;
 +      return err;
 +}
 +
 +const struct devlink_cmd devl_cmd_info_get = {
 +      .dump_one               = devlink_nl_cmd_info_get_dump_one,
 +};
 +
 +static int devlink_nl_flash_update_fill(struct sk_buff *msg,
 +                                      struct devlink *devlink,
 +                                      enum devlink_command cmd,
 +                                      struct devlink_flash_notify *params)
 +{
 +      void *hdr;
 +
 +      hdr = genlmsg_put(msg, 0, 0, &devlink_nl_family, 0, cmd);
 +      if (!hdr)
 +              return -EMSGSIZE;
 +
 +      if (devlink_nl_put_handle(msg, devlink))
 +              goto nla_put_failure;
 +
 +      if (cmd != DEVLINK_CMD_FLASH_UPDATE_STATUS)
 +              goto out;
 +
 +      if (params->status_msg &&
 +          nla_put_string(msg, DEVLINK_ATTR_FLASH_UPDATE_STATUS_MSG,
 +                         params->status_msg))
 +              goto nla_put_failure;
 +      if (params->component &&
 +          nla_put_string(msg, DEVLINK_ATTR_FLASH_UPDATE_COMPONENT,
 +                         params->component))
 +              goto nla_put_failure;
 +      if (nla_put_u64_64bit(msg, DEVLINK_ATTR_FLASH_UPDATE_STATUS_DONE,
 +                            params->done, DEVLINK_ATTR_PAD))
 +              goto nla_put_failure;
 +      if (nla_put_u64_64bit(msg, DEVLINK_ATTR_FLASH_UPDATE_STATUS_TOTAL,
 +                            params->total, DEVLINK_ATTR_PAD))
 +              goto nla_put_failure;
 +      if (nla_put_u64_64bit(msg, DEVLINK_ATTR_FLASH_UPDATE_STATUS_TIMEOUT,
 +                            params->timeout, DEVLINK_ATTR_PAD))
 +              goto nla_put_failure;
 +
 +out:
 +      genlmsg_end(msg, hdr);
 +      return 0;
 +
 +nla_put_failure:
 +      genlmsg_cancel(msg, hdr);
 +      return -EMSGSIZE;
 +}
 +
 +static void __devlink_flash_update_notify(struct devlink *devlink,
 +                                        enum devlink_command cmd,
 +                                        struct devlink_flash_notify *params)
 +{
 +      struct sk_buff *msg;
 +      int err;
 +
 +      WARN_ON(cmd != DEVLINK_CMD_FLASH_UPDATE &&
 +              cmd != DEVLINK_CMD_FLASH_UPDATE_END &&
 +              cmd != DEVLINK_CMD_FLASH_UPDATE_STATUS);
 +
 +      if (!xa_get_mark(&devlinks, devlink->index, DEVLINK_REGISTERED))
 +              return;
 +
 +      msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 +      if (!msg)
 +              return;
 +
 +      err = devlink_nl_flash_update_fill(msg, devlink, cmd, params);
 +      if (err)
 +              goto out_free_msg;
 +
 +      genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink),
 +                              msg, 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL);
 +      return;
 +
 +out_free_msg:
 +      nlmsg_free(msg);
 +}
 +
 +static void devlink_flash_update_begin_notify(struct devlink *devlink)
 +{
 +      struct devlink_flash_notify params = {};
 +
 +      __devlink_flash_update_notify(devlink,
 +                                    DEVLINK_CMD_FLASH_UPDATE,
 +                                    &params);
 +}
 +
 +static void devlink_flash_update_end_notify(struct devlink *devlink)
 +{
 +      struct devlink_flash_notify params = {};
 +
 +      __devlink_flash_update_notify(devlink,
 +                                    DEVLINK_CMD_FLASH_UPDATE_END,
 +                                    &params);
 +}
 +
 +void devlink_flash_update_status_notify(struct devlink *devlink,
 +                                      const char *status_msg,
 +                                      const char *component,
 +                                      unsigned long done,
 +                                      unsigned long total)
 +{
 +      struct devlink_flash_notify params = {
 +              .status_msg = status_msg,
 +              .component = component,
 +              .done = done,
 +              .total = total,
 +      };
 +
 +      __devlink_flash_update_notify(devlink,
 +                                    DEVLINK_CMD_FLASH_UPDATE_STATUS,
 +                                    &params);
 +}
 +EXPORT_SYMBOL_GPL(devlink_flash_update_status_notify);
 +
 +void devlink_flash_update_timeout_notify(struct devlink *devlink,
 +                                       const char *status_msg,
 +                                       const char *component,
 +                                       unsigned long timeout)
 +{
 +      struct devlink_flash_notify params = {
 +              .status_msg = status_msg,
 +              .component = component,
 +              .timeout = timeout,
 +      };
 +
 +      __devlink_flash_update_notify(devlink,
 +                                    DEVLINK_CMD_FLASH_UPDATE_STATUS,
 +                                    &params);
 +}
 +EXPORT_SYMBOL_GPL(devlink_flash_update_timeout_notify);
 +
 +struct devlink_flash_component_lookup_ctx {
 +      const char *lookup_name;
 +      bool lookup_name_found;
 +};
 +
 +static void
 +devlink_flash_component_lookup_cb(const char *version_name,
 +                                enum devlink_info_version_type version_type,
 +                                void *version_cb_priv)
 +{
 +      struct devlink_flash_component_lookup_ctx *lookup_ctx = version_cb_priv;
 +
 +      if (version_type != DEVLINK_INFO_VERSION_TYPE_COMPONENT ||
 +          lookup_ctx->lookup_name_found)
 +              return;
 +
 +      lookup_ctx->lookup_name_found =
 +              !strcmp(lookup_ctx->lookup_name, version_name);
 +}
 +
 +static int devlink_flash_component_get(struct devlink *devlink,
 +                                     struct nlattr *nla_component,
 +                                     const char **p_component,
 +                                     struct netlink_ext_ack *extack)
 +{
 +      struct devlink_flash_component_lookup_ctx lookup_ctx = {};
 +      struct devlink_info_req req = {};
 +      const char *component;
 +      int ret;
 +
 +      if (!nla_component)
 +              return 0;
 +
 +      component = nla_data(nla_component);
 +
 +      if (!devlink->ops->info_get) {
 +              NL_SET_ERR_MSG_ATTR(extack, nla_component,
 +                                  "component update is not supported by this device");
 +              return -EOPNOTSUPP;
 +      }
 +
 +      lookup_ctx.lookup_name = component;
 +      req.version_cb = devlink_flash_component_lookup_cb;
 +      req.version_cb_priv = &lookup_ctx;
 +
 +      ret = devlink->ops->info_get(devlink, &req, NULL);
 +      if (ret)
 +              return ret;
 +
 +      if (!lookup_ctx.lookup_name_found) {
 +              NL_SET_ERR_MSG_ATTR(extack, nla_component,
 +                                  "selected component is not supported by this device");
 +              return -EINVAL;
 +      }
 +      *p_component = component;
 +      return 0;
 +}
 +
 +int devlink_nl_cmd_flash_update(struct sk_buff *skb, struct genl_info *info)
 +{
 +      struct nlattr *nla_overwrite_mask, *nla_file_name;
 +      struct devlink_flash_update_params params = {};
 +      struct devlink *devlink = info->user_ptr[0];
 +      const char *file_name;
 +      u32 supported_params;
 +      int ret;
 +
 +      if (!devlink->ops->flash_update)
 +              return -EOPNOTSUPP;
 +
 +      if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_FLASH_UPDATE_FILE_NAME))
 +              return -EINVAL;
 +
 +      ret = devlink_flash_component_get(devlink,
 +                                        info->attrs[DEVLINK_ATTR_FLASH_UPDATE_COMPONENT],
 +                                        &params.component, info->extack);
 +      if (ret)
 +              return ret;
 +
 +      supported_params = devlink->ops->supported_flash_update_params;
 +
 +      nla_overwrite_mask = info->attrs[DEVLINK_ATTR_FLASH_UPDATE_OVERWRITE_MASK];
 +      if (nla_overwrite_mask) {
 +              struct nla_bitfield32 sections;
 +
 +              if (!(supported_params & DEVLINK_SUPPORT_FLASH_UPDATE_OVERWRITE_MASK)) {
 +                      NL_SET_ERR_MSG_ATTR(info->extack, nla_overwrite_mask,
 +                                          "overwrite settings are not supported by this device");
 +                      return -EOPNOTSUPP;
 +              }
 +              sections = nla_get_bitfield32(nla_overwrite_mask);
 +              params.overwrite_mask = sections.value & sections.selector;
 +      }
 +
 +      nla_file_name = info->attrs[DEVLINK_ATTR_FLASH_UPDATE_FILE_NAME];
 +      file_name = nla_data(nla_file_name);
 +      ret = request_firmware(&params.fw, file_name, devlink->dev);
 +      if (ret) {
 +              NL_SET_ERR_MSG_ATTR(info->extack, nla_file_name,
 +                                  "failed to locate the requested firmware file");
 +              return ret;
 +      }
 +
 +      devlink_flash_update_begin_notify(devlink);
 +      ret = devlink->ops->flash_update(devlink, &params, info->extack);
 +      devlink_flash_update_end_notify(devlink);
 +
 +      release_firmware(params.fw);
 +
 +      return ret;
 +}
 +
 +static void __devlink_compat_running_version(struct devlink *devlink,
 +                                           char *buf, size_t len)
 +{
 +      struct devlink_info_req req = {};
 +      const struct nlattr *nlattr;
 +      struct sk_buff *msg;
 +      int rem, err;
 +
 +      msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 +      if (!msg)
 +              return;
 +
 +      req.msg = msg;
 +      err = devlink->ops->info_get(devlink, &req, NULL);
 +      if (err)
 +              goto free_msg;
 +
 +      nla_for_each_attr(nlattr, (void *)msg->data, msg->len, rem) {
 +              const struct nlattr *kv;
 +              int rem_kv;
 +
 +              if (nla_type(nlattr) != DEVLINK_ATTR_INFO_VERSION_RUNNING)
 +                      continue;
 +
 +              nla_for_each_nested(kv, nlattr, rem_kv) {
 +                      if (nla_type(kv) != DEVLINK_ATTR_INFO_VERSION_VALUE)
 +                              continue;
 +
 +                      strlcat(buf, nla_data(kv), len);
 +                      strlcat(buf, " ", len);
 +              }
 +      }
 +free_msg:
 +      nlmsg_free(msg);
 +}
 +
 +void devlink_compat_running_version(struct devlink *devlink,
 +                                  char *buf, size_t len)
 +{
 +      if (!devlink->ops->info_get)
 +              return;
 +
 +      devl_lock(devlink);
 +      if (devl_is_registered(devlink))
 +              __devlink_compat_running_version(devlink, buf, len);
 +      devl_unlock(devlink);
 +}
 +
 +int devlink_compat_flash_update(struct devlink *devlink, const char *file_name)
 +{
 +      struct devlink_flash_update_params params = {};
 +      int ret;
 +
 +      devl_lock(devlink);
 +      if (!devl_is_registered(devlink)) {
 +              ret = -ENODEV;
 +              goto out_unlock;
 +      }
 +
 +      if (!devlink->ops->flash_update) {
 +              ret = -EOPNOTSUPP;
 +              goto out_unlock;
 +      }
 +
 +      ret = request_firmware(&params.fw, file_name, devlink->dev);
 +      if (ret)
 +              goto out_unlock;
 +
 +      devlink_flash_update_begin_notify(devlink);
 +      ret = devlink->ops->flash_update(devlink, &params, NULL);
 +      devlink_flash_update_end_notify(devlink);
 +
 +      release_firmware(params.fw);
 +out_unlock:
 +      devl_unlock(devlink);
 +
 +      return ret;
 +}
 +
 +static int
 +devlink_nl_selftests_fill(struct sk_buff *msg, struct devlink *devlink,
 +                        u32 portid, u32 seq, int flags,
 +                        struct netlink_ext_ack *extack)
 +{
 +      struct nlattr *selftests;
 +      void *hdr;
 +      int err;
 +      int i;
 +
 +      hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags,
 +                        DEVLINK_CMD_SELFTESTS_GET);
 +      if (!hdr)
 +              return -EMSGSIZE;
 +
 +      err = -EMSGSIZE;
 +      if (devlink_nl_put_handle(msg, devlink))
 +              goto err_cancel_msg;
 +
 +      selftests = nla_nest_start(msg, DEVLINK_ATTR_SELFTESTS);
 +      if (!selftests)
 +              goto err_cancel_msg;
 +
 +      for (i = DEVLINK_ATTR_SELFTEST_ID_UNSPEC + 1;
 +           i <= DEVLINK_ATTR_SELFTEST_ID_MAX; i++) {
 +              if (devlink->ops->selftest_check(devlink, i, extack)) {
 +                      err = nla_put_flag(msg, i);
 +                      if (err)
 +                              goto err_cancel_msg;
 +              }
 +      }
 +
 +      nla_nest_end(msg, selftests);
 +      genlmsg_end(msg, hdr);
 +      return 0;
 +
 +err_cancel_msg:
 +      genlmsg_cancel(msg, hdr);
 +      return err;
 +}
 +
 +int devlink_nl_cmd_selftests_get_doit(struct sk_buff *skb,
 +                                    struct genl_info *info)
 +{
 +      struct devlink *devlink = info->user_ptr[0];
 +      struct sk_buff *msg;
 +      int err;
 +
 +      if (!devlink->ops->selftest_check)
 +              return -EOPNOTSUPP;
 +
 +      msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 +      if (!msg)
 +              return -ENOMEM;
 +
 +      err = devlink_nl_selftests_fill(msg, devlink, info->snd_portid,
 +                                      info->snd_seq, 0, info->extack);
 +      if (err) {
 +              nlmsg_free(msg);
 +              return err;
 +      }
 +
 +      return genlmsg_reply(msg, info);
 +}
 +
 +static int
 +devlink_nl_cmd_selftests_get_dump_one(struct sk_buff *msg,
 +                                    struct devlink *devlink,
 +                                    struct netlink_callback *cb)
 +{
 +      if (!devlink->ops->selftest_check)
 +              return 0;
 +
 +      return devlink_nl_selftests_fill(msg, devlink,
 +                                       NETLINK_CB(cb->skb).portid,
 +                                       cb->nlh->nlmsg_seq, NLM_F_MULTI,
 +                                       cb->extack);
 +}
 +
 +const struct devlink_cmd devl_cmd_selftests_get = {
 +      .dump_one               = devlink_nl_cmd_selftests_get_dump_one,
 +};
 +
 +static int devlink_selftest_result_put(struct sk_buff *skb, unsigned int id,
 +                                     enum devlink_selftest_status test_status)
 +{
 +      struct nlattr *result_attr;
 +
 +      result_attr = nla_nest_start(skb, DEVLINK_ATTR_SELFTEST_RESULT);
 +      if (!result_attr)
 +              return -EMSGSIZE;
 +
 +      if (nla_put_u32(skb, DEVLINK_ATTR_SELFTEST_RESULT_ID, id) ||
 +          nla_put_u8(skb, DEVLINK_ATTR_SELFTEST_RESULT_STATUS,
 +                     test_status))
 +              goto nla_put_failure;
 +
 +      nla_nest_end(skb, result_attr);
 +      return 0;
 +
 +nla_put_failure:
 +      nla_nest_cancel(skb, result_attr);
 +      return -EMSGSIZE;
 +}
 +
 +static const struct nla_policy devlink_selftest_nl_policy[DEVLINK_ATTR_SELFTEST_ID_MAX + 1] = {
 +      [DEVLINK_ATTR_SELFTEST_ID_FLASH] = { .type = NLA_FLAG },
 +};
 +
 +int devlink_nl_cmd_selftests_run(struct sk_buff *skb, struct genl_info *info)
 +{
 +      struct nlattr *tb[DEVLINK_ATTR_SELFTEST_ID_MAX + 1];
 +      struct devlink *devlink = info->user_ptr[0];
 +      struct nlattr *attrs, *selftests;
 +      struct sk_buff *msg;
 +      void *hdr;
 +      int err;
 +      int i;
 +
 +      if (!devlink->ops->selftest_run || !devlink->ops->selftest_check)
 +              return -EOPNOTSUPP;
 +
 +      if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_SELFTESTS))
 +              return -EINVAL;
 +
 +      attrs = info->attrs[DEVLINK_ATTR_SELFTESTS];
 +
 +      err = nla_parse_nested(tb, DEVLINK_ATTR_SELFTEST_ID_MAX, attrs,
 +                             devlink_selftest_nl_policy, info->extack);
 +      if (err < 0)
 +              return err;
 +
 +      msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 +      if (!msg)
 +              return -ENOMEM;
 +
 +      err = -EMSGSIZE;
 +      hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq,
 +                        &devlink_nl_family, 0, DEVLINK_CMD_SELFTESTS_RUN);
 +      if (!hdr)
 +              goto free_msg;
 +
 +      if (devlink_nl_put_handle(msg, devlink))
 +              goto genlmsg_cancel;
 +
 +      selftests = nla_nest_start(msg, DEVLINK_ATTR_SELFTESTS);
 +      if (!selftests)
 +              goto genlmsg_cancel;
 +
 +      for (i = DEVLINK_ATTR_SELFTEST_ID_UNSPEC + 1;
 +           i <= DEVLINK_ATTR_SELFTEST_ID_MAX; i++) {
 +              enum devlink_selftest_status test_status;
 +
 +              if (nla_get_flag(tb[i])) {
 +                      if (!devlink->ops->selftest_check(devlink, i,
 +                                                        info->extack)) {
 +                              if (devlink_selftest_result_put(msg, i,
 +                                                              DEVLINK_SELFTEST_STATUS_SKIP))
 +                                      goto selftests_nest_cancel;
 +                              continue;
 +                      }
 +
 +                      test_status = devlink->ops->selftest_run(devlink, i,
 +                                                               info->extack);
 +                      if (devlink_selftest_result_put(msg, i, test_status))
 +                              goto selftests_nest_cancel;
 +              }
 +      }
 +
 +      nla_nest_end(msg, selftests);
 +      genlmsg_end(msg, hdr);
 +      return genlmsg_reply(msg, info);
 +
 +selftests_nest_cancel:
 +      nla_nest_cancel(msg, selftests);
 +genlmsg_cancel:
 +      genlmsg_cancel(msg, hdr);
 +free_msg:
 +      nlmsg_free(msg);
 +      return err;
 +}
index dffca2f9bfa7ff0a6982ba33b577e1e3f3d6c727,0bfc144df8b991ca15b2e91d5194746ce7936546..8fc85ba3f6e077819beab83976f7ab11dc094135
@@@ -3837,271 -4584,2433 +3837,284 @@@ int devlink_resources_validate(struct d
        return err;
  }
  
 -static struct net *devlink_netns_get(struct sk_buff *skb,
 -                                   struct genl_info *info)
 -{
 -      struct nlattr *netns_pid_attr = info->attrs[DEVLINK_ATTR_NETNS_PID];
 -      struct nlattr *netns_fd_attr = info->attrs[DEVLINK_ATTR_NETNS_FD];
 -      struct nlattr *netns_id_attr = info->attrs[DEVLINK_ATTR_NETNS_ID];
 -      struct net *net;
++static void devlink_param_notify(struct devlink *devlink,
++                               unsigned int port_index,
++                               struct devlink_param_item *param_item,
++                               enum devlink_command cmd);
 -      if (!!netns_pid_attr + !!netns_fd_attr + !!netns_id_attr > 1) {
 -              NL_SET_ERR_MSG_MOD(info->extack, "multiple netns identifying attributes specified");
 -              return ERR_PTR(-EINVAL);
 -      }
 -
 -      if (netns_pid_attr) {
 -              net = get_net_ns_by_pid(nla_get_u32(netns_pid_attr));
 -      } else if (netns_fd_attr) {
 -              net = get_net_ns_by_fd(nla_get_u32(netns_fd_attr));
 -      } else if (netns_id_attr) {
 -              net = get_net_ns_by_id(sock_net(skb->sk),
 -                                     nla_get_u32(netns_id_attr));
 -              if (!net)
 -                      net = ERR_PTR(-EINVAL);
 -      } else {
 -              WARN_ON(1);
 -              net = ERR_PTR(-EINVAL);
 -      }
 -      if (IS_ERR(net)) {
 -              NL_SET_ERR_MSG_MOD(info->extack, "Unknown network namespace");
 -              return ERR_PTR(-EINVAL);
 -      }
 -      if (!netlink_ns_capable(skb, net->user_ns, CAP_NET_ADMIN)) {
 -              put_net(net);
 -              return ERR_PTR(-EPERM);
 -      }
 -      return net;
 -}
 -
 -static void devlink_param_notify(struct devlink *devlink,
 -                               unsigned int port_index,
 -                               struct devlink_param_item *param_item,
 -                               enum devlink_command cmd);
 -
 -static void devlink_ns_change_notify(struct devlink *devlink,
 -                                   struct net *dest_net, struct net *curr_net,
 -                                   bool new)
 -{
 -      struct devlink_param_item *param_item;
 -      enum devlink_command cmd;
 -
 -      /* Userspace needs to be notified about devlink objects
 -       * removed from original and entering new network namespace.
 -       * The rest of the devlink objects are re-created during
 -       * reload process so the notifications are generated separatelly.
 -       */
 -
 -      if (!dest_net || net_eq(dest_net, curr_net))
 -              return;
 -
 -      if (new)
 -              devlink_notify(devlink, DEVLINK_CMD_NEW);
 -
 -      cmd = new ? DEVLINK_CMD_PARAM_NEW : DEVLINK_CMD_PARAM_DEL;
 -      list_for_each_entry(param_item, &devlink->param_list, list)
 -              devlink_param_notify(devlink, 0, param_item, cmd);
 -
 -      if (!new)
 -              devlink_notify(devlink, DEVLINK_CMD_DEL);
 -}
 -
 -static bool devlink_reload_supported(const struct devlink_ops *ops)
 -{
 -      return ops->reload_down && ops->reload_up;
 -}
 -
 -static void devlink_reload_failed_set(struct devlink *devlink,
 -                                    bool reload_failed)
 -{
 -      if (devlink->reload_failed == reload_failed)
 -              return;
 -      devlink->reload_failed = reload_failed;
 -      devlink_notify(devlink, DEVLINK_CMD_NEW);
 -}
 -
 -bool devlink_is_reload_failed(const struct devlink *devlink)
 -{
 -      return devlink->reload_failed;
 -}
 -EXPORT_SYMBOL_GPL(devlink_is_reload_failed);
 -
 -static void
 -__devlink_reload_stats_update(struct devlink *devlink, u32 *reload_stats,
 -                            enum devlink_reload_limit limit, u32 actions_performed)
 -{
 -      unsigned long actions = actions_performed;
 -      int stat_idx;
 -      int action;
 -
 -      for_each_set_bit(action, &actions, __DEVLINK_RELOAD_ACTION_MAX) {
 -              stat_idx = limit * __DEVLINK_RELOAD_ACTION_MAX + action;
 -              reload_stats[stat_idx]++;
 -      }
 -      devlink_notify(devlink, DEVLINK_CMD_NEW);
 -}
 -
 -static void
 -devlink_reload_stats_update(struct devlink *devlink, enum devlink_reload_limit limit,
 -                          u32 actions_performed)
 -{
 -      __devlink_reload_stats_update(devlink, devlink->stats.reload_stats, limit,
 -                                    actions_performed);
 -}
 -
 -/**
 - *    devlink_remote_reload_actions_performed - Update devlink on reload actions
 - *      performed which are not a direct result of devlink reload call.
 - *
 - *    This should be called by a driver after performing reload actions in case it was not
 - *    a result of devlink reload call. For example fw_activate was performed as a result
 - *    of devlink reload triggered fw_activate on another host.
 - *    The motivation for this function is to keep data on reload actions performed on this
 - *    function whether it was done due to direct devlink reload call or not.
 - *
 - *    @devlink: devlink
 - *    @limit: reload limit
 - *    @actions_performed: bitmask of actions performed
 - */
 -void devlink_remote_reload_actions_performed(struct devlink *devlink,
 -                                           enum devlink_reload_limit limit,
 -                                           u32 actions_performed)
 -{
 -      if (WARN_ON(!actions_performed ||
 -                  actions_performed & BIT(DEVLINK_RELOAD_ACTION_UNSPEC) ||
 -                  actions_performed >= BIT(__DEVLINK_RELOAD_ACTION_MAX) ||
 -                  limit > DEVLINK_RELOAD_LIMIT_MAX))
 -              return;
 -
 -      __devlink_reload_stats_update(devlink, devlink->stats.remote_reload_stats, limit,
 -                                    actions_performed);
 -}
 -EXPORT_SYMBOL_GPL(devlink_remote_reload_actions_performed);
 -
 -static int devlink_reload(struct devlink *devlink, struct net *dest_net,
 -                        enum devlink_reload_action action, enum devlink_reload_limit limit,
 -                        u32 *actions_performed, struct netlink_ext_ack *extack)
 -{
 -      u32 remote_reload_stats[DEVLINK_RELOAD_STATS_ARRAY_SIZE];
 -      struct net *curr_net;
 -      int err;
 -
 -      memcpy(remote_reload_stats, devlink->stats.remote_reload_stats,
 -             sizeof(remote_reload_stats));
 -
 -      curr_net = devlink_net(devlink);
 -      devlink_ns_change_notify(devlink, dest_net, curr_net, false);
 -      err = devlink->ops->reload_down(devlink, !!dest_net, action, limit, extack);
 -      if (err)
 -              return err;
 -
 -      if (dest_net && !net_eq(dest_net, curr_net))
 -              write_pnet(&devlink->_net, dest_net);
 -
 -      err = devlink->ops->reload_up(devlink, action, limit, actions_performed, extack);
 -      devlink_reload_failed_set(devlink, !!err);
 -      if (err)
 -              return err;
 -
 -      devlink_ns_change_notify(devlink, dest_net, curr_net, true);
 -      WARN_ON(!(*actions_performed & BIT(action)));
 -      /* Catch driver on updating the remote action within devlink reload */
 -      WARN_ON(memcmp(remote_reload_stats, devlink->stats.remote_reload_stats,
 -                     sizeof(remote_reload_stats)));
 -      devlink_reload_stats_update(devlink, limit, *actions_performed);
 -      return 0;
 -}
 -
 -static int
 -devlink_nl_reload_actions_performed_snd(struct devlink *devlink, u32 actions_performed,
 -                                      enum devlink_command cmd, struct genl_info *info)
 -{
++struct devlink_info_req {
+       struct sk_buff *msg;
 -      void *hdr;
 -
 -      msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 -      if (!msg)
 -              return -ENOMEM;
 -
 -      hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq, &devlink_nl_family, 0, cmd);
 -      if (!hdr)
 -              goto free_msg;
 -
 -      if (devlink_nl_put_handle(msg, devlink))
 -              goto nla_put_failure;
 -
 -      if (nla_put_bitfield32(msg, DEVLINK_ATTR_RELOAD_ACTIONS_PERFORMED, actions_performed,
 -                             actions_performed))
 -              goto nla_put_failure;
 -      genlmsg_end(msg, hdr);
 -
 -      return genlmsg_reply(msg, info);
++      void (*version_cb)(const char *version_name,
++                         enum devlink_info_version_type version_type,
++                         void *version_cb_priv);
++      void *version_cb_priv;
++};
 -nla_put_failure:
 -      genlmsg_cancel(msg, hdr);
 -free_msg:
 -      nlmsg_free(msg);
 -      return -EMSGSIZE;
 -}
 +static const struct devlink_param devlink_param_generic[] = {
 +      {
 +              .id = DEVLINK_PARAM_GENERIC_ID_INT_ERR_RESET,
 +              .name = DEVLINK_PARAM_GENERIC_INT_ERR_RESET_NAME,
 +              .type = DEVLINK_PARAM_GENERIC_INT_ERR_RESET_TYPE,
 +      },
 +      {
 +              .id = DEVLINK_PARAM_GENERIC_ID_MAX_MACS,
 +              .name = DEVLINK_PARAM_GENERIC_MAX_MACS_NAME,
 +              .type = DEVLINK_PARAM_GENERIC_MAX_MACS_TYPE,
 +      },
 +      {
 +              .id = DEVLINK_PARAM_GENERIC_ID_ENABLE_SRIOV,
 +              .name = DEVLINK_PARAM_GENERIC_ENABLE_SRIOV_NAME,
 +              .type = DEVLINK_PARAM_GENERIC_ENABLE_SRIOV_TYPE,
 +      },
 +      {
 +              .id = DEVLINK_PARAM_GENERIC_ID_REGION_SNAPSHOT,
 +              .name = DEVLINK_PARAM_GENERIC_REGION_SNAPSHOT_NAME,
 +              .type = DEVLINK_PARAM_GENERIC_REGION_SNAPSHOT_TYPE,
 +      },
 +      {
 +              .id = DEVLINK_PARAM_GENERIC_ID_IGNORE_ARI,
 +              .name = DEVLINK_PARAM_GENERIC_IGNORE_ARI_NAME,
 +              .type = DEVLINK_PARAM_GENERIC_IGNORE_ARI_TYPE,
 +      },
 +      {
 +              .id = DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MAX,
 +              .name = DEVLINK_PARAM_GENERIC_MSIX_VEC_PER_PF_MAX_NAME,
 +              .type = DEVLINK_PARAM_GENERIC_MSIX_VEC_PER_PF_MAX_TYPE,
 +      },
 +      {
 +              .id = DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MIN,
 +              .name = DEVLINK_PARAM_GENERIC_MSIX_VEC_PER_PF_MIN_NAME,
 +              .type = DEVLINK_PARAM_GENERIC_MSIX_VEC_PER_PF_MIN_TYPE,
 +      },
 +      {
 +              .id = DEVLINK_PARAM_GENERIC_ID_FW_LOAD_POLICY,
 +              .name = DEVLINK_PARAM_GENERIC_FW_LOAD_POLICY_NAME,
 +              .type = DEVLINK_PARAM_GENERIC_FW_LOAD_POLICY_TYPE,
 +      },
 +      {
 +              .id = DEVLINK_PARAM_GENERIC_ID_RESET_DEV_ON_DRV_PROBE,
 +              .name = DEVLINK_PARAM_GENERIC_RESET_DEV_ON_DRV_PROBE_NAME,
 +              .type = DEVLINK_PARAM_GENERIC_RESET_DEV_ON_DRV_PROBE_TYPE,
 +      },
 +      {
 +              .id = DEVLINK_PARAM_GENERIC_ID_ENABLE_ROCE,
 +              .name = DEVLINK_PARAM_GENERIC_ENABLE_ROCE_NAME,
 +              .type = DEVLINK_PARAM_GENERIC_ENABLE_ROCE_TYPE,
 +      },
 +      {
 +              .id = DEVLINK_PARAM_GENERIC_ID_ENABLE_REMOTE_DEV_RESET,
 +              .name = DEVLINK_PARAM_GENERIC_ENABLE_REMOTE_DEV_RESET_NAME,
 +              .type = DEVLINK_PARAM_GENERIC_ENABLE_REMOTE_DEV_RESET_TYPE,
 +      },
 +      {
 +              .id = DEVLINK_PARAM_GENERIC_ID_ENABLE_ETH,
 +              .name = DEVLINK_PARAM_GENERIC_ENABLE_ETH_NAME,
 +              .type = DEVLINK_PARAM_GENERIC_ENABLE_ETH_TYPE,
 +      },
 +      {
 +              .id = DEVLINK_PARAM_GENERIC_ID_ENABLE_RDMA,
 +              .name = DEVLINK_PARAM_GENERIC_ENABLE_RDMA_NAME,
 +              .type = DEVLINK_PARAM_GENERIC_ENABLE_RDMA_TYPE,
 +      },
 +      {
 +              .id = DEVLINK_PARAM_GENERIC_ID_ENABLE_VNET,
 +              .name = DEVLINK_PARAM_GENERIC_ENABLE_VNET_NAME,
 +              .type = DEVLINK_PARAM_GENERIC_ENABLE_VNET_TYPE,
 +      },
 +      {
 +              .id = DEVLINK_PARAM_GENERIC_ID_ENABLE_IWARP,
 +              .name = DEVLINK_PARAM_GENERIC_ENABLE_IWARP_NAME,
 +              .type = DEVLINK_PARAM_GENERIC_ENABLE_IWARP_TYPE,
 +      },
 +      {
 +              .id = DEVLINK_PARAM_GENERIC_ID_IO_EQ_SIZE,
 +              .name = DEVLINK_PARAM_GENERIC_IO_EQ_SIZE_NAME,
 +              .type = DEVLINK_PARAM_GENERIC_IO_EQ_SIZE_TYPE,
 +      },
 +      {
 +              .id = DEVLINK_PARAM_GENERIC_ID_EVENT_EQ_SIZE,
 +              .name = DEVLINK_PARAM_GENERIC_EVENT_EQ_SIZE_NAME,
 +              .type = DEVLINK_PARAM_GENERIC_EVENT_EQ_SIZE_TYPE,
 +      },
 +};
  
 -static int devlink_nl_cmd_reload(struct sk_buff *skb, struct genl_info *info)
 +static int devlink_param_generic_verify(const struct devlink_param *param)
  {
 -      struct devlink *devlink = info->user_ptr[0];
 -      enum devlink_reload_action action;
 -      enum devlink_reload_limit limit;
 -      struct net *dest_net = NULL;
 -      u32 actions_performed;
 -      int err;
 -
 -      if (!(devlink->features & DEVLINK_F_RELOAD))
 -              return -EOPNOTSUPP;
 -
 -      err = devlink_resources_validate(devlink, NULL, info);
 -      if (err) {
 -              NL_SET_ERR_MSG_MOD(info->extack, "resources size validation failed");
 -              return err;
 -      }
 -
 -      if (info->attrs[DEVLINK_ATTR_RELOAD_ACTION])
 -              action = nla_get_u8(info->attrs[DEVLINK_ATTR_RELOAD_ACTION]);
 -      else
 -              action = DEVLINK_RELOAD_ACTION_DRIVER_REINIT;
 -
 -      if (!devlink_reload_action_is_supported(devlink, action)) {
 -              NL_SET_ERR_MSG_MOD(info->extack,
 -                                 "Requested reload action is not supported by the driver");
 -              return -EOPNOTSUPP;
 -      }
 -
 -      limit = DEVLINK_RELOAD_LIMIT_UNSPEC;
 -      if (info->attrs[DEVLINK_ATTR_RELOAD_LIMITS]) {
 -              struct nla_bitfield32 limits;
 -              u32 limits_selected;
 -
 -              limits = nla_get_bitfield32(info->attrs[DEVLINK_ATTR_RELOAD_LIMITS]);
 -              limits_selected = limits.value & limits.selector;
 -              if (!limits_selected) {
 -                      NL_SET_ERR_MSG_MOD(info->extack, "Invalid limit selected");
 -                      return -EINVAL;
 -              }
 -              for (limit = 0 ; limit <= DEVLINK_RELOAD_LIMIT_MAX ; limit++)
 -                      if (limits_selected & BIT(limit))
 -                              break;
 -              /* UAPI enables multiselection, but currently it is not used */
 -              if (limits_selected != BIT(limit)) {
 -                      NL_SET_ERR_MSG_MOD(info->extack,
 -                                         "Multiselection of limit is not supported");
 -                      return -EOPNOTSUPP;
 -              }
 -              if (!devlink_reload_limit_is_supported(devlink, limit)) {
 -                      NL_SET_ERR_MSG_MOD(info->extack,
 -                                         "Requested limit is not supported by the driver");
 -                      return -EOPNOTSUPP;
 -              }
 -              if (devlink_reload_combination_is_invalid(action, limit)) {
 -                      NL_SET_ERR_MSG_MOD(info->extack,
 -                                         "Requested limit is invalid for this action");
 -                      return -EINVAL;
 -              }
 -      }
 -      if (info->attrs[DEVLINK_ATTR_NETNS_PID] ||
 -          info->attrs[DEVLINK_ATTR_NETNS_FD] ||
 -          info->attrs[DEVLINK_ATTR_NETNS_ID]) {
 -              dest_net = devlink_netns_get(skb, info);
 -              if (IS_ERR(dest_net))
 -                      return PTR_ERR(dest_net);
 -      }
 -
 -      err = devlink_reload(devlink, dest_net, action, limit, &actions_performed, info->extack);
 -
 -      if (dest_net)
 -              put_net(dest_net);
 +      /* verify it match generic parameter by id and name */
 +      if (param->id > DEVLINK_PARAM_GENERIC_ID_MAX)
 +              return -EINVAL;
 +      if (strcmp(param->name, devlink_param_generic[param->id].name))
 +              return -ENOENT;
  
 -      if (err)
 -              return err;
 -      /* For backward compatibility generate reply only if attributes used by user */
 -      if (!info->attrs[DEVLINK_ATTR_RELOAD_ACTION] && !info->attrs[DEVLINK_ATTR_RELOAD_LIMITS])
 -              return 0;
 +      WARN_ON(param->type != devlink_param_generic[param->id].type);
  
 -      return devlink_nl_reload_actions_performed_snd(devlink, actions_performed,
 -                                                     DEVLINK_CMD_RELOAD, info);
 +      return 0;
  }
  
 -static int devlink_nl_flash_update_fill(struct sk_buff *msg,
 -                                      struct devlink *devlink,
 -                                      enum devlink_command cmd,
 -                                      struct devlink_flash_notify *params)
 +static int devlink_param_driver_verify(const struct devlink_param *param)
  {
 -      void *hdr;
 -
 -      hdr = genlmsg_put(msg, 0, 0, &devlink_nl_family, 0, cmd);
 -      if (!hdr)
 -              return -EMSGSIZE;
 -
 -      if (devlink_nl_put_handle(msg, devlink))
 -              goto nla_put_failure;
 -
 -      if (cmd != DEVLINK_CMD_FLASH_UPDATE_STATUS)
 -              goto out;
 +      int i;
  
 -      if (params->status_msg &&
 -          nla_put_string(msg, DEVLINK_ATTR_FLASH_UPDATE_STATUS_MSG,
 -                         params->status_msg))
 -              goto nla_put_failure;
 -      if (params->component &&
 -          nla_put_string(msg, DEVLINK_ATTR_FLASH_UPDATE_COMPONENT,
 -                         params->component))
 -              goto nla_put_failure;
 -      if (nla_put_u64_64bit(msg, DEVLINK_ATTR_FLASH_UPDATE_STATUS_DONE,
 -                            params->done, DEVLINK_ATTR_PAD))
 -              goto nla_put_failure;
 -      if (nla_put_u64_64bit(msg, DEVLINK_ATTR_FLASH_UPDATE_STATUS_TOTAL,
 -                            params->total, DEVLINK_ATTR_PAD))
 -              goto nla_put_failure;
 -      if (nla_put_u64_64bit(msg, DEVLINK_ATTR_FLASH_UPDATE_STATUS_TIMEOUT,
 -                            params->timeout, DEVLINK_ATTR_PAD))
 -              goto nla_put_failure;
 +      if (param->id <= DEVLINK_PARAM_GENERIC_ID_MAX)
 +              return -EINVAL;
 +      /* verify no such name in generic params */
 +      for (i = 0; i <= DEVLINK_PARAM_GENERIC_ID_MAX; i++)
 +              if (!strcmp(param->name, devlink_param_generic[i].name))
 +                      return -EEXIST;
  
 -out:
 -      genlmsg_end(msg, hdr);
        return 0;
 -
 -nla_put_failure:
 -      genlmsg_cancel(msg, hdr);
 -      return -EMSGSIZE;
  }
  
 -static void __devlink_flash_update_notify(struct devlink *devlink,
 -                                        enum devlink_command cmd,
 -                                        struct devlink_flash_notify *params)
 +static struct devlink_param_item *
 +devlink_param_find_by_name(struct xarray *params, const char *param_name)
  {
 -      struct sk_buff *msg;
 -      int err;
 -
 -      WARN_ON(cmd != DEVLINK_CMD_FLASH_UPDATE &&
 -              cmd != DEVLINK_CMD_FLASH_UPDATE_END &&
 -              cmd != DEVLINK_CMD_FLASH_UPDATE_STATUS);
 -
 -      if (!xa_get_mark(&devlinks, devlink->index, DEVLINK_REGISTERED))
 -              return;
 -
 -      msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 -      if (!msg)
 -              return;
 -
 -      err = devlink_nl_flash_update_fill(msg, devlink, cmd, params);
 -      if (err)
 -              goto out_free_msg;
 -
 -      genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink),
 -                              msg, 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL);
 -      return;
 -
 -out_free_msg:
 -      nlmsg_free(msg);
 -}
 -
 -static void devlink_flash_update_begin_notify(struct devlink *devlink)
 -{
 -      struct devlink_flash_notify params = {};
 -
 -      __devlink_flash_update_notify(devlink,
 -                                    DEVLINK_CMD_FLASH_UPDATE,
 -                                    &params);
 -}
 -
 -static void devlink_flash_update_end_notify(struct devlink *devlink)
 -{
 -      struct devlink_flash_notify params = {};
 -
 -      __devlink_flash_update_notify(devlink,
 -                                    DEVLINK_CMD_FLASH_UPDATE_END,
 -                                    &params);
 -}
 -
 -void devlink_flash_update_status_notify(struct devlink *devlink,
 -                                      const char *status_msg,
 -                                      const char *component,
 -                                      unsigned long done,
 -                                      unsigned long total)
 -{
 -      struct devlink_flash_notify params = {
 -              .status_msg = status_msg,
 -              .component = component,
 -              .done = done,
 -              .total = total,
 -      };
 -
 -      __devlink_flash_update_notify(devlink,
 -                                    DEVLINK_CMD_FLASH_UPDATE_STATUS,
 -                                    &params);
 -}
 -EXPORT_SYMBOL_GPL(devlink_flash_update_status_notify);
 -
 -void devlink_flash_update_timeout_notify(struct devlink *devlink,
 -                                       const char *status_msg,
 -                                       const char *component,
 -                                       unsigned long timeout)
 -{
 -      struct devlink_flash_notify params = {
 -              .status_msg = status_msg,
 -              .component = component,
 -              .timeout = timeout,
 -      };
 -
 -      __devlink_flash_update_notify(devlink,
 -                                    DEVLINK_CMD_FLASH_UPDATE_STATUS,
 -                                    &params);
 -}
 -EXPORT_SYMBOL_GPL(devlink_flash_update_timeout_notify);
 -
 -struct devlink_info_req {
 -      struct sk_buff *msg;
 -      void (*version_cb)(const char *version_name,
 -                         enum devlink_info_version_type version_type,
 -                         void *version_cb_priv);
 -      void *version_cb_priv;
 -};
 -
 -struct devlink_flash_component_lookup_ctx {
 -      const char *lookup_name;
 -      bool lookup_name_found;
 -};
 -
 -static void
 -devlink_flash_component_lookup_cb(const char *version_name,
 -                                enum devlink_info_version_type version_type,
 -                                void *version_cb_priv)
 -{
 -      struct devlink_flash_component_lookup_ctx *lookup_ctx = version_cb_priv;
 -
 -      if (version_type != DEVLINK_INFO_VERSION_TYPE_COMPONENT ||
 -          lookup_ctx->lookup_name_found)
 -              return;
 -
 -      lookup_ctx->lookup_name_found =
 -              !strcmp(lookup_ctx->lookup_name, version_name);
 -}
 -
 -static int devlink_flash_component_get(struct devlink *devlink,
 -                                     struct nlattr *nla_component,
 -                                     const char **p_component,
 -                                     struct netlink_ext_ack *extack)
 -{
 -      struct devlink_flash_component_lookup_ctx lookup_ctx = {};
 -      struct devlink_info_req req = {};
 -      const char *component;
 -      int ret;
 -
 -      if (!nla_component)
 -              return 0;
 -
 -      component = nla_data(nla_component);
 -
 -      if (!devlink->ops->info_get) {
 -              NL_SET_ERR_MSG_ATTR(extack, nla_component,
 -                                  "component update is not supported by this device");
 -              return -EOPNOTSUPP;
 -      }
 -
 -      lookup_ctx.lookup_name = component;
 -      req.version_cb = devlink_flash_component_lookup_cb;
 -      req.version_cb_priv = &lookup_ctx;
 -
 -      ret = devlink->ops->info_get(devlink, &req, NULL);
 -      if (ret)
 -              return ret;
 -
 -      if (!lookup_ctx.lookup_name_found) {
 -              NL_SET_ERR_MSG_ATTR(extack, nla_component,
 -                                  "selected component is not supported by this device");
 -              return -EINVAL;
 -      }
 -      *p_component = component;
 -      return 0;
 -}
 -
 -static int devlink_nl_cmd_flash_update(struct sk_buff *skb,
 -                                     struct genl_info *info)
 -{
 -      struct nlattr *nla_overwrite_mask, *nla_file_name;
 -      struct devlink_flash_update_params params = {};
 -      struct devlink *devlink = info->user_ptr[0];
 -      const char *file_name;
 -      u32 supported_params;
 -      int ret;
 -
 -      if (!devlink->ops->flash_update)
 -              return -EOPNOTSUPP;
 -
 -      if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_FLASH_UPDATE_FILE_NAME))
 -              return -EINVAL;
 -
 -      ret = devlink_flash_component_get(devlink,
 -                                        info->attrs[DEVLINK_ATTR_FLASH_UPDATE_COMPONENT],
 -                                        &params.component, info->extack);
 -      if (ret)
 -              return ret;
 -
 -      supported_params = devlink->ops->supported_flash_update_params;
 -
 -      nla_overwrite_mask = info->attrs[DEVLINK_ATTR_FLASH_UPDATE_OVERWRITE_MASK];
 -      if (nla_overwrite_mask) {
 -              struct nla_bitfield32 sections;
 -
 -              if (!(supported_params & DEVLINK_SUPPORT_FLASH_UPDATE_OVERWRITE_MASK)) {
 -                      NL_SET_ERR_MSG_ATTR(info->extack, nla_overwrite_mask,
 -                                          "overwrite settings are not supported by this device");
 -                      return -EOPNOTSUPP;
 -              }
 -              sections = nla_get_bitfield32(nla_overwrite_mask);
 -              params.overwrite_mask = sections.value & sections.selector;
 -      }
 -
 -      nla_file_name = info->attrs[DEVLINK_ATTR_FLASH_UPDATE_FILE_NAME];
 -      file_name = nla_data(nla_file_name);
 -      ret = request_firmware(&params.fw, file_name, devlink->dev);
 -      if (ret) {
 -              NL_SET_ERR_MSG_ATTR(info->extack, nla_file_name, "failed to locate the requested firmware file");
 -              return ret;
 -      }
 -
 -      devlink_flash_update_begin_notify(devlink);
 -      ret = devlink->ops->flash_update(devlink, &params, info->extack);
 -      devlink_flash_update_end_notify(devlink);
 -
 -      release_firmware(params.fw);
 -
 -      return ret;
 -}
 -
 -static int
 -devlink_nl_selftests_fill(struct sk_buff *msg, struct devlink *devlink,
 -                        u32 portid, u32 seq, int flags,
 -                        struct netlink_ext_ack *extack)
 -{
 -      struct nlattr *selftests;
 -      void *hdr;
 -      int err;
 -      int i;
 -
 -      hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags,
 -                        DEVLINK_CMD_SELFTESTS_GET);
 -      if (!hdr)
 -              return -EMSGSIZE;
 -
 -      err = -EMSGSIZE;
 -      if (devlink_nl_put_handle(msg, devlink))
 -              goto err_cancel_msg;
 -
 -      selftests = nla_nest_start(msg, DEVLINK_ATTR_SELFTESTS);
 -      if (!selftests)
 -              goto err_cancel_msg;
 -
 -      for (i = DEVLINK_ATTR_SELFTEST_ID_UNSPEC + 1;
 -           i <= DEVLINK_ATTR_SELFTEST_ID_MAX; i++) {
 -              if (devlink->ops->selftest_check(devlink, i, extack)) {
 -                      err = nla_put_flag(msg, i);
 -                      if (err)
 -                              goto err_cancel_msg;
 -              }
 -      }
 -
 -      nla_nest_end(msg, selftests);
 -      genlmsg_end(msg, hdr);
 -      return 0;
 -
 -err_cancel_msg:
 -      genlmsg_cancel(msg, hdr);
 -      return err;
 -}
 -
 -static int devlink_nl_cmd_selftests_get_doit(struct sk_buff *skb,
 -                                           struct genl_info *info)
 -{
 -      struct devlink *devlink = info->user_ptr[0];
 -      struct sk_buff *msg;
 -      int err;
 -
 -      if (!devlink->ops->selftest_check)
 -              return -EOPNOTSUPP;
 -
 -      msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 -      if (!msg)
 -              return -ENOMEM;
 -
 -      err = devlink_nl_selftests_fill(msg, devlink, info->snd_portid,
 -                                      info->snd_seq, 0, info->extack);
 -      if (err) {
 -              nlmsg_free(msg);
 -              return err;
 -      }
 -
 -      return genlmsg_reply(msg, info);
 -}
 -
 -static int devlink_nl_cmd_selftests_get_dumpit(struct sk_buff *msg,
 -                                             struct netlink_callback *cb)
 -{
 -      struct devlink *devlink;
 -      int start = cb->args[0];
 -      unsigned long index;
 -      int idx = 0;
 -      int err = 0;
 -
 -      devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) {
 -              if (idx < start || !devlink->ops->selftest_check)
 -                      goto inc;
 -
 -              devl_lock(devlink);
 -              err = devlink_nl_selftests_fill(msg, devlink,
 -                                              NETLINK_CB(cb->skb).portid,
 -                                              cb->nlh->nlmsg_seq, NLM_F_MULTI,
 -                                              cb->extack);
 -              devl_unlock(devlink);
 -              if (err) {
 -                      devlink_put(devlink);
 -                      break;
 -              }
 -inc:
 -              idx++;
 -              devlink_put(devlink);
 -      }
 -
 -      if (err != -EMSGSIZE)
 -              return err;
 -
 -      cb->args[0] = idx;
 -      return msg->len;
 -}
 -
 -static int devlink_selftest_result_put(struct sk_buff *skb, unsigned int id,
 -                                     enum devlink_selftest_status test_status)
 -{
 -      struct nlattr *result_attr;
 -
 -      result_attr = nla_nest_start(skb, DEVLINK_ATTR_SELFTEST_RESULT);
 -      if (!result_attr)
 -              return -EMSGSIZE;
 -
 -      if (nla_put_u32(skb, DEVLINK_ATTR_SELFTEST_RESULT_ID, id) ||
 -          nla_put_u8(skb, DEVLINK_ATTR_SELFTEST_RESULT_STATUS,
 -                     test_status))
 -              goto nla_put_failure;
 -
 -      nla_nest_end(skb, result_attr);
 -      return 0;
 -
 -nla_put_failure:
 -      nla_nest_cancel(skb, result_attr);
 -      return -EMSGSIZE;
 -}
 -
 -static int devlink_nl_cmd_selftests_run(struct sk_buff *skb,
 -                                      struct genl_info *info)
 -{
 -      struct nlattr *tb[DEVLINK_ATTR_SELFTEST_ID_MAX + 1];
 -      struct devlink *devlink = info->user_ptr[0];
 -      struct nlattr *attrs, *selftests;
 -      struct sk_buff *msg;
 -      void *hdr;
 -      int err;
 -      int i;
 -
 -      if (!devlink->ops->selftest_run || !devlink->ops->selftest_check)
 -              return -EOPNOTSUPP;
 -
 -      if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_SELFTESTS))
 -              return -EINVAL;
 -
 -      attrs = info->attrs[DEVLINK_ATTR_SELFTESTS];
 -
 -      err = nla_parse_nested(tb, DEVLINK_ATTR_SELFTEST_ID_MAX, attrs,
 -                             devlink_selftest_nl_policy, info->extack);
 -      if (err < 0)
 -              return err;
 -
 -      msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 -      if (!msg)
 -              return -ENOMEM;
 -
 -      err = -EMSGSIZE;
 -      hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq,
 -                        &devlink_nl_family, 0, DEVLINK_CMD_SELFTESTS_RUN);
 -      if (!hdr)
 -              goto free_msg;
 -
 -      if (devlink_nl_put_handle(msg, devlink))
 -              goto genlmsg_cancel;
 -
 -      selftests = nla_nest_start(msg, DEVLINK_ATTR_SELFTESTS);
 -      if (!selftests)
 -              goto genlmsg_cancel;
 -
 -      for (i = DEVLINK_ATTR_SELFTEST_ID_UNSPEC + 1;
 -           i <= DEVLINK_ATTR_SELFTEST_ID_MAX; i++) {
 -              enum devlink_selftest_status test_status;
 -
 -              if (nla_get_flag(tb[i])) {
 -                      if (!devlink->ops->selftest_check(devlink, i,
 -                                                        info->extack)) {
 -                              if (devlink_selftest_result_put(msg, i,
 -                                                              DEVLINK_SELFTEST_STATUS_SKIP))
 -                                      goto selftests_nest_cancel;
 -                              continue;
 -                      }
 -
 -                      test_status = devlink->ops->selftest_run(devlink, i,
 -                                                               info->extack);
 -                      if (devlink_selftest_result_put(msg, i, test_status))
 -                              goto selftests_nest_cancel;
 -              }
 -      }
 -
 -      nla_nest_end(msg, selftests);
 -      genlmsg_end(msg, hdr);
 -      return genlmsg_reply(msg, info);
 -
 -selftests_nest_cancel:
 -      nla_nest_cancel(msg, selftests);
 -genlmsg_cancel:
 -      genlmsg_cancel(msg, hdr);
 -free_msg:
 -      nlmsg_free(msg);
 -      return err;
 -}
 -
 -static const struct devlink_param devlink_param_generic[] = {
 -      {
 -              .id = DEVLINK_PARAM_GENERIC_ID_INT_ERR_RESET,
 -              .name = DEVLINK_PARAM_GENERIC_INT_ERR_RESET_NAME,
 -              .type = DEVLINK_PARAM_GENERIC_INT_ERR_RESET_TYPE,
 -      },
 -      {
 -              .id = DEVLINK_PARAM_GENERIC_ID_MAX_MACS,
 -              .name = DEVLINK_PARAM_GENERIC_MAX_MACS_NAME,
 -              .type = DEVLINK_PARAM_GENERIC_MAX_MACS_TYPE,
 -      },
 -      {
 -              .id = DEVLINK_PARAM_GENERIC_ID_ENABLE_SRIOV,
 -              .name = DEVLINK_PARAM_GENERIC_ENABLE_SRIOV_NAME,
 -              .type = DEVLINK_PARAM_GENERIC_ENABLE_SRIOV_TYPE,
 -      },
 -      {
 -              .id = DEVLINK_PARAM_GENERIC_ID_REGION_SNAPSHOT,
 -              .name = DEVLINK_PARAM_GENERIC_REGION_SNAPSHOT_NAME,
 -              .type = DEVLINK_PARAM_GENERIC_REGION_SNAPSHOT_TYPE,
 -      },
 -      {
 -              .id = DEVLINK_PARAM_GENERIC_ID_IGNORE_ARI,
 -              .name = DEVLINK_PARAM_GENERIC_IGNORE_ARI_NAME,
 -              .type = DEVLINK_PARAM_GENERIC_IGNORE_ARI_TYPE,
 -      },
 -      {
 -              .id = DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MAX,
 -              .name = DEVLINK_PARAM_GENERIC_MSIX_VEC_PER_PF_MAX_NAME,
 -              .type = DEVLINK_PARAM_GENERIC_MSIX_VEC_PER_PF_MAX_TYPE,
 -      },
 -      {
 -              .id = DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MIN,
 -              .name = DEVLINK_PARAM_GENERIC_MSIX_VEC_PER_PF_MIN_NAME,
 -              .type = DEVLINK_PARAM_GENERIC_MSIX_VEC_PER_PF_MIN_TYPE,
 -      },
 -      {
 -              .id = DEVLINK_PARAM_GENERIC_ID_FW_LOAD_POLICY,
 -              .name = DEVLINK_PARAM_GENERIC_FW_LOAD_POLICY_NAME,
 -              .type = DEVLINK_PARAM_GENERIC_FW_LOAD_POLICY_TYPE,
 -      },
 -      {
 -              .id = DEVLINK_PARAM_GENERIC_ID_RESET_DEV_ON_DRV_PROBE,
 -              .name = DEVLINK_PARAM_GENERIC_RESET_DEV_ON_DRV_PROBE_NAME,
 -              .type = DEVLINK_PARAM_GENERIC_RESET_DEV_ON_DRV_PROBE_TYPE,
 -      },
 -      {
 -              .id = DEVLINK_PARAM_GENERIC_ID_ENABLE_ROCE,
 -              .name = DEVLINK_PARAM_GENERIC_ENABLE_ROCE_NAME,
 -              .type = DEVLINK_PARAM_GENERIC_ENABLE_ROCE_TYPE,
 -      },
 -      {
 -              .id = DEVLINK_PARAM_GENERIC_ID_ENABLE_REMOTE_DEV_RESET,
 -              .name = DEVLINK_PARAM_GENERIC_ENABLE_REMOTE_DEV_RESET_NAME,
 -              .type = DEVLINK_PARAM_GENERIC_ENABLE_REMOTE_DEV_RESET_TYPE,
 -      },
 -      {
 -              .id = DEVLINK_PARAM_GENERIC_ID_ENABLE_ETH,
 -              .name = DEVLINK_PARAM_GENERIC_ENABLE_ETH_NAME,
 -              .type = DEVLINK_PARAM_GENERIC_ENABLE_ETH_TYPE,
 -      },
 -      {
 -              .id = DEVLINK_PARAM_GENERIC_ID_ENABLE_RDMA,
 -              .name = DEVLINK_PARAM_GENERIC_ENABLE_RDMA_NAME,
 -              .type = DEVLINK_PARAM_GENERIC_ENABLE_RDMA_TYPE,
 -      },
 -      {
 -              .id = DEVLINK_PARAM_GENERIC_ID_ENABLE_VNET,
 -              .name = DEVLINK_PARAM_GENERIC_ENABLE_VNET_NAME,
 -              .type = DEVLINK_PARAM_GENERIC_ENABLE_VNET_TYPE,
 -      },
 -      {
 -              .id = DEVLINK_PARAM_GENERIC_ID_ENABLE_IWARP,
 -              .name = DEVLINK_PARAM_GENERIC_ENABLE_IWARP_NAME,
 -              .type = DEVLINK_PARAM_GENERIC_ENABLE_IWARP_TYPE,
 -      },
 -      {
 -              .id = DEVLINK_PARAM_GENERIC_ID_IO_EQ_SIZE,
 -              .name = DEVLINK_PARAM_GENERIC_IO_EQ_SIZE_NAME,
 -              .type = DEVLINK_PARAM_GENERIC_IO_EQ_SIZE_TYPE,
 -      },
 -      {
 -              .id = DEVLINK_PARAM_GENERIC_ID_EVENT_EQ_SIZE,
 -              .name = DEVLINK_PARAM_GENERIC_EVENT_EQ_SIZE_NAME,
 -              .type = DEVLINK_PARAM_GENERIC_EVENT_EQ_SIZE_TYPE,
 -      },
 -};
 -
 -static int devlink_param_generic_verify(const struct devlink_param *param)
 -{
 -      /* verify it match generic parameter by id and name */
 -      if (param->id > DEVLINK_PARAM_GENERIC_ID_MAX)
 -              return -EINVAL;
 -      if (strcmp(param->name, devlink_param_generic[param->id].name))
 -              return -ENOENT;
 -
 -      WARN_ON(param->type != devlink_param_generic[param->id].type);
 -
 -      return 0;
 -}
 -
 -static int devlink_param_driver_verify(const struct devlink_param *param)
 -{
 -      int i;
 -
 -      if (param->id <= DEVLINK_PARAM_GENERIC_ID_MAX)
 -              return -EINVAL;
 -      /* verify no such name in generic params */
 -      for (i = 0; i <= DEVLINK_PARAM_GENERIC_ID_MAX; i++)
 -              if (!strcmp(param->name, devlink_param_generic[i].name))
 -                      return -EEXIST;
 -
 -      return 0;
 -}
 -
 -static struct devlink_param_item *
 -devlink_param_find_by_name(struct list_head *param_list,
 -                         const char *param_name)
 -{
 -      struct devlink_param_item *param_item;
 -
 -      list_for_each_entry(param_item, param_list, list)
 -              if (!strcmp(param_item->param->name, param_name))
 -                      return param_item;
 -      return NULL;
 -}
 -
 -static struct devlink_param_item *
 -devlink_param_find_by_id(struct list_head *param_list, u32 param_id)
 -{
 -      struct devlink_param_item *param_item;
 -
 -      list_for_each_entry(param_item, param_list, list)
 -              if (param_item->param->id == param_id)
 -                      return param_item;
 -      return NULL;
 -}
 -
 -static bool
 -devlink_param_cmode_is_supported(const struct devlink_param *param,
 -                               enum devlink_param_cmode cmode)
 -{
 -      return test_bit(cmode, &param->supported_cmodes);
 -}
 -
 -static int devlink_param_get(struct devlink *devlink,
 -                           const struct devlink_param *param,
 -                           struct devlink_param_gset_ctx *ctx)
 -{
 -      if (!param->get || devlink->reload_failed)
 -              return -EOPNOTSUPP;
 -      return param->get(devlink, param->id, ctx);
 -}
 -
 -static int devlink_param_set(struct devlink *devlink,
 -                           const struct devlink_param *param,
 -                           struct devlink_param_gset_ctx *ctx)
 -{
 -      if (!param->set || devlink->reload_failed)
 -              return -EOPNOTSUPP;
 -      return param->set(devlink, param->id, ctx);
 -}
 -
 -static int
 -devlink_param_type_to_nla_type(enum devlink_param_type param_type)
 -{
 -      switch (param_type) {
 -      case DEVLINK_PARAM_TYPE_U8:
 -              return NLA_U8;
 -      case DEVLINK_PARAM_TYPE_U16:
 -              return NLA_U16;
 -      case DEVLINK_PARAM_TYPE_U32:
 -              return NLA_U32;
 -      case DEVLINK_PARAM_TYPE_STRING:
 -              return NLA_STRING;
 -      case DEVLINK_PARAM_TYPE_BOOL:
 -              return NLA_FLAG;
 -      default:
 -              return -EINVAL;
 -      }
 -}
 -
 -static int
 -devlink_nl_param_value_fill_one(struct sk_buff *msg,
 -                              enum devlink_param_type type,
 -                              enum devlink_param_cmode cmode,
 -                              union devlink_param_value val)
 -{
 -      struct nlattr *param_value_attr;
 -
 -      param_value_attr = nla_nest_start_noflag(msg,
 -                                               DEVLINK_ATTR_PARAM_VALUE);
 -      if (!param_value_attr)
 -              goto nla_put_failure;
 -
 -      if (nla_put_u8(msg, DEVLINK_ATTR_PARAM_VALUE_CMODE, cmode))
 -              goto value_nest_cancel;
 -
 -      switch (type) {
 -      case DEVLINK_PARAM_TYPE_U8:
 -              if (nla_put_u8(msg, DEVLINK_ATTR_PARAM_VALUE_DATA, val.vu8))
 -                      goto value_nest_cancel;
 -              break;
 -      case DEVLINK_PARAM_TYPE_U16:
 -              if (nla_put_u16(msg, DEVLINK_ATTR_PARAM_VALUE_DATA, val.vu16))
 -                      goto value_nest_cancel;
 -              break;
 -      case DEVLINK_PARAM_TYPE_U32:
 -              if (nla_put_u32(msg, DEVLINK_ATTR_PARAM_VALUE_DATA, val.vu32))
 -                      goto value_nest_cancel;
 -              break;
 -      case DEVLINK_PARAM_TYPE_STRING:
 -              if (nla_put_string(msg, DEVLINK_ATTR_PARAM_VALUE_DATA,
 -                                 val.vstr))
 -                      goto value_nest_cancel;
 -              break;
 -      case DEVLINK_PARAM_TYPE_BOOL:
 -              if (val.vbool &&
 -                  nla_put_flag(msg, DEVLINK_ATTR_PARAM_VALUE_DATA))
 -                      goto value_nest_cancel;
 -              break;
 -      }
 -
 -      nla_nest_end(msg, param_value_attr);
 -      return 0;
 -
 -value_nest_cancel:
 -      nla_nest_cancel(msg, param_value_attr);
 -nla_put_failure:
 -      return -EMSGSIZE;
 -}
 -
 -static int devlink_nl_param_fill(struct sk_buff *msg, struct devlink *devlink,
 -                               unsigned int port_index,
 -                               struct devlink_param_item *param_item,
 -                               enum devlink_command cmd,
 -                               u32 portid, u32 seq, int flags)
 -{
 -      union devlink_param_value param_value[DEVLINK_PARAM_CMODE_MAX + 1];
 -      bool param_value_set[DEVLINK_PARAM_CMODE_MAX + 1] = {};
 -      const struct devlink_param *param = param_item->param;
 -      struct devlink_param_gset_ctx ctx;
 -      struct nlattr *param_values_list;
 -      struct nlattr *param_attr;
 -      int nla_type;
 -      void *hdr;
 -      int err;
 -      int i;
 -
 -      /* Get value from driver part to driverinit configuration mode */
 -      for (i = 0; i <= DEVLINK_PARAM_CMODE_MAX; i++) {
 -              if (!devlink_param_cmode_is_supported(param, i))
 -                      continue;
 -              if (i == DEVLINK_PARAM_CMODE_DRIVERINIT) {
 -                      if (!param_item->driverinit_value_valid)
 -                              return -EOPNOTSUPP;
 -                      param_value[i] = param_item->driverinit_value;
 -              } else {
 -                      ctx.cmode = i;
 -                      err = devlink_param_get(devlink, param, &ctx);
 -                      if (err)
 -                              return err;
 -                      param_value[i] = ctx.val;
 -              }
 -              param_value_set[i] = true;
 -      }
 -
 -      hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
 -      if (!hdr)
 -              return -EMSGSIZE;
 -
 -      if (devlink_nl_put_handle(msg, devlink))
 -              goto genlmsg_cancel;
 -
 -      if (cmd == DEVLINK_CMD_PORT_PARAM_GET ||
 -          cmd == DEVLINK_CMD_PORT_PARAM_NEW ||
 -          cmd == DEVLINK_CMD_PORT_PARAM_DEL)
 -              if (nla_put_u32(msg, DEVLINK_ATTR_PORT_INDEX, port_index))
 -                      goto genlmsg_cancel;
 -
 -      param_attr = nla_nest_start_noflag(msg, DEVLINK_ATTR_PARAM);
 -      if (!param_attr)
 -              goto genlmsg_cancel;
 -      if (nla_put_string(msg, DEVLINK_ATTR_PARAM_NAME, param->name))
 -              goto param_nest_cancel;
 -      if (param->generic && nla_put_flag(msg, DEVLINK_ATTR_PARAM_GENERIC))
 -              goto param_nest_cancel;
 -
 -      nla_type = devlink_param_type_to_nla_type(param->type);
 -      if (nla_type < 0)
 -              goto param_nest_cancel;
 -      if (nla_put_u8(msg, DEVLINK_ATTR_PARAM_TYPE, nla_type))
 -              goto param_nest_cancel;
 -
 -      param_values_list = nla_nest_start_noflag(msg,
 -                                                DEVLINK_ATTR_PARAM_VALUES_LIST);
 -      if (!param_values_list)
 -              goto param_nest_cancel;
 -
 -      for (i = 0; i <= DEVLINK_PARAM_CMODE_MAX; i++) {
 -              if (!param_value_set[i])
 -                      continue;
 -              err = devlink_nl_param_value_fill_one(msg, param->type,
 -                                                    i, param_value[i]);
 -              if (err)
 -                      goto values_list_nest_cancel;
 -      }
 -
 -      nla_nest_end(msg, param_values_list);
 -      nla_nest_end(msg, param_attr);
 -      genlmsg_end(msg, hdr);
 -      return 0;
 -
 -values_list_nest_cancel:
 -      nla_nest_end(msg, param_values_list);
 -param_nest_cancel:
 -      nla_nest_cancel(msg, param_attr);
 -genlmsg_cancel:
 -      genlmsg_cancel(msg, hdr);
 -      return -EMSGSIZE;
 -}
 -
 -static void devlink_param_notify(struct devlink *devlink,
 -                               unsigned int port_index,
 -                               struct devlink_param_item *param_item,
 -                               enum devlink_command cmd)
 -{
 -      struct sk_buff *msg;
 -      int err;
 -
 -      WARN_ON(cmd != DEVLINK_CMD_PARAM_NEW && cmd != DEVLINK_CMD_PARAM_DEL &&
 -              cmd != DEVLINK_CMD_PORT_PARAM_NEW &&
 -              cmd != DEVLINK_CMD_PORT_PARAM_DEL);
 -      ASSERT_DEVLINK_REGISTERED(devlink);
 -
 -      msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 -      if (!msg)
 -              return;
 -      err = devlink_nl_param_fill(msg, devlink, port_index, param_item, cmd,
 -                                  0, 0, 0);
 -      if (err) {
 -              nlmsg_free(msg);
 -              return;
 -      }
 -
 -      genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink),
 -                              msg, 0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL);
 -}
 -
 -static int devlink_nl_cmd_param_get_dumpit(struct sk_buff *msg,
 -                                         struct netlink_callback *cb)
 -{
 -      struct devlink_param_item *param_item;
 -      struct devlink *devlink;
 -      int start = cb->args[0];
 -      unsigned long index;
 -      int idx = 0;
 -      int err = 0;
 -
 -      devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) {
 -              devl_lock(devlink);
 -              list_for_each_entry(param_item, &devlink->param_list, list) {
 -                      if (idx < start) {
 -                              idx++;
 -                              continue;
 -                      }
 -                      err = devlink_nl_param_fill(msg, devlink, 0, param_item,
 -                                                  DEVLINK_CMD_PARAM_GET,
 -                                                  NETLINK_CB(cb->skb).portid,
 -                                                  cb->nlh->nlmsg_seq,
 -                                                  NLM_F_MULTI);
 -                      if (err == -EOPNOTSUPP) {
 -                              err = 0;
 -                      } else if (err) {
 -                              devl_unlock(devlink);
 -                              devlink_put(devlink);
 -                              goto out;
 -                      }
 -                      idx++;
 -              }
 -              devl_unlock(devlink);
 -              devlink_put(devlink);
 -      }
 -out:
 -      if (err != -EMSGSIZE)
 -              return err;
 -
 -      cb->args[0] = idx;
 -      return msg->len;
 -}
 -
 -static int
 -devlink_param_type_get_from_info(struct genl_info *info,
 -                               enum devlink_param_type *param_type)
 -{
 -      if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_PARAM_TYPE))
 -              return -EINVAL;
 -
 -      switch (nla_get_u8(info->attrs[DEVLINK_ATTR_PARAM_TYPE])) {
 -      case NLA_U8:
 -              *param_type = DEVLINK_PARAM_TYPE_U8;
 -              break;
 -      case NLA_U16:
 -              *param_type = DEVLINK_PARAM_TYPE_U16;
 -              break;
 -      case NLA_U32:
 -              *param_type = DEVLINK_PARAM_TYPE_U32;
 -              break;
 -      case NLA_STRING:
 -              *param_type = DEVLINK_PARAM_TYPE_STRING;
 -              break;
 -      case NLA_FLAG:
 -              *param_type = DEVLINK_PARAM_TYPE_BOOL;
 -              break;
 -      default:
 -              return -EINVAL;
 -      }
 -
 -      return 0;
 -}
 -
 -static int
 -devlink_param_value_get_from_info(const struct devlink_param *param,
 -                                struct genl_info *info,
 -                                union devlink_param_value *value)
 -{
 -      struct nlattr *param_data;
 -      int len;
 -
 -      param_data = info->attrs[DEVLINK_ATTR_PARAM_VALUE_DATA];
 -
 -      if (param->type != DEVLINK_PARAM_TYPE_BOOL && !param_data)
 -              return -EINVAL;
 -
 -      switch (param->type) {
 -      case DEVLINK_PARAM_TYPE_U8:
 -              if (nla_len(param_data) != sizeof(u8))
 -                      return -EINVAL;
 -              value->vu8 = nla_get_u8(param_data);
 -              break;
 -      case DEVLINK_PARAM_TYPE_U16:
 -              if (nla_len(param_data) != sizeof(u16))
 -                      return -EINVAL;
 -              value->vu16 = nla_get_u16(param_data);
 -              break;
 -      case DEVLINK_PARAM_TYPE_U32:
 -              if (nla_len(param_data) != sizeof(u32))
 -                      return -EINVAL;
 -              value->vu32 = nla_get_u32(param_data);
 -              break;
 -      case DEVLINK_PARAM_TYPE_STRING:
 -              len = strnlen(nla_data(param_data), nla_len(param_data));
 -              if (len == nla_len(param_data) ||
 -                  len >= __DEVLINK_PARAM_MAX_STRING_VALUE)
 -                      return -EINVAL;
 -              strcpy(value->vstr, nla_data(param_data));
 -              break;
 -      case DEVLINK_PARAM_TYPE_BOOL:
 -              if (param_data && nla_len(param_data))
 -                      return -EINVAL;
 -              value->vbool = nla_get_flag(param_data);
 -              break;
 -      }
 -      return 0;
 -}
 -
 -static struct devlink_param_item *
 -devlink_param_get_from_info(struct list_head *param_list,
 -                          struct genl_info *info)
 -{
 -      char *param_name;
 -
 -      if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_PARAM_NAME))
 -              return NULL;
 -
 -      param_name = nla_data(info->attrs[DEVLINK_ATTR_PARAM_NAME]);
 -      return devlink_param_find_by_name(param_list, param_name);
 -}
 -
 -static int devlink_nl_cmd_param_get_doit(struct sk_buff *skb,
 -                                       struct genl_info *info)
 -{
 -      struct devlink *devlink = info->user_ptr[0];
 -      struct devlink_param_item *param_item;
 -      struct sk_buff *msg;
 -      int err;
 -
 -      param_item = devlink_param_get_from_info(&devlink->param_list, info);
 -      if (!param_item)
 -              return -EINVAL;
 -
 -      msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 -      if (!msg)
 -              return -ENOMEM;
 -
 -      err = devlink_nl_param_fill(msg, devlink, 0, param_item,
 -                                  DEVLINK_CMD_PARAM_GET,
 -                                  info->snd_portid, info->snd_seq, 0);
 -      if (err) {
 -              nlmsg_free(msg);
 -              return err;
 -      }
 -
 -      return genlmsg_reply(msg, info);
 -}
 -
 -static int __devlink_nl_cmd_param_set_doit(struct devlink *devlink,
 -                                         unsigned int port_index,
 -                                         struct list_head *param_list,
 -                                         struct genl_info *info,
 -                                         enum devlink_command cmd)
 -{
 -      enum devlink_param_type param_type;
 -      struct devlink_param_gset_ctx ctx;
 -      enum devlink_param_cmode cmode;
 -      struct devlink_param_item *param_item;
 -      const struct devlink_param *param;
 -      union devlink_param_value value;
 -      int err = 0;
 -
 -      param_item = devlink_param_get_from_info(param_list, info);
 -      if (!param_item)
 -              return -EINVAL;
 -      param = param_item->param;
 -      err = devlink_param_type_get_from_info(info, &param_type);
 -      if (err)
 -              return err;
 -      if (param_type != param->type)
 -              return -EINVAL;
 -      err = devlink_param_value_get_from_info(param, info, &value);
 -      if (err)
 -              return err;
 -      if (param->validate) {
 -              err = param->validate(devlink, param->id, value, info->extack);
 -              if (err)
 -                      return err;
 -      }
 -
 -      if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_PARAM_VALUE_CMODE))
 -              return -EINVAL;
 -      cmode = nla_get_u8(info->attrs[DEVLINK_ATTR_PARAM_VALUE_CMODE]);
 -      if (!devlink_param_cmode_is_supported(param, cmode))
 -              return -EOPNOTSUPP;
 -
 -      if (cmode == DEVLINK_PARAM_CMODE_DRIVERINIT) {
 -              if (param->type == DEVLINK_PARAM_TYPE_STRING)
 -                      strcpy(param_item->driverinit_value.vstr, value.vstr);
 -              else
 -                      param_item->driverinit_value = value;
 -              param_item->driverinit_value_valid = true;
 -      } else {
 -              if (!param->set)
 -                      return -EOPNOTSUPP;
 -              ctx.val = value;
 -              ctx.cmode = cmode;
 -              err = devlink_param_set(devlink, param, &ctx);
 -              if (err)
 -                      return err;
 -      }
 -
 -      devlink_param_notify(devlink, port_index, param_item, cmd);
 -      return 0;
 -}
 -
 -static int devlink_nl_cmd_param_set_doit(struct sk_buff *skb,
 -                                       struct genl_info *info)
 -{
 -      struct devlink *devlink = info->user_ptr[0];
 -
 -      return __devlink_nl_cmd_param_set_doit(devlink, 0, &devlink->param_list,
 -                                             info, DEVLINK_CMD_PARAM_NEW);
 -}
 -
 -static int devlink_nl_cmd_port_param_get_dumpit(struct sk_buff *msg,
 -                                              struct netlink_callback *cb)
 -{
 -      NL_SET_ERR_MSG_MOD(cb->extack, "Port params are not supported");
 -      return msg->len;
 -}
 -
 -static int devlink_nl_cmd_port_param_get_doit(struct sk_buff *skb,
 -                                            struct genl_info *info)
 -{
 -      NL_SET_ERR_MSG_MOD(info->extack, "Port params are not supported");
 -      return -EINVAL;
 -}
 -
 -static int devlink_nl_cmd_port_param_set_doit(struct sk_buff *skb,
 -                                            struct genl_info *info)
 -{
 -      NL_SET_ERR_MSG_MOD(info->extack, "Port params are not supported");
 -      return -EINVAL;
 -}
 -
 -static int devlink_nl_region_snapshot_id_put(struct sk_buff *msg,
 -                                           struct devlink *devlink,
 -                                           struct devlink_snapshot *snapshot)
 -{
 -      struct nlattr *snap_attr;
 -      int err;
 -
 -      snap_attr = nla_nest_start_noflag(msg, DEVLINK_ATTR_REGION_SNAPSHOT);
 -      if (!snap_attr)
 -              return -EINVAL;
 -
 -      err = nla_put_u32(msg, DEVLINK_ATTR_REGION_SNAPSHOT_ID, snapshot->id);
 -      if (err)
 -              goto nla_put_failure;
 -
 -      nla_nest_end(msg, snap_attr);
 -      return 0;
 -
 -nla_put_failure:
 -      nla_nest_cancel(msg, snap_attr);
 -      return err;
 -}
 -
 -static int devlink_nl_region_snapshots_id_put(struct sk_buff *msg,
 -                                            struct devlink *devlink,
 -                                            struct devlink_region *region)
 -{
 -      struct devlink_snapshot *snapshot;
 -      struct nlattr *snapshots_attr;
 -      int err;
 -
 -      snapshots_attr = nla_nest_start_noflag(msg,
 -                                             DEVLINK_ATTR_REGION_SNAPSHOTS);
 -      if (!snapshots_attr)
 -              return -EINVAL;
 -
 -      list_for_each_entry(snapshot, &region->snapshot_list, list) {
 -              err = devlink_nl_region_snapshot_id_put(msg, devlink, snapshot);
 -              if (err)
 -                      goto nla_put_failure;
 -      }
 -
 -      nla_nest_end(msg, snapshots_attr);
 -      return 0;
 -
 -nla_put_failure:
 -      nla_nest_cancel(msg, snapshots_attr);
 -      return err;
 -}
 -
 -static int devlink_nl_region_fill(struct sk_buff *msg, struct devlink *devlink,
 -                                enum devlink_command cmd, u32 portid,
 -                                u32 seq, int flags,
 -                                struct devlink_region *region)
 -{
 -      void *hdr;
 -      int err;
 -
 -      hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
 -      if (!hdr)
 -              return -EMSGSIZE;
 -
 -      err = devlink_nl_put_handle(msg, devlink);
 -      if (err)
 -              goto nla_put_failure;
 -
 -      if (region->port) {
 -              err = nla_put_u32(msg, DEVLINK_ATTR_PORT_INDEX,
 -                                region->port->index);
 -              if (err)
 -                      goto nla_put_failure;
 -      }
 -
 -      err = nla_put_string(msg, DEVLINK_ATTR_REGION_NAME, region->ops->name);
 -      if (err)
 -              goto nla_put_failure;
 -
 -      err = nla_put_u64_64bit(msg, DEVLINK_ATTR_REGION_SIZE,
 -                              region->size,
 -                              DEVLINK_ATTR_PAD);
 -      if (err)
 -              goto nla_put_failure;
 -
 -      err = nla_put_u32(msg, DEVLINK_ATTR_REGION_MAX_SNAPSHOTS,
 -                        region->max_snapshots);
 -      if (err)
 -              goto nla_put_failure;
 -
 -      err = devlink_nl_region_snapshots_id_put(msg, devlink, region);
 -      if (err)
 -              goto nla_put_failure;
 -
 -      genlmsg_end(msg, hdr);
 -      return 0;
 -
 -nla_put_failure:
 -      genlmsg_cancel(msg, hdr);
 -      return err;
 -}
 -
 -static struct sk_buff *
 -devlink_nl_region_notify_build(struct devlink_region *region,
 -                             struct devlink_snapshot *snapshot,
 -                             enum devlink_command cmd, u32 portid, u32 seq)
 -{
 -      struct devlink *devlink = region->devlink;
 -      struct sk_buff *msg;
 -      void *hdr;
 -      int err;
 -
 -
 -      msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 -      if (!msg)
 -              return ERR_PTR(-ENOMEM);
 -
 -      hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, 0, cmd);
 -      if (!hdr) {
 -              err = -EMSGSIZE;
 -              goto out_free_msg;
 -      }
 -
 -      err = devlink_nl_put_handle(msg, devlink);
 -      if (err)
 -              goto out_cancel_msg;
 -
 -      if (region->port) {
 -              err = nla_put_u32(msg, DEVLINK_ATTR_PORT_INDEX,
 -                                region->port->index);
 -              if (err)
 -                      goto out_cancel_msg;
 -      }
 -
 -      err = nla_put_string(msg, DEVLINK_ATTR_REGION_NAME,
 -                           region->ops->name);
 -      if (err)
 -              goto out_cancel_msg;
 -
 -      if (snapshot) {
 -              err = nla_put_u32(msg, DEVLINK_ATTR_REGION_SNAPSHOT_ID,
 -                                snapshot->id);
 -              if (err)
 -                      goto out_cancel_msg;
 -      } else {
 -              err = nla_put_u64_64bit(msg, DEVLINK_ATTR_REGION_SIZE,
 -                                      region->size, DEVLINK_ATTR_PAD);
 -              if (err)
 -                      goto out_cancel_msg;
 -      }
 -      genlmsg_end(msg, hdr);
 -
 -      return msg;
 -
 -out_cancel_msg:
 -      genlmsg_cancel(msg, hdr);
 -out_free_msg:
 -      nlmsg_free(msg);
 -      return ERR_PTR(err);
 -}
 -
 -static void devlink_nl_region_notify(struct devlink_region *region,
 -                                   struct devlink_snapshot *snapshot,
 -                                   enum devlink_command cmd)
 -{
 -      struct devlink *devlink = region->devlink;
 -      struct sk_buff *msg;
 -
 -      WARN_ON(cmd != DEVLINK_CMD_REGION_NEW && cmd != DEVLINK_CMD_REGION_DEL);
 -      if (!xa_get_mark(&devlinks, devlink->index, DEVLINK_REGISTERED))
 -              return;
 -
 -      msg = devlink_nl_region_notify_build(region, snapshot, cmd, 0, 0);
 -      if (IS_ERR(msg))
 -              return;
 -
 -      genlmsg_multicast_netns(&devlink_nl_family, devlink_net(devlink), msg,
 -                              0, DEVLINK_MCGRP_CONFIG, GFP_KERNEL);
 -}
 -
 -/**
 - * __devlink_snapshot_id_increment - Increment number of snapshots using an id
 - *    @devlink: devlink instance
 - *    @id: the snapshot id
 - *
 - *    Track when a new snapshot begins using an id. Load the count for the
 - *    given id from the snapshot xarray, increment it, and store it back.
 - *
 - *    Called when a new snapshot is created with the given id.
 - *
 - *    The id *must* have been previously allocated by
 - *    devlink_region_snapshot_id_get().
 - *
 - *    Returns 0 on success, or an error on failure.
 - */
 -static int __devlink_snapshot_id_increment(struct devlink *devlink, u32 id)
 -{
 -      unsigned long count;
 -      void *p;
 -      int err;
 -
 -      xa_lock(&devlink->snapshot_ids);
 -      p = xa_load(&devlink->snapshot_ids, id);
 -      if (WARN_ON(!p)) {
 -              err = -EINVAL;
 -              goto unlock;
 -      }
 -
 -      if (WARN_ON(!xa_is_value(p))) {
 -              err = -EINVAL;
 -              goto unlock;
 -      }
 -
 -      count = xa_to_value(p);
 -      count++;
 -
 -      err = xa_err(__xa_store(&devlink->snapshot_ids, id, xa_mk_value(count),
 -                              GFP_ATOMIC));
 -unlock:
 -      xa_unlock(&devlink->snapshot_ids);
 -      return err;
 -}
 -
 -/**
 - * __devlink_snapshot_id_decrement - Decrease number of snapshots using an id
 - *    @devlink: devlink instance
 - *    @id: the snapshot id
 - *
 - *    Track when a snapshot is deleted and stops using an id. Load the count
 - *    for the given id from the snapshot xarray, decrement it, and store it
 - *    back.
 - *
 - *    If the count reaches zero, erase this id from the xarray, freeing it
 - *    up for future re-use by devlink_region_snapshot_id_get().
 - *
 - *    Called when a snapshot using the given id is deleted, and when the
 - *    initial allocator of the id is finished using it.
 - */
 -static void __devlink_snapshot_id_decrement(struct devlink *devlink, u32 id)
 -{
 -      unsigned long count;
 -      void *p;
 -
 -      xa_lock(&devlink->snapshot_ids);
 -      p = xa_load(&devlink->snapshot_ids, id);
 -      if (WARN_ON(!p))
 -              goto unlock;
 -
 -      if (WARN_ON(!xa_is_value(p)))
 -              goto unlock;
 -
 -      count = xa_to_value(p);
 -
 -      if (count > 1) {
 -              count--;
 -              __xa_store(&devlink->snapshot_ids, id, xa_mk_value(count),
 -                         GFP_ATOMIC);
 -      } else {
 -              /* If this was the last user, we can erase this id */
 -              __xa_erase(&devlink->snapshot_ids, id);
 -      }
 -unlock:
 -      xa_unlock(&devlink->snapshot_ids);
 -}
 -
 -/**
 - *    __devlink_snapshot_id_insert - Insert a specific snapshot ID
 - *    @devlink: devlink instance
 - *    @id: the snapshot id
 - *
 - *    Mark the given snapshot id as used by inserting a zero value into the
 - *    snapshot xarray.
 - *
 - *    This must be called while holding the devlink instance lock. Unlike
 - *    devlink_snapshot_id_get, the initial reference count is zero, not one.
 - *    It is expected that the id will immediately be used before
 - *    releasing the devlink instance lock.
 - *
 - *    Returns zero on success, or an error code if the snapshot id could not
 - *    be inserted.
 - */
 -static int __devlink_snapshot_id_insert(struct devlink *devlink, u32 id)
 -{
 -      int err;
 -
 -      xa_lock(&devlink->snapshot_ids);
 -      if (xa_load(&devlink->snapshot_ids, id)) {
 -              xa_unlock(&devlink->snapshot_ids);
 -              return -EEXIST;
 -      }
 -      err = xa_err(__xa_store(&devlink->snapshot_ids, id, xa_mk_value(0),
 -                              GFP_ATOMIC));
 -      xa_unlock(&devlink->snapshot_ids);
 -      return err;
 -}
 -
 -/**
 - *    __devlink_region_snapshot_id_get - get snapshot ID
 - *    @devlink: devlink instance
 - *    @id: storage to return snapshot id
 - *
 - *    Allocates a new snapshot id. Returns zero on success, or a negative
 - *    error on failure. Must be called while holding the devlink instance
 - *    lock.
 - *
 - *    Snapshot IDs are tracked using an xarray which stores the number of
 - *    users of the snapshot id.
 - *
 - *    Note that the caller of this function counts as a 'user', in order to
 - *    avoid race conditions. The caller must release its hold on the
 - *    snapshot by using devlink_region_snapshot_id_put.
 - */
 -static int __devlink_region_snapshot_id_get(struct devlink *devlink, u32 *id)
 -{
 -      return xa_alloc(&devlink->snapshot_ids, id, xa_mk_value(1),
 -                      xa_limit_32b, GFP_KERNEL);
 -}
 -
 -/**
 - *    __devlink_region_snapshot_create - create a new snapshot
 - *    This will add a new snapshot of a region. The snapshot
 - *    will be stored on the region struct and can be accessed
 - *    from devlink. This is useful for future analyses of snapshots.
 - *    Multiple snapshots can be created on a region.
 - *    The @snapshot_id should be obtained using the getter function.
 - *
 - *    Must be called only while holding the region snapshot lock.
 - *
 - *    @region: devlink region of the snapshot
 - *    @data: snapshot data
 - *    @snapshot_id: snapshot id to be created
 - */
 -static int
 -__devlink_region_snapshot_create(struct devlink_region *region,
 -                               u8 *data, u32 snapshot_id)
 -{
 -      struct devlink *devlink = region->devlink;
 -      struct devlink_snapshot *snapshot;
 -      int err;
 -
 -      lockdep_assert_held(&region->snapshot_lock);
 -
 -      /* check if region can hold one more snapshot */
 -      if (region->cur_snapshots == region->max_snapshots)
 -              return -ENOSPC;
 -
 -      if (devlink_region_snapshot_get_by_id(region, snapshot_id))
 -              return -EEXIST;
 -
 -      snapshot = kzalloc(sizeof(*snapshot), GFP_KERNEL);
 -      if (!snapshot)
 -              return -ENOMEM;
 -
 -      err = __devlink_snapshot_id_increment(devlink, snapshot_id);
 -      if (err)
 -              goto err_snapshot_id_increment;
 -
 -      snapshot->id = snapshot_id;
 -      snapshot->region = region;
 -      snapshot->data = data;
 -
 -      list_add_tail(&snapshot->list, &region->snapshot_list);
 -
 -      region->cur_snapshots++;
 -
 -      devlink_nl_region_notify(region, snapshot, DEVLINK_CMD_REGION_NEW);
 -      return 0;
 -
 -err_snapshot_id_increment:
 -      kfree(snapshot);
 -      return err;
 -}
 -
 -static void devlink_region_snapshot_del(struct devlink_region *region,
 -                                      struct devlink_snapshot *snapshot)
 -{
 -      struct devlink *devlink = region->devlink;
 -
 -      lockdep_assert_held(&region->snapshot_lock);
 -
 -      devlink_nl_region_notify(region, snapshot, DEVLINK_CMD_REGION_DEL);
 -      region->cur_snapshots--;
 -      list_del(&snapshot->list);
 -      region->ops->destructor(snapshot->data);
 -      __devlink_snapshot_id_decrement(devlink, snapshot->id);
 -      kfree(snapshot);
 -}
 -
 -static int devlink_nl_cmd_region_get_doit(struct sk_buff *skb,
 -                                        struct genl_info *info)
 -{
 -      struct devlink *devlink = info->user_ptr[0];
 -      struct devlink_port *port = NULL;
 -      struct devlink_region *region;
 -      const char *region_name;
 -      struct sk_buff *msg;
 -      unsigned int index;
 -      int err;
 -
 -      if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_REGION_NAME))
 -              return -EINVAL;
 -
 -      if (info->attrs[DEVLINK_ATTR_PORT_INDEX]) {
 -              index = nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_INDEX]);
 -
 -              port = devlink_port_get_by_index(devlink, index);
 -              if (!port)
 -                      return -ENODEV;
 -      }
 -
 -      region_name = nla_data(info->attrs[DEVLINK_ATTR_REGION_NAME]);
 -      if (port)
 -              region = devlink_port_region_get_by_name(port, region_name);
 -      else
 -              region = devlink_region_get_by_name(devlink, region_name);
 -
 -      if (!region)
 -              return -EINVAL;
 -
 -      msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
 -      if (!msg)
 -              return -ENOMEM;
 -
 -      err = devlink_nl_region_fill(msg, devlink, DEVLINK_CMD_REGION_GET,
 -                                   info->snd_portid, info->snd_seq, 0,
 -                                   region);
 -      if (err) {
 -              nlmsg_free(msg);
 -              return err;
 -      }
 -
 -      return genlmsg_reply(msg, info);
 -}
 -
 -static int devlink_nl_cmd_region_get_port_dumpit(struct sk_buff *msg,
 -                                               struct netlink_callback *cb,
 -                                               struct devlink_port *port,
 -                                               int *idx,
 -                                               int start)
 -{
 -      struct devlink_region *region;
 -      int err = 0;
 -
 -      list_for_each_entry(region, &port->region_list, list) {
 -              if (*idx < start) {
 -                      (*idx)++;
 -                      continue;
 -              }
 -              err = devlink_nl_region_fill(msg, port->devlink,
 -                                           DEVLINK_CMD_REGION_GET,
 -                                           NETLINK_CB(cb->skb).portid,
 -                                           cb->nlh->nlmsg_seq,
 -                                           NLM_F_MULTI, region);
 -              if (err)
 -                      goto out;
 -              (*idx)++;
 -      }
 -
 -out:
 -      return err;
 -}
 -
 -static int devlink_nl_cmd_region_get_devlink_dumpit(struct sk_buff *msg,
 -                                                  struct netlink_callback *cb,
 -                                                  struct devlink *devlink,
 -                                                  int *idx,
 -                                                  int start)
 -{
 -      struct devlink_region *region;
 -      struct devlink_port *port;
 -      unsigned long port_index;
 -      int err = 0;
 -
 -      devl_lock(devlink);
 -      list_for_each_entry(region, &devlink->region_list, list) {
 -              if (*idx < start) {
 -                      (*idx)++;
 -                      continue;
 -              }
 -              err = devlink_nl_region_fill(msg, devlink,
 -                                           DEVLINK_CMD_REGION_GET,
 -                                           NETLINK_CB(cb->skb).portid,
 -                                           cb->nlh->nlmsg_seq,
 -                                           NLM_F_MULTI, region);
 -              if (err)
 -                      goto out;
 -              (*idx)++;
 -      }
 -
 -      xa_for_each(&devlink->ports, port_index, port) {
 -              err = devlink_nl_cmd_region_get_port_dumpit(msg, cb, port, idx,
 -                                                          start);
 -              if (err)
 -                      goto out;
 -      }
 -
 -out:
 -      devl_unlock(devlink);
 -      return err;
 -}
 -
 -static int devlink_nl_cmd_region_get_dumpit(struct sk_buff *msg,
 -                                          struct netlink_callback *cb)
 -{
 -      struct devlink *devlink;
 -      int start = cb->args[0];
 -      unsigned long index;
 -      int idx = 0;
 -      int err = 0;
 -
 -      devlinks_xa_for_each_registered_get(sock_net(msg->sk), index, devlink) {
 -              err = devlink_nl_cmd_region_get_devlink_dumpit(msg, cb, devlink,
 -                                                             &idx, start);
 -              devlink_put(devlink);
 -              if (err)
 -                      goto out;
 -      }
 -out:
 -      cb->args[0] = idx;
 -      return msg->len;
 -}
 -
 -static int devlink_nl_cmd_region_del(struct sk_buff *skb,
 -                                   struct genl_info *info)
 -{
 -      struct devlink *devlink = info->user_ptr[0];
 -      struct devlink_snapshot *snapshot;
 -      struct devlink_port *port = NULL;
 -      struct devlink_region *region;
 -      const char *region_name;
 -      unsigned int index;
 -      u32 snapshot_id;
 -
 -      if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_REGION_NAME) ||
 -          GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_REGION_SNAPSHOT_ID))
 -              return -EINVAL;
 -
 -      region_name = nla_data(info->attrs[DEVLINK_ATTR_REGION_NAME]);
 -      snapshot_id = nla_get_u32(info->attrs[DEVLINK_ATTR_REGION_SNAPSHOT_ID]);
 -
 -      if (info->attrs[DEVLINK_ATTR_PORT_INDEX]) {
 -              index = nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_INDEX]);
 -
 -              port = devlink_port_get_by_index(devlink, index);
 -              if (!port)
 -                      return -ENODEV;
 -      }
 -
 -      if (port)
 -              region = devlink_port_region_get_by_name(port, region_name);
 -      else
 -              region = devlink_region_get_by_name(devlink, region_name);
 -
 -      if (!region)
 -              return -EINVAL;
 -
 -      mutex_lock(&region->snapshot_lock);
 -      snapshot = devlink_region_snapshot_get_by_id(region, snapshot_id);
 -      if (!snapshot) {
 -              mutex_unlock(&region->snapshot_lock);
 -              return -EINVAL;
 -      }
 -
 -      devlink_region_snapshot_del(region, snapshot);
 -      mutex_unlock(&region->snapshot_lock);
 -      return 0;
 -}
 -
 -static int
 -devlink_nl_cmd_region_new(struct sk_buff *skb, struct genl_info *info)
 -{
 -      struct devlink *devlink = info->user_ptr[0];
 -      struct devlink_snapshot *snapshot;
 -      struct devlink_port *port = NULL;
 -      struct nlattr *snapshot_id_attr;
 -      struct devlink_region *region;
 -      const char *region_name;
 -      unsigned int index;
 -      u32 snapshot_id;
 -      u8 *data;
 -      int err;
 -
 -      if (GENL_REQ_ATTR_CHECK(info, DEVLINK_ATTR_REGION_NAME)) {
 -              NL_SET_ERR_MSG_MOD(info->extack, "No region name provided");
 -              return -EINVAL;
 -      }
 -
 -      region_name = nla_data(info->attrs[DEVLINK_ATTR_REGION_NAME]);
 -
 -      if (info->attrs[DEVLINK_ATTR_PORT_INDEX]) {
 -              index = nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_INDEX]);
 -
 -              port = devlink_port_get_by_index(devlink, index);
 -              if (!port)
 -                      return -ENODEV;
 -      }
 -
 -      if (port)
 -              region = devlink_port_region_get_by_name(port, region_name);
 -      else
 -              region = devlink_region_get_by_name(devlink, region_name);
 -
 -      if (!region) {
 -              NL_SET_ERR_MSG_MOD(info->extack, "The requested region does not exist");
 -              return -EINVAL;
 -      }
 -
 -      if (!region->ops->snapshot) {
 -              NL_SET_ERR_MSG_MOD(info->extack, "The requested region does not support taking an immediate snapshot");
 -              return -EOPNOTSUPP;
 -      }
 -
 -      mutex_lock(&region->snapshot_lock);
 -
 -      if (region->cur_snapshots == region->max_snapshots) {
 -              NL_SET_ERR_MSG_MOD(info->extack, "The region has reached the maximum number of stored snapshots");
 -              err = -ENOSPC;
 -              goto unlock;
 -      }
 -
 -      snapshot_id_attr = info->attrs[DEVLINK_ATTR_REGION_SNAPSHOT_ID];
 -      if (snapshot_id_attr) {
 -              snapshot_id = nla_get_u32(snapshot_id_attr);
 -
 -              if (devlink_region_snapshot_get_by_id(region, snapshot_id)) {
 -                      NL_SET_ERR_MSG_MOD(info->extack, "The requested snapshot id is already in use");
 -                      err = -EEXIST;
 -                      goto unlock;
 -              }
 -
 -              err = __devlink_snapshot_id_insert(devlink, snapshot_id);
 -              if (err)
 -                      goto unlock;
 -      } else {
 -              err = __devlink_region_snapshot_id_get(devlink, &snapshot_id);
 -              if (err) {
 -                      NL_SET_ERR_MSG_MOD(info->extack, "Failed to allocate a new snapshot id");
 -                      goto unlock;
 -              }
 -      }
 -
 -      if (port)
 -              err = region->port_ops->snapshot(port, region->port_ops,
 -                                               info->extack, &data);
 -      else
 -              err = region->ops->snapshot(devlink, region->ops,
 -                                          info->extack, &data);
 -      if (err)
 -              goto err_snapshot_capture;
 -
 -      err = __devlink_region_snapshot_create(region, data, snapshot_id);
 -      if (err)
 -              goto err_snapshot_create;
 -
 -      if (!snapshot_id_attr) {
 -              struct sk_buff *msg;
 -
 -              snapshot = devlink_region_snapshot_get_by_id(region,
 -                                                           snapshot_id);
 -              if (WARN_ON(!snapshot)) {
 -                      err = -EINVAL;
 -                      goto unlock;
 -              }
 -
 -              msg = devlink_nl_region_notify_build(region, snapshot,
 -                                                   DEVLINK_CMD_REGION_NEW,
 -                                                   info->snd_portid,
 -                                                   info->snd_seq);
 -              err = PTR_ERR_OR_ZERO(msg);
 -              if (err)
 -                      goto err_notify;
 -
 -              err = genlmsg_reply(msg, info);
 -              if (err)
 -                      goto err_notify;
 -      }
 -
 -      mutex_unlock(&region->snapshot_lock);
 -      return 0;
 -
 -err_snapshot_create:
 -      region->ops->destructor(data);
 -err_snapshot_capture:
 -      __devlink_snapshot_id_decrement(devlink, snapshot_id);
 -      mutex_unlock(&region->snapshot_lock);
 -      return err;
 -
 -err_notify:
 -      devlink_region_snapshot_del(region, snapshot);
 -unlock:
 -      mutex_unlock(&region->snapshot_lock);
 -      return err;
 -}
 -
 -static int devlink_nl_cmd_region_read_chunk_fill(struct sk_buff *msg,
 -                                               u8 *chunk, u32 chunk_size,
 -                                               u64 addr)
 -{
 -      struct nlattr *chunk_attr;
 -      int err;
 -
 -      chunk_attr = nla_nest_start_noflag(msg, DEVLINK_ATTR_REGION_CHUNK);
 -      if (!chunk_attr)
 -              return -EINVAL;
 -
 -      err = nla_put(msg, DEVLINK_ATTR_REGION_CHUNK_DATA, chunk_size, chunk);
 -      if (err)
 -              goto nla_put_failure;
 -
 -      err = nla_put_u64_64bit(msg, DEVLINK_ATTR_REGION_CHUNK_ADDR, addr,
 -                              DEVLINK_ATTR_PAD);
 -      if (err)
 -              goto nla_put_failure;
 -
 -      nla_nest_end(msg, chunk_attr);
 -      return 0;
 -
 -nla_put_failure:
 -      nla_nest_cancel(msg, chunk_attr);
 -      return err;
 -}
 -
 -#define DEVLINK_REGION_READ_CHUNK_SIZE 256
 -
 -typedef int devlink_chunk_fill_t(void *cb_priv, u8 *chunk, u32 chunk_size,
 -                               u64 curr_offset,
 -                               struct netlink_ext_ack *extack);
 -
 -static int
 -devlink_nl_region_read_fill(struct sk_buff *skb, devlink_chunk_fill_t *cb,
 -                          void *cb_priv, u64 start_offset, u64 end_offset,
 -                          u64 *new_offset, struct netlink_ext_ack *extack)
 -{
 -      u64 curr_offset = start_offset;
 -      int err = 0;
 -      u8 *data;
 -
 -      /* Allocate and re-use a single buffer */
 -      data = kmalloc(DEVLINK_REGION_READ_CHUNK_SIZE, GFP_KERNEL);
 -      if (!data)
 -              return -ENOMEM;
 -
 -      *new_offset = start_offset;
 -
 -      while (curr_offset < end_offset) {
 -              u32 data_size;
 -
 -              data_size = min_t(u32, end_offset - curr_offset,
 -                                DEVLINK_REGION_READ_CHUNK_SIZE);
 -
 -              err = cb(cb_priv, data, data_size, curr_offset, extack);
 -              if (err)
 -                      break;
 -
 -              err = devlink_nl_cmd_region_read_chunk_fill(skb, data, data_size, curr_offset);
 -              if (err)
 -                      break;
 -
 -              curr_offset += data_size;
 -      }
 -      *new_offset = curr_offset;
 -
 -      kfree(data);
 -
 -      return err;
 -}
 -
 -static int
 -devlink_region_snapshot_fill(void *cb_priv, u8 *chunk, u32 chunk_size,
 -                           u64 curr_offset,
 -                           struct netlink_ext_ack __always_unused *extack)
 -{
 -      struct devlink_snapshot *snapshot = cb_priv;
 -
 -      memcpy(chunk, &snapshot->data[curr_offset], chunk_size);
 -
 -      return 0;
 -}
 -
 -static int
 -devlink_region_port_direct_fill(void *cb_priv, u8 *chunk, u32 chunk_size,
 -                              u64 curr_offset, struct netlink_ext_ack *extack)
 -{
 -      struct devlink_region *region = cb_priv;
 -
 -      return region->port_ops->read(region->port, region->port_ops, extack,
 -                                    curr_offset, chunk_size, chunk);
 -}
 -
 -static int
 -devlink_region_direct_fill(void *cb_priv, u8 *chunk, u32 chunk_size,
 -                         u64 curr_offset, struct netlink_ext_ack *extack)
 -{
 -      struct devlink_region *region = cb_priv;
 -
 -      return region->ops->read(region->devlink, region->ops, extack,
 -                               curr_offset, chunk_size, chunk);
 -}
 -
 -static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb,
 -                                           struct netlink_callback *cb)
 -{
 -      const struct genl_dumpit_info *info = genl_dumpit_info(cb);
 -      struct nlattr *chunks_attr, *region_attr, *snapshot_attr;
 -      u64 ret_offset, start_offset, end_offset = U64_MAX;
 -      struct nlattr **attrs = info->attrs;
 -      struct devlink_port *port = NULL;
 -      devlink_chunk_fill_t *region_cb;
 -      struct devlink_region *region;
 -      const char *region_name;
 -      struct devlink *devlink;
 -      unsigned int index;
 -      void *region_cb_priv;
 -      void *hdr;
 -      int err;
 -
 -      start_offset = *((u64 *)&cb->args[0]);
 -
 -      devlink = devlink_get_from_attrs(sock_net(cb->skb->sk), attrs);
 -      if (IS_ERR(devlink))
 -              return PTR_ERR(devlink);
 -
 -      devl_lock(devlink);
 -
 -      if (!attrs[DEVLINK_ATTR_REGION_NAME]) {
 -              NL_SET_ERR_MSG(cb->extack, "No region name provided");
 -              err = -EINVAL;
 -              goto out_unlock;
 -      }
 -
 -      if (info->attrs[DEVLINK_ATTR_PORT_INDEX]) {
 -              index = nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_INDEX]);
 -
 -              port = devlink_port_get_by_index(devlink, index);
 -              if (!port) {
 -                      err = -ENODEV;
 -                      goto out_unlock;
 -              }
 -      }
 -
 -      region_attr = attrs[DEVLINK_ATTR_REGION_NAME];
 -      region_name = nla_data(region_attr);
 -
 -      if (port)
 -              region = devlink_port_region_get_by_name(port, region_name);
 -      else
 -              region = devlink_region_get_by_name(devlink, region_name);
 -
 -      if (!region) {
 -              NL_SET_ERR_MSG_ATTR(cb->extack, region_attr, "Requested region does not exist");
 -              err = -EINVAL;
 -              goto out_unlock;
 -      }
 -
 -      snapshot_attr = attrs[DEVLINK_ATTR_REGION_SNAPSHOT_ID];
 -      if (!snapshot_attr) {
 -              if (!nla_get_flag(attrs[DEVLINK_ATTR_REGION_DIRECT])) {
 -                      NL_SET_ERR_MSG(cb->extack, "No snapshot id provided");
 -                      err = -EINVAL;
 -                      goto out_unlock;
 -              }
 -
 -              if (!region->ops->read) {
 -                      NL_SET_ERR_MSG(cb->extack, "Requested region does not support direct read");
 -                      err = -EOPNOTSUPP;
 -                      goto out_unlock;
 -              }
 -
 -              if (port)
 -                      region_cb = &devlink_region_port_direct_fill;
 -              else
 -                      region_cb = &devlink_region_direct_fill;
 -              region_cb_priv = region;
 -      } else {
 -              struct devlink_snapshot *snapshot;
 -              u32 snapshot_id;
 -
 -              if (nla_get_flag(attrs[DEVLINK_ATTR_REGION_DIRECT])) {
 -                      NL_SET_ERR_MSG_ATTR(cb->extack, snapshot_attr, "Direct region read does not use snapshot");
 -                      err = -EINVAL;
 -                      goto out_unlock;
 -              }
 -
 -              snapshot_id = nla_get_u32(snapshot_attr);
 -              snapshot = devlink_region_snapshot_get_by_id(region, snapshot_id);
 -              if (!snapshot) {
 -                      NL_SET_ERR_MSG_ATTR(cb->extack, snapshot_attr, "Requested snapshot does not exist");
 -                      err = -EINVAL;
 -                      goto out_unlock;
 -              }
 -              region_cb = &devlink_region_snapshot_fill;
 -              region_cb_priv = snapshot;
 -      }
 -
 -      if (attrs[DEVLINK_ATTR_REGION_CHUNK_ADDR] &&
 -          attrs[DEVLINK_ATTR_REGION_CHUNK_LEN]) {
 -              if (!start_offset)
 -                      start_offset =
 -                              nla_get_u64(attrs[DEVLINK_ATTR_REGION_CHUNK_ADDR]);
 -
 -              end_offset = nla_get_u64(attrs[DEVLINK_ATTR_REGION_CHUNK_ADDR]);
 -              end_offset += nla_get_u64(attrs[DEVLINK_ATTR_REGION_CHUNK_LEN]);
 -      }
 -
 -      if (end_offset > region->size)
 -              end_offset = region->size;
 -
 -      /* return 0 if there is no further data to read */
 -      if (start_offset == end_offset) {
 -              err = 0;
 -              goto out_unlock;
 -      }
 -
 -      hdr = genlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq,
 -                        &devlink_nl_family, NLM_F_ACK | NLM_F_MULTI,
 -                        DEVLINK_CMD_REGION_READ);
 -      if (!hdr) {
 -              err = -EMSGSIZE;
 -              goto out_unlock;
 -      }
 -
 -      err = devlink_nl_put_handle(skb, devlink);
 -      if (err)
 -              goto nla_put_failure;
 -
 -      if (region->port) {
 -              err = nla_put_u32(skb, DEVLINK_ATTR_PORT_INDEX,
 -                                region->port->index);
 -              if (err)
 -                      goto nla_put_failure;
 -      }
 -
 -      err = nla_put_string(skb, DEVLINK_ATTR_REGION_NAME, region_name);
 -      if (err)
 -              goto nla_put_failure;
 -
 -      chunks_attr = nla_nest_start_noflag(skb, DEVLINK_ATTR_REGION_CHUNKS);
 -      if (!chunks_attr) {
 -              err = -EMSGSIZE;
 -              goto nla_put_failure;
 -      }
 -
 -      err = devlink_nl_region_read_fill(skb, region_cb, region_cb_priv,
 -                                        start_offset, end_offset, &ret_offset,
 -                                        cb->extack);
 -
 -      if (err && err != -EMSGSIZE)
 -              goto nla_put_failure;
 -
 -      /* Check if there was any progress done to prevent infinite loop */
 -      if (ret_offset == start_offset) {
 -              err = -EINVAL;
 -              goto nla_put_failure;
 -      }
 -
 -      *((u64 *)&cb->args[0]) = ret_offset;
 -
 -      nla_nest_end(skb, chunks_attr);
 -      genlmsg_end(skb, hdr);
 -      devl_unlock(devlink);
 -      devlink_put(devlink);
 -      return skb->len;
 -
 -nla_put_failure:
 -      genlmsg_cancel(skb, hdr);
 -out_unlock:
 -      devl_unlock(devlink);
 -      devlink_put(devlink);
 -      return err;
 -}
 -
 -int devlink_info_serial_number_put(struct devlink_info_req *req, const char *sn)
 -{
 -      if (!req->msg)
 -              return 0;
 -      return nla_put_string(req->msg, DEVLINK_ATTR_INFO_SERIAL_NUMBER, sn);
 -}
 -EXPORT_SYMBOL_GPL(devlink_info_serial_number_put);
 -
 -int devlink_info_board_serial_number_put(struct devlink_info_req *req,
 -                                       const char *bsn)
 -{
 -      if (!req->msg)
 -              return 0;
 -      return nla_put_string(req->msg, DEVLINK_ATTR_INFO_BOARD_SERIAL_NUMBER,
 -                            bsn);
 -}
 -EXPORT_SYMBOL_GPL(devlink_info_board_serial_number_put);
 -
 -static int devlink_info_version_put(struct devlink_info_req *req, int attr,
 -                                  const char *version_name,
 -                                  const char *version_value,
 -                                  enum devlink_info_version_type version_type)
 -{
 -      struct nlattr *nest;
 -      int err;
 -
 -      if (req->version_cb)
 -              req->version_cb(version_name, version_type,
 -                              req->version_cb_priv);
 -
 -      if (!req->msg)
 -              return 0;
 -
 -      nest = nla_nest_start_noflag(req->msg, attr);
 -      if (!nest)
 -              return -EMSGSIZE;
 -
 -      err = nla_put_string(req->msg, DEVLINK_ATTR_INFO_VERSION_NAME,
 -                           version_name);
 -      if (err)
 -              goto nla_put_failure;
 -
 -      err = nla_put_string(req->msg, DEVLINK_ATTR_INFO_VERSION_VALUE,
 -                           version_value);
 -      if (err)
 -              goto nla_put_failure;
 -
 -      nla_nest_end(req->msg, nest);
 -
 -      return 0;
 +      struct devlink_param_item *param_item;
 +      unsigned long param_id;
  
 -nla_put_failure:
 -      nla_nest_cancel(req->msg, nest);
 -      return err;
 +      xa_for_each(params, param_id, param_item) {
 +              if (!strcmp(param_item->param->name, param_name))
 +                      return param_item;
 +      }
 +      return NULL;
  }
  
 -int devlink_info_version_fixed_put(struct devlink_info_req *req,
 -                                 const char *version_name,
 -                                 const char *version_value)
 +static struct devlink_param_item *
 +devlink_param_find_by_id(struct xarray *params, u32 param_id)
  {
 -      return devlink_info_version_put(req, DEVLINK_ATTR_INFO_VERSION_FIXED,
 -                                      version_name, version_value,
 -                                      DEVLINK_INFO_VERSION_TYPE_NONE);
 +      return xa_load(params, param_id);
  }
 -EXPORT_SYMBOL_GPL(devlink_info_version_fixed_put);
  
 -int devlink_info_version_stored_put(struct devlink_info_req *req,
 -                                  const char *version_name,
 -                                  const char *version_value)
 +static bool
 +devlink_param_cmode_is_supported(const struct devlink_param *param,
 +                               enum devlink_param_cmode cmode)
  {
 -      return devlink_info_version_put(req, DEVLINK_ATTR_INFO_VERSION_STORED,
 -                                      version_name, version_value,
 -                                      DEVLINK_INFO_VERSION_TYPE_NONE);
 +      return test_bit(cmode, &param->supported_cmodes);
  }
 -EXPORT_SYMBOL_GPL(devlink_info_version_stored_put);
  
 -int devlink_info_version_stored_put_ext(struct devlink_info_req *req,
 -                                      const char *version_name,
 -                                      const char *version_value,
 -                                      enum devlink_info_version_type version_type)
 +static int devlink_param_get(struct devlink *devlink,
 +                           const struct devlink_param *param,
 +                           struct devlink_param_gset_ctx *ctx)
  {
 -      return devlink_info_version_put(req, DEVLINK_ATTR_INFO_VERSION_STORED,
 -                                      version_name, version_value,
 -                                      version_type);
 +      if (!param->get || devlink->reload_failed)
 +              return -EOPNOTSUPP;
 +      return param->get(devlink, param->id, ctx);
  }
 -EXPORT_SYMBOL_GPL(devlink_info_version_stored_put_ext);
  
 -int devlink_info_version_running_put(struct devlink_info_req *req,
 -                                   const char *version_name,
 -                                   const char *version_value)
 +static int devlink_param_set(struct devlink *devlink,
 +                           const struct devlink_param *param,
 +                           struct devlink_param_gset_ctx *ctx)
  {
 -      return devlink_info_version_put(req, DEVLINK_ATTR_INFO_VERSION_RUNNING,
 -                                      version_name, version_value,
 -                                      DEVLINK_INFO_VERSION_TYPE_NONE);
 +      if (!param->set || devlink->reload_failed)
 +              return -EOPNOTSUPP;
 +      return param->set(devlink, param->id, ctx);
  }
 -EXPORT_SYMBOL_GPL(devlink_info_version_running_put);
  
 -int devlink_info_version_running_put_ext(struct devlink_info_req *req,
 -                                       const char *version_name,
 -                                       const char *version_value,
 -                                       enum devlink_info_version_type version_type)
 +static int
 +devlink_param_type_to_nla_type(enum devlink_param_type param_type)
  {
 -      return devlink_info_version_put(req, DEVLINK_ATTR_INFO_VERSION_RUNNING,
 -                                      version_name, version_value,
 -                                      version_type);
 +      switch (param_type) {
 +      case DEVLINK_PARAM_TYPE_U8:
 +              return NLA_U8;
 +      case DEVLINK_PARAM_TYPE_U16:
 +              return NLA_U16;
 +      case DEVLINK_PARAM_TYPE_U32:
 +              return NLA_U32;
 +      case DEVLINK_PARAM_TYPE_STRING:
 +              return NLA_STRING;
 +      case DEVLINK_PARAM_TYPE_BOOL:
 +              return NLA_FLAG;
 +      default:
 +              return -EINVAL;
 +      }
  }
 -EXPORT_SYMBOL_GPL(devlink_info_version_running_put_ext);
  
 -static int devlink_nl_driver_info_get(struct device_driver *drv,
 -                                    struct devlink_info_req *req)
 +static int
 +devlink_nl_param_value_fill_one(struct sk_buff *msg,
 +                              enum devlink_param_type type,
 +                              enum devlink_param_cmode cmode,
 +                              union devlink_param_value val)
  {
 -      if (!drv)
 -              return 0;
 +      struct nlattr *param_value_attr;
 +
 +      param_value_attr = nla_nest_start_noflag(msg,
 +                                               DEVLINK_ATTR_PARAM_VALUE);
 +      if (!param_value_attr)
 +              goto nla_put_failure;
 +
 +      if (nla_put_u8(msg, DEVLINK_ATTR_PARAM_VALUE_CMODE, cmode))
 +              goto value_nest_cancel;
  
 -      if (drv->name[0])
 -              return nla_put_string(req->msg, DEVLINK_ATTR_INFO_DRIVER_NAME,
 -                                    drv->name);
 +      switch (type) {
 +      case DEVLINK_PARAM_TYPE_U8:
 +              if (nla_put_u8(msg, DEVLINK_ATTR_PARAM_VALUE_DATA, val.vu8))
 +                      goto value_nest_cancel;
 +              break;
 +      case DEVLINK_PARAM_TYPE_U16:
 +              if (nla_put_u16(msg, DEVLINK_ATTR_PARAM_VALUE_DATA, val.vu16))
 +                      goto value_nest_cancel;
 +              break;
 +      case DEVLINK_PARAM_TYPE_U32:
 +              if (nla_put_u32(msg, DEVLINK_ATTR_PARAM_VALUE_DATA, val.vu32))
 +                      goto value_nest_cancel;
 +              break;
 +      case DEVLINK_PARAM_TYPE_STRING:
 +              if (nla_put_string(msg, DEVLINK_ATTR_PARAM_VALUE_DATA,
 +                                 val.vstr))
 +                      goto value_nest_cancel;
 +              break;
 +      case DEVLINK_PARAM_TYPE_BOOL:
 +              if (val.vbool &&
 +                  nla_put_flag(msg, DEVLINK_ATTR_PARAM_VALUE_DATA))
 +                      goto value_nest_cancel;
 +              break;
 +      }
  
 +      nla_nest_end(msg, param_value_attr);
        return 0;
 +
 +value_nest_cancel:
 +      nla_nest_cancel(msg, param_value_attr);
 +nla_put_failure:
 +      return -EMSGSIZE;
  }
  
 -static int
 -devlink_nl_info_fill(struct sk_buff *msg, struct devlink *devlink,
 -                   enum devlink_command cmd, u32 portid,
 -                   u32 seq, int flags, struct netlink_ext_ack *extack)
 +static int devlink_nl_param_fill(struct sk_buff *msg, struct devlink *devlink,
 +                               unsigned int port_index,
 +                               struct devlink_param_item *param_item,
 +                               enum devlink_command cmd,
 +                               u32 portid, u32 seq, int flags)
  {
 -      struct device *dev = devlink_to_dev(devlink);
 -      struct devlink_info_req req = {};
 +      union devlink_param_value param_value[DEVLINK_PARAM_CMODE_MAX + 1];
 +      bool param_value_set[DEVLINK_PARAM_CMODE_MAX + 1] = {};
 +      const struct devlink_param *param = param_item->param;
 +      struct devlink_param_gset_ctx ctx;
 +      struct nlattr *param_values_list;
 +      struct nlattr *param_attr;
 +      int nla_type;
        void *hdr;
        int err;
 +      int i;
 +
 +      /* Get value from driver part to driverinit configuration mode */
 +      for (i = 0; i <= DEVLINK_PARAM_CMODE_MAX; i++) {
 +              if (!devlink_param_cmode_is_supported(param, i))
 +                      continue;
 +              if (i == DEVLINK_PARAM_CMODE_DRIVERINIT) {
 +                      if (param_item->driverinit_value_new_valid)
 +                              param_value[i] = param_item->driverinit_value_new;
 +                      else if (param_item->driverinit_value_valid)
 +                              param_value[i] = param_item->driverinit_value;
 +                      else
 +                              return -EOPNOTSUPP;
 +              } else {
 +                      ctx.cmode = i;
 +                      err = devlink_param_get(devlink, param, &ctx);
 +                      if (err)
 +                              return err;
 +                      param_value[i] = ctx.val;
 +              }
 +              param_value_set[i] = true;
 +      }
  
        hdr = genlmsg_put(msg, portid, seq, &devlink_nl_family, flags, cmd);
        if (!hdr)
Simple merge
diff --cc net/socket.c
Simple merge
Simple merge