staging: fsl-dpaa2/ethsw: add .ndo_fdb_dump callback
authorIoana Ciornei <ioana.ciornei@nxp.com>
Mon, 29 Jul 2019 16:11:50 +0000 (19:11 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Tue, 30 Jul 2019 16:46:54 +0000 (18:46 +0200)
Implement the .ndo_fdb_dump callback for the switch net devices.  The
list of all offloaded FDB entries is retrieved through the dpsw_fdb_dump()
firmware call. Filter the entries by the switch port on which the
callback was called and for each of them create a new neighbour message.
Also remove the requirement from the TODO list.

Signed-off-by: Ioana Ciornei <ioana.ciornei@nxp.com>
Link: https://lore.kernel.org/r/1564416712-16946-4-git-send-email-ioana.ciornei@nxp.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/staging/fsl-dpaa2/ethsw/TODO
drivers/staging/fsl-dpaa2/ethsw/dpsw-cmd.h
drivers/staging/fsl-dpaa2/ethsw/dpsw.c
drivers/staging/fsl-dpaa2/ethsw/dpsw.h
drivers/staging/fsl-dpaa2/ethsw/ethsw.c

index 24b5e95..4d46857 100644 (file)
@@ -1,7 +1,6 @@
 * Add I/O capabilities on switch port netdevices. This will allow control
 traffic to reach the CPU.
 * Add ACL to redirect control traffic to CPU.
-* Add support for displaying learned FDB entries
 * Add support for multiple FDBs and switch port partitioning
 * MC firmware uprev; the DPAA2 objects used by the Ethernet Switch driver
 need to be kept in sync with binary interface changes in MC
index 14b974d..5e1339d 100644 (file)
@@ -10,7 +10,7 @@
 
 /* DPSW Version */
 #define DPSW_VER_MAJOR         8
-#define DPSW_VER_MINOR         0
+#define DPSW_VER_MINOR         1
 
 #define DPSW_CMD_BASE_VERSION  1
 #define DPSW_CMD_ID_OFFSET     4
@@ -67,6 +67,7 @@
 #define DPSW_CMDID_FDB_ADD_MULTICAST        DPSW_CMD_ID(0x086)
 #define DPSW_CMDID_FDB_REMOVE_MULTICAST     DPSW_CMD_ID(0x087)
 #define DPSW_CMDID_FDB_SET_LEARNING_MODE    DPSW_CMD_ID(0x088)
+#define DPSW_CMDID_FDB_DUMP                 DPSW_CMD_ID(0x08A)
 
 /* Macros for accessing command fields smaller than 1byte */
 #define DPSW_MASK(field)        \
@@ -351,6 +352,18 @@ struct dpsw_cmd_fdb_set_learning_mode {
        u8 mode;
 };
 
+struct dpsw_cmd_fdb_dump {
+       __le16 fdb_id;
+       __le16 pad0;
+       __le32 pad1;
+       __le64 iova_addr;
+       __le32 iova_size;
+};
+
+struct dpsw_rsp_fdb_dump {
+       __le16 num_entries;
+};
+
 struct dpsw_rsp_get_api_version {
        __le16 version_major;
        __le16 version_minor;
index cabed77..56b0fa7 100644 (file)
@@ -980,6 +980,57 @@ int dpsw_fdb_add_unicast(struct fsl_mc_io *mc_io,
        return mc_send_command(mc_io, &cmd);
 }
 
+/**
+ * dpsw_fdb_dump() - Dump the content of FDB table into memory.
+ * @mc_io:     Pointer to MC portal's I/O object
+ * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_'
+ * @token:     Token of DPSW object
+ * @fdb_id:    Forwarding Database Identifier
+ * @iova_addr: Data will be stored here as an array of struct fdb_dump_entry
+ * @iova_size: Memory size allocated at iova_addr
+ * @num_entries:Number of entries written at iova_addr
+ *
+ * Return:     Completion status. '0' on Success; Error code otherwise.
+ *
+ * The memory allocated at iova_addr must be initialized with zero before
+ * command execution. If the FDB table does not fit into memory MC will stop
+ * after the memory is filled up.
+ * The struct fdb_dump_entry array must be parsed until the end of memory
+ * area or until an entry with mac_addr set to zero is found.
+ */
+int dpsw_fdb_dump(struct fsl_mc_io *mc_io,
+                 u32 cmd_flags,
+                 u16 token,
+                 u16 fdb_id,
+                 u64 iova_addr,
+                 u32 iova_size,
+                 u16 *num_entries)
+{
+       struct dpsw_cmd_fdb_dump *cmd_params;
+       struct dpsw_rsp_fdb_dump *rsp_params;
+       struct fsl_mc_command cmd = { 0 };
+       int err;
+
+       /* prepare command */
+       cmd.header = mc_encode_cmd_header(DPSW_CMDID_FDB_DUMP,
+                                         cmd_flags,
+                                         token);
+       cmd_params = (struct dpsw_cmd_fdb_dump *)cmd.params;
+       cmd_params->fdb_id = cpu_to_le16(fdb_id);
+       cmd_params->iova_addr = cpu_to_le64(iova_addr);
+       cmd_params->iova_size = cpu_to_le32(iova_size);
+
+       /* send command to mc */
+       err = mc_send_command(mc_io, &cmd);
+       if (err)
+               return err;
+
+       rsp_params = (struct dpsw_rsp_fdb_dump *)cmd.params;
+       *num_entries = le16_to_cpu(rsp_params->num_entries);
+
+       return 0;
+}
+
 /**
  * dpsw_fdb_remove_unicast() - removes an entry from MAC lookup table
  * @mc_io:     Pointer to MC portal's I/O object
index 0d9330e..25b4585 100644 (file)
@@ -465,6 +465,31 @@ int dpsw_fdb_remove_unicast(struct fsl_mc_io *mc_io,
                            u16 fdb_id,
                            const struct dpsw_fdb_unicast_cfg *cfg);
 
+#define DPSW_FDB_ENTRY_TYPE_DYNAMIC  BIT(0)
+#define DPSW_FDB_ENTRY_TYPE_UNICAST  BIT(1)
+
+/**
+ * struct fdb_dump_entry - fdb snapshot entry
+ * @mac_addr: MAC address
+ * @type: bit0 - DINAMIC(1)/STATIC(0), bit1 - UNICAST(1)/MULTICAST(0)
+ * @if_info: unicast - egress interface, multicast - number of egress interfaces
+ * @if_mask: multicast - egress interface mask
+ */
+struct fdb_dump_entry {
+       u8 mac_addr[6];
+       u8 type;
+       u8 if_info;
+       u8 if_mask[8];
+};
+
+int dpsw_fdb_dump(struct fsl_mc_io *mc_io,
+                 u32 cmd_flags,
+                 u16 token,
+                 u16 fdb_id,
+                 u64 iova_addr,
+                 u32 iova_size,
+                 u16 *num_entries);
+
 /**
  * struct dpsw_fdb_multicast_cfg - Multi-cast entry configuration
  * @type: Select static or dynamic entry
index d6953ac..e6423f1 100644 (file)
@@ -22,7 +22,7 @@ static struct workqueue_struct *ethsw_owq;
 
 /* Minimal supported DPSW version */
 #define DPSW_MIN_VER_MAJOR             8
-#define DPSW_MIN_VER_MINOR             0
+#define DPSW_MIN_VER_MINOR             1
 
 #define DEFAULT_VLAN_ID                        1
 
@@ -529,6 +529,138 @@ static int port_get_phys_name(struct net_device *netdev, char *name,
        return 0;
 }
 
+struct ethsw_dump_ctx {
+       struct net_device *dev;
+       struct sk_buff *skb;
+       struct netlink_callback *cb;
+       int idx;
+};
+
+static int ethsw_fdb_do_dump(struct fdb_dump_entry *entry,
+                            struct ethsw_dump_ctx *dump)
+{
+       int is_dynamic = entry->type & DPSW_FDB_ENTRY_DINAMIC;
+       u32 portid = NETLINK_CB(dump->cb->skb).portid;
+       u32 seq = dump->cb->nlh->nlmsg_seq;
+       struct nlmsghdr *nlh;
+       struct ndmsg *ndm;
+
+       if (dump->idx < dump->cb->args[2])
+               goto skip;
+
+       nlh = nlmsg_put(dump->skb, portid, seq, RTM_NEWNEIGH,
+                       sizeof(*ndm), NLM_F_MULTI);
+       if (!nlh)
+               return -EMSGSIZE;
+
+       ndm = nlmsg_data(nlh);
+       ndm->ndm_family  = AF_BRIDGE;
+       ndm->ndm_pad1    = 0;
+       ndm->ndm_pad2    = 0;
+       ndm->ndm_flags   = NTF_SELF;
+       ndm->ndm_type    = 0;
+       ndm->ndm_ifindex = dump->dev->ifindex;
+       ndm->ndm_state   = is_dynamic ? NUD_REACHABLE : NUD_NOARP;
+
+       if (nla_put(dump->skb, NDA_LLADDR, ETH_ALEN, entry->mac_addr))
+               goto nla_put_failure;
+
+       nlmsg_end(dump->skb, nlh);
+
+skip:
+       dump->idx++;
+       return 0;
+
+nla_put_failure:
+       nlmsg_cancel(dump->skb, nlh);
+       return -EMSGSIZE;
+}
+
+static int port_fdb_valid_entry(struct fdb_dump_entry *entry,
+                               struct ethsw_port_priv *port_priv)
+{
+       int idx = port_priv->idx;
+       int valid;
+
+       if (entry->type & DPSW_FDB_ENTRY_TYPE_UNICAST)
+               valid = entry->if_info == port_priv->idx;
+       else
+               valid = entry->if_mask[idx / 8] & BIT(idx % 8);
+
+       return valid;
+}
+
+static int port_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
+                        struct net_device *net_dev,
+                        struct net_device *filter_dev, int *idx)
+{
+       struct ethsw_port_priv *port_priv = netdev_priv(net_dev);
+       struct ethsw_core *ethsw = port_priv->ethsw_data;
+       struct device *dev = net_dev->dev.parent;
+       struct fdb_dump_entry *fdb_entries;
+       struct fdb_dump_entry fdb_entry;
+       struct ethsw_dump_ctx dump = {
+               .dev = net_dev,
+               .skb = skb,
+               .cb = cb,
+               .idx = *idx,
+       };
+       dma_addr_t fdb_dump_iova;
+       u16 num_fdb_entries;
+       u32 fdb_dump_size;
+       int err = 0, i;
+       u8 *dma_mem;
+
+       fdb_dump_size = ethsw->sw_attr.max_fdb_entries * sizeof(fdb_entry);
+       dma_mem = kzalloc(fdb_dump_size, GFP_KERNEL);
+       if (!dma_mem)
+               return -ENOMEM;
+
+       memset(dma_mem, 0, fdb_dump_size);
+
+       fdb_dump_iova = dma_map_single(dev, dma_mem, fdb_dump_size,
+                                      DMA_FROM_DEVICE);
+       if (dma_mapping_error(dev, fdb_dump_iova)) {
+               netdev_err(net_dev, "dma_map_single() failed\n");
+               err = -ENOMEM;
+               goto err_map;
+       }
+
+       err = dpsw_fdb_dump(ethsw->mc_io, 0, ethsw->dpsw_handle, 0,
+                           fdb_dump_iova, fdb_dump_size, &num_fdb_entries);
+       if (err) {
+               netdev_err(net_dev, "dpsw_fdb_dump() = %d\n", err);
+               goto err_dump;
+       }
+
+       dma_unmap_single(dev, fdb_dump_iova, fdb_dump_size, DMA_FROM_DEVICE);
+
+       fdb_entries = (struct fdb_dump_entry *)dma_mem;
+       for (i = 0; i < num_fdb_entries; i++) {
+               fdb_entry = fdb_entries[i];
+
+               if (!port_fdb_valid_entry(&fdb_entry, port_priv))
+                       continue;
+
+               err = ethsw_fdb_do_dump(&fdb_entry, &dump);
+               if (err)
+                       goto end;
+       }
+
+end:
+       *idx = dump.idx;
+
+       kfree(dma_mem);
+
+       return 0;
+
+err_dump:
+       dma_unmap_single(dev, fdb_dump_iova, fdb_dump_size, DMA_TO_DEVICE);
+err_map:
+       kfree(dma_mem);
+       return err;
+}
+
 static const struct net_device_ops ethsw_port_ops = {
        .ndo_open               = port_open,
        .ndo_stop               = port_stop,
@@ -538,6 +670,7 @@ static const struct net_device_ops ethsw_port_ops = {
        .ndo_change_mtu         = port_change_mtu,
        .ndo_has_offload_stats  = port_has_offload_stats,
        .ndo_get_offload_stats  = port_get_offload_stats,
+       .ndo_fdb_dump           = port_fdb_dump,
 
        .ndo_start_xmit         = port_dropframe,
        .ndo_get_port_parent_id = swdev_get_port_parent_id,