mlx4: In RoCE allow guests to have multiple GIDS
[cascardo/linux.git] / drivers / net / ethernet / mellanox / mlx4 / port.c
index a58bcbf..ece3281 100644 (file)
@@ -505,6 +505,32 @@ int mlx4_get_port_ib_caps(struct mlx4_dev *dev, u8 port, __be32 *caps)
        mlx4_free_cmd_mailbox(dev, outmailbox);
        return err;
 }
+static struct mlx4_roce_gid_entry zgid_entry;
+
+int mlx4_get_slave_num_gids(struct mlx4_dev *dev, int slave)
+{
+       if (slave == 0)
+               return MLX4_ROCE_PF_GIDS;
+       if (slave <= ((MLX4_ROCE_MAX_GIDS - MLX4_ROCE_PF_GIDS) % dev->num_vfs))
+               return ((MLX4_ROCE_MAX_GIDS - MLX4_ROCE_PF_GIDS) / dev->num_vfs) + 1;
+       return (MLX4_ROCE_MAX_GIDS - MLX4_ROCE_PF_GIDS) / dev->num_vfs;
+}
+
+int mlx4_get_base_gid_ix(struct mlx4_dev *dev, int slave)
+{
+       int gids;
+       int vfs;
+
+       gids = MLX4_ROCE_MAX_GIDS - MLX4_ROCE_PF_GIDS;
+       vfs = dev->num_vfs;
+
+       if (slave == 0)
+               return 0;
+       if (slave <= gids % vfs)
+               return MLX4_ROCE_PF_GIDS + ((gids / vfs) + 1) * (slave - 1);
+
+       return MLX4_ROCE_PF_GIDS + (gids % vfs) + ((gids / vfs) * (slave - 1));
+}
 
 static int mlx4_common_set_port(struct mlx4_dev *dev, int slave, u32 in_mod,
                                u8 op_mod, struct mlx4_cmd_mailbox *inbox)
