aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/infiniband/hw/mlx5/qp.c104
-rw-r--r--drivers/infiniband/ulp/iser/iser_memory.c50
-rw-r--r--drivers/infiniband/ulp/isert/ib_isert.c40
-rw-r--r--include/rdma/ib_verbs.h32
4 files changed, 97 insertions, 129 deletions
diff --git a/drivers/infiniband/hw/mlx5/qp.c b/drivers/infiniband/hw/mlx5/qp.c
index 13924a256290..d7f35e9e6522 100644
--- a/drivers/infiniband/hw/mlx5/qp.c
+++ b/drivers/infiniband/hw/mlx5/qp.c
@@ -2020,50 +2020,31 @@ static u8 bs_selector(int block_size)
2020 } 2020 }
2021} 2021}
2022 2022
2023static int mlx5_fill_inl_bsf(struct ib_sig_domain *domain, 2023static void mlx5_fill_inl_bsf(struct ib_sig_domain *domain,
2024 struct mlx5_bsf_inl *inl) 2024 struct mlx5_bsf_inl *inl)
2025{ 2025{
2026 /* Valid inline section and allow BSF refresh */ 2026 /* Valid inline section and allow BSF refresh */
2027 inl->vld_refresh = cpu_to_be16(MLX5_BSF_INL_VALID | 2027 inl->vld_refresh = cpu_to_be16(MLX5_BSF_INL_VALID |
2028 MLX5_BSF_REFRESH_DIF); 2028 MLX5_BSF_REFRESH_DIF);
2029 inl->dif_apptag = cpu_to_be16(domain->sig.dif.app_tag); 2029 inl->dif_apptag = cpu_to_be16(domain->sig.dif.app_tag);
2030 inl->dif_reftag = cpu_to_be32(domain->sig.dif.ref_tag); 2030 inl->dif_reftag = cpu_to_be32(domain->sig.dif.ref_tag);
2031 /* repeating block */
2032 inl->rp_inv_seed = MLX5_BSF_REPEAT_BLOCK;
2033 inl->sig_type = domain->sig.dif.bg_type == IB_T10DIF_CRC ?
2034 MLX5_DIF_CRC : MLX5_DIF_IPCS;
2031 2035
2032 switch (domain->sig.dif.type) { 2036 if (domain->sig.dif.ref_remap)
2033 case IB_T10DIF_NONE: 2037 inl->dif_inc_ref_guard_check |= MLX5_BSF_INC_REFTAG;
2034 /* No DIF */ 2038
2035 break; 2039 if (domain->sig.dif.app_escape) {
2036 case IB_T10DIF_TYPE1: /* Fall through */ 2040 if (domain->sig.dif.ref_escape)
2037 case IB_T10DIF_TYPE2: 2041 inl->dif_inc_ref_guard_check |= MLX5_BSF_APPREF_ESCAPE;
2038 inl->sig_type = domain->sig.dif.bg_type == IB_T10DIF_CRC ? 2042 else
2039 MLX5_DIF_CRC : MLX5_DIF_IPCS; 2043 inl->dif_inc_ref_guard_check |= MLX5_BSF_APPTAG_ESCAPE;
2040 /*
2041 * increment reftag and don't check if
2042 * apptag=0xffff and reftag=0xffffffff
2043 */
2044 inl->dif_inc_ref_guard_check = MLX5_BSF_INC_REFTAG |
2045 MLX5_BSF_APPREF_ESCAPE;
2046 inl->dif_app_bitmask_check = 0xffff;
2047 /* repeating block */
2048 inl->rp_inv_seed = MLX5_BSF_REPEAT_BLOCK;
2049 break;
2050 case IB_T10DIF_TYPE3:
2051 inl->sig_type = domain->sig.dif.bg_type == IB_T10DIF_CRC ?
2052 MLX5_DIF_CRC : MLX5_DIF_IPCS;
2053 /*
2054 * Don't inc reftag and don't check if
2055 * apptag=0xffff and reftag=0xffffffff
2056 */
2057 inl->dif_inc_ref_guard_check = MLX5_BSF_APPREF_ESCAPE;
2058 inl->dif_app_bitmask_check = 0xffff;
2059 /* Repeating block */
2060 inl->rp_inv_seed = MLX5_BSF_REPEAT_BLOCK;
2061 break;
2062 default:
2063 return -EINVAL;
2064 } 2044 }
2065 2045
2066 return 0; 2046 inl->dif_app_bitmask_check =
2047 cpu_to_be16(domain->sig.dif.apptag_check_mask);
2067} 2048}
2068 2049
2069static int mlx5_set_bsf(struct ib_mr *sig_mr, 2050static int mlx5_set_bsf(struct ib_mr *sig_mr,
@@ -2074,20 +2055,35 @@ static int mlx5_set_bsf(struct ib_mr *sig_mr,
2074 struct mlx5_bsf_basic *basic = &bsf->basic; 2055 struct mlx5_bsf_basic *basic = &bsf->basic;
2075 struct ib_sig_domain *mem = &sig_attrs->mem; 2056 struct ib_sig_domain *mem = &sig_attrs->mem;
2076 struct ib_sig_domain *wire = &sig_attrs->wire; 2057 struct ib_sig_domain *wire = &sig_attrs->wire;
2077 int ret;
2078 2058
2079 memset(bsf, 0, sizeof(*bsf)); 2059 memset(bsf, 0, sizeof(*bsf));
2060
2061 /* Basic + Extended + Inline */
2062 basic->bsf_size_sbs = 1 << 7;
2063 /* Input domain check byte mask */
2064 basic->check_byte_mask = sig_attrs->check_mask;
2065 basic->raw_data_size = cpu_to_be32(data_size);
2066
2067 /* Memory domain */
2080 switch (sig_attrs->mem.sig_type) { 2068 switch (sig_attrs->mem.sig_type) {
2069 case IB_SIG_TYPE_NONE:
2070 break;
2081 case IB_SIG_TYPE_T10_DIF: 2071 case IB_SIG_TYPE_T10_DIF:
2082 if (sig_attrs->wire.sig_type != IB_SIG_TYPE_T10_DIF) 2072 basic->mem.bs_selector = bs_selector(mem->sig.dif.pi_interval);
2083 return -EINVAL; 2073 basic->m_bfs_psv = cpu_to_be32(msig->psv_memory.psv_idx);
2074 mlx5_fill_inl_bsf(mem, &bsf->m_inl);
2075 break;
2076 default:
2077 return -EINVAL;
2078 }
2084 2079
2085 /* Basic + Extended + Inline */ 2080 /* Wire domain */
2086 basic->bsf_size_sbs = 1 << 7; 2081 switch (sig_attrs->wire.sig_type) {
2087 /* Input domain check byte mask */ 2082 case IB_SIG_TYPE_NONE:
2088 basic->check_byte_mask = sig_attrs->check_mask; 2083 break;
2084 case IB_SIG_TYPE_T10_DIF:
2089 if (mem->sig.dif.pi_interval == wire->sig.dif.pi_interval && 2085 if (mem->sig.dif.pi_interval == wire->sig.dif.pi_interval &&
2090 mem->sig.dif.type == wire->sig.dif.type) { 2086 mem->sig_type == wire->sig_type) {
2091 /* Same block structure */ 2087 /* Same block structure */
2092 basic->bsf_size_sbs |= 1 << 4; 2088 basic->bsf_size_sbs |= 1 << 4;
2093 if (mem->sig.dif.bg_type == wire->sig.dif.bg_type) 2089 if (mem->sig.dif.bg_type == wire->sig.dif.bg_type)
@@ -2099,20 +2095,9 @@ static int mlx5_set_bsf(struct ib_mr *sig_mr,
2099 } else 2095 } else
2100 basic->wire.bs_selector = bs_selector(wire->sig.dif.pi_interval); 2096 basic->wire.bs_selector = bs_selector(wire->sig.dif.pi_interval);
2101 2097
2102 basic->mem.bs_selector = bs_selector(mem->sig.dif.pi_interval);
2103 basic->raw_data_size = cpu_to_be32(data_size);
2104 basic->m_bfs_psv = cpu_to_be32(msig->psv_memory.psv_idx);
2105 basic->w_bfs_psv = cpu_to_be32(msig->psv_wire.psv_idx); 2098 basic->w_bfs_psv = cpu_to_be32(msig->psv_wire.psv_idx);
2106 2099 mlx5_fill_inl_bsf(wire, &bsf->w_inl);
2107 ret = mlx5_fill_inl_bsf(wire, &bsf->w_inl);
2108 if (ret)
2109 return -EINVAL;
2110
2111 ret = mlx5_fill_inl_bsf(mem, &bsf->m_inl);
2112 if (ret)
2113 return -EINVAL;
2114 break; 2100 break;
2115
2116 default: 2101 default:
2117 return -EINVAL; 2102 return -EINVAL;
2118 } 2103 }
@@ -2311,20 +2296,21 @@ static int set_psv_wr(struct ib_sig_domain *domain,
2311 memset(psv_seg, 0, sizeof(*psv_seg)); 2296 memset(psv_seg, 0, sizeof(*psv_seg));
2312 psv_seg->psv_num = cpu_to_be32(psv_idx); 2297 psv_seg->psv_num = cpu_to_be32(psv_idx);
2313 switch (domain->sig_type) { 2298 switch (domain->sig_type) {
2299 case IB_SIG_TYPE_NONE:
2300 break;
2314 case IB_SIG_TYPE_T10_DIF: 2301 case IB_SIG_TYPE_T10_DIF:
2315 psv_seg->transient_sig = cpu_to_be32(domain->sig.dif.bg << 16 | 2302 psv_seg->transient_sig = cpu_to_be32(domain->sig.dif.bg << 16 |
2316 domain->sig.dif.app_tag); 2303 domain->sig.dif.app_tag);
2317 psv_seg->ref_tag = cpu_to_be32(domain->sig.dif.ref_tag); 2304 psv_seg->ref_tag = cpu_to_be32(domain->sig.dif.ref_tag);
2318
2319 *seg += sizeof(*psv_seg);
2320 *size += sizeof(*psv_seg) / 16;
2321 break; 2305 break;
2322
2323 default: 2306 default:
2324 pr_err("Bad signature type given.\n"); 2307 pr_err("Bad signature type given.\n");
2325 return 1; 2308 return 1;
2326 } 2309 }
2327 2310
2311 *seg += sizeof(*psv_seg);
2312 *size += sizeof(*psv_seg) / 16;
2313
2328 return 0; 2314 return 0;
2329} 2315}
2330 2316
diff --git a/drivers/infiniband/ulp/iser/iser_memory.c b/drivers/infiniband/ulp/iser/iser_memory.c
index d9ed6234c505..6c5ce357fba6 100644
--- a/drivers/infiniband/ulp/iser/iser_memory.c
+++ b/drivers/infiniband/ulp/iser/iser_memory.c
@@ -440,51 +440,44 @@ int iser_reg_rdma_mem_fmr(struct iscsi_iser_task *iser_task,
440 return 0; 440 return 0;
441} 441}
442 442
443static inline enum ib_t10_dif_type
444scsi2ib_prot_type(unsigned char prot_type)
445{
446 switch (prot_type) {
447 case SCSI_PROT_DIF_TYPE0:
448 return IB_T10DIF_NONE;
449 case SCSI_PROT_DIF_TYPE1:
450 return IB_T10DIF_TYPE1;
451 case SCSI_PROT_DIF_TYPE2:
452 return IB_T10DIF_TYPE2;
453 case SCSI_PROT_DIF_TYPE3:
454 return IB_T10DIF_TYPE3;
455 default:
456 return IB_T10DIF_NONE;
457 }
458}
459
460static inline void 443static inline void
461iser_set_dif_domain(struct scsi_cmnd *sc, struct ib_sig_attrs *sig_attrs, 444iser_set_dif_domain(struct scsi_cmnd *sc, struct ib_sig_attrs *sig_attrs,
462 struct ib_sig_domain *domain) 445 struct ib_sig_domain *domain)
463{ 446{
464 unsigned char scsi_ptype = scsi_get_prot_type(sc); 447 domain->sig_type = IB_SIG_TYPE_T10_DIF;
465
466 domain->sig.dif.type = scsi2ib_prot_type(scsi_ptype);
467 domain->sig.dif.pi_interval = sc->device->sector_size; 448 domain->sig.dif.pi_interval = sc->device->sector_size;
468 domain->sig.dif.ref_tag = scsi_get_lba(sc) & 0xffffffff; 449 domain->sig.dif.ref_tag = scsi_get_lba(sc) & 0xffffffff;
450 /*
451 * At the moment we hard code those, but in the future
452 * we will take them from sc.
453 */
454 domain->sig.dif.apptag_check_mask = 0xffff;
455 domain->sig.dif.app_escape = true;
456 domain->sig.dif.ref_escape = true;
457 if (scsi_get_prot_type(sc) == SCSI_PROT_DIF_TYPE1 ||
458 scsi_get_prot_type(sc) == SCSI_PROT_DIF_TYPE2)
459 domain->sig.dif.ref_remap = true;
469}; 460};
470 461
471static int 462static int
472iser_set_sig_attrs(struct scsi_cmnd *sc, struct ib_sig_attrs *sig_attrs) 463iser_set_sig_attrs(struct scsi_cmnd *sc, struct ib_sig_attrs *sig_attrs)
473{ 464{
474 sig_attrs->mem.sig_type = IB_SIG_TYPE_T10_DIF;
475 sig_attrs->wire.sig_type = IB_SIG_TYPE_T10_DIF;
476
477 switch (scsi_get_prot_op(sc)) { 465 switch (scsi_get_prot_op(sc)) {
478 case SCSI_PROT_WRITE_INSERT: 466 case SCSI_PROT_WRITE_INSERT:
479 case SCSI_PROT_READ_STRIP: 467 case SCSI_PROT_READ_STRIP:
480 sig_attrs->mem.sig.dif.type = IB_T10DIF_NONE; 468 sig_attrs->mem.sig_type = IB_SIG_TYPE_NONE;
481 iser_set_dif_domain(sc, sig_attrs, &sig_attrs->wire); 469 iser_set_dif_domain(sc, sig_attrs, &sig_attrs->wire);
482 sig_attrs->wire.sig.dif.bg_type = IB_T10DIF_CRC; 470 sig_attrs->wire.sig.dif.bg_type = IB_T10DIF_CRC;
483 break; 471 break;
484 case SCSI_PROT_READ_INSERT: 472 case SCSI_PROT_READ_INSERT:
485 case SCSI_PROT_WRITE_STRIP: 473 case SCSI_PROT_WRITE_STRIP:
486 sig_attrs->wire.sig.dif.type = IB_T10DIF_NONE; 474 sig_attrs->wire.sig_type = IB_SIG_TYPE_NONE;
487 iser_set_dif_domain(sc, sig_attrs, &sig_attrs->mem); 475 iser_set_dif_domain(sc, sig_attrs, &sig_attrs->mem);
476 /*
477 * At the moment we use this modparam to tell what is
478 * the memory bg_type, in the future we will take it
479 * from sc.
480 */
488 sig_attrs->mem.sig.dif.bg_type = iser_pi_guard ? IB_T10DIF_CSUM : 481 sig_attrs->mem.sig.dif.bg_type = iser_pi_guard ? IB_T10DIF_CSUM :
489 IB_T10DIF_CRC; 482 IB_T10DIF_CRC;
490 break; 483 break;
@@ -493,6 +486,11 @@ iser_set_sig_attrs(struct scsi_cmnd *sc, struct ib_sig_attrs *sig_attrs)
493 iser_set_dif_domain(sc, sig_attrs, &sig_attrs->wire); 486 iser_set_dif_domain(sc, sig_attrs, &sig_attrs->wire);
494 sig_attrs->wire.sig.dif.bg_type = IB_T10DIF_CRC; 487 sig_attrs->wire.sig.dif.bg_type = IB_T10DIF_CRC;
495 iser_set_dif_domain(sc, sig_attrs, &sig_attrs->mem); 488 iser_set_dif_domain(sc, sig_attrs, &sig_attrs->mem);
489 /*
490 * At the moment we use this modparam to tell what is
491 * the memory bg_type, in the future we will take it
492 * from sc.
493 */
496 sig_attrs->mem.sig.dif.bg_type = iser_pi_guard ? IB_T10DIF_CSUM : 494 sig_attrs->mem.sig.dif.bg_type = iser_pi_guard ? IB_T10DIF_CSUM :
497 IB_T10DIF_CRC; 495 IB_T10DIF_CRC;
498 break; 496 break;
@@ -501,10 +499,10 @@ iser_set_sig_attrs(struct scsi_cmnd *sc, struct ib_sig_attrs *sig_attrs)
501 scsi_get_prot_op(sc)); 499 scsi_get_prot_op(sc));
502 return -EINVAL; 500 return -EINVAL;
503 } 501 }
502
504 return 0; 503 return 0;
505} 504}
506 505
507
508static int 506static int
509iser_set_prot_checks(struct scsi_cmnd *sc, u8 *mask) 507iser_set_prot_checks(struct scsi_cmnd *sc, u8 *mask)
510{ 508{
diff --git a/drivers/infiniband/ulp/isert/ib_isert.c b/drivers/infiniband/ulp/isert/ib_isert.c
index 73092756460b..0bea5776bcbc 100644
--- a/drivers/infiniband/ulp/isert/ib_isert.c
+++ b/drivers/infiniband/ulp/isert/ib_isert.c
@@ -2609,51 +2609,39 @@ isert_fast_reg_mr(struct isert_conn *isert_conn,
2609 return ret; 2609 return ret;
2610} 2610}
2611 2611
2612static inline enum ib_t10_dif_type
2613se2ib_prot_type(enum target_prot_type prot_type)
2614{
2615 switch (prot_type) {
2616 case TARGET_DIF_TYPE0_PROT:
2617 return IB_T10DIF_NONE;
2618 case TARGET_DIF_TYPE1_PROT:
2619 return IB_T10DIF_TYPE1;
2620 case TARGET_DIF_TYPE2_PROT:
2621 return IB_T10DIF_TYPE2;
2622 case TARGET_DIF_TYPE3_PROT:
2623 return IB_T10DIF_TYPE3;
2624 default:
2625 return IB_T10DIF_NONE;
2626 }
2627}
2628
2629static inline void 2612static inline void
2630isert_set_dif_domain(struct se_cmd *se_cmd, struct ib_sig_attrs *sig_attrs, 2613isert_set_dif_domain(struct se_cmd *se_cmd, struct ib_sig_attrs *sig_attrs,
2631 struct ib_sig_domain *domain) 2614 struct ib_sig_domain *domain)
2632{ 2615{
2633 enum ib_t10_dif_type ib_prot_type = se2ib_prot_type(se_cmd->prot_type); 2616 domain->sig_type = IB_SIG_TYPE_T10_DIF;
2634
2635 domain->sig.dif.type = ib_prot_type;
2636 domain->sig.dif.bg_type = IB_T10DIF_CRC; 2617 domain->sig.dif.bg_type = IB_T10DIF_CRC;
2637 domain->sig.dif.pi_interval = se_cmd->se_dev->dev_attrib.block_size; 2618 domain->sig.dif.pi_interval = se_cmd->se_dev->dev_attrib.block_size;
2638 domain->sig.dif.ref_tag = se_cmd->reftag_seed; 2619 domain->sig.dif.ref_tag = se_cmd->reftag_seed;
2620 /*
2621 * At the moment we hard code those, but if in the future
2622 * the target core would like to use it, we will take it
2623 * from se_cmd.
2624 */
2625 domain->sig.dif.apptag_check_mask = 0xffff;
2626 domain->sig.dif.app_escape = true;
2627 domain->sig.dif.ref_escape = true;
2628 if (se_cmd->prot_type == TARGET_DIF_TYPE1_PROT ||
2629 se_cmd->prot_type == TARGET_DIF_TYPE2_PROT)
2630 domain->sig.dif.ref_remap = true;
2639}; 2631};
2640 2632
2641static int 2633static int
2642isert_set_sig_attrs(struct se_cmd *se_cmd, struct ib_sig_attrs *sig_attrs) 2634isert_set_sig_attrs(struct se_cmd *se_cmd, struct ib_sig_attrs *sig_attrs)
2643{ 2635{
2644
2645 sig_attrs->mem.sig_type = IB_SIG_TYPE_T10_DIF;
2646 sig_attrs->wire.sig_type = IB_SIG_TYPE_T10_DIF;
2647
2648 switch (se_cmd->prot_op) { 2636 switch (se_cmd->prot_op) {
2649 case TARGET_PROT_DIN_INSERT: 2637 case TARGET_PROT_DIN_INSERT:
2650 case TARGET_PROT_DOUT_STRIP: 2638 case TARGET_PROT_DOUT_STRIP:
2651 sig_attrs->mem.sig.dif.type = IB_T10DIF_NONE; 2639 sig_attrs->mem.sig_type = IB_SIG_TYPE_NONE;
2652 isert_set_dif_domain(se_cmd, sig_attrs, &sig_attrs->wire); 2640 isert_set_dif_domain(se_cmd, sig_attrs, &sig_attrs->wire);
2653 break; 2641 break;
2654 case TARGET_PROT_DOUT_INSERT: 2642 case TARGET_PROT_DOUT_INSERT:
2655 case TARGET_PROT_DIN_STRIP: 2643 case TARGET_PROT_DIN_STRIP:
2656 sig_attrs->wire.sig.dif.type = IB_T10DIF_NONE; 2644 sig_attrs->wire.sig_type = IB_SIG_TYPE_NONE;
2657 isert_set_dif_domain(se_cmd, sig_attrs, &sig_attrs->mem); 2645 isert_set_dif_domain(se_cmd, sig_attrs, &sig_attrs->mem);
2658 break; 2646 break;
2659 case TARGET_PROT_DIN_PASS: 2647 case TARGET_PROT_DIN_PASS:
diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h
index ed44cc07a7b3..470a011d6fa4 100644
--- a/include/rdma/ib_verbs.h
+++ b/include/rdma/ib_verbs.h
@@ -491,20 +491,14 @@ struct ib_mr_init_attr {
491 u32 flags; 491 u32 flags;
492}; 492};
493 493
494enum ib_signature_type {
495 IB_SIG_TYPE_T10_DIF,
496};
497
498/** 494/**
499 * T10-DIF Signature types 495 * Signature types
500 * T10-DIF types are defined by SCSI 496 * IB_SIG_TYPE_NONE: Unprotected.
501 * specifications. 497 * IB_SIG_TYPE_T10_DIF: Type T10-DIF
502 */ 498 */
503enum ib_t10_dif_type { 499enum ib_signature_type {
504 IB_T10DIF_NONE, 500 IB_SIG_TYPE_NONE,
505 IB_T10DIF_TYPE1, 501 IB_SIG_TYPE_T10_DIF,
506 IB_T10DIF_TYPE2,
507 IB_T10DIF_TYPE3
508}; 502};
509 503
510/** 504/**
@@ -520,24 +514,26 @@ enum ib_t10_dif_bg_type {
520/** 514/**
521 * struct ib_t10_dif_domain - Parameters specific for T10-DIF 515 * struct ib_t10_dif_domain - Parameters specific for T10-DIF
522 * domain. 516 * domain.
523 * @type: T10-DIF type (0|1|2|3)
524 * @bg_type: T10-DIF block guard type (CRC|CSUM) 517 * @bg_type: T10-DIF block guard type (CRC|CSUM)
525 * @pi_interval: protection information interval. 518 * @pi_interval: protection information interval.
526 * @bg: seed of guard computation. 519 * @bg: seed of guard computation.
527 * @app_tag: application tag of guard block 520 * @app_tag: application tag of guard block
528 * @ref_tag: initial guard block reference tag. 521 * @ref_tag: initial guard block reference tag.
529 * @type3_inc_reftag: T10-DIF type 3 does not state 522 * @ref_remap: Indicate wethear the reftag increments each block
530 * about the reference tag, it is the user 523 * @app_escape: Indicate to skip block check if apptag=0xffff
531 * choice to increment it or not. 524 * @ref_escape: Indicate to skip block check if reftag=0xffffffff
525 * @apptag_check_mask: check bitmask of application tag.
532 */ 526 */
533struct ib_t10_dif_domain { 527struct ib_t10_dif_domain {
534 enum ib_t10_dif_type type;
535 enum ib_t10_dif_bg_type bg_type; 528 enum ib_t10_dif_bg_type bg_type;
536 u16 pi_interval; 529 u16 pi_interval;
537 u16 bg; 530 u16 bg;
538 u16 app_tag; 531 u16 app_tag;
539 u32 ref_tag; 532 u32 ref_tag;
540 bool type3_inc_reftag; 533 bool ref_remap;
534 bool app_escape;
535 bool ref_escape;
536 u16 apptag_check_mask;
541}; 537};
542 538
543/** 539/**