RE: [PATCH v2 5/5] scsi: ufs: Allow UFS host drivers to override the sg entry size

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



> From: Eric Biggers <ebiggers@xxxxxxxxxx>
> 
> Modify the UFSHCD core to allow 'struct ufshcd_sg_entry' to be
> variable-length. The default is the standard length, but variants can
> override ufs_hba::sg_entry_size with a larger value if there are
> vendor-specific fields following the standard ones.
> 
> This is needed to support inline encryption with ufs-exynos (FMP).
> 
> Cc: Eric Biggers <ebiggers@xxxxxxxxxx>
> Signed-off-by: Eric Biggers <ebiggers@xxxxxxxxxx>
> [ bvanassche: edited commit message and introduced
> CONFIG_SCSI_UFS_VARIABLE_SG_ENTRY_SIZE ]
> Signed-off-by: Bart Van Assche <bvanassche@xxxxxxx>
> ---
>  drivers/ufs/core/ufshcd.c | 39 ++++++++++++++++++---------------------
>  drivers/ufs/host/Kconfig  | 10 ++++++++++
>  include/ufs/ufshcd.h      | 32 ++++++++++++++++++++++++++++++++
>  include/ufs/ufshci.h      |  9 +++++++--
>  4 files changed, 67 insertions(+), 23 deletions(-)
> 
> diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c
> index 8363d2ff622c..8894d66413e1 100644
> --- a/drivers/ufs/core/ufshcd.c
> +++ b/drivers/ufs/core/ufshcd.c
> @@ -523,7 +523,7 @@ void ufshcd_print_trs(struct ufs_hba *hba, unsigned
> long bitmap, bool pr_prdt)
>                 prdt_length = le16_to_cpu(
>                         lrbp->utr_descriptor_ptr->prd_table_length);
>                 if (hba->quirks & UFSHCD_QUIRK_PRDT_BYTE_GRAN)
> -                       prdt_length /= sizeof(struct ufshcd_sg_entry);
> +                       prdt_length /= ufshcd_sg_entry_size(hba);
> 
>                 dev_err(hba->dev,
>                         "UPIU[%d] - PRDT - %d entries  phys@0x%llx\n",
> @@ -532,7 +532,7 @@ void ufshcd_print_trs(struct ufs_hba *hba, unsigned
> long bitmap, bool pr_prdt)
> 
>                 if (pr_prdt)
>                         ufshcd_hex_dump("UPIU PRDT: ", lrbp->ucd_prdt_ptr,
> -                               sizeof(struct ufshcd_sg_entry) * prdt_length);
> +                               ufshcd_sg_entry_size(hba) * prdt_length);
>         }
>  }
> 
> @@ -2437,7 +2437,7 @@ int ufshcd_send_uic_cmd(struct ufs_hba *hba,
> struct uic_command *uic_cmd)
>   */
>  static int ufshcd_map_sg(struct ufs_hba *hba, struct ufshcd_lrb *lrbp)
>  {
> -       struct ufshcd_sg_entry *prd_table;
> +       struct ufshcd_sg_entry *prd;
>         struct scatterlist *sg;
>         struct scsi_cmnd *cmd;
>         int sg_segments;
> @@ -2452,13 +2452,12 @@ static int ufshcd_map_sg(struct ufs_hba *hba,
> struct ufshcd_lrb *lrbp)
> 
>                 if (hba->quirks & UFSHCD_QUIRK_PRDT_BYTE_GRAN)
>                         lrbp->utr_descriptor_ptr->prd_table_length =
> -                               cpu_to_le16((sg_segments *
> -                                       sizeof(struct ufshcd_sg_entry)));
> +                               cpu_to_le16(sg_segments * ufshcd_sg_entry_size(hba));
>                 else
>                         lrbp->utr_descriptor_ptr->prd_table_length =
>                                 cpu_to_le16(sg_segments);
> 
> -               prd_table = lrbp->ucd_prdt_ptr;
> +               prd = lrbp->ucd_prdt_ptr;
> 
>                 scsi_for_each_sg(cmd, sg, sg_segments, i) {
>                         const unsigned int len = sg_dma_len(sg);
> @@ -2472,9 +2471,10 @@ static int ufshcd_map_sg(struct ufs_hba *hba,
> struct ufshcd_lrb *lrbp)
>                          * indicates 4 bytes, '7' indicates 8 bytes, etc."
>                          */
>                         WARN_ONCE(len > 256 * 1024, "len = %#x\n", len);
> -                       prd_table[i].size = cpu_to_le32(len - 1);
> -                       prd_table[i].addr = cpu_to_le64(sg->dma_address);
> -                       prd_table[i].reserved = 0;
> +                       prd->size = cpu_to_le32(len - 1);
> +                       prd->addr = cpu_to_le64(sg->dma_address);
> +                       prd->reserved = 0;
> +                       prd = (void *)prd + ufshcd_sg_entry_size(hba);
>                 }
>         } else {
>                 lrbp->utr_descriptor_ptr->prd_table_length = 0;
> @@ -2767,10 +2767,11 @@ static int ufshcd_map_queues(struct Scsi_Host
> *shost)
> 
>  static void ufshcd_init_lrb(struct ufs_hba *hba, struct ufshcd_lrb *lrb, int i)
>  {
> -       struct utp_transfer_cmd_desc *cmd_descp = hba->ucdl_base_addr;
> +       struct utp_transfer_cmd_desc *cmd_descp = (void *)hba-
> >ucdl_base_addr +
> +               i * sizeof_utp_transfer_cmd_desc(hba);
>         struct utp_transfer_req_desc *utrdlp = hba->utrdl_base_addr;
>         dma_addr_t cmd_desc_element_addr = hba->ucdl_dma_addr +
> -               i * sizeof(struct utp_transfer_cmd_desc);
> +               i * sizeof_utp_transfer_cmd_desc(hba);
>         u16 response_offset = offsetof(struct utp_transfer_cmd_desc,
>                                        response_upiu);
>         u16 prdt_offset = offsetof(struct utp_transfer_cmd_desc, prd_table);
> @@ -2778,11 +2779,11 @@ static void ufshcd_init_lrb(struct ufs_hba *hba,
> struct ufshcd_lrb *lrb, int i)
>         lrb->utr_descriptor_ptr = utrdlp + i;
>         lrb->utrd_dma_addr = hba->utrdl_dma_addr +
>                 i * sizeof(struct utp_transfer_req_desc);
> -       lrb->ucd_req_ptr = (struct utp_upiu_req *)(cmd_descp + i);
> +       lrb->ucd_req_ptr = (struct utp_upiu_req *)cmd_descp;
Maybe here cmd_descp->command_upiu ?

>         lrb->ucd_req_dma_addr = cmd_desc_element_addr;
> -       lrb->ucd_rsp_ptr = (struct utp_upiu_rsp *)cmd_descp[i].response_upiu;
> +       lrb->ucd_rsp_ptr = (struct utp_upiu_rsp *)cmd_descp->response_upiu;
>         lrb->ucd_rsp_dma_addr = cmd_desc_element_addr + response_offset;
> -       lrb->ucd_prdt_ptr = cmd_descp[i].prd_table;
> +       lrb->ucd_prdt_ptr = (struct ufshcd_sg_entry *)cmd_descp->prd_table;
>         lrb->ucd_prdt_dma_addr = cmd_desc_element_addr + prdt_offset;
>  }
> 
> @@ -3669,7 +3670,7 @@ static int ufshcd_memory_alloc(struct ufs_hba
> *hba)
>         size_t utmrdl_size, utrdl_size, ucdl_size;
> 
>         /* Allocate memory for UTP command descriptors */
> -       ucdl_size = (sizeof(struct utp_transfer_cmd_desc) * hba->nutrs);
> +       ucdl_size = sizeof_utp_transfer_cmd_desc(hba) * hba->nutrs;
>         hba->ucdl_base_addr = dmam_alloc_coherent(hba->dev,
>                                                   ucdl_size,
>                                                   &hba->ucdl_dma_addr,
> @@ -3763,7 +3764,7 @@ static void ufshcd_host_memory_configure(struct
> ufs_hba *hba)
>         prdt_offset =
>                 offsetof(struct utp_transfer_cmd_desc, prd_table);
> 
> -       cmd_desc_size = sizeof(struct utp_transfer_cmd_desc);
> +       cmd_desc_size = sizeof_utp_transfer_cmd_desc(hba);
>         cmd_desc_dma_addr = hba->ucdl_dma_addr;
> 
>         for (i = 0; i < hba->nutrs; i++) {
> @@ -9601,6 +9602,7 @@ int ufshcd_alloc_host(struct device *dev, struct
> ufs_hba **hba_handle)
>         hba->dev = dev;
>         hba->dev_ref_clk_freq = REF_CLK_FREQ_INVAL;
>         hba->nop_out_timeout = NOP_OUT_TIMEOUT;
> +       ufshcd_set_sg_entry_size(hba, sizeof(struct ufshcd_sg_entry));
Where are you setting this variant for ufs-exynos?
I would expect here a set_sg_entry_size vop.


>         INIT_LIST_HEAD(&hba->clk_list_head);
>         spin_lock_init(&hba->outstanding_lock);
> 
> @@ -9979,11 +9981,6 @@ static int __init ufshcd_core_init(void)
>  {
>         int ret;
> 
> -       /* Verify that there are no gaps in struct utp_transfer_cmd_desc. */
> -       static_assert(sizeof(struct utp_transfer_cmd_desc) ==
> -                     2 * ALIGNED_UPIU_SIZE +
> -                             SG_ALL * sizeof(struct ufshcd_sg_entry));
> -
>         ufs_debugfs_init();
> 
>         ret = scsi_register_driver(&ufs_dev_wlun_template.gendrv);
> diff --git a/drivers/ufs/host/Kconfig b/drivers/ufs/host/Kconfig
> index 4cc2dbd79ed0..49017abdac92 100644
> --- a/drivers/ufs/host/Kconfig
> +++ b/drivers/ufs/host/Kconfig
> @@ -124,3 +124,13 @@ config SCSI_UFS_EXYNOS
> 
>           Select this if you have UFS host controller on Samsung Exynos SoC.
>           If unsure, say N.
> +
> +config SCSI_UFS_VARIABLE_SG_ENTRY_SIZE
> +       bool "Variable size UTP physical region descriptor"
> +       help
> +         In the UFSHCI 3.0 standard the Physical Region Descriptor (PRD) is a
> +         data structure used for transferring data between host and UFS
> +         device. This data structure describes a single region in physical
> +         memory. Although the standard requires that this data structure has a
> +         size of 16 bytes, for some controllers this data structure has a
> +         different size. Enable this option for UFS controllers that need it.
Then this change should take the form of a quirk, AKA "opts" in exynos_ufs_drv_data.


> diff --git a/include/ufs/ufshcd.h b/include/ufs/ufshcd.h
> index 6d78bcbedb9e..a1d0dab9a01e 100644
> --- a/include/ufs/ufshcd.h
> +++ b/include/ufs/ufshcd.h
> @@ -744,6 +744,9 @@ struct ufs_hba_monitor {
>   * @vops: pointer to variant specific operations
>   * @vps: pointer to variant specific parameters
>   * @priv: pointer to variant specific private data
> +#ifdef CONFIG_SCSI_UFS_VARIABLE_SG_ENTRY_SIZE
> + * @sg_entry_size: size of struct ufshcd_sg_entry (may include variant
> fields)
> +#endif
>   * @irq: Irq number of the controller
>   * @is_irq_enabled: whether or not the UFS controller interrupt is enabled.
>   * @dev_ref_clk_freq: reference clock frequency
> @@ -865,6 +868,9 @@ struct ufs_hba {
>         const struct ufs_hba_variant_ops *vops;
>         struct ufs_hba_variant_params *vps;
>         void *priv;
> +#ifdef CONFIG_SCSI_UFS_VARIABLE_SG_ENTRY_SIZE
> +       size_t sg_entry_size;
> +#endif
>         unsigned int irq;
>         bool is_irq_enabled;
>         enum ufs_ref_clk_freq dev_ref_clk_freq;
> @@ -967,6 +973,32 @@ struct ufs_hba {
>         bool complete_put;
>  };
> 
> +#ifdef CONFIG_SCSI_UFS_VARIABLE_SG_ENTRY_SIZE
> +static inline size_t ufshcd_sg_entry_size(const struct ufs_hba *hba)
> +{
> +       return hba->sg_entry_size;
> +}
> +
> +static inline void ufshcd_set_sg_entry_size(struct ufs_hba *hba, size_t
> sg_entry_size)
> +{
> +       WARN_ON_ONCE(sg_entry_size < sizeof(struct ufshcd_sg_entry));
> +       hba->sg_entry_size = sg_entry_size;
> +}
> +#else
> +static inline size_t ufshcd_sg_entry_size(const struct ufs_hba *hba)
> +{
> +       return sizeof(struct ufshcd_sg_entry);
> +}
> +
> +#define ufshcd_set_sg_entry_size(hba, sg_entry_size)                   \
> +       ({ (void)(hba); BUILD_BUG_ON(sg_entry_size != sizeof(struct
> ufshcd_sg_entry)); })
Why not static inline void?

> +#endif
> +
> +static inline size_t sizeof_utp_transfer_cmd_desc(const struct ufs_hba
> *hba)
> +{
> +       return sizeof(struct utp_transfer_cmd_desc) + SG_ALL *
> ufshcd_sg_entry_size(hba);
> +}
> +
>  /* Returns true if clocks can be gated. Otherwise false */
>  static inline bool ufshcd_is_clkgating_allowed(struct ufs_hba *hba)
>  {
> diff --git a/include/ufs/ufshci.h b/include/ufs/ufshci.h
> index f81aa95ffbc4..4e764016895d 100644
> --- a/include/ufs/ufshci.h
> +++ b/include/ufs/ufshci.h
> @@ -426,18 +426,23 @@ struct ufshcd_sg_entry {
>         __le64    addr;
>         __le32    reserved;
>         __le32    size;
> +       /*
> +        * followed by variant-specific fields if
> +        * CONFIG_SCSI_UFS_VARIABLE_SG_ENTRY_SIZE has been defined.
> +        */
>  };
> 
>  /**
>   * struct utp_transfer_cmd_desc - UTP Command Descriptor (UCD)
>   * @command_upiu: Command UPIU Frame address
>   * @response_upiu: Response UPIU Frame address
> - * @prd_table: Physical Region Descriptor
> + * @prd_table: Physical Region Descriptor: an array of SG_ALL struct
> + *     ufshcd_sg_entry's.  Variant-specific fields may be present after each.
>   */
>  struct utp_transfer_cmd_desc {
>         u8 command_upiu[ALIGNED_UPIU_SIZE];
>         u8 response_upiu[ALIGNED_UPIU_SIZE];
> -       struct ufshcd_sg_entry    prd_table[SG_ALL];
> +       u8 prd_table[];
>  };

Thanks,
Avri

> 
>  /**




[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [SCSI Target Devel]     [Linux SCSI Target Infrastructure]     [Kernel Newbies]     [IDE]     [Security]     [Git]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux ATA RAID]     [Linux IIO]     [Samba]     [Device Mapper]

  Powered by Linux