summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/dev/nvme/nvme_ctrlr.c86
-rw-r--r--sys/dev/nvme/nvme_private.h6
2 files changed, 83 insertions, 9 deletions
diff --git a/sys/dev/nvme/nvme_ctrlr.c b/sys/dev/nvme/nvme_ctrlr.c
index 345be9c..d4a9574 100644
--- a/sys/dev/nvme/nvme_ctrlr.c
+++ b/sys/dev/nvme/nvme_ctrlr.c
@@ -557,10 +557,65 @@ nvme_ctrlr_construct_namespaces(struct nvme_controller *ctrlr)
return (0);
}
+static boolean_t
+is_log_page_id_valid(uint8_t page_id)
+{
+
+ switch (page_id) {
+ case NVME_LOG_ERROR:
+ case NVME_LOG_HEALTH_INFORMATION:
+ case NVME_LOG_FIRMWARE_SLOT:
+ return (TRUE);
+ }
+
+ return (FALSE);
+}
+
+static uint32_t
+nvme_ctrlr_get_log_page_size(struct nvme_controller *ctrlr, uint8_t page_id)
+{
+ uint32_t log_page_size;
+
+ switch (page_id) {
+ case NVME_LOG_ERROR:
+ log_page_size = min(
+ sizeof(struct nvme_error_information_entry) *
+ ctrlr->cdata.elpe,
+ NVME_MAX_AER_LOG_SIZE);
+ break;
+ case NVME_LOG_HEALTH_INFORMATION:
+ log_page_size = sizeof(struct nvme_health_information_page);
+ break;
+ case NVME_LOG_FIRMWARE_SLOT:
+ log_page_size = sizeof(struct nvme_firmware_page);
+ break;
+ default:
+ log_page_size = 0;
+ break;
+ }
+
+ return (log_page_size);
+}
+
+static void
+nvme_ctrlr_async_event_log_page_cb(void *arg, const struct nvme_completion *cpl)
+{
+ struct nvme_async_event_request *aer = arg;
+
+ nvme_notify_async_consumers(aer->ctrlr, &aer->cpl);
+
+ /*
+ * Repost another asynchronous event request to replace the one
+ * that just completed.
+ */
+ nvme_ctrlr_construct_and_submit_aer(aer->ctrlr, aer);
+}
+
static void
nvme_ctrlr_async_event_cb(void *arg, const struct nvme_completion *cpl)
{
- struct nvme_async_event_request *aer = arg;
+ struct nvme_async_event_request *aer = arg;
+ uint8_t log_page_id;
if (cpl->status.sc == NVME_SC_ABORTED_SQ_DELETION) {
/*
@@ -572,16 +627,29 @@ nvme_ctrlr_async_event_cb(void *arg, const struct nvme_completion *cpl)
return;
}
- nvme_notify_async_consumers(aer->ctrlr, cpl);
+ printf("Asynchronous event occurred.\n");
- /* TODO: decode async event type based on status */
+ /* Associated log page is in bits 23:16 of completion entry dw0. */
+ log_page_id = (cpl->cdw0 & 0xFF0000) >> 16;
+
+ if (is_log_page_id_valid(log_page_id)) {
+ aer->log_page_size = nvme_ctrlr_get_log_page_size(aer->ctrlr,
+ log_page_id);
+ memcpy(&aer->cpl, cpl, sizeof(*cpl));
+ nvme_ctrlr_cmd_get_log_page(aer->ctrlr, log_page_id,
+ NVME_GLOBAL_NAMESPACE_TAG, aer->log_page_buffer,
+ aer->log_page_size, nvme_ctrlr_async_event_log_page_cb,
+ aer);
+ /* Wait to notify consumers until after log page is fetched. */
+ } else {
+ nvme_notify_async_consumers(aer->ctrlr, cpl);
- /*
- * Repost another asynchronous event request to replace the one that
- * just completed.
- */
- printf("Asynchronous event occurred.\n");
- nvme_ctrlr_construct_and_submit_aer(aer->ctrlr, aer);
+ /*
+ * Repost another asynchronous event request to replace the one
+ * that just completed.
+ */
+ nvme_ctrlr_construct_and_submit_aer(aer->ctrlr, aer);
+ }
}
static void
diff --git a/sys/dev/nvme/nvme_private.h b/sys/dev/nvme/nvme_private.h
index 624ae11..53d403e 100644
--- a/sys/dev/nvme/nvme_private.h
+++ b/sys/dev/nvme/nvme_private.h
@@ -104,6 +104,9 @@ MALLOC_DECLARE(M_NVME);
#define NVME_MIN_TIMEOUT_PERIOD (5)
#define NVME_MAX_TIMEOUT_PERIOD (120)
+/* Maximum log page size to fetch for AERs. */
+#define NVME_MAX_AER_LOG_SIZE (4096)
+
#ifndef CACHE_LINE_SIZE
#define CACHE_LINE_SIZE (64)
#endif
@@ -126,6 +129,9 @@ struct nvme_async_event_request {
struct nvme_controller *ctrlr;
struct nvme_request *req;
+ struct nvme_completion cpl;
+ uint32_t log_page_size;
+ uint8_t log_page_buffer[NVME_MAX_AER_LOG_SIZE];
};
struct nvme_tracker {
OpenPOWER on IntegriCloud