Index: sbin/nvmecontrol/Makefile =================================================================== --- sbin/nvmecontrol/Makefile +++ sbin/nvmecontrol/Makefile @@ -3,7 +3,7 @@ PACKAGE=runtime PROG= nvmecontrol SRCS= nvmecontrol.c devlist.c firmware.c identify.c logpage.c \ - perftest.c reset.c nvme_util.c power.c util.c wdc.c + perftest.c reset.c nvme_util.c power.c util.c wdc.c ns.c MAN= nvmecontrol.8 .PATH: ${SRCTOP}/sys/dev/nvme Index: sbin/nvmecontrol/identify.c =================================================================== --- sbin/nvmecontrol/identify.c +++ sbin/nvmecontrol/identify.c @@ -66,7 +66,7 @@ printf("Unlimited\n"); else printf("%d\n", PAGE_SIZE * (1 << cdata->mdts)); - + printf("Controller ID: 0x%02x\n", cdata->ctrlr_id); printf("\n"); printf("Admin Command Set Attributes\n"); printf("============================\n"); Index: sbin/nvmecontrol/ns.c =================================================================== --- /dev/null +++ sbin/nvmecontrol/ns.c @@ -0,0 +1,357 @@ +/*- + * Copyright (c) 2017 Netflix, Inc + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer, + * without modification, immediately at the beginning of the file. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "nvmecontrol.h" + +/* handles NVME_OPC_NAMESPACE_MANAGEMENT and ATTACHMENT admin cmds */ + +static void +nscreate_usage(void) +{ + fprintf(stderr, "usage:\n"); + fprintf(stderr, NSCREATE_USAGE); + exit(1); +} +static void +nsdelete_usage(void) +{ + fprintf(stderr, "usage:\n"); + fprintf(stderr, NSDELETE_USAGE); + exit(1); +} +static void +nsattach_usage(void) +{ + fprintf(stderr, "usage:\n"); + fprintf(stderr, NSATTACH_USAGE); + exit(1); +} +static void +nsdetach_usage(void) +{ + fprintf(stderr, "usage:\n"); + fprintf(stderr, NSDETACH_USAGE); + exit(1); +} + +/* which tbl of error values to look in */ +#define ADMIN 0 +#define ATTACH 1 + +struct ns_result_str { + uint16_t res; + const char * str; +}; + +static struct ns_result_str ns_result[] = { + { 0x2, "Invalid Feild"}, + { 0xa, "Invalid Format"}, + { 0xb, "Invalid Namespace or format"}, + { 0x15, "Namespace insufficent capacity"}, + { 0x16, "Namespace ID unavaliable"}, + { 0x18, "Namespace already attached"}, + { 0x19, "Namespace is private"}, + { 0x1a, "Namespace is not attached"}, + { 0x1b, "Thin provisioning not supported"}, + { 0x1c, "Controller list invalid"}, + { 0xFFFF, "Unknown"} +}; + +static const char * +get_res_str(uint16_t res) +{ + struct ns_result_str *t = ns_result; + + while (t->res != 0xFFFF) { + if (t->res == res) + return (t->str); + t++; + } + return t->str; +} + +/* + * NS MGMT Command specific status values: + * 0xa = Invalid Format + * 0x15 = Namespace Insuffience capacity + * 0x16 = Namespace ID unavailable (number namespaces exceeded) + * 0xb = Thin Provisioning Not supported + */ +void +nscreate(int argc, char *argv[]) +{ + struct nvme_pt_command pt; + struct nvme_namespace_data nsdata; + + int64_t nsze = -1; + int64_t cap = -1; + int ch, fd, result, format = -1; + + if (optind >= argc) + nscreate_usage(); + + while ((ch = getopt(argc, argv, "s:c:f:")) != -1) { + switch (ch) { + case 's': + nsze = atol(optarg); + break; + case 'c': + cap = atol(optarg); + break; + case 'f': + format = atoi(optarg); + break; + default: + nscreate_usage(); + } + } + + if (optind >= argc) + nscreate_usage(); + + if (nsze == -1 || cap == -1) + nscreate_usage(); + + nsdata.nsze = (uint64_t)nsze; + nsdata.ncap = (uint64_t)cap; + + if (format == -1) { + nsdata.flbas.format = 0x2; + } + + memset(&pt, 0, sizeof(pt)); + pt.cmd.opc = NVME_OPC_NAMESPACE_MANAGEMENT; + + pt.cmd.cdw10 = 0; /* create */ + pt.buf = &nsdata; + pt.len = sizeof(struct nvme_namespace_data); + pt.is_read = 0; /* passthrough writes data to ctrlr */ + open_dev(argv[optind], &fd, 1, 1); + if ((result = ioctl(fd, NVME_PASSTHROUGH_CMD, &pt)) < 0) + errx(1, "ioctl request to %s failed: %d", argv[optind], result); + + if (nvme_completion_is_error(&pt.cpl)) { + printf("%s: nscreate failed - %s\n", argv[optind], + get_res_str(pt.cpl.status.sc)); + exit(-1); + } + printf("%s: new namespace created nsid: %d\n", + argv[optind], pt.cpl.cdw0); + + exit(0); +} +#define LINELEN 80 +void +nsdelete(int argc, char *argv[]) +{ + struct nvme_pt_command pt; + int ch, fd, result, nsid = -2; + char answer[LINELEN]; + char buf[2]; + + if (optind >= argc) + nsdelete_usage(); + + while ((ch = getopt(argc, argv, "n:")) != -1) { + switch ((char)ch) { + case 'n': + nsid = atoi(optarg); + break; + default: + nsdelete_usage(); + } + } + if (optind >= argc || nsid == -2) + nsdelete_usage(); + + + if (nsid == -1) { /* delete all hurts */ + printf("using -1 as nsid will remove ALL namespaces, are you sure? [yes|no] \n"); + fgets(answer, LINELEN, stdin); + if (strncmp(answer,"yes",3) != 0) { + printf("nsdelete cancelled.\n"); + exit(0); + } + } + + memset(&pt, 0, sizeof(pt)); + pt.cmd.opc = NVME_OPC_NAMESPACE_MANAGEMENT; + pt.cmd.cdw10 = 1; /* delete */ + pt.buf = buf; + pt.len = sizeof(buf); + pt.is_read = 1; + pt.cmd.nsid = (uint32_t)nsid; + + open_dev(argv[optind], &fd, 1, 1); + if ((result = ioctl(fd, NVME_PASSTHROUGH_CMD, &pt)) < 0) + errx(1, "ioctl request to %s failed: %d", argv[optind], result); + + if (nvme_completion_is_error(&pt.cpl)) { + printf("%s nsdelete failed - %s\n", argv[optind], + get_res_str(pt.cpl.status.sc)); + exit(-1); + } + printf("%s namespace nsid: %d deleted\n", argv[optind], nsid); + exit(0); +} + +/* + * Attach and Detach use Dword 10, and a controller list (section 4.9) + * This struct is 4096 bytes in size. + * 0h = attach + * 1h = detach + * + * Result values for both attach/detach: + * + * Completion 18h = Already attached + * 19h = NS is private and already attached to a controller + * 1Ah = Not attached, request could not be completed + * 1Ch = Controller list invalid. + * + * 0x2 Invalid Field can occur if ctrlrid d.n.e in system. + */ +void +nsattach(int argc, char *argv[]) +{ + struct nvme_pt_command pt; + struct ctrlr_list clist; + int ctrlrid = -1; + int fd, ch, result, nsid = -1; + + if (optind >= argc) + nsattach_usage(); + + while ((ch = getopt(argc, argv, "n:c:")) != -1) { + switch (ch) { + case 'n': + nsid = atoi(optarg); + break; + case 'c': + ctrlrid = atoi(optarg); + break; + default: + nsattach_usage(); + } + } + + if (optind >= argc) + nsattach_usage(); + + if (ctrlrid == -1 || nsid == -1 ) + nsattach_usage(); + + clist.ctrlr_cnt = 1; + clist.ctrlrs[0] = (uint16_t)ctrlrid; + + memset(&pt, 0, sizeof(pt)); + pt.cmd.opc = NVME_OPC_NAMESPACE_ATTACHMENT; + pt.cmd.cdw10 = 0; /* attach */ + pt.cmd.nsid = (uint32_t)nsid; + pt.buf = &clist; + pt.len = sizeof(clist); + + open_dev(argv[optind], &fd, 1, 1); + if ((result = ioctl(fd, NVME_PASSTHROUGH_CMD, &pt)) < 0) + errx(1, "ioctl request to %s failed: %d", argv[optind], result); + + if (nvme_completion_is_error(&pt.cpl)) { + printf("%s: nsdetach failed - %s\n", argv[optind], + get_res_str(pt.cpl.status.sc)); + exit(-1); + } else { + fprintf(stderr,"%s nsid %d attached\n", argv[optind], + nsid); + } + exit(0); +} + +void +nsdetach(int argc, char *argv[]) +{ + struct nvme_pt_command pt; + struct ctrlr_list clist; + int ctrlrid = -1; + int fd, ch, result, nsid = -1; + + if (optind >= argc) + nsdetach_usage(); + + while ((ch = getopt(argc, argv, "n:c:")) != -1) { + switch (ch) { + case 'n': + nsid = atoi(optarg); + break; + case 'c': + ctrlrid = atoi(optarg); + break; + default: + nsdetach_usage(); + } + } + + if (optind >= argc) + nsdetach_usage(); + + if (ctrlrid == -1 || nsid == -1) + nsdetach_usage(); + + clist.ctrlr_cnt = 1; + clist.ctrlrs[0] = (uint16_t)ctrlrid; /* shared not supported */ + + memset(&pt, 0, sizeof(pt)); + pt.cmd.opc = NVME_OPC_NAMESPACE_ATTACHMENT; + pt.cmd.cdw10 = 1; /* detach */ + pt.cmd.nsid = (uint32_t)nsid; + pt.buf = &clist; + pt.len = sizeof(clist); + + open_dev(argv[optind], &fd, 1, 1); + if ((result = ioctl(fd, NVME_PASSTHROUGH_CMD, &pt)) < 0) + errx(1, "ioctl request to %s failed: %d", argv[optind], result); + + if (nvme_completion_is_error(&pt.cpl)) { + printf("%s: nsdetach failed - %s\n", argv[optind], + get_res_str(pt.cpl.status.sc)); + exit(-1); + } else { + fprintf(stderr,"nsid %d detached from %s\n", + nsid, argv[optind]); + } + exit(0); +} Index: sbin/nvmecontrol/nvmecontrol.h =================================================================== --- sbin/nvmecontrol/nvmecontrol.h +++ sbin/nvmecontrol/nvmecontrol.h @@ -69,6 +69,18 @@ #define WDC_USAGE \ " nvmecontrol wdc (cap-diag|drive-log|get-crash-dump|purge|purge-montior)\n" +#define NSCREATE_USAGE \ +" nvmecontrol nscreate -s nssize -c capacity [-f format] nvmeN\n" + +#define NSDELETE_USAGE \ +" nvmecontrol nsdelete -n namespace id nvmeN\n" + +#define NSATTACH_USAGE \ +" nvmecontrol nsattach -n namespace id -c controller id nvmeN \n" + +#define NSDETACH_USAGE \ +" nvmecontrol nsdetach -n namespace id -c controller id nvmeN\n" + void devlist(int argc, char *argv[]); void identify(int argc, char *argv[]); void perftest(int argc, char *argv[]); @@ -77,6 +89,10 @@ void firmware(int argc, char *argv[]); void power(int argc, char *argv[]); void wdc(int argc, char *argv[]); +void nscreate(int argc, char *argv[]); +void nsdelete(int argc, char *argv[]); +void nsattach(int argc, char *argv[]); +void nsdetach(int argc, char *argv[]); int open_dev(const char *str, int *fd, int show_error, int exit_on_error); void parse_ns_str(const char *ns_str, char *ctrlr_str, int *nsid); Index: sbin/nvmecontrol/nvmecontrol.c =================================================================== --- sbin/nvmecontrol/nvmecontrol.c +++ sbin/nvmecontrol/nvmecontrol.c @@ -55,6 +55,10 @@ {"firmware", firmware, FIRMWARE_USAGE}, {"power", power, POWER_USAGE}, {"wdc", wdc, WDC_USAGE}, + {"nscreate", nscreate, NSCREATE_USAGE}, + {"nsdelete", nsdelete, NSDELETE_USAGE}, + {"nsattach", nsattach, NSATTACH_USAGE}, + {"nsdetach", nsdetach, NSDETACH_USAGE}, {NULL, NULL, NULL}, }; Index: sys/dev/nvd/nvd.c =================================================================== --- sys/dev/nvd/nvd.c +++ sys/dev/nvd/nvd.c @@ -35,10 +35,10 @@ #include #include #include - +#include #include #include - +#include /* xxx */ #include #define NVD_STR "nvd" @@ -56,7 +56,7 @@ static void *nvd_new_controller(struct nvme_controller *ctrlr); static void nvd_controller_fail(void *ctrlr); - +static void *nvd_new_controller(struct nvme_controller *ctrlr); static int nvd_load(void); static void nvd_unload(void); @@ -139,7 +139,7 @@ TAILQ_INIT(&disk_head); consumer_handle = nvme_register_consumer(nvd_new_disk, - nvd_new_controller, NULL, nvd_controller_fail); + nvd_new_controller, NULL, nvd_controller_fail, nvd_remove_disk); return (consumer_handle != NULL ? 0 : -1); } @@ -419,6 +419,32 @@ mtx_destroy(&ndisk->bioqlock); } +/* + * namemspace disks are removed at detach time + * from the periph's perspective. + */ +void * +nvd_remove_disk(int nsid, void *ctrlr_arg) { + + struct nvd_controller *ctrlr = ctrlr_arg; + struct nvd_disk *disk, *tdisk; + + if (TAILQ_EMPTY(&ctrlr->disk_head)) { + return (NULL); + } + + TAILQ_FOREACH_SAFE(disk, (&ctrlr->disk_head), ctrlr_tailq, tdisk ) { + if (disk->ns->id == nsid) { + TAILQ_REMOVE(&disk_head, disk, global_tailq); + TAILQ_REMOVE(&ctrlr->disk_head, disk, ctrlr_tailq); + destroy_geom_disk(disk); + free(disk, M_NVD); + return (NULL); + } + } + return (NULL); +} + static void nvd_controller_fail(void *ctrlr_arg) { Index: sys/dev/nvme/nvme.h =================================================================== --- sys/dev/nvme/nvme.h +++ sys/dev/nvme/nvme.h @@ -863,6 +863,7 @@ * by the caller. */ struct mtx * driver_lock; + struct nvme_controller *ctrlr; }; #define nvme_completion_is_error(cpl) \ @@ -885,6 +886,7 @@ typedef void (*nvme_cons_async_fn_t)(void *, const struct nvme_completion *, uint32_t, void *, uint32_t); typedef void (*nvme_cons_fail_fn_t)(void *); +typedef void *(*nvme_cons_rm_ns_fn_t)(int nsid, void *); enum nvme_namespace_flags { NVME_NS_DEALLOCATE_SUPPORTED = 0x1, @@ -934,6 +936,7 @@ nvme_cons_ctrlr_fn_t ctrlr_fn, nvme_cons_async_fn_t async_fn, nvme_cons_fail_fn_t fail_fn); + nvme_cons_rm_ns_fn_t rm_ns_fn); void nvme_unregister_consumer(struct nvme_consumer *consumer); /* Controller helper functions */ Index: sys/dev/nvme/nvme.c =================================================================== --- sys/dev/nvme/nvme.c +++ sys/dev/nvme/nvme.c @@ -45,6 +45,7 @@ nvme_cons_ctrlr_fn_t ctrlr_fn; nvme_cons_async_fn_t async_fn; nvme_cons_fail_fn_t fail_fn; + nvme_cons_rm_ns_fn_t rm_ns_fn; }; struct nvme_consumer nvme_consumer[NVME_MAX_CONSUMERS]; @@ -290,13 +291,49 @@ return (0); } +/* + * For geom plumbed periph consumer(s) use notify to call the + * given namespace function (new disk) + * + */ +void +nvme_notify_namespace(struct nvme_controller *ctrlr, int ns_idx, int op) +{ + struct nvme_namespace *ns; + struct nvme_consumer *cons; + void *ctrlr_cookie = NULL; + int i; + + for (i = 0; i < NVME_MAX_CONSUMERS; i++) { + + if (nvme_consumer[i].id != INVALID_CONSUMER_ID) { + ns = &ctrlr->ns[ns_idx]; + cons = &nvme_consumer[i]; + + if (ns->data.nsze == 0) { + continue; + } + ctrlr_cookie = ctrlr->cons_cookie[i]; + + if (op && cons->ns_fn != NULL) { + ns->cons_cookie[cons->id] = + (*cons->ns_fn)(ns, ctrlr_cookie); + + } else { + ns->cons_cookie[cons->id] = + (*cons->rm_ns_fn)(ns_idx, ctrlr_cookie); + } + } + } +} + static void nvme_notify(struct nvme_consumer *cons, struct nvme_controller *ctrlr) { struct nvme_namespace *ns; void *ctrlr_cookie; - int cmpset, ns_idx; + int ns_idx; /* * The consumer may register itself after the nvme devices @@ -308,16 +345,18 @@ if (!ctrlr->is_initialized) return; - cmpset = atomic_cmpset_32(&ctrlr->notification_sent, 0, 1); - - if (cmpset == 0) - return; + if (ctrlr->cons_cookie[cons->id] == NULL) { + if (cons->ctrlr_fn != NULL) { + ctrlr_cookie = (*cons->ctrlr_fn)(ctrlr); + } else { + ctrlr_cookie = NULL; + } + } else { + ctrlr_cookie = ctrlr->cons_cookie[cons->id]; + } - if (cons->ctrlr_fn != NULL) - ctrlr_cookie = (*cons->ctrlr_fn)(ctrlr); - else - ctrlr_cookie = NULL; ctrlr->cons_cookie[cons->id] = ctrlr_cookie; + if (ctrlr->is_failed) { if (cons->fail_fn != NULL) (*cons->fail_fn)(ctrlr_cookie); @@ -408,8 +447,8 @@ struct nvme_consumer * nvme_register_consumer(nvme_cons_ns_fn_t ns_fn, nvme_cons_ctrlr_fn_t ctrlr_fn, - nvme_cons_async_fn_t async_fn, - nvme_cons_fail_fn_t fail_fn) + nvme_cons_async_fn_t async_fn, + nvme_cons_fail_fn_t fail_fn, nvme_cons_rm_ns_fn_t rm_ns_fn) { int i; @@ -424,7 +463,7 @@ nvme_consumer[i].ctrlr_fn = ctrlr_fn; nvme_consumer[i].async_fn = async_fn; nvme_consumer[i].fail_fn = fail_fn; - + nvme_consumer[i].rm_ns_fn = rm_ns_fn; nvme_notify_new_consumer(&nvme_consumer[i]); return (&nvme_consumer[i]); } Index: sys/dev/nvme/nvme_ctrlr.c =================================================================== --- sys/dev/nvme/nvme_ctrlr.c +++ sys/dev/nvme/nvme_ctrlr.c @@ -850,6 +850,96 @@ return (0); } +struct ns_create_arg { + uint16_t nsid; + struct nvme_controller *ctrlr; +}; + +static void +nvme_ns_create(void *arg, int pending) +{ + struct nvme_controller *ctrlr; + struct nvme_sim_softc *sc; + struct ns_create_arg *nsarg = (struct ns_create_arg *)arg; + int nsid = nsarg->nsid; + + ctrlr = nsarg->ctrlr; + sc = ctrlr->sim_softc; + nvme_ns_construct(&ctrlr->ns[nsid], nsid, ctrlr); + nvme_notify_namespace(ctrlr, nsid, 1); /* new disk */ + free(arg, M_NVME); + return; +} + +static void nvme_ns_detach(void *arg, int pending) +{ + struct ns_create_arg *nsarg = arg; + struct nvme_controller *ctrlr = nsarg->ctrlr; + int nsid = nsarg->nsid; + + nvme_notify_namespace(ctrlr, nsid, 0); /* remove disk */ +} + +static void +nvme_ns_mgmt_done(void *arg, const struct nvme_completion *cpl) +{ + struct nvme_pt_command *pt = (struct nvme_pt_command *)arg; + struct nvme_controller *ctrlr = pt->ctrlr; + + bzero(&pt->cpl, sizeof(pt->cpl)); + pt->cpl.cdw0 = cpl->cdw0; + pt->cpl.status = cpl->status; + pt->cpl.status.p = 0; + + if (pt->cmd.cdw10 == 1 && cpl->status.sc == 0) { /* good delete */ + memset(&ctrlr->ns[pt->cmd.nsid], 0, sizeof(struct nvme_namespace)); + } + + mtx_lock(pt->driver_lock); + wakeup(pt); + mtx_unlock(pt->driver_lock); +} + + + +static void +nvme_ns_attachment_done(void *arg, const struct nvme_completion *cpl) +{ + struct nvme_pt_command *pt = arg; + struct ns_create_arg *ns_arg = NULL; + struct nvme_controller *ctrlr = pt->ctrlr; + + bzero(&pt->cpl, sizeof(pt->cpl)); + pt->cpl.cdw0 = cpl->cdw0; + pt->cpl.status = cpl->status; + pt->cpl.status.p = 0; + + if (cpl->status.sc == 0) { /* setup task entry */ + ns_arg = malloc(sizeof(struct ns_create_arg), M_NVME, M_ZERO | M_NOWAIT); + ns_arg->nsid = pt->cmd.nsid; + ns_arg->ctrlr = ctrlr; + memset(&ctrlr->ns_task, 0, sizeof (struct task)); + + ctrlr->ns_task.ta_context = ns_arg; + ctrlr->ns_task.ta_pending = 0; + + if (pt->cmd.cdw10 == 0) { /* good attach */ + ctrlr->ns_task.ta_func = nvme_ns_create; /* create + attach = usable create xxx */ + + } else if (pt->cmd.cdw10 == 1) { /* good detach */ + ctrlr->ns_task.ta_func = nvme_ns_detach;; + } + taskqueue_enqueue(ctrlr->taskqueue, &ctrlr->ns_task); + + } + + mtx_lock(pt->driver_lock); + wakeup(pt); + mtx_unlock(pt->driver_lock); + +} + + static void nvme_pt_done(void *arg, const struct nvme_completion *cpl) { @@ -912,12 +1002,27 @@ ret = EFAULT; goto err; } - req = nvme_allocate_request_vaddr(buf->b_data, pt->len, - nvme_pt_done, pt); - } else - req = nvme_allocate_request_vaddr(pt->buf, pt->len, - nvme_pt_done, pt); - } else + + if (is_admin_cmd && pt->cmd.opc == NVME_OPC_NAMESPACE_MANAGEMENT) { + pt->ctrlr = ctrlr; + req = nvme_allocate_request_vaddr(buf->b_data, pt->len, + nvme_ns_mgmt_done, pt); + } else if (is_admin_cmd && pt->cmd.opc == NVME_OPC_NAMESPACE_ATTACHMENT) { + pt->ctrlr = ctrlr; + req = nvme_allocate_request_vaddr(buf->b_data, pt->len, + nvme_ns_attachment_done, pt); + + } else if (pt->cmd.opc == NVME_OPC_NAMESPACE_ATTACHMENT) { + req = nvme_allocate_request_vaddr(buf->b_data, pt->len, + nvme_ns_attachment_done, pt); + } else { + req = nvme_allocate_request_vaddr(buf->b_data, pt->len, + nvme_pt_done, pt); + } + } else + req = nvme_allocate_request_vaddr(buf->b_data, pt->len, + nvme_pt_done, pt); + } else req = nvme_allocate_request_null(nvme_pt_done, pt); req->cmd.opc = pt->cmd.opc; Index: sys/dev/nvme/nvme_ns.c =================================================================== --- sys/dev/nvme/nvme_ns.c +++ sys/dev/nvme/nvme_ns.c @@ -508,6 +508,7 @@ DELAY(5); if (nvme_completion_is_error(&status.cpl)) { nvme_printf(ctrlr, "nvme_identify_namespace failed\n"); + ns->id = 0; return (ENXIO); } @@ -517,9 +518,10 @@ * standard says the entire id will be zeros, so this is a * cheap way to test for that. */ - if (ns->data.nsze == 0) + if (ns->data.nsze == 0) { + ns->id = 0; return (ENXIO); - + } /* * Note: format is a 0-based value, so > is appropriate here, * not >=. Index: sys/dev/nvme/nvme_private.h =================================================================== --- sys/dev/nvme/nvme_private.h +++ sys/dev/nvme/nvme_private.h @@ -319,6 +319,9 @@ uint32_t notification_sent; boolean_t is_failed; + void *sim_softc; + struct task ns_task; + STAILQ_HEAD(, nvme_request) fail_req; }; @@ -527,4 +530,6 @@ void nvme_ctrlr_intx_handler(void *arg); +void nvme_notify_namespace(struct nvme_controller *ctrlr, int nsid, int op); + #endif /* __NVME_PRIVATE_H__ */ Index: sys/dev/nvme/nvme_sim.c =================================================================== --- sys/dev/nvme/nvme_sim.c +++ sys/dev/nvme/nvme_sim.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include // Yes, this is wrong. #include @@ -169,7 +170,7 @@ cpi->hba_eng_cnt = 0; cpi->max_target = 0; cpi->max_lun = ctrlr->cdata.nn; - cpi->maxio = nvme_ns_get_max_io_xfer_size(ns); + cpi->maxio = ctrlr->max_xfer_size; cpi->initiator_id = 0; cpi->bus_id = cam_sim_bus(sim); cpi->base_transfer_speed = 4000000; /* 4 GB/s 4 lanes pcie 3 */ @@ -181,7 +182,14 @@ cpi->transport_version = 1; /* XXX Get PCIe spec ? */ cpi->protocol = PROTO_NVME; cpi->protocol_version = NVME_REV_1; /* Groks all 1.x NVMe cards */ - cpi->xport_specific.nvme.nsid = ns->id; + /* it's only the first base namespace that suffers from + * a path inq before it's created + */ + if (ns) { + cpi->xport_specific.nvme.nsid = ns->id; + } else { + cpi->xport_specific.nvme.nsid = 1; + } cpi->ccb_h.status = CAM_REQ_CMP; break; } @@ -248,12 +256,14 @@ int max_trans; int unit; struct nvme_sim_softc *sc = NULL; + int err; max_trans = 256;/* XXX not so simple -- must match queues */ + // max_trans = ctrlr->num_io_queues; /* correct here? */ unit = device_get_unit(ctrlr->dev); devq = cam_simq_alloc(max_trans); if (devq == NULL) - return NULL; + return (NULL); sc = malloc(sizeof(*sc), M_NVME, M_ZERO | M_WAITOK); @@ -265,9 +275,14 @@ printf("Failed to allocate a sim\n"); cam_simq_free(devq); free(sc, M_NVME); - return NULL; + return (NULL); } - + err = xpt_bus_register(sc->s_sim, ctrlr->dev, 0); + if (err != CAM_SUCCESS) { + nvme_printf(ctrlr,"new ctrlr: failed xpt_bus_register : %d\n", + err); + } + ctrlr->sim_softc = sc; return sc; } @@ -296,25 +311,9 @@ { struct nvme_sim_softc *sc = sc_arg; struct nvme_controller *ctrlr = sc->s_ctrlr; - int i; sc->s_ns = ns; - - /* - * XXX this is creating one bus per ns, but it should be one - * XXX target per controller, and one LUN per namespace. - * XXX Current drives only support one NS, so there's time - * XXX to fix it later when new drives arrive. - * - * XXX I'm pretty sure the xpt_bus_register() call below is - * XXX like super lame and it really belongs in the sim_new_ctrlr - * XXX callback. Then the create_path below would be pretty close - * XXX to being right. Except we should be per-ns not per-ctrlr - * XXX data. - */ - mtx_lock(&ctrlr->lock); -/* Create bus */ /* * XXX do I need to lock ctrlr->lock ? @@ -324,36 +323,22 @@ * time, and nothing is in parallel. */ - i = 0; - if (xpt_bus_register(sc->s_sim, ctrlr->dev, 0) != CAM_SUCCESS) - goto error; - i++; if (xpt_create_path(&sc->s_path, /*periph*/NULL, cam_sim_path(sc->s_sim), - 1, ns->id) != CAM_REQ_CMP) - goto error; - i++; + 1, ns->id) != CAM_REQ_CMP) { + mtx_unlock(&ctrlr->lock); + cam_sim_free(sc->s_sim, /*free_devq*/TRUE); + return (NULL); + } sc->s_path->device->nvme_data = nvme_ns_get_data(ns); sc->s_path->device->nvme_cdata = nvme_ctrlr_get_data(ns->ctrlr); /* Scan bus */ - nvme_sim_rescan_target(ctrlr, sc->s_path); + nvme_sim_rescan_target(ctrlr, sc->s_path); mtx_unlock(&ctrlr->lock); - return ns; - -error: - switch (i) { - case 2: - xpt_free_path(sc->s_path); - case 1: - xpt_bus_deregister(cam_sim_path(sc->s_sim)); - case 0: - cam_sim_free(sc->s_sim, /*free_devq*/TRUE); - } - mtx_unlock(&ctrlr->lock); - return NULL; + return (ns); } static void @@ -361,6 +346,15 @@ { /* XXX cleanup XXX */ } +static void * +nvme_sim_rm_namespace(int nsid, void *ctrlr_arg) +{ + struct nvme_sim_softc *sc = ctrlr_arg; + + xpt_async(AC_LOST_DEVICE, sc->s_path, NULL); + xpt_free_path(sc->s_path); + return (NULL); +} struct nvme_consumer *consumer_cookie; @@ -369,7 +363,10 @@ { consumer_cookie = nvme_register_consumer(nvme_sim_new_ns, - nvme_sim_new_controller, NULL, nvme_sim_controller_fail); + nvme_sim_new_controller, + NULL, + nvme_sim_controller_fail, + nvme_sim_rm_namespace); } SYSINIT(nvme_sim_register, SI_SUB_DRIVERS, SI_ORDER_ANY,