]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
nvme: Add a quirk mechanism that uses identify_ctrl
authorAndy Lutomirski <luto@kernel.org>
Wed, 22 Feb 2017 20:32:36 +0000 (13:32 -0700)
committerJens Axboe <axboe@fb.com>
Wed, 22 Feb 2017 20:34:00 +0000 (13:34 -0700)
Currently, all NVMe quirks are based on PCI IDs.  Add a mechanism to
define quirks based on identify_ctrl's vendor id, model number,
and/or firmware revision.

Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Andy Lutomirski <luto@kernel.org>
Signed-off-by: Sagi Grimberg <sagi@grimberg.me>
Signed-off-by: Jens Axboe <axboe@fb.com>
drivers/nvme/host/core.c
drivers/nvme/host/nvme.h

index 8b1be128b66ebdec97dbaaff127958cf4fb2d2ac..38efe4f752a196e1d2323bbe4d48006411d90290 100644 (file)
@@ -1252,6 +1252,50 @@ static void nvme_set_queue_limits(struct nvme_ctrl *ctrl,
        blk_queue_write_cache(q, vwc, vwc);
 }
 
+struct nvme_core_quirk_entry {
+       /*
+        * NVMe model and firmware strings are padded with spaces.  For
+        * simplicity, strings in the quirk table are padded with NULLs
+        * instead.
+        */
+       u16 vid;
+       const char *mn;
+       const char *fr;
+       unsigned long quirks;
+};
+
+static const struct nvme_core_quirk_entry core_quirks[] = {
+};
+
+/* match is null-terminated but idstr is space-padded. */
+static bool string_matches(const char *idstr, const char *match, size_t len)
+{
+       size_t matchlen;
+
+       if (!match)
+               return true;
+
+       matchlen = strlen(match);
+       WARN_ON_ONCE(matchlen > len);
+
+       if (memcmp(idstr, match, matchlen))
+               return false;
+
+       for (; matchlen < len; matchlen++)
+               if (idstr[matchlen] != ' ')
+                       return false;
+
+       return true;
+}
+
+static bool quirk_matches(const struct nvme_id_ctrl *id,
+                         const struct nvme_core_quirk_entry *q)
+{
+       return q->vid == le16_to_cpu(id->vid) &&
+               string_matches(id->mn, q->mn, sizeof(id->mn)) &&
+               string_matches(id->fr, q->fr, sizeof(id->fr));
+}
+
 /*
  * Initialize the cached copies of the Identify data and various controller
  * register in our nvme_ctrl structure.  This should be called as soon as
@@ -1286,6 +1330,24 @@ int nvme_init_identify(struct nvme_ctrl *ctrl)
                return -EIO;
        }
 
+       if (!ctrl->identified) {
+               /*
+                * Check for quirks.  Quirk can depend on firmware version,
+                * so, in principle, the set of quirks present can change
+                * across a reset.  As a possible future enhancement, we
+                * could re-scan for quirks every time we reinitialize
+                * the device, but we'd have to make sure that the driver
+                * behaves intelligently if the quirks change.
+                */
+
+               int i;
+
+               for (i = 0; i < ARRAY_SIZE(core_quirks); i++) {
+                       if (quirk_matches(id, &core_quirks[i]))
+                               ctrl->quirks |= core_quirks[i].quirks;
+               }
+       }
+
        ctrl->oacs = le16_to_cpu(id->oacs);
        ctrl->vid = le16_to_cpu(id->vid);
        ctrl->oncs = le16_to_cpup(&id->oncs);
@@ -1329,6 +1391,8 @@ int nvme_init_identify(struct nvme_ctrl *ctrl)
        }
 
        kfree(id);
+
+       ctrl->identified = true;
        return ret;
 }
 EXPORT_SYMBOL_GPL(nvme_init_identify);
index 14cfc6f7facb240a96630a637ccc60bd45911302..42ede67bfbc642d33715c16c43ee4dcdb7c9d381 100644 (file)
@@ -112,6 +112,7 @@ enum nvme_ctrl_state {
 
 struct nvme_ctrl {
        enum nvme_ctrl_state state;
+       bool identified;
        spinlock_t lock;
        const struct nvme_ctrl_ops *ops;
        struct request_queue *admin_q;