Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 29 Jun 2020 00:32:08 +0000 (UTC)
From:      Chuck Tuffli <chuck@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r362761 - head/usr.sbin/bhyve
Message-ID:  <202006290032.05T0W8hJ049961@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: chuck
Date: Mon Jun 29 00:32:08 2020
New Revision: 362761
URL: https://svnweb.freebsd.org/changeset/base/362761

Log:
  bhyve: Add AER support to NVMe emulation
  
  This adds support to bhyve's NVMe device emulation for processing Async
  Event Requests but not returning them (i.e. Async Event Notifications).
  
  Fixes UNH Test 5.5.2
  
  Tested by:	Jason Tubnor
  MFC after:	2 weeks
  Differential Revision: https://reviews.freebsd.org/D24896

Modified:
  head/usr.sbin/bhyve/pci_nvme.c

Modified: head/usr.sbin/bhyve/pci_nvme.c
==============================================================================
--- head/usr.sbin/bhyve/pci_nvme.c	Mon Jun 29 00:32:04 2020	(r362760)
+++ head/usr.sbin/bhyve/pci_nvme.c	Mon Jun 29 00:32:08 2020	(r362761)
@@ -251,6 +251,11 @@ struct nvme_feature_obj {
 
 #define NVME_FID_MAX		(NVME_FEAT_ENDURANCE_GROUP_EVENT_CONFIGURATION + 1)
 
+struct pci_nvme_aer {
+	STAILQ_ENTRY(pci_nvme_aer) link;
+	uint16_t	cid;	/* Command ID of the submitted AER */
+};
+
 struct pci_nvme_softc {
 	struct pci_devinst *nsc_pi;
 
@@ -296,6 +301,9 @@ struct pci_nvme_softc {
 	__uint128_t	write_commands;
 	uint32_t	read_dunits_remainder;
 	uint32_t	write_dunits_remainder;
+
+	STAILQ_HEAD(, pci_nvme_aer) aer_list;
+	uint32_t	aer_count;
 };
 
 
@@ -604,6 +612,93 @@ pci_nvme_init_features(struct pci_nvme_softc *sc)
 }
 
 static void
+pci_nvme_aer_init(struct pci_nvme_softc *sc)
+{
+
+	STAILQ_INIT(&sc->aer_list);
+	sc->aer_count = 0;
+}
+
+static void
+pci_nvme_aer_destroy(struct pci_nvme_softc *sc)
+{
+	struct pci_nvme_aer *aer = NULL;
+
+	while (!STAILQ_EMPTY(&sc->aer_list)) {
+		aer = STAILQ_FIRST(&sc->aer_list);
+		STAILQ_REMOVE_HEAD(&sc->aer_list, link);
+		free(aer);
+	}
+
+	pci_nvme_aer_init(sc);
+}
+
+static bool
+pci_nvme_aer_available(struct pci_nvme_softc *sc)
+{
+
+	return (!STAILQ_EMPTY(&sc->aer_list));
+}
+
+static bool
+pci_nvme_aer_limit_reached(struct pci_nvme_softc *sc)
+{
+	struct nvme_controller_data *cd = &sc->ctrldata;
+
+	/* AERL is a zero based value while aer_count is one's based */
+	return (sc->aer_count == (cd->aerl + 1));
+}
+
+/*
+ * Add an Async Event Request
+ *
+ * Stores an AER to be returned later if the Controller needs to notify the
+ * host of an event.
+ * Note that while the NVMe spec doesn't require Controllers to return AER's
+ * in order, this implementation does preserve the order.
+ */
+static int
+pci_nvme_aer_add(struct pci_nvme_softc *sc, uint16_t cid)
+{
+	struct pci_nvme_aer *aer = NULL;
+
+	if (pci_nvme_aer_limit_reached(sc))
+		return (-1);
+
+	aer = calloc(1, sizeof(struct pci_nvme_aer));
+	if (aer == NULL)
+		return (-1);
+
+	sc->aer_count++;
+
+	/* Save the Command ID for use in the completion message */
+	aer->cid = cid;
+	STAILQ_INSERT_TAIL(&sc->aer_list, aer, link);
+
+	return (0);
+}
+
+/*
+ * Get an Async Event Request structure
+ *
+ * Returns a pointer to an AER previously submitted by the host or NULL if
+ * no AER's exist. Caller is responsible for freeing the returned struct.
+ */
+static struct pci_nvme_aer *
+pci_nvme_aer_get(struct pci_nvme_softc *sc)
+{
+	struct pci_nvme_aer *aer = NULL;
+
+	aer = STAILQ_FIRST(&sc->aer_list);
+	if (aer != NULL) {
+		STAILQ_REMOVE_HEAD(&sc->aer_list, link);
+		sc->aer_count--;
+	}
+	
+	return (aer);
+}
+
+static void
 pci_nvme_reset_locked(struct pci_nvme_softc *sc)
 {
 	uint32_t i;
@@ -641,6 +736,8 @@ pci_nvme_reset_locked(struct pci_nvme_softc *sc)
 	}
 
 	sc->num_q_is_set = false;
+
+	pci_nvme_aer_destroy(sc);
 }
 
 static void
@@ -1376,13 +1473,26 @@ nvme_opc_async_event_req(struct pci_nvme_softc* sc,
 {
 	DPRINTF("%s async event request 0x%x", __func__, command->cdw11);
 
+	/* Don't exceed the Async Event Request Limit (AERL). */
+	if (pci_nvme_aer_limit_reached(sc)) {
+		pci_nvme_status_tc(&compl->status, NVME_SCT_COMMAND_SPECIFIC,
+				NVME_SC_ASYNC_EVENT_REQUEST_LIMIT_EXCEEDED);
+		return (1);
+	}
+
+	if (pci_nvme_aer_add(sc, command->cid)) {
+		pci_nvme_status_tc(&compl->status, NVME_SCT_GENERIC,
+				NVME_SC_INTERNAL_DEVICE_ERROR);
+		return (1);
+	}
+
 	/*
-	 * TODO: raise events when they happen based on the Set Features cmd.
+	 * Raise events when they happen based on the Set Features cmd.
 	 * These events happen async, so only set completion successful if
 	 * there is an event reflective of the request to get event.
 	 */
-	pci_nvme_status_tc(&compl->status, NVME_SCT_COMMAND_SPECIFIC,
-	    NVME_SC_ASYNC_EVENT_REQUEST_LIMIT_EXCEEDED);
+	compl->status = NVME_NO_STATUS;
+
 	return (0);
 }
 
@@ -1449,10 +1559,7 @@ pci_nvme_handle_admin_cmd(struct pci_nvme_softc* sc, u
 			break;
 		case NVME_OPC_ASYNC_EVENT_REQUEST:
 			DPRINTF("%s command ASYNC_EVENT_REQ", __func__);
-			/* XXX dont care, unhandled for now
 			nvme_opc_async_event_req(sc, cmd, &compl);
-			*/
-			compl.status = NVME_NO_STATUS;
 			break;
 		case NVME_OPC_FORMAT_NVM:
 			DPRINTF("%s command FORMAT_NVM", __func__);
@@ -2619,6 +2726,8 @@ pci_nvme_init(struct vmctx *ctx, struct pci_devinst *p
 	pci_nvme_init_ctrldata(sc);
 	pci_nvme_init_logpages(sc);
 	pci_nvme_init_features(sc);
+
+	pci_nvme_aer_init(sc);
 
 	pci_nvme_reset(sc);
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202006290032.05T0W8hJ049961>