b.liu | e958203 | 2025-04-17 19:18:16 +0800 | [diff] [blame] | 1 | From ea2e3b62b170e0f896aa837c8fec8085fb1063f8 Mon Sep 17 00:00:00 2001 |
| 2 | From: Ioana Radulescu <ruxandra.radulescu@nxp.com> |
| 3 | Date: Tue, 3 Sep 2019 21:11:35 +0300 |
| 4 | Subject: [PATCH] dpaa2-eth: Distribute ingress frames based on VLAN prio |
| 5 | |
| 6 | Configure static ingress classification based on VLAN PCP field. |
| 7 | If the DPNI doesn't have enough traffic classes to accommodate all |
| 8 | priority levels, the lowest ones end up on TC 0 (default on miss). |
| 9 | |
| 10 | Signed-off-by: Ioana Radulescu <ruxandra.radulescu@nxp.com> |
| 11 | --- |
| 12 | drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c | 116 ++++++++++++++++++++ |
| 13 | drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h | 1 + |
| 14 | drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h | 34 ++++++ |
| 15 | drivers/net/ethernet/freescale/dpaa2/dpni.c | 131 +++++++++++++++++++++++ |
| 16 | drivers/net/ethernet/freescale/dpaa2/dpni.h | 36 +++++++ |
| 17 | 5 files changed, 318 insertions(+) |
| 18 | |
| 19 | --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c |
| 20 | +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c |
| 21 | @@ -2608,6 +2608,118 @@ out_err: |
| 22 | priv->enqueue = dpaa2_eth_enqueue_qd; |
| 23 | } |
| 24 | |
| 25 | +/* Configure ingress classification based on VLAN PCP */ |
| 26 | +static int set_vlan_qos(struct dpaa2_eth_priv *priv) |
| 27 | +{ |
| 28 | + struct device *dev = priv->net_dev->dev.parent; |
| 29 | + struct dpkg_profile_cfg kg_cfg = {0}; |
| 30 | + struct dpni_qos_tbl_cfg qos_cfg = {0}; |
| 31 | + struct dpni_rule_cfg key_params; |
| 32 | + void *dma_mem, *key, *mask; |
| 33 | + u8 key_size = 2; /* VLAN TCI field */ |
| 34 | + int i, pcp, err; |
| 35 | + |
| 36 | + /* VLAN-based classification only makes sense if we have multiple |
| 37 | + * traffic classes. |
| 38 | + * Also, we need to extract just the 3-bit PCP field from the VLAN |
| 39 | + * header and we can only do that by using a mask |
| 40 | + */ |
| 41 | + if (dpaa2_eth_tc_count(priv) == 1 || !dpaa2_eth_fs_mask_enabled(priv)) { |
| 42 | + dev_dbg(dev, "VLAN-based QoS classification not supported\n"); |
| 43 | + return -ENOTSUPP; |
| 44 | + } |
| 45 | + |
| 46 | + dma_mem = kzalloc(DPAA2_CLASSIFIER_DMA_SIZE, GFP_KERNEL); |
| 47 | + if (!dma_mem) |
| 48 | + return -ENOMEM; |
| 49 | + |
| 50 | + kg_cfg.num_extracts = 1; |
| 51 | + kg_cfg.extracts[0].type = DPKG_EXTRACT_FROM_HDR; |
| 52 | + kg_cfg.extracts[0].extract.from_hdr.prot = NET_PROT_VLAN; |
| 53 | + kg_cfg.extracts[0].extract.from_hdr.type = DPKG_FULL_FIELD; |
| 54 | + kg_cfg.extracts[0].extract.from_hdr.field = NH_FLD_VLAN_TCI; |
| 55 | + |
| 56 | + err = dpni_prepare_key_cfg(&kg_cfg, dma_mem); |
| 57 | + if (err) { |
| 58 | + dev_err(dev, "dpni_prepare_key_cfg failed\n"); |
| 59 | + goto out_free_tbl; |
| 60 | + } |
| 61 | + |
| 62 | + /* set QoS table */ |
| 63 | + qos_cfg.default_tc = 0; |
| 64 | + qos_cfg.discard_on_miss = 0; |
| 65 | + qos_cfg.key_cfg_iova = dma_map_single(dev, dma_mem, |
| 66 | + DPAA2_CLASSIFIER_DMA_SIZE, |
| 67 | + DMA_TO_DEVICE); |
| 68 | + if (dma_mapping_error(dev, qos_cfg.key_cfg_iova)) { |
| 69 | + dev_err(dev, "QoS table DMA mapping failed\n"); |
| 70 | + err = -ENOMEM; |
| 71 | + goto out_free_tbl; |
| 72 | + } |
| 73 | + |
| 74 | + err = dpni_set_qos_table(priv->mc_io, 0, priv->mc_token, &qos_cfg); |
| 75 | + if (err) { |
| 76 | + dev_err(dev, "dpni_set_qos_table failed\n"); |
| 77 | + goto out_unmap_tbl; |
| 78 | + } |
| 79 | + |
| 80 | + /* Add QoS table entries */ |
| 81 | + key = kzalloc(key_size * 2, GFP_KERNEL); |
| 82 | + if (!key) { |
| 83 | + err = -ENOMEM; |
| 84 | + goto out_unmap_tbl; |
| 85 | + } |
| 86 | + mask = key + key_size; |
| 87 | + *(u16 *)mask = cpu_to_be16(VLAN_PRIO_MASK); |
| 88 | + |
| 89 | + key_params.key_iova = dma_map_single(dev, key, key_size * 2, |
| 90 | + DMA_TO_DEVICE); |
| 91 | + if (dma_mapping_error(dev, key_params.key_iova)) { |
| 92 | + dev_err(dev, "Qos table entry DMA mapping failed\n"); |
| 93 | + err = -ENOMEM; |
| 94 | + goto out_free_key; |
| 95 | + } |
| 96 | + |
| 97 | + key_params.mask_iova = key_params.key_iova + key_size; |
| 98 | + key_params.key_size = key_size; |
| 99 | + |
| 100 | + /* We add rules for PCP-based distribution starting with highest |
| 101 | + * priority (VLAN PCP = 7). If this DPNI doesn't have enough traffic |
| 102 | + * classes to accommodate all priority levels, the lowest ones end up |
| 103 | + * on TC 0 which was configured as default |
| 104 | + */ |
| 105 | + for (i = dpaa2_eth_tc_count(priv) - 1, pcp = 7; i >= 0; i--, pcp--) { |
| 106 | + *(u16 *)key = cpu_to_be16(pcp << VLAN_PRIO_SHIFT); |
| 107 | + dma_sync_single_for_device(dev, key_params.key_iova, |
| 108 | + key_size * 2, DMA_TO_DEVICE); |
| 109 | + |
| 110 | + err = dpni_add_qos_entry(priv->mc_io, 0, priv->mc_token, |
| 111 | + &key_params, i, i); |
| 112 | + if (err) { |
| 113 | + dev_err(dev, "dpni_add_qos_entry failed\n"); |
| 114 | + dpni_clear_qos_table(priv->mc_io, 0, priv->mc_token); |
| 115 | + goto out_unmap_key; |
| 116 | + } |
| 117 | + } |
| 118 | + |
| 119 | + priv->vlan_cls_enabled = true; |
| 120 | + |
| 121 | + /* Table and key memory is not persistent, clean everything up after |
| 122 | + * configuration is finished |
| 123 | + */ |
| 124 | +out_unmap_key: |
| 125 | + dma_unmap_single(dev, key_params.key_iova, key_size * 2, DMA_TO_DEVICE); |
| 126 | +out_free_key: |
| 127 | + kfree(key); |
| 128 | +out_unmap_tbl: |
| 129 | + dma_unmap_single(dev, qos_cfg.key_cfg_iova, DPAA2_CLASSIFIER_DMA_SIZE, |
| 130 | + DMA_TO_DEVICE); |
| 131 | +out_free_tbl: |
| 132 | + kfree(dma_mem); |
| 133 | + |
| 134 | + return err; |
| 135 | +} |
| 136 | + |
| 137 | /* Configure the DPNI object this interface is associated with */ |
| 138 | static int setup_dpni(struct fsl_mc_device *ls_dev) |
| 139 | { |
| 140 | @@ -2670,6 +2782,10 @@ static int setup_dpni(struct fsl_mc_devi |
| 141 | goto close; |
| 142 | } |
| 143 | |
| 144 | + err = set_vlan_qos(priv); |
| 145 | + if (err && err != -ENOTSUPP) |
| 146 | + goto close; |
| 147 | + |
| 148 | priv->cls_rules = devm_kzalloc(dev, sizeof(struct dpaa2_eth_cls_rule) * |
| 149 | dpaa2_eth_fs_count(priv), GFP_KERNEL); |
| 150 | if (!priv->cls_rules) { |
| 151 | --- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h |
| 152 | +++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h |
| 153 | @@ -414,6 +414,7 @@ struct dpaa2_eth_priv { |
| 154 | u64 rx_cls_fields; |
| 155 | struct dpaa2_eth_cls_rule *cls_rules; |
| 156 | u8 rx_cls_enabled; |
| 157 | + u8 vlan_cls_enabled; |
| 158 | struct bpf_prog *xdp_prog; |
| 159 | #ifdef CONFIG_DEBUG_FS |
| 160 | struct dpaa2_debugfs dbg; |
| 161 | --- a/drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h |
| 162 | +++ b/drivers/net/ethernet/freescale/dpaa2/dpni-cmd.h |
| 163 | @@ -59,6 +59,10 @@ |
| 164 | |
| 165 | #define DPNI_CMDID_SET_RX_TC_DIST DPNI_CMD(0x235) |
| 166 | |
| 167 | +#define DPNI_CMDID_SET_QOS_TBL DPNI_CMD(0x240) |
| 168 | +#define DPNI_CMDID_ADD_QOS_ENT DPNI_CMD(0x241) |
| 169 | +#define DPNI_CMDID_REMOVE_QOS_ENT DPNI_CMD(0x242) |
| 170 | +#define DPNI_CMDID_CLR_QOS_TBL DPNI_CMD(0x243) |
| 171 | #define DPNI_CMDID_ADD_FS_ENT DPNI_CMD(0x244) |
| 172 | #define DPNI_CMDID_REMOVE_FS_ENT DPNI_CMD(0x245) |
| 173 | #define DPNI_CMDID_CLR_FS_ENT DPNI_CMD(0x246) |
| 174 | @@ -567,4 +571,34 @@ struct dpni_cmd_remove_fs_entry { |
| 175 | __le64 mask_iova; |
| 176 | }; |
| 177 | |
| 178 | +#define DPNI_DISCARD_ON_MISS_SHIFT 0 |
| 179 | +#define DPNI_DISCARD_ON_MISS_SIZE 1 |
| 180 | + |
| 181 | +struct dpni_cmd_set_qos_table { |
| 182 | + __le32 pad; |
| 183 | + u8 default_tc; |
| 184 | + /* only the LSB */ |
| 185 | + u8 discard_on_miss; |
| 186 | + __le16 pad1[21]; |
| 187 | + __le64 key_cfg_iova; |
| 188 | +}; |
| 189 | + |
| 190 | +struct dpni_cmd_add_qos_entry { |
| 191 | + __le16 pad; |
| 192 | + u8 tc_id; |
| 193 | + u8 key_size; |
| 194 | + __le16 index; |
| 195 | + __le16 pad1; |
| 196 | + __le64 key_iova; |
| 197 | + __le64 mask_iova; |
| 198 | +}; |
| 199 | + |
| 200 | +struct dpni_cmd_remove_qos_entry { |
| 201 | + u8 pad[3]; |
| 202 | + u8 key_size; |
| 203 | + __le32 pad1; |
| 204 | + __le64 key_iova; |
| 205 | + __le64 mask_iova; |
| 206 | +}; |
| 207 | + |
| 208 | #endif /* _FSL_DPNI_CMD_H */ |
| 209 | --- a/drivers/net/ethernet/freescale/dpaa2/dpni.c |
| 210 | +++ b/drivers/net/ethernet/freescale/dpaa2/dpni.c |
| 211 | @@ -1786,3 +1786,134 @@ int dpni_remove_fs_entry(struct fsl_mc_i |
| 212 | /* send command to mc*/ |
| 213 | return mc_send_command(mc_io, &cmd); |
| 214 | } |
| 215 | + |
| 216 | +/** |
| 217 | + * dpni_set_qos_table() - Set QoS mapping table |
| 218 | + * @mc_io: Pointer to MC portal's I/O object |
| 219 | + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' |
| 220 | + * @token: Token of DPNI object |
| 221 | + * @cfg: QoS table configuration |
| 222 | + * |
| 223 | + * This function and all QoS-related functions require that |
| 224 | + *'max_tcs > 1' was set at DPNI creation. |
| 225 | + * |
| 226 | + * warning: Before calling this function, call dpkg_prepare_key_cfg() to |
| 227 | + * prepare the key_cfg_iova parameter |
| 228 | + * |
| 229 | + * Return: '0' on Success; Error code otherwise. |
| 230 | + */ |
| 231 | +int dpni_set_qos_table(struct fsl_mc_io *mc_io, |
| 232 | + u32 cmd_flags, |
| 233 | + u16 token, |
| 234 | + const struct dpni_qos_tbl_cfg *cfg) |
| 235 | +{ |
| 236 | + struct dpni_cmd_set_qos_table *cmd_params; |
| 237 | + struct fsl_mc_command cmd = { 0 }; |
| 238 | + |
| 239 | + /* prepare command */ |
| 240 | + cmd.header = mc_encode_cmd_header(DPNI_CMDID_SET_QOS_TBL, |
| 241 | + cmd_flags, |
| 242 | + token); |
| 243 | + cmd_params = (struct dpni_cmd_set_qos_table *)cmd.params; |
| 244 | + cmd_params->default_tc = cfg->default_tc; |
| 245 | + cmd_params->key_cfg_iova = cpu_to_le64(cfg->key_cfg_iova); |
| 246 | + dpni_set_field(cmd_params->discard_on_miss, DISCARD_ON_MISS, |
| 247 | + cfg->discard_on_miss); |
| 248 | + |
| 249 | + /* send command to mc*/ |
| 250 | + return mc_send_command(mc_io, &cmd); |
| 251 | +} |
| 252 | + |
| 253 | +/** |
| 254 | + * dpni_add_qos_entry() - Add QoS mapping entry (to select a traffic class) |
| 255 | + * @mc_io: Pointer to MC portal's I/O object |
| 256 | + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' |
| 257 | + * @token: Token of DPNI object |
| 258 | + * @cfg: QoS rule to add |
| 259 | + * @tc_id: Traffic class selection (0-7) |
| 260 | + * @index: Location in the QoS table where to insert the entry. |
| 261 | + * Only relevant if MASKING is enabled for QoS classification on |
| 262 | + * this DPNI, it is ignored for exact match. |
| 263 | + * |
| 264 | + * Return: '0' on Success; Error code otherwise. |
| 265 | + */ |
| 266 | +int dpni_add_qos_entry(struct fsl_mc_io *mc_io, |
| 267 | + u32 cmd_flags, |
| 268 | + u16 token, |
| 269 | + const struct dpni_rule_cfg *cfg, |
| 270 | + u8 tc_id, |
| 271 | + u16 index) |
| 272 | +{ |
| 273 | + struct dpni_cmd_add_qos_entry *cmd_params; |
| 274 | + struct fsl_mc_command cmd = { 0 }; |
| 275 | + |
| 276 | + /* prepare command */ |
| 277 | + cmd.header = mc_encode_cmd_header(DPNI_CMDID_ADD_QOS_ENT, |
| 278 | + cmd_flags, |
| 279 | + token); |
| 280 | + cmd_params = (struct dpni_cmd_add_qos_entry *)cmd.params; |
| 281 | + cmd_params->tc_id = tc_id; |
| 282 | + cmd_params->key_size = cfg->key_size; |
| 283 | + cmd_params->index = cpu_to_le16(index); |
| 284 | + cmd_params->key_iova = cpu_to_le64(cfg->key_iova); |
| 285 | + cmd_params->mask_iova = cpu_to_le64(cfg->mask_iova); |
| 286 | + |
| 287 | + /* send command to mc*/ |
| 288 | + return mc_send_command(mc_io, &cmd); |
| 289 | +} |
| 290 | + |
| 291 | +/** |
| 292 | + * dpni_remove_qos_entry() - Remove QoS mapping entry |
| 293 | + * @mc_io: Pointer to MC portal's I/O object |
| 294 | + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' |
| 295 | + * @token: Token of DPNI object |
| 296 | + * @cfg: QoS rule to remove |
| 297 | + * |
| 298 | + * Return: '0' on Success; Error code otherwise. |
| 299 | + */ |
| 300 | +int dpni_remove_qos_entry(struct fsl_mc_io *mc_io, |
| 301 | + u32 cmd_flags, |
| 302 | + u16 token, |
| 303 | + const struct dpni_rule_cfg *cfg) |
| 304 | +{ |
| 305 | + struct dpni_cmd_remove_qos_entry *cmd_params; |
| 306 | + struct fsl_mc_command cmd = { 0 }; |
| 307 | + |
| 308 | + /* prepare command */ |
| 309 | + cmd.header = mc_encode_cmd_header(DPNI_CMDID_REMOVE_QOS_ENT, |
| 310 | + cmd_flags, |
| 311 | + token); |
| 312 | + cmd_params = (struct dpni_cmd_remove_qos_entry *)cmd.params; |
| 313 | + cmd_params->key_size = cfg->key_size; |
| 314 | + cmd_params->key_iova = cpu_to_le64(cfg->key_iova); |
| 315 | + cmd_params->mask_iova = cpu_to_le64(cfg->mask_iova); |
| 316 | + |
| 317 | + /* send command to mc*/ |
| 318 | + return mc_send_command(mc_io, &cmd); |
| 319 | +} |
| 320 | + |
| 321 | +/** |
| 322 | + * dpni_clear_qos_table() - Clear all QoS mapping entries |
| 323 | + * @mc_io: Pointer to MC portal's I/O object |
| 324 | + * @cmd_flags: Command flags; one or more of 'MC_CMD_FLAG_' |
| 325 | + * @token: Token of DPNI object |
| 326 | + * |
| 327 | + * Following this function call, all frames are directed to |
| 328 | + * the default traffic class (0) |
| 329 | + * |
| 330 | + * Return: '0' on Success; Error code otherwise. |
| 331 | + */ |
| 332 | +int dpni_clear_qos_table(struct fsl_mc_io *mc_io, |
| 333 | + u32 cmd_flags, |
| 334 | + u16 token) |
| 335 | +{ |
| 336 | + struct fsl_mc_command cmd = { 0 }; |
| 337 | + |
| 338 | + /* prepare command */ |
| 339 | + cmd.header = mc_encode_cmd_header(DPNI_CMDID_CLR_QOS_TBL, |
| 340 | + cmd_flags, |
| 341 | + token); |
| 342 | + |
| 343 | + /* send command to mc*/ |
| 344 | + return mc_send_command(mc_io, &cmd); |
| 345 | +} |
| 346 | --- a/drivers/net/ethernet/freescale/dpaa2/dpni.h |
| 347 | +++ b/drivers/net/ethernet/freescale/dpaa2/dpni.h |
| 348 | @@ -716,6 +716,26 @@ int dpni_set_rx_hash_dist(struct fsl_mc_ |
| 349 | const struct dpni_rx_dist_cfg *cfg); |
| 350 | |
| 351 | /** |
| 352 | + * struct dpni_qos_tbl_cfg - Structure representing QOS table configuration |
| 353 | + * @key_cfg_iova: I/O virtual address of 256 bytes DMA-able memory filled with |
| 354 | + * key extractions to be used as the QoS criteria by calling |
| 355 | + * dpkg_prepare_key_cfg() |
| 356 | + * @discard_on_miss: Set to '1' to discard frames in case of no match (miss); |
| 357 | + * '0' to use the 'default_tc' in such cases |
| 358 | + * @default_tc: Used in case of no-match and 'discard_on_miss'= 0 |
| 359 | + */ |
| 360 | +struct dpni_qos_tbl_cfg { |
| 361 | + u64 key_cfg_iova; |
| 362 | + int discard_on_miss; |
| 363 | + u8 default_tc; |
| 364 | +}; |
| 365 | + |
| 366 | +int dpni_set_qos_table(struct fsl_mc_io *mc_io, |
| 367 | + u32 cmd_flags, |
| 368 | + u16 token, |
| 369 | + const struct dpni_qos_tbl_cfg *cfg); |
| 370 | + |
| 371 | +/** |
| 372 | * enum dpni_dest - DPNI destination types |
| 373 | * @DPNI_DEST_NONE: Unassigned destination; The queue is set in parked mode and |
| 374 | * does not generate FQDAN notifications; user is expected to |
| 375 | @@ -961,6 +981,22 @@ int dpni_remove_fs_entry(struct fsl_mc_i |
| 376 | u8 tc_id, |
| 377 | const struct dpni_rule_cfg *cfg); |
| 378 | |
| 379 | +int dpni_add_qos_entry(struct fsl_mc_io *mc_io, |
| 380 | + u32 cmd_flags, |
| 381 | + u16 token, |
| 382 | + const struct dpni_rule_cfg *cfg, |
| 383 | + u8 tc_id, |
| 384 | + u16 index); |
| 385 | + |
| 386 | +int dpni_remove_qos_entry(struct fsl_mc_io *mc_io, |
| 387 | + u32 cmd_flags, |
| 388 | + u16 token, |
| 389 | + const struct dpni_rule_cfg *cfg); |
| 390 | + |
| 391 | +int dpni_clear_qos_table(struct fsl_mc_io *mc_io, |
| 392 | + u32 cmd_flags, |
| 393 | + u16 token); |
| 394 | + |
| 395 | int dpni_get_api_version(struct fsl_mc_io *mc_io, |
| 396 | u32 cmd_flags, |
| 397 | u16 *major_ver, |