1788c47fcSHans Rosenfeld /*
2788c47fcSHans Rosenfeld * This file and its contents are supplied under the terms of the
3788c47fcSHans Rosenfeld * Common Development and Distribution License ("CDDL"), version 1.0.
4788c47fcSHans Rosenfeld * You may only use this file in accordance with the terms of version
5788c47fcSHans Rosenfeld * 1.0 of the CDDL.
6788c47fcSHans Rosenfeld *
7788c47fcSHans Rosenfeld * A full copy of the text of the CDDL should have accompanied this
8788c47fcSHans Rosenfeld * source. A copy of the CDDL is also available via the Internet at
9788c47fcSHans Rosenfeld * http://www.illumos.org/license/CDDL.
10788c47fcSHans Rosenfeld */
11788c47fcSHans Rosenfeld
12788c47fcSHans Rosenfeld /*
13071aaa7bSHans Rosenfeld * Copyright 2016 Nexenta Systems, Inc. All rights reserved.
140fe6d382SPete Shephard * Copyright 2016 Tegile Systems, Inc. All rights reserved.
15babc8e1dSYouzhong Yang * Copyright (c) 2016 The MathWorks, Inc. All rights reserved.
16*c8ded1b3SRobert Mustacchi * Copyright 2018 Joyent, Inc.
17788c47fcSHans Rosenfeld */
18788c47fcSHans Rosenfeld
19788c47fcSHans Rosenfeld /*
20788c47fcSHans Rosenfeld * blkdev driver for NVMe compliant storage devices
21788c47fcSHans Rosenfeld *
22892848dfSRobert Mustacchi * This driver was written to conform to version 1.2.1 of the NVMe
23892848dfSRobert Mustacchi * specification. It may work with newer versions, but that is completely
24892848dfSRobert Mustacchi * untested and disabled by default.
25788c47fcSHans Rosenfeld *
26788c47fcSHans Rosenfeld * The driver has only been tested on x86 systems and will not work on big-
27788c47fcSHans Rosenfeld * endian systems without changes to the code accessing registers and data
28788c47fcSHans Rosenfeld * structures used by the hardware.
29788c47fcSHans Rosenfeld *
30788c47fcSHans Rosenfeld *
31788c47fcSHans Rosenfeld * Interrupt Usage:
32788c47fcSHans Rosenfeld *
3320a5804bSHans Rosenfeld * The driver will use a single interrupt while configuring the device as the
3420a5804bSHans Rosenfeld * specification requires, but contrary to the specification it will try to use
3520a5804bSHans Rosenfeld * a single-message MSI(-X) or FIXED interrupt. Later in the attach process it
3620a5804bSHans Rosenfeld * will switch to multiple-message MSI(-X) if supported. The driver wants to
3720a5804bSHans Rosenfeld * have one interrupt vector per CPU, but it will work correctly if less are
3820a5804bSHans Rosenfeld * available. Interrupts can be shared by queues, the interrupt handler will
3920a5804bSHans Rosenfeld * iterate through the I/O queue array by steps of n_intr_cnt. Usually only
4020a5804bSHans Rosenfeld * the admin queue will share an interrupt with one I/O queue. The interrupt
4120a5804bSHans Rosenfeld * handler will retrieve completed commands from all queues sharing an interrupt
4220a5804bSHans Rosenfeld * vector and will post them to a taskq for completion processing.
43788c47fcSHans Rosenfeld *
44788c47fcSHans Rosenfeld *
45788c47fcSHans Rosenfeld * Command Processing:
46788c47fcSHans Rosenfeld *
47ba636d1cSHans Rosenfeld * NVMe devices can have up to 65535 I/O queue pairs, with each queue holding up
48788c47fcSHans Rosenfeld * to 65536 I/O commands. The driver will configure one I/O queue pair per
49788c47fcSHans Rosenfeld * available interrupt vector, with the queue length usually much smaller than
50788c47fcSHans Rosenfeld * the maximum of 65536. If the hardware doesn't provide enough queues, fewer
51788c47fcSHans Rosenfeld * interrupt vectors will be used.
52788c47fcSHans Rosenfeld *
53788c47fcSHans Rosenfeld * Additionally the hardware provides a single special admin queue pair that can
54788c47fcSHans Rosenfeld * hold up to 4096 admin commands.
55788c47fcSHans Rosenfeld *
56788c47fcSHans Rosenfeld * From the hardware perspective both queues of a queue pair are independent,
57788c47fcSHans Rosenfeld * but they share some driver state: the command array (holding pointers to
58788c47fcSHans Rosenfeld * commands currently being processed by the hardware) and the active command
5969a33c23SHans Rosenfeld * counter. Access to a queue pair and the shared state is protected by
6069a33c23SHans Rosenfeld * nq_mutex.
61788c47fcSHans Rosenfeld *
62788c47fcSHans Rosenfeld * When a command is submitted to a queue pair the active command counter is
63788c47fcSHans Rosenfeld * incremented and a pointer to the command is stored in the command array. The
64788c47fcSHans Rosenfeld * array index is used as command identifier (CID) in the submission queue
65788c47fcSHans Rosenfeld * entry. Some commands may take a very long time to complete, and if the queue
66788c47fcSHans Rosenfeld * wraps around in that time a submission may find the next array slot to still
67788c47fcSHans Rosenfeld * be used by a long-running command. In this case the array is sequentially
68788c47fcSHans Rosenfeld * searched for the next free slot. The length of the command array is the same
69ba636d1cSHans Rosenfeld * as the configured queue length. Queue overrun is prevented by the semaphore,
70ba636d1cSHans Rosenfeld * so a command submission may block if the queue is full.
71788c47fcSHans Rosenfeld *
72788c47fcSHans Rosenfeld *
732ca49728SHans Rosenfeld * Polled I/O Support:
742ca49728SHans Rosenfeld *
752ca49728SHans Rosenfeld * For kernel core dump support the driver can do polled I/O. As interrupts are
762ca49728SHans Rosenfeld * turned off while dumping the driver will just submit a command in the regular
772ca49728SHans Rosenfeld * way, and then repeatedly attempt a command retrieval until it gets the
782ca49728SHans Rosenfeld * command back.
792ca49728SHans Rosenfeld *
802ca49728SHans Rosenfeld *
81788c47fcSHans Rosenfeld * Namespace Support:
82788c47fcSHans Rosenfeld *
83788c47fcSHans Rosenfeld * NVMe devices can have multiple namespaces, each being a independent data
84788c47fcSHans Rosenfeld * store. The driver supports multiple namespaces and creates a blkdev interface
85788c47fcSHans Rosenfeld * for each namespace found. Namespaces can have various attributes to support
86db9e8950SHans Rosenfeld * thin provisioning and protection information. This driver does not support
87db9e8950SHans Rosenfeld * any of this and ignores namespaces that have these attributes.
88788c47fcSHans Rosenfeld *
89265d85e9SHans Rosenfeld * As of NVMe 1.1 namespaces can have an 64bit Extended Unique Identifier
90265d85e9SHans Rosenfeld * (EUI64). This driver uses the EUI64 if present to generate the devid and
91265d85e9SHans Rosenfeld * passes it to blkdev to use it in the device node names. As this is currently
92265d85e9SHans Rosenfeld * untested namespaces with EUI64 are ignored by default.
93265d85e9SHans Rosenfeld *
94032cbfebSHans Rosenfeld * We currently support only (2 << NVME_MINOR_INST_SHIFT) - 2 namespaces in a
95032cbfebSHans Rosenfeld * single controller. This is an artificial limit imposed by the driver to be
96032cbfebSHans Rosenfeld * able to address a reasonable number of controllers and namespaces using a
97032cbfebSHans Rosenfeld * 32bit minor node number.
98032cbfebSHans Rosenfeld *
99032cbfebSHans Rosenfeld *
100032cbfebSHans Rosenfeld * Minor nodes:
101032cbfebSHans Rosenfeld *
102032cbfebSHans Rosenfeld * For each NVMe device the driver exposes one minor node for the controller and
103032cbfebSHans Rosenfeld * one minor node for each namespace. The only operations supported by those
104032cbfebSHans Rosenfeld * minor nodes are open(9E), close(9E), and ioctl(9E). This serves as the
105032cbfebSHans Rosenfeld * interface for the nvmeadm(1M) utility.
106032cbfebSHans Rosenfeld *
107788c47fcSHans Rosenfeld *
108788c47fcSHans Rosenfeld * Blkdev Interface:
109788c47fcSHans Rosenfeld *
110788c47fcSHans Rosenfeld * This driver uses blkdev to do all the heavy lifting involved with presenting
111788c47fcSHans Rosenfeld * a disk device to the system. As a result, the processing of I/O requests is
112788c47fcSHans Rosenfeld * relatively simple as blkdev takes care of partitioning, boundary checks, DMA
113788c47fcSHans Rosenfeld * setup, and splitting of transfers into manageable chunks.
114788c47fcSHans Rosenfeld *
115788c47fcSHans Rosenfeld * I/O requests coming in from blkdev are turned into NVM commands and posted to
116788c47fcSHans Rosenfeld * an I/O queue. The queue is selected by taking the CPU id modulo the number of
117788c47fcSHans Rosenfeld * queues. There is currently no timeout handling of I/O commands.
118788c47fcSHans Rosenfeld *
119788c47fcSHans Rosenfeld * Blkdev also supports querying device/media information and generating a
120788c47fcSHans Rosenfeld * devid. The driver reports the best block size as determined by the namespace
121788c47fcSHans Rosenfeld * format back to blkdev as physical block size to support partition and block
122265d85e9SHans Rosenfeld * alignment. The devid is either based on the namespace EUI64, if present, or
123265d85e9SHans Rosenfeld * composed using the device vendor ID, model number, serial number, and the
124265d85e9SHans Rosenfeld * namespace ID.
125788c47fcSHans Rosenfeld *
126788c47fcSHans Rosenfeld *
127788c47fcSHans Rosenfeld * Error Handling:
128788c47fcSHans Rosenfeld *
129788c47fcSHans Rosenfeld * Error handling is currently limited to detecting fatal hardware errors,
130788c47fcSHans Rosenfeld * either by asynchronous events, or synchronously through command status or
131788c47fcSHans Rosenfeld * admin command timeouts. In case of severe errors the device is fenced off,
132788c47fcSHans Rosenfeld * all further requests will return EIO. FMA is then called to fault the device.
133788c47fcSHans Rosenfeld *
134788c47fcSHans Rosenfeld * The hardware has a limit for outstanding asynchronous event requests. Before
135788c47fcSHans Rosenfeld * this limit is known the driver assumes it is at least 1 and posts a single
136788c47fcSHans Rosenfeld * asynchronous request. Later when the limit is known more asynchronous event
137788c47fcSHans Rosenfeld * requests are posted to allow quicker reception of error information. When an
138788c47fcSHans Rosenfeld * asynchronous event is posted by the hardware the driver will parse the error
139788c47fcSHans Rosenfeld * status fields and log information or fault the device, depending on the
140788c47fcSHans Rosenfeld * severity of the asynchronous event. The asynchronous event request is then
141788c47fcSHans Rosenfeld * reused and posted to the admin queue again.
142788c47fcSHans Rosenfeld *
143788c47fcSHans Rosenfeld * On command completion the command status is checked for errors. In case of
144788c47fcSHans Rosenfeld * errors indicating a driver bug the driver panics. Almost all other error
145788c47fcSHans Rosenfeld * status values just cause EIO to be returned.
146788c47fcSHans Rosenfeld *
147788c47fcSHans Rosenfeld * Command timeouts are currently detected for all admin commands except
148788c47fcSHans Rosenfeld * asynchronous event requests. If a command times out and the hardware appears
14969a33c23SHans Rosenfeld * to be healthy the driver attempts to abort the command. The original command
15069a33c23SHans Rosenfeld * timeout is also applied to the abort command. If the abort times out too the
151788c47fcSHans Rosenfeld * driver assumes the device to be dead, fences it off, and calls FMA to retire
15269a33c23SHans Rosenfeld * it. In all other cases the aborted command should return immediately with a
15369a33c23SHans Rosenfeld * status indicating it was aborted, and the driver will wait indefinitely for
15469a33c23SHans Rosenfeld * that to happen. No timeout handling of normal I/O commands is presently done.
155788c47fcSHans Rosenfeld *
15669a33c23SHans Rosenfeld * Any command that times out due to the controller dropping dead will be put on
15769a33c23SHans Rosenfeld * nvme_lost_cmds list if it references DMA memory. This will prevent the DMA
15869a33c23SHans Rosenfeld * memory being reused by the system and later be written to by a "dead" NVMe
15969a33c23SHans Rosenfeld * controller.
16069a33c23SHans Rosenfeld *
16169a33c23SHans Rosenfeld *
16269a33c23SHans Rosenfeld * Locking:
16369a33c23SHans Rosenfeld *
16469a33c23SHans Rosenfeld * Each queue pair has its own nq_mutex, which must be held when accessing the
16569a33c23SHans Rosenfeld * associated queue registers or the shared state of the queue pair. Callers of
16669a33c23SHans Rosenfeld * nvme_unqueue_cmd() must make sure that nq_mutex is held, while
16769a33c23SHans Rosenfeld * nvme_submit_{admin,io}_cmd() and nvme_retrieve_cmd() take care of this
16869a33c23SHans Rosenfeld * themselves.
16969a33c23SHans Rosenfeld *
17069a33c23SHans Rosenfeld * Each command also has its own nc_mutex, which is associated with the
17169a33c23SHans Rosenfeld * condition variable nc_cv. It is only used on admin commands which are run
17269a33c23SHans Rosenfeld * synchronously. In that case it must be held across calls to
17369a33c23SHans Rosenfeld * nvme_submit_{admin,io}_cmd() and nvme_wait_cmd(), which is taken care of by
17469a33c23SHans Rosenfeld * nvme_admin_cmd(). It must also be held whenever the completion state of the
17569a33c23SHans Rosenfeld * command is changed or while a admin command timeout is handled.
17669a33c23SHans Rosenfeld *
17769a33c23SHans Rosenfeld * If both nc_mutex and nq_mutex must be held, nc_mutex must be acquired first.
17869a33c23SHans Rosenfeld * More than one nc_mutex may only be held when aborting commands. In this case,
17969a33c23SHans Rosenfeld * the nc_mutex of the command to be aborted must be held across the call to
18069a33c23SHans Rosenfeld * nvme_abort_cmd() to prevent the command from completing while the abort is in
18169a33c23SHans Rosenfeld * progress.
18269a33c23SHans Rosenfeld *
18369a33c23SHans Rosenfeld * Each minor node has its own nm_mutex, which protects the open count nm_ocnt
18469a33c23SHans Rosenfeld * and exclusive-open flag nm_oexcl.
185788c47fcSHans Rosenfeld *
186788c47fcSHans Rosenfeld *
187788c47fcSHans Rosenfeld * Quiesce / Fast Reboot:
188788c47fcSHans Rosenfeld *
189788c47fcSHans Rosenfeld * The driver currently does not support fast reboot. A quiesce(9E) entry point
190788c47fcSHans Rosenfeld * is still provided which is used to send a shutdown notification to the
191788c47fcSHans Rosenfeld * device.
192788c47fcSHans Rosenfeld *
193788c47fcSHans Rosenfeld *
194788c47fcSHans Rosenfeld * Driver Configuration:
195788c47fcSHans Rosenfeld *
196788c47fcSHans Rosenfeld * The following driver properties can be changed to control some aspects of the
197788c47fcSHans Rosenfeld * drivers operation:
198788c47fcSHans Rosenfeld * - strict-version: can be set to 0 to allow devices conforming to newer
199*c8ded1b3SRobert Mustacchi * major versions to be used
200788c47fcSHans Rosenfeld * - ignore-unknown-vendor-status: can be set to 1 to not handle any vendor
201788c47fcSHans Rosenfeld * specific command status as a fatal error leading device faulting
202788c47fcSHans Rosenfeld * - admin-queue-len: the maximum length of the admin queue (16-4096)
203788c47fcSHans Rosenfeld * - io-queue-len: the maximum length of the I/O queues (16-65536)
204788c47fcSHans Rosenfeld * - async-event-limit: the maximum number of asynchronous event requests to be
205788c47fcSHans Rosenfeld * posted by the driver
2065f836503SHans Rosenfeld * - volatile-write-cache-enable: can be set to 0 to disable the volatile write
2075f836503SHans Rosenfeld * cache
2089974ca0cSHans Rosenfeld * - min-phys-block-size: the minimum physical block size to report to blkdev,
2099974ca0cSHans Rosenfeld * which is among other things the basis for ZFS vdev ashift
210788c47fcSHans Rosenfeld *
211788c47fcSHans Rosenfeld *
212788c47fcSHans Rosenfeld * TODO:
213788c47fcSHans Rosenfeld * - figure out sane default for I/O queue depth reported to blkdev
214788c47fcSHans Rosenfeld * - FMA handling of media errors
215788c47fcSHans Rosenfeld * - support for devices supporting very large I/O requests using chained PRPs
216788c47fcSHans Rosenfeld * - support for configuring hardware parameters like interrupt coalescing
217788c47fcSHans Rosenfeld * - support for media formatting and hard partitioning into namespaces
218788c47fcSHans Rosenfeld * - support for big-endian systems
219788c47fcSHans Rosenfeld * - support for fast reboot
220265d85e9SHans Rosenfeld * - support for firmware updates
221265d85e9SHans Rosenfeld * - support for NVMe Subsystem Reset (1.1)
222265d85e9SHans Rosenfeld * - support for Scatter/Gather lists (1.1)
223265d85e9SHans Rosenfeld * - support for Reservations (1.1)
224265d85e9SHans Rosenfeld * - support for power management
225788c47fcSHans Rosenfeld */
226788c47fcSHans Rosenfeld
227788c47fcSHans Rosenfeld #include <sys/byteorder.h>
228788c47fcSHans Rosenfeld #ifdef _BIG_ENDIAN
229788c47fcSHans Rosenfeld #error nvme driver needs porting for big-endian platforms
230788c47fcSHans Rosenfeld #endif
231788c47fcSHans Rosenfeld
232788c47fcSHans Rosenfeld #include <sys/modctl.h>
233788c47fcSHans Rosenfeld #include <sys/conf.h>
234788c47fcSHans Rosenfeld #include <sys/devops.h>
235788c47fcSHans Rosenfeld #include <sys/ddi.h>
236788c47fcSHans Rosenfeld #include <sys/sunddi.h>
237032cbfebSHans Rosenfeld #include <sys/sunndi.h>
238788c47fcSHans Rosenfeld #include <sys/bitmap.h>
239788c47fcSHans Rosenfeld #include <sys/sysmacros.h>
240788c47fcSHans Rosenfeld #include <sys/param.h>
241788c47fcSHans Rosenfeld #include <sys/varargs.h>
242788c47fcSHans Rosenfeld #include <sys/cpuvar.h>
243788c47fcSHans Rosenfeld #include <sys/disp.h>
244788c47fcSHans Rosenfeld #include <sys/blkdev.h>
245788c47fcSHans Rosenfeld #include <sys/atomic.h>
246788c47fcSHans Rosenfeld #include <sys/archsystm.h>
247f0e4d8f0SHans Rosenfeld #include <sys/sata/sata_hba.h>
248032cbfebSHans Rosenfeld #include <sys/stat.h>
249032cbfebSHans Rosenfeld #include <sys/policy.h>
25069a33c23SHans Rosenfeld #include <sys/list.h>
251032cbfebSHans Rosenfeld
252032cbfebSHans Rosenfeld #include <sys/nvme.h>
253788c47fcSHans Rosenfeld
2541c08585fSHans Rosenfeld #ifdef __x86
2551c08585fSHans Rosenfeld #include <sys/x86_archext.h>
2561c08585fSHans Rosenfeld #endif
2571c08585fSHans Rosenfeld
258788c47fcSHans Rosenfeld #include "nvme_reg.h"
259788c47fcSHans Rosenfeld #include "nvme_var.h"
260788c47fcSHans Rosenfeld
261*c8ded1b3SRobert Mustacchi /*
262*c8ded1b3SRobert Mustacchi * Assertions to make sure that we've properly captured various aspects of the
263*c8ded1b3SRobert Mustacchi * packed structures and haven't broken them during updates.
264*c8ded1b3SRobert Mustacchi */
265*c8ded1b3SRobert Mustacchi CTASSERT(sizeof (nvme_identify_ctrl_t) == 0x1000);
266*c8ded1b3SRobert Mustacchi CTASSERT(offsetof(nvme_identify_ctrl_t, id_oacs) == 256);
267*c8ded1b3SRobert Mustacchi CTASSERT(offsetof(nvme_identify_ctrl_t, id_sqes) == 512);
268*c8ded1b3SRobert Mustacchi CTASSERT(offsetof(nvme_identify_ctrl_t, id_subnqn) == 768);
269*c8ded1b3SRobert Mustacchi CTASSERT(offsetof(nvme_identify_ctrl_t, id_nvmof) == 1792);
270*c8ded1b3SRobert Mustacchi CTASSERT(offsetof(nvme_identify_ctrl_t, id_psd) == 2048);
271*c8ded1b3SRobert Mustacchi CTASSERT(offsetof(nvme_identify_ctrl_t, id_vs) == 3072);
272*c8ded1b3SRobert Mustacchi
273*c8ded1b3SRobert Mustacchi CTASSERT(sizeof (nvme_identify_nsid_t) == 0x1000);
274*c8ded1b3SRobert Mustacchi CTASSERT(offsetof(nvme_identify_nsid_t, id_fpi) == 32);
275*c8ded1b3SRobert Mustacchi CTASSERT(offsetof(nvme_identify_nsid_t, id_nguid) == 104);
276*c8ded1b3SRobert Mustacchi CTASSERT(offsetof(nvme_identify_nsid_t, id_lbaf) == 128);
277*c8ded1b3SRobert Mustacchi CTASSERT(offsetof(nvme_identify_nsid_t, id_vs) == 384);
278*c8ded1b3SRobert Mustacchi
279*c8ded1b3SRobert Mustacchi CTASSERT(sizeof (nvme_identify_primary_caps_t) == 0x1000);
280*c8ded1b3SRobert Mustacchi CTASSERT(offsetof(nvme_identify_primary_caps_t, nipc_vqfrt) == 32);
281*c8ded1b3SRobert Mustacchi CTASSERT(offsetof(nvme_identify_primary_caps_t, nipc_vifrt) == 64);
282*c8ded1b3SRobert Mustacchi
283788c47fcSHans Rosenfeld
284788c47fcSHans Rosenfeld /* NVMe spec version supported */
285788c47fcSHans Rosenfeld static const int nvme_version_major = 1;
286788c47fcSHans Rosenfeld
2876f37ecd5SHans Rosenfeld /* tunable for admin command timeout in seconds, default is 1s */
288032cbfebSHans Rosenfeld int nvme_admin_cmd_timeout = 1;
289032cbfebSHans Rosenfeld
290032cbfebSHans Rosenfeld /* tunable for FORMAT NVM command timeout in seconds, default is 600s */
291032cbfebSHans Rosenfeld int nvme_format_cmd_timeout = 600;
2926f37ecd5SHans Rosenfeld
293788c47fcSHans Rosenfeld static int nvme_attach(dev_info_t *, ddi_attach_cmd_t);
294788c47fcSHans Rosenfeld static int nvme_detach(dev_info_t *, ddi_detach_cmd_t);
295788c47fcSHans Rosenfeld static int nvme_quiesce(dev_info_t *);
296788c47fcSHans Rosenfeld static int nvme_fm_errcb(dev_info_t *, ddi_fm_error_t *, const void *);
297788c47fcSHans Rosenfeld static int nvme_setup_interrupts(nvme_t *, int, int);
298788c47fcSHans Rosenfeld static void nvme_release_interrupts(nvme_t *);
299788c47fcSHans Rosenfeld static uint_t nvme_intr(caddr_t, caddr_t);
300788c47fcSHans Rosenfeld
301788c47fcSHans Rosenfeld static void nvme_shutdown(nvme_t *, int, boolean_t);
302788c47fcSHans Rosenfeld static boolean_t nvme_reset(nvme_t *, boolean_t);
303788c47fcSHans Rosenfeld static int nvme_init(nvme_t *);
304788c47fcSHans Rosenfeld static nvme_cmd_t *nvme_alloc_cmd(nvme_t *, int);
305788c47fcSHans Rosenfeld static void nvme_free_cmd(nvme_cmd_t *);
306788c47fcSHans Rosenfeld static nvme_cmd_t *nvme_create_nvm_cmd(nvme_namespace_t *, uint8_t,
307788c47fcSHans Rosenfeld bd_xfer_t *);
30869a33c23SHans Rosenfeld static void nvme_admin_cmd(nvme_cmd_t *, int);
309ba636d1cSHans Rosenfeld static void nvme_submit_admin_cmd(nvme_qpair_t *, nvme_cmd_t *);
310ba636d1cSHans Rosenfeld static int nvme_submit_io_cmd(nvme_qpair_t *, nvme_cmd_t *);
311ba636d1cSHans Rosenfeld static void nvme_submit_cmd_common(nvme_qpair_t *, nvme_cmd_t *);
31269a33c23SHans Rosenfeld static nvme_cmd_t *nvme_unqueue_cmd(nvme_t *, nvme_qpair_t *, int);
313788c47fcSHans Rosenfeld static nvme_cmd_t *nvme_retrieve_cmd(nvme_t *, nvme_qpair_t *);
31469a33c23SHans Rosenfeld static void nvme_wait_cmd(nvme_cmd_t *, uint_t);
315788c47fcSHans Rosenfeld static void nvme_wakeup_cmd(void *);
316788c47fcSHans Rosenfeld static void nvme_async_event_task(void *);
317788c47fcSHans Rosenfeld
318788c47fcSHans Rosenfeld static int nvme_check_unknown_cmd_status(nvme_cmd_t *);
319788c47fcSHans Rosenfeld static int nvme_check_vendor_cmd_status(nvme_cmd_t *);
320788c47fcSHans Rosenfeld static int nvme_check_integrity_cmd_status(nvme_cmd_t *);
321788c47fcSHans Rosenfeld static int nvme_check_specific_cmd_status(nvme_cmd_t *);
322788c47fcSHans Rosenfeld static int nvme_check_generic_cmd_status(nvme_cmd_t *);
323788c47fcSHans Rosenfeld static inline int nvme_check_cmd_status(nvme_cmd_t *);
324788c47fcSHans Rosenfeld
32569a33c23SHans Rosenfeld static int nvme_abort_cmd(nvme_cmd_t *, uint_t);
326ba636d1cSHans Rosenfeld static void nvme_async_event(nvme_t *);
327032cbfebSHans Rosenfeld static int nvme_format_nvm(nvme_t *, uint32_t, uint8_t, boolean_t, uint8_t,
328032cbfebSHans Rosenfeld boolean_t, uint8_t);
329032cbfebSHans Rosenfeld static int nvme_get_logpage(nvme_t *, void **, size_t *, uint8_t, ...);
33069a33c23SHans Rosenfeld static int nvme_identify(nvme_t *, uint32_t, void **);
33169a33c23SHans Rosenfeld static int nvme_set_features(nvme_t *, uint32_t, uint8_t, uint32_t,
3325f836503SHans Rosenfeld uint32_t *);
33369a33c23SHans Rosenfeld static int nvme_get_features(nvme_t *, uint32_t, uint8_t, uint32_t *,
334032cbfebSHans Rosenfeld void **, size_t *);
33569a33c23SHans Rosenfeld static int nvme_write_cache_set(nvme_t *, boolean_t);
33669a33c23SHans Rosenfeld static int nvme_set_nqueues(nvme_t *, uint16_t *);
337788c47fcSHans Rosenfeld
338788c47fcSHans Rosenfeld static void nvme_free_dma(nvme_dma_t *);
339788c47fcSHans Rosenfeld static int nvme_zalloc_dma(nvme_t *, size_t, uint_t, ddi_dma_attr_t *,
340788c47fcSHans Rosenfeld nvme_dma_t **);
341788c47fcSHans Rosenfeld static int nvme_zalloc_queue_dma(nvme_t *, uint32_t, uint16_t, uint_t,
342788c47fcSHans Rosenfeld nvme_dma_t **);
343788c47fcSHans Rosenfeld static void nvme_free_qpair(nvme_qpair_t *);
344788c47fcSHans Rosenfeld static int nvme_alloc_qpair(nvme_t *, uint32_t, nvme_qpair_t **, int);
345788c47fcSHans Rosenfeld static int nvme_create_io_qpair(nvme_t *, nvme_qpair_t *, uint16_t);
346788c47fcSHans Rosenfeld
347788c47fcSHans Rosenfeld static inline void nvme_put64(nvme_t *, uintptr_t, uint64_t);
348788c47fcSHans Rosenfeld static inline void nvme_put32(nvme_t *, uintptr_t, uint32_t);
349788c47fcSHans Rosenfeld static inline uint64_t nvme_get64(nvme_t *, uintptr_t);
350788c47fcSHans Rosenfeld static inline uint32_t nvme_get32(nvme_t *, uintptr_t);
351788c47fcSHans Rosenfeld
352788c47fcSHans Rosenfeld static boolean_t nvme_check_regs_hdl(nvme_t *);
353788c47fcSHans Rosenfeld static boolean_t nvme_check_dma_hdl(nvme_dma_t *);
354788c47fcSHans Rosenfeld
355788c47fcSHans Rosenfeld static int nvme_fill_prp(nvme_cmd_t *, bd_xfer_t *);
356788c47fcSHans Rosenfeld
357788c47fcSHans Rosenfeld static void nvme_bd_xfer_done(void *);
358788c47fcSHans Rosenfeld static void nvme_bd_driveinfo(void *, bd_drive_t *);
359788c47fcSHans Rosenfeld static int nvme_bd_mediainfo(void *, bd_media_t *);
360788c47fcSHans Rosenfeld static int nvme_bd_cmd(nvme_namespace_t *, bd_xfer_t *, uint8_t);
361788c47fcSHans Rosenfeld static int nvme_bd_read(void *, bd_xfer_t *);
362788c47fcSHans Rosenfeld static int nvme_bd_write(void *, bd_xfer_t *);
363788c47fcSHans Rosenfeld static int nvme_bd_sync(void *, bd_xfer_t *);
364788c47fcSHans Rosenfeld static int nvme_bd_devid(void *, dev_info_t *, ddi_devid_t *);
365788c47fcSHans Rosenfeld
366b4eda73dSYouzhong Yang static int nvme_prp_dma_constructor(void *, void *, int);
367b4eda73dSYouzhong Yang static void nvme_prp_dma_destructor(void *, void *);
368b4eda73dSYouzhong Yang
369788c47fcSHans Rosenfeld static void nvme_prepare_devid(nvme_t *, uint32_t);
370788c47fcSHans Rosenfeld
371032cbfebSHans Rosenfeld static int nvme_open(dev_t *, int, int, cred_t *);
372032cbfebSHans Rosenfeld static int nvme_close(dev_t, int, int, cred_t *);
373032cbfebSHans Rosenfeld static int nvme_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
374032cbfebSHans Rosenfeld
37597a4f861SHans Rosenfeld #define NVME_MINOR_INST_SHIFT 9
376032cbfebSHans Rosenfeld #define NVME_MINOR(inst, nsid) (((inst) << NVME_MINOR_INST_SHIFT) | (nsid))
377032cbfebSHans Rosenfeld #define NVME_MINOR_INST(minor) ((minor) >> NVME_MINOR_INST_SHIFT)
378032cbfebSHans Rosenfeld #define NVME_MINOR_NSID(minor) ((minor) & ((1 << NVME_MINOR_INST_SHIFT) - 1))
379032cbfebSHans Rosenfeld #define NVME_MINOR_MAX (NVME_MINOR(1, 0) - 2)
380032cbfebSHans Rosenfeld
381788c47fcSHans Rosenfeld static void *nvme_state;
382788c47fcSHans Rosenfeld static kmem_cache_t *nvme_cmd_cache;
383788c47fcSHans Rosenfeld
384788c47fcSHans Rosenfeld /*
385788c47fcSHans Rosenfeld * DMA attributes for queue DMA memory
386788c47fcSHans Rosenfeld *
387788c47fcSHans Rosenfeld * Queue DMA memory must be page aligned. The maximum length of a queue is
388788c47fcSHans Rosenfeld * 65536 entries, and an entry can be 64 bytes long.
389788c47fcSHans Rosenfeld */
390788c47fcSHans Rosenfeld static ddi_dma_attr_t nvme_queue_dma_attr = {
391788c47fcSHans Rosenfeld .dma_attr_version = DMA_ATTR_V0,
392788c47fcSHans Rosenfeld .dma_attr_addr_lo = 0,
393788c47fcSHans Rosenfeld .dma_attr_addr_hi = 0xffffffffffffffffULL,
39469561f77SYouzhong Yang .dma_attr_count_max = (UINT16_MAX + 1) * sizeof (nvme_sqe_t) - 1,
395788c47fcSHans Rosenfeld .dma_attr_align = 0x1000,
396788c47fcSHans Rosenfeld .dma_attr_burstsizes = 0x7ff,
397788c47fcSHans Rosenfeld .dma_attr_minxfer = 0x1000,
398788c47fcSHans Rosenfeld .dma_attr_maxxfer = (UINT16_MAX + 1) * sizeof (nvme_sqe_t),
399788c47fcSHans Rosenfeld .dma_attr_seg = 0xffffffffffffffffULL,
400788c47fcSHans Rosenfeld .dma_attr_sgllen = 1,
401788c47fcSHans Rosenfeld .dma_attr_granular = 1,
402788c47fcSHans Rosenfeld .dma_attr_flags = 0,
403788c47fcSHans Rosenfeld };
404788c47fcSHans Rosenfeld
405788c47fcSHans Rosenfeld /*
406788c47fcSHans Rosenfeld * DMA attributes for transfers using Physical Region Page (PRP) entries
407788c47fcSHans Rosenfeld *
408788c47fcSHans Rosenfeld * A PRP entry describes one page of DMA memory using the page size specified
409788c47fcSHans Rosenfeld * in the controller configuration's memory page size register (CC.MPS). It uses
410788c47fcSHans Rosenfeld * a 64bit base address aligned to this page size. There is no limitation on
411788c47fcSHans Rosenfeld * chaining PRPs together for arbitrarily large DMA transfers.
412788c47fcSHans Rosenfeld */
413788c47fcSHans Rosenfeld static ddi_dma_attr_t nvme_prp_dma_attr = {
414788c47fcSHans Rosenfeld .dma_attr_version = DMA_ATTR_V0,
415788c47fcSHans Rosenfeld .dma_attr_addr_lo = 0,
416788c47fcSHans Rosenfeld .dma_attr_addr_hi = 0xffffffffffffffffULL,
417788c47fcSHans Rosenfeld .dma_attr_count_max = 0xfff,
418788c47fcSHans Rosenfeld .dma_attr_align = 0x1000,
419788c47fcSHans Rosenfeld .dma_attr_burstsizes = 0x7ff,
420788c47fcSHans Rosenfeld .dma_attr_minxfer = 0x1000,
421788c47fcSHans Rosenfeld .dma_attr_maxxfer = 0x1000,
422babc8e1dSYouzhong Yang .dma_attr_seg = 0xfff,
423788c47fcSHans Rosenfeld .dma_attr_sgllen = -1,
424788c47fcSHans Rosenfeld .dma_attr_granular = 1,
425788c47fcSHans Rosenfeld .dma_attr_flags = 0,
426788c47fcSHans Rosenfeld };
427788c47fcSHans Rosenfeld
428788c47fcSHans Rosenfeld /*
429788c47fcSHans Rosenfeld * DMA attributes for transfers using scatter/gather lists
430788c47fcSHans Rosenfeld *
431788c47fcSHans Rosenfeld * A SGL entry describes a chunk of DMA memory using a 64bit base address and a
432788c47fcSHans Rosenfeld * 32bit length field. SGL Segment and SGL Last Segment entries require the
433788c47fcSHans Rosenfeld * length to be a multiple of 16 bytes.
434788c47fcSHans Rosenfeld */
435788c47fcSHans Rosenfeld static ddi_dma_attr_t nvme_sgl_dma_attr = {
436788c47fcSHans Rosenfeld .dma_attr_version = DMA_ATTR_V0,
437788c47fcSHans Rosenfeld .dma_attr_addr_lo = 0,
438788c47fcSHans Rosenfeld .dma_attr_addr_hi = 0xffffffffffffffffULL,
439788c47fcSHans Rosenfeld .dma_attr_count_max = 0xffffffffUL,
440788c47fcSHans Rosenfeld .dma_attr_align = 1,
441788c47fcSHans Rosenfeld .dma_attr_burstsizes = 0x7ff,
442788c47fcSHans Rosenfeld .dma_attr_minxfer = 0x10,
443788c47fcSHans Rosenfeld .dma_attr_maxxfer = 0xfffffffffULL,
444788c47fcSHans Rosenfeld .dma_attr_seg = 0xffffffffffffffffULL,
445788c47fcSHans Rosenfeld .dma_attr_sgllen = -1,
446788c47fcSHans Rosenfeld .dma_attr_granular = 0x10,
447788c47fcSHans Rosenfeld .dma_attr_flags = 0
448788c47fcSHans Rosenfeld };
449788c47fcSHans Rosenfeld
450788c47fcSHans Rosenfeld static ddi_device_acc_attr_t nvme_reg_acc_attr = {
451788c47fcSHans Rosenfeld .devacc_attr_version = DDI_DEVICE_ATTR_V0,
452788c47fcSHans Rosenfeld .devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC,
453788c47fcSHans Rosenfeld .devacc_attr_dataorder = DDI_STRICTORDER_ACC
454788c47fcSHans Rosenfeld };
455788c47fcSHans Rosenfeld
456032cbfebSHans Rosenfeld static struct cb_ops nvme_cb_ops = {
457032cbfebSHans Rosenfeld .cb_open = nvme_open,
458032cbfebSHans Rosenfeld .cb_close = nvme_close,
459032cbfebSHans Rosenfeld .cb_strategy = nodev,
460032cbfebSHans Rosenfeld .cb_print = nodev,
461032cbfebSHans Rosenfeld .cb_dump = nodev,
462032cbfebSHans Rosenfeld .cb_read = nodev,
463032cbfebSHans Rosenfeld .cb_write = nodev,
464032cbfebSHans Rosenfeld .cb_ioctl = nvme_ioctl,
465032cbfebSHans Rosenfeld .cb_devmap = nodev,
466032cbfebSHans Rosenfeld .cb_mmap = nodev,
467032cbfebSHans Rosenfeld .cb_segmap = nodev,
468032cbfebSHans Rosenfeld .cb_chpoll = nochpoll,
469032cbfebSHans Rosenfeld .cb_prop_op = ddi_prop_op,
470032cbfebSHans Rosenfeld .cb_str = 0,
471032cbfebSHans Rosenfeld .cb_flag = D_NEW | D_MP,
472032cbfebSHans Rosenfeld .cb_rev = CB_REV,
473032cbfebSHans Rosenfeld .cb_aread = nodev,
474032cbfebSHans Rosenfeld .cb_awrite = nodev
475032cbfebSHans Rosenfeld };
476032cbfebSHans Rosenfeld
477788c47fcSHans Rosenfeld static struct dev_ops nvme_dev_ops = {
478788c47fcSHans Rosenfeld .devo_rev = DEVO_REV,
479788c47fcSHans Rosenfeld .devo_refcnt = 0,
480788c47fcSHans Rosenfeld .devo_getinfo = ddi_no_info,
481788c47fcSHans Rosenfeld .devo_identify = nulldev,
482788c47fcSHans Rosenfeld .devo_probe = nulldev,
483788c47fcSHans Rosenfeld .devo_attach = nvme_attach,
484788c47fcSHans Rosenfeld .devo_detach = nvme_detach,
485788c47fcSHans Rosenfeld .devo_reset = nodev,
486032cbfebSHans Rosenfeld .devo_cb_ops = &nvme_cb_ops,
487788c47fcSHans Rosenfeld .devo_bus_ops = NULL,
488788c47fcSHans Rosenfeld .devo_power = NULL,
489788c47fcSHans Rosenfeld .devo_quiesce = nvme_quiesce,
490788c47fcSHans Rosenfeld };
491788c47fcSHans Rosenfeld
492788c47fcSHans Rosenfeld static struct modldrv nvme_modldrv = {
493788c47fcSHans Rosenfeld .drv_modops = &mod_driverops,
494265d85e9SHans Rosenfeld .drv_linkinfo = "NVMe v1.1b",
495788c47fcSHans Rosenfeld .drv_dev_ops = &nvme_dev_ops
496788c47fcSHans Rosenfeld };
497788c47fcSHans Rosenfeld
498788c47fcSHans Rosenfeld static struct modlinkage nvme_modlinkage = {
499788c47fcSHans Rosenfeld .ml_rev = MODREV_1,
500788c47fcSHans Rosenfeld .ml_linkage = { &nvme_modldrv, NULL }
501788c47fcSHans Rosenfeld };
502788c47fcSHans Rosenfeld
503788c47fcSHans Rosenfeld static bd_ops_t nvme_bd_ops = {
504788c47fcSHans Rosenfeld .o_version = BD_OPS_VERSION_0,
505788c47fcSHans Rosenfeld .o_drive_info = nvme_bd_driveinfo,
506788c47fcSHans Rosenfeld .o_media_info = nvme_bd_mediainfo,
507788c47fcSHans Rosenfeld .o_devid_init = nvme_bd_devid,
508788c47fcSHans Rosenfeld .o_sync_cache = nvme_bd_sync,
509788c47fcSHans Rosenfeld .o_read = nvme_bd_read,
510788c47fcSHans Rosenfeld .o_write = nvme_bd_write,
511788c47fcSHans Rosenfeld };
512788c47fcSHans Rosenfeld
51369a33c23SHans Rosenfeld /*
51469a33c23SHans Rosenfeld * This list will hold commands that have timed out and couldn't be aborted.
51569a33c23SHans Rosenfeld * As we don't know what the hardware may still do with the DMA memory we can't
51669a33c23SHans Rosenfeld * free them, so we'll keep them forever on this list where we can easily look
51769a33c23SHans Rosenfeld * at them with mdb.
51869a33c23SHans Rosenfeld */
51969a33c23SHans Rosenfeld static struct list nvme_lost_cmds;
52069a33c23SHans Rosenfeld static kmutex_t nvme_lc_mutex;
52169a33c23SHans Rosenfeld
522788c47fcSHans Rosenfeld int
_init(void)523788c47fcSHans Rosenfeld _init(void)
524788c47fcSHans Rosenfeld {
525788c47fcSHans Rosenfeld int error;
526788c47fcSHans Rosenfeld
527788c47fcSHans Rosenfeld error = ddi_soft_state_init(&nvme_state, sizeof (nvme_t), 1);
528788c47fcSHans Rosenfeld if (error != DDI_SUCCESS)
529788c47fcSHans Rosenfeld return (error);
530788c47fcSHans Rosenfeld
531788c47fcSHans Rosenfeld nvme_cmd_cache = kmem_cache_create("nvme_cmd_cache",
532788c47fcSHans Rosenfeld sizeof (nvme_cmd_t), 64, NULL, NULL, NULL, NULL, NULL, 0);
533788c47fcSHans Rosenfeld
53469a33c23SHans Rosenfeld mutex_init(&nvme_lc_mutex, NULL, MUTEX_DRIVER, NULL);
53569a33c23SHans Rosenfeld list_create(&nvme_lost_cmds, sizeof (nvme_cmd_t),
53669a33c23SHans Rosenfeld offsetof(nvme_cmd_t, nc_list));
53769a33c23SHans Rosenfeld
538788c47fcSHans Rosenfeld bd_mod_init(&nvme_dev_ops);
539788c47fcSHans Rosenfeld
540788c47fcSHans Rosenfeld error = mod_install(&nvme_modlinkage);
541788c47fcSHans Rosenfeld if (error != DDI_SUCCESS) {
542788c47fcSHans Rosenfeld ddi_soft_state_fini(&nvme_state);
54369a33c23SHans Rosenfeld mutex_destroy(&nvme_lc_mutex);
54469a33c23SHans Rosenfeld list_destroy(&nvme_lost_cmds);
545788c47fcSHans Rosenfeld bd_mod_fini(&nvme_dev_ops);
546788c47fcSHans Rosenfeld }
547788c47fcSHans Rosenfeld
548788c47fcSHans Rosenfeld return (error);
549788c47fcSHans Rosenfeld }
550788c47fcSHans Rosenfeld
551788c47fcSHans Rosenfeld int
_fini(void)552788c47fcSHans Rosenfeld _fini(void)
553788c47fcSHans Rosenfeld {
554788c47fcSHans Rosenfeld int error;
555788c47fcSHans Rosenfeld
55669a33c23SHans Rosenfeld if (!list_is_empty(&nvme_lost_cmds))
55769a33c23SHans Rosenfeld return (DDI_FAILURE);
55869a33c23SHans Rosenfeld
559788c47fcSHans Rosenfeld error = mod_remove(&nvme_modlinkage);
560788c47fcSHans Rosenfeld if (error == DDI_SUCCESS) {
561788c47fcSHans Rosenfeld ddi_soft_state_fini(&nvme_state);
562788c47fcSHans Rosenfeld kmem_cache_destroy(nvme_cmd_cache);
56369a33c23SHans Rosenfeld mutex_destroy(&nvme_lc_mutex);
56469a33c23SHans Rosenfeld list_destroy(&nvme_lost_cmds);
565788c47fcSHans Rosenfeld bd_mod_fini(&nvme_dev_ops);
566788c47fcSHans Rosenfeld }
567788c47fcSHans Rosenfeld
568788c47fcSHans Rosenfeld return (error);
569788c47fcSHans Rosenfeld }
570788c47fcSHans Rosenfeld
571788c47fcSHans Rosenfeld int
_info(struct modinfo * modinfop)572788c47fcSHans Rosenfeld _info(struct modinfo *modinfop)
573788c47fcSHans Rosenfeld {
574788c47fcSHans Rosenfeld return (mod_info(&nvme_modlinkage, modinfop));
575788c47fcSHans Rosenfeld }
576788c47fcSHans Rosenfeld
577788c47fcSHans Rosenfeld static inline void
nvme_put64(nvme_t * nvme,uintptr_t reg,uint64_t val)578788c47fcSHans Rosenfeld nvme_put64(nvme_t *nvme, uintptr_t reg, uint64_t val)
579788c47fcSHans Rosenfeld {
580788c47fcSHans Rosenfeld ASSERT(((uintptr_t)(nvme->n_regs + reg) & 0x7) == 0);
581788c47fcSHans Rosenfeld
582788c47fcSHans Rosenfeld /*LINTED: E_BAD_PTR_CAST_ALIGN*/
583788c47fcSHans Rosenfeld ddi_put64(nvme->n_regh, (uint64_t *)(nvme->n_regs + reg), val);
584788c47fcSHans Rosenfeld }
585788c47fcSHans Rosenfeld
586788c47fcSHans Rosenfeld static inline void
nvme_put32(nvme_t * nvme,uintptr_t reg,uint32_t val)587788c47fcSHans Rosenfeld nvme_put32(nvme_t *nvme, uintptr_t reg, uint32_t val)
588788c47fcSHans Rosenfeld {
589788c47fcSHans Rosenfeld ASSERT(((uintptr_t)(nvme->n_regs + reg) & 0x3) == 0);
590788c47fcSHans Rosenfeld
591788c47fcSHans Rosenfeld /*LINTED: E_BAD_PTR_CAST_ALIGN*/
592788c47fcSHans Rosenfeld ddi_put32(nvme->n_regh, (uint32_t *)(nvme->n_regs + reg), val);
593788c47fcSHans Rosenfeld }
594788c47fcSHans Rosenfeld
595788c47fcSHans Rosenfeld static inline uint64_t
nvme_get64(nvme_t * nvme,uintptr_t reg)596788c47fcSHans Rosenfeld nvme_get64(nvme_t *nvme, uintptr_t reg)
597788c47fcSHans Rosenfeld {
598788c47fcSHans Rosenfeld uint64_t val;
599788c47fcSHans Rosenfeld
600788c47fcSHans Rosenfeld ASSERT(((uintptr_t)(nvme->n_regs + reg) & 0x7) == 0);
601788c47fcSHans Rosenfeld
602788c47fcSHans Rosenfeld /*LINTED: E_BAD_PTR_CAST_ALIGN*/
603788c47fcSHans Rosenfeld val = ddi_get64(nvme->n_regh, (uint64_t *)(nvme->n_regs + reg));
604788c47fcSHans Rosenfeld
605788c47fcSHans Rosenfeld return (val);
606788c47fcSHans Rosenfeld }
607788c47fcSHans Rosenfeld
608788c47fcSHans Rosenfeld static inline uint32_t
nvme_get32(nvme_t * nvme,uintptr_t reg)609788c47fcSHans Rosenfeld nvme_get32(nvme_t *nvme, uintptr_t reg)
610788c47fcSHans Rosenfeld {
611788c47fcSHans Rosenfeld uint32_t val;
612788c47fcSHans Rosenfeld
613788c47fcSHans Rosenfeld ASSERT(((uintptr_t)(nvme->n_regs + reg) & 0x3) == 0);
614788c47fcSHans Rosenfeld
615788c47fcSHans Rosenfeld /*LINTED: E_BAD_PTR_CAST_ALIGN*/
616788c47fcSHans Rosenfeld val = ddi_get32(nvme->n_regh, (uint32_t *)(nvme->n_regs + reg));
617788c47fcSHans Rosenfeld
618788c47fcSHans Rosenfeld return (val);
619788c47fcSHans Rosenfeld }
620788c47fcSHans Rosenfeld
621788c47fcSHans Rosenfeld static boolean_t
nvme_check_regs_hdl(nvme_t * nvme)622788c47fcSHans Rosenfeld nvme_check_regs_hdl(nvme_t *nvme)
623788c47fcSHans Rosenfeld {
624788c47fcSHans Rosenfeld ddi_fm_error_t error;
625788c47fcSHans Rosenfeld
626788c47fcSHans Rosenfeld ddi_fm_acc_err_get(nvme->n_regh, &error, DDI_FME_VERSION);
627788c47fcSHans Rosenfeld
628788c47fcSHans Rosenfeld if (error.fme_status != DDI_FM_OK)
629788c47fcSHans Rosenfeld return (B_TRUE);
630788c47fcSHans Rosenfeld
631788c47fcSHans Rosenfeld return (B_FALSE);
632788c47fcSHans Rosenfeld }
633788c47fcSHans Rosenfeld
634788c47fcSHans Rosenfeld static boolean_t
nvme_check_dma_hdl(nvme_dma_t * dma)635788c47fcSHans Rosenfeld nvme_check_dma_hdl(nvme_dma_t *dma)
636788c47fcSHans Rosenfeld {
637788c47fcSHans Rosenfeld ddi_fm_error_t error;
638788c47fcSHans Rosenfeld
639788c47fcSHans Rosenfeld if (dma == NULL)
640788c47fcSHans Rosenfeld return (B_FALSE);
641788c47fcSHans Rosenfeld
642788c47fcSHans Rosenfeld ddi_fm_dma_err_get(dma->nd_dmah, &error, DDI_FME_VERSION);
643788c47fcSHans Rosenfeld
644788c47fcSHans Rosenfeld if (error.fme_status != DDI_FM_OK)
645788c47fcSHans Rosenfeld return (B_TRUE);
646788c47fcSHans Rosenfeld
647788c47fcSHans Rosenfeld return (B_FALSE);
648788c47fcSHans Rosenfeld }
649788c47fcSHans Rosenfeld
650788c47fcSHans Rosenfeld static void
nvme_free_dma_common(nvme_dma_t * dma)651b4eda73dSYouzhong Yang nvme_free_dma_common(nvme_dma_t *dma)
652788c47fcSHans Rosenfeld {
653788c47fcSHans Rosenfeld if (dma->nd_dmah != NULL)
654788c47fcSHans Rosenfeld (void) ddi_dma_unbind_handle(dma->nd_dmah);
655788c47fcSHans Rosenfeld if (dma->nd_acch != NULL)
656788c47fcSHans Rosenfeld ddi_dma_mem_free(&dma->nd_acch);
657788c47fcSHans Rosenfeld if (dma->nd_dmah != NULL)
658788c47fcSHans Rosenfeld ddi_dma_free_handle(&dma->nd_dmah);
659b4eda73dSYouzhong Yang }
660b4eda73dSYouzhong Yang
661b4eda73dSYouzhong Yang static void
nvme_free_dma(nvme_dma_t * dma)662b4eda73dSYouzhong Yang nvme_free_dma(nvme_dma_t *dma)
663b4eda73dSYouzhong Yang {
664b4eda73dSYouzhong Yang nvme_free_dma_common(dma);
665b4eda73dSYouzhong Yang kmem_free(dma, sizeof (*dma));
666b4eda73dSYouzhong Yang }
667b4eda73dSYouzhong Yang
668f19ea22aSDan McDonald /* ARGSUSED */
669b4eda73dSYouzhong Yang static void
nvme_prp_dma_destructor(void * buf,void * private)670b4eda73dSYouzhong Yang nvme_prp_dma_destructor(void *buf, void *private)
671b4eda73dSYouzhong Yang {
672b4eda73dSYouzhong Yang nvme_dma_t *dma = (nvme_dma_t *)buf;
673b4eda73dSYouzhong Yang
674b4eda73dSYouzhong Yang nvme_free_dma_common(dma);
675788c47fcSHans Rosenfeld }
676788c47fcSHans Rosenfeld
677788c47fcSHans Rosenfeld static int
nvme_alloc_dma_common(nvme_t * nvme,nvme_dma_t * dma,size_t len,uint_t flags,ddi_dma_attr_t * dma_attr)678b4eda73dSYouzhong Yang nvme_alloc_dma_common(nvme_t *nvme, nvme_dma_t *dma,
679b4eda73dSYouzhong Yang size_t len, uint_t flags, ddi_dma_attr_t *dma_attr)
680788c47fcSHans Rosenfeld {
681788c47fcSHans Rosenfeld if (ddi_dma_alloc_handle(nvme->n_dip, dma_attr, DDI_DMA_SLEEP, NULL,
682788c47fcSHans Rosenfeld &dma->nd_dmah) != DDI_SUCCESS) {
683788c47fcSHans Rosenfeld /*
684788c47fcSHans Rosenfeld * Due to DDI_DMA_SLEEP this can't be DDI_DMA_NORESOURCES, and
685788c47fcSHans Rosenfeld * the only other possible error is DDI_DMA_BADATTR which
686788c47fcSHans Rosenfeld * indicates a driver bug which should cause a panic.
687788c47fcSHans Rosenfeld */
688788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_PANIC,
689788c47fcSHans Rosenfeld "!failed to get DMA handle, check DMA attributes");
690788c47fcSHans Rosenfeld return (DDI_FAILURE);
691788c47fcSHans Rosenfeld }
692788c47fcSHans Rosenfeld
693788c47fcSHans Rosenfeld /*
694788c47fcSHans Rosenfeld * ddi_dma_mem_alloc() can only fail when DDI_DMA_NOSLEEP is specified
695788c47fcSHans Rosenfeld * or the flags are conflicting, which isn't the case here.
696788c47fcSHans Rosenfeld */
697788c47fcSHans Rosenfeld (void) ddi_dma_mem_alloc(dma->nd_dmah, len, &nvme->n_reg_acc_attr,
698788c47fcSHans Rosenfeld DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL, &dma->nd_memp,
699788c47fcSHans Rosenfeld &dma->nd_len, &dma->nd_acch);
700788c47fcSHans Rosenfeld
701788c47fcSHans Rosenfeld if (ddi_dma_addr_bind_handle(dma->nd_dmah, NULL, dma->nd_memp,
702788c47fcSHans Rosenfeld dma->nd_len, flags | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, NULL,
703788c47fcSHans Rosenfeld &dma->nd_cookie, &dma->nd_ncookie) != DDI_DMA_MAPPED) {
704788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN,
705788c47fcSHans Rosenfeld "!failed to bind DMA memory");
706788c47fcSHans Rosenfeld atomic_inc_32(&nvme->n_dma_bind_err);
707b4eda73dSYouzhong Yang nvme_free_dma_common(dma);
708b4eda73dSYouzhong Yang return (DDI_FAILURE);
709b4eda73dSYouzhong Yang }
710b4eda73dSYouzhong Yang
711b4eda73dSYouzhong Yang return (DDI_SUCCESS);
712b4eda73dSYouzhong Yang }
713b4eda73dSYouzhong Yang
714b4eda73dSYouzhong Yang static int
nvme_zalloc_dma(nvme_t * nvme,size_t len,uint_t flags,ddi_dma_attr_t * dma_attr,nvme_dma_t ** ret)715b4eda73dSYouzhong Yang nvme_zalloc_dma(nvme_t *nvme, size_t len, uint_t flags,
716b4eda73dSYouzhong Yang ddi_dma_attr_t *dma_attr, nvme_dma_t **ret)
717b4eda73dSYouzhong Yang {
718b4eda73dSYouzhong Yang nvme_dma_t *dma = kmem_zalloc(sizeof (nvme_dma_t), KM_SLEEP);
719b4eda73dSYouzhong Yang
720b4eda73dSYouzhong Yang if (nvme_alloc_dma_common(nvme, dma, len, flags, dma_attr) !=
721b4eda73dSYouzhong Yang DDI_SUCCESS) {
722788c47fcSHans Rosenfeld *ret = NULL;
723b4eda73dSYouzhong Yang kmem_free(dma, sizeof (nvme_dma_t));
724788c47fcSHans Rosenfeld return (DDI_FAILURE);
725788c47fcSHans Rosenfeld }
726788c47fcSHans Rosenfeld
727788c47fcSHans Rosenfeld bzero(dma->nd_memp, dma->nd_len);
728788c47fcSHans Rosenfeld
729788c47fcSHans Rosenfeld *ret = dma;
730788c47fcSHans Rosenfeld return (DDI_SUCCESS);
731788c47fcSHans Rosenfeld }
732788c47fcSHans Rosenfeld
733f19ea22aSDan McDonald /* ARGSUSED */
734788c47fcSHans Rosenfeld static int
nvme_prp_dma_constructor(void * buf,void * private,int flags)735b4eda73dSYouzhong Yang nvme_prp_dma_constructor(void *buf, void *private, int flags)
736b4eda73dSYouzhong Yang {
737b4eda73dSYouzhong Yang nvme_dma_t *dma = (nvme_dma_t *)buf;
738b4eda73dSYouzhong Yang nvme_t *nvme = (nvme_t *)private;
739b4eda73dSYouzhong Yang
740b4eda73dSYouzhong Yang dma->nd_dmah = NULL;
741b4eda73dSYouzhong Yang dma->nd_acch = NULL;
742b4eda73dSYouzhong Yang
743b4eda73dSYouzhong Yang if (nvme_alloc_dma_common(nvme, dma, nvme->n_pagesize,
744b4eda73dSYouzhong Yang DDI_DMA_READ, &nvme->n_prp_dma_attr) != DDI_SUCCESS) {
745b4eda73dSYouzhong Yang return (-1);
746b4eda73dSYouzhong Yang }
747b4eda73dSYouzhong Yang
748b4eda73dSYouzhong Yang ASSERT(dma->nd_ncookie == 1);
749b4eda73dSYouzhong Yang
750b4eda73dSYouzhong Yang dma->nd_cached = B_TRUE;
751b4eda73dSYouzhong Yang
752b4eda73dSYouzhong Yang return (0);
753b4eda73dSYouzhong Yang }
754b4eda73dSYouzhong Yang
755b4eda73dSYouzhong Yang static int
nvme_zalloc_queue_dma(nvme_t * nvme,uint32_t nentry,uint16_t qe_len,uint_t flags,nvme_dma_t ** dma)756788c47fcSHans Rosenfeld nvme_zalloc_queue_dma(nvme_t *nvme, uint32_t nentry, uint16_t qe_len,
757788c47fcSHans Rosenfeld uint_t flags, nvme_dma_t **dma)
758788c47fcSHans Rosenfeld {
759788c47fcSHans Rosenfeld uint32_t len = nentry * qe_len;
760788c47fcSHans Rosenfeld ddi_dma_attr_t q_dma_attr = nvme->n_queue_dma_attr;
761788c47fcSHans Rosenfeld
762788c47fcSHans Rosenfeld len = roundup(len, nvme->n_pagesize);
763788c47fcSHans Rosenfeld
764788c47fcSHans Rosenfeld q_dma_attr.dma_attr_minxfer = len;
765788c47fcSHans Rosenfeld
766788c47fcSHans Rosenfeld if (nvme_zalloc_dma(nvme, len, flags, &q_dma_attr, dma)
767788c47fcSHans Rosenfeld != DDI_SUCCESS) {
768788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN,
769788c47fcSHans Rosenfeld "!failed to get DMA memory for queue");
770788c47fcSHans Rosenfeld goto fail;
771788c47fcSHans Rosenfeld }
772788c47fcSHans Rosenfeld
773788c47fcSHans Rosenfeld if ((*dma)->nd_ncookie != 1) {
774788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN,
775788c47fcSHans Rosenfeld "!got too many cookies for queue DMA");
776788c47fcSHans Rosenfeld goto fail;
777788c47fcSHans Rosenfeld }
778788c47fcSHans Rosenfeld
779788c47fcSHans Rosenfeld return (DDI_SUCCESS);
780788c47fcSHans Rosenfeld
781788c47fcSHans Rosenfeld fail:
782788c47fcSHans Rosenfeld if (*dma) {
783788c47fcSHans Rosenfeld nvme_free_dma(*dma);
784788c47fcSHans Rosenfeld *dma = NULL;
785788c47fcSHans Rosenfeld }
786788c47fcSHans Rosenfeld
787788c47fcSHans Rosenfeld return (DDI_FAILURE);
788788c47fcSHans Rosenfeld }
789788c47fcSHans Rosenfeld
790788c47fcSHans Rosenfeld static void
nvme_free_qpair(nvme_qpair_t * qp)791788c47fcSHans Rosenfeld nvme_free_qpair(nvme_qpair_t *qp)
792788c47fcSHans Rosenfeld {
793788c47fcSHans Rosenfeld int i;
794788c47fcSHans Rosenfeld
795788c47fcSHans Rosenfeld mutex_destroy(&qp->nq_mutex);
796ba636d1cSHans Rosenfeld sema_destroy(&qp->nq_sema);
797788c47fcSHans Rosenfeld
798788c47fcSHans Rosenfeld if (qp->nq_sqdma != NULL)
799788c47fcSHans Rosenfeld nvme_free_dma(qp->nq_sqdma);
800788c47fcSHans Rosenfeld if (qp->nq_cqdma != NULL)
801788c47fcSHans Rosenfeld nvme_free_dma(qp->nq_cqdma);
802788c47fcSHans Rosenfeld
803788c47fcSHans Rosenfeld if (qp->nq_active_cmds > 0)
804788c47fcSHans Rosenfeld for (i = 0; i != qp->nq_nentry; i++)
805788c47fcSHans Rosenfeld if (qp->nq_cmd[i] != NULL)
806788c47fcSHans Rosenfeld nvme_free_cmd(qp->nq_cmd[i]);
807788c47fcSHans Rosenfeld
808788c47fcSHans Rosenfeld if (qp->nq_cmd != NULL)
809788c47fcSHans Rosenfeld kmem_free(qp->nq_cmd, sizeof (nvme_cmd_t *) * qp->nq_nentry);
810788c47fcSHans Rosenfeld
811788c47fcSHans Rosenfeld kmem_free(qp, sizeof (nvme_qpair_t));
812788c47fcSHans Rosenfeld }
813788c47fcSHans Rosenfeld
814788c47fcSHans Rosenfeld static int
nvme_alloc_qpair(nvme_t * nvme,uint32_t nentry,nvme_qpair_t ** nqp,int idx)815788c47fcSHans Rosenfeld nvme_alloc_qpair(nvme_t *nvme, uint32_t nentry, nvme_qpair_t **nqp,
816788c47fcSHans Rosenfeld int idx)
817788c47fcSHans Rosenfeld {
818788c47fcSHans Rosenfeld nvme_qpair_t *qp = kmem_zalloc(sizeof (*qp), KM_SLEEP);
819788c47fcSHans Rosenfeld
820788c47fcSHans Rosenfeld mutex_init(&qp->nq_mutex, NULL, MUTEX_DRIVER,
821788c47fcSHans Rosenfeld DDI_INTR_PRI(nvme->n_intr_pri));
822ba636d1cSHans Rosenfeld sema_init(&qp->nq_sema, nentry, NULL, SEMA_DRIVER, NULL);
823788c47fcSHans Rosenfeld
824788c47fcSHans Rosenfeld if (nvme_zalloc_queue_dma(nvme, nentry, sizeof (nvme_sqe_t),
825788c47fcSHans Rosenfeld DDI_DMA_WRITE, &qp->nq_sqdma) != DDI_SUCCESS)
826788c47fcSHans Rosenfeld goto fail;
827788c47fcSHans Rosenfeld
828788c47fcSHans Rosenfeld if (nvme_zalloc_queue_dma(nvme, nentry, sizeof (nvme_cqe_t),
829788c47fcSHans Rosenfeld DDI_DMA_READ, &qp->nq_cqdma) != DDI_SUCCESS)
830788c47fcSHans Rosenfeld goto fail;
831788c47fcSHans Rosenfeld
832788c47fcSHans Rosenfeld qp->nq_sq = (nvme_sqe_t *)qp->nq_sqdma->nd_memp;
833788c47fcSHans Rosenfeld qp->nq_cq = (nvme_cqe_t *)qp->nq_cqdma->nd_memp;
834788c47fcSHans Rosenfeld qp->nq_nentry = nentry;
835788c47fcSHans Rosenfeld
836788c47fcSHans Rosenfeld qp->nq_sqtdbl = NVME_REG_SQTDBL(nvme, idx);
837788c47fcSHans Rosenfeld qp->nq_cqhdbl = NVME_REG_CQHDBL(nvme, idx);
838788c47fcSHans Rosenfeld
839788c47fcSHans Rosenfeld qp->nq_cmd = kmem_zalloc(sizeof (nvme_cmd_t *) * nentry, KM_SLEEP);
840788c47fcSHans Rosenfeld qp->nq_next_cmd = 0;
841788c47fcSHans Rosenfeld
842788c47fcSHans Rosenfeld *nqp = qp;
843788c47fcSHans Rosenfeld return (DDI_SUCCESS);
844788c47fcSHans Rosenfeld
845788c47fcSHans Rosenfeld fail:
846788c47fcSHans Rosenfeld nvme_free_qpair(qp);
847788c47fcSHans Rosenfeld *nqp = NULL;
848788c47fcSHans Rosenfeld
849788c47fcSHans Rosenfeld return (DDI_FAILURE);
850788c47fcSHans Rosenfeld }
851788c47fcSHans Rosenfeld
852788c47fcSHans Rosenfeld static nvme_cmd_t *
nvme_alloc_cmd(nvme_t * nvme,int kmflag)853788c47fcSHans Rosenfeld nvme_alloc_cmd(nvme_t *nvme, int kmflag)
854788c47fcSHans Rosenfeld {
855788c47fcSHans Rosenfeld nvme_cmd_t *cmd = kmem_cache_alloc(nvme_cmd_cache, kmflag);
856788c47fcSHans Rosenfeld
857788c47fcSHans Rosenfeld if (cmd == NULL)
858788c47fcSHans Rosenfeld return (cmd);
859788c47fcSHans Rosenfeld
860788c47fcSHans Rosenfeld bzero(cmd, sizeof (nvme_cmd_t));
861788c47fcSHans Rosenfeld
862788c47fcSHans Rosenfeld cmd->nc_nvme = nvme;
863788c47fcSHans Rosenfeld
864788c47fcSHans Rosenfeld mutex_init(&cmd->nc_mutex, NULL, MUTEX_DRIVER,
865788c47fcSHans Rosenfeld DDI_INTR_PRI(nvme->n_intr_pri));
866788c47fcSHans Rosenfeld cv_init(&cmd->nc_cv, NULL, CV_DRIVER, NULL);
867788c47fcSHans Rosenfeld
868788c47fcSHans Rosenfeld return (cmd);
869788c47fcSHans Rosenfeld }
870788c47fcSHans Rosenfeld
871788c47fcSHans Rosenfeld static void
nvme_free_cmd(nvme_cmd_t * cmd)872788c47fcSHans Rosenfeld nvme_free_cmd(nvme_cmd_t *cmd)
873788c47fcSHans Rosenfeld {
87469a33c23SHans Rosenfeld /* Don't free commands on the lost commands list. */
87569a33c23SHans Rosenfeld if (list_link_active(&cmd->nc_list))
87669a33c23SHans Rosenfeld return;
87769a33c23SHans Rosenfeld
878788c47fcSHans Rosenfeld if (cmd->nc_dma) {
879b4eda73dSYouzhong Yang if (cmd->nc_dma->nd_cached)
880b4eda73dSYouzhong Yang kmem_cache_free(cmd->nc_nvme->n_prp_cache,
881b4eda73dSYouzhong Yang cmd->nc_dma);
882b4eda73dSYouzhong Yang else
883788c47fcSHans Rosenfeld nvme_free_dma(cmd->nc_dma);
884788c47fcSHans Rosenfeld cmd->nc_dma = NULL;
885788c47fcSHans Rosenfeld }
886788c47fcSHans Rosenfeld
887788c47fcSHans Rosenfeld cv_destroy(&cmd->nc_cv);
888788c47fcSHans Rosenfeld mutex_destroy(&cmd->nc_mutex);
889788c47fcSHans Rosenfeld
890788c47fcSHans Rosenfeld kmem_cache_free(nvme_cmd_cache, cmd);
891788c47fcSHans Rosenfeld }
892788c47fcSHans Rosenfeld
893ba636d1cSHans Rosenfeld static void
nvme_submit_admin_cmd(nvme_qpair_t * qp,nvme_cmd_t * cmd)894ba636d1cSHans Rosenfeld nvme_submit_admin_cmd(nvme_qpair_t *qp, nvme_cmd_t *cmd)
895ba636d1cSHans Rosenfeld {
896ba636d1cSHans Rosenfeld sema_p(&qp->nq_sema);
897ba636d1cSHans Rosenfeld nvme_submit_cmd_common(qp, cmd);
898ba636d1cSHans Rosenfeld }
899ba636d1cSHans Rosenfeld
900788c47fcSHans Rosenfeld static int
nvme_submit_io_cmd(nvme_qpair_t * qp,nvme_cmd_t * cmd)901ba636d1cSHans Rosenfeld nvme_submit_io_cmd(nvme_qpair_t *qp, nvme_cmd_t *cmd)
902ba636d1cSHans Rosenfeld {
903ba636d1cSHans Rosenfeld if (sema_tryp(&qp->nq_sema) == 0)
904ba636d1cSHans Rosenfeld return (EAGAIN);
905ba636d1cSHans Rosenfeld
906ba636d1cSHans Rosenfeld nvme_submit_cmd_common(qp, cmd);
907ba636d1cSHans Rosenfeld return (0);
908ba636d1cSHans Rosenfeld }
909ba636d1cSHans Rosenfeld
910ba636d1cSHans Rosenfeld static void
nvme_submit_cmd_common(nvme_qpair_t * qp,nvme_cmd_t * cmd)911ba636d1cSHans Rosenfeld nvme_submit_cmd_common(nvme_qpair_t *qp, nvme_cmd_t *cmd)
912788c47fcSHans Rosenfeld {
913788c47fcSHans Rosenfeld nvme_reg_sqtdbl_t tail = { 0 };
914788c47fcSHans Rosenfeld
915788c47fcSHans Rosenfeld mutex_enter(&qp->nq_mutex);
916788c47fcSHans Rosenfeld cmd->nc_completed = B_FALSE;
917788c47fcSHans Rosenfeld
918788c47fcSHans Rosenfeld /*
919788c47fcSHans Rosenfeld * Try to insert the cmd into the active cmd array at the nq_next_cmd
920788c47fcSHans Rosenfeld * slot. If the slot is already occupied advance to the next slot and
921788c47fcSHans Rosenfeld * try again. This can happen for long running commands like async event
922788c47fcSHans Rosenfeld * requests.
923788c47fcSHans Rosenfeld */
924788c47fcSHans Rosenfeld while (qp->nq_cmd[qp->nq_next_cmd] != NULL)
925788c47fcSHans Rosenfeld qp->nq_next_cmd = (qp->nq_next_cmd + 1) % qp->nq_nentry;
926788c47fcSHans Rosenfeld qp->nq_cmd[qp->nq_next_cmd] = cmd;
927788c47fcSHans Rosenfeld
928788c47fcSHans Rosenfeld qp->nq_active_cmds++;
929788c47fcSHans Rosenfeld
930788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_cid = qp->nq_next_cmd;
931788c47fcSHans Rosenfeld bcopy(&cmd->nc_sqe, &qp->nq_sq[qp->nq_sqtail], sizeof (nvme_sqe_t));
932788c47fcSHans Rosenfeld (void) ddi_dma_sync(qp->nq_sqdma->nd_dmah,
933788c47fcSHans Rosenfeld sizeof (nvme_sqe_t) * qp->nq_sqtail,
934788c47fcSHans Rosenfeld sizeof (nvme_sqe_t), DDI_DMA_SYNC_FORDEV);
935788c47fcSHans Rosenfeld qp->nq_next_cmd = (qp->nq_next_cmd + 1) % qp->nq_nentry;
936788c47fcSHans Rosenfeld
937788c47fcSHans Rosenfeld tail.b.sqtdbl_sqt = qp->nq_sqtail = (qp->nq_sqtail + 1) % qp->nq_nentry;
938788c47fcSHans Rosenfeld nvme_put32(cmd->nc_nvme, qp->nq_sqtdbl, tail.r);
939788c47fcSHans Rosenfeld
940788c47fcSHans Rosenfeld mutex_exit(&qp->nq_mutex);
941788c47fcSHans Rosenfeld }
942788c47fcSHans Rosenfeld
943788c47fcSHans Rosenfeld static nvme_cmd_t *
nvme_unqueue_cmd(nvme_t * nvme,nvme_qpair_t * qp,int cid)94469a33c23SHans Rosenfeld nvme_unqueue_cmd(nvme_t *nvme, nvme_qpair_t *qp, int cid)
94569a33c23SHans Rosenfeld {
94669a33c23SHans Rosenfeld nvme_cmd_t *cmd;
94769a33c23SHans Rosenfeld
94869a33c23SHans Rosenfeld ASSERT(mutex_owned(&qp->nq_mutex));
94969a33c23SHans Rosenfeld ASSERT3S(cid, <, qp->nq_nentry);
95069a33c23SHans Rosenfeld
95169a33c23SHans Rosenfeld cmd = qp->nq_cmd[cid];
95269a33c23SHans Rosenfeld qp->nq_cmd[cid] = NULL;
95369a33c23SHans Rosenfeld ASSERT3U(qp->nq_active_cmds, >, 0);
95469a33c23SHans Rosenfeld qp->nq_active_cmds--;
95569a33c23SHans Rosenfeld sema_v(&qp->nq_sema);
95669a33c23SHans Rosenfeld
95769a33c23SHans Rosenfeld ASSERT3P(cmd, !=, NULL);
95869a33c23SHans Rosenfeld ASSERT3P(cmd->nc_nvme, ==, nvme);
95969a33c23SHans Rosenfeld ASSERT3S(cmd->nc_sqe.sqe_cid, ==, cid);
96069a33c23SHans Rosenfeld
96169a33c23SHans Rosenfeld return (cmd);
96269a33c23SHans Rosenfeld }
96369a33c23SHans Rosenfeld
96469a33c23SHans Rosenfeld static nvme_cmd_t *
nvme_retrieve_cmd(nvme_t * nvme,nvme_qpair_t * qp)965788c47fcSHans Rosenfeld nvme_retrieve_cmd(nvme_t *nvme, nvme_qpair_t *qp)
966788c47fcSHans Rosenfeld {
967788c47fcSHans Rosenfeld nvme_reg_cqhdbl_t head = { 0 };
968788c47fcSHans Rosenfeld
969788c47fcSHans Rosenfeld nvme_cqe_t *cqe;
970788c47fcSHans Rosenfeld nvme_cmd_t *cmd;
971788c47fcSHans Rosenfeld
972788c47fcSHans Rosenfeld (void) ddi_dma_sync(qp->nq_cqdma->nd_dmah, 0,
973788c47fcSHans Rosenfeld sizeof (nvme_cqe_t) * qp->nq_nentry, DDI_DMA_SYNC_FORKERNEL);
974788c47fcSHans Rosenfeld
9752ca49728SHans Rosenfeld mutex_enter(&qp->nq_mutex);
976788c47fcSHans Rosenfeld cqe = &qp->nq_cq[qp->nq_cqhead];
977788c47fcSHans Rosenfeld
978788c47fcSHans Rosenfeld /* Check phase tag of CQE. Hardware inverts it for new entries. */
9792ca49728SHans Rosenfeld if (cqe->cqe_sf.sf_p == qp->nq_phase) {
9802ca49728SHans Rosenfeld mutex_exit(&qp->nq_mutex);
981788c47fcSHans Rosenfeld return (NULL);
9822ca49728SHans Rosenfeld }
983788c47fcSHans Rosenfeld
984788c47fcSHans Rosenfeld ASSERT(nvme->n_ioq[cqe->cqe_sqid] == qp);
985788c47fcSHans Rosenfeld
98669a33c23SHans Rosenfeld cmd = nvme_unqueue_cmd(nvme, qp, cqe->cqe_cid);
987788c47fcSHans Rosenfeld
988788c47fcSHans Rosenfeld ASSERT(cmd->nc_sqid == cqe->cqe_sqid);
989788c47fcSHans Rosenfeld bcopy(cqe, &cmd->nc_cqe, sizeof (nvme_cqe_t));
990788c47fcSHans Rosenfeld
991788c47fcSHans Rosenfeld qp->nq_sqhead = cqe->cqe_sqhd;
992788c47fcSHans Rosenfeld
993788c47fcSHans Rosenfeld head.b.cqhdbl_cqh = qp->nq_cqhead = (qp->nq_cqhead + 1) % qp->nq_nentry;
994788c47fcSHans Rosenfeld
995788c47fcSHans Rosenfeld /* Toggle phase on wrap-around. */
996788c47fcSHans Rosenfeld if (qp->nq_cqhead == 0)
997788c47fcSHans Rosenfeld qp->nq_phase = qp->nq_phase ? 0 : 1;
998788c47fcSHans Rosenfeld
999788c47fcSHans Rosenfeld nvme_put32(cmd->nc_nvme, qp->nq_cqhdbl, head.r);
10002ca49728SHans Rosenfeld mutex_exit(&qp->nq_mutex);
1001788c47fcSHans Rosenfeld
1002788c47fcSHans Rosenfeld return (cmd);
1003788c47fcSHans Rosenfeld }
1004788c47fcSHans Rosenfeld
1005788c47fcSHans Rosenfeld static int
nvme_check_unknown_cmd_status(nvme_cmd_t * cmd)1006788c47fcSHans Rosenfeld nvme_check_unknown_cmd_status(nvme_cmd_t *cmd)
1007788c47fcSHans Rosenfeld {
1008788c47fcSHans Rosenfeld nvme_cqe_t *cqe = &cmd->nc_cqe;
1009788c47fcSHans Rosenfeld
1010788c47fcSHans Rosenfeld dev_err(cmd->nc_nvme->n_dip, CE_WARN,
1011788c47fcSHans Rosenfeld "!unknown command status received: opc = %x, sqid = %d, cid = %d, "
1012788c47fcSHans Rosenfeld "sc = %x, sct = %x, dnr = %d, m = %d", cmd->nc_sqe.sqe_opc,
1013788c47fcSHans Rosenfeld cqe->cqe_sqid, cqe->cqe_cid, cqe->cqe_sf.sf_sc, cqe->cqe_sf.sf_sct,
1014788c47fcSHans Rosenfeld cqe->cqe_sf.sf_dnr, cqe->cqe_sf.sf_m);
1015788c47fcSHans Rosenfeld
1016032cbfebSHans Rosenfeld if (cmd->nc_xfer != NULL)
10174ba35b4bSHans Rosenfeld bd_error(cmd->nc_xfer, BD_ERR_ILLRQ);
10184ba35b4bSHans Rosenfeld
1019788c47fcSHans Rosenfeld if (cmd->nc_nvme->n_strict_version) {
1020788c47fcSHans Rosenfeld cmd->nc_nvme->n_dead = B_TRUE;
1021788c47fcSHans Rosenfeld ddi_fm_service_impact(cmd->nc_nvme->n_dip, DDI_SERVICE_LOST);
1022788c47fcSHans Rosenfeld }
1023788c47fcSHans Rosenfeld
1024788c47fcSHans Rosenfeld return (EIO);
1025788c47fcSHans Rosenfeld }
1026788c47fcSHans Rosenfeld
1027788c47fcSHans Rosenfeld static int
nvme_check_vendor_cmd_status(nvme_cmd_t * cmd)1028788c47fcSHans Rosenfeld nvme_check_vendor_cmd_status(nvme_cmd_t *cmd)
1029788c47fcSHans Rosenfeld {
1030788c47fcSHans Rosenfeld nvme_cqe_t *cqe = &cmd->nc_cqe;
1031788c47fcSHans Rosenfeld
1032788c47fcSHans Rosenfeld dev_err(cmd->nc_nvme->n_dip, CE_WARN,
1033788c47fcSHans Rosenfeld "!unknown command status received: opc = %x, sqid = %d, cid = %d, "
1034788c47fcSHans Rosenfeld "sc = %x, sct = %x, dnr = %d, m = %d", cmd->nc_sqe.sqe_opc,
1035788c47fcSHans Rosenfeld cqe->cqe_sqid, cqe->cqe_cid, cqe->cqe_sf.sf_sc, cqe->cqe_sf.sf_sct,
1036788c47fcSHans Rosenfeld cqe->cqe_sf.sf_dnr, cqe->cqe_sf.sf_m);
10370fe6d382SPete Shephard if (!cmd->nc_nvme->n_ignore_unknown_vendor_status) {
1038788c47fcSHans Rosenfeld cmd->nc_nvme->n_dead = B_TRUE;
1039788c47fcSHans Rosenfeld ddi_fm_service_impact(cmd->nc_nvme->n_dip, DDI_SERVICE_LOST);
1040788c47fcSHans Rosenfeld }
1041788c47fcSHans Rosenfeld
1042788c47fcSHans Rosenfeld return (EIO);
1043788c47fcSHans Rosenfeld }
1044788c47fcSHans Rosenfeld
1045788c47fcSHans Rosenfeld static int
nvme_check_integrity_cmd_status(nvme_cmd_t * cmd)1046788c47fcSHans Rosenfeld nvme_check_integrity_cmd_status(nvme_cmd_t *cmd)
1047788c47fcSHans Rosenfeld {
1048788c47fcSHans Rosenfeld nvme_cqe_t *cqe = &cmd->nc_cqe;
1049788c47fcSHans Rosenfeld
1050788c47fcSHans Rosenfeld switch (cqe->cqe_sf.sf_sc) {
1051788c47fcSHans Rosenfeld case NVME_CQE_SC_INT_NVM_WRITE:
1052788c47fcSHans Rosenfeld /* write fail */
1053788c47fcSHans Rosenfeld /* TODO: post ereport */
1054032cbfebSHans Rosenfeld if (cmd->nc_xfer != NULL)
10554ba35b4bSHans Rosenfeld bd_error(cmd->nc_xfer, BD_ERR_MEDIA);
1056788c47fcSHans Rosenfeld return (EIO);
1057788c47fcSHans Rosenfeld
1058788c47fcSHans Rosenfeld case NVME_CQE_SC_INT_NVM_READ:
1059788c47fcSHans Rosenfeld /* read fail */
1060788c47fcSHans Rosenfeld /* TODO: post ereport */
1061032cbfebSHans Rosenfeld if (cmd->nc_xfer != NULL)
10624ba35b4bSHans Rosenfeld bd_error(cmd->nc_xfer, BD_ERR_MEDIA);
1063788c47fcSHans Rosenfeld return (EIO);
1064788c47fcSHans Rosenfeld
1065788c47fcSHans Rosenfeld default:
1066788c47fcSHans Rosenfeld return (nvme_check_unknown_cmd_status(cmd));
1067788c47fcSHans Rosenfeld }
1068788c47fcSHans Rosenfeld }
1069788c47fcSHans Rosenfeld
1070788c47fcSHans Rosenfeld static int
nvme_check_generic_cmd_status(nvme_cmd_t * cmd)1071788c47fcSHans Rosenfeld nvme_check_generic_cmd_status(nvme_cmd_t *cmd)
1072788c47fcSHans Rosenfeld {
1073788c47fcSHans Rosenfeld nvme_cqe_t *cqe = &cmd->nc_cqe;
1074788c47fcSHans Rosenfeld
1075788c47fcSHans Rosenfeld switch (cqe->cqe_sf.sf_sc) {
1076788c47fcSHans Rosenfeld case NVME_CQE_SC_GEN_SUCCESS:
1077788c47fcSHans Rosenfeld return (0);
1078788c47fcSHans Rosenfeld
1079788c47fcSHans Rosenfeld /*
1080788c47fcSHans Rosenfeld * Errors indicating a bug in the driver should cause a panic.
1081788c47fcSHans Rosenfeld */
1082788c47fcSHans Rosenfeld case NVME_CQE_SC_GEN_INV_OPC:
1083788c47fcSHans Rosenfeld /* Invalid Command Opcode */
1084788c47fcSHans Rosenfeld dev_err(cmd->nc_nvme->n_dip, CE_PANIC, "programming error: "
1085788c47fcSHans Rosenfeld "invalid opcode in cmd %p", (void *)cmd);
1086788c47fcSHans Rosenfeld return (0);
1087788c47fcSHans Rosenfeld
1088788c47fcSHans Rosenfeld case NVME_CQE_SC_GEN_INV_FLD:
1089788c47fcSHans Rosenfeld /* Invalid Field in Command */
1090032cbfebSHans Rosenfeld if (!cmd->nc_dontpanic)
1091032cbfebSHans Rosenfeld dev_err(cmd->nc_nvme->n_dip, CE_PANIC,
1092032cbfebSHans Rosenfeld "programming error: invalid field in cmd %p",
1093032cbfebSHans Rosenfeld (void *)cmd);
1094032cbfebSHans Rosenfeld return (EIO);
1095788c47fcSHans Rosenfeld
1096788c47fcSHans Rosenfeld case NVME_CQE_SC_GEN_ID_CNFL:
1097788c47fcSHans Rosenfeld /* Command ID Conflict */
1098788c47fcSHans Rosenfeld dev_err(cmd->nc_nvme->n_dip, CE_PANIC, "programming error: "
1099788c47fcSHans Rosenfeld "cmd ID conflict in cmd %p", (void *)cmd);
1100788c47fcSHans Rosenfeld return (0);
1101788c47fcSHans Rosenfeld
1102788c47fcSHans Rosenfeld case NVME_CQE_SC_GEN_INV_NS:
1103788c47fcSHans Rosenfeld /* Invalid Namespace or Format */
1104032cbfebSHans Rosenfeld if (!cmd->nc_dontpanic)
1105032cbfebSHans Rosenfeld dev_err(cmd->nc_nvme->n_dip, CE_PANIC,
1106032cbfebSHans Rosenfeld "programming error: " "invalid NS/format in cmd %p",
1107032cbfebSHans Rosenfeld (void *)cmd);
1108032cbfebSHans Rosenfeld return (EINVAL);
1109788c47fcSHans Rosenfeld
1110788c47fcSHans Rosenfeld case NVME_CQE_SC_GEN_NVM_LBA_RANGE:
1111788c47fcSHans Rosenfeld /* LBA Out Of Range */
1112788c47fcSHans Rosenfeld dev_err(cmd->nc_nvme->n_dip, CE_PANIC, "programming error: "
1113788c47fcSHans Rosenfeld "LBA out of range in cmd %p", (void *)cmd);
1114788c47fcSHans Rosenfeld return (0);
1115788c47fcSHans Rosenfeld
1116788c47fcSHans Rosenfeld /*
1117788c47fcSHans Rosenfeld * Non-fatal errors, handle gracefully.
1118788c47fcSHans Rosenfeld */
1119788c47fcSHans Rosenfeld case NVME_CQE_SC_GEN_DATA_XFR_ERR:
1120788c47fcSHans Rosenfeld /* Data Transfer Error (DMA) */
1121788c47fcSHans Rosenfeld /* TODO: post ereport */
1122788c47fcSHans Rosenfeld atomic_inc_32(&cmd->nc_nvme->n_data_xfr_err);
1123032cbfebSHans Rosenfeld if (cmd->nc_xfer != NULL)
11244ba35b4bSHans Rosenfeld bd_error(cmd->nc_xfer, BD_ERR_NTRDY);
1125788c47fcSHans Rosenfeld return (EIO);
1126788c47fcSHans Rosenfeld
1127788c47fcSHans Rosenfeld case NVME_CQE_SC_GEN_INTERNAL_ERR:
1128788c47fcSHans Rosenfeld /*
1129788c47fcSHans Rosenfeld * Internal Error. The spec (v1.0, section 4.5.1.2) says
1130788c47fcSHans Rosenfeld * detailed error information is returned as async event,
1131788c47fcSHans Rosenfeld * so we pretty much ignore the error here and handle it
1132788c47fcSHans Rosenfeld * in the async event handler.
1133788c47fcSHans Rosenfeld */
1134788c47fcSHans Rosenfeld atomic_inc_32(&cmd->nc_nvme->n_internal_err);
1135032cbfebSHans Rosenfeld if (cmd->nc_xfer != NULL)
11364ba35b4bSHans Rosenfeld bd_error(cmd->nc_xfer, BD_ERR_NTRDY);
1137788c47fcSHans Rosenfeld return (EIO);
1138788c47fcSHans Rosenfeld
1139788c47fcSHans Rosenfeld case NVME_CQE_SC_GEN_ABORT_REQUEST:
1140788c47fcSHans Rosenfeld /*
1141788c47fcSHans Rosenfeld * Command Abort Requested. This normally happens only when a
1142788c47fcSHans Rosenfeld * command times out.
1143788c47fcSHans Rosenfeld */
1144788c47fcSHans Rosenfeld /* TODO: post ereport or change blkdev to handle this? */
1145788c47fcSHans Rosenfeld atomic_inc_32(&cmd->nc_nvme->n_abort_rq_err);
1146788c47fcSHans Rosenfeld return (ECANCELED);
1147788c47fcSHans Rosenfeld
1148788c47fcSHans Rosenfeld case NVME_CQE_SC_GEN_ABORT_PWRLOSS:
1149788c47fcSHans Rosenfeld /* Command Aborted due to Power Loss Notification */
1150788c47fcSHans Rosenfeld ddi_fm_service_impact(cmd->nc_nvme->n_dip, DDI_SERVICE_LOST);
1151788c47fcSHans Rosenfeld cmd->nc_nvme->n_dead = B_TRUE;
1152788c47fcSHans Rosenfeld return (EIO);
1153788c47fcSHans Rosenfeld
1154788c47fcSHans Rosenfeld case NVME_CQE_SC_GEN_ABORT_SQ_DEL:
1155788c47fcSHans Rosenfeld /* Command Aborted due to SQ Deletion */
1156788c47fcSHans Rosenfeld atomic_inc_32(&cmd->nc_nvme->n_abort_sq_del);
1157788c47fcSHans Rosenfeld return (EIO);
1158788c47fcSHans Rosenfeld
1159788c47fcSHans Rosenfeld case NVME_CQE_SC_GEN_NVM_CAP_EXC:
1160788c47fcSHans Rosenfeld /* Capacity Exceeded */
1161788c47fcSHans Rosenfeld atomic_inc_32(&cmd->nc_nvme->n_nvm_cap_exc);
1162032cbfebSHans Rosenfeld if (cmd->nc_xfer != NULL)
11634ba35b4bSHans Rosenfeld bd_error(cmd->nc_xfer, BD_ERR_MEDIA);
1164788c47fcSHans Rosenfeld return (EIO);
1165788c47fcSHans Rosenfeld
1166788c47fcSHans Rosenfeld case NVME_CQE_SC_GEN_NVM_NS_NOTRDY:
1167788c47fcSHans Rosenfeld /* Namespace Not Ready */
1168788c47fcSHans Rosenfeld atomic_inc_32(&cmd->nc_nvme->n_nvm_ns_notrdy);
1169032cbfebSHans Rosenfeld if (cmd->nc_xfer != NULL)
11704ba35b4bSHans Rosenfeld bd_error(cmd->nc_xfer, BD_ERR_NTRDY);
1171788c47fcSHans Rosenfeld return (EIO);
1172788c47fcSHans Rosenfeld
1173788c47fcSHans Rosenfeld default:
1174788c47fcSHans Rosenfeld return (nvme_check_unknown_cmd_status(cmd));
1175788c47fcSHans Rosenfeld }
1176788c47fcSHans Rosenfeld }
1177788c47fcSHans Rosenfeld
1178788c47fcSHans Rosenfeld static int
nvme_check_specific_cmd_status(nvme_cmd_t * cmd)1179788c47fcSHans Rosenfeld nvme_check_specific_cmd_status(nvme_cmd_t *cmd)
1180788c47fcSHans Rosenfeld {
1181788c47fcSHans Rosenfeld nvme_cqe_t *cqe = &cmd->nc_cqe;
1182788c47fcSHans Rosenfeld
1183788c47fcSHans Rosenfeld switch (cqe->cqe_sf.sf_sc) {
1184788c47fcSHans Rosenfeld case NVME_CQE_SC_SPC_INV_CQ:
1185788c47fcSHans Rosenfeld /* Completion Queue Invalid */
1186788c47fcSHans Rosenfeld ASSERT(cmd->nc_sqe.sqe_opc == NVME_OPC_CREATE_SQUEUE);
1187788c47fcSHans Rosenfeld atomic_inc_32(&cmd->nc_nvme->n_inv_cq_err);
1188788c47fcSHans Rosenfeld return (EINVAL);
1189788c47fcSHans Rosenfeld
1190788c47fcSHans Rosenfeld case NVME_CQE_SC_SPC_INV_QID:
1191788c47fcSHans Rosenfeld /* Invalid Queue Identifier */
1192788c47fcSHans Rosenfeld ASSERT(cmd->nc_sqe.sqe_opc == NVME_OPC_CREATE_SQUEUE ||
1193788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_opc == NVME_OPC_DELETE_SQUEUE ||
1194788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_opc == NVME_OPC_CREATE_CQUEUE ||
1195788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_opc == NVME_OPC_DELETE_CQUEUE);
1196788c47fcSHans Rosenfeld atomic_inc_32(&cmd->nc_nvme->n_inv_qid_err);
1197788c47fcSHans Rosenfeld return (EINVAL);
1198788c47fcSHans Rosenfeld
1199788c47fcSHans Rosenfeld case NVME_CQE_SC_SPC_MAX_QSZ_EXC:
1200788c47fcSHans Rosenfeld /* Max Queue Size Exceeded */
1201788c47fcSHans Rosenfeld ASSERT(cmd->nc_sqe.sqe_opc == NVME_OPC_CREATE_SQUEUE ||
1202788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_opc == NVME_OPC_CREATE_CQUEUE);
1203788c47fcSHans Rosenfeld atomic_inc_32(&cmd->nc_nvme->n_max_qsz_exc);
1204788c47fcSHans Rosenfeld return (EINVAL);
1205788c47fcSHans Rosenfeld
1206788c47fcSHans Rosenfeld case NVME_CQE_SC_SPC_ABRT_CMD_EXC:
1207788c47fcSHans Rosenfeld /* Abort Command Limit Exceeded */
1208788c47fcSHans Rosenfeld ASSERT(cmd->nc_sqe.sqe_opc == NVME_OPC_ABORT);
1209788c47fcSHans Rosenfeld dev_err(cmd->nc_nvme->n_dip, CE_PANIC, "programming error: "
1210788c47fcSHans Rosenfeld "abort command limit exceeded in cmd %p", (void *)cmd);
1211788c47fcSHans Rosenfeld return (0);
1212788c47fcSHans Rosenfeld
1213788c47fcSHans Rosenfeld case NVME_CQE_SC_SPC_ASYNC_EVREQ_EXC:
1214788c47fcSHans Rosenfeld /* Async Event Request Limit Exceeded */
1215788c47fcSHans Rosenfeld ASSERT(cmd->nc_sqe.sqe_opc == NVME_OPC_ASYNC_EVENT);
1216788c47fcSHans Rosenfeld dev_err(cmd->nc_nvme->n_dip, CE_PANIC, "programming error: "
1217788c47fcSHans Rosenfeld "async event request limit exceeded in cmd %p",
1218788c47fcSHans Rosenfeld (void *)cmd);
1219788c47fcSHans Rosenfeld return (0);
1220788c47fcSHans Rosenfeld
1221788c47fcSHans Rosenfeld case NVME_CQE_SC_SPC_INV_INT_VECT:
1222788c47fcSHans Rosenfeld /* Invalid Interrupt Vector */
1223788c47fcSHans Rosenfeld ASSERT(cmd->nc_sqe.sqe_opc == NVME_OPC_CREATE_CQUEUE);
1224788c47fcSHans Rosenfeld atomic_inc_32(&cmd->nc_nvme->n_inv_int_vect);
1225788c47fcSHans Rosenfeld return (EINVAL);
1226788c47fcSHans Rosenfeld
1227788c47fcSHans Rosenfeld case NVME_CQE_SC_SPC_INV_LOG_PAGE:
1228788c47fcSHans Rosenfeld /* Invalid Log Page */
1229788c47fcSHans Rosenfeld ASSERT(cmd->nc_sqe.sqe_opc == NVME_OPC_GET_LOG_PAGE);
1230788c47fcSHans Rosenfeld atomic_inc_32(&cmd->nc_nvme->n_inv_log_page);
1231788c47fcSHans Rosenfeld return (EINVAL);
1232788c47fcSHans Rosenfeld
1233788c47fcSHans Rosenfeld case NVME_CQE_SC_SPC_INV_FORMAT:
1234788c47fcSHans Rosenfeld /* Invalid Format */
1235788c47fcSHans Rosenfeld ASSERT(cmd->nc_sqe.sqe_opc == NVME_OPC_NVM_FORMAT);
1236788c47fcSHans Rosenfeld atomic_inc_32(&cmd->nc_nvme->n_inv_format);
1237032cbfebSHans Rosenfeld if (cmd->nc_xfer != NULL)
12384ba35b4bSHans Rosenfeld bd_error(cmd->nc_xfer, BD_ERR_ILLRQ);
1239788c47fcSHans Rosenfeld return (EINVAL);
1240788c47fcSHans Rosenfeld
1241788c47fcSHans Rosenfeld case NVME_CQE_SC_SPC_INV_Q_DEL:
1242788c47fcSHans Rosenfeld /* Invalid Queue Deletion */
1243788c47fcSHans Rosenfeld ASSERT(cmd->nc_sqe.sqe_opc == NVME_OPC_DELETE_CQUEUE);
1244788c47fcSHans Rosenfeld atomic_inc_32(&cmd->nc_nvme->n_inv_q_del);
1245788c47fcSHans Rosenfeld return (EINVAL);
1246788c47fcSHans Rosenfeld
1247788c47fcSHans Rosenfeld case NVME_CQE_SC_SPC_NVM_CNFL_ATTR:
1248788c47fcSHans Rosenfeld /* Conflicting Attributes */
1249788c47fcSHans Rosenfeld ASSERT(cmd->nc_sqe.sqe_opc == NVME_OPC_NVM_DSET_MGMT ||
1250788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_opc == NVME_OPC_NVM_READ ||
1251788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_opc == NVME_OPC_NVM_WRITE);
1252788c47fcSHans Rosenfeld atomic_inc_32(&cmd->nc_nvme->n_cnfl_attr);
1253032cbfebSHans Rosenfeld if (cmd->nc_xfer != NULL)
12544ba35b4bSHans Rosenfeld bd_error(cmd->nc_xfer, BD_ERR_ILLRQ);
1255788c47fcSHans Rosenfeld return (EINVAL);
1256788c47fcSHans Rosenfeld
1257788c47fcSHans Rosenfeld case NVME_CQE_SC_SPC_NVM_INV_PROT:
1258788c47fcSHans Rosenfeld /* Invalid Protection Information */
1259788c47fcSHans Rosenfeld ASSERT(cmd->nc_sqe.sqe_opc == NVME_OPC_NVM_COMPARE ||
1260788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_opc == NVME_OPC_NVM_READ ||
1261788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_opc == NVME_OPC_NVM_WRITE);
1262788c47fcSHans Rosenfeld atomic_inc_32(&cmd->nc_nvme->n_inv_prot);
1263032cbfebSHans Rosenfeld if (cmd->nc_xfer != NULL)
12644ba35b4bSHans Rosenfeld bd_error(cmd->nc_xfer, BD_ERR_ILLRQ);
1265788c47fcSHans Rosenfeld return (EINVAL);
1266788c47fcSHans Rosenfeld
1267788c47fcSHans Rosenfeld case NVME_CQE_SC_SPC_NVM_READONLY:
1268788c47fcSHans Rosenfeld /* Write to Read Only Range */
1269788c47fcSHans Rosenfeld ASSERT(cmd->nc_sqe.sqe_opc == NVME_OPC_NVM_WRITE);
1270788c47fcSHans Rosenfeld atomic_inc_32(&cmd->nc_nvme->n_readonly);
1271032cbfebSHans Rosenfeld if (cmd->nc_xfer != NULL)
12724ba35b4bSHans Rosenfeld bd_error(cmd->nc_xfer, BD_ERR_ILLRQ);
1273788c47fcSHans Rosenfeld return (EROFS);
1274788c47fcSHans Rosenfeld
1275788c47fcSHans Rosenfeld default:
1276788c47fcSHans Rosenfeld return (nvme_check_unknown_cmd_status(cmd));
1277788c47fcSHans Rosenfeld }
1278788c47fcSHans Rosenfeld }
1279788c47fcSHans Rosenfeld
1280788c47fcSHans Rosenfeld static inline int
nvme_check_cmd_status(nvme_cmd_t * cmd)1281788c47fcSHans Rosenfeld nvme_check_cmd_status(nvme_cmd_t *cmd)
1282788c47fcSHans Rosenfeld {
1283788c47fcSHans Rosenfeld nvme_cqe_t *cqe = &cmd->nc_cqe;
1284788c47fcSHans Rosenfeld
128569a33c23SHans Rosenfeld /*
128669a33c23SHans Rosenfeld * Take a shortcut if the controller is dead, or if
128769a33c23SHans Rosenfeld * command status indicates no error.
128869a33c23SHans Rosenfeld */
128969a33c23SHans Rosenfeld if (cmd->nc_nvme->n_dead)
129069a33c23SHans Rosenfeld return (EIO);
129169a33c23SHans Rosenfeld
1292788c47fcSHans Rosenfeld if (cqe->cqe_sf.sf_sct == NVME_CQE_SCT_GENERIC &&
1293788c47fcSHans Rosenfeld cqe->cqe_sf.sf_sc == NVME_CQE_SC_GEN_SUCCESS)
1294788c47fcSHans Rosenfeld return (0);
1295788c47fcSHans Rosenfeld
1296788c47fcSHans Rosenfeld if (cqe->cqe_sf.sf_sct == NVME_CQE_SCT_GENERIC)
1297788c47fcSHans Rosenfeld return (nvme_check_generic_cmd_status(cmd));
1298788c47fcSHans Rosenfeld else if (cqe->cqe_sf.sf_sct == NVME_CQE_SCT_SPECIFIC)
1299788c47fcSHans Rosenfeld return (nvme_check_specific_cmd_status(cmd));
1300788c47fcSHans Rosenfeld else if (cqe->cqe_sf.sf_sct == NVME_CQE_SCT_INTEGRITY)
1301788c47fcSHans Rosenfeld return (nvme_check_integrity_cmd_status(cmd));
1302788c47fcSHans Rosenfeld else if (cqe->cqe_sf.sf_sct == NVME_CQE_SCT_VENDOR)
1303788c47fcSHans Rosenfeld return (nvme_check_vendor_cmd_status(cmd));
1304788c47fcSHans Rosenfeld
1305788c47fcSHans Rosenfeld return (nvme_check_unknown_cmd_status(cmd));
1306788c47fcSHans Rosenfeld }
1307788c47fcSHans Rosenfeld
130869a33c23SHans Rosenfeld static int
nvme_abort_cmd(nvme_cmd_t * abort_cmd,uint_t sec)130969a33c23SHans Rosenfeld nvme_abort_cmd(nvme_cmd_t *abort_cmd, uint_t sec)
1310788c47fcSHans Rosenfeld {
1311788c47fcSHans Rosenfeld nvme_t *nvme = abort_cmd->nc_nvme;
1312788c47fcSHans Rosenfeld nvme_cmd_t *cmd = nvme_alloc_cmd(nvme, KM_SLEEP);
1313788c47fcSHans Rosenfeld nvme_abort_cmd_t ac = { 0 };
131469a33c23SHans Rosenfeld int ret = 0;
1315788c47fcSHans Rosenfeld
1316788c47fcSHans Rosenfeld sema_p(&nvme->n_abort_sema);
1317788c47fcSHans Rosenfeld
1318788c47fcSHans Rosenfeld ac.b.ac_cid = abort_cmd->nc_sqe.sqe_cid;
1319788c47fcSHans Rosenfeld ac.b.ac_sqid = abort_cmd->nc_sqid;
1320788c47fcSHans Rosenfeld
1321788c47fcSHans Rosenfeld cmd->nc_sqid = 0;
1322788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_opc = NVME_OPC_ABORT;
1323788c47fcSHans Rosenfeld cmd->nc_callback = nvme_wakeup_cmd;
1324788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_cdw10 = ac.r;
1325788c47fcSHans Rosenfeld
1326788c47fcSHans Rosenfeld /*
1327788c47fcSHans Rosenfeld * Send the ABORT to the hardware. The ABORT command will return _after_
132869a33c23SHans Rosenfeld * the aborted command has completed (aborted or otherwise), but since
132969a33c23SHans Rosenfeld * we still hold the aborted command's mutex its callback hasn't been
133069a33c23SHans Rosenfeld * processed yet.
1331788c47fcSHans Rosenfeld */
133269a33c23SHans Rosenfeld nvme_admin_cmd(cmd, sec);
1333788c47fcSHans Rosenfeld sema_v(&nvme->n_abort_sema);
1334788c47fcSHans Rosenfeld
133569a33c23SHans Rosenfeld if ((ret = nvme_check_cmd_status(cmd)) != 0) {
1336788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN,
1337788c47fcSHans Rosenfeld "!ABORT failed with sct = %x, sc = %x",
1338788c47fcSHans Rosenfeld cmd->nc_cqe.cqe_sf.sf_sct, cmd->nc_cqe.cqe_sf.sf_sc);
1339788c47fcSHans Rosenfeld atomic_inc_32(&nvme->n_abort_failed);
1340788c47fcSHans Rosenfeld } else {
134169a33c23SHans Rosenfeld dev_err(nvme->n_dip, CE_WARN,
134269a33c23SHans Rosenfeld "!ABORT of command %d/%d %ssuccessful",
134369a33c23SHans Rosenfeld abort_cmd->nc_sqe.sqe_cid, abort_cmd->nc_sqid,
134469a33c23SHans Rosenfeld cmd->nc_cqe.cqe_dw0 & 1 ? "un" : "");
134569a33c23SHans Rosenfeld if ((cmd->nc_cqe.cqe_dw0 & 1) == 0)
1346788c47fcSHans Rosenfeld atomic_inc_32(&nvme->n_cmd_aborted);
1347788c47fcSHans Rosenfeld }
1348788c47fcSHans Rosenfeld
1349788c47fcSHans Rosenfeld nvme_free_cmd(cmd);
135069a33c23SHans Rosenfeld return (ret);
1351788c47fcSHans Rosenfeld }
1352788c47fcSHans Rosenfeld
1353788c47fcSHans Rosenfeld /*
1354788c47fcSHans Rosenfeld * nvme_wait_cmd -- wait for command completion or timeout
1355788c47fcSHans Rosenfeld *
1356788c47fcSHans Rosenfeld * In case of a serious error or a timeout of the abort command the hardware
1357788c47fcSHans Rosenfeld * will be declared dead and FMA will be notified.
1358788c47fcSHans Rosenfeld */
135969a33c23SHans Rosenfeld static void
nvme_wait_cmd(nvme_cmd_t * cmd,uint_t sec)13606f37ecd5SHans Rosenfeld nvme_wait_cmd(nvme_cmd_t *cmd, uint_t sec)
1361788c47fcSHans Rosenfeld {
13626f37ecd5SHans Rosenfeld clock_t timeout = ddi_get_lbolt() + drv_usectohz(sec * MICROSEC);
1363788c47fcSHans Rosenfeld nvme_t *nvme = cmd->nc_nvme;
1364788c47fcSHans Rosenfeld nvme_reg_csts_t csts;
136569a33c23SHans Rosenfeld nvme_qpair_t *qp;
1366788c47fcSHans Rosenfeld
1367788c47fcSHans Rosenfeld ASSERT(mutex_owned(&cmd->nc_mutex));
1368788c47fcSHans Rosenfeld
1369788c47fcSHans Rosenfeld while (!cmd->nc_completed) {
1370788c47fcSHans Rosenfeld if (cv_timedwait(&cmd->nc_cv, &cmd->nc_mutex, timeout) == -1)
1371788c47fcSHans Rosenfeld break;
1372788c47fcSHans Rosenfeld }
1373788c47fcSHans Rosenfeld
1374788c47fcSHans Rosenfeld if (cmd->nc_completed)
137569a33c23SHans Rosenfeld return;
1376788c47fcSHans Rosenfeld
1377788c47fcSHans Rosenfeld /*
137869a33c23SHans Rosenfeld * The command timed out.
137969a33c23SHans Rosenfeld *
1380788c47fcSHans Rosenfeld * Check controller for fatal status, any errors associated with the
1381788c47fcSHans Rosenfeld * register or DMA handle, or for a double timeout (abort command timed
1382788c47fcSHans Rosenfeld * out). If necessary log a warning and call FMA.
1383788c47fcSHans Rosenfeld */
1384788c47fcSHans Rosenfeld csts.r = nvme_get32(nvme, NVME_REG_CSTS);
138569a33c23SHans Rosenfeld dev_err(nvme->n_dip, CE_WARN, "!command %d/%d timeout, "
138669a33c23SHans Rosenfeld "OPC = %x, CFS = %d", cmd->nc_sqe.sqe_cid, cmd->nc_sqid,
138769a33c23SHans Rosenfeld cmd->nc_sqe.sqe_opc, csts.b.csts_cfs);
1388788c47fcSHans Rosenfeld atomic_inc_32(&nvme->n_cmd_timeout);
1389788c47fcSHans Rosenfeld
1390788c47fcSHans Rosenfeld if (csts.b.csts_cfs ||
1391788c47fcSHans Rosenfeld nvme_check_regs_hdl(nvme) ||
1392788c47fcSHans Rosenfeld nvme_check_dma_hdl(cmd->nc_dma) ||
1393788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_opc == NVME_OPC_ABORT) {
1394788c47fcSHans Rosenfeld ddi_fm_service_impact(nvme->n_dip, DDI_SERVICE_LOST);
1395788c47fcSHans Rosenfeld nvme->n_dead = B_TRUE;
139669a33c23SHans Rosenfeld } else if (nvme_abort_cmd(cmd, sec) == 0) {
1397788c47fcSHans Rosenfeld /*
139869a33c23SHans Rosenfeld * If the abort succeeded the command should complete
139969a33c23SHans Rosenfeld * immediately with an appropriate status.
1400788c47fcSHans Rosenfeld */
140169a33c23SHans Rosenfeld while (!cmd->nc_completed)
140269a33c23SHans Rosenfeld cv_wait(&cmd->nc_cv, &cmd->nc_mutex);
140369a33c23SHans Rosenfeld
140469a33c23SHans Rosenfeld return;
1405788c47fcSHans Rosenfeld }
1406788c47fcSHans Rosenfeld
140769a33c23SHans Rosenfeld qp = nvme->n_ioq[cmd->nc_sqid];
140869a33c23SHans Rosenfeld
140969a33c23SHans Rosenfeld mutex_enter(&qp->nq_mutex);
141069a33c23SHans Rosenfeld (void) nvme_unqueue_cmd(nvme, qp, cmd->nc_sqe.sqe_cid);
141169a33c23SHans Rosenfeld mutex_exit(&qp->nq_mutex);
141269a33c23SHans Rosenfeld
141369a33c23SHans Rosenfeld /*
141469a33c23SHans Rosenfeld * As we don't know what the presumed dead hardware might still do with
141569a33c23SHans Rosenfeld * the DMA memory, we'll put the command on the lost commands list if it
141669a33c23SHans Rosenfeld * has any DMA memory.
141769a33c23SHans Rosenfeld */
141869a33c23SHans Rosenfeld if (cmd->nc_dma != NULL) {
141969a33c23SHans Rosenfeld mutex_enter(&nvme_lc_mutex);
142069a33c23SHans Rosenfeld list_insert_head(&nvme_lost_cmds, cmd);
142169a33c23SHans Rosenfeld mutex_exit(&nvme_lc_mutex);
142269a33c23SHans Rosenfeld }
1423788c47fcSHans Rosenfeld }
1424788c47fcSHans Rosenfeld
1425788c47fcSHans Rosenfeld static void
nvme_wakeup_cmd(void * arg)1426788c47fcSHans Rosenfeld nvme_wakeup_cmd(void *arg)
1427788c47fcSHans Rosenfeld {
1428788c47fcSHans Rosenfeld nvme_cmd_t *cmd = arg;
1429788c47fcSHans Rosenfeld
1430788c47fcSHans Rosenfeld mutex_enter(&cmd->nc_mutex);
1431788c47fcSHans Rosenfeld cmd->nc_completed = B_TRUE;
1432788c47fcSHans Rosenfeld cv_signal(&cmd->nc_cv);
1433788c47fcSHans Rosenfeld mutex_exit(&cmd->nc_mutex);
1434788c47fcSHans Rosenfeld }
1435788c47fcSHans Rosenfeld
1436788c47fcSHans Rosenfeld static void
nvme_async_event_task(void * arg)1437788c47fcSHans Rosenfeld nvme_async_event_task(void *arg)
1438788c47fcSHans Rosenfeld {
1439788c47fcSHans Rosenfeld nvme_cmd_t *cmd = arg;
1440788c47fcSHans Rosenfeld nvme_t *nvme = cmd->nc_nvme;
1441788c47fcSHans Rosenfeld nvme_error_log_entry_t *error_log = NULL;
1442788c47fcSHans Rosenfeld nvme_health_log_t *health_log = NULL;
1443032cbfebSHans Rosenfeld size_t logsize = 0;
1444788c47fcSHans Rosenfeld nvme_async_event_t event;
1445788c47fcSHans Rosenfeld
1446788c47fcSHans Rosenfeld /*
1447788c47fcSHans Rosenfeld * Check for errors associated with the async request itself. The only
1448788c47fcSHans Rosenfeld * command-specific error is "async event limit exceeded", which
1449788c47fcSHans Rosenfeld * indicates a programming error in the driver and causes a panic in
1450788c47fcSHans Rosenfeld * nvme_check_cmd_status().
1451788c47fcSHans Rosenfeld *
1452788c47fcSHans Rosenfeld * Other possible errors are various scenarios where the async request
1453788c47fcSHans Rosenfeld * was aborted, or internal errors in the device. Internal errors are
1454788c47fcSHans Rosenfeld * reported to FMA, the command aborts need no special handling here.
1455788c47fcSHans Rosenfeld */
145669a33c23SHans Rosenfeld if (nvme_check_cmd_status(cmd) != 0) {
1457788c47fcSHans Rosenfeld dev_err(cmd->nc_nvme->n_dip, CE_WARN,
1458788c47fcSHans Rosenfeld "!async event request returned failure, sct = %x, "
1459788c47fcSHans Rosenfeld "sc = %x, dnr = %d, m = %d", cmd->nc_cqe.cqe_sf.sf_sct,
1460788c47fcSHans Rosenfeld cmd->nc_cqe.cqe_sf.sf_sc, cmd->nc_cqe.cqe_sf.sf_dnr,
1461788c47fcSHans Rosenfeld cmd->nc_cqe.cqe_sf.sf_m);
1462788c47fcSHans Rosenfeld
1463788c47fcSHans Rosenfeld if (cmd->nc_cqe.cqe_sf.sf_sct == NVME_CQE_SCT_GENERIC &&
1464788c47fcSHans Rosenfeld cmd->nc_cqe.cqe_sf.sf_sc == NVME_CQE_SC_GEN_INTERNAL_ERR) {
1465788c47fcSHans Rosenfeld cmd->nc_nvme->n_dead = B_TRUE;
1466788c47fcSHans Rosenfeld ddi_fm_service_impact(cmd->nc_nvme->n_dip,
1467788c47fcSHans Rosenfeld DDI_SERVICE_LOST);
1468788c47fcSHans Rosenfeld }
1469788c47fcSHans Rosenfeld nvme_free_cmd(cmd);
1470788c47fcSHans Rosenfeld return;
1471788c47fcSHans Rosenfeld }
1472788c47fcSHans Rosenfeld
1473788c47fcSHans Rosenfeld
1474788c47fcSHans Rosenfeld event.r = cmd->nc_cqe.cqe_dw0;
1475788c47fcSHans Rosenfeld
1476788c47fcSHans Rosenfeld /* Clear CQE and re-submit the async request. */
1477788c47fcSHans Rosenfeld bzero(&cmd->nc_cqe, sizeof (nvme_cqe_t));
1478ba636d1cSHans Rosenfeld nvme_submit_admin_cmd(nvme->n_adminq, cmd);
1479788c47fcSHans Rosenfeld
1480788c47fcSHans Rosenfeld switch (event.b.ae_type) {
1481788c47fcSHans Rosenfeld case NVME_ASYNC_TYPE_ERROR:
1482788c47fcSHans Rosenfeld if (event.b.ae_logpage == NVME_LOGPAGE_ERROR) {
1483032cbfebSHans Rosenfeld (void) nvme_get_logpage(nvme, (void **)&error_log,
1484032cbfebSHans Rosenfeld &logsize, event.b.ae_logpage);
1485788c47fcSHans Rosenfeld } else {
1486788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN, "!wrong logpage in "
1487788c47fcSHans Rosenfeld "async event reply: %d", event.b.ae_logpage);
1488788c47fcSHans Rosenfeld atomic_inc_32(&nvme->n_wrong_logpage);
1489788c47fcSHans Rosenfeld }
1490788c47fcSHans Rosenfeld
1491788c47fcSHans Rosenfeld switch (event.b.ae_info) {
1492788c47fcSHans Rosenfeld case NVME_ASYNC_ERROR_INV_SQ:
1493788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_PANIC, "programming error: "
1494788c47fcSHans Rosenfeld "invalid submission queue");
1495788c47fcSHans Rosenfeld return;
1496788c47fcSHans Rosenfeld
1497788c47fcSHans Rosenfeld case NVME_ASYNC_ERROR_INV_DBL:
1498788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_PANIC, "programming error: "
1499788c47fcSHans Rosenfeld "invalid doorbell write value");
1500788c47fcSHans Rosenfeld return;
1501788c47fcSHans Rosenfeld
1502788c47fcSHans Rosenfeld case NVME_ASYNC_ERROR_DIAGFAIL:
1503788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN, "!diagnostic failure");
1504788c47fcSHans Rosenfeld ddi_fm_service_impact(nvme->n_dip, DDI_SERVICE_LOST);
1505788c47fcSHans Rosenfeld nvme->n_dead = B_TRUE;
1506788c47fcSHans Rosenfeld atomic_inc_32(&nvme->n_diagfail_event);
1507788c47fcSHans Rosenfeld break;
1508788c47fcSHans Rosenfeld
1509788c47fcSHans Rosenfeld case NVME_ASYNC_ERROR_PERSISTENT:
1510788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN, "!persistent internal "
1511788c47fcSHans Rosenfeld "device error");
1512788c47fcSHans Rosenfeld ddi_fm_service_impact(nvme->n_dip, DDI_SERVICE_LOST);
1513788c47fcSHans Rosenfeld nvme->n_dead = B_TRUE;
1514788c47fcSHans Rosenfeld atomic_inc_32(&nvme->n_persistent_event);
1515788c47fcSHans Rosenfeld break;
1516788c47fcSHans Rosenfeld
1517788c47fcSHans Rosenfeld case NVME_ASYNC_ERROR_TRANSIENT:
1518788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN, "!transient internal "
1519788c47fcSHans Rosenfeld "device error");
1520788c47fcSHans Rosenfeld /* TODO: send ereport */
1521788c47fcSHans Rosenfeld atomic_inc_32(&nvme->n_transient_event);
1522788c47fcSHans Rosenfeld break;
1523788c47fcSHans Rosenfeld
1524788c47fcSHans Rosenfeld case NVME_ASYNC_ERROR_FW_LOAD:
1525788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN,
1526788c47fcSHans Rosenfeld "!firmware image load error");
1527788c47fcSHans Rosenfeld atomic_inc_32(&nvme->n_fw_load_event);
1528788c47fcSHans Rosenfeld break;
1529788c47fcSHans Rosenfeld }
1530788c47fcSHans Rosenfeld break;
1531788c47fcSHans Rosenfeld
1532788c47fcSHans Rosenfeld case NVME_ASYNC_TYPE_HEALTH:
1533788c47fcSHans Rosenfeld if (event.b.ae_logpage == NVME_LOGPAGE_HEALTH) {
1534032cbfebSHans Rosenfeld (void) nvme_get_logpage(nvme, (void **)&health_log,
1535032cbfebSHans Rosenfeld &logsize, event.b.ae_logpage, -1);
1536788c47fcSHans Rosenfeld } else {
1537788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN, "!wrong logpage in "
1538788c47fcSHans Rosenfeld "async event reply: %d", event.b.ae_logpage);
1539788c47fcSHans Rosenfeld atomic_inc_32(&nvme->n_wrong_logpage);
1540788c47fcSHans Rosenfeld }
1541788c47fcSHans Rosenfeld
1542788c47fcSHans Rosenfeld switch (event.b.ae_info) {
1543788c47fcSHans Rosenfeld case NVME_ASYNC_HEALTH_RELIABILITY:
1544788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN,
1545788c47fcSHans Rosenfeld "!device reliability compromised");
1546788c47fcSHans Rosenfeld /* TODO: send ereport */
1547788c47fcSHans Rosenfeld atomic_inc_32(&nvme->n_reliability_event);
1548788c47fcSHans Rosenfeld break;
1549788c47fcSHans Rosenfeld
1550788c47fcSHans Rosenfeld case NVME_ASYNC_HEALTH_TEMPERATURE:
1551788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN,
1552788c47fcSHans Rosenfeld "!temperature above threshold");
1553788c47fcSHans Rosenfeld /* TODO: send ereport */
1554788c47fcSHans Rosenfeld atomic_inc_32(&nvme->n_temperature_event);
1555788c47fcSHans Rosenfeld break;
1556788c47fcSHans Rosenfeld
1557788c47fcSHans Rosenfeld case NVME_ASYNC_HEALTH_SPARE:
1558788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN,
1559788c47fcSHans Rosenfeld "!spare space below threshold");
1560788c47fcSHans Rosenfeld /* TODO: send ereport */
1561788c47fcSHans Rosenfeld atomic_inc_32(&nvme->n_spare_event);
1562788c47fcSHans Rosenfeld break;
1563788c47fcSHans Rosenfeld }
1564788c47fcSHans Rosenfeld break;
1565788c47fcSHans Rosenfeld
1566788c47fcSHans Rosenfeld case NVME_ASYNC_TYPE_VENDOR:
1567788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN, "!vendor specific async event "
1568788c47fcSHans Rosenfeld "received, info = %x, logpage = %x", event.b.ae_info,
1569788c47fcSHans Rosenfeld event.b.ae_logpage);
1570788c47fcSHans Rosenfeld atomic_inc_32(&nvme->n_vendor_event);
1571788c47fcSHans Rosenfeld break;
1572788c47fcSHans Rosenfeld
1573788c47fcSHans Rosenfeld default:
1574788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN, "!unknown async event received, "
1575788c47fcSHans Rosenfeld "type = %x, info = %x, logpage = %x", event.b.ae_type,
1576788c47fcSHans Rosenfeld event.b.ae_info, event.b.ae_logpage);
1577788c47fcSHans Rosenfeld atomic_inc_32(&nvme->n_unknown_event);
1578788c47fcSHans Rosenfeld break;
1579788c47fcSHans Rosenfeld }
1580788c47fcSHans Rosenfeld
1581788c47fcSHans Rosenfeld if (error_log)
1582032cbfebSHans Rosenfeld kmem_free(error_log, logsize);
1583788c47fcSHans Rosenfeld
1584788c47fcSHans Rosenfeld if (health_log)
1585032cbfebSHans Rosenfeld kmem_free(health_log, logsize);
1586788c47fcSHans Rosenfeld }
1587788c47fcSHans Rosenfeld
158869a33c23SHans Rosenfeld static void
nvme_admin_cmd(nvme_cmd_t * cmd,int sec)15896f37ecd5SHans Rosenfeld nvme_admin_cmd(nvme_cmd_t *cmd, int sec)
1590788c47fcSHans Rosenfeld {
1591788c47fcSHans Rosenfeld mutex_enter(&cmd->nc_mutex);
1592ba636d1cSHans Rosenfeld nvme_submit_admin_cmd(cmd->nc_nvme->n_adminq, cmd);
159369a33c23SHans Rosenfeld nvme_wait_cmd(cmd, sec);
1594788c47fcSHans Rosenfeld mutex_exit(&cmd->nc_mutex);
1595788c47fcSHans Rosenfeld }
1596788c47fcSHans Rosenfeld
1597ba636d1cSHans Rosenfeld static void
nvme_async_event(nvme_t * nvme)1598788c47fcSHans Rosenfeld nvme_async_event(nvme_t *nvme)
1599788c47fcSHans Rosenfeld {
1600788c47fcSHans Rosenfeld nvme_cmd_t *cmd = nvme_alloc_cmd(nvme, KM_SLEEP);
1601788c47fcSHans Rosenfeld
1602788c47fcSHans Rosenfeld cmd->nc_sqid = 0;
1603788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_opc = NVME_OPC_ASYNC_EVENT;
1604788c47fcSHans Rosenfeld cmd->nc_callback = nvme_async_event_task;
1605788c47fcSHans Rosenfeld
1606ba636d1cSHans Rosenfeld nvme_submit_admin_cmd(nvme->n_adminq, cmd);
1607788c47fcSHans Rosenfeld }
1608788c47fcSHans Rosenfeld
1609032cbfebSHans Rosenfeld static int
nvme_format_nvm(nvme_t * nvme,uint32_t nsid,uint8_t lbaf,boolean_t ms,uint8_t pi,boolean_t pil,uint8_t ses)1610032cbfebSHans Rosenfeld nvme_format_nvm(nvme_t *nvme, uint32_t nsid, uint8_t lbaf, boolean_t ms,
1611032cbfebSHans Rosenfeld uint8_t pi, boolean_t pil, uint8_t ses)
1612788c47fcSHans Rosenfeld {
1613788c47fcSHans Rosenfeld nvme_cmd_t *cmd = nvme_alloc_cmd(nvme, KM_SLEEP);
1614032cbfebSHans Rosenfeld nvme_format_nvm_t format_nvm = { 0 };
1615032cbfebSHans Rosenfeld int ret;
1616032cbfebSHans Rosenfeld
1617032cbfebSHans Rosenfeld format_nvm.b.fm_lbaf = lbaf & 0xf;
1618032cbfebSHans Rosenfeld format_nvm.b.fm_ms = ms ? 1 : 0;
1619032cbfebSHans Rosenfeld format_nvm.b.fm_pi = pi & 0x7;
1620032cbfebSHans Rosenfeld format_nvm.b.fm_pil = pil ? 1 : 0;
1621032cbfebSHans Rosenfeld format_nvm.b.fm_ses = ses & 0x7;
1622032cbfebSHans Rosenfeld
1623032cbfebSHans Rosenfeld cmd->nc_sqid = 0;
1624032cbfebSHans Rosenfeld cmd->nc_callback = nvme_wakeup_cmd;
1625032cbfebSHans Rosenfeld cmd->nc_sqe.sqe_nsid = nsid;
1626032cbfebSHans Rosenfeld cmd->nc_sqe.sqe_opc = NVME_OPC_NVM_FORMAT;
1627032cbfebSHans Rosenfeld cmd->nc_sqe.sqe_cdw10 = format_nvm.r;
1628032cbfebSHans Rosenfeld
1629032cbfebSHans Rosenfeld /*
1630032cbfebSHans Rosenfeld * Some devices like Samsung SM951 don't allow formatting of all
1631032cbfebSHans Rosenfeld * namespaces in one command. Handle that gracefully.
1632032cbfebSHans Rosenfeld */
1633032cbfebSHans Rosenfeld if (nsid == (uint32_t)-1)
1634032cbfebSHans Rosenfeld cmd->nc_dontpanic = B_TRUE;
1635032cbfebSHans Rosenfeld
163669a33c23SHans Rosenfeld nvme_admin_cmd(cmd, nvme_format_cmd_timeout);
1637032cbfebSHans Rosenfeld
1638032cbfebSHans Rosenfeld if ((ret = nvme_check_cmd_status(cmd)) != 0) {
1639032cbfebSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN,
1640032cbfebSHans Rosenfeld "!FORMAT failed with sct = %x, sc = %x",
1641032cbfebSHans Rosenfeld cmd->nc_cqe.cqe_sf.sf_sct, cmd->nc_cqe.cqe_sf.sf_sc);
1642032cbfebSHans Rosenfeld }
1643032cbfebSHans Rosenfeld
1644032cbfebSHans Rosenfeld nvme_free_cmd(cmd);
1645032cbfebSHans Rosenfeld return (ret);
1646032cbfebSHans Rosenfeld }
1647032cbfebSHans Rosenfeld
1648032cbfebSHans Rosenfeld static int
nvme_get_logpage(nvme_t * nvme,void ** buf,size_t * bufsize,uint8_t logpage,...)1649032cbfebSHans Rosenfeld nvme_get_logpage(nvme_t *nvme, void **buf, size_t *bufsize, uint8_t logpage,
1650032cbfebSHans Rosenfeld ...)
1651032cbfebSHans Rosenfeld {
1652032cbfebSHans Rosenfeld nvme_cmd_t *cmd = nvme_alloc_cmd(nvme, KM_SLEEP);
16530fe6d382SPete Shephard nvme_getlogpage_t getlogpage = { 0 };
1654788c47fcSHans Rosenfeld va_list ap;
165569a33c23SHans Rosenfeld int ret;
1656788c47fcSHans Rosenfeld
1657788c47fcSHans Rosenfeld va_start(ap, logpage);
1658788c47fcSHans Rosenfeld
1659788c47fcSHans Rosenfeld cmd->nc_sqid = 0;
1660788c47fcSHans Rosenfeld cmd->nc_callback = nvme_wakeup_cmd;
1661788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_opc = NVME_OPC_GET_LOG_PAGE;
1662788c47fcSHans Rosenfeld
1663788c47fcSHans Rosenfeld getlogpage.b.lp_lid = logpage;
1664788c47fcSHans Rosenfeld
1665788c47fcSHans Rosenfeld switch (logpage) {
1666788c47fcSHans Rosenfeld case NVME_LOGPAGE_ERROR:
1667788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_nsid = (uint32_t)-1;
1668032cbfebSHans Rosenfeld /*
1669032cbfebSHans Rosenfeld * The GET LOG PAGE command can use at most 2 pages to return
1670032cbfebSHans Rosenfeld * data, PRP lists are not supported.
1671032cbfebSHans Rosenfeld */
1672032cbfebSHans Rosenfeld *bufsize = MIN(2 * nvme->n_pagesize,
1673032cbfebSHans Rosenfeld nvme->n_error_log_len * sizeof (nvme_error_log_entry_t));
1674788c47fcSHans Rosenfeld break;
1675788c47fcSHans Rosenfeld
1676788c47fcSHans Rosenfeld case NVME_LOGPAGE_HEALTH:
1677788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_nsid = va_arg(ap, uint32_t);
1678032cbfebSHans Rosenfeld *bufsize = sizeof (nvme_health_log_t);
1679788c47fcSHans Rosenfeld break;
1680788c47fcSHans Rosenfeld
1681788c47fcSHans Rosenfeld case NVME_LOGPAGE_FWSLOT:
1682788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_nsid = (uint32_t)-1;
1683032cbfebSHans Rosenfeld *bufsize = sizeof (nvme_fwslot_log_t);
1684788c47fcSHans Rosenfeld break;
1685788c47fcSHans Rosenfeld
1686788c47fcSHans Rosenfeld default:
1687788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN, "!unknown log page requested: %d",
1688788c47fcSHans Rosenfeld logpage);
1689788c47fcSHans Rosenfeld atomic_inc_32(&nvme->n_unknown_logpage);
169069a33c23SHans Rosenfeld ret = EINVAL;
1691788c47fcSHans Rosenfeld goto fail;
1692788c47fcSHans Rosenfeld }
1693788c47fcSHans Rosenfeld
1694788c47fcSHans Rosenfeld va_end(ap);
1695788c47fcSHans Rosenfeld
1696032cbfebSHans Rosenfeld getlogpage.b.lp_numd = *bufsize / sizeof (uint32_t) - 1;
1697788c47fcSHans Rosenfeld
1698788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_cdw10 = getlogpage.r;
1699788c47fcSHans Rosenfeld
1700788c47fcSHans Rosenfeld if (nvme_zalloc_dma(nvme, getlogpage.b.lp_numd * sizeof (uint32_t),
1701788c47fcSHans Rosenfeld DDI_DMA_READ, &nvme->n_prp_dma_attr, &cmd->nc_dma) != DDI_SUCCESS) {
1702788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN,
1703788c47fcSHans Rosenfeld "!nvme_zalloc_dma failed for GET LOG PAGE");
170469a33c23SHans Rosenfeld ret = ENOMEM;
1705788c47fcSHans Rosenfeld goto fail;
1706788c47fcSHans Rosenfeld }
1707788c47fcSHans Rosenfeld
1708788c47fcSHans Rosenfeld if (cmd->nc_dma->nd_ncookie > 2) {
1709788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN,
1710788c47fcSHans Rosenfeld "!too many DMA cookies for GET LOG PAGE");
1711788c47fcSHans Rosenfeld atomic_inc_32(&nvme->n_too_many_cookies);
171269a33c23SHans Rosenfeld ret = ENOMEM;
1713788c47fcSHans Rosenfeld goto fail;
1714788c47fcSHans Rosenfeld }
1715788c47fcSHans Rosenfeld
1716788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_dptr.d_prp[0] = cmd->nc_dma->nd_cookie.dmac_laddress;
1717788c47fcSHans Rosenfeld if (cmd->nc_dma->nd_ncookie > 1) {
1718788c47fcSHans Rosenfeld ddi_dma_nextcookie(cmd->nc_dma->nd_dmah,
1719788c47fcSHans Rosenfeld &cmd->nc_dma->nd_cookie);
1720788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_dptr.d_prp[1] =
1721788c47fcSHans Rosenfeld cmd->nc_dma->nd_cookie.dmac_laddress;
1722788c47fcSHans Rosenfeld }
1723788c47fcSHans Rosenfeld
172469a33c23SHans Rosenfeld nvme_admin_cmd(cmd, nvme_admin_cmd_timeout);
1725788c47fcSHans Rosenfeld
172669a33c23SHans Rosenfeld if ((ret = nvme_check_cmd_status(cmd)) != 0) {
1727788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN,
1728788c47fcSHans Rosenfeld "!GET LOG PAGE failed with sct = %x, sc = %x",
1729788c47fcSHans Rosenfeld cmd->nc_cqe.cqe_sf.sf_sct, cmd->nc_cqe.cqe_sf.sf_sc);
1730788c47fcSHans Rosenfeld goto fail;
1731788c47fcSHans Rosenfeld }
1732788c47fcSHans Rosenfeld
1733032cbfebSHans Rosenfeld *buf = kmem_alloc(*bufsize, KM_SLEEP);
1734032cbfebSHans Rosenfeld bcopy(cmd->nc_dma->nd_memp, *buf, *bufsize);
1735032cbfebSHans Rosenfeld
1736788c47fcSHans Rosenfeld fail:
1737788c47fcSHans Rosenfeld nvme_free_cmd(cmd);
1738788c47fcSHans Rosenfeld
1739032cbfebSHans Rosenfeld return (ret);
1740788c47fcSHans Rosenfeld }
1741788c47fcSHans Rosenfeld
174269a33c23SHans Rosenfeld static int
nvme_identify(nvme_t * nvme,uint32_t nsid,void ** buf)174369a33c23SHans Rosenfeld nvme_identify(nvme_t *nvme, uint32_t nsid, void **buf)
1744788c47fcSHans Rosenfeld {
1745788c47fcSHans Rosenfeld nvme_cmd_t *cmd = nvme_alloc_cmd(nvme, KM_SLEEP);
174669a33c23SHans Rosenfeld int ret;
174769a33c23SHans Rosenfeld
174869a33c23SHans Rosenfeld if (buf == NULL)
174969a33c23SHans Rosenfeld return (EINVAL);
1750788c47fcSHans Rosenfeld
1751788c47fcSHans Rosenfeld cmd->nc_sqid = 0;
1752788c47fcSHans Rosenfeld cmd->nc_callback = nvme_wakeup_cmd;
1753788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_opc = NVME_OPC_IDENTIFY;
1754788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_nsid = nsid;
1755788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_cdw10 = nsid ? NVME_IDENTIFY_NSID : NVME_IDENTIFY_CTRL;
1756788c47fcSHans Rosenfeld
1757788c47fcSHans Rosenfeld if (nvme_zalloc_dma(nvme, NVME_IDENTIFY_BUFSIZE, DDI_DMA_READ,
1758788c47fcSHans Rosenfeld &nvme->n_prp_dma_attr, &cmd->nc_dma) != DDI_SUCCESS) {
1759788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN,
1760788c47fcSHans Rosenfeld "!nvme_zalloc_dma failed for IDENTIFY");
176169a33c23SHans Rosenfeld ret = ENOMEM;
1762788c47fcSHans Rosenfeld goto fail;
1763788c47fcSHans Rosenfeld }
1764788c47fcSHans Rosenfeld
1765788c47fcSHans Rosenfeld if (cmd->nc_dma->nd_ncookie > 2) {
1766788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN,
1767788c47fcSHans Rosenfeld "!too many DMA cookies for IDENTIFY");
1768788c47fcSHans Rosenfeld atomic_inc_32(&nvme->n_too_many_cookies);
176969a33c23SHans Rosenfeld ret = ENOMEM;
1770788c47fcSHans Rosenfeld goto fail;
1771788c47fcSHans Rosenfeld }
1772788c47fcSHans Rosenfeld
1773788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_dptr.d_prp[0] = cmd->nc_dma->nd_cookie.dmac_laddress;
1774788c47fcSHans Rosenfeld if (cmd->nc_dma->nd_ncookie > 1) {
1775788c47fcSHans Rosenfeld ddi_dma_nextcookie(cmd->nc_dma->nd_dmah,
1776788c47fcSHans Rosenfeld &cmd->nc_dma->nd_cookie);
1777788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_dptr.d_prp[1] =
1778788c47fcSHans Rosenfeld cmd->nc_dma->nd_cookie.dmac_laddress;
1779788c47fcSHans Rosenfeld }
1780788c47fcSHans Rosenfeld
178169a33c23SHans Rosenfeld nvme_admin_cmd(cmd, nvme_admin_cmd_timeout);
1782788c47fcSHans Rosenfeld
178369a33c23SHans Rosenfeld if ((ret = nvme_check_cmd_status(cmd)) != 0) {
1784788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN,
1785788c47fcSHans Rosenfeld "!IDENTIFY failed with sct = %x, sc = %x",
1786788c47fcSHans Rosenfeld cmd->nc_cqe.cqe_sf.sf_sct, cmd->nc_cqe.cqe_sf.sf_sc);
1787788c47fcSHans Rosenfeld goto fail;
1788788c47fcSHans Rosenfeld }
1789788c47fcSHans Rosenfeld
179069a33c23SHans Rosenfeld *buf = kmem_alloc(NVME_IDENTIFY_BUFSIZE, KM_SLEEP);
179169a33c23SHans Rosenfeld bcopy(cmd->nc_dma->nd_memp, *buf, NVME_IDENTIFY_BUFSIZE);
1792788c47fcSHans Rosenfeld
1793788c47fcSHans Rosenfeld fail:
1794788c47fcSHans Rosenfeld nvme_free_cmd(cmd);
1795788c47fcSHans Rosenfeld
179669a33c23SHans Rosenfeld return (ret);
1797788c47fcSHans Rosenfeld }
1798788c47fcSHans Rosenfeld
179969a33c23SHans Rosenfeld static int
nvme_set_features(nvme_t * nvme,uint32_t nsid,uint8_t feature,uint32_t val,uint32_t * res)18005f836503SHans Rosenfeld nvme_set_features(nvme_t *nvme, uint32_t nsid, uint8_t feature, uint32_t val,
18015f836503SHans Rosenfeld uint32_t *res)
1802788c47fcSHans Rosenfeld {
18035f836503SHans Rosenfeld _NOTE(ARGUNUSED(nsid));
1804788c47fcSHans Rosenfeld nvme_cmd_t *cmd = nvme_alloc_cmd(nvme, KM_SLEEP);
180569a33c23SHans Rosenfeld int ret = EINVAL;
1806788c47fcSHans Rosenfeld
18075f836503SHans Rosenfeld ASSERT(res != NULL);
1808788c47fcSHans Rosenfeld
1809788c47fcSHans Rosenfeld cmd->nc_sqid = 0;
1810788c47fcSHans Rosenfeld cmd->nc_callback = nvme_wakeup_cmd;
1811788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_opc = NVME_OPC_SET_FEATURES;
18125f836503SHans Rosenfeld cmd->nc_sqe.sqe_cdw10 = feature;
18135f836503SHans Rosenfeld cmd->nc_sqe.sqe_cdw11 = val;
18145f836503SHans Rosenfeld
18155f836503SHans Rosenfeld switch (feature) {
18165f836503SHans Rosenfeld case NVME_FEAT_WRITE_CACHE:
18175f836503SHans Rosenfeld if (!nvme->n_write_cache_present)
18185f836503SHans Rosenfeld goto fail;
18195f836503SHans Rosenfeld break;
18205f836503SHans Rosenfeld
18215f836503SHans Rosenfeld case NVME_FEAT_NQUEUES:
18225f836503SHans Rosenfeld break;
18235f836503SHans Rosenfeld
18245f836503SHans Rosenfeld default:
18255f836503SHans Rosenfeld goto fail;
18265f836503SHans Rosenfeld }
1827788c47fcSHans Rosenfeld
182869a33c23SHans Rosenfeld nvme_admin_cmd(cmd, nvme_admin_cmd_timeout);
1829788c47fcSHans Rosenfeld
183069a33c23SHans Rosenfeld if ((ret = nvme_check_cmd_status(cmd)) != 0) {
1831788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN,
18325f836503SHans Rosenfeld "!SET FEATURES %d failed with sct = %x, sc = %x",
18335f836503SHans Rosenfeld feature, cmd->nc_cqe.cqe_sf.sf_sct,
18345f836503SHans Rosenfeld cmd->nc_cqe.cqe_sf.sf_sc);
18355f836503SHans Rosenfeld goto fail;
1836788c47fcSHans Rosenfeld }
1837788c47fcSHans Rosenfeld
18385f836503SHans Rosenfeld *res = cmd->nc_cqe.cqe_dw0;
18395f836503SHans Rosenfeld
18405f836503SHans Rosenfeld fail:
1841788c47fcSHans Rosenfeld nvme_free_cmd(cmd);
18425f836503SHans Rosenfeld return (ret);
18435f836503SHans Rosenfeld }
18445f836503SHans Rosenfeld
184569a33c23SHans Rosenfeld static int
nvme_get_features(nvme_t * nvme,uint32_t nsid,uint8_t feature,uint32_t * res,void ** buf,size_t * bufsize)1846032cbfebSHans Rosenfeld nvme_get_features(nvme_t *nvme, uint32_t nsid, uint8_t feature, uint32_t *res,
1847032cbfebSHans Rosenfeld void **buf, size_t *bufsize)
1848032cbfebSHans Rosenfeld {
1849032cbfebSHans Rosenfeld nvme_cmd_t *cmd = nvme_alloc_cmd(nvme, KM_SLEEP);
185069a33c23SHans Rosenfeld int ret = EINVAL;
1851032cbfebSHans Rosenfeld
1852032cbfebSHans Rosenfeld ASSERT(res != NULL);
1853032cbfebSHans Rosenfeld
1854032cbfebSHans Rosenfeld if (bufsize != NULL)
1855032cbfebSHans Rosenfeld *bufsize = 0;
1856032cbfebSHans Rosenfeld
1857032cbfebSHans Rosenfeld cmd->nc_sqid = 0;
1858032cbfebSHans Rosenfeld cmd->nc_callback = nvme_wakeup_cmd;
1859032cbfebSHans Rosenfeld cmd->nc_sqe.sqe_opc = NVME_OPC_GET_FEATURES;
1860032cbfebSHans Rosenfeld cmd->nc_sqe.sqe_cdw10 = feature;
1861032cbfebSHans Rosenfeld cmd->nc_sqe.sqe_cdw11 = *res;
1862032cbfebSHans Rosenfeld
1863032cbfebSHans Rosenfeld switch (feature) {
1864032cbfebSHans Rosenfeld case NVME_FEAT_ARBITRATION:
1865032cbfebSHans Rosenfeld case NVME_FEAT_POWER_MGMT:
1866032cbfebSHans Rosenfeld case NVME_FEAT_TEMPERATURE:
1867032cbfebSHans Rosenfeld case NVME_FEAT_ERROR:
1868032cbfebSHans Rosenfeld case NVME_FEAT_NQUEUES:
1869032cbfebSHans Rosenfeld case NVME_FEAT_INTR_COAL:
1870032cbfebSHans Rosenfeld case NVME_FEAT_INTR_VECT:
1871032cbfebSHans Rosenfeld case NVME_FEAT_WRITE_ATOM:
1872032cbfebSHans Rosenfeld case NVME_FEAT_ASYNC_EVENT:
1873032cbfebSHans Rosenfeld case NVME_FEAT_PROGRESS:
1874032cbfebSHans Rosenfeld break;
1875032cbfebSHans Rosenfeld
1876032cbfebSHans Rosenfeld case NVME_FEAT_WRITE_CACHE:
1877032cbfebSHans Rosenfeld if (!nvme->n_write_cache_present)
1878032cbfebSHans Rosenfeld goto fail;
1879032cbfebSHans Rosenfeld break;
1880032cbfebSHans Rosenfeld
1881032cbfebSHans Rosenfeld case NVME_FEAT_LBA_RANGE:
1882032cbfebSHans Rosenfeld if (!nvme->n_lba_range_supported)
1883032cbfebSHans Rosenfeld goto fail;
1884032cbfebSHans Rosenfeld
1885032cbfebSHans Rosenfeld /*
1886032cbfebSHans Rosenfeld * The LBA Range Type feature is optional. There doesn't seem
1887032cbfebSHans Rosenfeld * be a method of detecting whether it is supported other than
1888032cbfebSHans Rosenfeld * using it. This will cause a "invalid field in command" error,
1889032cbfebSHans Rosenfeld * which is normally considered a programming error and causes
1890032cbfebSHans Rosenfeld * panic in nvme_check_generic_cmd_status().
1891032cbfebSHans Rosenfeld */
1892032cbfebSHans Rosenfeld cmd->nc_dontpanic = B_TRUE;
1893032cbfebSHans Rosenfeld cmd->nc_sqe.sqe_nsid = nsid;
1894032cbfebSHans Rosenfeld ASSERT(bufsize != NULL);
1895032cbfebSHans Rosenfeld *bufsize = NVME_LBA_RANGE_BUFSIZE;
1896032cbfebSHans Rosenfeld
1897032cbfebSHans Rosenfeld break;
1898032cbfebSHans Rosenfeld
1899032cbfebSHans Rosenfeld case NVME_FEAT_AUTO_PST:
1900032cbfebSHans Rosenfeld if (!nvme->n_auto_pst_supported)
1901032cbfebSHans Rosenfeld goto fail;
1902032cbfebSHans Rosenfeld
1903032cbfebSHans Rosenfeld ASSERT(bufsize != NULL);
1904032cbfebSHans Rosenfeld *bufsize = NVME_AUTO_PST_BUFSIZE;
1905032cbfebSHans Rosenfeld break;
1906032cbfebSHans Rosenfeld
1907032cbfebSHans Rosenfeld default:
1908032cbfebSHans Rosenfeld goto fail;
1909032cbfebSHans Rosenfeld }
1910032cbfebSHans Rosenfeld
1911032cbfebSHans Rosenfeld if (bufsize != NULL && *bufsize != 0) {
1912032cbfebSHans Rosenfeld if (nvme_zalloc_dma(nvme, *bufsize, DDI_DMA_READ,
1913032cbfebSHans Rosenfeld &nvme->n_prp_dma_attr, &cmd->nc_dma) != DDI_SUCCESS) {
1914032cbfebSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN,
1915032cbfebSHans Rosenfeld "!nvme_zalloc_dma failed for GET FEATURES");
191669a33c23SHans Rosenfeld ret = ENOMEM;
1917032cbfebSHans Rosenfeld goto fail;
1918032cbfebSHans Rosenfeld }
1919032cbfebSHans Rosenfeld
1920032cbfebSHans Rosenfeld if (cmd->nc_dma->nd_ncookie > 2) {
1921032cbfebSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN,
1922032cbfebSHans Rosenfeld "!too many DMA cookies for GET FEATURES");
1923032cbfebSHans Rosenfeld atomic_inc_32(&nvme->n_too_many_cookies);
192469a33c23SHans Rosenfeld ret = ENOMEM;
1925032cbfebSHans Rosenfeld goto fail;
1926032cbfebSHans Rosenfeld }
1927032cbfebSHans Rosenfeld
1928032cbfebSHans Rosenfeld cmd->nc_sqe.sqe_dptr.d_prp[0] =
1929032cbfebSHans Rosenfeld cmd->nc_dma->nd_cookie.dmac_laddress;
1930032cbfebSHans Rosenfeld if (cmd->nc_dma->nd_ncookie > 1) {
1931032cbfebSHans Rosenfeld ddi_dma_nextcookie(cmd->nc_dma->nd_dmah,
1932032cbfebSHans Rosenfeld &cmd->nc_dma->nd_cookie);
1933032cbfebSHans Rosenfeld cmd->nc_sqe.sqe_dptr.d_prp[1] =
1934032cbfebSHans Rosenfeld cmd->nc_dma->nd_cookie.dmac_laddress;
1935032cbfebSHans Rosenfeld }
1936032cbfebSHans Rosenfeld }
1937032cbfebSHans Rosenfeld
193869a33c23SHans Rosenfeld nvme_admin_cmd(cmd, nvme_admin_cmd_timeout);
1939032cbfebSHans Rosenfeld
194069a33c23SHans Rosenfeld if ((ret = nvme_check_cmd_status(cmd)) != 0) {
1941032cbfebSHans Rosenfeld if (feature == NVME_FEAT_LBA_RANGE &&
1942032cbfebSHans Rosenfeld cmd->nc_cqe.cqe_sf.sf_sct == NVME_CQE_SCT_GENERIC &&
1943032cbfebSHans Rosenfeld cmd->nc_cqe.cqe_sf.sf_sc == NVME_CQE_SC_GEN_INV_FLD)
1944032cbfebSHans Rosenfeld nvme->n_lba_range_supported = B_FALSE;
1945032cbfebSHans Rosenfeld else
1946032cbfebSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN,
1947032cbfebSHans Rosenfeld "!GET FEATURES %d failed with sct = %x, sc = %x",
1948032cbfebSHans Rosenfeld feature, cmd->nc_cqe.cqe_sf.sf_sct,
1949032cbfebSHans Rosenfeld cmd->nc_cqe.cqe_sf.sf_sc);
1950032cbfebSHans Rosenfeld goto fail;
1951032cbfebSHans Rosenfeld }
1952032cbfebSHans Rosenfeld
1953032cbfebSHans Rosenfeld if (bufsize != NULL && *bufsize != 0) {
1954032cbfebSHans Rosenfeld ASSERT(buf != NULL);
1955032cbfebSHans Rosenfeld *buf = kmem_alloc(*bufsize, KM_SLEEP);
1956032cbfebSHans Rosenfeld bcopy(cmd->nc_dma->nd_memp, *buf, *bufsize);
1957032cbfebSHans Rosenfeld }
1958032cbfebSHans Rosenfeld
1959032cbfebSHans Rosenfeld *res = cmd->nc_cqe.cqe_dw0;
1960032cbfebSHans Rosenfeld
1961032cbfebSHans Rosenfeld fail:
1962032cbfebSHans Rosenfeld nvme_free_cmd(cmd);
1963032cbfebSHans Rosenfeld return (ret);
1964032cbfebSHans Rosenfeld }
1965032cbfebSHans Rosenfeld
196669a33c23SHans Rosenfeld static int
nvme_write_cache_set(nvme_t * nvme,boolean_t enable)19675f836503SHans Rosenfeld nvme_write_cache_set(nvme_t *nvme, boolean_t enable)
19685f836503SHans Rosenfeld {
19695f836503SHans Rosenfeld nvme_write_cache_t nwc = { 0 };
19705f836503SHans Rosenfeld
19715f836503SHans Rosenfeld if (enable)
19725f836503SHans Rosenfeld nwc.b.wc_wce = 1;
19735f836503SHans Rosenfeld
197469a33c23SHans Rosenfeld return (nvme_set_features(nvme, 0, NVME_FEAT_WRITE_CACHE, nwc.r,
197569a33c23SHans Rosenfeld &nwc.r));
19765f836503SHans Rosenfeld }
19775f836503SHans Rosenfeld
19785f836503SHans Rosenfeld static int
nvme_set_nqueues(nvme_t * nvme,uint16_t * nqueues)197969a33c23SHans Rosenfeld nvme_set_nqueues(nvme_t *nvme, uint16_t *nqueues)
19805f836503SHans Rosenfeld {
1981032cbfebSHans Rosenfeld nvme_nqueues_t nq = { 0 };
198269a33c23SHans Rosenfeld int ret;
19835f836503SHans Rosenfeld
198469a33c23SHans Rosenfeld nq.b.nq_nsq = nq.b.nq_ncq = *nqueues - 1;
19855f836503SHans Rosenfeld
198669a33c23SHans Rosenfeld ret = nvme_set_features(nvme, 0, NVME_FEAT_NQUEUES, nq.r, &nq.r);
198769a33c23SHans Rosenfeld
198869a33c23SHans Rosenfeld if (ret == 0) {
198969a33c23SHans Rosenfeld /*
199069a33c23SHans Rosenfeld * Always use the same number of submission and completion
199169a33c23SHans Rosenfeld * queues, and never use more than the requested number of
199269a33c23SHans Rosenfeld * queues.
199369a33c23SHans Rosenfeld */
199469a33c23SHans Rosenfeld *nqueues = MIN(*nqueues, MIN(nq.b.nq_nsq, nq.b.nq_ncq) + 1);
19955f836503SHans Rosenfeld }
1996788c47fcSHans Rosenfeld
199769a33c23SHans Rosenfeld return (ret);
1998788c47fcSHans Rosenfeld }
1999788c47fcSHans Rosenfeld
2000788c47fcSHans Rosenfeld static int
nvme_create_io_qpair(nvme_t * nvme,nvme_qpair_t * qp,uint16_t idx)2001788c47fcSHans Rosenfeld nvme_create_io_qpair(nvme_t *nvme, nvme_qpair_t *qp, uint16_t idx)
2002788c47fcSHans Rosenfeld {
2003788c47fcSHans Rosenfeld nvme_cmd_t *cmd = nvme_alloc_cmd(nvme, KM_SLEEP);
2004788c47fcSHans Rosenfeld nvme_create_queue_dw10_t dw10 = { 0 };
2005788c47fcSHans Rosenfeld nvme_create_cq_dw11_t c_dw11 = { 0 };
2006788c47fcSHans Rosenfeld nvme_create_sq_dw11_t s_dw11 = { 0 };
200769a33c23SHans Rosenfeld int ret;
2008788c47fcSHans Rosenfeld
2009788c47fcSHans Rosenfeld dw10.b.q_qid = idx;
2010788c47fcSHans Rosenfeld dw10.b.q_qsize = qp->nq_nentry - 1;
2011788c47fcSHans Rosenfeld
2012788c47fcSHans Rosenfeld c_dw11.b.cq_pc = 1;
2013788c47fcSHans Rosenfeld c_dw11.b.cq_ien = 1;
2014788c47fcSHans Rosenfeld c_dw11.b.cq_iv = idx % nvme->n_intr_cnt;
2015788c47fcSHans Rosenfeld
2016788c47fcSHans Rosenfeld cmd->nc_sqid = 0;
2017788c47fcSHans Rosenfeld cmd->nc_callback = nvme_wakeup_cmd;
2018788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_opc = NVME_OPC_CREATE_CQUEUE;
2019788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_cdw10 = dw10.r;
2020788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_cdw11 = c_dw11.r;
2021788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_dptr.d_prp[0] = qp->nq_cqdma->nd_cookie.dmac_laddress;
2022788c47fcSHans Rosenfeld
202369a33c23SHans Rosenfeld nvme_admin_cmd(cmd, nvme_admin_cmd_timeout);
2024788c47fcSHans Rosenfeld
202569a33c23SHans Rosenfeld if ((ret = nvme_check_cmd_status(cmd)) != 0) {
2026788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN,
2027788c47fcSHans Rosenfeld "!CREATE CQUEUE failed with sct = %x, sc = %x",
2028788c47fcSHans Rosenfeld cmd->nc_cqe.cqe_sf.sf_sct, cmd->nc_cqe.cqe_sf.sf_sc);
202969a33c23SHans Rosenfeld goto fail;
2030788c47fcSHans Rosenfeld }
2031788c47fcSHans Rosenfeld
2032788c47fcSHans Rosenfeld nvme_free_cmd(cmd);
2033788c47fcSHans Rosenfeld
2034788c47fcSHans Rosenfeld s_dw11.b.sq_pc = 1;
2035788c47fcSHans Rosenfeld s_dw11.b.sq_cqid = idx;
2036788c47fcSHans Rosenfeld
2037788c47fcSHans Rosenfeld cmd = nvme_alloc_cmd(nvme, KM_SLEEP);
2038788c47fcSHans Rosenfeld cmd->nc_sqid = 0;
2039788c47fcSHans Rosenfeld cmd->nc_callback = nvme_wakeup_cmd;
2040788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_opc = NVME_OPC_CREATE_SQUEUE;
2041788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_cdw10 = dw10.r;
2042788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_cdw11 = s_dw11.r;
2043788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_dptr.d_prp[0] = qp->nq_sqdma->nd_cookie.dmac_laddress;
2044788c47fcSHans Rosenfeld
204569a33c23SHans Rosenfeld nvme_admin_cmd(cmd, nvme_admin_cmd_timeout);
2046788c47fcSHans Rosenfeld
204769a33c23SHans Rosenfeld if ((ret = nvme_check_cmd_status(cmd)) != 0) {
2048788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN,
2049788c47fcSHans Rosenfeld "!CREATE SQUEUE failed with sct = %x, sc = %x",
2050788c47fcSHans Rosenfeld cmd->nc_cqe.cqe_sf.sf_sct, cmd->nc_cqe.cqe_sf.sf_sc);
205169a33c23SHans Rosenfeld goto fail;
2052788c47fcSHans Rosenfeld }
2053788c47fcSHans Rosenfeld
205469a33c23SHans Rosenfeld fail:
2055788c47fcSHans Rosenfeld nvme_free_cmd(cmd);
2056788c47fcSHans Rosenfeld
205769a33c23SHans Rosenfeld return (ret);
2058788c47fcSHans Rosenfeld }
2059788c47fcSHans Rosenfeld
2060788c47fcSHans Rosenfeld static boolean_t
nvme_reset(nvme_t * nvme,boolean_t quiesce)2061788c47fcSHans Rosenfeld nvme_reset(nvme_t *nvme, boolean_t quiesce)
2062788c47fcSHans Rosenfeld {
2063788c47fcSHans Rosenfeld nvme_reg_csts_t csts;
2064788c47fcSHans Rosenfeld int i;
2065788c47fcSHans Rosenfeld
2066788c47fcSHans Rosenfeld nvme_put32(nvme, NVME_REG_CC, 0);
2067788c47fcSHans Rosenfeld
2068788c47fcSHans Rosenfeld csts.r = nvme_get32(nvme, NVME_REG_CSTS);
2069788c47fcSHans Rosenfeld if (csts.b.csts_rdy == 1) {
2070788c47fcSHans Rosenfeld nvme_put32(nvme, NVME_REG_CC, 0);
2071788c47fcSHans Rosenfeld for (i = 0; i != nvme->n_timeout * 10; i++) {
2072788c47fcSHans Rosenfeld csts.r = nvme_get32(nvme, NVME_REG_CSTS);
2073788c47fcSHans Rosenfeld if (csts.b.csts_rdy == 0)
2074788c47fcSHans Rosenfeld break;
2075788c47fcSHans Rosenfeld
2076788c47fcSHans Rosenfeld if (quiesce)
2077788c47fcSHans Rosenfeld drv_usecwait(50000);
2078788c47fcSHans Rosenfeld else
2079788c47fcSHans Rosenfeld delay(drv_usectohz(50000));
2080788c47fcSHans Rosenfeld }
2081788c47fcSHans Rosenfeld }
2082788c47fcSHans Rosenfeld
2083788c47fcSHans Rosenfeld nvme_put32(nvme, NVME_REG_AQA, 0);
2084788c47fcSHans Rosenfeld nvme_put32(nvme, NVME_REG_ASQ, 0);
2085788c47fcSHans Rosenfeld nvme_put32(nvme, NVME_REG_ACQ, 0);
2086788c47fcSHans Rosenfeld
2087788c47fcSHans Rosenfeld csts.r = nvme_get32(nvme, NVME_REG_CSTS);
2088788c47fcSHans Rosenfeld return (csts.b.csts_rdy == 0 ? B_TRUE : B_FALSE);
2089788c47fcSHans Rosenfeld }
2090788c47fcSHans Rosenfeld
2091788c47fcSHans Rosenfeld static void
nvme_shutdown(nvme_t * nvme,int mode,boolean_t quiesce)2092788c47fcSHans Rosenfeld nvme_shutdown(nvme_t *nvme, int mode, boolean_t quiesce)
2093788c47fcSHans Rosenfeld {
2094788c47fcSHans Rosenfeld nvme_reg_cc_t cc;
2095788c47fcSHans Rosenfeld nvme_reg_csts_t csts;
2096788c47fcSHans Rosenfeld int i;
2097788c47fcSHans Rosenfeld
2098788c47fcSHans Rosenfeld ASSERT(mode == NVME_CC_SHN_NORMAL || mode == NVME_CC_SHN_ABRUPT);
2099788c47fcSHans Rosenfeld
2100788c47fcSHans Rosenfeld cc.r = nvme_get32(nvme, NVME_REG_CC);
2101788c47fcSHans Rosenfeld cc.b.cc_shn = mode & 0x3;
2102788c47fcSHans Rosenfeld nvme_put32(nvme, NVME_REG_CC, cc.r);
2103788c47fcSHans Rosenfeld
2104788c47fcSHans Rosenfeld for (i = 0; i != 10; i++) {
2105788c47fcSHans Rosenfeld csts.r = nvme_get32(nvme, NVME_REG_CSTS);
2106788c47fcSHans Rosenfeld if (csts.b.csts_shst == NVME_CSTS_SHN_COMPLETE)
2107788c47fcSHans Rosenfeld break;
2108788c47fcSHans Rosenfeld
2109788c47fcSHans Rosenfeld if (quiesce)
2110788c47fcSHans Rosenfeld drv_usecwait(100000);
2111788c47fcSHans Rosenfeld else
2112788c47fcSHans Rosenfeld delay(drv_usectohz(100000));
2113788c47fcSHans Rosenfeld }
2114788c47fcSHans Rosenfeld }
2115788c47fcSHans Rosenfeld
2116788c47fcSHans Rosenfeld
2117788c47fcSHans Rosenfeld static void
nvme_prepare_devid(nvme_t * nvme,uint32_t nsid)2118788c47fcSHans Rosenfeld nvme_prepare_devid(nvme_t *nvme, uint32_t nsid)
2119788c47fcSHans Rosenfeld {
2120265d85e9SHans Rosenfeld /*
2121265d85e9SHans Rosenfeld * Section 7.7 of the spec describes how to get a unique ID for
2122265d85e9SHans Rosenfeld * the controller: the vendor ID, the model name and the serial
2123265d85e9SHans Rosenfeld * number shall be unique when combined.
2124265d85e9SHans Rosenfeld *
2125265d85e9SHans Rosenfeld * If a namespace has no EUI64 we use the above and add the hex
2126265d85e9SHans Rosenfeld * namespace ID to get a unique ID for the namespace.
2127265d85e9SHans Rosenfeld */
2128788c47fcSHans Rosenfeld char model[sizeof (nvme->n_idctl->id_model) + 1];
2129788c47fcSHans Rosenfeld char serial[sizeof (nvme->n_idctl->id_serial) + 1];
2130788c47fcSHans Rosenfeld
2131788c47fcSHans Rosenfeld bcopy(nvme->n_idctl->id_model, model, sizeof (nvme->n_idctl->id_model));
2132788c47fcSHans Rosenfeld bcopy(nvme->n_idctl->id_serial, serial,
2133788c47fcSHans Rosenfeld sizeof (nvme->n_idctl->id_serial));
2134788c47fcSHans Rosenfeld
2135788c47fcSHans Rosenfeld model[sizeof (nvme->n_idctl->id_model)] = '\0';
2136788c47fcSHans Rosenfeld serial[sizeof (nvme->n_idctl->id_serial)] = '\0';
2137788c47fcSHans Rosenfeld
2138265d85e9SHans Rosenfeld nvme->n_ns[nsid - 1].ns_devid = kmem_asprintf("%4X-%s-%s-%X",
2139788c47fcSHans Rosenfeld nvme->n_idctl->id_vid, model, serial, nsid);
2140788c47fcSHans Rosenfeld }
2141788c47fcSHans Rosenfeld
2142788c47fcSHans Rosenfeld static int
nvme_init_ns(nvme_t * nvme,int nsid)2143032cbfebSHans Rosenfeld nvme_init_ns(nvme_t *nvme, int nsid)
2144032cbfebSHans Rosenfeld {
2145032cbfebSHans Rosenfeld nvme_namespace_t *ns = &nvme->n_ns[nsid - 1];
2146032cbfebSHans Rosenfeld nvme_identify_nsid_t *idns;
2147032cbfebSHans Rosenfeld int last_rp;
2148032cbfebSHans Rosenfeld
2149032cbfebSHans Rosenfeld ns->ns_nvme = nvme;
2150032cbfebSHans Rosenfeld
215169a33c23SHans Rosenfeld if (nvme_identify(nvme, nsid, (void **)&idns) != 0) {
2152032cbfebSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN,
2153032cbfebSHans Rosenfeld "!failed to identify namespace %d", nsid);
2154032cbfebSHans Rosenfeld return (DDI_FAILURE);
2155032cbfebSHans Rosenfeld }
2156032cbfebSHans Rosenfeld
2157032cbfebSHans Rosenfeld ns->ns_idns = idns;
2158032cbfebSHans Rosenfeld ns->ns_id = nsid;
2159032cbfebSHans Rosenfeld ns->ns_block_count = idns->id_nsize;
2160032cbfebSHans Rosenfeld ns->ns_block_size =
2161032cbfebSHans Rosenfeld 1 << idns->id_lbaf[idns->id_flbas.lba_format].lbaf_lbads;
2162032cbfebSHans Rosenfeld ns->ns_best_block_size = ns->ns_block_size;
2163032cbfebSHans Rosenfeld
2164032cbfebSHans Rosenfeld /*
2165032cbfebSHans Rosenfeld * Get the EUI64 if present. Use it for devid and device node names.
2166032cbfebSHans Rosenfeld */
2167032cbfebSHans Rosenfeld if (NVME_VERSION_ATLEAST(&nvme->n_version, 1, 1))
2168032cbfebSHans Rosenfeld bcopy(idns->id_eui64, ns->ns_eui64, sizeof (ns->ns_eui64));
2169032cbfebSHans Rosenfeld
2170032cbfebSHans Rosenfeld /*LINTED: E_BAD_PTR_CAST_ALIGN*/
2171032cbfebSHans Rosenfeld if (*(uint64_t *)ns->ns_eui64 != 0) {
2172032cbfebSHans Rosenfeld uint8_t *eui64 = ns->ns_eui64;
2173032cbfebSHans Rosenfeld
2174032cbfebSHans Rosenfeld (void) snprintf(ns->ns_name, sizeof (ns->ns_name),
2175032cbfebSHans Rosenfeld "%02x%02x%02x%02x%02x%02x%02x%02x",
2176032cbfebSHans Rosenfeld eui64[0], eui64[1], eui64[2], eui64[3],
2177032cbfebSHans Rosenfeld eui64[4], eui64[5], eui64[6], eui64[7]);
2178032cbfebSHans Rosenfeld } else {
2179032cbfebSHans Rosenfeld (void) snprintf(ns->ns_name, sizeof (ns->ns_name), "%d",
2180032cbfebSHans Rosenfeld ns->ns_id);
2181032cbfebSHans Rosenfeld
2182032cbfebSHans Rosenfeld nvme_prepare_devid(nvme, ns->ns_id);
2183032cbfebSHans Rosenfeld }
2184032cbfebSHans Rosenfeld
2185032cbfebSHans Rosenfeld /*
2186032cbfebSHans Rosenfeld * Find the LBA format with no metadata and the best relative
2187032cbfebSHans Rosenfeld * performance. A value of 3 means "degraded", 0 is best.
2188032cbfebSHans Rosenfeld */
2189032cbfebSHans Rosenfeld last_rp = 3;
2190032cbfebSHans Rosenfeld for (int j = 0; j <= idns->id_nlbaf; j++) {
2191032cbfebSHans Rosenfeld if (idns->id_lbaf[j].lbaf_lbads == 0)
2192032cbfebSHans Rosenfeld break;
2193032cbfebSHans Rosenfeld if (idns->id_lbaf[j].lbaf_ms != 0)
2194032cbfebSHans Rosenfeld continue;
2195032cbfebSHans Rosenfeld if (idns->id_lbaf[j].lbaf_rp >= last_rp)
2196032cbfebSHans Rosenfeld continue;
2197032cbfebSHans Rosenfeld last_rp = idns->id_lbaf[j].lbaf_rp;
2198032cbfebSHans Rosenfeld ns->ns_best_block_size =
2199032cbfebSHans Rosenfeld 1 << idns->id_lbaf[j].lbaf_lbads;
2200032cbfebSHans Rosenfeld }
2201032cbfebSHans Rosenfeld
2202032cbfebSHans Rosenfeld if (ns->ns_best_block_size < nvme->n_min_block_size)
2203032cbfebSHans Rosenfeld ns->ns_best_block_size = nvme->n_min_block_size;
2204032cbfebSHans Rosenfeld
2205032cbfebSHans Rosenfeld /*
2206032cbfebSHans Rosenfeld * We currently don't support namespaces that use either:
2207032cbfebSHans Rosenfeld * - thin provisioning
2208032cbfebSHans Rosenfeld * - protection information
220917e0fc67SHans Rosenfeld * - illegal block size (< 512)
2210032cbfebSHans Rosenfeld */
2211032cbfebSHans Rosenfeld if (idns->id_nsfeat.f_thin ||
2212032cbfebSHans Rosenfeld idns->id_dps.dp_pinfo) {
2213032cbfebSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN,
2214032cbfebSHans Rosenfeld "!ignoring namespace %d, unsupported features: "
2215032cbfebSHans Rosenfeld "thin = %d, pinfo = %d", nsid,
2216032cbfebSHans Rosenfeld idns->id_nsfeat.f_thin, idns->id_dps.dp_pinfo);
2217032cbfebSHans Rosenfeld ns->ns_ignore = B_TRUE;
221817e0fc67SHans Rosenfeld } else if (ns->ns_block_size < 512) {
221917e0fc67SHans Rosenfeld dev_err(nvme->n_dip, CE_WARN,
222017e0fc67SHans Rosenfeld "!ignoring namespace %d, unsupported block size %"PRIu64,
222117e0fc67SHans Rosenfeld nsid, (uint64_t)ns->ns_block_size);
2222ed2a4cceSHans Rosenfeld ns->ns_ignore = B_TRUE;
2223032cbfebSHans Rosenfeld } else {
2224032cbfebSHans Rosenfeld ns->ns_ignore = B_FALSE;
2225032cbfebSHans Rosenfeld }
2226032cbfebSHans Rosenfeld
2227032cbfebSHans Rosenfeld return (DDI_SUCCESS);
2228032cbfebSHans Rosenfeld }
2229032cbfebSHans Rosenfeld
2230032cbfebSHans Rosenfeld static int
nvme_init(nvme_t * nvme)2231788c47fcSHans Rosenfeld nvme_init(nvme_t *nvme)
2232788c47fcSHans Rosenfeld {
2233788c47fcSHans Rosenfeld nvme_reg_cc_t cc = { 0 };
2234788c47fcSHans Rosenfeld nvme_reg_aqa_t aqa = { 0 };
2235788c47fcSHans Rosenfeld nvme_reg_asq_t asq = { 0 };
2236788c47fcSHans Rosenfeld nvme_reg_acq_t acq = { 0 };
2237788c47fcSHans Rosenfeld nvme_reg_cap_t cap;
2238788c47fcSHans Rosenfeld nvme_reg_vs_t vs;
2239788c47fcSHans Rosenfeld nvme_reg_csts_t csts;
2240788c47fcSHans Rosenfeld int i = 0;
224169a33c23SHans Rosenfeld uint16_t nqueues;
2242f0e4d8f0SHans Rosenfeld char model[sizeof (nvme->n_idctl->id_model) + 1];
2243f0e4d8f0SHans Rosenfeld char *vendor, *product;
2244788c47fcSHans Rosenfeld
2245788c47fcSHans Rosenfeld /* Check controller version */
2246788c47fcSHans Rosenfeld vs.r = nvme_get32(nvme, NVME_REG_VS);
2247265d85e9SHans Rosenfeld nvme->n_version.v_major = vs.b.vs_mjr;
2248265d85e9SHans Rosenfeld nvme->n_version.v_minor = vs.b.vs_mnr;
2249788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_CONT, "?NVMe spec version %d.%d",
2250265d85e9SHans Rosenfeld nvme->n_version.v_major, nvme->n_version.v_minor);
2251788c47fcSHans Rosenfeld
2252*c8ded1b3SRobert Mustacchi if (nvme->n_version.v_major > nvme_version_major) {
2253*c8ded1b3SRobert Mustacchi dev_err(nvme->n_dip, CE_WARN, "!no support for version > %d.x",
2254*c8ded1b3SRobert Mustacchi nvme_version_major);
2255788c47fcSHans Rosenfeld if (nvme->n_strict_version)
2256788c47fcSHans Rosenfeld goto fail;
2257788c47fcSHans Rosenfeld }
2258788c47fcSHans Rosenfeld
2259788c47fcSHans Rosenfeld /* retrieve controller configuration */
2260788c47fcSHans Rosenfeld cap.r = nvme_get64(nvme, NVME_REG_CAP);
2261788c47fcSHans Rosenfeld
2262788c47fcSHans Rosenfeld if ((cap.b.cap_css & NVME_CAP_CSS_NVM) == 0) {
2263788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN,
2264788c47fcSHans Rosenfeld "!NVM command set not supported by hardware");
2265788c47fcSHans Rosenfeld goto fail;
2266788c47fcSHans Rosenfeld }
2267788c47fcSHans Rosenfeld
2268788c47fcSHans Rosenfeld nvme->n_nssr_supported = cap.b.cap_nssrs;
2269788c47fcSHans Rosenfeld nvme->n_doorbell_stride = 4 << cap.b.cap_dstrd;
2270788c47fcSHans Rosenfeld nvme->n_timeout = cap.b.cap_to;
2271788c47fcSHans Rosenfeld nvme->n_arbitration_mechanisms = cap.b.cap_ams;
2272788c47fcSHans Rosenfeld nvme->n_cont_queues_reqd = cap.b.cap_cqr;
2273788c47fcSHans Rosenfeld nvme->n_max_queue_entries = cap.b.cap_mqes + 1;
2274788c47fcSHans Rosenfeld
2275788c47fcSHans Rosenfeld /*
2276788c47fcSHans Rosenfeld * The MPSMIN and MPSMAX fields in the CAP register use 0 to specify
2277788c47fcSHans Rosenfeld * the base page size of 4k (1<<12), so add 12 here to get the real
2278788c47fcSHans Rosenfeld * page size value.
2279788c47fcSHans Rosenfeld */
2280788c47fcSHans Rosenfeld nvme->n_pageshift = MIN(MAX(cap.b.cap_mpsmin + 12, PAGESHIFT),
2281788c47fcSHans Rosenfeld cap.b.cap_mpsmax + 12);
2282788c47fcSHans Rosenfeld nvme->n_pagesize = 1UL << (nvme->n_pageshift);
2283788c47fcSHans Rosenfeld
2284788c47fcSHans Rosenfeld /*
2285788c47fcSHans Rosenfeld * Set up Queue DMA to transfer at least 1 page-aligned page at a time.
2286788c47fcSHans Rosenfeld */
2287788c47fcSHans Rosenfeld nvme->n_queue_dma_attr.dma_attr_align = nvme->n_pagesize;
2288788c47fcSHans Rosenfeld nvme->n_queue_dma_attr.dma_attr_minxfer = nvme->n_pagesize;
2289788c47fcSHans Rosenfeld
2290788c47fcSHans Rosenfeld /*
2291788c47fcSHans Rosenfeld * Set up PRP DMA to transfer 1 page-aligned page at a time.
2292788c47fcSHans Rosenfeld * Maxxfer may be increased after we identified the controller limits.
2293788c47fcSHans Rosenfeld */
2294788c47fcSHans Rosenfeld nvme->n_prp_dma_attr.dma_attr_maxxfer = nvme->n_pagesize;
2295788c47fcSHans Rosenfeld nvme->n_prp_dma_attr.dma_attr_minxfer = nvme->n_pagesize;
2296788c47fcSHans Rosenfeld nvme->n_prp_dma_attr.dma_attr_align = nvme->n_pagesize;
2297babc8e1dSYouzhong Yang nvme->n_prp_dma_attr.dma_attr_seg = nvme->n_pagesize - 1;
2298788c47fcSHans Rosenfeld
2299788c47fcSHans Rosenfeld /*
2300788c47fcSHans Rosenfeld * Reset controller if it's still in ready state.
2301788c47fcSHans Rosenfeld */
2302788c47fcSHans Rosenfeld if (nvme_reset(nvme, B_FALSE) == B_FALSE) {
2303788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN, "!unable to reset controller");
2304788c47fcSHans Rosenfeld ddi_fm_service_impact(nvme->n_dip, DDI_SERVICE_LOST);
2305788c47fcSHans Rosenfeld nvme->n_dead = B_TRUE;
2306788c47fcSHans Rosenfeld goto fail;
2307788c47fcSHans Rosenfeld }
2308788c47fcSHans Rosenfeld
2309788c47fcSHans Rosenfeld /*
2310788c47fcSHans Rosenfeld * Create the admin queue pair.
2311788c47fcSHans Rosenfeld */
2312788c47fcSHans Rosenfeld if (nvme_alloc_qpair(nvme, nvme->n_admin_queue_len, &nvme->n_adminq, 0)
2313788c47fcSHans Rosenfeld != DDI_SUCCESS) {
2314788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN,
2315788c47fcSHans Rosenfeld "!unable to allocate admin qpair");
2316788c47fcSHans Rosenfeld goto fail;
2317788c47fcSHans Rosenfeld }
2318788c47fcSHans Rosenfeld nvme->n_ioq = kmem_alloc(sizeof (nvme_qpair_t *), KM_SLEEP);
2319788c47fcSHans Rosenfeld nvme->n_ioq[0] = nvme->n_adminq;
2320788c47fcSHans Rosenfeld
2321788c47fcSHans Rosenfeld nvme->n_progress |= NVME_ADMIN_QUEUE;
2322788c47fcSHans Rosenfeld
2323788c47fcSHans Rosenfeld (void) ddi_prop_update_int(DDI_DEV_T_NONE, nvme->n_dip,
2324788c47fcSHans Rosenfeld "admin-queue-len", nvme->n_admin_queue_len);
2325788c47fcSHans Rosenfeld
2326788c47fcSHans Rosenfeld aqa.b.aqa_asqs = aqa.b.aqa_acqs = nvme->n_admin_queue_len - 1;
2327788c47fcSHans Rosenfeld asq = nvme->n_adminq->nq_sqdma->nd_cookie.dmac_laddress;
2328788c47fcSHans Rosenfeld acq = nvme->n_adminq->nq_cqdma->nd_cookie.dmac_laddress;
2329788c47fcSHans Rosenfeld
2330788c47fcSHans Rosenfeld ASSERT((asq & (nvme->n_pagesize - 1)) == 0);
2331788c47fcSHans Rosenfeld ASSERT((acq & (nvme->n_pagesize - 1)) == 0);
2332788c47fcSHans Rosenfeld
2333788c47fcSHans Rosenfeld nvme_put32(nvme, NVME_REG_AQA, aqa.r);
2334788c47fcSHans Rosenfeld nvme_put64(nvme, NVME_REG_ASQ, asq);
2335788c47fcSHans Rosenfeld nvme_put64(nvme, NVME_REG_ACQ, acq);
2336788c47fcSHans Rosenfeld
2337788c47fcSHans Rosenfeld cc.b.cc_ams = 0; /* use Round-Robin arbitration */
2338788c47fcSHans Rosenfeld cc.b.cc_css = 0; /* use NVM command set */
2339788c47fcSHans Rosenfeld cc.b.cc_mps = nvme->n_pageshift - 12;
2340788c47fcSHans Rosenfeld cc.b.cc_shn = 0; /* no shutdown in progress */
2341788c47fcSHans Rosenfeld cc.b.cc_en = 1; /* enable controller */
23420fe6d382SPete Shephard cc.b.cc_iosqes = 6; /* submission queue entry is 2^6 bytes long */
23430fe6d382SPete Shephard cc.b.cc_iocqes = 4; /* completion queue entry is 2^4 bytes long */
2344788c47fcSHans Rosenfeld
2345788c47fcSHans Rosenfeld nvme_put32(nvme, NVME_REG_CC, cc.r);
2346788c47fcSHans Rosenfeld
2347788c47fcSHans Rosenfeld /*
2348788c47fcSHans Rosenfeld * Wait for the controller to become ready.
2349788c47fcSHans Rosenfeld */
2350788c47fcSHans Rosenfeld csts.r = nvme_get32(nvme, NVME_REG_CSTS);
2351788c47fcSHans Rosenfeld if (csts.b.csts_rdy == 0) {
2352788c47fcSHans Rosenfeld for (i = 0; i != nvme->n_timeout * 10; i++) {
2353788c47fcSHans Rosenfeld delay(drv_usectohz(50000));
2354788c47fcSHans Rosenfeld csts.r = nvme_get32(nvme, NVME_REG_CSTS);
2355788c47fcSHans Rosenfeld
2356788c47fcSHans Rosenfeld if (csts.b.csts_cfs == 1) {
2357788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN,
2358788c47fcSHans Rosenfeld "!controller fatal status at init");
2359788c47fcSHans Rosenfeld ddi_fm_service_impact(nvme->n_dip,
2360788c47fcSHans Rosenfeld DDI_SERVICE_LOST);
2361788c47fcSHans Rosenfeld nvme->n_dead = B_TRUE;
2362788c47fcSHans Rosenfeld goto fail;
2363788c47fcSHans Rosenfeld }
2364788c47fcSHans Rosenfeld
2365788c47fcSHans Rosenfeld if (csts.b.csts_rdy == 1)
2366788c47fcSHans Rosenfeld break;
2367788c47fcSHans Rosenfeld }
2368788c47fcSHans Rosenfeld }
2369788c47fcSHans Rosenfeld
2370788c47fcSHans Rosenfeld if (csts.b.csts_rdy == 0) {
2371788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN, "!controller not ready");
2372788c47fcSHans Rosenfeld ddi_fm_service_impact(nvme->n_dip, DDI_SERVICE_LOST);
2373788c47fcSHans Rosenfeld nvme->n_dead = B_TRUE;
2374788c47fcSHans Rosenfeld goto fail;
2375788c47fcSHans Rosenfeld }
2376788c47fcSHans Rosenfeld
2377788c47fcSHans Rosenfeld /*
2378788c47fcSHans Rosenfeld * Assume an abort command limit of 1. We'll destroy and re-init
2379788c47fcSHans Rosenfeld * that later when we know the true abort command limit.
2380788c47fcSHans Rosenfeld */
2381788c47fcSHans Rosenfeld sema_init(&nvme->n_abort_sema, 1, NULL, SEMA_DRIVER, NULL);
2382788c47fcSHans Rosenfeld
2383788c47fcSHans Rosenfeld /*
2384f0b0e0b1SHans Rosenfeld * Setup initial interrupt for admin queue.
2385f0b0e0b1SHans Rosenfeld */
2386f0b0e0b1SHans Rosenfeld if ((nvme_setup_interrupts(nvme, DDI_INTR_TYPE_MSIX, 1)
2387f0b0e0b1SHans Rosenfeld != DDI_SUCCESS) &&
2388f0b0e0b1SHans Rosenfeld (nvme_setup_interrupts(nvme, DDI_INTR_TYPE_MSI, 1)
2389f0b0e0b1SHans Rosenfeld != DDI_SUCCESS) &&
2390f0b0e0b1SHans Rosenfeld (nvme_setup_interrupts(nvme, DDI_INTR_TYPE_FIXED, 1)
2391f0b0e0b1SHans Rosenfeld != DDI_SUCCESS)) {
2392f0b0e0b1SHans Rosenfeld dev_err(nvme->n_dip, CE_WARN,
2393f0b0e0b1SHans Rosenfeld "!failed to setup initial interrupt");
2394f0b0e0b1SHans Rosenfeld goto fail;
2395f0b0e0b1SHans Rosenfeld }
2396f0b0e0b1SHans Rosenfeld
2397f0b0e0b1SHans Rosenfeld /*
2398788c47fcSHans Rosenfeld * Post an asynchronous event command to catch errors.
2399788c47fcSHans Rosenfeld */
2400ba636d1cSHans Rosenfeld nvme_async_event(nvme);
2401788c47fcSHans Rosenfeld
2402788c47fcSHans Rosenfeld /*
2403788c47fcSHans Rosenfeld * Identify Controller
2404788c47fcSHans Rosenfeld */
240569a33c23SHans Rosenfeld if (nvme_identify(nvme, 0, (void **)&nvme->n_idctl) != 0) {
2406788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN,
2407788c47fcSHans Rosenfeld "!failed to identify controller");
2408788c47fcSHans Rosenfeld goto fail;
2409788c47fcSHans Rosenfeld }
2410788c47fcSHans Rosenfeld
2411788c47fcSHans Rosenfeld /*
2412f0e4d8f0SHans Rosenfeld * Get Vendor & Product ID
2413f0e4d8f0SHans Rosenfeld */
2414f0e4d8f0SHans Rosenfeld bcopy(nvme->n_idctl->id_model, model, sizeof (nvme->n_idctl->id_model));
2415f0e4d8f0SHans Rosenfeld model[sizeof (nvme->n_idctl->id_model)] = '\0';
2416f0e4d8f0SHans Rosenfeld sata_split_model(model, &vendor, &product);
2417f0e4d8f0SHans Rosenfeld
2418f0e4d8f0SHans Rosenfeld if (vendor == NULL)
2419f0e4d8f0SHans Rosenfeld nvme->n_vendor = strdup("NVMe");
2420f0e4d8f0SHans Rosenfeld else
2421f0e4d8f0SHans Rosenfeld nvme->n_vendor = strdup(vendor);
2422f0e4d8f0SHans Rosenfeld
2423f0e4d8f0SHans Rosenfeld nvme->n_product = strdup(product);
2424f0e4d8f0SHans Rosenfeld
2425f0e4d8f0SHans Rosenfeld /*
2426788c47fcSHans Rosenfeld * Get controller limits.
2427788c47fcSHans Rosenfeld */
2428788c47fcSHans Rosenfeld nvme->n_async_event_limit = MAX(NVME_MIN_ASYNC_EVENT_LIMIT,
2429788c47fcSHans Rosenfeld MIN(nvme->n_admin_queue_len / 10,
2430788c47fcSHans Rosenfeld MIN(nvme->n_idctl->id_aerl + 1, nvme->n_async_event_limit)));
2431788c47fcSHans Rosenfeld
2432788c47fcSHans Rosenfeld (void) ddi_prop_update_int(DDI_DEV_T_NONE, nvme->n_dip,
2433788c47fcSHans Rosenfeld "async-event-limit", nvme->n_async_event_limit);
2434788c47fcSHans Rosenfeld
2435788c47fcSHans Rosenfeld nvme->n_abort_command_limit = nvme->n_idctl->id_acl + 1;
2436788c47fcSHans Rosenfeld
2437071aaa7bSHans Rosenfeld /*
2438071aaa7bSHans Rosenfeld * Reinitialize the semaphore with the true abort command limit
2439071aaa7bSHans Rosenfeld * supported by the hardware. It's not necessary to disable interrupts
2440071aaa7bSHans Rosenfeld * as only command aborts use the semaphore, and no commands are
2441071aaa7bSHans Rosenfeld * executed or aborted while we're here.
2442071aaa7bSHans Rosenfeld */
2443788c47fcSHans Rosenfeld sema_destroy(&nvme->n_abort_sema);
2444788c47fcSHans Rosenfeld sema_init(&nvme->n_abort_sema, nvme->n_abort_command_limit - 1, NULL,
2445788c47fcSHans Rosenfeld SEMA_DRIVER, NULL);
2446788c47fcSHans Rosenfeld
2447788c47fcSHans Rosenfeld nvme->n_progress |= NVME_CTRL_LIMITS;
2448788c47fcSHans Rosenfeld
2449788c47fcSHans Rosenfeld if (nvme->n_idctl->id_mdts == 0)
2450788c47fcSHans Rosenfeld nvme->n_max_data_transfer_size = nvme->n_pagesize * 65536;
2451788c47fcSHans Rosenfeld else
2452788c47fcSHans Rosenfeld nvme->n_max_data_transfer_size =
2453788c47fcSHans Rosenfeld 1ull << (nvme->n_pageshift + nvme->n_idctl->id_mdts);
2454788c47fcSHans Rosenfeld
2455788c47fcSHans Rosenfeld nvme->n_error_log_len = nvme->n_idctl->id_elpe + 1;
2456788c47fcSHans Rosenfeld
2457788c47fcSHans Rosenfeld /*
2458788c47fcSHans Rosenfeld * Limit n_max_data_transfer_size to what we can handle in one PRP.
2459788c47fcSHans Rosenfeld * Chained PRPs are currently unsupported.
2460788c47fcSHans Rosenfeld *
2461788c47fcSHans Rosenfeld * This is a no-op on hardware which doesn't support a transfer size
2462788c47fcSHans Rosenfeld * big enough to require chained PRPs.
2463788c47fcSHans Rosenfeld */
2464788c47fcSHans Rosenfeld nvme->n_max_data_transfer_size = MIN(nvme->n_max_data_transfer_size,
2465788c47fcSHans Rosenfeld (nvme->n_pagesize / sizeof (uint64_t) * nvme->n_pagesize));
2466788c47fcSHans Rosenfeld
2467788c47fcSHans Rosenfeld nvme->n_prp_dma_attr.dma_attr_maxxfer = nvme->n_max_data_transfer_size;
2468788c47fcSHans Rosenfeld
2469788c47fcSHans Rosenfeld /*
2470788c47fcSHans Rosenfeld * Make sure the minimum/maximum queue entry sizes are not
2471788c47fcSHans Rosenfeld * larger/smaller than the default.
2472788c47fcSHans Rosenfeld */
2473788c47fcSHans Rosenfeld
2474788c47fcSHans Rosenfeld if (((1 << nvme->n_idctl->id_sqes.qes_min) > sizeof (nvme_sqe_t)) ||
2475788c47fcSHans Rosenfeld ((1 << nvme->n_idctl->id_sqes.qes_max) < sizeof (nvme_sqe_t)) ||
2476788c47fcSHans Rosenfeld ((1 << nvme->n_idctl->id_cqes.qes_min) > sizeof (nvme_cqe_t)) ||
2477788c47fcSHans Rosenfeld ((1 << nvme->n_idctl->id_cqes.qes_max) < sizeof (nvme_cqe_t)))
2478788c47fcSHans Rosenfeld goto fail;
2479788c47fcSHans Rosenfeld
2480788c47fcSHans Rosenfeld /*
2481788c47fcSHans Rosenfeld * Check for the presence of a Volatile Write Cache. If present,
24825f836503SHans Rosenfeld * enable or disable based on the value of the property
24835f836503SHans Rosenfeld * volatile-write-cache-enable (default is enabled).
2484788c47fcSHans Rosenfeld */
24855f836503SHans Rosenfeld nvme->n_write_cache_present =
24865f836503SHans Rosenfeld nvme->n_idctl->id_vwc.vwc_present == 0 ? B_FALSE : B_TRUE;
24875f836503SHans Rosenfeld
24885f836503SHans Rosenfeld (void) ddi_prop_update_int(DDI_DEV_T_NONE, nvme->n_dip,
24895f836503SHans Rosenfeld "volatile-write-cache-present",
24905f836503SHans Rosenfeld nvme->n_write_cache_present ? 1 : 0);
24915f836503SHans Rosenfeld
24925f836503SHans Rosenfeld if (!nvme->n_write_cache_present) {
24935f836503SHans Rosenfeld nvme->n_write_cache_enabled = B_FALSE;
249469a33c23SHans Rosenfeld } else if (nvme_write_cache_set(nvme, nvme->n_write_cache_enabled)
249569a33c23SHans Rosenfeld != 0) {
24965f836503SHans Rosenfeld dev_err(nvme->n_dip, CE_WARN,
24975f836503SHans Rosenfeld "!failed to %sable volatile write cache",
24985f836503SHans Rosenfeld nvme->n_write_cache_enabled ? "en" : "dis");
2499788c47fcSHans Rosenfeld /*
25005f836503SHans Rosenfeld * Assume the cache is (still) enabled.
2501788c47fcSHans Rosenfeld */
25025f836503SHans Rosenfeld nvme->n_write_cache_enabled = B_TRUE;
2503788c47fcSHans Rosenfeld }
2504788c47fcSHans Rosenfeld
25055f836503SHans Rosenfeld (void) ddi_prop_update_int(DDI_DEV_T_NONE, nvme->n_dip,
25065f836503SHans Rosenfeld "volatile-write-cache-enable",
25075f836503SHans Rosenfeld nvme->n_write_cache_enabled ? 1 : 0);
25085f836503SHans Rosenfeld
2509788c47fcSHans Rosenfeld /*
2510032cbfebSHans Rosenfeld * Assume LBA Range Type feature is supported. If it isn't this
2511032cbfebSHans Rosenfeld * will be set to B_FALSE by nvme_get_features().
2512788c47fcSHans Rosenfeld */
2513032cbfebSHans Rosenfeld nvme->n_lba_range_supported = B_TRUE;
2514032cbfebSHans Rosenfeld
2515032cbfebSHans Rosenfeld /*
2516032cbfebSHans Rosenfeld * Check support for Autonomous Power State Transition.
2517032cbfebSHans Rosenfeld */
2518032cbfebSHans Rosenfeld if (NVME_VERSION_ATLEAST(&nvme->n_version, 1, 1))
2519032cbfebSHans Rosenfeld nvme->n_auto_pst_supported =
2520032cbfebSHans Rosenfeld nvme->n_idctl->id_apsta.ap_sup == 0 ? B_FALSE : B_TRUE;
2521788c47fcSHans Rosenfeld
2522788c47fcSHans Rosenfeld /*
2523788c47fcSHans Rosenfeld * Identify Namespaces
2524788c47fcSHans Rosenfeld */
2525788c47fcSHans Rosenfeld nvme->n_namespace_count = nvme->n_idctl->id_nn;
2526032cbfebSHans Rosenfeld if (nvme->n_namespace_count > NVME_MINOR_MAX) {
2527032cbfebSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN,
2528032cbfebSHans Rosenfeld "!too many namespaces: %d, limiting to %d\n",
2529032cbfebSHans Rosenfeld nvme->n_namespace_count, NVME_MINOR_MAX);
2530032cbfebSHans Rosenfeld nvme->n_namespace_count = NVME_MINOR_MAX;
2531032cbfebSHans Rosenfeld }
2532032cbfebSHans Rosenfeld
2533788c47fcSHans Rosenfeld nvme->n_ns = kmem_zalloc(sizeof (nvme_namespace_t) *
2534788c47fcSHans Rosenfeld nvme->n_namespace_count, KM_SLEEP);
2535788c47fcSHans Rosenfeld
2536788c47fcSHans Rosenfeld for (i = 0; i != nvme->n_namespace_count; i++) {
2537032cbfebSHans Rosenfeld mutex_init(&nvme->n_ns[i].ns_minor.nm_mutex, NULL, MUTEX_DRIVER,
2538032cbfebSHans Rosenfeld NULL);
2539032cbfebSHans Rosenfeld if (nvme_init_ns(nvme, i + 1) != DDI_SUCCESS)
2540788c47fcSHans Rosenfeld goto fail;
2541788c47fcSHans Rosenfeld }
2542788c47fcSHans Rosenfeld
2543788c47fcSHans Rosenfeld /*
2544788c47fcSHans Rosenfeld * Try to set up MSI/MSI-X interrupts.
2545788c47fcSHans Rosenfeld */
2546788c47fcSHans Rosenfeld if ((nvme->n_intr_types & (DDI_INTR_TYPE_MSI | DDI_INTR_TYPE_MSIX))
2547788c47fcSHans Rosenfeld != 0) {
2548788c47fcSHans Rosenfeld nvme_release_interrupts(nvme);
2549788c47fcSHans Rosenfeld
2550788c47fcSHans Rosenfeld nqueues = MIN(UINT16_MAX, ncpus);
2551788c47fcSHans Rosenfeld
2552788c47fcSHans Rosenfeld if ((nvme_setup_interrupts(nvme, DDI_INTR_TYPE_MSIX,
2553788c47fcSHans Rosenfeld nqueues) != DDI_SUCCESS) &&
2554788c47fcSHans Rosenfeld (nvme_setup_interrupts(nvme, DDI_INTR_TYPE_MSI,
2555788c47fcSHans Rosenfeld nqueues) != DDI_SUCCESS)) {
2556788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN,
2557788c47fcSHans Rosenfeld "!failed to setup MSI/MSI-X interrupts");
2558788c47fcSHans Rosenfeld goto fail;
2559788c47fcSHans Rosenfeld }
2560788c47fcSHans Rosenfeld }
2561788c47fcSHans Rosenfeld
2562788c47fcSHans Rosenfeld nqueues = nvme->n_intr_cnt;
2563788c47fcSHans Rosenfeld
2564788c47fcSHans Rosenfeld /*
2565788c47fcSHans Rosenfeld * Create I/O queue pairs.
2566788c47fcSHans Rosenfeld */
256769a33c23SHans Rosenfeld
256869a33c23SHans Rosenfeld if (nvme_set_nqueues(nvme, &nqueues) != 0) {
2569788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN,
257069a33c23SHans Rosenfeld "!failed to set number of I/O queues to %d",
257169a33c23SHans Rosenfeld nvme->n_intr_cnt);
2572788c47fcSHans Rosenfeld goto fail;
2573788c47fcSHans Rosenfeld }
2574788c47fcSHans Rosenfeld
2575788c47fcSHans Rosenfeld /*
2576788c47fcSHans Rosenfeld * Reallocate I/O queue array
2577788c47fcSHans Rosenfeld */
2578788c47fcSHans Rosenfeld kmem_free(nvme->n_ioq, sizeof (nvme_qpair_t *));
2579788c47fcSHans Rosenfeld nvme->n_ioq = kmem_zalloc(sizeof (nvme_qpair_t *) *
258069a33c23SHans Rosenfeld (nqueues + 1), KM_SLEEP);
2581788c47fcSHans Rosenfeld nvme->n_ioq[0] = nvme->n_adminq;
2582788c47fcSHans Rosenfeld
258369a33c23SHans Rosenfeld nvme->n_ioq_count = nqueues;
258469a33c23SHans Rosenfeld
2585788c47fcSHans Rosenfeld /*
2586788c47fcSHans Rosenfeld * If we got less queues than we asked for we might as well give
2587788c47fcSHans Rosenfeld * some of the interrupt vectors back to the system.
2588788c47fcSHans Rosenfeld */
258969a33c23SHans Rosenfeld if (nvme->n_ioq_count < nvme->n_intr_cnt) {
2590788c47fcSHans Rosenfeld nvme_release_interrupts(nvme);
2591788c47fcSHans Rosenfeld
25920fe6d382SPete Shephard if (nvme_setup_interrupts(nvme, nvme->n_intr_type,
25930fe6d382SPete Shephard nvme->n_ioq_count) != DDI_SUCCESS) {
2594788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN,
2595788c47fcSHans Rosenfeld "!failed to reduce number of interrupts");
2596788c47fcSHans Rosenfeld goto fail;
2597788c47fcSHans Rosenfeld }
2598788c47fcSHans Rosenfeld }
2599788c47fcSHans Rosenfeld
2600788c47fcSHans Rosenfeld /*
2601788c47fcSHans Rosenfeld * Alloc & register I/O queue pairs
2602788c47fcSHans Rosenfeld */
2603788c47fcSHans Rosenfeld nvme->n_io_queue_len =
2604788c47fcSHans Rosenfeld MIN(nvme->n_io_queue_len, nvme->n_max_queue_entries);
2605788c47fcSHans Rosenfeld (void) ddi_prop_update_int(DDI_DEV_T_NONE, nvme->n_dip, "io-queue-len",
2606788c47fcSHans Rosenfeld nvme->n_io_queue_len);
2607788c47fcSHans Rosenfeld
2608788c47fcSHans Rosenfeld for (i = 1; i != nvme->n_ioq_count + 1; i++) {
2609788c47fcSHans Rosenfeld if (nvme_alloc_qpair(nvme, nvme->n_io_queue_len,
2610788c47fcSHans Rosenfeld &nvme->n_ioq[i], i) != DDI_SUCCESS) {
2611788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN,
2612788c47fcSHans Rosenfeld "!unable to allocate I/O qpair %d", i);
2613788c47fcSHans Rosenfeld goto fail;
2614788c47fcSHans Rosenfeld }
2615788c47fcSHans Rosenfeld
261669a33c23SHans Rosenfeld if (nvme_create_io_qpair(nvme, nvme->n_ioq[i], i) != 0) {
2617788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN,
2618788c47fcSHans Rosenfeld "!unable to create I/O qpair %d", i);
2619788c47fcSHans Rosenfeld goto fail;
2620788c47fcSHans Rosenfeld }
2621788c47fcSHans Rosenfeld }
2622788c47fcSHans Rosenfeld
2623788c47fcSHans Rosenfeld /*
2624788c47fcSHans Rosenfeld * Post more asynchronous events commands to reduce event reporting
2625788c47fcSHans Rosenfeld * latency as suggested by the spec.
2626788c47fcSHans Rosenfeld */
2627ba636d1cSHans Rosenfeld for (i = 1; i != nvme->n_async_event_limit; i++)
2628ba636d1cSHans Rosenfeld nvme_async_event(nvme);
2629788c47fcSHans Rosenfeld
2630788c47fcSHans Rosenfeld return (DDI_SUCCESS);
2631788c47fcSHans Rosenfeld
2632788c47fcSHans Rosenfeld fail:
2633788c47fcSHans Rosenfeld (void) nvme_reset(nvme, B_FALSE);
2634788c47fcSHans Rosenfeld return (DDI_FAILURE);
2635788c47fcSHans Rosenfeld }
2636788c47fcSHans Rosenfeld
2637788c47fcSHans Rosenfeld static uint_t
nvme_intr(caddr_t arg1,caddr_t arg2)2638788c47fcSHans Rosenfeld nvme_intr(caddr_t arg1, caddr_t arg2)
2639788c47fcSHans Rosenfeld {
2640788c47fcSHans Rosenfeld /*LINTED: E_PTR_BAD_CAST_ALIGN*/
2641788c47fcSHans Rosenfeld nvme_t *nvme = (nvme_t *)arg1;
2642788c47fcSHans Rosenfeld int inum = (int)(uintptr_t)arg2;
2643f0b0e0b1SHans Rosenfeld int ccnt = 0;
2644788c47fcSHans Rosenfeld int qnum;
2645788c47fcSHans Rosenfeld nvme_cmd_t *cmd;
2646788c47fcSHans Rosenfeld
2647788c47fcSHans Rosenfeld if (inum >= nvme->n_intr_cnt)
2648788c47fcSHans Rosenfeld return (DDI_INTR_UNCLAIMED);
2649788c47fcSHans Rosenfeld
265069a33c23SHans Rosenfeld if (nvme->n_dead)
265169a33c23SHans Rosenfeld return (nvme->n_intr_type == DDI_INTR_TYPE_FIXED ?
265269a33c23SHans Rosenfeld DDI_INTR_UNCLAIMED : DDI_INTR_CLAIMED);
265369a33c23SHans Rosenfeld
2654788c47fcSHans Rosenfeld /*
2655788c47fcSHans Rosenfeld * The interrupt vector a queue uses is calculated as queue_idx %
2656788c47fcSHans Rosenfeld * intr_cnt in nvme_create_io_qpair(). Iterate through the queue array
2657788c47fcSHans Rosenfeld * in steps of n_intr_cnt to process all queues using this vector.
2658788c47fcSHans Rosenfeld */
2659788c47fcSHans Rosenfeld for (qnum = inum;
2660788c47fcSHans Rosenfeld qnum < nvme->n_ioq_count + 1 && nvme->n_ioq[qnum] != NULL;
2661788c47fcSHans Rosenfeld qnum += nvme->n_intr_cnt) {
2662788c47fcSHans Rosenfeld while ((cmd = nvme_retrieve_cmd(nvme, nvme->n_ioq[qnum]))) {
2663788c47fcSHans Rosenfeld taskq_dispatch_ent((taskq_t *)cmd->nc_nvme->n_cmd_taskq,
2664788c47fcSHans Rosenfeld cmd->nc_callback, cmd, TQ_NOSLEEP, &cmd->nc_tqent);
2665f0b0e0b1SHans Rosenfeld ccnt++;
2666788c47fcSHans Rosenfeld }
2667788c47fcSHans Rosenfeld }
2668788c47fcSHans Rosenfeld
2669f0b0e0b1SHans Rosenfeld return (ccnt > 0 ? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED);
2670788c47fcSHans Rosenfeld }
2671788c47fcSHans Rosenfeld
2672788c47fcSHans Rosenfeld static void
nvme_release_interrupts(nvme_t * nvme)2673071aaa7bSHans Rosenfeld nvme_release_interrupts(nvme_t *nvme)
2674788c47fcSHans Rosenfeld {
2675788c47fcSHans Rosenfeld int i;
2676788c47fcSHans Rosenfeld
2677788c47fcSHans Rosenfeld for (i = 0; i < nvme->n_intr_cnt; i++) {
2678788c47fcSHans Rosenfeld if (nvme->n_inth[i] == NULL)
2679788c47fcSHans Rosenfeld break;
2680788c47fcSHans Rosenfeld
2681788c47fcSHans Rosenfeld if (nvme->n_intr_cap & DDI_INTR_FLAG_BLOCK)
2682788c47fcSHans Rosenfeld (void) ddi_intr_block_disable(&nvme->n_inth[i], 1);
2683788c47fcSHans Rosenfeld else
2684788c47fcSHans Rosenfeld (void) ddi_intr_disable(nvme->n_inth[i]);
2685788c47fcSHans Rosenfeld
2686788c47fcSHans Rosenfeld (void) ddi_intr_remove_handler(nvme->n_inth[i]);
2687788c47fcSHans Rosenfeld (void) ddi_intr_free(nvme->n_inth[i]);
2688788c47fcSHans Rosenfeld }
2689788c47fcSHans Rosenfeld
2690788c47fcSHans Rosenfeld kmem_free(nvme->n_inth, nvme->n_inth_sz);
2691788c47fcSHans Rosenfeld nvme->n_inth = NULL;
2692788c47fcSHans Rosenfeld nvme->n_inth_sz = 0;
2693788c47fcSHans Rosenfeld
2694788c47fcSHans Rosenfeld nvme->n_progress &= ~NVME_INTERRUPTS;
2695788c47fcSHans Rosenfeld }
2696788c47fcSHans Rosenfeld
2697788c47fcSHans Rosenfeld static int
nvme_setup_interrupts(nvme_t * nvme,int intr_type,int nqpairs)2698788c47fcSHans Rosenfeld nvme_setup_interrupts(nvme_t *nvme, int intr_type, int nqpairs)
2699788c47fcSHans Rosenfeld {
2700788c47fcSHans Rosenfeld int nintrs, navail, count;
2701788c47fcSHans Rosenfeld int ret;
2702788c47fcSHans Rosenfeld int i;
2703788c47fcSHans Rosenfeld
2704788c47fcSHans Rosenfeld if (nvme->n_intr_types == 0) {
2705788c47fcSHans Rosenfeld ret = ddi_intr_get_supported_types(nvme->n_dip,
2706788c47fcSHans Rosenfeld &nvme->n_intr_types);
2707788c47fcSHans Rosenfeld if (ret != DDI_SUCCESS) {
2708788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN,
2709788c47fcSHans Rosenfeld "!%s: ddi_intr_get_supported types failed",
2710788c47fcSHans Rosenfeld __func__);
2711788c47fcSHans Rosenfeld return (ret);
2712788c47fcSHans Rosenfeld }
27131c08585fSHans Rosenfeld #ifdef __x86
27141c08585fSHans Rosenfeld if (get_hwenv() == HW_VMWARE)
27151c08585fSHans Rosenfeld nvme->n_intr_types &= ~DDI_INTR_TYPE_MSIX;
27161c08585fSHans Rosenfeld #endif
2717788c47fcSHans Rosenfeld }
2718788c47fcSHans Rosenfeld
2719788c47fcSHans Rosenfeld if ((nvme->n_intr_types & intr_type) == 0)
2720788c47fcSHans Rosenfeld return (DDI_FAILURE);
2721788c47fcSHans Rosenfeld
2722788c47fcSHans Rosenfeld ret = ddi_intr_get_nintrs(nvme->n_dip, intr_type, &nintrs);
2723788c47fcSHans Rosenfeld if (ret != DDI_SUCCESS) {
2724788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN, "!%s: ddi_intr_get_nintrs failed",
2725788c47fcSHans Rosenfeld __func__);
2726788c47fcSHans Rosenfeld return (ret);
2727788c47fcSHans Rosenfeld }
2728788c47fcSHans Rosenfeld
2729788c47fcSHans Rosenfeld ret = ddi_intr_get_navail(nvme->n_dip, intr_type, &navail);
2730788c47fcSHans Rosenfeld if (ret != DDI_SUCCESS) {
2731788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN, "!%s: ddi_intr_get_navail failed",
2732788c47fcSHans Rosenfeld __func__);
2733788c47fcSHans Rosenfeld return (ret);
2734788c47fcSHans Rosenfeld }
2735788c47fcSHans Rosenfeld
2736788c47fcSHans Rosenfeld /* We want at most one interrupt per queue pair. */
2737788c47fcSHans Rosenfeld if (navail > nqpairs)
2738788c47fcSHans Rosenfeld navail = nqpairs;
2739788c47fcSHans Rosenfeld
2740788c47fcSHans Rosenfeld nvme->n_inth_sz = sizeof (ddi_intr_handle_t) * navail;
2741788c47fcSHans Rosenfeld nvme->n_inth = kmem_zalloc(nvme->n_inth_sz, KM_SLEEP);
2742788c47fcSHans Rosenfeld
2743788c47fcSHans Rosenfeld ret = ddi_intr_alloc(nvme->n_dip, nvme->n_inth, intr_type, 0, navail,
2744788c47fcSHans Rosenfeld &count, 0);
2745788c47fcSHans Rosenfeld if (ret != DDI_SUCCESS) {
2746788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN, "!%s: ddi_intr_alloc failed",
2747788c47fcSHans Rosenfeld __func__);
2748788c47fcSHans Rosenfeld goto fail;
2749788c47fcSHans Rosenfeld }
2750788c47fcSHans Rosenfeld
2751788c47fcSHans Rosenfeld nvme->n_intr_cnt = count;
2752788c47fcSHans Rosenfeld
2753788c47fcSHans Rosenfeld ret = ddi_intr_get_pri(nvme->n_inth[0], &nvme->n_intr_pri);
2754788c47fcSHans Rosenfeld if (ret != DDI_SUCCESS) {
2755788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN, "!%s: ddi_intr_get_pri failed",
2756788c47fcSHans Rosenfeld __func__);
2757788c47fcSHans Rosenfeld goto fail;
2758788c47fcSHans Rosenfeld }
2759788c47fcSHans Rosenfeld
2760788c47fcSHans Rosenfeld for (i = 0; i < count; i++) {
2761788c47fcSHans Rosenfeld ret = ddi_intr_add_handler(nvme->n_inth[i], nvme_intr,
2762788c47fcSHans Rosenfeld (void *)nvme, (void *)(uintptr_t)i);
2763788c47fcSHans Rosenfeld if (ret != DDI_SUCCESS) {
2764788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN,
2765788c47fcSHans Rosenfeld "!%s: ddi_intr_add_handler failed", __func__);
2766788c47fcSHans Rosenfeld goto fail;
2767788c47fcSHans Rosenfeld }
2768788c47fcSHans Rosenfeld }
2769788c47fcSHans Rosenfeld
2770788c47fcSHans Rosenfeld (void) ddi_intr_get_cap(nvme->n_inth[0], &nvme->n_intr_cap);
2771788c47fcSHans Rosenfeld
2772071aaa7bSHans Rosenfeld for (i = 0; i < count; i++) {
2773b68758a4SHans Rosenfeld if (nvme->n_intr_cap & DDI_INTR_FLAG_BLOCK)
2774b68758a4SHans Rosenfeld ret = ddi_intr_block_enable(&nvme->n_inth[i], 1);
2775b68758a4SHans Rosenfeld else
2776b68758a4SHans Rosenfeld ret = ddi_intr_enable(nvme->n_inth[i]);
2777788c47fcSHans Rosenfeld
2778b68758a4SHans Rosenfeld if (ret != DDI_SUCCESS) {
2779788c47fcSHans Rosenfeld dev_err(nvme->n_dip, CE_WARN,
2780b68758a4SHans Rosenfeld "!%s: enabling interrupt %d failed", __func__, i);
2781788c47fcSHans Rosenfeld goto fail;
2782788c47fcSHans Rosenfeld }
2783b68758a4SHans Rosenfeld }
2784788c47fcSHans Rosenfeld
2785788c47fcSHans Rosenfeld nvme->n_intr_type = intr_type;
2786788c47fcSHans Rosenfeld
2787788c47fcSHans Rosenfeld nvme->n_progress |= NVME_INTERRUPTS;
2788788c47fcSHans Rosenfeld
2789788c47fcSHans Rosenfeld return (DDI_SUCCESS);
2790788c47fcSHans Rosenfeld
2791788c47fcSHans Rosenfeld fail:
2792788c47fcSHans Rosenfeld nvme_release_interrupts(nvme);
2793788c47fcSHans Rosenfeld
2794788c47fcSHans Rosenfeld return (ret);
2795788c47fcSHans Rosenfeld }
2796788c47fcSHans Rosenfeld
2797788c47fcSHans Rosenfeld static int
nvme_fm_errcb(dev_info_t * dip,ddi_fm_error_t * fm_error,const void * arg)2798788c47fcSHans Rosenfeld nvme_fm_errcb(dev_info_t *dip, ddi_fm_error_t *fm_error, const void *arg)
2799788c47fcSHans Rosenfeld {
2800788c47fcSHans Rosenfeld _NOTE(ARGUNUSED(arg));
2801788c47fcSHans Rosenfeld
2802788c47fcSHans Rosenfeld pci_ereport_post(dip, fm_error, NULL);
2803788c47fcSHans Rosenfeld return (fm_error->fme_status);
2804788c47fcSHans Rosenfeld }
2805788c47fcSHans Rosenfeld
2806788c47fcSHans Rosenfeld static int
nvme_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)2807788c47fcSHans Rosenfeld nvme_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
2808788c47fcSHans Rosenfeld {
2809788c47fcSHans Rosenfeld nvme_t *nvme;
2810788c47fcSHans Rosenfeld int instance;
2811788c47fcSHans Rosenfeld int nregs;
2812788c47fcSHans Rosenfeld off_t regsize;
2813788c47fcSHans Rosenfeld int i;
2814788c47fcSHans Rosenfeld char name[32];
2815788c47fcSHans Rosenfeld
2816788c47fcSHans Rosenfeld if (cmd != DDI_ATTACH)
2817788c47fcSHans Rosenfeld return (DDI_FAILURE);
2818788c47fcSHans Rosenfeld
2819788c47fcSHans Rosenfeld instance = ddi_get_instance(dip);
2820788c47fcSHans Rosenfeld
2821788c47fcSHans Rosenfeld if (ddi_soft_state_zalloc(nvme_state, instance) != DDI_SUCCESS)
2822788c47fcSHans Rosenfeld return (DDI_FAILURE);
2823788c47fcSHans Rosenfeld
2824788c47fcSHans Rosenfeld nvme = ddi_get_soft_state(nvme_state, instance);
2825788c47fcSHans Rosenfeld ddi_set_driver_private(dip, nvme);
2826788c47fcSHans Rosenfeld nvme->n_dip = dip;
2827788c47fcSHans Rosenfeld
2828032cbfebSHans Rosenfeld mutex_init(&nvme->n_minor.nm_mutex, NULL, MUTEX_DRIVER, NULL);
2829032cbfebSHans Rosenfeld
2830788c47fcSHans Rosenfeld nvme->n_strict_version = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
2831788c47fcSHans Rosenfeld DDI_PROP_DONTPASS, "strict-version", 1) == 1 ? B_TRUE : B_FALSE;
2832788c47fcSHans Rosenfeld nvme->n_ignore_unknown_vendor_status = ddi_prop_get_int(DDI_DEV_T_ANY,
2833788c47fcSHans Rosenfeld dip, DDI_PROP_DONTPASS, "ignore-unknown-vendor-status", 0) == 1 ?
2834788c47fcSHans Rosenfeld B_TRUE : B_FALSE;
2835788c47fcSHans Rosenfeld nvme->n_admin_queue_len = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
2836788c47fcSHans Rosenfeld DDI_PROP_DONTPASS, "admin-queue-len", NVME_DEFAULT_ADMIN_QUEUE_LEN);
2837788c47fcSHans Rosenfeld nvme->n_io_queue_len = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
2838788c47fcSHans Rosenfeld DDI_PROP_DONTPASS, "io-queue-len", NVME_DEFAULT_IO_QUEUE_LEN);
2839788c47fcSHans Rosenfeld nvme->n_async_event_limit = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
2840788c47fcSHans Rosenfeld DDI_PROP_DONTPASS, "async-event-limit",
2841788c47fcSHans Rosenfeld NVME_DEFAULT_ASYNC_EVENT_LIMIT);
28425f836503SHans Rosenfeld nvme->n_write_cache_enabled = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
28435f836503SHans Rosenfeld DDI_PROP_DONTPASS, "volatile-write-cache-enable", 1) != 0 ?
28445f836503SHans Rosenfeld B_TRUE : B_FALSE;
28459974ca0cSHans Rosenfeld nvme->n_min_block_size = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
28469974ca0cSHans Rosenfeld DDI_PROP_DONTPASS, "min-phys-block-size",
28479974ca0cSHans Rosenfeld NVME_DEFAULT_MIN_BLOCK_SIZE);
28489974ca0cSHans Rosenfeld
28499974ca0cSHans Rosenfeld if (!ISP2(nvme->n_min_block_size) ||
28509974ca0cSHans Rosenfeld (nvme->n_min_block_size < NVME_DEFAULT_MIN_BLOCK_SIZE)) {
28519974ca0cSHans Rosenfeld dev_err(dip, CE_WARN, "!min-phys-block-size %s, "
28529974ca0cSHans Rosenfeld "using default %d", ISP2(nvme->n_min_block_size) ?
28539974ca0cSHans Rosenfeld "too low" : "not a power of 2",
28549974ca0cSHans Rosenfeld NVME_DEFAULT_MIN_BLOCK_SIZE);
28559974ca0cSHans Rosenfeld nvme->n_min_block_size = NVME_DEFAULT_MIN_BLOCK_SIZE;
28569974ca0cSHans Rosenfeld }
2857788c47fcSHans Rosenfeld
2858788c47fcSHans Rosenfeld if (nvme->n_admin_queue_len < NVME_MIN_ADMIN_QUEUE_LEN)
2859788c47fcSHans Rosenfeld nvme->n_admin_queue_len = NVME_MIN_ADMIN_QUEUE_LEN;
2860788c47fcSHans Rosenfeld else if (nvme->n_admin_queue_len > NVME_MAX_ADMIN_QUEUE_LEN)
2861788c47fcSHans Rosenfeld nvme->n_admin_queue_len = NVME_MAX_ADMIN_QUEUE_LEN;
2862788c47fcSHans Rosenfeld
2863788c47fcSHans Rosenfeld if (nvme->n_io_queue_len < NVME_MIN_IO_QUEUE_LEN)
2864788c47fcSHans Rosenfeld nvme->n_io_queue_len = NVME_MIN_IO_QUEUE_LEN;
2865788c47fcSHans Rosenfeld
2866788c47fcSHans Rosenfeld if (nvme->n_async_event_limit < 1)
2867788c47fcSHans Rosenfeld nvme->n_async_event_limit = NVME_DEFAULT_ASYNC_EVENT_LIMIT;
2868788c47fcSHans Rosenfeld
2869788c47fcSHans Rosenfeld nvme->n_reg_acc_attr = nvme_reg_acc_attr;
2870788c47fcSHans Rosenfeld nvme->n_queue_dma_attr = nvme_queue_dma_attr;
2871788c47fcSHans Rosenfeld nvme->n_prp_dma_attr = nvme_prp_dma_attr;
2872788c47fcSHans Rosenfeld nvme->n_sgl_dma_attr = nvme_sgl_dma_attr;
2873788c47fcSHans Rosenfeld
2874788c47fcSHans Rosenfeld /*
2875788c47fcSHans Rosenfeld * Setup FMA support.
2876788c47fcSHans Rosenfeld */
2877788c47fcSHans Rosenfeld nvme->n_fm_cap = ddi_getprop(DDI_DEV_T_ANY, dip,
2878788c47fcSHans Rosenfeld DDI_PROP_CANSLEEP | DDI_PROP_DONTPASS, "fm-capable",
2879788c47fcSHans Rosenfeld DDI_FM_EREPORT_CAPABLE | DDI_FM_ACCCHK_CAPABLE |
2880788c47fcSHans Rosenfeld DDI_FM_DMACHK_CAPABLE | DDI_FM_ERRCB_CAPABLE);
2881788c47fcSHans Rosenfeld
2882788c47fcSHans Rosenfeld ddi_fm_init(dip, &nvme->n_fm_cap, &nvme->n_fm_ibc);
2883788c47fcSHans Rosenfeld
2884788c47fcSHans Rosenfeld if (nvme->n_fm_cap) {
2885788c47fcSHans Rosenfeld if (nvme->n_fm_cap & DDI_FM_ACCCHK_CAPABLE)
2886788c47fcSHans Rosenfeld nvme->n_reg_acc_attr.devacc_attr_access =
2887788c47fcSHans Rosenfeld DDI_FLAGERR_ACC;
2888788c47fcSHans Rosenfeld
2889788c47fcSHans Rosenfeld if (nvme->n_fm_cap & DDI_FM_DMACHK_CAPABLE) {
2890788c47fcSHans Rosenfeld nvme->n_prp_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR;
2891788c47fcSHans Rosenfeld nvme->n_sgl_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR;
2892788c47fcSHans Rosenfeld }
2893788c47fcSHans Rosenfeld
2894788c47fcSHans Rosenfeld if (DDI_FM_EREPORT_CAP(nvme->n_fm_cap) ||
2895788c47fcSHans Rosenfeld DDI_FM_ERRCB_CAP(nvme->n_fm_cap))
2896788c47fcSHans Rosenfeld pci_ereport_setup(dip);
2897788c47fcSHans Rosenfeld
2898788c47fcSHans Rosenfeld if (DDI_FM_ERRCB_CAP(nvme->n_fm_cap))
2899788c47fcSHans Rosenfeld ddi_fm_handler_register(dip, nvme_fm_errcb,
2900788c47fcSHans Rosenfeld (void *)nvme);
2901788c47fcSHans Rosenfeld }
2902788c47fcSHans Rosenfeld
2903788c47fcSHans Rosenfeld nvme->n_progress |= NVME_FMA_INIT;
2904788c47fcSHans Rosenfeld
2905788c47fcSHans Rosenfeld /*
2906788c47fcSHans Rosenfeld * The spec defines several register sets. Only the controller
2907788c47fcSHans Rosenfeld * registers (set 1) are currently used.
2908788c47fcSHans Rosenfeld */
2909788c47fcSHans Rosenfeld if (ddi_dev_nregs(dip, &nregs) == DDI_FAILURE ||
2910788c47fcSHans Rosenfeld nregs < 2 ||
2911788c47fcSHans Rosenfeld ddi_dev_regsize(dip, 1, ®size) == DDI_FAILURE)
2912788c47fcSHans Rosenfeld goto fail;
2913788c47fcSHans Rosenfeld
2914788c47fcSHans Rosenfeld if (ddi_regs_map_setup(dip, 1, &nvme->n_regs, 0, regsize,
2915788c47fcSHans Rosenfeld &nvme->n_reg_acc_attr, &nvme->n_regh) != DDI_SUCCESS) {
2916788c47fcSHans Rosenfeld dev_err(dip, CE_WARN, "!failed to map regset 1");
2917788c47fcSHans Rosenfeld goto fail;
2918788c47fcSHans Rosenfeld }
2919788c47fcSHans Rosenfeld
2920788c47fcSHans Rosenfeld nvme->n_progress |= NVME_REGS_MAPPED;
2921788c47fcSHans Rosenfeld
2922788c47fcSHans Rosenfeld /*
2923788c47fcSHans Rosenfeld * Create taskq for command completion.
2924788c47fcSHans Rosenfeld */
2925788c47fcSHans Rosenfeld (void) snprintf(name, sizeof (name), "%s%d_cmd_taskq",
2926788c47fcSHans Rosenfeld ddi_driver_name(dip), ddi_get_instance(dip));
2927788c47fcSHans Rosenfeld nvme->n_cmd_taskq = ddi_taskq_create(dip, name, MIN(UINT16_MAX, ncpus),
2928788c47fcSHans Rosenfeld TASKQ_DEFAULTPRI, 0);
2929788c47fcSHans Rosenfeld if (nvme->n_cmd_taskq == NULL) {
2930788c47fcSHans Rosenfeld dev_err(dip, CE_WARN, "!failed to create cmd taskq");
2931788c47fcSHans Rosenfeld goto fail;
2932788c47fcSHans Rosenfeld }
2933788c47fcSHans Rosenfeld
2934b4eda73dSYouzhong Yang /*
2935b4eda73dSYouzhong Yang * Create PRP DMA cache
2936b4eda73dSYouzhong Yang */
2937b4eda73dSYouzhong Yang (void) snprintf(name, sizeof (name), "%s%d_prp_cache",
2938b4eda73dSYouzhong Yang ddi_driver_name(dip), ddi_get_instance(dip));
2939b4eda73dSYouzhong Yang nvme->n_prp_cache = kmem_cache_create(name, sizeof (nvme_dma_t),
2940b4eda73dSYouzhong Yang 0, nvme_prp_dma_constructor, nvme_prp_dma_destructor,
2941b4eda73dSYouzhong Yang NULL, (void *)nvme, NULL, 0);
2942788c47fcSHans Rosenfeld
2943788c47fcSHans Rosenfeld if (nvme_init(nvme) != DDI_SUCCESS)
2944788c47fcSHans Rosenfeld goto fail;
2945788c47fcSHans Rosenfeld
2946788c47fcSHans Rosenfeld /*
2947788c47fcSHans Rosenfeld * Attach the blkdev driver for each namespace.
2948788c47fcSHans Rosenfeld */
2949788c47fcSHans Rosenfeld for (i = 0; i != nvme->n_namespace_count; i++) {
2950032cbfebSHans Rosenfeld if (ddi_create_minor_node(nvme->n_dip, nvme->n_ns[i].ns_name,
2951032cbfebSHans Rosenfeld S_IFCHR, NVME_MINOR(ddi_get_instance(nvme->n_dip), i + 1),
2952032cbfebSHans Rosenfeld DDI_NT_NVME_ATTACHMENT_POINT, 0) != DDI_SUCCESS) {
2953032cbfebSHans Rosenfeld dev_err(dip, CE_WARN,
2954032cbfebSHans Rosenfeld "!failed to create minor node for namespace %d", i);
2955032cbfebSHans Rosenfeld goto fail;
2956032cbfebSHans Rosenfeld }
2957032cbfebSHans Rosenfeld
2958788c47fcSHans Rosenfeld if (nvme->n_ns[i].ns_ignore)
2959788c47fcSHans Rosenfeld continue;
2960788c47fcSHans Rosenfeld
2961788c47fcSHans Rosenfeld nvme->n_ns[i].ns_bd_hdl = bd_alloc_handle(&nvme->n_ns[i],
2962788c47fcSHans Rosenfeld &nvme_bd_ops, &nvme->n_prp_dma_attr, KM_SLEEP);
2963788c47fcSHans Rosenfeld
2964788c47fcSHans Rosenfeld if (nvme->n_ns[i].ns_bd_hdl == NULL) {
2965788c47fcSHans Rosenfeld dev_err(dip, CE_WARN,
2966788c47fcSHans Rosenfeld "!failed to get blkdev handle for namespace %d", i);
2967788c47fcSHans Rosenfeld goto fail;
2968788c47fcSHans Rosenfeld }
2969788c47fcSHans Rosenfeld
2970788c47fcSHans Rosenfeld if (bd_attach_handle(dip, nvme->n_ns[i].ns_bd_hdl)
2971788c47fcSHans Rosenfeld != DDI_SUCCESS) {
2972788c47fcSHans Rosenfeld dev_err(dip, CE_WARN,
2973788c47fcSHans Rosenfeld "!failed to attach blkdev handle for namespace %d",
2974788c47fcSHans Rosenfeld i);
2975788c47fcSHans Rosenfeld goto fail;
2976788c47fcSHans Rosenfeld }
2977788c47fcSHans Rosenfeld }
2978788c47fcSHans Rosenfeld
2979032cbfebSHans Rosenfeld if (ddi_create_minor_node(dip, "devctl", S_IFCHR,
2980032cbfebSHans Rosenfeld NVME_MINOR(ddi_get_instance(dip), 0), DDI_NT_NVME_NEXUS, 0)
2981032cbfebSHans Rosenfeld != DDI_SUCCESS) {
2982032cbfebSHans Rosenfeld dev_err(dip, CE_WARN, "nvme_attach: "
2983032cbfebSHans Rosenfeld "cannot create devctl minor node");
2984032cbfebSHans Rosenfeld goto fail;
2985032cbfebSHans Rosenfeld }
2986032cbfebSHans Rosenfeld
2987788c47fcSHans Rosenfeld return (DDI_SUCCESS);
2988788c47fcSHans Rosenfeld
2989788c47fcSHans Rosenfeld fail:
2990788c47fcSHans Rosenfeld /* attach successful anyway so that FMA can retire the device */
2991788c47fcSHans Rosenfeld if (nvme->n_dead)
2992788c47fcSHans Rosenfeld return (DDI_SUCCESS);
2993788c47fcSHans Rosenfeld
2994788c47fcSHans Rosenfeld (void) nvme_detach(dip, DDI_DETACH);
2995788c47fcSHans Rosenfeld
2996788c47fcSHans Rosenfeld return (DDI_FAILURE);
2997788c47fcSHans Rosenfeld }
2998788c47fcSHans Rosenfeld
2999788c47fcSHans Rosenfeld static int
nvme_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)3000788c47fcSHans Rosenfeld nvme_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
3001788c47fcSHans Rosenfeld {
3002788c47fcSHans Rosenfeld int instance, i;
3003788c47fcSHans Rosenfeld nvme_t *nvme;
3004788c47fcSHans Rosenfeld
3005788c47fcSHans Rosenfeld if (cmd != DDI_DETACH)
3006788c47fcSHans Rosenfeld return (DDI_FAILURE);
3007788c47fcSHans Rosenfeld
3008788c47fcSHans Rosenfeld instance = ddi_get_instance(dip);
3009788c47fcSHans Rosenfeld
3010788c47fcSHans Rosenfeld nvme = ddi_get_soft_state(nvme_state, instance);
3011788c47fcSHans Rosenfeld
3012788c47fcSHans Rosenfeld if (nvme == NULL)
3013788c47fcSHans Rosenfeld return (DDI_FAILURE);
3014788c47fcSHans Rosenfeld
3015032cbfebSHans Rosenfeld ddi_remove_minor_node(dip, "devctl");
3016032cbfebSHans Rosenfeld mutex_destroy(&nvme->n_minor.nm_mutex);
3017032cbfebSHans Rosenfeld
3018788c47fcSHans Rosenfeld if (nvme->n_ns) {
3019788c47fcSHans Rosenfeld for (i = 0; i != nvme->n_namespace_count; i++) {
3020032cbfebSHans Rosenfeld ddi_remove_minor_node(dip, nvme->n_ns[i].ns_name);
3021032cbfebSHans Rosenfeld mutex_destroy(&nvme->n_ns[i].ns_minor.nm_mutex);
3022032cbfebSHans Rosenfeld
3023788c47fcSHans Rosenfeld if (nvme->n_ns[i].ns_bd_hdl) {
3024788c47fcSHans Rosenfeld (void) bd_detach_handle(
3025788c47fcSHans Rosenfeld nvme->n_ns[i].ns_bd_hdl);
3026788c47fcSHans Rosenfeld bd_free_handle(nvme->n_ns[i].ns_bd_hdl);
3027788c47fcSHans Rosenfeld }
3028788c47fcSHans Rosenfeld
3029788c47fcSHans Rosenfeld if (nvme->n_ns[i].ns_idns)
3030788c47fcSHans Rosenfeld kmem_free(nvme->n_ns[i].ns_idns,
3031788c47fcSHans Rosenfeld sizeof (nvme_identify_nsid_t));
3032265d85e9SHans Rosenfeld if (nvme->n_ns[i].ns_devid)
3033265d85e9SHans Rosenfeld strfree(nvme->n_ns[i].ns_devid);
3034788c47fcSHans Rosenfeld }
3035788c47fcSHans Rosenfeld
3036788c47fcSHans Rosenfeld kmem_free(nvme->n_ns, sizeof (nvme_namespace_t) *
3037788c47fcSHans Rosenfeld nvme->n_namespace_count);
3038788c47fcSHans Rosenfeld }
3039788c47fcSHans Rosenfeld
3040788c47fcSHans Rosenfeld if (nvme->n_progress & NVME_INTERRUPTS)
3041788c47fcSHans Rosenfeld nvme_release_interrupts(nvme);
3042788c47fcSHans Rosenfeld
3043788c47fcSHans Rosenfeld if (nvme->n_cmd_taskq)
3044788c47fcSHans Rosenfeld ddi_taskq_wait(nvme->n_cmd_taskq);
3045788c47fcSHans Rosenfeld
3046788c47fcSHans Rosenfeld if (nvme->n_ioq_count > 0) {
3047788c47fcSHans Rosenfeld for (i = 1; i != nvme->n_ioq_count + 1; i++) {
3048788c47fcSHans Rosenfeld if (nvme->n_ioq[i] != NULL) {
3049788c47fcSHans Rosenfeld /* TODO: send destroy queue commands */
3050788c47fcSHans Rosenfeld nvme_free_qpair(nvme->n_ioq[i]);
3051788c47fcSHans Rosenfeld }
3052788c47fcSHans Rosenfeld }
3053788c47fcSHans Rosenfeld
3054788c47fcSHans Rosenfeld kmem_free(nvme->n_ioq, sizeof (nvme_qpair_t *) *
3055788c47fcSHans Rosenfeld (nvme->n_ioq_count + 1));
3056788c47fcSHans Rosenfeld }
3057788c47fcSHans Rosenfeld
3058b4eda73dSYouzhong Yang if (nvme->n_prp_cache != NULL) {
3059b4eda73dSYouzhong Yang kmem_cache_destroy(nvme->n_prp_cache);
3060b4eda73dSYouzhong Yang }
3061b4eda73dSYouzhong Yang
3062788c47fcSHans Rosenfeld if (nvme->n_progress & NVME_REGS_MAPPED) {
3063788c47fcSHans Rosenfeld nvme_shutdown(nvme, NVME_CC_SHN_NORMAL, B_FALSE);
3064788c47fcSHans Rosenfeld (void) nvme_reset(nvme, B_FALSE);
3065788c47fcSHans Rosenfeld }
3066788c47fcSHans Rosenfeld
3067788c47fcSHans Rosenfeld if (nvme->n_cmd_taskq)
3068788c47fcSHans Rosenfeld ddi_taskq_destroy(nvme->n_cmd_taskq);
3069788c47fcSHans Rosenfeld
3070788c47fcSHans Rosenfeld if (nvme->n_progress & NVME_CTRL_LIMITS)
3071788c47fcSHans Rosenfeld sema_destroy(&nvme->n_abort_sema);
3072788c47fcSHans Rosenfeld
3073788c47fcSHans Rosenfeld if (nvme->n_progress & NVME_ADMIN_QUEUE)
3074788c47fcSHans Rosenfeld nvme_free_qpair(nvme->n_adminq);
3075788c47fcSHans Rosenfeld
3076788c47fcSHans Rosenfeld if (nvme->n_idctl)
3077032cbfebSHans Rosenfeld kmem_free(nvme->n_idctl, NVME_IDENTIFY_BUFSIZE);
3078788c47fcSHans Rosenfeld
3079788c47fcSHans Rosenfeld if (nvme->n_progress & NVME_REGS_MAPPED)
3080788c47fcSHans Rosenfeld ddi_regs_map_free(&nvme->n_regh);
3081788c47fcSHans Rosenfeld
3082788c47fcSHans Rosenfeld if (nvme->n_progress & NVME_FMA_INIT) {
3083788c47fcSHans Rosenfeld if (DDI_FM_ERRCB_CAP(nvme->n_fm_cap))
3084788c47fcSHans Rosenfeld ddi_fm_handler_unregister(nvme->n_dip);
3085788c47fcSHans Rosenfeld
3086788c47fcSHans Rosenfeld if (DDI_FM_EREPORT_CAP(nvme->n_fm_cap) ||
3087788c47fcSHans Rosenfeld DDI_FM_ERRCB_CAP(nvme->n_fm_cap))
3088788c47fcSHans Rosenfeld pci_ereport_teardown(nvme->n_dip);
3089788c47fcSHans Rosenfeld
3090788c47fcSHans Rosenfeld ddi_fm_fini(nvme->n_dip);
3091788c47fcSHans Rosenfeld }
3092788c47fcSHans Rosenfeld
3093f0e4d8f0SHans Rosenfeld if (nvme->n_vendor != NULL)
3094f0e4d8f0SHans Rosenfeld strfree(nvme->n_vendor);
3095f0e4d8f0SHans Rosenfeld
3096f0e4d8f0SHans Rosenfeld if (nvme->n_product != NULL)
3097f0e4d8f0SHans Rosenfeld strfree(nvme->n_product);
3098f0e4d8f0SHans Rosenfeld
3099788c47fcSHans Rosenfeld ddi_soft_state_free(nvme_state, instance);
3100788c47fcSHans Rosenfeld
3101788c47fcSHans Rosenfeld return (DDI_SUCCESS);
3102788c47fcSHans Rosenfeld }
3103788c47fcSHans Rosenfeld
3104788c47fcSHans Rosenfeld static int
nvme_quiesce(dev_info_t * dip)3105788c47fcSHans Rosenfeld nvme_quiesce(dev_info_t *dip)
3106788c47fcSHans Rosenfeld {
3107788c47fcSHans Rosenfeld int instance;
3108788c47fcSHans Rosenfeld nvme_t *nvme;
3109788c47fcSHans Rosenfeld
3110788c47fcSHans Rosenfeld instance = ddi_get_instance(dip);
3111788c47fcSHans Rosenfeld
3112788c47fcSHans Rosenfeld nvme = ddi_get_soft_state(nvme_state, instance);
3113788c47fcSHans Rosenfeld
3114788c47fcSHans Rosenfeld if (nvme == NULL)
3115788c47fcSHans Rosenfeld return (DDI_FAILURE);
3116788c47fcSHans Rosenfeld
3117788c47fcSHans Rosenfeld nvme_shutdown(nvme, NVME_CC_SHN_ABRUPT, B_TRUE);
3118788c47fcSHans Rosenfeld
3119788c47fcSHans Rosenfeld (void) nvme_reset(nvme, B_TRUE);
3120788c47fcSHans Rosenfeld
3121788c47fcSHans Rosenfeld return (DDI_FAILURE);
3122788c47fcSHans Rosenfeld }
3123788c47fcSHans Rosenfeld
3124788c47fcSHans Rosenfeld static int
nvme_fill_prp(nvme_cmd_t * cmd,bd_xfer_t * xfer)3125788c47fcSHans Rosenfeld nvme_fill_prp(nvme_cmd_t *cmd, bd_xfer_t *xfer)
3126788c47fcSHans Rosenfeld {
3127788c47fcSHans Rosenfeld nvme_t *nvme = cmd->nc_nvme;
3128788c47fcSHans Rosenfeld int nprp_page, nprp;
3129788c47fcSHans Rosenfeld uint64_t *prp;
3130788c47fcSHans Rosenfeld
3131788c47fcSHans Rosenfeld if (xfer->x_ndmac == 0)
3132788c47fcSHans Rosenfeld return (DDI_FAILURE);
3133788c47fcSHans Rosenfeld
3134788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_dptr.d_prp[0] = xfer->x_dmac.dmac_laddress;
3135788c47fcSHans Rosenfeld ddi_dma_nextcookie(xfer->x_dmah, &xfer->x_dmac);
3136788c47fcSHans Rosenfeld
3137788c47fcSHans Rosenfeld if (xfer->x_ndmac == 1) {
3138788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_dptr.d_prp[1] = 0;
3139788c47fcSHans Rosenfeld return (DDI_SUCCESS);
3140788c47fcSHans Rosenfeld } else if (xfer->x_ndmac == 2) {
3141788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_dptr.d_prp[1] = xfer->x_dmac.dmac_laddress;
3142788c47fcSHans Rosenfeld return (DDI_SUCCESS);
3143788c47fcSHans Rosenfeld }
3144788c47fcSHans Rosenfeld
3145788c47fcSHans Rosenfeld xfer->x_ndmac--;
3146788c47fcSHans Rosenfeld
3147788c47fcSHans Rosenfeld nprp_page = nvme->n_pagesize / sizeof (uint64_t) - 1;
3148788c47fcSHans Rosenfeld ASSERT(nprp_page > 0);
3149788c47fcSHans Rosenfeld nprp = (xfer->x_ndmac + nprp_page - 1) / nprp_page;
3150788c47fcSHans Rosenfeld
3151788c47fcSHans Rosenfeld /*
3152788c47fcSHans Rosenfeld * We currently don't support chained PRPs and set up our DMA
3153788c47fcSHans Rosenfeld * attributes to reflect that. If we still get an I/O request
3154788c47fcSHans Rosenfeld * that needs a chained PRP something is very wrong.
3155788c47fcSHans Rosenfeld */
3156788c47fcSHans Rosenfeld VERIFY(nprp == 1);
3157788c47fcSHans Rosenfeld
3158b4eda73dSYouzhong Yang cmd->nc_dma = kmem_cache_alloc(nvme->n_prp_cache, KM_SLEEP);
3159b4eda73dSYouzhong Yang bzero(cmd->nc_dma->nd_memp, cmd->nc_dma->nd_len);
3160788c47fcSHans Rosenfeld
3161788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_dptr.d_prp[1] = cmd->nc_dma->nd_cookie.dmac_laddress;
3162788c47fcSHans Rosenfeld
3163788c47fcSHans Rosenfeld /*LINTED: E_PTR_BAD_CAST_ALIGN*/
3164788c47fcSHans Rosenfeld for (prp = (uint64_t *)cmd->nc_dma->nd_memp;
3165788c47fcSHans Rosenfeld xfer->x_ndmac > 0;
3166788c47fcSHans Rosenfeld prp++, xfer->x_ndmac--) {
3167788c47fcSHans Rosenfeld *prp = xfer->x_dmac.dmac_laddress;
3168788c47fcSHans Rosenfeld ddi_dma_nextcookie(xfer->x_dmah, &xfer->x_dmac);
3169788c47fcSHans Rosenfeld }
3170788c47fcSHans Rosenfeld
3171788c47fcSHans Rosenfeld (void) ddi_dma_sync(cmd->nc_dma->nd_dmah, 0, cmd->nc_dma->nd_len,
3172788c47fcSHans Rosenfeld DDI_DMA_SYNC_FORDEV);
3173788c47fcSHans Rosenfeld return (DDI_SUCCESS);
3174788c47fcSHans Rosenfeld }
3175788c47fcSHans Rosenfeld
3176788c47fcSHans Rosenfeld static nvme_cmd_t *
nvme_create_nvm_cmd(nvme_namespace_t * ns,uint8_t opc,bd_xfer_t * xfer)3177788c47fcSHans Rosenfeld nvme_create_nvm_cmd(nvme_namespace_t *ns, uint8_t opc, bd_xfer_t *xfer)
3178788c47fcSHans Rosenfeld {
3179788c47fcSHans Rosenfeld nvme_t *nvme = ns->ns_nvme;
3180788c47fcSHans Rosenfeld nvme_cmd_t *cmd;
3181788c47fcSHans Rosenfeld
3182788c47fcSHans Rosenfeld /*
3183788c47fcSHans Rosenfeld * Blkdev only sets BD_XFER_POLL when dumping, so don't sleep.
3184788c47fcSHans Rosenfeld */
3185788c47fcSHans Rosenfeld cmd = nvme_alloc_cmd(nvme, (xfer->x_flags & BD_XFER_POLL) ?
3186788c47fcSHans Rosenfeld KM_NOSLEEP : KM_SLEEP);
3187788c47fcSHans Rosenfeld
3188788c47fcSHans Rosenfeld if (cmd == NULL)
3189788c47fcSHans Rosenfeld return (NULL);
3190788c47fcSHans Rosenfeld
3191788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_opc = opc;
3192788c47fcSHans Rosenfeld cmd->nc_callback = nvme_bd_xfer_done;
3193788c47fcSHans Rosenfeld cmd->nc_xfer = xfer;
3194788c47fcSHans Rosenfeld
3195788c47fcSHans Rosenfeld switch (opc) {
3196788c47fcSHans Rosenfeld case NVME_OPC_NVM_WRITE:
3197788c47fcSHans Rosenfeld case NVME_OPC_NVM_READ:
3198788c47fcSHans Rosenfeld VERIFY(xfer->x_nblks <= 0x10000);
3199788c47fcSHans Rosenfeld
3200788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_nsid = ns->ns_id;
3201788c47fcSHans Rosenfeld
3202788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_cdw10 = xfer->x_blkno & 0xffffffffu;
3203788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_cdw11 = (xfer->x_blkno >> 32);
3204788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_cdw12 = (uint16_t)(xfer->x_nblks - 1);
3205788c47fcSHans Rosenfeld
3206788c47fcSHans Rosenfeld if (nvme_fill_prp(cmd, xfer) != DDI_SUCCESS)
3207788c47fcSHans Rosenfeld goto fail;
3208788c47fcSHans Rosenfeld break;
3209788c47fcSHans Rosenfeld
3210788c47fcSHans Rosenfeld case NVME_OPC_NVM_FLUSH:
3211788c47fcSHans Rosenfeld cmd->nc_sqe.sqe_nsid = ns->ns_id;
3212788c47fcSHans Rosenfeld break;
3213788c47fcSHans Rosenfeld
3214788c47fcSHans Rosenfeld default:
3215788c47fcSHans Rosenfeld goto fail;
3216788c47fcSHans Rosenfeld }
3217788c47fcSHans Rosenfeld
3218788c47fcSHans Rosenfeld return (cmd);
3219788c47fcSHans Rosenfeld
3220788c47fcSHans Rosenfeld fail:
3221788c47fcSHans Rosenfeld nvme_free_cmd(cmd);
3222788c47fcSHans Rosenfeld return (NULL);
3223788c47fcSHans Rosenfeld }
3224788c47fcSHans Rosenfeld
3225788c47fcSHans Rosenfeld static void
nvme_bd_xfer_done(void * arg)3226788c47fcSHans Rosenfeld nvme_bd_xfer_done(void *arg)
3227788c47fcSHans Rosenfeld {
3228788c47fcSHans Rosenfeld nvme_cmd_t *cmd = arg;
3229788c47fcSHans Rosenfeld bd_xfer_t *xfer = cmd->nc_xfer;
3230788c47fcSHans Rosenfeld int error = 0;
3231788c47fcSHans Rosenfeld
3232788c47fcSHans Rosenfeld error = nvme_check_cmd_status(cmd);
3233788c47fcSHans Rosenfeld nvme_free_cmd(cmd);
3234788c47fcSHans Rosenfeld
3235788c47fcSHans Rosenfeld bd_xfer_done(xfer, error);
3236788c47fcSHans Rosenfeld }
3237788c47fcSHans Rosenfeld
3238788c47fcSHans Rosenfeld static void
nvme_bd_driveinfo(void * arg,bd_drive_t * drive)3239788c47fcSHans Rosenfeld nvme_bd_driveinfo(void *arg, bd_drive_t *drive)
3240788c47fcSHans Rosenfeld {
3241788c47fcSHans Rosenfeld nvme_namespace_t *ns = arg;
3242788c47fcSHans Rosenfeld nvme_t *nvme = ns->ns_nvme;
3243788c47fcSHans Rosenfeld
3244788c47fcSHans Rosenfeld /*
3245788c47fcSHans Rosenfeld * blkdev maintains one queue size per instance (namespace),
3246788c47fcSHans Rosenfeld * but all namespace share the I/O queues.
3247788c47fcSHans Rosenfeld * TODO: need to figure out a sane default, or use per-NS I/O queues,
3248788c47fcSHans Rosenfeld * or change blkdev to handle EAGAIN
3249788c47fcSHans Rosenfeld */
3250788c47fcSHans Rosenfeld drive->d_qsize = nvme->n_ioq_count * nvme->n_io_queue_len
3251788c47fcSHans Rosenfeld / nvme->n_namespace_count;
3252788c47fcSHans Rosenfeld
3253788c47fcSHans Rosenfeld /*
3254788c47fcSHans Rosenfeld * d_maxxfer is not set, which means the value is taken from the DMA
3255788c47fcSHans Rosenfeld * attributes specified to bd_alloc_handle.
3256788c47fcSHans Rosenfeld */
3257788c47fcSHans Rosenfeld
3258788c47fcSHans Rosenfeld drive->d_removable = B_FALSE;
3259788c47fcSHans Rosenfeld drive->d_hotpluggable = B_FALSE;
3260788c47fcSHans Rosenfeld
3261265d85e9SHans Rosenfeld bcopy(ns->ns_eui64, drive->d_eui64, sizeof (drive->d_eui64));
3262788c47fcSHans Rosenfeld drive->d_target = ns->ns_id;
3263788c47fcSHans Rosenfeld drive->d_lun = 0;
3264f0e4d8f0SHans Rosenfeld
32654ba35b4bSHans Rosenfeld drive->d_model = nvme->n_idctl->id_model;
32664ba35b4bSHans Rosenfeld drive->d_model_len = sizeof (nvme->n_idctl->id_model);
3267f0e4d8f0SHans Rosenfeld drive->d_vendor = nvme->n_vendor;
3268f0e4d8f0SHans Rosenfeld drive->d_vendor_len = strlen(nvme->n_vendor);
3269f0e4d8f0SHans Rosenfeld drive->d_product = nvme->n_product;
3270f0e4d8f0SHans Rosenfeld drive->d_product_len = strlen(nvme->n_product);
3271f0e4d8f0SHans Rosenfeld drive->d_serial = nvme->n_idctl->id_serial;
3272f0e4d8f0SHans Rosenfeld drive->d_serial_len = sizeof (nvme->n_idctl->id_serial);
3273f0e4d8f0SHans Rosenfeld drive->d_revision = nvme->n_idctl->id_fwrev;
3274f0e4d8f0SHans Rosenfeld drive->d_revision_len = sizeof (nvme->n_idctl->id_fwrev);
3275788c47fcSHans Rosenfeld }
3276788c47fcSHans Rosenfeld
3277788c47fcSHans Rosenfeld static int
nvme_bd_mediainfo(void * arg,bd_media_t * media)3278788c47fcSHans Rosenfeld nvme_bd_mediainfo(void *arg, bd_media_t *media)
3279788c47fcSHans Rosenfeld {
3280788c47fcSHans Rosenfeld nvme_namespace_t *ns = arg;
3281788c47fcSHans Rosenfeld
3282788c47fcSHans Rosenfeld media->m_nblks = ns->ns_block_count;
3283788c47fcSHans Rosenfeld media->m_blksize = ns->ns_block_size;
3284788c47fcSHans Rosenfeld media->m_readonly = B_FALSE;
3285788c47fcSHans Rosenfeld media->m_solidstate = B_TRUE;
3286788c47fcSHans Rosenfeld
3287788c47fcSHans Rosenfeld media->m_pblksize = ns->ns_best_block_size;
3288788c47fcSHans Rosenfeld
3289788c47fcSHans Rosenfeld return (0);
3290788c47fcSHans Rosenfeld }
3291788c47fcSHans Rosenfeld
3292788c47fcSHans Rosenfeld static int
nvme_bd_cmd(nvme_namespace_t * ns,bd_xfer_t * xfer,uint8_t opc)3293788c47fcSHans Rosenfeld nvme_bd_cmd(nvme_namespace_t *ns, bd_xfer_t *xfer, uint8_t opc)
3294788c47fcSHans Rosenfeld {
3295788c47fcSHans Rosenfeld nvme_t *nvme = ns->ns_nvme;
3296ba636d1cSHans Rosenfeld nvme_cmd_t *cmd;
32972ca49728SHans Rosenfeld nvme_qpair_t *ioq;
32982ca49728SHans Rosenfeld boolean_t poll;
3299ba636d1cSHans Rosenfeld int ret;
3300788c47fcSHans Rosenfeld
3301788c47fcSHans Rosenfeld if (nvme->n_dead)
3302788c47fcSHans Rosenfeld return (EIO);
3303788c47fcSHans Rosenfeld
3304788c47fcSHans Rosenfeld cmd = nvme_create_nvm_cmd(ns, opc, xfer);
3305788c47fcSHans Rosenfeld if (cmd == NULL)
3306788c47fcSHans Rosenfeld return (ENOMEM);
3307788c47fcSHans Rosenfeld
3308788c47fcSHans Rosenfeld cmd->nc_sqid = (CPU->cpu_id % nvme->n_ioq_count) + 1;
3309788c47fcSHans Rosenfeld ASSERT(cmd->nc_sqid <= nvme->n_ioq_count);
33102ca49728SHans Rosenfeld ioq = nvme->n_ioq[cmd->nc_sqid];
3311788c47fcSHans Rosenfeld
33122ca49728SHans Rosenfeld /*
33132ca49728SHans Rosenfeld * Get the polling flag before submitting the command. The command may
33142ca49728SHans Rosenfeld * complete immediately after it was submitted, which means we must
33152ca49728SHans Rosenfeld * treat both cmd and xfer as if they have been freed already.
33162ca49728SHans Rosenfeld */
33172ca49728SHans Rosenfeld poll = (xfer->x_flags & BD_XFER_POLL) != 0;
33182ca49728SHans Rosenfeld
3319ba636d1cSHans Rosenfeld ret = nvme_submit_io_cmd(ioq, cmd);
3320ba636d1cSHans Rosenfeld
3321ba636d1cSHans Rosenfeld if (ret != 0)
3322ba636d1cSHans Rosenfeld return (ret);
3323788c47fcSHans Rosenfeld
33242ca49728SHans Rosenfeld if (!poll)
33252ca49728SHans Rosenfeld return (0);
33262ca49728SHans Rosenfeld
33272ca49728SHans Rosenfeld do {
3328ba636d1cSHans Rosenfeld cmd = nvme_retrieve_cmd(nvme, ioq);
3329ba636d1cSHans Rosenfeld if (cmd != NULL)
3330ba636d1cSHans Rosenfeld nvme_bd_xfer_done(cmd);
33312ca49728SHans Rosenfeld else
33322ca49728SHans Rosenfeld drv_usecwait(10);
33332ca49728SHans Rosenfeld } while (ioq->nq_active_cmds != 0);
33342ca49728SHans Rosenfeld
3335788c47fcSHans Rosenfeld return (0);
3336788c47fcSHans Rosenfeld }
3337788c47fcSHans Rosenfeld
3338788c47fcSHans Rosenfeld static int
nvme_bd_read(void * arg,bd_xfer_t * xfer)3339788c47fcSHans Rosenfeld nvme_bd_read(void *arg, bd_xfer_t *xfer)
3340788c47fcSHans Rosenfeld {
3341788c47fcSHans Rosenfeld nvme_namespace_t *ns = arg;
3342788c47fcSHans Rosenfeld
3343788c47fcSHans Rosenfeld return (nvme_bd_cmd(ns, xfer, NVME_OPC_NVM_READ));
3344788c47fcSHans Rosenfeld }
3345788c47fcSHans Rosenfeld
3346788c47fcSHans Rosenfeld static int
nvme_bd_write(void * arg,bd_xfer_t * xfer)3347788c47fcSHans Rosenfeld nvme_bd_write(void *arg, bd_xfer_t *xfer)
3348788c47fcSHans Rosenfeld {
3349788c47fcSHans Rosenfeld nvme_namespace_t *ns = arg;
3350788c47fcSHans Rosenfeld
3351788c47fcSHans Rosenfeld return (nvme_bd_cmd(ns, xfer, NVME_OPC_NVM_WRITE));
3352788c47fcSHans Rosenfeld }
3353788c47fcSHans Rosenfeld
3354788c47fcSHans Rosenfeld static int
nvme_bd_sync(void * arg,bd_xfer_t * xfer)3355788c47fcSHans Rosenfeld nvme_bd_sync(void *arg, bd_xfer_t *xfer)
3356788c47fcSHans Rosenfeld {
3357788c47fcSHans Rosenfeld nvme_namespace_t *ns = arg;
3358788c47fcSHans Rosenfeld
3359788c47fcSHans Rosenfeld if (ns->ns_nvme->n_dead)
3360788c47fcSHans Rosenfeld return (EIO);
3361788c47fcSHans Rosenfeld
3362788c47fcSHans Rosenfeld /*
33635f836503SHans Rosenfeld * If the volatile write cache is not present or not enabled the FLUSH
33645f836503SHans Rosenfeld * command is a no-op, so we can take a shortcut here.
3365788c47fcSHans Rosenfeld */
33665f836503SHans Rosenfeld if (!ns->ns_nvme->n_write_cache_present) {
3367788c47fcSHans Rosenfeld bd_xfer_done(xfer, ENOTSUP);
3368788c47fcSHans Rosenfeld return (0);
3369788c47fcSHans Rosenfeld }
3370788c47fcSHans Rosenfeld
33715f836503SHans Rosenfeld if (!ns->ns_nvme->n_write_cache_enabled) {
33725f836503SHans Rosenfeld bd_xfer_done(xfer, 0);
33735f836503SHans Rosenfeld return (0);
33745f836503SHans Rosenfeld }
33755f836503SHans Rosenfeld
3376788c47fcSHans Rosenfeld return (nvme_bd_cmd(ns, xfer, NVME_OPC_NVM_FLUSH));
3377788c47fcSHans Rosenfeld }
3378788c47fcSHans Rosenfeld
3379788c47fcSHans Rosenfeld static int
nvme_bd_devid(void * arg,dev_info_t * devinfo,ddi_devid_t * devid)3380788c47fcSHans Rosenfeld nvme_bd_devid(void *arg, dev_info_t *devinfo, ddi_devid_t *devid)
3381788c47fcSHans Rosenfeld {
3382788c47fcSHans Rosenfeld nvme_namespace_t *ns = arg;
3383788c47fcSHans Rosenfeld
3384265d85e9SHans Rosenfeld /*LINTED: E_BAD_PTR_CAST_ALIGN*/
3385265d85e9SHans Rosenfeld if (*(uint64_t *)ns->ns_eui64 != 0) {
3386265d85e9SHans Rosenfeld return (ddi_devid_init(devinfo, DEVID_SCSI3_WWN,
3387265d85e9SHans Rosenfeld sizeof (ns->ns_eui64), ns->ns_eui64, devid));
3388265d85e9SHans Rosenfeld } else {
3389265d85e9SHans Rosenfeld return (ddi_devid_init(devinfo, DEVID_ENCAP,
3390265d85e9SHans Rosenfeld strlen(ns->ns_devid), ns->ns_devid, devid));
3391265d85e9SHans Rosenfeld }
3392788c47fcSHans Rosenfeld }
3393032cbfebSHans Rosenfeld
3394032cbfebSHans Rosenfeld static int
nvme_open(dev_t * devp,int flag,int otyp,cred_t * cred_p)3395032cbfebSHans Rosenfeld nvme_open(dev_t *devp, int flag, int otyp, cred_t *cred_p)
3396032cbfebSHans Rosenfeld {
3397032cbfebSHans Rosenfeld #ifndef __lock_lint
3398032cbfebSHans Rosenfeld _NOTE(ARGUNUSED(cred_p));
3399032cbfebSHans Rosenfeld #endif
3400032cbfebSHans Rosenfeld minor_t minor = getminor(*devp);
3401032cbfebSHans Rosenfeld nvme_t *nvme = ddi_get_soft_state(nvme_state, NVME_MINOR_INST(minor));
3402032cbfebSHans Rosenfeld int nsid = NVME_MINOR_NSID(minor);
3403032cbfebSHans Rosenfeld nvme_minor_state_t *nm;
3404032cbfebSHans Rosenfeld int rv = 0;
3405032cbfebSHans Rosenfeld
3406032cbfebSHans Rosenfeld if (otyp != OTYP_CHR)
3407032cbfebSHans Rosenfeld return (EINVAL);
3408032cbfebSHans Rosenfeld
3409032cbfebSHans Rosenfeld if (nvme == NULL)
3410032cbfebSHans Rosenfeld return (ENXIO);
3411032cbfebSHans Rosenfeld
3412032cbfebSHans Rosenfeld if (nsid > nvme->n_namespace_count)
3413032cbfebSHans Rosenfeld return (ENXIO);
3414032cbfebSHans Rosenfeld
341569a33c23SHans Rosenfeld if (nvme->n_dead)
341669a33c23SHans Rosenfeld return (EIO);
341769a33c23SHans Rosenfeld
3418032cbfebSHans Rosenfeld nm = nsid == 0 ? &nvme->n_minor : &nvme->n_ns[nsid - 1].ns_minor;
3419032cbfebSHans Rosenfeld
3420032cbfebSHans Rosenfeld mutex_enter(&nm->nm_mutex);
3421032cbfebSHans Rosenfeld if (nm->nm_oexcl) {
3422032cbfebSHans Rosenfeld rv = EBUSY;
3423032cbfebSHans Rosenfeld goto out;
3424032cbfebSHans Rosenfeld }
3425032cbfebSHans Rosenfeld
3426032cbfebSHans Rosenfeld if (flag & FEXCL) {
3427032cbfebSHans Rosenfeld if (nm->nm_ocnt != 0) {
3428032cbfebSHans Rosenfeld rv = EBUSY;
3429032cbfebSHans Rosenfeld goto out;
3430032cbfebSHans Rosenfeld }
3431032cbfebSHans Rosenfeld nm->nm_oexcl = B_TRUE;
3432032cbfebSHans Rosenfeld }
3433032cbfebSHans Rosenfeld
3434032cbfebSHans Rosenfeld nm->nm_ocnt++;
3435032cbfebSHans Rosenfeld
3436032cbfebSHans Rosenfeld out:
3437032cbfebSHans Rosenfeld mutex_exit(&nm->nm_mutex);
3438032cbfebSHans Rosenfeld return (rv);
3439032cbfebSHans Rosenfeld
3440032cbfebSHans Rosenfeld }
3441032cbfebSHans Rosenfeld
3442032cbfebSHans Rosenfeld static int
nvme_close(dev_t dev,int flag,int otyp,cred_t * cred_p)3443032cbfebSHans Rosenfeld nvme_close(dev_t dev, int flag, int otyp, cred_t *cred_p)
3444032cbfebSHans Rosenfeld {
3445032cbfebSHans Rosenfeld #ifndef __lock_lint
3446032cbfebSHans Rosenfeld _NOTE(ARGUNUSED(cred_p));
3447032cbfebSHans Rosenfeld _NOTE(ARGUNUSED(flag));
3448032cbfebSHans Rosenfeld #endif
3449032cbfebSHans Rosenfeld minor_t minor = getminor(dev);
3450032cbfebSHans Rosenfeld nvme_t *nvme = ddi_get_soft_state(nvme_state, NVME_MINOR_INST(minor));
3451032cbfebSHans Rosenfeld int nsid = NVME_MINOR_NSID(minor);
3452032cbfebSHans Rosenfeld nvme_minor_state_t *nm;
3453032cbfebSHans Rosenfeld
3454032cbfebSHans Rosenfeld if (otyp != OTYP_CHR)
3455032cbfebSHans Rosenfeld return (ENXIO);
3456032cbfebSHans Rosenfeld
3457032cbfebSHans Rosenfeld if (nvme == NULL)
3458032cbfebSHans Rosenfeld return (ENXIO);
3459032cbfebSHans Rosenfeld
3460032cbfebSHans Rosenfeld if (nsid > nvme->n_namespace_count)
3461032cbfebSHans Rosenfeld return (ENXIO);
3462032cbfebSHans Rosenfeld
3463032cbfebSHans Rosenfeld nm = nsid == 0 ? &nvme->n_minor : &nvme->n_ns[nsid - 1].ns_minor;
3464032cbfebSHans Rosenfeld
3465032cbfebSHans Rosenfeld mutex_enter(&nm->nm_mutex);
3466032cbfebSHans Rosenfeld if (nm->nm_oexcl)
3467032cbfebSHans Rosenfeld nm->nm_oexcl = B_FALSE;
3468032cbfebSHans Rosenfeld
3469032cbfebSHans Rosenfeld ASSERT(nm->nm_ocnt > 0);
3470032cbfebSHans Rosenfeld nm->nm_ocnt--;
3471032cbfebSHans Rosenfeld mutex_exit(&nm->nm_mutex);
3472032cbfebSHans Rosenfeld
3473032cbfebSHans Rosenfeld return (0);
3474032cbfebSHans Rosenfeld }
3475032cbfebSHans Rosenfeld
3476032cbfebSHans Rosenfeld static int
nvme_ioctl_identify(nvme_t * nvme,int nsid,nvme_ioctl_t * nioc,int mode,cred_t * cred_p)3477032cbfebSHans Rosenfeld nvme_ioctl_identify(nvme_t *nvme, int nsid, nvme_ioctl_t *nioc, int mode,
3478032cbfebSHans Rosenfeld cred_t *cred_p)
3479032cbfebSHans Rosenfeld {
3480032cbfebSHans Rosenfeld _NOTE(ARGUNUSED(cred_p));
3481032cbfebSHans Rosenfeld int rv = 0;
3482032cbfebSHans Rosenfeld void *idctl;
3483032cbfebSHans Rosenfeld
3484032cbfebSHans Rosenfeld if ((mode & FREAD) == 0)
3485032cbfebSHans Rosenfeld return (EPERM);
3486032cbfebSHans Rosenfeld
3487032cbfebSHans Rosenfeld if (nioc->n_len < NVME_IDENTIFY_BUFSIZE)
3488032cbfebSHans Rosenfeld return (EINVAL);
3489032cbfebSHans Rosenfeld
349069a33c23SHans Rosenfeld if ((rv = nvme_identify(nvme, nsid, (void **)&idctl)) != 0)
349169a33c23SHans Rosenfeld return (rv);
3492032cbfebSHans Rosenfeld
3493032cbfebSHans Rosenfeld if (ddi_copyout(idctl, (void *)nioc->n_buf, NVME_IDENTIFY_BUFSIZE, mode)
3494032cbfebSHans Rosenfeld != 0)
3495032cbfebSHans Rosenfeld rv = EFAULT;
3496032cbfebSHans Rosenfeld
3497032cbfebSHans Rosenfeld kmem_free(idctl, NVME_IDENTIFY_BUFSIZE);
3498032cbfebSHans Rosenfeld
3499032cbfebSHans Rosenfeld return (rv);
3500032cbfebSHans Rosenfeld }
3501032cbfebSHans Rosenfeld
3502032cbfebSHans Rosenfeld static int
nvme_ioctl_capabilities(nvme_t * nvme,int nsid,nvme_ioctl_t * nioc,int mode,cred_t * cred_p)3503032cbfebSHans Rosenfeld nvme_ioctl_capabilities(nvme_t *nvme, int nsid, nvme_ioctl_t *nioc,
3504032cbfebSHans Rosenfeld int mode, cred_t *cred_p)
3505032cbfebSHans Rosenfeld {
3506032cbfebSHans Rosenfeld _NOTE(ARGUNUSED(nsid, cred_p));
3507032cbfebSHans Rosenfeld int rv = 0;
3508032cbfebSHans Rosenfeld nvme_reg_cap_t cap = { 0 };
3509032cbfebSHans Rosenfeld nvme_capabilities_t nc;
3510032cbfebSHans Rosenfeld
3511032cbfebSHans Rosenfeld if ((mode & FREAD) == 0)
3512032cbfebSHans Rosenfeld return (EPERM);
3513032cbfebSHans Rosenfeld
3514032cbfebSHans Rosenfeld if (nioc->n_len < sizeof (nc))
3515032cbfebSHans Rosenfeld return (EINVAL);
3516032cbfebSHans Rosenfeld
3517032cbfebSHans Rosenfeld cap.r = nvme_get64(nvme, NVME_REG_CAP);
3518032cbfebSHans Rosenfeld
3519032cbfebSHans Rosenfeld /*
3520032cbfebSHans Rosenfeld * The MPSMIN and MPSMAX fields in the CAP register use 0 to
3521032cbfebSHans Rosenfeld * specify the base page size of 4k (1<<12), so add 12 here to
3522032cbfebSHans Rosenfeld * get the real page size value.
3523032cbfebSHans Rosenfeld */
3524032cbfebSHans Rosenfeld nc.mpsmax = 1 << (12 + cap.b.cap_mpsmax);
3525032cbfebSHans Rosenfeld nc.mpsmin = 1 << (12 + cap.b.cap_mpsmin);
3526032cbfebSHans Rosenfeld
3527032cbfebSHans Rosenfeld if (ddi_copyout(&nc, (void *)nioc->n_buf, sizeof (nc), mode) != 0)
3528032cbfebSHans Rosenfeld rv = EFAULT;
3529032cbfebSHans Rosenfeld
3530032cbfebSHans Rosenfeld return (rv);
3531032cbfebSHans Rosenfeld }
3532032cbfebSHans Rosenfeld
3533032cbfebSHans Rosenfeld static int
nvme_ioctl_get_logpage(nvme_t * nvme,int nsid,nvme_ioctl_t * nioc,int mode,cred_t * cred_p)3534032cbfebSHans Rosenfeld nvme_ioctl_get_logpage(nvme_t *nvme, int nsid, nvme_ioctl_t *nioc,
3535032cbfebSHans Rosenfeld int mode, cred_t *cred_p)
3536032cbfebSHans Rosenfeld {
3537032cbfebSHans Rosenfeld _NOTE(ARGUNUSED(cred_p));
3538032cbfebSHans Rosenfeld void *log = NULL;
3539032cbfebSHans Rosenfeld size_t bufsize = 0;
3540032cbfebSHans Rosenfeld int rv = 0;
3541032cbfebSHans Rosenfeld
3542032cbfebSHans Rosenfeld if ((mode & FREAD) == 0)
3543032cbfebSHans Rosenfeld return (EPERM);
3544032cbfebSHans Rosenfeld
3545032cbfebSHans Rosenfeld switch (nioc->n_arg) {
3546032cbfebSHans Rosenfeld case NVME_LOGPAGE_ERROR:
3547032cbfebSHans Rosenfeld if (nsid != 0)
3548032cbfebSHans Rosenfeld return (EINVAL);
3549032cbfebSHans Rosenfeld break;
3550032cbfebSHans Rosenfeld case NVME_LOGPAGE_HEALTH:
3551032cbfebSHans Rosenfeld if (nsid != 0 && nvme->n_idctl->id_lpa.lp_smart == 0)
3552032cbfebSHans Rosenfeld return (EINVAL);
3553032cbfebSHans Rosenfeld
3554032cbfebSHans Rosenfeld if (nsid == 0)
3555032cbfebSHans Rosenfeld nsid = (uint32_t)-1;
3556032cbfebSHans Rosenfeld
3557032cbfebSHans Rosenfeld break;
3558032cbfebSHans Rosenfeld case NVME_LOGPAGE_FWSLOT:
3559032cbfebSHans Rosenfeld if (nsid != 0)
3560032cbfebSHans Rosenfeld return (EINVAL);
3561032cbfebSHans Rosenfeld break;
3562032cbfebSHans Rosenfeld default:
3563032cbfebSHans Rosenfeld return (EINVAL);
3564032cbfebSHans Rosenfeld }
3565032cbfebSHans Rosenfeld
3566032cbfebSHans Rosenfeld if (nvme_get_logpage(nvme, &log, &bufsize, nioc->n_arg, nsid)
3567032cbfebSHans Rosenfeld != DDI_SUCCESS)
3568032cbfebSHans Rosenfeld return (EIO);
3569032cbfebSHans Rosenfeld
3570032cbfebSHans Rosenfeld if (nioc->n_len < bufsize) {
3571032cbfebSHans Rosenfeld kmem_free(log, bufsize);
3572032cbfebSHans Rosenfeld return (EINVAL);
3573032cbfebSHans Rosenfeld }
3574032cbfebSHans Rosenfeld
3575032cbfebSHans Rosenfeld if (ddi_copyout(log, (void *)nioc->n_buf, bufsize, mode) != 0)
3576032cbfebSHans Rosenfeld rv = EFAULT;
3577032cbfebSHans Rosenfeld
3578032cbfebSHans Rosenfeld nioc->n_len = bufsize;
3579032cbfebSHans Rosenfeld kmem_free(log, bufsize);
3580032cbfebSHans Rosenfeld
3581032cbfebSHans Rosenfeld return (rv);
3582032cbfebSHans Rosenfeld }
3583032cbfebSHans Rosenfeld
3584032cbfebSHans Rosenfeld static int
nvme_ioctl_get_features(nvme_t * nvme,int nsid,nvme_ioctl_t * nioc,int mode,cred_t * cred_p)3585032cbfebSHans Rosenfeld nvme_ioctl_get_features(nvme_t *nvme, int nsid, nvme_ioctl_t *nioc,
3586032cbfebSHans Rosenfeld int mode, cred_t *cred_p)
3587032cbfebSHans Rosenfeld {
3588032cbfebSHans Rosenfeld _NOTE(ARGUNUSED(cred_p));
3589032cbfebSHans Rosenfeld void *buf = NULL;
3590032cbfebSHans Rosenfeld size_t bufsize = 0;
3591032cbfebSHans Rosenfeld uint32_t res = 0;
3592032cbfebSHans Rosenfeld uint8_t feature;
3593032cbfebSHans Rosenfeld int rv = 0;
3594032cbfebSHans Rosenfeld
3595032cbfebSHans Rosenfeld if ((mode & FREAD) == 0)
3596032cbfebSHans Rosenfeld return (EPERM);
3597032cbfebSHans Rosenfeld
3598032cbfebSHans Rosenfeld if ((nioc->n_arg >> 32) > 0xff)
3599032cbfebSHans Rosenfeld return (EINVAL);
3600032cbfebSHans Rosenfeld
3601032cbfebSHans Rosenfeld feature = (uint8_t)(nioc->n_arg >> 32);
3602032cbfebSHans Rosenfeld
3603032cbfebSHans Rosenfeld switch (feature) {
3604032cbfebSHans Rosenfeld case NVME_FEAT_ARBITRATION:
3605032cbfebSHans Rosenfeld case NVME_FEAT_POWER_MGMT:
3606032cbfebSHans Rosenfeld case NVME_FEAT_TEMPERATURE:
3607032cbfebSHans Rosenfeld case NVME_FEAT_ERROR:
3608032cbfebSHans Rosenfeld case NVME_FEAT_NQUEUES:
3609032cbfebSHans Rosenfeld case NVME_FEAT_INTR_COAL:
3610032cbfebSHans Rosenfeld case NVME_FEAT_WRITE_ATOM:
3611032cbfebSHans Rosenfeld case NVME_FEAT_ASYNC_EVENT:
3612032cbfebSHans Rosenfeld case NVME_FEAT_PROGRESS:
3613032cbfebSHans Rosenfeld if (nsid != 0)
3614032cbfebSHans Rosenfeld return (EINVAL);
3615032cbfebSHans Rosenfeld break;
3616032cbfebSHans Rosenfeld
3617032cbfebSHans Rosenfeld case NVME_FEAT_INTR_VECT:
3618032cbfebSHans Rosenfeld if (nsid != 0)
3619032cbfebSHans Rosenfeld return (EINVAL);
3620032cbfebSHans Rosenfeld
3621032cbfebSHans Rosenfeld res = nioc->n_arg & 0xffffffffUL;
3622032cbfebSHans Rosenfeld if (res >= nvme->n_intr_cnt)
3623032cbfebSHans Rosenfeld return (EINVAL);
3624032cbfebSHans Rosenfeld break;
3625032cbfebSHans Rosenfeld
3626032cbfebSHans Rosenfeld case NVME_FEAT_LBA_RANGE:
3627032cbfebSHans Rosenfeld if (nvme->n_lba_range_supported == B_FALSE)
3628032cbfebSHans Rosenfeld return (EINVAL);
3629032cbfebSHans Rosenfeld
3630032cbfebSHans Rosenfeld if (nsid == 0 ||
3631032cbfebSHans Rosenfeld nsid > nvme->n_namespace_count)
3632032cbfebSHans Rosenfeld return (EINVAL);
3633032cbfebSHans Rosenfeld
3634032cbfebSHans Rosenfeld break;
3635032cbfebSHans Rosenfeld
3636032cbfebSHans Rosenfeld case NVME_FEAT_WRITE_CACHE:
3637032cbfebSHans Rosenfeld if (nsid != 0)
3638032cbfebSHans Rosenfeld return (EINVAL);
3639032cbfebSHans Rosenfeld
3640032cbfebSHans Rosenfeld if (!nvme->n_write_cache_present)
3641032cbfebSHans Rosenfeld return (EINVAL);
3642032cbfebSHans Rosenfeld
3643032cbfebSHans Rosenfeld break;
3644032cbfebSHans Rosenfeld
3645032cbfebSHans Rosenfeld case NVME_FEAT_AUTO_PST:
3646032cbfebSHans Rosenfeld if (nsid != 0)
3647032cbfebSHans Rosenfeld return (EINVAL);
3648032cbfebSHans Rosenfeld
3649032cbfebSHans Rosenfeld if (!nvme->n_auto_pst_supported)
3650032cbfebSHans Rosenfeld return (EINVAL);
3651032cbfebSHans Rosenfeld
3652032cbfebSHans Rosenfeld break;
3653032cbfebSHans Rosenfeld
3654032cbfebSHans Rosenfeld default:
3655032cbfebSHans Rosenfeld return (EINVAL);
3656032cbfebSHans Rosenfeld }
3657032cbfebSHans Rosenfeld
365869a33c23SHans Rosenfeld rv = nvme_get_features(nvme, nsid, feature, &res, &buf, &bufsize);
365969a33c23SHans Rosenfeld if (rv != 0)
366069a33c23SHans Rosenfeld return (rv);
3661032cbfebSHans Rosenfeld
3662032cbfebSHans Rosenfeld if (nioc->n_len < bufsize) {
3663032cbfebSHans Rosenfeld kmem_free(buf, bufsize);
3664032cbfebSHans Rosenfeld return (EINVAL);
3665032cbfebSHans Rosenfeld }
3666032cbfebSHans Rosenfeld
3667032cbfebSHans Rosenfeld if (buf && ddi_copyout(buf, (void*)nioc->n_buf, bufsize, mode) != 0)
3668032cbfebSHans Rosenfeld rv = EFAULT;
3669032cbfebSHans Rosenfeld
3670032cbfebSHans Rosenfeld kmem_free(buf, bufsize);
3671032cbfebSHans Rosenfeld nioc->n_arg = res;
3672032cbfebSHans Rosenfeld nioc->n_len = bufsize;
3673032cbfebSHans Rosenfeld
3674032cbfebSHans Rosenfeld return (rv);
3675032cbfebSHans Rosenfeld }
3676032cbfebSHans Rosenfeld
3677032cbfebSHans Rosenfeld static int
nvme_ioctl_intr_cnt(nvme_t * nvme,int nsid,nvme_ioctl_t * nioc,int mode,cred_t * cred_p)3678032cbfebSHans Rosenfeld nvme_ioctl_intr_cnt(nvme_t *nvme, int nsid, nvme_ioctl_t *nioc, int mode,
3679032cbfebSHans Rosenfeld cred_t *cred_p)
3680032cbfebSHans Rosenfeld {
3681032cbfebSHans Rosenfeld _NOTE(ARGUNUSED(nsid, mode, cred_p));
3682032cbfebSHans Rosenfeld
3683032cbfebSHans Rosenfeld if ((mode & FREAD) == 0)
3684032cbfebSHans Rosenfeld return (EPERM);
3685032cbfebSHans Rosenfeld
3686032cbfebSHans Rosenfeld nioc->n_arg = nvme->n_intr_cnt;
3687032cbfebSHans Rosenfeld return (0);
3688032cbfebSHans Rosenfeld }
3689032cbfebSHans Rosenfeld
3690032cbfebSHans Rosenfeld static int
nvme_ioctl_version(nvme_t * nvme,int nsid,nvme_ioctl_t * nioc,int mode,cred_t * cred_p)3691032cbfebSHans Rosenfeld nvme_ioctl_version(nvme_t *nvme, int nsid, nvme_ioctl_t *nioc, int mode,
3692032cbfebSHans Rosenfeld cred_t *cred_p)
3693032cbfebSHans Rosenfeld {
3694032cbfebSHans Rosenfeld _NOTE(ARGUNUSED(nsid, cred_p));
3695032cbfebSHans Rosenfeld int rv = 0;
3696032cbfebSHans Rosenfeld
3697032cbfebSHans Rosenfeld if ((mode & FREAD) == 0)
3698032cbfebSHans Rosenfeld return (EPERM);
3699032cbfebSHans Rosenfeld
3700032cbfebSHans Rosenfeld if (nioc->n_len < sizeof (nvme->n_version))
3701032cbfebSHans Rosenfeld return (ENOMEM);
3702032cbfebSHans Rosenfeld
3703032cbfebSHans Rosenfeld if (ddi_copyout(&nvme->n_version, (void *)nioc->n_buf,
3704032cbfebSHans Rosenfeld sizeof (nvme->n_version), mode) != 0)
3705032cbfebSHans Rosenfeld rv = EFAULT;
3706032cbfebSHans Rosenfeld
3707032cbfebSHans Rosenfeld return (rv);
3708032cbfebSHans Rosenfeld }
3709032cbfebSHans Rosenfeld
3710032cbfebSHans Rosenfeld static int
nvme_ioctl_format(nvme_t * nvme,int nsid,nvme_ioctl_t * nioc,int mode,cred_t * cred_p)3711032cbfebSHans Rosenfeld nvme_ioctl_format(nvme_t *nvme, int nsid, nvme_ioctl_t *nioc, int mode,
3712032cbfebSHans Rosenfeld cred_t *cred_p)
3713032cbfebSHans Rosenfeld {
3714032cbfebSHans Rosenfeld _NOTE(ARGUNUSED(mode));
3715032cbfebSHans Rosenfeld nvme_format_nvm_t frmt = { 0 };
3716032cbfebSHans Rosenfeld int c_nsid = nsid != 0 ? nsid - 1 : 0;
3717032cbfebSHans Rosenfeld
3718032cbfebSHans Rosenfeld if ((mode & FWRITE) == 0 || secpolicy_sys_config(cred_p, B_FALSE) != 0)
3719032cbfebSHans Rosenfeld return (EPERM);
3720032cbfebSHans Rosenfeld
3721032cbfebSHans Rosenfeld frmt.r = nioc->n_arg & 0xffffffff;
3722032cbfebSHans Rosenfeld
3723032cbfebSHans Rosenfeld /*
3724032cbfebSHans Rosenfeld * Check whether the FORMAT NVM command is supported.
3725032cbfebSHans Rosenfeld */
3726032cbfebSHans Rosenfeld if (nvme->n_idctl->id_oacs.oa_format == 0)
3727032cbfebSHans Rosenfeld return (EINVAL);
3728032cbfebSHans Rosenfeld
3729032cbfebSHans Rosenfeld /*
3730032cbfebSHans Rosenfeld * Don't allow format or secure erase of individual namespace if that
3731032cbfebSHans Rosenfeld * would cause a format or secure erase of all namespaces.
3732032cbfebSHans Rosenfeld */
3733032cbfebSHans Rosenfeld if (nsid != 0 && nvme->n_idctl->id_fna.fn_format != 0)
3734032cbfebSHans Rosenfeld return (EINVAL);
3735032cbfebSHans Rosenfeld
3736032cbfebSHans Rosenfeld if (nsid != 0 && frmt.b.fm_ses != NVME_FRMT_SES_NONE &&
3737032cbfebSHans Rosenfeld nvme->n_idctl->id_fna.fn_sec_erase != 0)
3738032cbfebSHans Rosenfeld return (EINVAL);
3739032cbfebSHans Rosenfeld
3740032cbfebSHans Rosenfeld /*
3741032cbfebSHans Rosenfeld * Don't allow formatting with Protection Information.
3742032cbfebSHans Rosenfeld */
3743032cbfebSHans Rosenfeld if (frmt.b.fm_pi != 0 || frmt.b.fm_pil != 0 || frmt.b.fm_ms != 0)
3744032cbfebSHans Rosenfeld return (EINVAL);
3745032cbfebSHans Rosenfeld
3746032cbfebSHans Rosenfeld /*
3747032cbfebSHans Rosenfeld * Don't allow formatting using an illegal LBA format, or any LBA format
3748032cbfebSHans Rosenfeld * that uses metadata.
3749032cbfebSHans Rosenfeld */
3750032cbfebSHans Rosenfeld if (frmt.b.fm_lbaf > nvme->n_ns[c_nsid].ns_idns->id_nlbaf ||
3751032cbfebSHans Rosenfeld nvme->n_ns[c_nsid].ns_idns->id_lbaf[frmt.b.fm_lbaf].lbaf_ms != 0)
3752032cbfebSHans Rosenfeld return (EINVAL);
3753032cbfebSHans Rosenfeld
3754032cbfebSHans Rosenfeld /*
3755032cbfebSHans Rosenfeld * Don't allow formatting using an illegal Secure Erase setting.
3756032cbfebSHans Rosenfeld */
3757032cbfebSHans Rosenfeld if (frmt.b.fm_ses > NVME_FRMT_MAX_SES ||
3758032cbfebSHans Rosenfeld (frmt.b.fm_ses == NVME_FRMT_SES_CRYPTO &&
3759032cbfebSHans Rosenfeld nvme->n_idctl->id_fna.fn_crypt_erase == 0))
3760032cbfebSHans Rosenfeld return (EINVAL);
3761032cbfebSHans Rosenfeld
3762032cbfebSHans Rosenfeld if (nsid == 0)
3763032cbfebSHans Rosenfeld nsid = (uint32_t)-1;
3764032cbfebSHans Rosenfeld
3765032cbfebSHans Rosenfeld return (nvme_format_nvm(nvme, nsid, frmt.b.fm_lbaf, B_FALSE, 0, B_FALSE,
3766032cbfebSHans Rosenfeld frmt.b.fm_ses));
3767032cbfebSHans Rosenfeld }
3768032cbfebSHans Rosenfeld
3769032cbfebSHans Rosenfeld static int
nvme_ioctl_detach(nvme_t * nvme,int nsid,nvme_ioctl_t * nioc,int mode,cred_t * cred_p)3770032cbfebSHans Rosenfeld nvme_ioctl_detach(nvme_t *nvme, int nsid, nvme_ioctl_t *nioc, int mode,
3771032cbfebSHans Rosenfeld cred_t *cred_p)
3772032cbfebSHans Rosenfeld {
3773032cbfebSHans Rosenfeld _NOTE(ARGUNUSED(nioc, mode));
3774032cbfebSHans Rosenfeld int rv = 0;
3775032cbfebSHans Rosenfeld
3776032cbfebSHans Rosenfeld if ((mode & FWRITE) == 0 || secpolicy_sys_config(cred_p, B_FALSE) != 0)
3777032cbfebSHans Rosenfeld return (EPERM);
3778032cbfebSHans Rosenfeld
3779032cbfebSHans Rosenfeld if (nsid == 0)
3780032cbfebSHans Rosenfeld return (EINVAL);
3781032cbfebSHans Rosenfeld
3782032cbfebSHans Rosenfeld rv = bd_detach_handle(nvme->n_ns[nsid - 1].ns_bd_hdl);
3783032cbfebSHans Rosenfeld if (rv != DDI_SUCCESS)
3784032cbfebSHans Rosenfeld rv = EBUSY;
3785032cbfebSHans Rosenfeld
3786032cbfebSHans Rosenfeld return (rv);
3787032cbfebSHans Rosenfeld }
3788032cbfebSHans Rosenfeld
3789032cbfebSHans Rosenfeld static int
nvme_ioctl_attach(nvme_t * nvme,int nsid,nvme_ioctl_t * nioc,int mode,cred_t * cred_p)3790032cbfebSHans Rosenfeld nvme_ioctl_attach(nvme_t *nvme, int nsid, nvme_ioctl_t *nioc, int mode,
3791032cbfebSHans Rosenfeld cred_t *cred_p)
3792032cbfebSHans Rosenfeld {
3793032cbfebSHans Rosenfeld _NOTE(ARGUNUSED(nioc, mode));
3794032cbfebSHans Rosenfeld nvme_identify_nsid_t *idns;
3795032cbfebSHans Rosenfeld int rv = 0;
3796032cbfebSHans Rosenfeld
3797032cbfebSHans Rosenfeld if ((mode & FWRITE) == 0 || secpolicy_sys_config(cred_p, B_FALSE) != 0)
3798032cbfebSHans Rosenfeld return (EPERM);
3799032cbfebSHans Rosenfeld
3800032cbfebSHans Rosenfeld if (nsid == 0)
3801032cbfebSHans Rosenfeld return (EINVAL);
3802032cbfebSHans Rosenfeld
3803032cbfebSHans Rosenfeld /*
3804032cbfebSHans Rosenfeld * Identify namespace again, free old identify data.
3805032cbfebSHans Rosenfeld */
3806032cbfebSHans Rosenfeld idns = nvme->n_ns[nsid - 1].ns_idns;
3807032cbfebSHans Rosenfeld if (nvme_init_ns(nvme, nsid) != DDI_SUCCESS)
3808032cbfebSHans Rosenfeld return (EIO);
3809032cbfebSHans Rosenfeld
3810032cbfebSHans Rosenfeld kmem_free(idns, sizeof (nvme_identify_nsid_t));
3811032cbfebSHans Rosenfeld
3812032cbfebSHans Rosenfeld rv = bd_attach_handle(nvme->n_dip, nvme->n_ns[nsid - 1].ns_bd_hdl);
3813032cbfebSHans Rosenfeld if (rv != DDI_SUCCESS)
3814032cbfebSHans Rosenfeld rv = EBUSY;
3815032cbfebSHans Rosenfeld
3816032cbfebSHans Rosenfeld return (rv);
3817032cbfebSHans Rosenfeld }
3818032cbfebSHans Rosenfeld
3819032cbfebSHans Rosenfeld static int
nvme_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * cred_p,int * rval_p)3820032cbfebSHans Rosenfeld nvme_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred_p,
3821032cbfebSHans Rosenfeld int *rval_p)
3822032cbfebSHans Rosenfeld {
3823032cbfebSHans Rosenfeld #ifndef __lock_lint
3824032cbfebSHans Rosenfeld _NOTE(ARGUNUSED(rval_p));
3825032cbfebSHans Rosenfeld #endif
3826032cbfebSHans Rosenfeld minor_t minor = getminor(dev);
3827032cbfebSHans Rosenfeld nvme_t *nvme = ddi_get_soft_state(nvme_state, NVME_MINOR_INST(minor));
3828032cbfebSHans Rosenfeld int nsid = NVME_MINOR_NSID(minor);
3829032cbfebSHans Rosenfeld int rv = 0;
3830032cbfebSHans Rosenfeld nvme_ioctl_t nioc;
3831032cbfebSHans Rosenfeld
3832032cbfebSHans Rosenfeld int (*nvme_ioctl[])(nvme_t *, int, nvme_ioctl_t *, int, cred_t *) = {
3833032cbfebSHans Rosenfeld NULL,
3834032cbfebSHans Rosenfeld nvme_ioctl_identify,
3835032cbfebSHans Rosenfeld nvme_ioctl_identify,
3836032cbfebSHans Rosenfeld nvme_ioctl_capabilities,
3837032cbfebSHans Rosenfeld nvme_ioctl_get_logpage,
3838032cbfebSHans Rosenfeld nvme_ioctl_get_features,
3839032cbfebSHans Rosenfeld nvme_ioctl_intr_cnt,
3840032cbfebSHans Rosenfeld nvme_ioctl_version,
3841032cbfebSHans Rosenfeld nvme_ioctl_format,
3842032cbfebSHans Rosenfeld nvme_ioctl_detach,
3843032cbfebSHans Rosenfeld nvme_ioctl_attach
3844032cbfebSHans Rosenfeld };
3845032cbfebSHans Rosenfeld
3846032cbfebSHans Rosenfeld if (nvme == NULL)
3847032cbfebSHans Rosenfeld return (ENXIO);
3848032cbfebSHans Rosenfeld
3849032cbfebSHans Rosenfeld if (nsid > nvme->n_namespace_count)
3850032cbfebSHans Rosenfeld return (ENXIO);
3851032cbfebSHans Rosenfeld
3852032cbfebSHans Rosenfeld if (IS_DEVCTL(cmd))
3853032cbfebSHans Rosenfeld return (ndi_devctl_ioctl(nvme->n_dip, cmd, arg, mode, 0));
3854032cbfebSHans Rosenfeld
3855032cbfebSHans Rosenfeld #ifdef _MULTI_DATAMODEL
3856032cbfebSHans Rosenfeld switch (ddi_model_convert_from(mode & FMODELS)) {
3857032cbfebSHans Rosenfeld case DDI_MODEL_ILP32: {
3858032cbfebSHans Rosenfeld nvme_ioctl32_t nioc32;
3859032cbfebSHans Rosenfeld if (ddi_copyin((void*)arg, &nioc32, sizeof (nvme_ioctl32_t),
3860032cbfebSHans Rosenfeld mode) != 0)
3861032cbfebSHans Rosenfeld return (EFAULT);
3862032cbfebSHans Rosenfeld nioc.n_len = nioc32.n_len;
3863032cbfebSHans Rosenfeld nioc.n_buf = nioc32.n_buf;
3864032cbfebSHans Rosenfeld nioc.n_arg = nioc32.n_arg;
3865032cbfebSHans Rosenfeld break;
3866032cbfebSHans Rosenfeld }
3867032cbfebSHans Rosenfeld case DDI_MODEL_NONE:
3868032cbfebSHans Rosenfeld #endif
3869032cbfebSHans Rosenfeld if (ddi_copyin((void*)arg, &nioc, sizeof (nvme_ioctl_t), mode)
3870032cbfebSHans Rosenfeld != 0)
3871032cbfebSHans Rosenfeld return (EFAULT);
3872032cbfebSHans Rosenfeld #ifdef _MULTI_DATAMODEL
3873032cbfebSHans Rosenfeld break;
3874032cbfebSHans Rosenfeld }
3875032cbfebSHans Rosenfeld #endif
3876032cbfebSHans Rosenfeld
387769a33c23SHans Rosenfeld if (nvme->n_dead && cmd != NVME_IOC_DETACH)
387869a33c23SHans Rosenfeld return (EIO);
387969a33c23SHans Rosenfeld
388069a33c23SHans Rosenfeld
3881032cbfebSHans Rosenfeld if (cmd == NVME_IOC_IDENTIFY_CTRL) {
3882032cbfebSHans Rosenfeld /*
3883032cbfebSHans Rosenfeld * This makes NVME_IOC_IDENTIFY_CTRL work the same on devctl and
3884032cbfebSHans Rosenfeld * attachment point nodes.
3885032cbfebSHans Rosenfeld */
3886032cbfebSHans Rosenfeld nsid = 0;
3887032cbfebSHans Rosenfeld } else if (cmd == NVME_IOC_IDENTIFY_NSID && nsid == 0) {
3888032cbfebSHans Rosenfeld /*
3889032cbfebSHans Rosenfeld * This makes NVME_IOC_IDENTIFY_NSID work on a devctl node, it
3890032cbfebSHans Rosenfeld * will always return identify data for namespace 1.
3891032cbfebSHans Rosenfeld */
3892032cbfebSHans Rosenfeld nsid = 1;
3893032cbfebSHans Rosenfeld }
3894032cbfebSHans Rosenfeld
3895032cbfebSHans Rosenfeld if (IS_NVME_IOC(cmd) && nvme_ioctl[NVME_IOC_CMD(cmd)] != NULL)
3896032cbfebSHans Rosenfeld rv = nvme_ioctl[NVME_IOC_CMD(cmd)](nvme, nsid, &nioc, mode,
3897032cbfebSHans Rosenfeld cred_p);
3898032cbfebSHans Rosenfeld else
3899032cbfebSHans Rosenfeld rv = EINVAL;
3900032cbfebSHans Rosenfeld
3901032cbfebSHans Rosenfeld #ifdef _MULTI_DATAMODEL
3902032cbfebSHans Rosenfeld switch (ddi_model_convert_from(mode & FMODELS)) {
3903032cbfebSHans Rosenfeld case DDI_MODEL_ILP32: {
3904032cbfebSHans Rosenfeld nvme_ioctl32_t nioc32;
3905032cbfebSHans Rosenfeld
3906032cbfebSHans Rosenfeld nioc32.n_len = (size32_t)nioc.n_len;
3907032cbfebSHans Rosenfeld nioc32.n_buf = (uintptr32_t)nioc.n_buf;
3908032cbfebSHans Rosenfeld nioc32.n_arg = nioc.n_arg;
3909032cbfebSHans Rosenfeld
3910032cbfebSHans Rosenfeld if (ddi_copyout(&nioc32, (void *)arg, sizeof (nvme_ioctl32_t),
3911032cbfebSHans Rosenfeld mode) != 0)
3912032cbfebSHans Rosenfeld return (EFAULT);
3913032cbfebSHans Rosenfeld break;
3914032cbfebSHans Rosenfeld }
3915032cbfebSHans Rosenfeld case DDI_MODEL_NONE:
3916032cbfebSHans Rosenfeld #endif
3917032cbfebSHans Rosenfeld if (ddi_copyout(&nioc, (void *)arg, sizeof (nvme_ioctl_t), mode)
3918032cbfebSHans Rosenfeld != 0)
3919032cbfebSHans Rosenfeld return (EFAULT);
3920032cbfebSHans Rosenfeld #ifdef _MULTI_DATAMODEL
3921032cbfebSHans Rosenfeld break;
3922032cbfebSHans Rosenfeld }
3923032cbfebSHans Rosenfeld #endif
3924032cbfebSHans Rosenfeld
3925032cbfebSHans Rosenfeld return (rv);
3926032cbfebSHans Rosenfeld }
3927