diff options
author | Andy Lutomirski <luto@kernel.org> | 2017-02-22 15:32:36 -0500 |
---|---|---|
committer | Jens Axboe <axboe@fb.com> | 2017-02-22 15:34:00 -0500 |
commit | bd4da3abaabffdd2472fb7085fcadd5d1d8c2153 (patch) | |
tree | ccae778d88397594827cf6f146dfb8ca7ace189b | |
parent | e5a39dd8238e5d42c830bbd8d31211adf6fea6ca (diff) |
nvme: Add a quirk mechanism that uses identify_ctrl
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>
-rw-r--r-- | drivers/nvme/host/core.c | 64 | ||||
-rw-r--r-- | drivers/nvme/host/nvme.h | 1 |
2 files changed, 65 insertions, 0 deletions
diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 8b1be128b66e..38efe4f752a1 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c | |||
@@ -1252,6 +1252,50 @@ static void nvme_set_queue_limits(struct nvme_ctrl *ctrl, | |||
1252 | blk_queue_write_cache(q, vwc, vwc); | 1252 | blk_queue_write_cache(q, vwc, vwc); |
1253 | } | 1253 | } |
1254 | 1254 | ||
1255 | struct nvme_core_quirk_entry { | ||
1256 | /* | ||
1257 | * NVMe model and firmware strings are padded with spaces. For | ||
1258 | * simplicity, strings in the quirk table are padded with NULLs | ||
1259 | * instead. | ||
1260 | */ | ||
1261 | u16 vid; | ||
1262 | const char *mn; | ||
1263 | const char *fr; | ||
1264 | unsigned long quirks; | ||
1265 | }; | ||
1266 | |||
1267 | static const struct nvme_core_quirk_entry core_quirks[] = { | ||
1268 | }; | ||
1269 | |||
1270 | /* match is null-terminated but idstr is space-padded. */ | ||
1271 | static bool string_matches(const char *idstr, const char *match, size_t len) | ||
1272 | { | ||
1273 | size_t matchlen; | ||
1274 | |||
1275 | if (!match) | ||
1276 | return true; | ||
1277 | |||
1278 | matchlen = strlen(match); | ||
1279 | WARN_ON_ONCE(matchlen > len); | ||
1280 | |||
1281 | if (memcmp(idstr, match, matchlen)) | ||
1282 | return false; | ||
1283 | |||
1284 | for (; matchlen < len; matchlen++) | ||
1285 | if (idstr[matchlen] != ' ') | ||
1286 | return false; | ||
1287 | |||
1288 | return true; | ||
1289 | } | ||
1290 | |||
1291 | static bool quirk_matches(const struct nvme_id_ctrl *id, | ||
1292 | const struct nvme_core_quirk_entry *q) | ||
1293 | { | ||
1294 | return q->vid == le16_to_cpu(id->vid) && | ||
1295 | string_matches(id->mn, q->mn, sizeof(id->mn)) && | ||
1296 | string_matches(id->fr, q->fr, sizeof(id->fr)); | ||
1297 | } | ||
1298 | |||
1255 | /* | 1299 | /* |
1256 | * Initialize the cached copies of the Identify data and various controller | 1300 | * Initialize the cached copies of the Identify data and various controller |
1257 | * register in our nvme_ctrl structure. This should be called as soon as | 1301 | * 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) | |||
1286 | return -EIO; | 1330 | return -EIO; |
1287 | } | 1331 | } |
1288 | 1332 | ||
1333 | if (!ctrl->identified) { | ||
1334 | /* | ||
1335 | * Check for quirks. Quirk can depend on firmware version, | ||
1336 | * so, in principle, the set of quirks present can change | ||
1337 | * across a reset. As a possible future enhancement, we | ||
1338 | * could re-scan for quirks every time we reinitialize | ||
1339 | * the device, but we'd have to make sure that the driver | ||
1340 | * behaves intelligently if the quirks change. | ||
1341 | */ | ||
1342 | |||
1343 | int i; | ||
1344 | |||
1345 | for (i = 0; i < ARRAY_SIZE(core_quirks); i++) { | ||
1346 | if (quirk_matches(id, &core_quirks[i])) | ||
1347 | ctrl->quirks |= core_quirks[i].quirks; | ||
1348 | } | ||
1349 | } | ||
1350 | |||
1289 | ctrl->oacs = le16_to_cpu(id->oacs); | 1351 | ctrl->oacs = le16_to_cpu(id->oacs); |
1290 | ctrl->vid = le16_to_cpu(id->vid); | 1352 | ctrl->vid = le16_to_cpu(id->vid); |
1291 | ctrl->oncs = le16_to_cpup(&id->oncs); | 1353 | ctrl->oncs = le16_to_cpup(&id->oncs); |
@@ -1329,6 +1391,8 @@ int nvme_init_identify(struct nvme_ctrl *ctrl) | |||
1329 | } | 1391 | } |
1330 | 1392 | ||
1331 | kfree(id); | 1393 | kfree(id); |
1394 | |||
1395 | ctrl->identified = true; | ||
1332 | return ret; | 1396 | return ret; |
1333 | } | 1397 | } |
1334 | EXPORT_SYMBOL_GPL(nvme_init_identify); | 1398 | EXPORT_SYMBOL_GPL(nvme_init_identify); |
diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h index 14cfc6f7facb..42ede67bfbc6 100644 --- a/drivers/nvme/host/nvme.h +++ b/drivers/nvme/host/nvme.h | |||
@@ -112,6 +112,7 @@ enum nvme_ctrl_state { | |||
112 | 112 | ||
113 | struct nvme_ctrl { | 113 | struct nvme_ctrl { |
114 | enum nvme_ctrl_state state; | 114 | enum nvme_ctrl_state state; |
115 | bool identified; | ||
115 | spinlock_t lock; | 116 | spinlock_t lock; |
116 | const struct nvme_ctrl_ops *ops; | 117 | const struct nvme_ctrl_ops *ops; |
117 | struct request_queue *admin_q; | 118 | struct request_queue *admin_q; |