qemu-block
[Top][All Lists]
Advanced

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

Re: [PATCH 12/17] hw/block/nvme: support the get/set features select and


From: Klaus Jensen
Subject: Re: [PATCH 12/17] hw/block/nvme: support the get/set features select and save fields
Date: Fri, 3 Jul 2020 08:03:24 +0200

On Jul  3 00:46, Dmitry Fomichev wrote:
> On Mon, 2020-06-29 at 20:26 +0200, Klaus Jensen wrote:
> > From: Klaus Jensen <k.jensen@samsung.com>
> > 
> > Since the device does not have any persistance state storage, no
> > features are "saveable" and setting the Save (SV) field in any Set
> > Features command will result in a Feature Identifier Not Saveable status
> > code.
> > 
> > Similarly, if the Select (SEL) field is set to request saved values, the
> > devices will (as it should) return the default values instead.
> > 
> > Since this also introduces "Supported Capabilities", the nsid field is
> > now also checked for validity wrt. the feature being get/set'ed.
> > 
> > Signed-off-by: Klaus Jensen <k.jensen@samsung.com>
> > ---
> >  hw/block/nvme.c       | 87 +++++++++++++++++++++++++++++++++++++++----
> >  hw/block/nvme.h       |  8 ++++
> >  hw/block/trace-events |  4 +-
> >  include/block/nvme.h  | 27 +++++++++++++-
> >  4 files changed, 115 insertions(+), 11 deletions(-)
> > 
> > diff --git a/hw/block/nvme.c b/hw/block/nvme.c
> > index 647f408854ae..a41665746d33 100644
> > --- a/hw/block/nvme.c
> > +++ b/hw/block/nvme.c
> > @@ -1056,16 +1056,43 @@ static uint16_t nvme_get_feature(NvmeCtrl *n, 
> > NvmeCmd *cmd, NvmeRequest *req)
> >  {
> >      uint32_t dw10 = le32_to_cpu(cmd->cdw10);
> >      uint32_t dw11 = le32_to_cpu(cmd->cdw11);
> > +    uint32_t nsid = le32_to_cpu(cmd->nsid);
> >      uint32_t result;
> >      uint8_t fid = NVME_GETSETFEAT_FID(dw10);
> > +    NvmeGetFeatureSelect sel = NVME_GETFEAT_SELECT(dw10);
> >      uint16_t iv;
> >  
> > -    trace_pci_nvme_getfeat(nvme_cid(req), fid, dw11);
> > +    trace_pci_nvme_getfeat(nvme_cid(req), fid, sel, dw11);
> >  
> >      if (!nvme_feature_support[fid]) {
> >          return NVME_INVALID_FIELD | NVME_DNR;
> >      }
> >  
> > +    if (nvme_feature_cap[fid] & NVME_FEAT_CAP_NS) {
> > +        if (!nsid || nsid > n->num_namespaces) {
> > +            /*
> > +             * The Reservation Notification Mask and Reservation 
> > Persistence
> > +             * features require a status code of Invalid Field in Command 
> > when
> > +             * NSID is 0xFFFFFFFF. Since the device does not support those
> > +             * features we can always return Invalid Namespace or Format 
> > as we
> > +             * should do for all other features.
> > +             */
> > +            return NVME_INVALID_NSID | NVME_DNR;
> > +        }
> > +    }
> > +
> > +    switch (sel) {
> > +    case NVME_GETFEAT_SELECT_CURRENT:
> > +        break;
> > +    case NVME_GETFEAT_SELECT_SAVED:
> > +        /* no features are saveable by the controller; fallthrough */
> > +    case NVME_GETFEAT_SELECT_DEFAULT:
> > +        goto defaults;
> > +    case NVME_GETFEAT_SELECT_CAP:
> > +        result = cpu_to_le32(nvme_feature_cap[fid]);
> > +        goto out;
> > +    }
> > +
> >      switch (fid) {
> >      case NVME_TEMPERATURE_THRESHOLD:
> >          result = 0;
> > @@ -1091,6 +1118,29 @@ static uint16_t nvme_get_feature(NvmeCtrl *n, 
> > NvmeCmd *cmd, NvmeRequest *req)
> >      case NVME_VOLATILE_WRITE_CACHE:
> >          result = cpu_to_le32(blk_enable_write_cache(n->conf.blk));
> >          trace_pci_nvme_getfeat_vwcache(result ? "enabled" : "disabled");
> > +        break;
> > +    case NVME_ASYNCHRONOUS_EVENT_CONF:
> > +        result = cpu_to_le32(n->features.async_config);
> > +        break;
> > +    case NVME_TIMESTAMP:
> > +        return nvme_get_feature_timestamp(n, cmd);
> > +    default:
> > +        break;
> > +    }
> > +
> > +defaults:
> > +    switch (fid) {
> > +    case NVME_TEMPERATURE_THRESHOLD:
> > +        result = 0;
> 
> This will reset the high or low threshold value set earlier in this function.
> You could do the following to avoid this -
> 

Good catch! All the cases should really just `goto out` anyway, so fixed
that.

> @ -1163,7 +1163,7 @@ static uint16_t nvme_get_feature(NvmeCtrl *n, NvmeCmd 
> *cmd, NvmeRequest *req)
>              break;
>          }
>  
> -        break;
> +        goto out;

This actually needs to be a return NVME_INVALID_FIELD since if we reach
this, THSEL was set to a reserved value.

>      case NVME_VOLATILE_WRITE_CACHE:
>          result = cpu_to_le32(blk_enable_write_cache(n->conf.blk));
>          trace_pci_nvme_getfeat_vwcache(result ? "enabled" : "disabled");
> 
> > +
> > +        if (NVME_TEMP_TMPSEL(dw11) != NVME_TEMP_TMPSEL_COMPOSITE) {
> > +            break;
> > +        }
> > +
> > +        if (NVME_TEMP_THSEL(dw11) == NVME_TEMP_THSEL_OVER) {
> > +            result = cpu_to_le16(NVME_TEMPERATURE_WARNING);
> > +        }
> > +
> >          break;
> >      case NVME_NUMBER_OF_QUEUES:
> >          result = cpu_to_le32((n->params.max_ioqpairs - 1) |
> > @@ -1110,16 +1160,12 @@ static uint16_t nvme_get_feature(NvmeCtrl *n, 
> > NvmeCmd *cmd, NvmeRequest *req)
> >  
> >          result = cpu_to_le32(result);
> >          break;
> > -    case NVME_ASYNCHRONOUS_EVENT_CONF:
> > -        result = cpu_to_le32(n->features.async_config);
> > -        break;
> > -    case NVME_TIMESTAMP:
> > -        return nvme_get_feature_timestamp(n, cmd);
> >      default:
> >          result = cpu_to_le32(nvme_feature_default[fid]);
> >          break;
> >      }
> >  
> > +out:
> >      req->cqe.result = result;
> >      return NVME_SUCCESS;
> >  }
> > @@ -1146,14 +1192,37 @@ static uint16_t nvme_set_feature(NvmeCtrl *n, 
> > NvmeCmd *cmd, NvmeRequest *req)
> >  {
> >      uint32_t dw10 = le32_to_cpu(cmd->cdw10);
> >      uint32_t dw11 = le32_to_cpu(cmd->cdw11);
> > +    uint32_t nsid = le32_to_cpu(cmd->nsid);
> >      uint8_t fid = NVME_GETSETFEAT_FID(dw10);
> > +    uint8_t save = NVME_SETFEAT_SAVE(dw10);
> >  
> > -    trace_pci_nvme_setfeat(nvme_cid(req), fid, dw11);
> > +    trace_pci_nvme_setfeat(nvme_cid(req), fid, save, dw11);
> > +
> > +    if (save) {
> > +        return NVME_FID_NOT_SAVEABLE | NVME_DNR;
> > +    }
> >  
> >      if (!nvme_feature_support[fid]) {
> >          return NVME_INVALID_FIELD | NVME_DNR;
> >      }
> >  
> > +    if (nvme_feature_cap[fid] & NVME_FEAT_CAP_NS) {
> > +        if (!nsid || (nsid != NVME_NSID_BROADCAST &&
> > +                      nsid > n->num_namespaces)) {
> > +            return NVME_INVALID_NSID | NVME_DNR;
> > +        }
> > +    } else if (nsid && nsid != NVME_NSID_BROADCAST) {
> > +        if (nsid > n->num_namespaces) {
> > +            return NVME_INVALID_NSID | NVME_DNR;
> > +        }
> > +
> > +        return NVME_FEAT_NOT_NS_SPEC | NVME_DNR;
> > +    }
> > +
> > +    if (!(nvme_feature_cap[fid] & NVME_FEAT_CAP_CHANGE)) {
> > +        return NVME_FEAT_NOT_CHANGABLE | NVME_DNR;
> > +    }
> > +
> >      switch (fid) {
> >      case NVME_TEMPERATURE_THRESHOLD:
> >          if (NVME_TEMP_TMPSEL(dw11) != NVME_TEMP_TMPSEL_COMPOSITE) {
> > @@ -1998,7 +2067,9 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice 
> > *pci_dev)
> >      id->sqes = (0x6 << 4) | 0x6;
> >      id->cqes = (0x4 << 4) | 0x4;
> >      id->nn = cpu_to_le32(n->num_namespaces);
> > -    id->oncs = cpu_to_le16(NVME_ONCS_WRITE_ZEROS | NVME_ONCS_TIMESTAMP);
> > +    id->oncs = cpu_to_le16(NVME_ONCS_WRITE_ZEROS | NVME_ONCS_TIMESTAMP |
> > +                           NVME_ONCS_FEATURES);
> > +
> >      id->psd[0].mp = cpu_to_le16(0x9c4);
> >      id->psd[0].enlat = cpu_to_le32(0x10);
> >      id->psd[0].exlat = cpu_to_le32(0x4);
> > diff --git a/hw/block/nvme.h b/hw/block/nvme.h
> > index d0763eb59e5d..34dddda29d96 100644
> > --- a/hw/block/nvme.h
> > +++ b/hw/block/nvme.h
> > @@ -90,6 +90,14 @@ typedef struct NvmeFeatureVal {
> >      uint32_t    async_config;
> >  } NvmeFeatureVal;
> >  
> > +static const uint32_t nvme_feature_cap[0x100] = {
> > +    [NVME_TEMPERATURE_THRESHOLD]    = NVME_FEAT_CAP_CHANGE,
> > +    [NVME_VOLATILE_WRITE_CACHE]     = NVME_FEAT_CAP_CHANGE,
> > +    [NVME_NUMBER_OF_QUEUES]         = NVME_FEAT_CAP_CHANGE,
> > +    [NVME_ASYNCHRONOUS_EVENT_CONF]  = NVME_FEAT_CAP_CHANGE,
> > +    [NVME_TIMESTAMP]                = NVME_FEAT_CAP_CHANGE,
> > +};
> > +
> >  static const uint32_t nvme_feature_default[0x100] = {
> >      [NVME_ARBITRATION]           = NVME_ARB_AB_NOLIMIT,
> >  };
> > diff --git a/hw/block/trace-events b/hw/block/trace-events
> > index 42e62f4649f8..4a4ef34071df 100644
> > --- a/hw/block/trace-events
> > +++ b/hw/block/trace-events
> > @@ -46,8 +46,8 @@ pci_nvme_identify_ctrl(void) "identify controller"
> >  pci_nvme_identify_ns(uint32_t ns) "nsid %"PRIu32""
> >  pci_nvme_identify_nslist(uint32_t ns) "nsid %"PRIu32""
> >  pci_nvme_get_log(uint16_t cid, uint8_t lid, uint8_t lsp, uint8_t rae, 
> > uint32_t len, uint64_t off) "cid %"PRIu16" lid 0x%"PRIx8" lsp 0x%"PRIx8" 
> > rae 0x%"PRIx8" len %"PRIu32" off %"PRIu64""
> > -pci_nvme_getfeat(uint16_t cid, uint8_t fid, uint32_t cdw11) "cid %"PRIu16" 
> > fid 0x%"PRIx8" cdw11 0x%"PRIx32""
> > -pci_nvme_setfeat(uint16_t cid, uint8_t fid, uint32_t cdw11) "cid %"PRIu16" 
> > fid 0x%"PRIx8" cdw11 0x%"PRIx32""
> > +pci_nvme_getfeat(uint16_t cid, uint8_t fid, uint8_t sel, uint32_t cdw11) 
> > "cid %"PRIu16" fid 0x%"PRIx8" sel 0x%"PRIx8" cdw11 0x%"PRIx32""
> > +pci_nvme_setfeat(uint16_t cid, uint8_t fid, uint8_t save, uint32_t cdw11) 
> > "cid %"PRIu16" fid 0x%"PRIx8" save 0x%"PRIx8" cdw11 0x%"PRIx32""
> >  pci_nvme_getfeat_vwcache(const char* result) "get feature volatile write 
> > cache, result=%s"
> >  pci_nvme_getfeat_numq(int result) "get feature number of queues, result=%d"
> >  pci_nvme_setfeat_numq(int reqcq, int reqsq, int gotcq, int gotsq) 
> > "requested cq_count=%d sq_count=%d, responding with cq_count=%d sq_count=%d"
> > diff --git a/include/block/nvme.h b/include/block/nvme.h
> > index 662e521c9e9b..60833039a6c5 100644
> > --- a/include/block/nvme.h
> > +++ b/include/block/nvme.h
> > @@ -663,7 +663,7 @@ enum NvmeStatusCodes {
> >      NVME_INVALID_QUEUE_DEL      = 0x010c,
> >      NVME_FID_NOT_SAVEABLE       = 0x010d,
> >      NVME_FEAT_NOT_CHANGABLE     = 0x010e,
> > -    NVME_FID_NOT_NSID_SPEC      = 0x010f,
> > +    NVME_FEAT_NOT_NS_SPEC       = 0x010f,
> >      NVME_FW_REQ_SUSYSTEM_RESET  = 0x0110,
> >      NVME_CONFLICTING_ATTRS      = 0x0180,
> >      NVME_INVALID_PROT_INFO      = 0x0181,
> > @@ -906,9 +906,32 @@ enum NvmeFeatureIds {
> >      NVME_SOFTWARE_PROGRESS_MARKER   = 0x80
> >  };
> >  
> > +typedef enum NvmeFeatureCap {
> > +    NVME_FEAT_CAP_SAVE      = 1 << 0,
> > +    NVME_FEAT_CAP_NS        = 1 << 1,
> > +    NVME_FEAT_CAP_CHANGE    = 1 << 2,
> > +} NvmeFeatureCap;
> > +
> > +typedef enum NvmeGetFeatureSelect {
> > +    NVME_GETFEAT_SELECT_CURRENT = 0x0,
> > +    NVME_GETFEAT_SELECT_DEFAULT = 0x1,
> > +    NVME_GETFEAT_SELECT_SAVED   = 0x2,
> > +    NVME_GETFEAT_SELECT_CAP     = 0x3,
> > +} NvmeGetFeatureSelect;
> > +
> >  #define NVME_GETSETFEAT_FID_MASK 0xff
> >  #define NVME_GETSETFEAT_FID(dw10) (dw10 & NVME_GETSETFEAT_FID_MASK)
> >  
> > +#define NVME_GETFEAT_SELECT_SHIFT 8
> > +#define NVME_GETFEAT_SELECT_MASK  0x7
> > +#define NVME_GETFEAT_SELECT(dw10) \
> > +    ((dw10 >> NVME_GETFEAT_SELECT_SHIFT) & NVME_GETFEAT_SELECT_MASK)
> > +
> > +#define NVME_SETFEAT_SAVE_SHIFT 31
> > +#define NVME_SETFEAT_SAVE_MASK  0x1
> > +#define NVME_SETFEAT_SAVE(dw10) \
> > +    ((dw10 >> NVME_SETFEAT_SAVE_SHIFT) & NVME_SETFEAT_SAVE_MASK)
> > +
> >  typedef struct NvmeRangeType {
> >      uint8_t     type;
> >      uint8_t     attributes;
> > @@ -925,6 +948,8 @@ typedef struct NvmeLBAF {
> >      uint8_t     rp;
> >  } NvmeLBAF;
> >  
> > +#define NVME_NSID_BROADCAST 0xffffffff
> > +
> >  typedef struct NvmeIdNs {
> >      uint64_t    nsze;
> >      uint64_t    ncap;



reply via email to

[Prev in Thread] Current Thread [Next in Thread]