summaryrefslogtreecommitdiff
path: root/usr.sbin/bhyve
diff options
context:
space:
mode:
authorChuck Tuffli <chuck@FreeBSD.org>2020-06-29 00:32:01 +0000
committerChuck Tuffli <chuck@FreeBSD.org>2020-06-29 00:32:01 +0000
commit7d248cffd9a3d4add637a4eeecfeb76362f214e9 (patch)
tree3efaff3b5631985e52ce472a18135b93883902bc /usr.sbin/bhyve
parentae638f2bd5fefed3eee65e960cae71c7fd1d87f5 (diff)
downloadsrc-test2-7d248cffd9a3d4add637a4eeecfeb76362f214e9.tar.gz
src-test2-7d248cffd9a3d4add637a4eeecfeb76362f214e9.zip
bhyve: implement NVMe SMART data I/O statistics
SMART data in NVMe includes statistics for number of read and write commands issued as well as the number of "data units" read and written. NVMe defines "data unit" as thousands of 512 byte blocks (e.g. 1 data unit is 1-1,000 512 byte blocks, 3 data units are 2,001-3,000 512 byte blocks). This patch implements counters for: - Data Units Read - Data Units Written - Host Read Commands - Host Write Commands and exposes the values when the guest reads the SMART/Health Log Page. Fixes UNH Test 1.3.8 Tested by: Jason Tubnor MFC after: 2 weeks Differential Revision: https://reviews.freebsd.org/D24894
Notes
Notes: svn path=/head/; revision=362759
Diffstat (limited to 'usr.sbin/bhyve')
-rw-r--r--usr.sbin/bhyve/pci_nvme.c72
1 files changed, 71 insertions, 1 deletions
diff --git a/usr.sbin/bhyve/pci_nvme.c b/usr.sbin/bhyve/pci_nvme.c
index db02eeb4b75a..7f6e0d394a87 100644
--- a/usr.sbin/bhyve/pci_nvme.c
+++ b/usr.sbin/bhyve/pci_nvme.c
@@ -218,6 +218,7 @@ struct pci_nvme_ioreq {
uint64_t prev_gpaddr;
size_t prev_size;
+ size_t bytes;
struct blockif_req io_req;
@@ -287,6 +288,14 @@ struct pci_nvme_softc {
struct nvme_feature_obj feat[NVME_FID_MAX];
enum nvme_dsm_type dataset_management;
+
+ /* Accounting for SMART data */
+ __uint128_t read_data_units;
+ __uint128_t write_data_units;
+ __uint128_t read_commands;
+ __uint128_t write_commands;
+ uint32_t read_dunits_remainder;
+ uint32_t write_dunits_remainder;
};
@@ -576,6 +585,10 @@ pci_nvme_init_logpages(struct pci_nvme_softc *sc)
memset(&sc->err_log, 0, sizeof(sc->err_log));
memset(&sc->health_log, 0, sizeof(sc->health_log));
memset(&sc->fw_log, 0, sizeof(sc->fw_log));
+
+ /* Set read/write remainder to round up according to spec */
+ sc->read_dunits_remainder = 999;
+ sc->write_dunits_remainder = 999;
}
static void
@@ -961,7 +974,17 @@ nvme_opc_get_log_page(struct pci_nvme_softc* sc, struct nvme_command* command,
NVME_COPY_TO_PRP);
break;
case NVME_LOG_HEALTH_INFORMATION:
- /* TODO: present some smart info */
+ pthread_mutex_lock(&sc->mtx);
+ memcpy(&sc->health_log.data_units_read, &sc->read_data_units,
+ sizeof(sc->health_log.data_units_read));
+ memcpy(&sc->health_log.data_units_written, &sc->write_data_units,
+ sizeof(sc->health_log.data_units_written));
+ memcpy(&sc->health_log.host_read_commands, &sc->read_commands,
+ sizeof(sc->health_log.host_read_commands));
+ memcpy(&sc->health_log.host_write_commands, &sc->write_commands,
+ sizeof(sc->health_log.host_write_commands));
+ pthread_mutex_unlock(&sc->mtx);
+
nvme_prp_memcpy(sc->nsc_pi->pi_vmctx, command->prp1,
command->prp2, (uint8_t *)&sc->health_log,
MIN(logsize, sizeof(sc->health_log)),
@@ -1465,6 +1488,47 @@ pci_nvme_handle_admin_cmd(struct pci_nvme_softc* sc, uint64_t value)
pthread_mutex_unlock(&sq->mtx);
}
+/*
+ * Update the Write and Read statistics reported in SMART data
+ *
+ * NVMe defines "data unit" as thousand's of 512 byte blocks and is rounded up.
+ * E.g. 1 data unit is 1 - 1,000 512 byte blocks. 3 data units are 2,001 - 3,000
+ * 512 byte blocks. Rounding up is acheived by initializing the remainder to 999.
+ */
+static void
+pci_nvme_stats_write_read_update(struct pci_nvme_softc *sc, uint8_t opc,
+ size_t bytes, uint16_t status)
+{
+
+ pthread_mutex_lock(&sc->mtx);
+ switch (opc) {
+ case NVME_OPC_WRITE:
+ sc->write_commands++;
+ if (status != NVME_SC_SUCCESS)
+ break;
+ sc->write_dunits_remainder += (bytes / 512);
+ while (sc->write_dunits_remainder >= 1000) {
+ sc->write_data_units++;
+ sc->write_dunits_remainder -= 1000;
+ }
+ break;
+ case NVME_OPC_READ:
+ sc->read_commands++;
+ if (status != NVME_SC_SUCCESS)
+ break;
+ sc->read_dunits_remainder += (bytes / 512);
+ while (sc->read_dunits_remainder >= 1000) {
+ sc->read_data_units++;
+ sc->read_dunits_remainder -= 1000;
+ }
+ break;
+ default:
+ DPRINTF("%s: Invalid OPC 0x%02x for stats", __func__, opc);
+ break;
+ }
+ pthread_mutex_unlock(&sc->mtx);
+}
+
static int
pci_nvme_append_iov_req(struct pci_nvme_softc *sc, struct pci_nvme_ioreq *req,
uint64_t gpaddr, size_t size, int do_write, uint64_t lba)
@@ -1605,6 +1669,8 @@ pci_nvme_io_done(struct blockif_req *br, int err)
pci_nvme_status_genc(&status, code);
pci_nvme_set_completion(req->sc, sq, req->sqid, req->cid, 0, status);
+ pci_nvme_stats_write_read_update(req->sc, req->opc,
+ req->bytes, status);
pci_nvme_release_ioreq(req->sc, req);
}
@@ -1779,6 +1845,7 @@ nvme_opc_write_read(struct pci_nvme_softc *sc,
goto out;
}
+ req->bytes = bytes;
req->io_req.br_offset = lba;
/* PRP bits 1:0 must be zero */
@@ -1796,6 +1863,9 @@ nvme_opc_write_read(struct pci_nvme_softc *sc,
pending = true;
}
out:
+ if (!pending)
+ pci_nvme_stats_write_read_update(sc, cmd->opc, bytes, *status);
+
return (pending);
}