xref: /haiku/src/add-ons/kernel/drivers/disk/nvme/libnvme/nvme.c (revision 17889a8c70dbb3d59c1412f6431968753c767bab)
1 /*-
2  *   BSD LICENSE
3  *
4  *   Copyright (c) Intel Corporation. All rights reserved.
5  *   Copyright (c) 2017, Western Digital Corporation or its affiliates.
6  *
7  *   Redistribution and use in source and binary forms, with or without
8  *   modification, are permitted provided that the following conditions
9  *   are met:
10  *
11  *     * Redistributions of source code must retain the above copyright
12  *       notice, this list of conditions and the following disclaimer.
13  *     * Redistributions in binary form must reproduce the above copyright
14  *       notice, this list of conditions and the following disclaimer in
15  *       the documentation and/or other materials provided with the
16  *       distribution.
17  *     * Neither the name of Intel Corporation nor the names of its
18  *       contributors may be used to endorse or promote products derived
19  *       from this software without specific prior written permission.
20  *
21  *   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  *   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  *   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  *   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  *   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  *   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  *   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  *   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include "nvme_internal.h"
35 
36 /*
37  * List of open controllers and its lock.
38  */
39 LIST_HEAD(, nvme_ctrlr)	ctrlr_head = LIST_HEAD_INITIALIZER(ctrlr_head);
40 static pthread_mutex_t ctrlr_lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER;
41 
42 /*
43  * Search for an open controller.
44  */
45 static struct nvme_ctrlr *nvme_ctrlr_get(struct nvme_ctrlr *ctrlr,
46 					 bool remove)
47 {
48 	struct nvme_ctrlr *c;
49 
50 	pthread_mutex_lock(&ctrlr_lock);
51 
52 	LIST_FOREACH(c, &ctrlr_head, link) {
53 		if (c == ctrlr) {
54 			if (remove)
55 				LIST_REMOVE(c, link);
56 			goto out;
57 		}
58 	}
59 
60 	ctrlr = NULL;
61 
62 out:
63 	pthread_mutex_unlock(&ctrlr_lock);
64 
65 	return ctrlr;
66 }
67 
68 #ifndef __HAIKU__
69 /*
70  * Probe a pci device identified by its name.
71  * Name should be in the form: [0000:]00:00.0
72  * Return NULL if failed
73  */
74 static struct pci_device *nvme_pci_ctrlr_probe(const char *slot_name)
75 {
76 	char *domain = NULL, *bus = NULL, *dev = NULL, *func = NULL, *end = NULL;
77 	char *pciid = strdup(slot_name);
78 	struct pci_slot_match slot;
79 	struct pci_device *pci_dev = NULL;
80 
81 	if (!pciid)
82 		return NULL;
83 
84 	memset(&slot, 0, sizeof(struct pci_slot_match));
85 
86 	func = strrchr(pciid, '.');
87 	if (func) {
88 		*func = '\0';
89 		func++;
90 	}
91 
92 	dev = strrchr(pciid, ':');
93 	if (dev) {
94 		*dev = '\0';
95 		dev++;
96 	}
97 
98 	bus = strrchr(pciid, ':');
99 	if (!bus) {
100 		domain = NULL;
101 		bus = pciid;
102 	} else {
103 		domain = pciid;
104 		*bus = '\0';
105 		bus++;
106 	}
107 
108 	if (!bus || !dev || !func) {
109 		nvme_err("Malformed PCI device slot name %s\n",
110 			 slot_name);
111 		goto out;
112 	}
113 
114 	if (domain) {
115 		slot.domain = (uint32_t)strtoul(domain, &end, 16);
116 		if ((end && *end) || (slot.domain > 0xffff)) {
117 			nvme_err("Invalid domain number: 0x%X\n", slot.domain);
118 			return NULL;
119 		}
120 	} else {
121 		slot.domain = PCI_MATCH_ANY;
122 	}
123 
124 	slot.bus = (uint32_t)strtoul(bus, &end, 16);
125 	if ((end && *end) || (slot.bus > 0xff)) {
126 		nvme_err("Invalid bus number: 0x%X\n", slot.bus);
127 		return NULL;
128 	}
129 
130 	slot.dev = strtoul(dev, &end, 16);
131 	if ((end && *end) || (slot.dev > 0x1f)) {
132 		nvme_err("Invalid device number: 0x%X\n", slot.dev);
133 		return NULL;
134 	}
135 
136 	slot.func = strtoul(func, &end, 16);
137 	if ((end && *end) || (slot.func > 7)) {
138 		nvme_err("Invalid function number: 0x%X\n", slot.func);
139 		return NULL;
140 	}
141 
142 	nvme_debug("PCI URL: domain 0x%X, bus 0x%X, dev 0x%X, func 0x%X\n",
143 		   slot.domain, slot.bus, slot.dev, slot.func);
144 
145 	pci_dev = nvme_pci_device_probe(&slot);
146 	if (pci_dev) {
147 		slot.domain = pci_dev->domain;
148 		if (slot.domain == PCI_MATCH_ANY)
149 			slot.domain = 0;
150 		nvme_info("Found NVMe controller %04x:%02x:%02x.%1u\n",
151 			  slot.domain,
152 			  slot.bus,
153 			  slot.dev,
154 			  slot.func);
155 	}
156 
157 out:
158 	free(pciid);
159 
160 	return pci_dev;
161 }
162 #endif
163 
164 /*
165  * Open an NVMe controller.
166  */
167 #ifdef __HAIKU__
168 struct nvme_ctrlr *nvme_ctrlr_open(struct pci_device *pdev,
169 				   struct nvme_ctrlr_opts *opts)
170 #else
171 struct nvme_ctrlr *nvme_ctrlr_open(const char *url,
172 				   struct nvme_ctrlr_opts *opts)
173 #endif
174 {
175 	struct nvme_ctrlr *ctrlr;
176 #ifndef __HAIKU__
177 	char *slot;
178 
179 	/* Check url */
180 	if (strncmp(url, "pci://", 6) != 0) {
181 		nvme_err("Invalid URL %s\n", url);
182 		return NULL;
183 	}
184 
185 	/* Probe PCI device */
186 	slot = (char *)url + 6;
187 	pdev = nvme_pci_ctrlr_probe(slot);
188 	if (!pdev) {
189 		nvme_err("Device %s not found\n", url);
190 		return NULL;
191 	}
192 #endif
193 
194 	pthread_mutex_lock(&ctrlr_lock);
195 
196 	/* Verify that this controller is not already open */
197 	LIST_FOREACH(ctrlr, &ctrlr_head, link) {
198 		if (nvme_pci_dev_cmp(ctrlr->pci_dev, pdev) == 0) {
199 			nvme_err("Controller already open\n");
200 			ctrlr = NULL;
201 			goto out;
202 		}
203 	}
204 
205 	/* Attach the device */
206 	ctrlr = nvme_ctrlr_attach(pdev, opts);
207 	if (!ctrlr) {
208 		nvme_err("Attach failed\n");
209 		goto out;
210 	}
211 
212 	/* Add controller to the list */
213 	LIST_INSERT_HEAD(&ctrlr_head, ctrlr, link);
214 
215 out:
216 	pthread_mutex_unlock(&ctrlr_lock);
217 
218 	return ctrlr;
219 
220 }
221 
222 /*
223  * Close an open controller.
224  */
225 int nvme_ctrlr_close(struct nvme_ctrlr *ctrlr)
226 {
227 
228 	/*
229 	 * Verify that this controller is open.
230 	 * If it is, remove it from the list.
231 	 */
232 	ctrlr = nvme_ctrlr_get(ctrlr, true);
233 	if (!ctrlr) {
234 		nvme_err("Invalid controller\n");
235 		return EINVAL;
236 	}
237 
238 	nvme_ctrlr_detach(ctrlr);
239 
240 	return 0;
241 }
242 
243 /*
244  * Get controller information.
245  */
246 int nvme_ctrlr_stat(struct nvme_ctrlr *ctrlr, struct nvme_ctrlr_stat *cstat)
247 {
248 	struct pci_device *pdev = ctrlr->pci_dev;
249 	unsigned int i;
250 
251 	/* Verify that this controller is open */
252 	ctrlr = nvme_ctrlr_get(ctrlr, false);
253 	if (!ctrlr) {
254 		nvme_err("Invalid controller\n");
255 		return EINVAL;
256 	}
257 
258 	pthread_mutex_lock(&ctrlr->lock);
259 
260 	memset(cstat, 0, sizeof(struct nvme_ctrlr_stat));
261 
262 	/* Controller serial and model number */
263 	strncpy(cstat->sn, (char *)ctrlr->cdata.sn,
264 		NVME_SERIAL_NUMBER_LENGTH - 1);
265 	strncpy(cstat->mn, (char *)ctrlr->cdata.mn,
266 		NVME_MODEL_NUMBER_LENGTH - 1);
267 
268 	/* Remove heading and trailling spaces */
269 	nvme_str_trim(cstat->sn);
270 	nvme_str_trim(cstat->mn);
271 
272 	/* PCI device info */
273 	cstat->vendor_id = pdev->vendor_id;
274 	cstat->device_id = pdev->device_id;
275 	cstat->subvendor_id = pdev->subvendor_id;
276 	cstat->subdevice_id = pdev->subdevice_id;
277 #ifndef __HAIKU__
278 	cstat->device_class = pdev->device_class;
279 	cstat->revision = pdev->revision;
280 	cstat->domain = pdev->domain;
281 	cstat->bus = pdev->bus;
282 	cstat->dev = pdev->dev;
283 	cstat->func = pdev->func;
284 #endif
285 
286 	/* Maximum transfer size */
287 	cstat->max_xfer_size = ctrlr->max_xfer_size;
288 
289 	cstat->sgl_supported = (ctrlr->flags & NVME_CTRLR_SGL_SUPPORTED);
290 
291 	memcpy(&cstat->features, &ctrlr->feature_supported,
292 	       sizeof(ctrlr->feature_supported));
293 	memcpy(&cstat->log_pages, &ctrlr->log_page_supported,
294 	       sizeof(ctrlr->log_page_supported));
295 
296 	cstat->nr_ns = ctrlr->nr_ns;
297 	for (i = 0; i < ctrlr->nr_ns; i++) {
298 		cstat->ns_ids[i] = i + 1;
299 	}
300 
301 	/* Maximum io qpair possible */
302 	cstat->max_io_qpairs = ctrlr->max_io_queues;
303 
304 	/* Constructed io qpairs */
305 	cstat->io_qpairs = ctrlr->io_queues;
306 
307 	/* Enabled io qpairs */
308 	cstat->enabled_io_qpairs = ctrlr->enabled_io_qpairs;
309 
310 	/* Max queue depth */
311 	cstat->max_qd = ctrlr->io_qpairs_max_entries;
312 
313 	pthread_mutex_unlock(&ctrlr->lock);
314 
315 	return 0;
316 }
317 
318 /*
319  * Get controller data
320  */
321 int nvme_ctrlr_data(struct nvme_ctrlr *ctrlr, struct nvme_ctrlr_data *cdata,
322 		    struct nvme_register_data *rdata)
323 {
324 	union nvme_cap_register	cap;
325 
326 	/* Verify that this controller is open */
327 	ctrlr = nvme_ctrlr_get(ctrlr, false);
328 	if (!ctrlr) {
329 		nvme_err("Invalid controller\n");
330 		return EINVAL;
331 	}
332 
333 	pthread_mutex_lock(&ctrlr->lock);
334 
335 	/* Controller data */
336 	if (cdata)
337 		memcpy(cdata, &ctrlr->cdata, sizeof(struct nvme_ctrlr_data));
338 
339 	/* Read capabilities register */
340 	if (rdata) {
341 		cap.raw = nvme_reg_mmio_read_8(ctrlr, cap.raw);
342 		rdata->mqes = cap.bits.mqes;
343 	}
344 
345 	pthread_mutex_unlock(&ctrlr->lock);
346 
347 	return 0;
348 }
349 
350 /*
351  * Get qpair information
352  */
353 int nvme_qpair_stat(struct nvme_qpair *qpair, struct nvme_qpair_stat *qpstat)
354 {
355 	struct nvme_ctrlr *ctrlr = qpair->ctrlr;
356 
357 	/* Verify that this controller is open */
358 	ctrlr = nvme_ctrlr_get(ctrlr, false);
359 	if (!ctrlr) {
360 		nvme_err("Invalid controller\n");
361 		return EINVAL;
362 	}
363 
364 	pthread_mutex_lock(&ctrlr->lock);
365 
366 	qpstat->id = qpair->id;
367 	qpstat->qd = qpair->entries;
368 	qpstat->enabled = qpair->enabled;
369 	qpstat->qprio = qpair->qprio;
370 
371 	pthread_mutex_unlock(&ctrlr->lock);
372 
373 	return 0;
374 }
375 
376 /*
377  * Close all open controllers on exit.
378  */
379 void nvme_ctrlr_cleanup(void)
380 {
381 	struct nvme_ctrlr *ctrlr;
382 
383 	while ((ctrlr = LIST_FIRST(&ctrlr_head))) {
384 		LIST_REMOVE(ctrlr, link);
385 		nvme_ctrlr_detach(ctrlr);
386 	}
387 }
388