Re: [PATCH 5/8] lightnvm: implement get log report chunk helpers

2018-02-15 Thread Javier González
> On 15 Feb 2018, at 04.51, Matias Bjørling  wrote:
> 
> On 02/13/2018 03:06 PM, Javier González wrote:
>> From: Javier González 
>> The 2.0 spec provides a report chunk log page that can be retrieved
>> using the stangard nvme get log page. This replaces the dedicated
>> get/put bad block table in 1.2.
>> This patch implements the helper functions to allow targets retrieve the
>> chunk metadata using get log page
>> Signed-off-by: Javier González 
>> ---
>>  drivers/lightnvm/core.c  | 28 +
>>  drivers/nvme/host/lightnvm.c | 50 
>> 
>>  include/linux/lightnvm.h | 32 
>>  3 files changed, 110 insertions(+)
>> diff --git a/drivers/lightnvm/core.c b/drivers/lightnvm/core.c
>> index 80492fa6ee76..6857a888544a 100644
>> --- a/drivers/lightnvm/core.c
>> +++ b/drivers/lightnvm/core.c
>> @@ -43,6 +43,8 @@ struct nvm_ch_map {
>>  struct nvm_dev_map {
>>  struct nvm_ch_map *chnls;
>>  int nr_chnls;
>> +int bch;
>> +int blun;
>>  };
> 
> bch/blun should be unnecessary if the map_to_dev / map_to_tgt
> functions are implemented correctly (they can with the ppa_addr order
> update as far as I can see)
> 
> What is the reason they can't be used? I might be missing something.

This is a precalculated value used for the offset on
nvm_get_chunk_log_page() actually, not on map_to_dev and map_to_tgt in
the fast path.

The problem is that since we offset to always start at ch:0,lun:0 on
target creation, we need this value. How would you get the offset otherwise?

> 
>>static struct nvm_target *nvm_find_target(struct nvm_dev *dev, const char 
>> *name)
>> @@ -171,6 +173,9 @@ static struct nvm_tgt_dev *nvm_create_tgt_dev(struct 
>> nvm_dev *dev,
>>  if (!dev_map->chnls)
>>  goto err_chnls;
>>  +   dev_map->bch = bch;
>> +dev_map->blun = blun;
>> +
>>  luns = kcalloc(nr_luns, sizeof(struct ppa_addr), GFP_KERNEL);
>>  if (!luns)
>>  goto err_luns;
>> @@ -561,6 +566,19 @@ static void nvm_unregister_map(struct nvm_dev *dev)
>>  kfree(rmap);
>>  }
>>  +static unsigned long nvm_log_off_tgt_to_dev(struct nvm_tgt_dev *tgt_dev)
>> +{
>> +struct nvm_dev_map *dev_map = tgt_dev->map;
>> +struct nvm_geo *geo = _dev->geo;
>> +int lun_off;
>> +unsigned long off;
>> +
>> +lun_off = dev_map->blun + dev_map->bch * geo->num_lun;
>> +off = lun_off * geo->c.num_chk * sizeof(struct nvm_chunk_log_page);
>> +
>> +return off;
>> +}
>> +
>>  static void nvm_map_to_dev(struct nvm_tgt_dev *tgt_dev, struct ppa_addr *p)
>>  {
>>  struct nvm_dev_map *dev_map = tgt_dev->map;
>> @@ -720,6 +738,16 @@ static void nvm_free_rqd_ppalist(struct nvm_tgt_dev 
>> *tgt_dev,
>>  nvm_dev_dma_free(tgt_dev->parent, rqd->ppa_list, rqd->dma_ppa_list);
>>  }
>>  +int nvm_get_chunk_log_page(struct nvm_tgt_dev *tgt_dev,
>> +   struct nvm_chunk_log_page *log,
>> +   unsigned long off, unsigned long len)
>> +{
>> +struct nvm_dev *dev = tgt_dev->parent;
>> +
>> +off += nvm_log_off_tgt_to_dev(tgt_dev);
>> +
>> +return dev->ops->get_chunk_log_page(tgt_dev->parent, log, off, len);
>> +}
> 
> I think that this should be exported as get_bb and set_bb. Else
> linking fails if pblk is compiled as a module.
> 

It is implemented as get_bb and set_bb. Am I missing anything here?

>>int nvm_set_tgt_bb_tbl(struct nvm_tgt_dev *tgt_dev, struct ppa_addr *ppas,
>> int nr_ppas, int type)
>> diff --git a/drivers/nvme/host/lightnvm.c b/drivers/nvme/host/lightnvm.c
>> index 7bc75182c723..355d9b0cf084 100644
>> --- a/drivers/nvme/host/lightnvm.c
>> +++ b/drivers/nvme/host/lightnvm.c
>> @@ -35,6 +35,10 @@ enum nvme_nvm_admin_opcode {
>>  nvme_nvm_admin_set_bb_tbl   = 0xf1,
>>  };
>>  +enum nvme_nvm_log_page {
>> +NVME_NVM_LOG_REPORT_CHUNK   = 0xCA,
>> +};
>> +
> 
> The convention is to have it as lower-case.

Ok.

> 
>>  struct nvme_nvm_ph_rw {
>>  __u8opcode;
>>  __u8flags;
>> @@ -553,6 +557,50 @@ static int nvme_nvm_set_bb_tbl(struct nvm_dev *nvmdev, 
>> struct ppa_addr *ppas,
>>  return ret;
>>  }
>>  +static int nvme_nvm_get_chunk_log_page(struct nvm_dev *nvmdev,
>> +   struct nvm_chunk_log_page *log,
>> +   unsigned long off,
>> +   unsigned long total_len)
> 
> The chunk_log_page interface are both to be used by targets and the block 
> layer code. Therefore, it is not convenient to have a byte-addressible 
> interface exposed all the way up to a target. Instead, use slba and nlb. That 
> simplifies what a target has to implement, and also allows the offset check 
> to be removed.
> 
> Chunk log page should be defined in the nvme implementation, such that it can 
> be accessed through the traditional 

Re: [PATCH 5/8] lightnvm: implement get log report chunk helpers

2018-02-15 Thread Javier González
> On 15 Feb 2018, at 04.51, Matias Bjørling  wrote:
> 
> On 02/13/2018 03:06 PM, Javier González wrote:
>> From: Javier González 
>> The 2.0 spec provides a report chunk log page that can be retrieved
>> using the stangard nvme get log page. This replaces the dedicated
>> get/put bad block table in 1.2.
>> This patch implements the helper functions to allow targets retrieve the
>> chunk metadata using get log page
>> Signed-off-by: Javier González 
>> ---
>>  drivers/lightnvm/core.c  | 28 +
>>  drivers/nvme/host/lightnvm.c | 50 
>> 
>>  include/linux/lightnvm.h | 32 
>>  3 files changed, 110 insertions(+)
>> diff --git a/drivers/lightnvm/core.c b/drivers/lightnvm/core.c
>> index 80492fa6ee76..6857a888544a 100644
>> --- a/drivers/lightnvm/core.c
>> +++ b/drivers/lightnvm/core.c
>> @@ -43,6 +43,8 @@ struct nvm_ch_map {
>>  struct nvm_dev_map {
>>  struct nvm_ch_map *chnls;
>>  int nr_chnls;
>> +int bch;
>> +int blun;
>>  };
> 
> bch/blun should be unnecessary if the map_to_dev / map_to_tgt
> functions are implemented correctly (they can with the ppa_addr order
> update as far as I can see)
> 
> What is the reason they can't be used? I might be missing something.

This is a precalculated value used for the offset on
nvm_get_chunk_log_page() actually, not on map_to_dev and map_to_tgt in
the fast path.

The problem is that since we offset to always start at ch:0,lun:0 on
target creation, we need this value. How would you get the offset otherwise?

> 
>>static struct nvm_target *nvm_find_target(struct nvm_dev *dev, const char 
>> *name)
>> @@ -171,6 +173,9 @@ static struct nvm_tgt_dev *nvm_create_tgt_dev(struct 
>> nvm_dev *dev,
>>  if (!dev_map->chnls)
>>  goto err_chnls;
>>  +   dev_map->bch = bch;
>> +dev_map->blun = blun;
>> +
>>  luns = kcalloc(nr_luns, sizeof(struct ppa_addr), GFP_KERNEL);
>>  if (!luns)
>>  goto err_luns;
>> @@ -561,6 +566,19 @@ static void nvm_unregister_map(struct nvm_dev *dev)
>>  kfree(rmap);
>>  }
>>  +static unsigned long nvm_log_off_tgt_to_dev(struct nvm_tgt_dev *tgt_dev)
>> +{
>> +struct nvm_dev_map *dev_map = tgt_dev->map;
>> +struct nvm_geo *geo = _dev->geo;
>> +int lun_off;
>> +unsigned long off;
>> +
>> +lun_off = dev_map->blun + dev_map->bch * geo->num_lun;
>> +off = lun_off * geo->c.num_chk * sizeof(struct nvm_chunk_log_page);
>> +
>> +return off;
>> +}
>> +
>>  static void nvm_map_to_dev(struct nvm_tgt_dev *tgt_dev, struct ppa_addr *p)
>>  {
>>  struct nvm_dev_map *dev_map = tgt_dev->map;
>> @@ -720,6 +738,16 @@ static void nvm_free_rqd_ppalist(struct nvm_tgt_dev 
>> *tgt_dev,
>>  nvm_dev_dma_free(tgt_dev->parent, rqd->ppa_list, rqd->dma_ppa_list);
>>  }
>>  +int nvm_get_chunk_log_page(struct nvm_tgt_dev *tgt_dev,
>> +   struct nvm_chunk_log_page *log,
>> +   unsigned long off, unsigned long len)
>> +{
>> +struct nvm_dev *dev = tgt_dev->parent;
>> +
>> +off += nvm_log_off_tgt_to_dev(tgt_dev);
>> +
>> +return dev->ops->get_chunk_log_page(tgt_dev->parent, log, off, len);
>> +}
> 
> I think that this should be exported as get_bb and set_bb. Else
> linking fails if pblk is compiled as a module.
> 

It is implemented as get_bb and set_bb. Am I missing anything here?

>>int nvm_set_tgt_bb_tbl(struct nvm_tgt_dev *tgt_dev, struct ppa_addr *ppas,
>> int nr_ppas, int type)
>> diff --git a/drivers/nvme/host/lightnvm.c b/drivers/nvme/host/lightnvm.c
>> index 7bc75182c723..355d9b0cf084 100644
>> --- a/drivers/nvme/host/lightnvm.c
>> +++ b/drivers/nvme/host/lightnvm.c
>> @@ -35,6 +35,10 @@ enum nvme_nvm_admin_opcode {
>>  nvme_nvm_admin_set_bb_tbl   = 0xf1,
>>  };
>>  +enum nvme_nvm_log_page {
>> +NVME_NVM_LOG_REPORT_CHUNK   = 0xCA,
>> +};
>> +
> 
> The convention is to have it as lower-case.

Ok.

> 
>>  struct nvme_nvm_ph_rw {
>>  __u8opcode;
>>  __u8flags;
>> @@ -553,6 +557,50 @@ static int nvme_nvm_set_bb_tbl(struct nvm_dev *nvmdev, 
>> struct ppa_addr *ppas,
>>  return ret;
>>  }
>>  +static int nvme_nvm_get_chunk_log_page(struct nvm_dev *nvmdev,
>> +   struct nvm_chunk_log_page *log,
>> +   unsigned long off,
>> +   unsigned long total_len)
> 
> The chunk_log_page interface are both to be used by targets and the block 
> layer code. Therefore, it is not convenient to have a byte-addressible 
> interface exposed all the way up to a target. Instead, use slba and nlb. That 
> simplifies what a target has to implement, and also allows the offset check 
> to be removed.
> 
> Chunk log page should be defined in the nvme implementation, such that it can 
> be accessed through the traditional LBA path.
> 
> struct nvme_nvm_chk_meta {
>   __u8

Re: [PATCH 5/8] lightnvm: implement get log report chunk helpers

2018-02-15 Thread Matias Bjørling

On 02/13/2018 03:06 PM, Javier González wrote:

From: Javier González 

The 2.0 spec provides a report chunk log page that can be retrieved
using the stangard nvme get log page. This replaces the dedicated
get/put bad block table in 1.2.

This patch implements the helper functions to allow targets retrieve the
chunk metadata using get log page

Signed-off-by: Javier González 
---
  drivers/lightnvm/core.c  | 28 +
  drivers/nvme/host/lightnvm.c | 50 
  include/linux/lightnvm.h | 32 
  3 files changed, 110 insertions(+)

diff --git a/drivers/lightnvm/core.c b/drivers/lightnvm/core.c
index 80492fa6ee76..6857a888544a 100644
--- a/drivers/lightnvm/core.c
+++ b/drivers/lightnvm/core.c
@@ -43,6 +43,8 @@ struct nvm_ch_map {
  struct nvm_dev_map {
struct nvm_ch_map *chnls;
int nr_chnls;
+   int bch;
+   int blun;
  };


bch/blun should be unnecessary if the map_to_dev / map_to_tgt functions 
are implemented correctly (they can with the ppa_addr order update as 
far as I can see)


What is the reason they can't be used? I might be missing something.

  
  static struct nvm_target *nvm_find_target(struct nvm_dev *dev, const char *name)

@@ -171,6 +173,9 @@ static struct nvm_tgt_dev *nvm_create_tgt_dev(struct 
nvm_dev *dev,
if (!dev_map->chnls)
goto err_chnls;
  
+	dev_map->bch = bch;

+   dev_map->blun = blun;
+
luns = kcalloc(nr_luns, sizeof(struct ppa_addr), GFP_KERNEL);
if (!luns)
goto err_luns;
@@ -561,6 +566,19 @@ static void nvm_unregister_map(struct nvm_dev *dev)
kfree(rmap);
  }
  
+static unsigned long nvm_log_off_tgt_to_dev(struct nvm_tgt_dev *tgt_dev)

+{
+   struct nvm_dev_map *dev_map = tgt_dev->map;
+   struct nvm_geo *geo = _dev->geo;
+   int lun_off;
+   unsigned long off;
+
+   lun_off = dev_map->blun + dev_map->bch * geo->num_lun;
+   off = lun_off * geo->c.num_chk * sizeof(struct nvm_chunk_log_page);
+
+   return off;
+}
+
  static void nvm_map_to_dev(struct nvm_tgt_dev *tgt_dev, struct ppa_addr *p)
  {
struct nvm_dev_map *dev_map = tgt_dev->map;
@@ -720,6 +738,16 @@ static void nvm_free_rqd_ppalist(struct nvm_tgt_dev 
*tgt_dev,
nvm_dev_dma_free(tgt_dev->parent, rqd->ppa_list, rqd->dma_ppa_list);
  }
  
+int nvm_get_chunk_log_page(struct nvm_tgt_dev *tgt_dev,

+  struct nvm_chunk_log_page *log,
+  unsigned long off, unsigned long len)
+{
+   struct nvm_dev *dev = tgt_dev->parent;
+
+   off += nvm_log_off_tgt_to_dev(tgt_dev);
+
+   return dev->ops->get_chunk_log_page(tgt_dev->parent, log, off, len);
+}


I think that this should be exported as get_bb and set_bb. Else linking 
fails if pblk is compiled as a module.


  
  int nvm_set_tgt_bb_tbl(struct nvm_tgt_dev *tgt_dev, struct ppa_addr *ppas,

   int nr_ppas, int type)
diff --git a/drivers/nvme/host/lightnvm.c b/drivers/nvme/host/lightnvm.c
index 7bc75182c723..355d9b0cf084 100644
--- a/drivers/nvme/host/lightnvm.c
+++ b/drivers/nvme/host/lightnvm.c
@@ -35,6 +35,10 @@ enum nvme_nvm_admin_opcode {
nvme_nvm_admin_set_bb_tbl   = 0xf1,
  };
  
+enum nvme_nvm_log_page {

+   NVME_NVM_LOG_REPORT_CHUNK   = 0xCA,
+};
+


The convention is to have it as lower-case.


  struct nvme_nvm_ph_rw {
__u8opcode;
__u8flags;
@@ -553,6 +557,50 @@ static int nvme_nvm_set_bb_tbl(struct nvm_dev *nvmdev, 
struct ppa_addr *ppas,
return ret;
  }
  
+static int nvme_nvm_get_chunk_log_page(struct nvm_dev *nvmdev,

+  struct nvm_chunk_log_page *log,
+  unsigned long off,
+  unsigned long total_len)


The chunk_log_page interface are both to be used by targets and the 
block layer code. Therefore, it is not convenient to have a 
byte-addressible interface exposed all the way up to a target. Instead, 
use slba and nlb. That simplifies what a target has to implement, and 
also allows the offset check to be removed.


Chunk log page should be defined in the nvme implementation, such that 
it can be accessed through the traditional LBA path.


struct nvme_nvm_chk_meta {
   __u8state;
  __u8type;
   __u8wli;
   __u8rsvd[5];
   __le64  slba;
   __le64  cnlb;
   __le64  wp;
};


+{
+   struct nvme_ns *ns = nvmdev->q->queuedata;
+   struct nvme_command c = { };
+   unsigned long offset = off, left = total_len;
+   unsigned long len, len_dwords;
+   void *buf = log;
+   int ret;
+
+   /* The offset needs to be dword-aligned */
+   if (offset & 0x3)
+   return -EINVAL;


No need to check for this with the above interface changes.


+
+   do {
+  

Re: [PATCH 5/8] lightnvm: implement get log report chunk helpers

2018-02-15 Thread Matias Bjørling

On 02/13/2018 03:06 PM, Javier González wrote:

From: Javier González 

The 2.0 spec provides a report chunk log page that can be retrieved
using the stangard nvme get log page. This replaces the dedicated
get/put bad block table in 1.2.

This patch implements the helper functions to allow targets retrieve the
chunk metadata using get log page

Signed-off-by: Javier González 
---
  drivers/lightnvm/core.c  | 28 +
  drivers/nvme/host/lightnvm.c | 50 
  include/linux/lightnvm.h | 32 
  3 files changed, 110 insertions(+)

diff --git a/drivers/lightnvm/core.c b/drivers/lightnvm/core.c
index 80492fa6ee76..6857a888544a 100644
--- a/drivers/lightnvm/core.c
+++ b/drivers/lightnvm/core.c
@@ -43,6 +43,8 @@ struct nvm_ch_map {
  struct nvm_dev_map {
struct nvm_ch_map *chnls;
int nr_chnls;
+   int bch;
+   int blun;
  };


bch/blun should be unnecessary if the map_to_dev / map_to_tgt functions 
are implemented correctly (they can with the ppa_addr order update as 
far as I can see)


What is the reason they can't be used? I might be missing something.

  
  static struct nvm_target *nvm_find_target(struct nvm_dev *dev, const char *name)

@@ -171,6 +173,9 @@ static struct nvm_tgt_dev *nvm_create_tgt_dev(struct 
nvm_dev *dev,
if (!dev_map->chnls)
goto err_chnls;
  
+	dev_map->bch = bch;

+   dev_map->blun = blun;
+
luns = kcalloc(nr_luns, sizeof(struct ppa_addr), GFP_KERNEL);
if (!luns)
goto err_luns;
@@ -561,6 +566,19 @@ static void nvm_unregister_map(struct nvm_dev *dev)
kfree(rmap);
  }
  
+static unsigned long nvm_log_off_tgt_to_dev(struct nvm_tgt_dev *tgt_dev)

+{
+   struct nvm_dev_map *dev_map = tgt_dev->map;
+   struct nvm_geo *geo = _dev->geo;
+   int lun_off;
+   unsigned long off;
+
+   lun_off = dev_map->blun + dev_map->bch * geo->num_lun;
+   off = lun_off * geo->c.num_chk * sizeof(struct nvm_chunk_log_page);
+
+   return off;
+}
+
  static void nvm_map_to_dev(struct nvm_tgt_dev *tgt_dev, struct ppa_addr *p)
  {
struct nvm_dev_map *dev_map = tgt_dev->map;
@@ -720,6 +738,16 @@ static void nvm_free_rqd_ppalist(struct nvm_tgt_dev 
*tgt_dev,
nvm_dev_dma_free(tgt_dev->parent, rqd->ppa_list, rqd->dma_ppa_list);
  }
  
+int nvm_get_chunk_log_page(struct nvm_tgt_dev *tgt_dev,

+  struct nvm_chunk_log_page *log,
+  unsigned long off, unsigned long len)
+{
+   struct nvm_dev *dev = tgt_dev->parent;
+
+   off += nvm_log_off_tgt_to_dev(tgt_dev);
+
+   return dev->ops->get_chunk_log_page(tgt_dev->parent, log, off, len);
+}


I think that this should be exported as get_bb and set_bb. Else linking 
fails if pblk is compiled as a module.


  
  int nvm_set_tgt_bb_tbl(struct nvm_tgt_dev *tgt_dev, struct ppa_addr *ppas,

   int nr_ppas, int type)
diff --git a/drivers/nvme/host/lightnvm.c b/drivers/nvme/host/lightnvm.c
index 7bc75182c723..355d9b0cf084 100644
--- a/drivers/nvme/host/lightnvm.c
+++ b/drivers/nvme/host/lightnvm.c
@@ -35,6 +35,10 @@ enum nvme_nvm_admin_opcode {
nvme_nvm_admin_set_bb_tbl   = 0xf1,
  };
  
+enum nvme_nvm_log_page {

+   NVME_NVM_LOG_REPORT_CHUNK   = 0xCA,
+};
+


The convention is to have it as lower-case.


  struct nvme_nvm_ph_rw {
__u8opcode;
__u8flags;
@@ -553,6 +557,50 @@ static int nvme_nvm_set_bb_tbl(struct nvm_dev *nvmdev, 
struct ppa_addr *ppas,
return ret;
  }
  
+static int nvme_nvm_get_chunk_log_page(struct nvm_dev *nvmdev,

+  struct nvm_chunk_log_page *log,
+  unsigned long off,
+  unsigned long total_len)


The chunk_log_page interface are both to be used by targets and the 
block layer code. Therefore, it is not convenient to have a 
byte-addressible interface exposed all the way up to a target. Instead, 
use slba and nlb. That simplifies what a target has to implement, and 
also allows the offset check to be removed.


Chunk log page should be defined in the nvme implementation, such that 
it can be accessed through the traditional LBA path.


struct nvme_nvm_chk_meta {
   __u8state;
  __u8type;
   __u8wli;
   __u8rsvd[5];
   __le64  slba;
   __le64  cnlb;
   __le64  wp;
};


+{
+   struct nvme_ns *ns = nvmdev->q->queuedata;
+   struct nvme_command c = { };
+   unsigned long offset = off, left = total_len;
+   unsigned long len, len_dwords;
+   void *buf = log;
+   int ret;
+
+   /* The offset needs to be dword-aligned */
+   if (offset & 0x3)
+   return -EINVAL;


No need to check for this with the above interface changes.


+
+   do {
+   /* Send 256KB at a time */
+

[PATCH 5/8] lightnvm: implement get log report chunk helpers

2018-02-13 Thread Javier González
From: Javier González 

The 2.0 spec provides a report chunk log page that can be retrieved
using the stangard nvme get log page. This replaces the dedicated
get/put bad block table in 1.2.

This patch implements the helper functions to allow targets retrieve the
chunk metadata using get log page

Signed-off-by: Javier González 
---
 drivers/lightnvm/core.c  | 28 +
 drivers/nvme/host/lightnvm.c | 50 
 include/linux/lightnvm.h | 32 
 3 files changed, 110 insertions(+)

diff --git a/drivers/lightnvm/core.c b/drivers/lightnvm/core.c
index 80492fa6ee76..6857a888544a 100644
--- a/drivers/lightnvm/core.c
+++ b/drivers/lightnvm/core.c
@@ -43,6 +43,8 @@ struct nvm_ch_map {
 struct nvm_dev_map {
struct nvm_ch_map *chnls;
int nr_chnls;
+   int bch;
+   int blun;
 };
 
 static struct nvm_target *nvm_find_target(struct nvm_dev *dev, const char 
*name)
@@ -171,6 +173,9 @@ static struct nvm_tgt_dev *nvm_create_tgt_dev(struct 
nvm_dev *dev,
if (!dev_map->chnls)
goto err_chnls;
 
+   dev_map->bch = bch;
+   dev_map->blun = blun;
+
luns = kcalloc(nr_luns, sizeof(struct ppa_addr), GFP_KERNEL);
if (!luns)
goto err_luns;
@@ -561,6 +566,19 @@ static void nvm_unregister_map(struct nvm_dev *dev)
kfree(rmap);
 }
 
+static unsigned long nvm_log_off_tgt_to_dev(struct nvm_tgt_dev *tgt_dev)
+{
+   struct nvm_dev_map *dev_map = tgt_dev->map;
+   struct nvm_geo *geo = _dev->geo;
+   int lun_off;
+   unsigned long off;
+
+   lun_off = dev_map->blun + dev_map->bch * geo->num_lun;
+   off = lun_off * geo->c.num_chk * sizeof(struct nvm_chunk_log_page);
+
+   return off;
+}
+
 static void nvm_map_to_dev(struct nvm_tgt_dev *tgt_dev, struct ppa_addr *p)
 {
struct nvm_dev_map *dev_map = tgt_dev->map;
@@ -720,6 +738,16 @@ static void nvm_free_rqd_ppalist(struct nvm_tgt_dev 
*tgt_dev,
nvm_dev_dma_free(tgt_dev->parent, rqd->ppa_list, rqd->dma_ppa_list);
 }
 
+int nvm_get_chunk_log_page(struct nvm_tgt_dev *tgt_dev,
+  struct nvm_chunk_log_page *log,
+  unsigned long off, unsigned long len)
+{
+   struct nvm_dev *dev = tgt_dev->parent;
+
+   off += nvm_log_off_tgt_to_dev(tgt_dev);
+
+   return dev->ops->get_chunk_log_page(tgt_dev->parent, log, off, len);
+}
 
 int nvm_set_tgt_bb_tbl(struct nvm_tgt_dev *tgt_dev, struct ppa_addr *ppas,
   int nr_ppas, int type)
diff --git a/drivers/nvme/host/lightnvm.c b/drivers/nvme/host/lightnvm.c
index 7bc75182c723..355d9b0cf084 100644
--- a/drivers/nvme/host/lightnvm.c
+++ b/drivers/nvme/host/lightnvm.c
@@ -35,6 +35,10 @@ enum nvme_nvm_admin_opcode {
nvme_nvm_admin_set_bb_tbl   = 0xf1,
 };
 
+enum nvme_nvm_log_page {
+   NVME_NVM_LOG_REPORT_CHUNK   = 0xCA,
+};
+
 struct nvme_nvm_ph_rw {
__u8opcode;
__u8flags;
@@ -553,6 +557,50 @@ static int nvme_nvm_set_bb_tbl(struct nvm_dev *nvmdev, 
struct ppa_addr *ppas,
return ret;
 }
 
+static int nvme_nvm_get_chunk_log_page(struct nvm_dev *nvmdev,
+  struct nvm_chunk_log_page *log,
+  unsigned long off,
+  unsigned long total_len)
+{
+   struct nvme_ns *ns = nvmdev->q->queuedata;
+   struct nvme_command c = { };
+   unsigned long offset = off, left = total_len;
+   unsigned long len, len_dwords;
+   void *buf = log;
+   int ret;
+
+   /* The offset needs to be dword-aligned */
+   if (offset & 0x3)
+   return -EINVAL;
+
+   do {
+   /* Send 256KB at a time */
+   len = (1 << 18) > left ? left : (1 << 18);
+   len_dwords = (len >> 2) - 1;
+
+   c.get_log_page.opcode = nvme_admin_get_log_page;
+   c.get_log_page.nsid = cpu_to_le32(ns->head->ns_id);
+   c.get_log_page.lid = NVME_NVM_LOG_REPORT_CHUNK;
+   c.get_log_page.lpol = cpu_to_le32(offset & 0x);
+   c.get_log_page.lpou = cpu_to_le32(offset >> 32);
+   c.get_log_page.numdl = cpu_to_le16(len_dwords & 0x);
+   c.get_log_page.numdu = cpu_to_le16(len_dwords >> 16);
+
+   ret = nvme_submit_sync_cmd(ns->ctrl->admin_q, , buf, len);
+   if (ret) {
+   dev_err(ns->ctrl->device,
+   "get chunk log page failed (%d)\n", ret);
+   break;
+   }
+
+   buf += len;
+   offset += len;
+   left -= len;
+   } while (left);
+
+   return ret;
+}
+
 static inline void nvme_nvm_rqtocmd(struct nvm_rq *rqd, struct nvme_ns *ns,
struct 

[PATCH 5/8] lightnvm: implement get log report chunk helpers

2018-02-13 Thread Javier González
From: Javier González 

The 2.0 spec provides a report chunk log page that can be retrieved
using the stangard nvme get log page. This replaces the dedicated
get/put bad block table in 1.2.

This patch implements the helper functions to allow targets retrieve the
chunk metadata using get log page

Signed-off-by: Javier González 
---
 drivers/lightnvm/core.c  | 28 +
 drivers/nvme/host/lightnvm.c | 50 
 include/linux/lightnvm.h | 32 
 3 files changed, 110 insertions(+)

diff --git a/drivers/lightnvm/core.c b/drivers/lightnvm/core.c
index 80492fa6ee76..6857a888544a 100644
--- a/drivers/lightnvm/core.c
+++ b/drivers/lightnvm/core.c
@@ -43,6 +43,8 @@ struct nvm_ch_map {
 struct nvm_dev_map {
struct nvm_ch_map *chnls;
int nr_chnls;
+   int bch;
+   int blun;
 };
 
 static struct nvm_target *nvm_find_target(struct nvm_dev *dev, const char 
*name)
@@ -171,6 +173,9 @@ static struct nvm_tgt_dev *nvm_create_tgt_dev(struct 
nvm_dev *dev,
if (!dev_map->chnls)
goto err_chnls;
 
+   dev_map->bch = bch;
+   dev_map->blun = blun;
+
luns = kcalloc(nr_luns, sizeof(struct ppa_addr), GFP_KERNEL);
if (!luns)
goto err_luns;
@@ -561,6 +566,19 @@ static void nvm_unregister_map(struct nvm_dev *dev)
kfree(rmap);
 }
 
+static unsigned long nvm_log_off_tgt_to_dev(struct nvm_tgt_dev *tgt_dev)
+{
+   struct nvm_dev_map *dev_map = tgt_dev->map;
+   struct nvm_geo *geo = _dev->geo;
+   int lun_off;
+   unsigned long off;
+
+   lun_off = dev_map->blun + dev_map->bch * geo->num_lun;
+   off = lun_off * geo->c.num_chk * sizeof(struct nvm_chunk_log_page);
+
+   return off;
+}
+
 static void nvm_map_to_dev(struct nvm_tgt_dev *tgt_dev, struct ppa_addr *p)
 {
struct nvm_dev_map *dev_map = tgt_dev->map;
@@ -720,6 +738,16 @@ static void nvm_free_rqd_ppalist(struct nvm_tgt_dev 
*tgt_dev,
nvm_dev_dma_free(tgt_dev->parent, rqd->ppa_list, rqd->dma_ppa_list);
 }
 
+int nvm_get_chunk_log_page(struct nvm_tgt_dev *tgt_dev,
+  struct nvm_chunk_log_page *log,
+  unsigned long off, unsigned long len)
+{
+   struct nvm_dev *dev = tgt_dev->parent;
+
+   off += nvm_log_off_tgt_to_dev(tgt_dev);
+
+   return dev->ops->get_chunk_log_page(tgt_dev->parent, log, off, len);
+}
 
 int nvm_set_tgt_bb_tbl(struct nvm_tgt_dev *tgt_dev, struct ppa_addr *ppas,
   int nr_ppas, int type)
diff --git a/drivers/nvme/host/lightnvm.c b/drivers/nvme/host/lightnvm.c
index 7bc75182c723..355d9b0cf084 100644
--- a/drivers/nvme/host/lightnvm.c
+++ b/drivers/nvme/host/lightnvm.c
@@ -35,6 +35,10 @@ enum nvme_nvm_admin_opcode {
nvme_nvm_admin_set_bb_tbl   = 0xf1,
 };
 
+enum nvme_nvm_log_page {
+   NVME_NVM_LOG_REPORT_CHUNK   = 0xCA,
+};
+
 struct nvme_nvm_ph_rw {
__u8opcode;
__u8flags;
@@ -553,6 +557,50 @@ static int nvme_nvm_set_bb_tbl(struct nvm_dev *nvmdev, 
struct ppa_addr *ppas,
return ret;
 }
 
+static int nvme_nvm_get_chunk_log_page(struct nvm_dev *nvmdev,
+  struct nvm_chunk_log_page *log,
+  unsigned long off,
+  unsigned long total_len)
+{
+   struct nvme_ns *ns = nvmdev->q->queuedata;
+   struct nvme_command c = { };
+   unsigned long offset = off, left = total_len;
+   unsigned long len, len_dwords;
+   void *buf = log;
+   int ret;
+
+   /* The offset needs to be dword-aligned */
+   if (offset & 0x3)
+   return -EINVAL;
+
+   do {
+   /* Send 256KB at a time */
+   len = (1 << 18) > left ? left : (1 << 18);
+   len_dwords = (len >> 2) - 1;
+
+   c.get_log_page.opcode = nvme_admin_get_log_page;
+   c.get_log_page.nsid = cpu_to_le32(ns->head->ns_id);
+   c.get_log_page.lid = NVME_NVM_LOG_REPORT_CHUNK;
+   c.get_log_page.lpol = cpu_to_le32(offset & 0x);
+   c.get_log_page.lpou = cpu_to_le32(offset >> 32);
+   c.get_log_page.numdl = cpu_to_le16(len_dwords & 0x);
+   c.get_log_page.numdu = cpu_to_le16(len_dwords >> 16);
+
+   ret = nvme_submit_sync_cmd(ns->ctrl->admin_q, , buf, len);
+   if (ret) {
+   dev_err(ns->ctrl->device,
+   "get chunk log page failed (%d)\n", ret);
+   break;
+   }
+
+   buf += len;
+   offset += len;
+   left -= len;
+   } while (left);
+
+   return ret;
+}
+
 static inline void nvme_nvm_rqtocmd(struct nvm_rq *rqd, struct nvme_ns *ns,
struct nvme_nvm_command *c)
 {
@@ -684,6 +732,8 @@ static