OSDN Git Service

nvmet: add metadata/T10-PI support
authorIsrael Rukshin <israelr@mellanox.com>
Tue, 19 May 2020 14:06:01 +0000 (17:06 +0300)
committerChristoph Hellwig <hch@lst.de>
Wed, 27 May 2020 05:12:40 +0000 (07:12 +0200)
Expose the namespace metadata format when PI is enabled. The user needs
to enable the capability per subsystem and per port. The other metadata
properties are taken from the namespace/bdev.

Usage example:
echo 1 > /config/nvmet/subsystems/${NAME}/attr_pi_enable
echo 1 > /config/nvmet/ports/${PORT_NUM}/param_pi_enable

Signed-off-by: Israel Rukshin <israelr@mellanox.com>
Signed-off-by: Max Gurtovoy <maxg@mellanox.com>
Reviewed-by: James Smart <james.smart@broadcom.com>
Signed-off-by: Christoph Hellwig <hch@lst.de>
drivers/nvme/target/admin-cmd.c
drivers/nvme/target/configfs.c
drivers/nvme/target/core.c
drivers/nvme/target/fabrics-cmd.c
drivers/nvme/target/nvmet.h

index 7b6b039..1db8c04 100644 (file)
@@ -341,6 +341,7 @@ static void nvmet_execute_identify_ctrl(struct nvmet_req *req)
 {
        struct nvmet_ctrl *ctrl = req->sq->ctrl;
        struct nvme_id_ctrl *id;
+       u32 cmd_capsule_size;
        u16 status = 0;
 
        id = kzalloc(sizeof(*id), GFP_KERNEL);
@@ -433,9 +434,15 @@ static void nvmet_execute_identify_ctrl(struct nvmet_req *req)
 
        strlcpy(id->subnqn, ctrl->subsys->subsysnqn, sizeof(id->subnqn));
 
-       /* Max command capsule size is sqe + single page of in-capsule data */
-       id->ioccsz = cpu_to_le32((sizeof(struct nvme_command) +
-                                 req->port->inline_data_size) / 16);
+       /*
+        * Max command capsule size is sqe + in-capsule data size.
+        * Disable in-capsule data for Metadata capable controllers.
+        */
+       cmd_capsule_size = sizeof(struct nvme_command);
+       if (!ctrl->pi_support)
+               cmd_capsule_size += req->port->inline_data_size;
+       id->ioccsz = cpu_to_le32(cmd_capsule_size / 16);
+
        /* Max response capsule size is cqe */
        id->iorcsz = cpu_to_le32(sizeof(struct nvme_completion) / 16);
 
@@ -465,6 +472,7 @@ out:
 
 static void nvmet_execute_identify_ns(struct nvmet_req *req)
 {
+       struct nvmet_ctrl *ctrl = req->sq->ctrl;
        struct nvmet_ns *ns;
        struct nvme_id_ns *id;
        u16 status = 0;
@@ -482,7 +490,7 @@ static void nvmet_execute_identify_ns(struct nvmet_req *req)
        }
 
        /* return an all zeroed buffer if we can't find an active namespace */
-       ns = nvmet_find_namespace(req->sq->ctrl, req->cmd->identify.nsid);
+       ns = nvmet_find_namespace(ctrl, req->cmd->identify.nsid);
        if (!ns)
                goto done;
 
@@ -523,6 +531,16 @@ static void nvmet_execute_identify_ns(struct nvmet_req *req)
 
        id->lbaf[0].ds = ns->blksize_shift;
 
+       if (ctrl->pi_support && nvmet_ns_has_pi(ns)) {
+               id->dpc = NVME_NS_DPC_PI_FIRST | NVME_NS_DPC_PI_LAST |
+                         NVME_NS_DPC_PI_TYPE1 | NVME_NS_DPC_PI_TYPE2 |
+                         NVME_NS_DPC_PI_TYPE3;
+               id->mc = NVME_MC_EXTENDED_LBA;
+               id->dps = ns->pi_type;
+               id->flbas = NVME_NS_FLBAS_META_EXT;
+               id->lbaf[0].ms = cpu_to_le16(ns->metadata_size);
+       }
+
        if (ns->readonly)
                id->nsattr |= (1 << 0);
        nvmet_put_namespace(ns);
index 17c5292..419e0d4 100644 (file)
@@ -248,6 +248,36 @@ static ssize_t nvmet_param_inline_data_size_store(struct config_item *item,
 
 CONFIGFS_ATTR(nvmet_, param_inline_data_size);
 
+#ifdef CONFIG_BLK_DEV_INTEGRITY
+static ssize_t nvmet_param_pi_enable_show(struct config_item *item,
+               char *page)
+{
+       struct nvmet_port *port = to_nvmet_port(item);
+
+       return snprintf(page, PAGE_SIZE, "%d\n", port->pi_enable);
+}
+
+static ssize_t nvmet_param_pi_enable_store(struct config_item *item,
+               const char *page, size_t count)
+{
+       struct nvmet_port *port = to_nvmet_port(item);
+       bool val;
+
+       if (strtobool(page, &val))
+               return -EINVAL;
+
+       if (port->enabled) {
+               pr_err("Disable port before setting pi_enable value.\n");
+               return -EACCES;
+       }
+
+       port->pi_enable = val;
+       return count;
+}
+
+CONFIGFS_ATTR(nvmet_, param_pi_enable);
+#endif
+
 static ssize_t nvmet_addr_trtype_show(struct config_item *item,
                char *page)
 {
@@ -1010,6 +1040,28 @@ static ssize_t nvmet_subsys_attr_model_store(struct config_item *item,
 }
 CONFIGFS_ATTR(nvmet_subsys_, attr_model);
 
+#ifdef CONFIG_BLK_DEV_INTEGRITY
+static ssize_t nvmet_subsys_attr_pi_enable_show(struct config_item *item,
+                                               char *page)
+{
+       return snprintf(page, PAGE_SIZE, "%d\n", to_subsys(item)->pi_support);
+}
+
+static ssize_t nvmet_subsys_attr_pi_enable_store(struct config_item *item,
+                                                const char *page, size_t count)
+{
+       struct nvmet_subsys *subsys = to_subsys(item);
+       bool pi_enable;
+
+       if (strtobool(page, &pi_enable))
+               return -EINVAL;
+
+       subsys->pi_support = pi_enable;
+       return count;
+}
+CONFIGFS_ATTR(nvmet_subsys_, attr_pi_enable);
+#endif
+
 static struct configfs_attribute *nvmet_subsys_attrs[] = {
        &nvmet_subsys_attr_attr_allow_any_host,
        &nvmet_subsys_attr_attr_version,
@@ -1017,6 +1069,9 @@ static struct configfs_attribute *nvmet_subsys_attrs[] = {
        &nvmet_subsys_attr_attr_cntlid_min,
        &nvmet_subsys_attr_attr_cntlid_max,
        &nvmet_subsys_attr_attr_model,
+#ifdef CONFIG_BLK_DEV_INTEGRITY
+       &nvmet_subsys_attr_attr_pi_enable,
+#endif
        NULL,
 };
 
@@ -1316,6 +1371,9 @@ static struct configfs_attribute *nvmet_port_attrs[] = {
        &nvmet_attr_addr_trsvcid,
        &nvmet_attr_addr_trtype,
        &nvmet_attr_param_inline_data_size,
+#ifdef CONFIG_BLK_DEV_INTEGRITY
+       &nvmet_attr_param_pi_enable,
+#endif
        NULL,
 };
 
index a4ed70e..f9172d3 100644 (file)
@@ -323,12 +323,21 @@ int nvmet_enable_port(struct nvmet_port *port)
        if (!try_module_get(ops->owner))
                return -EINVAL;
 
-       ret = ops->add_port(port);
-       if (ret) {
-               module_put(ops->owner);
-               return ret;
+       /*
+        * If the user requested PI support and the transport isn't pi capable,
+        * don't enable the port.
+        */
+       if (port->pi_enable && !ops->metadata_support) {
+               pr_err("T10-PI is not supported by transport type %d\n",
+                      port->disc_addr.trtype);
+               ret = -EINVAL;
+               goto out_put;
        }
 
+       ret = ops->add_port(port);
+       if (ret)
+               goto out_put;
+
        /* If the transport didn't set inline_data_size, then disable it. */
        if (port->inline_data_size < 0)
                port->inline_data_size = 0;
@@ -336,6 +345,10 @@ int nvmet_enable_port(struct nvmet_port *port)
        port->enabled = true;
        port->tr_ops = ops;
        return 0;
+
+out_put:
+       module_put(ops->owner);
+       return ret;
 }
 
 void nvmet_disable_port(struct nvmet_port *port)
index 52a6f70..42bd12b 100644 (file)
@@ -197,6 +197,8 @@ static void nvmet_execute_admin_connect(struct nvmet_req *req)
                goto out;
        }
 
+       ctrl->pi_support = ctrl->port->pi_enable && ctrl->subsys->pi_support;
+
        uuid_copy(&ctrl->hostid, &d->hostid);
 
        status = nvmet_install_queue(ctrl, req);
@@ -205,8 +207,9 @@ static void nvmet_execute_admin_connect(struct nvmet_req *req)
                goto out;
        }
 
-       pr_info("creating controller %d for subsystem %s for NQN %s.\n",
-               ctrl->cntlid, ctrl->subsys->subsysnqn, ctrl->hostnqn);
+       pr_info("creating controller %d for subsystem %s for NQN %s%s.\n",
+               ctrl->cntlid, ctrl->subsys->subsysnqn, ctrl->hostnqn,
+               ctrl->pi_support ? " T10-PI is enabled" : "");
        req->cqe->result.u16 = cpu_to_le16(ctrl->cntlid);
 
 out:
index 29ab0b9..46b5d2b 100644 (file)
@@ -145,6 +145,7 @@ struct nvmet_port {
        bool                            enabled;
        int                             inline_data_size;
        const struct nvmet_fabrics_ops  *tr_ops;
+       bool                            pi_enable;
 };
 
 static inline struct nvmet_port *to_nvmet_port(struct config_item *item)
@@ -204,6 +205,7 @@ struct nvmet_ctrl {
        spinlock_t              error_lock;
        u64                     err_counter;
        struct nvme_error_slot  slots[NVMET_ERROR_LOG_SLOTS];
+       bool                    pi_support;
 };
 
 struct nvmet_subsys_model {
@@ -233,6 +235,7 @@ struct nvmet_subsys {
        u64                     ver;
        u64                     serial;
        char                    *subsysnqn;
+       bool                    pi_support;
 
        struct config_group     group;
 
@@ -284,6 +287,7 @@ struct nvmet_fabrics_ops {
        unsigned int type;
        unsigned int msdbd;
        bool has_keyed_sgls : 1;
+       bool metadata_support : 1;
        void (*queue_response)(struct nvmet_req *req);
        int (*add_port)(struct nvmet_port *port);
        void (*remove_port)(struct nvmet_port *port);
@@ -511,6 +515,14 @@ static inline u32 nvmet_rw_data_len(struct nvmet_req *req)
                        req->ns->blksize_shift;
 }
 
+static inline u32 nvmet_rw_metadata_len(struct nvmet_req *req)
+{
+       if (!IS_ENABLED(CONFIG_BLK_DEV_INTEGRITY))
+               return 0;
+       return ((u32)le16_to_cpu(req->cmd->rw.length) + 1) *
+                       req->ns->metadata_size;
+}
+
 static inline u32 nvmet_dsm_len(struct nvmet_req *req)
 {
        return (le32_to_cpu(req->cmd->dsm.nr) + 1) *
@@ -525,4 +537,11 @@ static inline __le16 to0based(u32 a)
        return cpu_to_le16(max(1U, min(1U << 16, a)) - 1);
 }
 
+static inline bool nvmet_ns_has_pi(struct nvmet_ns *ns)
+{
+       if (!IS_ENABLED(CONFIG_BLK_DEV_INTEGRITY))
+               return false;
+       return ns->pi_type && ns->metadata_size == sizeof(struct t10_pi_tuple);
+}
+
 #endif /* _NVMET_H */