@@ -515,14 +541,18 @@ static int mlx4_common_set_port(struct mlx4_dev *dev, int slave, u32 in_mod,
        struct mlx4_slave_state *slave_st = &master->slave_state[slave];
        struct mlx4_set_port_rqp_calc_context *qpn_context;
        struct mlx4_set_port_general_context *gen_context;
+       struct mlx4_roce_gid_entry *gid_entry_tbl, *gid_entry_mbox, *gid_entry_mb1;
        int reset_qkey_viols;
        int port;
        int is_eth;
+       int num_gids;
+       int base;
        u32 in_modifier;
        u32 promisc;
        u16 mtu, prev_mtu;
        int err;
-       int i;
+       int i, j;
+       int offset;
        __be32 agg_cap_mask;
        __be32 slave_cap_mask;
        __be32 new_cap_mask;
@@ -535,7 +565,8 @@ static int mlx4_common_set_port(struct mlx4_dev *dev, int slave, u32 in_mod,
        /* Slaves cannot perform SET_PORT operations except changing MTU */
        if (is_eth) {
                if (slave != dev->caps.function &&
-                   in_modifier != MLX4_SET_PORT_GENERAL) {
+                   in_modifier != MLX4_SET_PORT_GENERAL &&
+                   in_modifier != MLX4_SET_PORT_GID_TABLE) {
                        mlx4_warn(dev, "denying SET_PORT for slave:%d\n",
                                        slave);
                        return -EINVAL;
@@ -581,6 +612,67 @@ static int mlx4_common_set_port(struct mlx4_dev *dev, int slave, u32 in_mod,
 
                        gen_context->mtu = cpu_to_be16(master->max_mtu[port]);
                        break;
+               case MLX4_SET_PORT_GID_TABLE:
+                       /* change to MULTIPLE entries: number of guest's gids
+                        * need a FOR-loop here over number of gids the guest has.
+                        * 1. Check no duplicates in gids passed by slave
+                        */
+                       num_gids = mlx4_get_slave_num_gids(dev, slave);
+                       base = mlx4_get_base_gid_ix(dev, slave);
+                       gid_entry_mbox = (struct mlx4_roce_gid_entry *)(inbox->buf);
+                       for (i = 0; i < num_gids; gid_entry_mbox++, i++) {
+                               if (!memcmp(gid_entry_mbox->raw, zgid_entry.raw,
+                                           sizeof(zgid_entry)))
+                                       continue;
+                               gid_entry_mb1 = gid_entry_mbox + 1;
+                               for (j = i + 1; j < num_gids; gid_entry_mb1++, j++) {
+                                       if (!memcmp(gid_entry_mb1->raw,
+                                                   zgid_entry.raw, sizeof(zgid_entry)))
+                                               continue;
+                                       if (!memcmp(gid_entry_mb1->raw, gid_entry_mbox->raw,
+                                                   sizeof(gid_entry_mbox->raw))) {
+                                               /* found duplicate */
+                                               return -EINVAL;
+                                       }
+                               }
+                       }
+
+                       /* 2. Check that do not have duplicates in OTHER
+                        *    entries in the port GID table
+                        */
+                       for (i = 0; i < MLX4_ROCE_MAX_GIDS; i++) {
+                               if (i >= base && i < base + num_gids)
+                                       continue; /* don't compare to slave's current gids */
+                               gid_entry_tbl = &priv->roce_gids[port - 1][i];
+                               if (!memcmp(gid_entry_tbl->raw, zgid_entry.raw, sizeof(zgid_entry)))
+                                       continue;
+                               gid_entry_mbox = (struct mlx4_roce_gid_entry *)(inbox->buf);
+                               for (j = 0; j < num_gids; gid_entry_mbox++, j++) {
+                                       if (!memcmp(gid_entry_mbox->raw, zgid_entry.raw,
+                                                   sizeof(zgid_entry)))
+                                               continue;
+                                       if (!memcmp(gid_entry_mbox->raw, gid_entry_tbl->raw,
+                                                   sizeof(gid_entry_tbl->raw))) {
+                                               /* found duplicate */
+                                               mlx4_warn(dev, "requested gid entry for slave:%d "
+                                                         "is a duplicate of gid at index %d\n",
+                                                         slave, i);
+                                               return -EINVAL;
+                                       }
+                               }
+                       }
+
+                       /* insert slave GIDs with memcpy, starting at slave's base index */
+                       gid_entry_mbox = (struct mlx4_roce_gid_entry *)(inbox->buf);
+                       for (i = 0, offset = base; i < num_gids; gid_entry_mbox++, offset++, i++)
+                               memcpy(priv->roce_gids[port - 1][offset].raw, gid_entry_mbox->raw, 16);
+
+                       /* Now, copy roce port gids table to current mailbox for passing to FW */
+                       gid_entry_mbox = (struct mlx4_roce_gid_entry *)(inbox->buf);
+                       for (i = 0; i < MLX4_ROCE_MAX_GIDS; gid_entry_mbox++, i++)
+                               memcpy(gid_entry_mbox->raw, priv->roce_gids[port - 1][i].raw, 16);
+
+                       break;
                }
                return mlx4_cmd(dev, inbox->dma, in_mod, op_mod,
                                MLX4_CMD_SET_PORT, MLX4_CMD_TIME_CLASS_B,
@@ -927,3 +1019,51 @@ void mlx4_set_stats_bitmap(struct mlx4_dev *dev, u64 *stats_bitmap)
                *stats_bitmap |= MLX4_STATS_ERROR_COUNTERS_MASK;
 }
 EXPORT_SYMBOL(mlx4_set_stats_bitmap);
+
+int mlx4_get_slave_from_roce_gid(struct mlx4_dev *dev, int port, u8 *gid,
+                                int *slave_id)
+{
+       struct mlx4_priv *priv = mlx4_priv(dev);
+       int i, found_ix = -1;
+       int vf_gids = MLX4_ROCE_MAX_GIDS - MLX4_ROCE_PF_GIDS;
+
+       if (!mlx4_is_mfunc(dev))
+               return -EINVAL;
+
+       for (i = 0; i < MLX4_ROCE_MAX_GIDS; i++) {
+               if (!memcmp(priv->roce_gids[port - 1][i].raw, gid, 16)) {
+                       found_ix = i;
+                       break;
+               }
+       }
+
+       if (found_ix >= 0) {
+               if (found_ix < MLX4_ROCE_PF_GIDS)
+                       *slave_id = 0;
+               else if (found_ix < MLX4_ROCE_PF_GIDS + (vf_gids % dev->num_vfs) *
+                        (vf_gids / dev->num_vfs + 1))
+                       *slave_id = ((found_ix - MLX4_ROCE_PF_GIDS) /
+                                    (vf_gids / dev->num_vfs + 1)) + 1;
+               else
+                       *slave_id =
+                       ((found_ix - MLX4_ROCE_PF_GIDS -
+                         ((vf_gids % dev->num_vfs) * ((vf_gids / dev->num_vfs + 1)))) /
+                        (vf_gids / dev->num_vfs)) + vf_gids % dev->num_vfs + 1;
+       }
+
+       return (found_ix >= 0) ? 0 : -EINVAL;
+}
+EXPORT_SYMBOL(mlx4_get_slave_from_roce_gid);
+
+int mlx4_get_roce_gid_from_slave(struct mlx4_dev *dev, int port, int slave_id,
+                                u8 *gid)
+{
+       struct mlx4_priv *priv = mlx4_priv(dev);
+
+       if (!mlx4_is_master(dev))
+               return -EINVAL;
+
+       memcpy(gid, priv->roce_gids[port - 1][slave_id].raw, 16);
+       return 0;
+}
+EXPORT_SYMBOL(mlx4_get_roce_gid_from_slave);