xref: /freebsd/usr.sbin/nvmfd/devices.c (revision d37eb51047221dc3322b34db1038ff3aa533883f)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2023-2024 Chelsio Communications, Inc.
5  * Written by: John Baldwin <jhb@FreeBSD.org>
6  */
7 
8 #include <sys/disk.h>
9 #include <sys/gsb_crc32.h>
10 #include <sys/ioctl.h>
11 #include <sys/stat.h>
12 #include <net/ieee_oui.h>
13 #include <err.h>
14 #include <errno.h>
15 #include <fcntl.h>
16 #include <libnvmf.h>
17 #include <libutil.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
21 
22 #include "internal.h"
23 
24 #define	RAMDISK_PREFIX	"ramdisk:"
25 
26 struct backing_device {
27 	enum { RAMDISK, FILE, CDEV } type;
28 	union {
29 		int	fd;	/* FILE, CDEV */
30 		void	*mem;	/* RAMDISK */
31 	};
32 	u_int	sector_size;
33 	uint64_t nlbas;
34 	uint64_t eui64;
35 };
36 
37 static struct backing_device *devices;
38 static u_int ndevices;
39 
40 static uint64_t
41 generate_eui64(uint32_t low)
42 {
43 	return (OUI_FREEBSD_NVME_LOW << 16 | low);
44 }
45 
46 static uint32_t
47 crc32(const void *buf, size_t len)
48 {
49 	return (calculate_crc32c(0xffffffff, buf, len) ^ 0xffffffff);
50 }
51 
52 static void
53 init_ramdisk(const char *config, struct backing_device *dev)
54 {
55 	static uint32_t ramdisk_idx = 1;
56 	uint64_t num;
57 
58 	dev->type = RAMDISK;
59 	dev->sector_size = 512;
60 	if (expand_number(config, &num))
61 		errx(1, "Invalid ramdisk specification: %s", config);
62 	if ((num % dev->sector_size) != 0)
63 		errx(1, "Invalid ramdisk size %ju", (uintmax_t)num);
64 	dev->mem = calloc(num, 1);
65 	dev->nlbas = num / dev->sector_size;
66 	dev->eui64 = generate_eui64('M' << 24 | ramdisk_idx++);
67 }
68 
69 static void
70 init_filedevice(const char *config, int fd, struct stat *sb,
71     struct backing_device *dev)
72 {
73 	dev->type = FILE;
74 	dev->fd = fd;
75 	dev->sector_size = 512;
76 	if ((sb->st_size % dev->sector_size) != 0)
77 		errx(1, "File size is not a multiple of 512: %s", config);
78 	dev->nlbas = sb->st_size / dev->sector_size;
79 	dev->eui64 = generate_eui64('F' << 24 |
80 	    (crc32(config, strlen(config)) & 0xffffff));
81 }
82 
83 static void
84 init_chardevice(const char *config, int fd, struct backing_device *dev)
85 {
86 	off_t len;
87 
88 	dev->type = CDEV;
89 	dev->fd = fd;
90 	if (ioctl(fd, DIOCGSECTORSIZE, &dev->sector_size) != 0)
91 		err(1, "Failed to fetch sector size for %s", config);
92 	if (ioctl(fd, DIOCGMEDIASIZE, &len) != 0)
93 		err(1, "Failed to fetch sector size for %s", config);
94 	dev->nlbas = len / dev->sector_size;
95 	dev->eui64 = generate_eui64('C' << 24 |
96 	    (crc32(config, strlen(config)) & 0xffffff));
97 }
98 
99 static void
100 init_device(const char *config, struct backing_device *dev)
101 {
102 	struct stat sb;
103 	int fd;
104 
105 	/* Check for a RAM disk. */
106 	if (strncmp(RAMDISK_PREFIX, config, strlen(RAMDISK_PREFIX)) == 0) {
107 		init_ramdisk(config + strlen(RAMDISK_PREFIX), dev);
108 		return;
109 	}
110 
111 	fd = open(config, O_RDWR);
112 	if (fd == -1)
113 		err(1, "Failed to open %s", config);
114 	if (fstat(fd, &sb) == -1)
115 		err(1, "fstat");
116 	switch (sb.st_mode & S_IFMT) {
117 	case S_IFCHR:
118 		init_chardevice(config, fd, dev);
119 		break;
120 	case S_IFREG:
121 		init_filedevice(config, fd, &sb, dev);
122 		break;
123 	default:
124 		errx(1, "Invalid file type for %s", config);
125 	}
126 }
127 
128 void
129 register_devices(int ac, char **av)
130 {
131 	ndevices = ac;
132 	devices = calloc(ndevices, sizeof(*devices));
133 
134 	for (int i = 0; i < ac; i++)
135 		init_device(av[i], &devices[i]);
136 }
137 
138 u_int
139 device_count(void)
140 {
141 	return (ndevices);
142 }
143 
144 static struct backing_device *
145 lookup_device(uint32_t nsid)
146 {
147 	if (nsid == 0 || nsid > ndevices)
148 		return (NULL);
149 	return (&devices[nsid - 1]);
150 }
151 
152 void
153 device_active_nslist(uint32_t nsid, struct nvme_ns_list *nslist)
154 {
155 	u_int count;
156 
157 	memset(nslist, 0, sizeof(*nslist));
158 	count = 0;
159 	nsid++;
160 	while (nsid <= ndevices) {
161 		nslist->ns[count] = htole32(nsid);
162 		count++;
163 		if (count == nitems(nslist->ns))
164 			break;
165 		nsid++;
166 	}
167 }
168 
169 bool
170 device_identification_descriptor(uint32_t nsid, void *buf)
171 {
172 	struct backing_device *dev;
173 	char *p;
174 
175 	dev = lookup_device(nsid);
176 	if (dev == NULL)
177 		return (false);
178 
179 	memset(buf, 0, 4096);
180 
181 	p = buf;
182 
183 	/* EUI64 */
184 	*p++ = 1;
185 	*p++ = 8;
186 	p += 2;
187 	be64enc(p, dev->eui64);
188 	return (true);
189 }
190 
191 bool
192 device_namespace_data(uint32_t nsid, struct nvme_namespace_data *nsdata)
193 {
194 	struct backing_device *dev;
195 
196 	dev = lookup_device(nsid);
197 	if (dev == NULL)
198 		return (false);
199 
200 	memset(nsdata, 0, sizeof(*nsdata));
201 	nsdata->nsze = htole64(dev->nlbas);
202 	nsdata->ncap = nsdata->nsze;
203 	nsdata->nuse = nsdata->ncap;
204 	nsdata->nlbaf = 1 - 1;
205 	nsdata->flbas = NVMEF(NVME_NS_DATA_FLBAS_FORMAT, 0);
206 	nsdata->lbaf[0] = NVMEF(NVME_NS_DATA_LBAF_LBADS,
207 	    ffs(dev->sector_size) - 1);
208 
209 	be64enc(nsdata->eui64, dev->eui64);
210 	return (true);
211 }
212 
213 static bool
214 read_buffer(int fd, void *buf, size_t len, off_t offset)
215 {
216 	ssize_t nread;
217 	char *dst;
218 
219 	dst = buf;
220 	while (len > 0) {
221 		nread = pread(fd, dst, len, offset);
222 		if (nread == -1 && errno == EINTR)
223 			continue;
224 		if (nread <= 0)
225 			return (false);
226 		dst += nread;
227 		len -= nread;
228 		offset += nread;
229 	}
230 	return (true);
231 }
232 
233 void
234 device_read(uint32_t nsid, uint64_t lba, u_int nlb,
235     const struct nvmf_capsule *nc)
236 {
237 	struct backing_device *dev;
238 	char *p, *src;
239 	off_t off;
240 	size_t len;
241 
242 	dev = lookup_device(nsid);
243 	if (dev == NULL) {
244 		nvmf_send_generic_error(nc,
245 		    NVME_SC_INVALID_NAMESPACE_OR_FORMAT);
246 		return;
247 	}
248 
249 	if (lba + nlb < lba || lba + nlb > dev->nlbas) {
250 		nvmf_send_generic_error(nc, NVME_SC_LBA_OUT_OF_RANGE);
251 		return;
252 	}
253 
254 	off = lba * dev->sector_size;
255 	len = nlb * dev->sector_size;
256 	if (nvmf_capsule_data_len(nc) != len) {
257 		nvmf_send_generic_error(nc, NVME_SC_INVALID_FIELD);
258 		return;
259 	}
260 
261 	if (dev->type == RAMDISK) {
262 		p = NULL;
263 		src = (char *)dev->mem + off;
264 	} else {
265 		p = malloc(len);
266 		if (!read_buffer(dev->fd, p, len, off)) {
267 			free(p);
268 			nvmf_send_generic_error(nc,
269 			    NVME_SC_INTERNAL_DEVICE_ERROR);
270 			return;
271 		}
272 		src = p;
273 	}
274 
275 	nvmf_send_controller_data(nc, src, len);
276 	free(p);
277 }
278 
279 static bool
280 write_buffer(int fd, const void *buf, size_t len, off_t offset)
281 {
282 	ssize_t nwritten;
283 	const char *src;
284 
285 	src = buf;
286 	while (len > 0) {
287 		nwritten = pwrite(fd, src, len, offset);
288 		if (nwritten == -1 && errno == EINTR)
289 			continue;
290 		if (nwritten <= 0)
291 			return (false);
292 		src += nwritten;
293 		len -= nwritten;
294 		offset += nwritten;
295 	}
296 	return (true);
297 }
298 
299 void
300 device_write(uint32_t nsid, uint64_t lba, u_int nlb,
301     const struct nvmf_capsule *nc)
302 {
303 	struct backing_device *dev;
304 	char *p, *dst;
305 	off_t off;
306 	size_t len;
307 	int error;
308 
309 	dev = lookup_device(nsid);
310 	if (dev == NULL) {
311 		nvmf_send_generic_error(nc,
312 		    NVME_SC_INVALID_NAMESPACE_OR_FORMAT);
313 		return;
314 	}
315 
316 	if (lba + nlb < lba || lba + nlb > dev->nlbas) {
317 		nvmf_send_generic_error(nc, NVME_SC_LBA_OUT_OF_RANGE);
318 		return;
319 	}
320 
321 	off = lba * dev->sector_size;
322 	len = nlb * dev->sector_size;
323 	if (nvmf_capsule_data_len(nc) != len) {
324 		nvmf_send_generic_error(nc, NVME_SC_INVALID_FIELD);
325 		return;
326 	}
327 
328 	if (dev->type == RAMDISK) {
329 		p = NULL;
330 		dst = (char *)dev->mem + off;
331 	} else {
332 		p = malloc(len);
333 		dst = p;
334 	}
335 
336 	error = nvmf_receive_controller_data(nc, 0, dst, len);
337 	if (error != 0) {
338 		nvmf_send_generic_error(nc, NVME_SC_TRANSIENT_TRANSPORT_ERROR);
339 		free(p);
340 		return;
341 	}
342 
343 	if (dev->type != RAMDISK) {
344 		if (!write_buffer(dev->fd, p, len, off)) {
345 			free(p);
346 			nvmf_send_generic_error(nc,
347 			    NVME_SC_INTERNAL_DEVICE_ERROR);
348 			return;
349 		}
350 	}
351 	free(p);
352 	nvmf_send_success(nc);
353 }
354 
355 void
356 device_flush(uint32_t nsid, const struct nvmf_capsule *nc)
357 {
358 	struct backing_device *dev;
359 
360 	dev = lookup_device(nsid);
361 	if (dev == NULL) {
362 		nvmf_send_generic_error(nc,
363 		    NVME_SC_INVALID_NAMESPACE_OR_FORMAT);
364 		return;
365 	}
366 
367 	switch (dev->type) {
368 	case RAMDISK:
369 		break;
370 	case FILE:
371 		if (fdatasync(dev->fd) == -1) {
372 			nvmf_send_error(nc, NVME_SCT_MEDIA_ERROR,
373 			    NVME_SC_WRITE_FAULTS);
374 			return;
375 		}
376 		break;
377 	case CDEV:
378 		if (ioctl(dev->fd, DIOCGFLUSH) == -1) {
379 			nvmf_send_error(nc, NVME_SCT_MEDIA_ERROR,
380 			    NVME_SC_WRITE_FAULTS);
381 			return;
382 		}
383 	}
384 
385 	nvmf_send_success(nc);
386 }
387