xref: /haiku/src/add-ons/kernel/drivers/disk/virtual/nbd/nbd.c (revision 637cbfeeefd5633e5e2906c73e50657561605730)
1 /*
2  * Copyright 2006-2007, François Revol. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 /*
7  * nbd driver for Haiku
8  *
9  * Maps a Network Block Device as virtual partitions.
10  */
11 
12 #include <KernelExport.h>
13 #include <Drivers.h>
14 #include <Errors.h>
15 #include <driver_settings.h>
16 #include <ksocket.h>
17 #include <netinet/in.h>
18 
19 #define DEBUG 1
20 
21 /* locking support */
22 #ifdef __HAIKU__
23 #include <kernel/lock.h>
24 #else
25 /* wrappers for R5 */
26 #ifndef _IMPEXP_KERNEL
27 #define _IMPEXP_KERNEL
28 #endif
29 #include "lock.h"
30 #define benaphore lock
31 #define benaphore_init new_lock
32 #define benaphore_destroy free_lock
33 #define benaphore_lock LOCK
34 #define benaphore_unlock UNLOCK
35 #endif
36 
37 #include "nbd.h"
38 
39 #define DRV "nbd"
40 #define DP "nbd:"
41 #define MAX_NBDS 4
42 #define DEVICE_PREFIX "disk/virtual/nbd/"
43 #define DEVICE_FMT DEVICE_PREFIX "%d/raw"
44 #define DEVICE_NAME_MAX 32
45 #define MAX_REQ_SIZE (32*1024*1024)
46 
47 /* debugging */
48 #if DEBUG
49 #define PRINT(a) dprintf a
50 #define WHICH(dev) ((int)(dev - nbd_devices))
51 #else
52 #define PRINT(a)
53 #endif
54 
55 struct nbd_request_entry {
56 	struct nbd_request_entry *next;
57 	struct nbd_request req;
58 	bool r; /* is read */
59 	size_t len;
60 	void *buffer; /* write: ptr to passed buffer; read: ptr to malloc()ed extra */
61 };
62 
63 struct nbd_device {
64 	char target[64]; // "ip:port"
65 	struct sockaddr_in server;
66 	benaphore ben;
67 	vint32 refcnt;
68 	uint64 req; /* next ID for requests */
69 	int sock;
70 	thread_id postoffice;
71 	uint64 size;
72 	struct nbd_request_entry *reqs;
73 };
74 
75 typedef struct cookie {
76 	struct nbd_device *dev;
77 
78 } cookie_t;
79 
80 /* data=NULL on read */
81 status_t nbd_alloc_request(struct nbd_device, struct nbd_request_entry **req, size_t len, const char *data);
82 status_t nbd_post_request(struct nbd_device, uint64 handle, struct nbd_request_entry **req);
83 status_t nbd_dequeue_request(struct nbd_device, uint64 handle, struct nbd_request_entry **req);
84 status_t nbd_free_request(struct nbd_device, struct nbd_request_entry *req);
85 
86 struct nbd_device *nbd_find_device(const char* name);
87 
88 KSOCKET_MODULE_DECL;
89 
90 /* HACK:
91  * In BONE at least, if connect() fails (EINTR or ETIMEDOUT)
92  * keeps locked pages around (likely a bone_data,
93  * until TCP gets the last ACK). If that happens, we snooze()
94  * in unload_driver() to let TCP timeout before the kernel
95  * tries to delete the image. */
96 bool gDelayUnload = false;
97 #define BONE_TEARDOWN_DELAY 60000000
98 
99 #pragma mark ==== request manager ====
100 
101 
102 #pragma mark ==== nbd handler ====
103 
104 int32 nbd_postoffice(void *arg)
105 {
106 	struct nbd_device *dev = (struct nbd_device *)arg;
107 	int sock = dev->sock;
108 	PRINT((DP ">%s()\n", __FUNCTION__));
109 
110 
111 
112 	PRINT((DP "<%s\n", __FUNCTION__));
113 	return 0;
114 }
115 
116 status_t nbd_connect(struct nbd_device *dev)
117 {
118 	struct nbd_init_packet initpkt;
119 	status_t err;
120 	PRINT((DP ">%s()\n", __FUNCTION__));
121 
122 	PRINT((DP " %s: socket()\n", __FUNCTION__));
123 	err = dev->sock = ksocket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
124 	if (err == -1 && errno < 0)
125 		err = errno;
126 	if (err < 0)
127 		goto err0;
128 
129 	PRINT((DP " %s: connect()\n", __FUNCTION__));
130 	err = kconnect(dev->sock, (struct sockaddr *)&dev->server, sizeof(dev->server));
131 	//err = ENOSYS;
132 	if (err == -1 && errno < 0)
133 		err = errno;
134 	/* HACK: avoid the kernel unlading us with locked pages from TCP */
135 	if (err)
136 		gDelayUnload = true;
137 	if (err)
138 		goto err1;
139 
140 	PRINT((DP " %s: recv(initpkt)\n", __FUNCTION__));
141 	err = krecv(dev->sock, &initpkt, sizeof(initpkt), 0);
142 	if (err == -1 && errno < 0)
143 		err = errno;
144 	if (err < sizeof(initpkt))
145 		goto err2;
146 	err = EINVAL;//EPROTO;
147 	if (memcmp(initpkt.passwd, NBD_INIT_PASSWD, sizeof(initpkt.passwd)))
148 		goto err3;
149 	if (B_BENDIAN_TO_HOST_INT64(initpkt.magic) != NBD_INIT_MAGIC)
150 		goto err3;
151 
152 	dev->size = B_BENDIAN_TO_HOST_INT64(initpkt.device_size);
153 
154 	dprintf(DP " %s: connected, device size %Ld bytes.\n", __FUNCTION__, dev->size);
155 
156 	err = dev->postoffice = spawn_kernel_thread(nbd_postoffice, "nbd postoffice", B_REAL_TIME_PRIORITY, dev);
157 	if (err < B_OK)
158 		goto err4;
159 	resume_thread(dev->postoffice);
160 
161 	PRINT((DP "<%s\n", __FUNCTION__));
162 	return B_OK;
163 
164 err4:
165 	dev->postoffice = -1;
166 err3:
167 err2:
168 err1:
169 	kclosesocket(dev->sock);
170 	dev->sock = -1;
171 err0:
172 	dprintf(DP "<%s: error 0x%08lx\n", __FUNCTION__, err);
173 	return err;
174 }
175 
176 status_t nbd_teardown(struct nbd_device *dev)
177 {
178 	status_t err, ret;
179 	PRINT((DP ">%s()\n", __FUNCTION__));
180 	kshutdown(dev->sock, SHUTDOWN_BOTH);
181 	kclosesocket(dev->sock);
182 	dev->sock = -1;
183 	err = wait_for_thread(dev->postoffice, &ret);
184 	return B_OK;
185 }
186 
187 #pragma mark ==== device hooks ====
188 
189 static struct nbd_device nbd_devices[MAX_NBDS];
190 
191 status_t nbd_open(const char *name, uint32 flags, cookie_t **cookie) {
192 	status_t err;
193 	struct nbd_device *dev = NULL;
194 	PRINT((DP ">%s(%s, %x, )\n", __FUNCTION__, name, flags));
195 	(void)name; (void)flags;
196 	dev = nbd_find_device(name);
197 	if (!dev)
198 		return ENOENT;
199 	err = ENOMEM;
200 	*cookie = (void*)malloc(sizeof(cookie_t));
201 	if (*cookie == NULL)
202 		goto err0;
203 	memset(*cookie, 0, sizeof(cookie_t));
204 	(*cookie)->dev = dev;
205 	err = benaphore_lock(&dev->ben);
206 	if (err)
207 		goto err1;
208 	/*  */
209 	if (dev->sock < 0)
210 		err = nbd_connect(dev);
211 	if (err)
212 		goto err2;
213 	dev->refcnt++;
214 	benaphore_unlock(&dev->ben);
215 	return B_OK;
216 
217 err2:
218 	benaphore_unlock(&dev->ben);
219 err1:
220 	free(*cookie);
221 err0:
222 	dprintf(DP " %s: error 0x%08lx\n", __FUNCTION__, err);
223 	return err;
224 }
225 
226 status_t nbd_close(cookie_t *cookie) {
227 	struct nbd_device *dev = cookie->dev;
228 	status_t err;
229 	PRINT((DP ">%s(%d)\n", __FUNCTION__, WHICH(cookie->dev)));
230 
231 	err = benaphore_lock(&dev->ben);
232 	if (err)
233 		return err;
234 
235 	// XXX: do something ?
236 
237 	benaphore_unlock(&dev->ben);
238 	return B_OK;
239 }
240 
241 status_t nbd_free(cookie_t *cookie) {
242 	struct nbd_device *dev = cookie->dev;
243 	status_t err;
244 	PRINT((DP ">%s(%d)\n", __FUNCTION__, WHICH(cookie->dev)));
245 
246 	err = benaphore_lock(&dev->ben);
247 	if (err)
248 		return err;
249 
250 	if (--dev->refcnt == 0) {
251 		err = nbd_teardown(dev);
252 	}
253 
254 	benaphore_unlock(&dev->ben);
255 
256 	free(cookie);
257 	return err;
258 }
259 
260 status_t nbd_control(cookie_t *cookie, uint32 op, void *data, size_t len) {
261 	PRINT((DP ">%s(%d, %ul, , %d)\n", __FUNCTION__, WHICH(cookie->dev), op, len));
262 	switch (op) {
263 	case B_GET_DEVICE_SIZE: /* this one is broken anyway... */
264 		if (data) {
265 			*(size_t *)data = (size_t)cookie->dev->size;
266 			return B_OK;
267 		}
268 		return EINVAL;
269 	case B_SET_DEVICE_SIZE: /* broken */
270 		return EINVAL;
271 	case B_SET_NONBLOCKING_IO:
272 		return EINVAL;
273 	case B_SET_BLOCKING_IO:
274 		return B_OK;
275 	case B_GET_READ_STATUS:
276 	case B_GET_WRITE_STATUS:
277 		if (data) {
278 			*(bool *)data = false;
279 			return B_OK;
280 		}
281 		return EINVAL;
282 	case B_GET_GEOMETRY:
283 	case B_GET_BIOS_GEOMETRY:
284 		if (data) {
285 			device_geometry *geom = (device_geometry *)data;
286 			geom->bytes_per_sector = 256;
287 			geom->sectors_per_track = 1;
288 			geom->cylinder_count = cookie->dev->size / 256;
289 			geom->head_count = 1;
290 			geom->device_type = B_DISK;
291 			geom->removable = false;
292 			geom->read_only = false; // XXX
293 			geom->write_once = false;
294 			return B_OK;
295 		}
296 		return EINVAL;
297 	case B_GET_MEDIA_STATUS:
298 		if (data) {
299 			*(status_t *)data = B_OK;
300 			return B_OK;
301 		}
302 		return EINVAL;
303 
304 	case B_EJECT_DEVICE:
305 	case B_LOAD_MEDIA:
306 		return ENOSYS;
307 	case B_FLUSH_DRIVE_CACHE: /* wait for request list to be empty ? */
308 	default:
309 		return ENOSYS;
310 	}
311 	return B_NOT_ALLOWED;
312 }
313 
314 status_t nbd_read(cookie_t *cookie, off_t position, void *data, size_t *numbytes) {
315 	PRINT((DP ">%s(%d, %Ld, , )\n", __FUNCTION__, WHICH(cookie->dev), position));
316 	*numbytes = 0;
317 	return B_NOT_ALLOWED;
318 }
319 
320 status_t nbd_write(cookie_t *cookie, off_t position, const void *data, size_t *numbytes) {
321 	PRINT((DP ">%s(%d, %Ld, , )\n", __FUNCTION__, WHICH(cookie->dev), position));
322 	(void)cookie; (void)position; (void)data; (void)numbytes;
323 	*numbytes = 0;
324 	return EIO;
325 }
326 
327 device_hooks nbd_hooks={
328 	(device_open_hook)nbd_open,
329 	nbd_close,
330 	(device_free_hook)nbd_free,
331 	(device_control_hook)nbd_control,
332 	(device_read_hook)nbd_read,
333 	(device_write_hook)nbd_write,
334 	NULL,
335 	NULL,
336 	NULL,
337 	NULL
338 };
339 
340 
341 #pragma mark ==== driver hooks ====
342 
343 static const char *nbd_name[MAX_NBDS+1] = {
344 	NULL
345 };
346 
347 status_t
348 init_hardware (void)
349 {
350 	PRINT((DP ">%s()\n", __FUNCTION__));
351 	return B_OK;
352 }
353 
354 status_t
355 init_driver (void)
356 {
357 	status_t err;
358 	int i, j;
359 	// XXX: load settings
360 	void *handle;
361 	PRINT((DP ">%s()\n", __FUNCTION__));
362 
363 	err = ksocket_init();
364 	if (err < B_OK)
365 		return err;
366 
367 	for (i = 0; i < MAX_NBDS; i++) {
368 		memset(nbd_devices[i].target, 0, 64);
369 		err = benaphore_init(&nbd_devices[i].ben, "nbd lock");
370 		if (err < B_OK)
371 			return err; // XXX
372 		nbd_devices[i].refcnt = 0;
373 		nbd_devices[i].req = 0LL; /* next ID for requests */
374 		nbd_devices[i].sock = -1;
375 		nbd_devices[i].postoffice = -1;
376 		nbd_devices[i].size = 0LL;
377 		nbd_devices[i].reqs = NULL;
378 		nbd_name[i] = malloc(DEVICE_NAME_MAX);
379 		if (nbd_name[i] == NULL)
380 			break;
381 		sprintf(nbd_name[i], DEVICE_FMT, i);
382 		/* XXX: default init */
383 		nbd_devices[i].server.sin_len = sizeof(struct sockaddr_in);
384 		nbd_devices[i].server.sin_family = AF_INET;
385 		nbd_devices[i].server.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
386 		nbd_devices[i].server.sin_port = htons(1337 + i);
387 	}
388 	nbd_name[i] = NULL;
389 
390 	handle = load_driver_settings(DRV);
391 	if (handle) {
392 		for (i = 0; i < MAX_NBDS; i++) {
393 			char keyname[10];
394 			char *v;
395 			sprintf(keyname, "nbd%d", i);
396 			v = get_driver_parameter(handle, keyname, NULL, NULL);
397 			/* should be "ip:port" */
398 			// XXX: test
399 			if (v || 1) {
400 				//strncpy(nbd_devices[i].target, v, 64);
401 				//XXX:TEST
402 				//strncpy(nbd_devices[i].target, "127.0.0.1:1337", 64);
403 				//XXX:parse it
404 				nbd_devices[i].server.sin_len = sizeof(struct sockaddr_in);
405 				nbd_devices[i].server.sin_family = AF_INET;
406 				nbd_devices[i].server.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
407 				nbd_devices[i].server.sin_port = htons(1337 + i);
408 			}
409 		}
410 		/*should parse as a tree:
411 		  settings = get_driver_settings(handle);
412 		  for (i = 0; i < settings->parameter_count; i++) {
413 
414 		  }
415 		*/
416 
417 		unload_driver_settings(handle);
418 	}
419 
420 	return B_OK;
421 }
422 
423 void
424 uninit_driver (void)
425 {
426 	status_t err;
427 	int i;
428 	PRINT((DP ">%s()\n", __FUNCTION__));
429 	for (i = 0; i < MAX_NBDS; i++) {
430 		free(nbd_name[i]);
431 		err = benaphore_destroy(&nbd_devices[i].ben);
432 	}
433 	err = ksocket_cleanup();
434 	/* HACK */
435 	if (gDelayUnload)
436 		snooze(BONE_TEARDOWN_DELAY);
437 }
438 
439 const char**
440 publish_devices()
441 {
442 	PRINT((DP ">%s()\n", __FUNCTION__));
443 	return nbd_name;
444 }
445 
446 device_hooks*
447 find_device(const char* name)
448 {
449 	PRINT((DP ">%s(%s)\n", __FUNCTION__, name));
450 	return &nbd_hooks;
451 }
452 
453 struct nbd_device*
454 nbd_find_device(const char* name)
455 {
456 	int i;
457 	PRINT((DP ">%s(%s)\n", __FUNCTION__, name));
458 	for (i = 0; i < MAX_NBDS; i++) {
459 		if (!strcmp(nbd_name[i], name))
460 			return &nbd_devices[i];
461 	}
462 	return NULL;
463 }
464