diff options
Diffstat (limited to 'sys/dev/ic/nvme.c')
-rw-r--r-- | sys/dev/ic/nvme.c | 88 |
1 files changed, 74 insertions, 14 deletions
diff --git a/sys/dev/ic/nvme.c b/sys/dev/ic/nvme.c index ac6fb74..c65d7e0 100644 --- a/sys/dev/ic/nvme.c +++ b/sys/dev/ic/nvme.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023-2024 Ian Marco Moffett and the Osmora Team. + * Copyright (c) 2023-2025 Ian Marco Moffett and the Osmora Team. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -219,7 +219,7 @@ nvme_stop_ctrl(struct nvme_bar *bar) mmio_write32(&bar->config, config); if (nvme_poll_reg(bar, &bar->status, STATUS_RDY, false) < 0) { - pr_error("Controller reset timeout\n"); + pr_error("controller reset timeout\n"); return -ETIME; } @@ -246,7 +246,7 @@ nvme_start_ctrl(struct nvme_bar *bar) mmio_write32(&bar->config, config); if (nvme_poll_reg(bar, &bar->status, STATUS_RDY, true) < 0) { - pr_error("Controller startup timeout\n"); + pr_error("controller startup timeout\n"); return -ETIME; } @@ -292,7 +292,7 @@ nvme_poll_submit_cmd(struct nvme_queue *q, struct nvme_cmd cmd) /* Check for timeout */ if (spins > 5) { - pr_error("Hang while polling phase bit, giving up\n"); + pr_error("hang while polling phase bit, giving up\n"); return -ETIME; } @@ -309,6 +309,35 @@ nvme_poll_submit_cmd(struct nvme_queue *q, struct nvme_cmd cmd) return 0; } +/* + * Get NVMe log page + * + * @ctrl: NVMe controller to target + * @buf: Data buffer + * @lid: Log identifier + * @len: Length (in bytes) + */ +static int +nvme_get_logpage(struct nvme_ctrl *ctrl, void *buf, uint8_t lid, uint32_t len) +{ + struct nvme_cmd cmd = {0}; + struct nvme_get_logpage_cmd *cmdp; + + if (!is_4k_aligned(buf)) { + return -1; + } + + cmdp = &cmd.get_logpage; + cmdp->opcode = NVME_OP_GET_LOGPAGE; + cmdp->nsid = 0xFFFFFFFF; + cmdp->lid = lid; + cmdp->numdl = len / 4; + cmdp->numdu = 0; + cmdp->prp1 = VIRT_TO_PHYS(buf); + cmdp->prp2 = 0; + return nvme_poll_submit_cmd(&ctrl->adminq, cmd); +} + static int nvme_identify(struct nvme_ctrl *ctrl, void *buf, uint32_t nsid, uint8_t cns) { @@ -350,9 +379,9 @@ nvme_log_ctrl_id(struct nvme_id *id) sn[i] = id->sn[i]; } - pr_trace("Model number: %s\n", mn); - pr_trace("Serial number: %s\n", sn); - pr_trace("Firmware revision: %s\n", fr); + pr_trace("model number: %s\n", mn); + pr_trace("serial number: %s\n", sn); + pr_trace("firmware revision: %s\n", fr); } /* @@ -425,7 +454,7 @@ nvme_dev_rw(dev_t dev, struct sio_txn *sio, bool write) */ ns = nvme_get_ns(dev); if (__unlikely(ns == NULL)) - return -EIO; + return -ENODEV; /* Calculate the block count and offset */ block_count = ALIGN_UP(sio->len, ns->lba_bsize); @@ -470,6 +499,12 @@ nvme_dev_read(dev_t dev, struct sio_txn *sio, int flags) return nvme_dev_rw(dev, sio, false); } +static int +nvme_dev_write(dev_t dev, struct sio_txn *sio, int flags) +{ + return nvme_dev_rw(dev, sio, true); +} + /* * Initializes an NVMe namespace. * @@ -543,6 +578,7 @@ nvme_init_ctrl(struct nvme_bar *bar) uint16_t mqes; uint8_t *nsids; struct nvme_ctrl ctrl = { .bar = bar }; + struct nvme_smart_data *smart; struct nvme_queue *adminq; struct nvme_id *id; @@ -566,14 +602,21 @@ nvme_init_ctrl(struct nvme_bar *bar) return error; } + smart = dynalloc_memalign(sizeof(*smart), 0x1000); + if (smart == NULL) { + return -ENOMEM; + } + id = dynalloc_memalign(sizeof(*id), 0x1000); if (id == NULL) { + dynfree(smart); return -ENOMEM; } nsids = dynalloc_memalign(0x1000, 0x1000); if (nsids == NULL) { dynfree(id); + dynfree(smart); return -ENOMEM; } @@ -581,6 +624,18 @@ nvme_init_ctrl(struct nvme_bar *bar) nvme_log_ctrl_id(id); nvme_identify(&ctrl, nsids, 0, ID_CNS_NSID_LIST); + /* + * Attempt to read some SMART data but don't bother + * if it fails in any way. + */ + error = nvme_get_logpage(&ctrl, smart, NVME_LOGPAGE_SMART, sizeof(*smart)); + if (error == 0) { + if (smart->temp != 0 && smart->temp > 283) + pr_trace("temp: %d K\n", smart->temp); + + pr_trace("%d%% used\n", smart->percent_used); + } + ctrl.sqes = id->sqes >> 4; ctrl.cqes = id->cqes >> 4; @@ -601,12 +656,13 @@ nvme_init_ctrl(struct nvme_bar *bar) } if (nvme_init_ns(&ctrl, nsids[i]) != 0) { - pr_error("Failed to initialize NSID %d\n", nsids[i]); + pr_error("failed to initialize NSID %d\n", nsids[i]); } } dynfree(id); dynfree(nsids); + dynfree(smart); return 0; } @@ -625,21 +681,25 @@ nvme_init(void) return -ENODEV; } + pr_trace("NVMe storage ctrl <hba? at pci%d:%x.%x.%d>\n", + nvme_dev->bus, nvme_dev->device_id, nvme_dev->func, + nvme_dev->slot); + /* Try to request a general purpose timer */ if (req_timer(TIMER_GP, &tmr) != TMRR_SUCCESS) { - pr_error("Failed to fetch general purpose timer\n"); + pr_error("failed to fetch general purpose timer\n"); return -ENODEV; } /* Ensure it has get_time_usec() */ if (tmr.get_time_usec == NULL) { - pr_error("General purpose timer has no get_time_usec()\n"); + pr_error("general purpose timer has no get_time_usec()\n"); return -ENODEV; } /* We also need msleep() */ if (tmr.msleep == NULL) { - pr_error("General purpose timer has no msleep()\n"); + pr_error("general purpose timer has no msleep()\n"); return -ENODEV; } @@ -655,7 +715,7 @@ nvme_init(void) static struct bdevsw nvme_bdevsw = { .read = nvme_dev_read, - .write = nowrite + .write = nvme_dev_write }; -DRIVER_EXPORT(nvme_init); +DRIVER_EXPORT(nvme_init, "nvme"); |