FILE
psp.c
File preview with syntax highlighting.
linux-6.19.14/drivers/net/ethernet/mellanox/mlx5/core/en_accel/psp.c
1// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB 2/* Copyright (c) 2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved. */ 3#include <linux/mlx5/device.h> 4#include <net/psp.h> 5#include <linux/psp.h> 6#include "mlx5_core.h" 7#include "psp.h" 8#include "lib/crypto.h" 9#include "en_accel/psp.h" 10#include "fs_core.h" 11 12enum accel_fs_psp_type { 13 ACCEL_FS_PSP4, 14 ACCEL_FS_PSP6, 15 ACCEL_FS_PSP_NUM_TYPES, 16}; 17 18enum accel_psp_syndrome { 19 PSP_OK = 0, 20 PSP_ICV_FAIL, 21 PSP_BAD_TRAILER, 22}; 23 24struct mlx5e_psp_tx { 25 struct mlx5_flow_namespace *ns; 26 struct mlx5_flow_table *ft; 27 struct mlx5_flow_group *fg; 28 struct mlx5_flow_handle *rule; 29 struct mutex mutex; /* Protect PSP TX steering */ 30 u32 refcnt; 31 struct mlx5_fc *tx_counter; 32}; 33 34struct mlx5e_psp_rx_err { 35 struct mlx5_flow_table *ft; 36 struct mlx5_flow_handle *rule; 37 struct mlx5_flow_handle *auth_fail_rule; 38 struct mlx5_flow_handle *err_rule; 39 struct mlx5_flow_handle *bad_rule; 40 struct mlx5_modify_hdr *copy_modify_hdr; 41}; 42 43struct mlx5e_accel_fs_psp_prot { 44 struct mlx5_flow_table *ft; 45 struct mlx5_flow_group *miss_group; 46 struct mlx5_flow_handle *miss_rule; 47 struct mlx5_modify_hdr *rx_modify_hdr; 48 struct mlx5_flow_destination default_dest; 49 struct mlx5e_psp_rx_err rx_err; 50 u32 refcnt; 51 struct mutex prot_mutex; /* protect ESP4/ESP6 protocol */ 52 struct mlx5_flow_handle *def_rule; 53}; 54 55struct mlx5e_accel_fs_psp { 56 struct mlx5e_accel_fs_psp_prot fs_prot[ACCEL_FS_PSP_NUM_TYPES]; 57 struct mlx5_fc *rx_counter; 58 struct mlx5_fc *rx_auth_fail_counter; 59 struct mlx5_fc *rx_err_counter; 60 struct mlx5_fc *rx_bad_counter; 61}; 62 63struct mlx5e_psp_fs { 64 struct mlx5_core_dev *mdev; 65 struct mlx5e_psp_tx *tx_fs; 66 /* Rx manage */ 67 struct mlx5e_flow_steering *fs; 68 struct mlx5e_accel_fs_psp *rx_fs; 69}; 70 71/* PSP RX flow steering */ 72static enum mlx5_traffic_types fs_psp2tt(enum accel_fs_psp_type i) 73{ 74 if (i == ACCEL_FS_PSP4) 75 return MLX5_TT_IPV4_UDP; 76 77 return MLX5_TT_IPV6_UDP; 78} 79 80static void accel_psp_fs_rx_err_del_rules(struct mlx5e_psp_fs *fs, 81 struct mlx5e_psp_rx_err *rx_err) 82{ 83 if (rx_err->bad_rule) { 84 mlx5_del_flow_rules(rx_err->bad_rule); 85 rx_err->bad_rule = NULL; 86 } 87 88 if (rx_err->err_rule) { 89 mlx5_del_flow_rules(rx_err->err_rule); 90 rx_err->err_rule = NULL; 91 } 92 93 if (rx_err->auth_fail_rule) { 94 mlx5_del_flow_rules(rx_err->auth_fail_rule); 95 rx_err->auth_fail_rule = NULL; 96 } 97 98 if (rx_err->rule) { 99 mlx5_del_flow_rules(rx_err->rule); 100 rx_err->rule = NULL; 101 } 102 103 if (rx_err->copy_modify_hdr) { 104 mlx5_modify_header_dealloc(fs->mdev, rx_err->copy_modify_hdr); 105 rx_err->copy_modify_hdr = NULL; 106 } 107} 108 109static void accel_psp_fs_rx_err_destroy_ft(struct mlx5e_psp_fs *fs, 110 struct mlx5e_psp_rx_err *rx_err) 111{ 112 accel_psp_fs_rx_err_del_rules(fs, rx_err); 113 114 if (rx_err->ft) { 115 mlx5_destroy_flow_table(rx_err->ft); 116 rx_err->ft = NULL; 117 } 118} 119 120static void accel_psp_setup_syndrome_match(struct mlx5_flow_spec *spec, 121 enum accel_psp_syndrome syndrome) 122{ 123 void *misc_params_2; 124 125 spec->match_criteria_enable |= MLX5_MATCH_MISC_PARAMETERS_2; 126 misc_params_2 = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, misc_parameters_2); 127 MLX5_SET_TO_ONES(fte_match_set_misc2, misc_params_2, psp_syndrome); 128 misc_params_2 = MLX5_ADDR_OF(fte_match_param, spec->match_value, misc_parameters_2); 129 MLX5_SET(fte_match_set_misc2, misc_params_2, psp_syndrome, syndrome); 130} 131 132static int accel_psp_fs_rx_err_add_rule(struct mlx5e_psp_fs *fs, 133 struct mlx5e_accel_fs_psp_prot *fs_prot, 134 struct mlx5e_psp_rx_err *rx_err) 135{ 136 u8 action[MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto)] = {}; 137 struct mlx5_core_dev *mdev = fs->mdev; 138 struct mlx5_flow_destination dest[2]; 139 struct mlx5_flow_act flow_act = {}; 140 struct mlx5_modify_hdr *modify_hdr; 141 struct mlx5_flow_handle *fte; 142 struct mlx5_flow_spec *spec; 143 int err = 0; 144 145 spec = kzalloc(sizeof(*spec), GFP_KERNEL); 146 if (!spec) 147 return -ENOMEM; 148 149 /* Action to copy 7 bit psp_syndrome to regB[23:29] */ 150 MLX5_SET(copy_action_in, action, action_type, MLX5_ACTION_TYPE_COPY); 151 MLX5_SET(copy_action_in, action, src_field, MLX5_ACTION_IN_FIELD_PSP_SYNDROME); 152 MLX5_SET(copy_action_in, action, src_offset, 0); 153 MLX5_SET(copy_action_in, action, length, 7); 154 MLX5_SET(copy_action_in, action, dst_field, MLX5_ACTION_IN_FIELD_METADATA_REG_B); 155 MLX5_SET(copy_action_in, action, dst_offset, 23); 156 157 modify_hdr = mlx5_modify_header_alloc(mdev, MLX5_FLOW_NAMESPACE_KERNEL, 158 1, action); 159 if (IS_ERR(modify_hdr)) { 160 err = PTR_ERR(modify_hdr); 161 mlx5_core_err(mdev, 162 "fail to alloc psp copy modify_header_id err=%d\n", err); 163 goto out_spec; 164 } 165 166 accel_psp_setup_syndrome_match(spec, PSP_OK); 167 /* create fte */ 168 flow_act.action = MLX5_FLOW_CONTEXT_ACTION_MOD_HDR | 169 MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | 170 MLX5_FLOW_CONTEXT_ACTION_COUNT; 171 flow_act.modify_hdr = modify_hdr; 172 dest[0].type = fs_prot->default_dest.type; 173 dest[0].ft = fs_prot->default_dest.ft; 174 dest[1].type = MLX5_FLOW_DESTINATION_TYPE_COUNTER; 175 dest[1].counter = fs->rx_fs->rx_counter; 176 fte = mlx5_add_flow_rules(rx_err->ft, spec, &flow_act, dest, 2); 177 if (IS_ERR(fte)) { 178 err = PTR_ERR(fte); 179 mlx5_core_err(mdev, "fail to add psp rx err copy rule err=%d\n", err); 180 goto out; 181 } 182 rx_err->rule = fte; 183 184 /* add auth fail drop rule */ 185 memset(spec, 0, sizeof(*spec)); 186 memset(&flow_act, 0, sizeof(flow_act)); 187 accel_psp_setup_syndrome_match(spec, PSP_ICV_FAIL); 188 /* create fte */ 189 flow_act.action = MLX5_FLOW_CONTEXT_ACTION_DROP | 190 MLX5_FLOW_CONTEXT_ACTION_COUNT; 191 dest[0].type = MLX5_FLOW_DESTINATION_TYPE_COUNTER; 192 dest[0].counter = fs->rx_fs->rx_auth_fail_counter; 193 fte = mlx5_add_flow_rules(rx_err->ft, spec, &flow_act, dest, 1); 194 if (IS_ERR(fte)) { 195 err = PTR_ERR(fte); 196 mlx5_core_err(mdev, "fail to add psp rx auth fail drop rule err=%d\n", 197 err); 198 goto out_drop_rule; 199 } 200 rx_err->auth_fail_rule = fte; 201 202 /* add framing drop rule */ 203 memset(spec, 0, sizeof(*spec)); 204 memset(&flow_act, 0, sizeof(flow_act)); 205 accel_psp_setup_syndrome_match(spec, PSP_BAD_TRAILER); 206 /* create fte */ 207 flow_act.action = MLX5_FLOW_CONTEXT_ACTION_DROP | 208 MLX5_FLOW_CONTEXT_ACTION_COUNT; 209 dest[0].type = MLX5_FLOW_DESTINATION_TYPE_COUNTER; 210 dest[0].counter = fs->rx_fs->rx_err_counter; 211 fte = mlx5_add_flow_rules(rx_err->ft, spec, &flow_act, dest, 1); 212 if (IS_ERR(fte)) { 213 err = PTR_ERR(fte); 214 mlx5_core_err(mdev, "fail to add psp rx framing err drop rule err=%d\n", 215 err); 216 goto out_drop_auth_fail_rule; 217 } 218 rx_err->err_rule = fte; 219 220 /* add misc. errors drop rule */ 221 memset(spec, 0, sizeof(*spec)); 222 memset(&flow_act, 0, sizeof(flow_act)); 223 /* create fte */ 224 flow_act.action = MLX5_FLOW_CONTEXT_ACTION_DROP | 225 MLX5_FLOW_CONTEXT_ACTION_COUNT; 226 dest[0].type = MLX5_FLOW_DESTINATION_TYPE_COUNTER; 227 dest[0].counter = fs->rx_fs->rx_bad_counter; 228 fte = mlx5_add_flow_rules(rx_err->ft, spec, &flow_act, dest, 1); 229 if (IS_ERR(fte)) { 230 err = PTR_ERR(fte); 231 mlx5_core_err(mdev, "fail to add psp rx misc. err drop rule err=%d\n", 232 err); 233 goto out_drop_error_rule; 234 } 235 rx_err->bad_rule = fte; 236 237 rx_err->copy_modify_hdr = modify_hdr; 238 239 goto out_spec; 240 241out_drop_error_rule: 242 mlx5_del_flow_rules(rx_err->err_rule); 243 rx_err->err_rule = NULL; 244out_drop_auth_fail_rule: 245 mlx5_del_flow_rules(rx_err->auth_fail_rule); 246 rx_err->auth_fail_rule = NULL; 247out_drop_rule: 248 mlx5_del_flow_rules(rx_err->rule); 249 rx_err->rule = NULL; 250out: 251 mlx5_modify_header_dealloc(mdev, modify_hdr); 252out_spec: 253 kfree(spec); 254 return err; 255} 256 257static int accel_psp_fs_rx_err_create_ft(struct mlx5e_psp_fs *fs, 258 struct mlx5e_accel_fs_psp_prot *fs_prot, 259 struct mlx5e_psp_rx_err *rx_err) 260{ 261 struct mlx5_flow_namespace *ns = mlx5e_fs_get_ns(fs->fs, false); 262 struct mlx5_flow_table_attr ft_attr = {}; 263 struct mlx5_flow_table *ft; 264 int err; 265 266 ft_attr.max_fte = 2; 267 ft_attr.autogroup.max_num_groups = 2; 268 ft_attr.level = MLX5E_ACCEL_FS_ESP_FT_ERR_LEVEL; // MLX5E_ACCEL_FS_TCP_FT_LEVEL 269 ft_attr.prio = MLX5E_NIC_PRIO; 270 ft = mlx5_create_auto_grouped_flow_table(ns, &ft_attr); 271 if (IS_ERR(ft)) { 272 err = PTR_ERR(ft); 273 mlx5_core_err(fs->mdev, "fail to create psp rx inline ft err=%d\n", err); 274 return err; 275 } 276 277 rx_err->ft = ft; 278 err = accel_psp_fs_rx_err_add_rule(fs, fs_prot, rx_err); 279 if (err) 280 goto out_err; 281 282 return 0; 283 284out_err: 285 mlx5_destroy_flow_table(ft); 286 rx_err->ft = NULL; 287 return err; 288} 289 290static void accel_psp_fs_rx_fs_destroy(struct mlx5e_psp_fs *fs, 291 struct mlx5e_accel_fs_psp_prot *fs_prot) 292{ 293 if (fs_prot->def_rule) { 294 mlx5_del_flow_rules(fs_prot->def_rule); 295 fs_prot->def_rule = NULL; 296 } 297 298 if (fs_prot->rx_modify_hdr) { 299 mlx5_modify_header_dealloc(fs->mdev, fs_prot->rx_modify_hdr); 300 fs_prot->rx_modify_hdr = NULL; 301 } 302 303 if (fs_prot->miss_rule) { 304 mlx5_del_flow_rules(fs_prot->miss_rule); 305 fs_prot->miss_rule = NULL; 306 } 307 308 if (fs_prot->miss_group) { 309 mlx5_destroy_flow_group(fs_prot->miss_group); 310 fs_prot->miss_group = NULL; 311 } 312 313 if (fs_prot->ft) { 314 mlx5_destroy_flow_table(fs_prot->ft); 315 fs_prot->ft = NULL; 316 } 317} 318 319static void setup_fte_udp_psp(struct mlx5_flow_spec *spec, u16 udp_port) 320{ 321 spec->match_criteria_enable |= MLX5_MATCH_OUTER_HEADERS; 322 MLX5_SET(fte_match_set_lyr_2_4, spec->match_criteria, udp_dport, 0xffff); 323 MLX5_SET(fte_match_set_lyr_2_4, spec->match_value, udp_dport, udp_port); 324 MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, spec->match_criteria, ip_protocol); 325 MLX5_SET(fte_match_set_lyr_2_4, spec->match_value, ip_protocol, IPPROTO_UDP); 326} 327 328static int accel_psp_fs_rx_create_ft(struct mlx5e_psp_fs *fs, 329 struct mlx5e_accel_fs_psp_prot *fs_prot) 330{ 331 struct mlx5_flow_namespace *ns = mlx5e_fs_get_ns(fs->fs, false); 332 u8 action[MLX5_UN_SZ_BYTES(set_add_copy_action_in_auto)] = {}; 333 int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); 334 struct mlx5_modify_hdr *modify_hdr = NULL; 335 struct mlx5_flow_table_attr ft_attr = {}; 336 struct mlx5_flow_destination dest = {}; 337 struct mlx5_core_dev *mdev = fs->mdev; 338 struct mlx5_flow_group *miss_group; 339 MLX5_DECLARE_FLOW_ACT(flow_act); 340 struct mlx5_flow_handle *rule; 341 struct mlx5_flow_spec *spec; 342 struct mlx5_flow_table *ft; 343 u32 *flow_group_in; 344 int err = 0; 345 346 flow_group_in = kvzalloc(inlen, GFP_KERNEL); 347 spec = kvzalloc(sizeof(*spec), GFP_KERNEL); 348 if (!flow_group_in || !spec) { 349 err = -ENOMEM; 350 goto out; 351 } 352 353 /* Create FT */ 354 ft_attr.max_fte = 2; 355 ft_attr.level = MLX5E_ACCEL_FS_ESP_FT_LEVEL; 356 ft_attr.prio = MLX5E_NIC_PRIO; 357 ft_attr.autogroup.num_reserved_entries = 1; 358 ft_attr.autogroup.max_num_groups = 1; 359 ft = mlx5_create_auto_grouped_flow_table(ns, &ft_attr); 360 if (IS_ERR(ft)) { 361 err = PTR_ERR(ft); 362 mlx5_core_err(mdev, "fail to create psp rx ft err=%d\n", err); 363 goto out_err; 364 } 365 fs_prot->ft = ft; 366 367 /* Create miss_group */ 368 MLX5_SET(create_flow_group_in, flow_group_in, start_flow_index, ft->max_fte - 1); 369 MLX5_SET(create_flow_group_in, flow_group_in, end_flow_index, ft->max_fte - 1); 370 miss_group = mlx5_create_flow_group(ft, flow_group_in); 371 if (IS_ERR(miss_group)) { 372 err = PTR_ERR(miss_group); 373 mlx5_core_err(mdev, "fail to create psp rx miss_group err=%d\n", err); 374 goto out_err; 375 } 376 fs_prot->miss_group = miss_group; 377 378 /* Create miss rule */ 379 rule = mlx5_add_flow_rules(ft, spec, &flow_act, &fs_prot->default_dest, 1); 380 if (IS_ERR(rule)) { 381 err = PTR_ERR(rule); 382 mlx5_core_err(mdev, "fail to create psp rx miss_rule err=%d\n", err); 383 goto out_err; 384 } 385 fs_prot->miss_rule = rule; 386 387 /* Add default Rx psp rule */ 388 setup_fte_udp_psp(spec, PSP_DEFAULT_UDP_PORT); 389 flow_act.crypto.type = MLX5_FLOW_CONTEXT_ENCRYPT_DECRYPT_TYPE_PSP; 390 /* Set bit[31, 30] PSP marker */ 391 /* Set bit[29-23] psp_syndrome is set in error FT */ 392#define MLX5E_PSP_MARKER_BIT (BIT(30) | BIT(31)) 393 MLX5_SET(set_action_in, action, action_type, MLX5_ACTION_TYPE_SET); 394 MLX5_SET(set_action_in, action, field, MLX5_ACTION_IN_FIELD_METADATA_REG_B); 395 MLX5_SET(set_action_in, action, data, MLX5E_PSP_MARKER_BIT); 396 MLX5_SET(set_action_in, action, offset, 0); 397 MLX5_SET(set_action_in, action, length, 32); 398 399 modify_hdr = mlx5_modify_header_alloc(mdev, MLX5_FLOW_NAMESPACE_KERNEL, 1, action); 400 if (IS_ERR(modify_hdr)) { 401 err = PTR_ERR(modify_hdr); 402 mlx5_core_err(mdev, "fail to alloc psp set modify_header_id err=%d\n", err); 403 modify_hdr = NULL; 404 goto out_err; 405 } 406 fs_prot->rx_modify_hdr = modify_hdr; 407 408 flow_act.action = MLX5_FLOW_CONTEXT_ACTION_FWD_DEST | 409 MLX5_FLOW_CONTEXT_ACTION_CRYPTO_DECRYPT | 410 MLX5_FLOW_CONTEXT_ACTION_MOD_HDR; 411 flow_act.modify_hdr = modify_hdr; 412 dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; 413 dest.ft = fs_prot->rx_err.ft; 414 rule = mlx5_add_flow_rules(fs_prot->ft, spec, &flow_act, &dest, 1); 415 if (IS_ERR(rule)) { 416 err = PTR_ERR(rule); 417 mlx5_core_err(mdev, 418 "fail to add psp rule Rx decryption, err=%d, flow_act.action = %#04X\n", 419 err, flow_act.action); 420 goto out_err; 421 } 422 423 fs_prot->def_rule = rule; 424 goto out; 425 426out_err: 427 accel_psp_fs_rx_fs_destroy(fs, fs_prot); 428out: 429 kvfree(flow_group_in); 430 kvfree(spec); 431 return err; 432} 433 434static int accel_psp_fs_rx_destroy(struct mlx5e_psp_fs *fs, enum accel_fs_psp_type type) 435{ 436 struct mlx5e_accel_fs_psp_prot *fs_prot; 437 struct mlx5e_accel_fs_psp *accel_psp; 438 439 accel_psp = fs->rx_fs; 440 441 /* The netdev unreg already happened, so all offloaded rule are already removed */ 442 fs_prot = &accel_psp->fs_prot[type]; 443 444 accel_psp_fs_rx_fs_destroy(fs, fs_prot); 445 446 accel_psp_fs_rx_err_destroy_ft(fs, &fs_prot->rx_err); 447 448 return 0; 449} 450 451static int accel_psp_fs_rx_create(struct mlx5e_psp_fs *fs, enum accel_fs_psp_type type) 452{ 453 struct mlx5_ttc_table *ttc = mlx5e_fs_get_ttc(fs->fs, false); 454 struct mlx5e_accel_fs_psp_prot *fs_prot; 455 struct mlx5e_accel_fs_psp *accel_psp; 456 int err; 457 458 accel_psp = fs->rx_fs; 459 fs_prot = &accel_psp->fs_prot[type]; 460 461 fs_prot->default_dest = mlx5_ttc_get_default_dest(ttc, fs_psp2tt(type)); 462 463 err = accel_psp_fs_rx_err_create_ft(fs, fs_prot, &fs_prot->rx_err); 464 if (err) 465 return err; 466 467 err = accel_psp_fs_rx_create_ft(fs, fs_prot); 468 if (err) 469 accel_psp_fs_rx_err_destroy_ft(fs, &fs_prot->rx_err); 470 471 return err; 472} 473 474static int accel_psp_fs_rx_ft_get(struct mlx5e_psp_fs *fs, enum accel_fs_psp_type type) 475{ 476 struct mlx5e_accel_fs_psp_prot *fs_prot; 477 struct mlx5_flow_destination dest = {}; 478 struct mlx5e_accel_fs_psp *accel_psp; 479 struct mlx5_ttc_table *ttc; 480 int err = 0; 481 482 if (!fs || !fs->rx_fs) 483 return -EINVAL; 484 485 ttc = mlx5e_fs_get_ttc(fs->fs, false); 486 accel_psp = fs->rx_fs; 487 fs_prot = &accel_psp->fs_prot[type]; 488 mutex_lock(&fs_prot->prot_mutex); 489 if (fs_prot->refcnt++) 490 goto out; 491 492 /* create FT */ 493 err = accel_psp_fs_rx_create(fs, type); 494 if (err) { 495 fs_prot->refcnt--; 496 goto out; 497 } 498 499 /* connect */ 500 dest.type = MLX5_FLOW_DESTINATION_TYPE_FLOW_TABLE; 501 dest.ft = fs_prot->ft; 502 mlx5_ttc_fwd_dest(ttc, fs_psp2tt(type), &dest); 503 504out: 505 mutex_unlock(&fs_prot->prot_mutex); 506 return err; 507} 508 509static void accel_psp_fs_rx_ft_put(struct mlx5e_psp_fs *fs, enum accel_fs_psp_type type) 510{ 511 struct mlx5_ttc_table *ttc = mlx5e_fs_get_ttc(fs->fs, false); 512 struct mlx5e_accel_fs_psp_prot *fs_prot; 513 struct mlx5e_accel_fs_psp *accel_psp; 514 515 accel_psp = fs->rx_fs; 516 fs_prot = &accel_psp->fs_prot[type]; 517 mutex_lock(&fs_prot->prot_mutex); 518 if (--fs_prot->refcnt) 519 goto out; 520 521 /* disconnect */ 522 mlx5_ttc_fwd_default_dest(ttc, fs_psp2tt(type)); 523 524 /* remove FT */ 525 accel_psp_fs_rx_destroy(fs, type); 526 527out: 528 mutex_unlock(&fs_prot->prot_mutex); 529} 530 531static void accel_psp_fs_cleanup_rx(struct mlx5e_psp_fs *fs) 532{ 533 struct mlx5e_accel_fs_psp_prot *fs_prot; 534 struct mlx5e_accel_fs_psp *accel_psp; 535 enum accel_fs_psp_type i; 536 537 if (!fs->rx_fs) 538 return; 539 540 accel_psp = fs->rx_fs; 541 mlx5_fc_destroy(fs->mdev, accel_psp->rx_bad_counter); 542 mlx5_fc_destroy(fs->mdev, accel_psp->rx_err_counter); 543 mlx5_fc_destroy(fs->mdev, accel_psp->rx_auth_fail_counter); 544 mlx5_fc_destroy(fs->mdev, accel_psp->rx_counter); 545 for (i = 0; i < ACCEL_FS_PSP_NUM_TYPES; i++) { 546 fs_prot = &accel_psp->fs_prot[i]; 547 mutex_destroy(&fs_prot->prot_mutex); 548 WARN_ON(fs_prot->refcnt); 549 } 550 kfree(fs->rx_fs); 551 fs->rx_fs = NULL; 552} 553 554static int accel_psp_fs_init_rx(struct mlx5e_psp_fs *fs) 555{ 556 struct mlx5e_accel_fs_psp_prot *fs_prot; 557 struct mlx5e_accel_fs_psp *accel_psp; 558 struct mlx5_core_dev *mdev = fs->mdev; 559 struct mlx5_fc *flow_counter; 560 enum accel_fs_psp_type i; 561 int err; 562 563 accel_psp = kzalloc(sizeof(*accel_psp), GFP_KERNEL); 564 if (!accel_psp) 565 return -ENOMEM; 566 567 for (i = 0; i < ACCEL_FS_PSP_NUM_TYPES; i++) { 568 fs_prot = &accel_psp->fs_prot[i]; 569 mutex_init(&fs_prot->prot_mutex); 570 } 571 572 flow_counter = mlx5_fc_create(mdev, false); 573 if (IS_ERR(flow_counter)) { 574 mlx5_core_warn(mdev, 575 "fail to create psp rx flow counter err=%pe\n", 576 flow_counter); 577 err = PTR_ERR(flow_counter); 578 goto out_err; 579 } 580 accel_psp->rx_counter = flow_counter; 581 582 flow_counter = mlx5_fc_create(mdev, false); 583 if (IS_ERR(flow_counter)) { 584 mlx5_core_warn(mdev, 585 "fail to create psp rx auth fail flow counter err=%pe\n", 586 flow_counter); 587 err = PTR_ERR(flow_counter); 588 goto out_counter_err; 589 } 590 accel_psp->rx_auth_fail_counter = flow_counter; 591 592 flow_counter = mlx5_fc_create(mdev, false); 593 if (IS_ERR(flow_counter)) { 594 mlx5_core_warn(mdev, 595 "fail to create psp rx error flow counter err=%pe\n", 596 flow_counter); 597 err = PTR_ERR(flow_counter); 598 goto out_auth_fail_counter_err; 599 } 600 accel_psp->rx_err_counter = flow_counter; 601 602 flow_counter = mlx5_fc_create(mdev, false); 603 if (IS_ERR(flow_counter)) { 604 mlx5_core_warn(mdev, 605 "fail to create psp rx bad flow counter err=%pe\n", 606 flow_counter); 607 err = PTR_ERR(flow_counter); 608 goto out_err_counter_err; 609 } 610 accel_psp->rx_bad_counter = flow_counter; 611 612 fs->rx_fs = accel_psp; 613 614 return 0; 615 616out_err_counter_err: 617 mlx5_fc_destroy(mdev, accel_psp->rx_err_counter); 618 accel_psp->rx_err_counter = NULL; 619out_auth_fail_counter_err: 620 mlx5_fc_destroy(mdev, accel_psp->rx_auth_fail_counter); 621 accel_psp->rx_auth_fail_counter = NULL; 622out_counter_err: 623 mlx5_fc_destroy(mdev, accel_psp->rx_counter); 624 accel_psp->rx_counter = NULL; 625out_err: 626 for (i = 0; i < ACCEL_FS_PSP_NUM_TYPES; i++) { 627 fs_prot = &accel_psp->fs_prot[i]; 628 mutex_destroy(&fs_prot->prot_mutex); 629 } 630 kfree(accel_psp); 631 fs->rx_fs = NULL; 632 633 return err; 634} 635 636void mlx5_accel_psp_fs_cleanup_rx_tables(struct mlx5e_priv *priv) 637{ 638 int i; 639 640 if (!priv->psp) 641 return; 642 643 for (i = 0; i < ACCEL_FS_PSP_NUM_TYPES; i++) 644 accel_psp_fs_rx_ft_put(priv->psp->fs, i); 645} 646 647int mlx5_accel_psp_fs_init_rx_tables(struct mlx5e_priv *priv) 648{ 649 struct mlx5e_psp_fs *fs; 650 int err, i; 651 652 if (!priv->psp) 653 return 0; 654 655 fs = priv->psp->fs; 656 for (i = 0; i < ACCEL_FS_PSP_NUM_TYPES; i++) { 657 err = accel_psp_fs_rx_ft_get(fs, i); 658 if (err) 659 goto out_err; 660 } 661 662 return 0; 663 664out_err: 665 i--; 666 while (i >= 0) { 667 accel_psp_fs_rx_ft_put(fs, i); 668 --i; 669 } 670 671 return err; 672} 673 674static int accel_psp_fs_tx_create_ft_table(struct mlx5e_psp_fs *fs) 675{ 676 int inlen = MLX5_ST_SZ_BYTES(create_flow_group_in); 677 struct mlx5_flow_table_attr ft_attr = {}; 678 struct mlx5_flow_destination dest = {}; 679 struct mlx5_core_dev *mdev = fs->mdev; 680 struct mlx5_flow_act flow_act = {}; 681 u32 *in, *mc, *outer_headers_c; 682 struct mlx5_flow_handle *rule; 683 struct mlx5_flow_spec *spec; 684 struct mlx5e_psp_tx *tx_fs; 685 struct mlx5_flow_table *ft; 686 struct mlx5_flow_group *fg; 687 int err = 0; 688 689 spec = kvzalloc(sizeof(*spec), GFP_KERNEL); 690 in = kvzalloc(inlen, GFP_KERNEL); 691 if (!spec || !in) { 692 err = -ENOMEM; 693 goto out; 694 } 695 696 ft_attr.max_fte = 1; 697#define MLX5E_PSP_PRIO 0 698 ft_attr.prio = MLX5E_PSP_PRIO; 699#define MLX5E_PSP_LEVEL 0 700 ft_attr.level = MLX5E_PSP_LEVEL; 701 ft_attr.autogroup.max_num_groups = 1; 702 703 tx_fs = fs->tx_fs; 704 ft = mlx5_create_flow_table(tx_fs->ns, &ft_attr); 705 if (IS_ERR(ft)) { 706 err = PTR_ERR(ft); 707 mlx5_core_err(mdev, "PSP: fail to add psp tx flow table, err = %d\n", err); 708 goto out; 709 } 710 711 mc = MLX5_ADDR_OF(create_flow_group_in, in, match_criteria); 712 outer_headers_c = MLX5_ADDR_OF(fte_match_param, mc, outer_headers); 713 MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, outer_headers_c, ip_protocol); 714 MLX5_SET_TO_ONES(fte_match_set_lyr_2_4, outer_headers_c, udp_dport); 715 MLX5_SET_CFG(in, match_criteria_enable, MLX5_MATCH_OUTER_HEADERS); 716 fg = mlx5_create_flow_group(ft, in); 717 if (IS_ERR(fg)) { 718 err = PTR_ERR(fg); 719 mlx5_core_err(mdev, "PSP: fail to add psp tx flow group, err = %d\n", err); 720 goto err_create_fg; 721 } 722 723 setup_fte_udp_psp(spec, PSP_DEFAULT_UDP_PORT); 724 flow_act.crypto.type = MLX5_FLOW_CONTEXT_ENCRYPT_DECRYPT_TYPE_PSP; 725 flow_act.flags |= FLOW_ACT_NO_APPEND; 726 flow_act.action = MLX5_FLOW_CONTEXT_ACTION_ALLOW | 727 MLX5_FLOW_CONTEXT_ACTION_CRYPTO_ENCRYPT | 728 MLX5_FLOW_CONTEXT_ACTION_COUNT; 729 dest.type = MLX5_FLOW_DESTINATION_TYPE_COUNTER; 730 dest.counter = tx_fs->tx_counter; 731 rule = mlx5_add_flow_rules(ft, spec, &flow_act, &dest, 1); 732 if (IS_ERR(rule)) { 733 err = PTR_ERR(rule); 734 mlx5_core_err(mdev, "PSP: fail to add psp tx flow rule, err = %d\n", err); 735 goto err_add_flow_rule; 736 } 737 738 tx_fs->ft = ft; 739 tx_fs->fg = fg; 740 tx_fs->rule = rule; 741 goto out; 742 743err_add_flow_rule: 744 mlx5_destroy_flow_group(fg); 745err_create_fg: 746 mlx5_destroy_flow_table(ft); 747out: 748 kvfree(in); 749 kvfree(spec); 750 return err; 751} 752 753static void accel_psp_fs_tx_destroy(struct mlx5e_psp_tx *tx_fs) 754{ 755 if (!tx_fs->ft) 756 return; 757 758 mlx5_del_flow_rules(tx_fs->rule); 759 mlx5_destroy_flow_group(tx_fs->fg); 760 mlx5_destroy_flow_table(tx_fs->ft); 761} 762 763static int accel_psp_fs_tx_ft_get(struct mlx5e_psp_fs *fs) 764{ 765 struct mlx5e_psp_tx *tx_fs = fs->tx_fs; 766 int err = 0; 767 768 mutex_lock(&tx_fs->mutex); 769 if (tx_fs->refcnt++) 770 goto out; 771 772 err = accel_psp_fs_tx_create_ft_table(fs); 773 if (err) 774 tx_fs->refcnt--; 775out: 776 mutex_unlock(&tx_fs->mutex); 777 return err; 778} 779 780static void accel_psp_fs_tx_ft_put(struct mlx5e_psp_fs *fs) 781{ 782 struct mlx5e_psp_tx *tx_fs = fs->tx_fs; 783 784 mutex_lock(&tx_fs->mutex); 785 if (--tx_fs->refcnt) 786 goto out; 787 788 accel_psp_fs_tx_destroy(tx_fs); 789out: 790 mutex_unlock(&tx_fs->mutex); 791} 792 793static void accel_psp_fs_cleanup_tx(struct mlx5e_psp_fs *fs) 794{ 795 struct mlx5e_psp_tx *tx_fs = fs->tx_fs; 796 797 if (!tx_fs) 798 return; 799 800 mlx5_fc_destroy(fs->mdev, tx_fs->tx_counter); 801 mutex_destroy(&tx_fs->mutex); 802 WARN_ON(tx_fs->refcnt); 803 kfree(tx_fs); 804 fs->tx_fs = NULL; 805} 806 807static int accel_psp_fs_init_tx(struct mlx5e_psp_fs *fs) 808{ 809 struct mlx5_core_dev *mdev = fs->mdev; 810 struct mlx5_flow_namespace *ns; 811 struct mlx5_fc *flow_counter; 812 struct mlx5e_psp_tx *tx_fs; 813 814 ns = mlx5_get_flow_namespace(mdev, MLX5_FLOW_NAMESPACE_EGRESS_IPSEC); 815 if (!ns) 816 return -EOPNOTSUPP; 817 818 tx_fs = kzalloc(sizeof(*tx_fs), GFP_KERNEL); 819 if (!tx_fs) 820 return -ENOMEM; 821 822 flow_counter = mlx5_fc_create(mdev, false); 823 if (IS_ERR(flow_counter)) { 824 mlx5_core_warn(mdev, 825 "fail to create psp tx flow counter err=%pe\n", 826 flow_counter); 827 kfree(tx_fs); 828 return PTR_ERR(flow_counter); 829 } 830 tx_fs->tx_counter = flow_counter; 831 mutex_init(&tx_fs->mutex); 832 tx_fs->ns = ns; 833 fs->tx_fs = tx_fs; 834 return 0; 835} 836 837static void 838mlx5e_accel_psp_fs_get_stats_fill(struct mlx5e_priv *priv, 839 struct mlx5e_psp_stats *stats) 840{ 841 struct mlx5e_psp_tx *tx_fs = priv->psp->fs->tx_fs; 842 struct mlx5_core_dev *mdev = priv->mdev; 843 struct mlx5e_accel_fs_psp *accel_psp; 844 845 accel_psp = (struct mlx5e_accel_fs_psp *)priv->psp->fs->rx_fs; 846 847 if (tx_fs->tx_counter) 848 mlx5_fc_query(mdev, tx_fs->tx_counter, &stats->psp_tx_pkts, 849 &stats->psp_tx_bytes); 850 851 if (accel_psp->rx_counter) 852 mlx5_fc_query(mdev, accel_psp->rx_counter, &stats->psp_rx_pkts, 853 &stats->psp_rx_bytes); 854 855 if (accel_psp->rx_auth_fail_counter) 856 mlx5_fc_query(mdev, accel_psp->rx_auth_fail_counter, 857 &stats->psp_rx_pkts_auth_fail, 858 &stats->psp_rx_bytes_auth_fail); 859 860 if (accel_psp->rx_err_counter) 861 mlx5_fc_query(mdev, accel_psp->rx_err_counter, 862 &stats->psp_rx_pkts_frame_err, 863 &stats->psp_rx_bytes_frame_err); 864 865 if (accel_psp->rx_bad_counter) 866 mlx5_fc_query(mdev, accel_psp->rx_bad_counter, 867 &stats->psp_rx_pkts_drop, 868 &stats->psp_rx_bytes_drop); 869} 870 871void mlx5_accel_psp_fs_cleanup_tx_tables(struct mlx5e_priv *priv) 872{ 873 if (!priv->psp) 874 return; 875 876 accel_psp_fs_tx_ft_put(priv->psp->fs); 877} 878 879int mlx5_accel_psp_fs_init_tx_tables(struct mlx5e_priv *priv) 880{ 881 if (!priv->psp) 882 return 0; 883 884 return accel_psp_fs_tx_ft_get(priv->psp->fs); 885} 886 887static void mlx5e_accel_psp_fs_cleanup(struct mlx5e_psp_fs *fs) 888{ 889 accel_psp_fs_cleanup_rx(fs); 890 accel_psp_fs_cleanup_tx(fs); 891 kfree(fs); 892} 893 894static struct mlx5e_psp_fs *mlx5e_accel_psp_fs_init(struct mlx5e_priv *priv) 895{ 896 struct mlx5e_psp_fs *fs; 897 int err = 0; 898 899 fs = kzalloc(sizeof(*fs), GFP_KERNEL); 900 if (!fs) 901 return ERR_PTR(-ENOMEM); 902 903 fs->mdev = priv->mdev; 904 err = accel_psp_fs_init_tx(fs); 905 if (err) 906 goto err_tx; 907 908 fs->fs = priv->fs; 909 err = accel_psp_fs_init_rx(fs); 910 if (err) 911 goto err_rx; 912 913 return fs; 914 915err_rx: 916 accel_psp_fs_cleanup_tx(fs); 917err_tx: 918 kfree(fs); 919 return ERR_PTR(err); 920} 921 922static int 923mlx5e_psp_set_config(struct psp_dev *psd, struct psp_dev_config *conf, 924 struct netlink_ext_ack *extack) 925{ 926 return 0; /* TODO: this should actually do things to the device */ 927} 928 929static int 930mlx5e_psp_generate_key_spi(struct mlx5_core_dev *mdev, 931 enum mlx5_psp_gen_spi_in_key_size keysz, 932 unsigned int keysz_bytes, 933 struct psp_key_parsed *key) 934{ 935 u32 out[MLX5_ST_SZ_DW(psp_gen_spi_out) + MLX5_ST_SZ_DW(key_spi)] = {}; 936 u32 in[MLX5_ST_SZ_DW(psp_gen_spi_in)] = {}; 937 void *outkey; 938 int err; 939 940 WARN_ON_ONCE(keysz_bytes > PSP_MAX_KEY); 941 942 MLX5_SET(psp_gen_spi_in, in, opcode, MLX5_CMD_OP_PSP_GEN_SPI); 943 MLX5_SET(psp_gen_spi_in, in, key_size, keysz); 944 MLX5_SET(psp_gen_spi_in, in, num_of_spi, 1); 945 err = mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out)); 946 if (err) 947 return err; 948 949 outkey = MLX5_ADDR_OF(psp_gen_spi_out, out, key_spi); 950 key->spi = cpu_to_be32(MLX5_GET(key_spi, outkey, spi)); 951 memcpy(key->key, MLX5_ADDR_OF(key_spi, outkey, key) + 32 - keysz_bytes, 952 keysz_bytes); 953 954 return 0; 955} 956 957static int 958mlx5e_psp_rx_spi_alloc(struct psp_dev *psd, u32 version, 959 struct psp_key_parsed *assoc, 960 struct netlink_ext_ack *extack) 961{ 962 struct mlx5e_priv *priv = netdev_priv(psd->main_netdev); 963 enum mlx5_psp_gen_spi_in_key_size keysz; 964 u8 keysz_bytes; 965 966 switch (version) { 967 case PSP_VERSION_HDR0_AES_GCM_128: 968 keysz = MLX5_PSP_GEN_SPI_IN_KEY_SIZE_128; 969 keysz_bytes = 16; 970 break; 971 case PSP_VERSION_HDR0_AES_GCM_256: 972 keysz = MLX5_PSP_GEN_SPI_IN_KEY_SIZE_256; 973 keysz_bytes = 32; 974 break; 975 default: 976 return -EINVAL; 977 } 978 979 return mlx5e_psp_generate_key_spi(priv->mdev, keysz, keysz_bytes, assoc); 980} 981 982struct psp_key { 983 u32 id; 984}; 985 986static int mlx5e_psp_assoc_add(struct psp_dev *psd, struct psp_assoc *pas, 987 struct netlink_ext_ack *extack) 988{ 989 struct mlx5e_priv *priv = netdev_priv(psd->main_netdev); 990 struct mlx5_core_dev *mdev = priv->mdev; 991 struct psp_key_parsed *tx = &pas->tx; 992 struct mlx5e_psp *psp = priv->psp; 993 struct psp_key *nkey; 994 int err; 995 996 mdev = priv->mdev; 997 nkey = (struct psp_key *)pas->drv_data; 998 999 err = mlx5_create_encryption_key(mdev, tx->key, 1000 psp_key_size(pas->version), 1001 MLX5_ACCEL_OBJ_PSP_KEY, 1002 &nkey->id); 1003 if (err) { 1004 mlx5_core_err(mdev, "Failed to create encryption key (err = %d)\n", err); 1005 return err; 1006 } 1007 1008 atomic_inc(&psp->tx_key_cnt); 1009 return 0; 1010} 1011 1012static void mlx5e_psp_assoc_del(struct psp_dev *psd, struct psp_assoc *pas) 1013{ 1014 struct mlx5e_priv *priv = netdev_priv(psd->main_netdev); 1015 struct mlx5e_psp *psp = priv->psp; 1016 struct psp_key *nkey; 1017 1018 nkey = (struct psp_key *)pas->drv_data; 1019 mlx5_destroy_encryption_key(priv->mdev, nkey->id); 1020 atomic_dec(&psp->tx_key_cnt); 1021} 1022 1023static int mlx5e_psp_rotate_key(struct mlx5_core_dev *mdev) 1024{ 1025 u32 in[MLX5_ST_SZ_DW(psp_rotate_key_in)] = {}; 1026 u32 out[MLX5_ST_SZ_DW(psp_rotate_key_out)]; 1027 1028 MLX5_SET(psp_rotate_key_in, in, opcode, 1029 MLX5_CMD_OP_PSP_ROTATE_KEY); 1030 1031 return mlx5_cmd_exec(mdev, in, sizeof(in), out, sizeof(out)); 1032} 1033 1034static int 1035mlx5e_psp_key_rotate(struct psp_dev *psd, struct netlink_ext_ack *exack) 1036{ 1037 struct mlx5e_priv *priv = netdev_priv(psd->main_netdev); 1038 1039 /* no support for protecting against external rotations */ 1040 psd->generation = 0; 1041 1042 return mlx5e_psp_rotate_key(priv->mdev); 1043} 1044 1045static void 1046mlx5e_psp_get_stats(struct psp_dev *psd, struct psp_dev_stats *stats) 1047{ 1048 struct mlx5e_priv *priv = netdev_priv(psd->main_netdev); 1049 struct mlx5e_psp_stats nstats; 1050 1051 mlx5e_accel_psp_fs_get_stats_fill(priv, &nstats); 1052 stats->rx_packets = nstats.psp_rx_pkts; 1053 stats->rx_bytes = nstats.psp_rx_bytes; 1054 stats->rx_auth_fail = nstats.psp_rx_pkts_auth_fail; 1055 stats->rx_error = nstats.psp_rx_pkts_frame_err; 1056 stats->rx_bad = nstats.psp_rx_pkts_drop; 1057 stats->tx_packets = nstats.psp_tx_pkts; 1058 stats->tx_bytes = nstats.psp_tx_bytes; 1059 stats->tx_error = atomic_read(&priv->psp->tx_drop); 1060} 1061 1062static struct psp_dev_ops mlx5_psp_ops = { 1063 .set_config = mlx5e_psp_set_config, 1064 .rx_spi_alloc = mlx5e_psp_rx_spi_alloc, 1065 .tx_key_add = mlx5e_psp_assoc_add, 1066 .tx_key_del = mlx5e_psp_assoc_del, 1067 .key_rotate = mlx5e_psp_key_rotate, 1068 .get_stats = mlx5e_psp_get_stats, 1069}; 1070 1071void mlx5e_psp_unregister(struct mlx5e_priv *priv) 1072{ 1073 if (!priv->psp || !priv->psp->psp) 1074 return; 1075 1076 psp_dev_unregister(priv->psp->psp); 1077} 1078 1079void mlx5e_psp_register(struct mlx5e_priv *priv) 1080{ 1081 /* FW Caps missing */ 1082 if (!priv->psp) 1083 return; 1084 1085 priv->psp->caps.assoc_drv_spc = sizeof(u32); 1086 priv->psp->caps.versions = 1 << PSP_VERSION_HDR0_AES_GCM_128; 1087 if (MLX5_CAP_PSP(priv->mdev, psp_crypto_esp_aes_gcm_256_encrypt) && 1088 MLX5_CAP_PSP(priv->mdev, psp_crypto_esp_aes_gcm_256_decrypt)) 1089 priv->psp->caps.versions |= 1 << PSP_VERSION_HDR0_AES_GCM_256; 1090 1091 priv->psp->psp = psp_dev_create(priv->netdev, &mlx5_psp_ops, 1092 &priv->psp->caps, NULL); 1093 if (IS_ERR(priv->psp->psp)) 1094 mlx5_core_err(priv->mdev, "PSP failed to register due to %pe\n", 1095 priv->psp->psp); 1096} 1097 1098int mlx5e_psp_init(struct mlx5e_priv *priv) 1099{ 1100 struct mlx5_core_dev *mdev = priv->mdev; 1101 struct mlx5e_psp_fs *fs; 1102 struct mlx5e_psp *psp; 1103 int err; 1104 1105 if (!mlx5_is_psp_device(mdev)) { 1106 mlx5_core_dbg(mdev, "PSP offload not supported\n"); 1107 return 0; 1108 } 1109 1110 if (!MLX5_CAP_ETH(mdev, swp)) { 1111 mlx5_core_dbg(mdev, "SWP not supported\n"); 1112 return 0; 1113 } 1114 1115 if (!MLX5_CAP_ETH(mdev, swp_csum)) { 1116 mlx5_core_dbg(mdev, "SWP checksum not supported\n"); 1117 return 0; 1118 } 1119 1120 if (!MLX5_CAP_ETH(mdev, swp_csum_l4_partial)) { 1121 mlx5_core_dbg(mdev, "SWP L4 partial checksum not supported\n"); 1122 return 0; 1123 } 1124 1125 if (!MLX5_CAP_ETH(mdev, swp_lso)) { 1126 mlx5_core_dbg(mdev, "PSP LSO not supported\n"); 1127 return 0; 1128 } 1129 1130 psp = kzalloc(sizeof(*psp), GFP_KERNEL); 1131 if (!psp) 1132 return -ENOMEM; 1133 1134 priv->psp = psp; 1135 fs = mlx5e_accel_psp_fs_init(priv); 1136 if (IS_ERR(fs)) { 1137 err = PTR_ERR(fs); 1138 goto out_err; 1139 } 1140 1141 psp->fs = fs; 1142 1143 mlx5_core_dbg(priv->mdev, "PSP attached to netdevice\n"); 1144 return 0; 1145 1146out_err: 1147 priv->psp = NULL; 1148 kfree(psp); 1149 return err; 1150} 1151 1152void mlx5e_psp_cleanup(struct mlx5e_priv *priv) 1153{ 1154 struct mlx5e_psp *psp = priv->psp; 1155 1156 if (!psp) 1157 return; 1158 1159 WARN_ON(atomic_read(&psp->tx_key_cnt)); 1160 mlx5e_accel_psp_fs_cleanup(psp->fs); 1161 priv->psp = NULL; 1162 kfree(psp); 1163}
~/
Read-only file preview.