xref: /minix/minix/lib/libbdev/bdev.c (revision e3b78ef1)
1 /* libbdev - block device interfacing library, by D.C. van Moolenbroek */
2 
3 #include <minix/drivers.h>
4 #include <minix/bdev.h>
5 #include <minix/ioctl.h>
6 #include <assert.h>
7 
8 #include "const.h"
9 #include "type.h"
10 #include "proto.h"
11 
12 void bdev_driver(dev_t dev, char *label)
13 {
14 /* Associate a driver with the given (major) device, using its endpoint.
15  * File system usage note: typically called from mount and newdriver.
16  */
17   static int first = TRUE;
18 
19   if (first) {
20 	/* Initialize the driver endpoint array. */
21 	bdev_driver_init();
22 
23 	first = FALSE;
24   }
25 
26   bdev_update(dev, label);
27 }
28 
29 static int bdev_retry(int *driver_tries, int *transfer_tries, int *result)
30 {
31 /* Return TRUE iff the call result implies that we should retry the operation.
32  */
33 
34   switch (*result) {
35   case ERESTART:
36 	/* We get this error internally if the driver has restarted and the
37 	 * current operation may now go through. Check the retry count for
38 	 * driver restarts first, as we don't want to keep trying forever.
39 	 */
40 	if (++*driver_tries < DRIVER_TRIES)
41 		return TRUE;
42 
43 	*result = EDEADSRCDST;
44 
45 	break;
46 
47   case EIO:
48 	/* The 'transfer_tries' pointer is non-NULL if this was a transfer
49 	 * request. If we get back an I/O failure, keep retrying the request
50 	 * until we hit the transfer retry limit.
51 	 */
52 	if (transfer_tries != NULL && ++*transfer_tries < TRANSFER_TRIES)
53 		return TRUE;
54 
55 	break;
56   }
57 
58   return FALSE;
59 }
60 
61 static int bdev_opcl(int req, dev_t dev, int access)
62 {
63 /* Open or close the given minor device.
64  */
65   message m;
66   int r, driver_tries = 0;
67 
68   do {
69 	memset(&m, 0, sizeof(m));
70 	m.m_type = req;
71 	m.m_lbdev_lblockdriver_msg.minor = minor(dev);
72 	m.m_lbdev_lblockdriver_msg.access = access;
73 
74 	r = bdev_sendrec(dev, &m);
75   } while (bdev_retry(&driver_tries, NULL, &r));
76 
77   return r;
78 }
79 
80 int bdev_open(dev_t dev, int access)
81 {
82 /* Open the given minor device.
83  * File system usage note: typically called from mount, after bdev_driver.
84  */
85   int r;
86 
87   r = bdev_opcl(BDEV_OPEN, dev, access);
88 
89   if (r == OK)
90 	bdev_minor_add(dev, access);
91 
92   return r;
93 }
94 
95 int bdev_close(dev_t dev)
96 {
97 /* Close the given minor device.
98  * File system usage note: typically called from unmount.
99  */
100   int r;
101 
102   bdev_flush_asyn(dev);
103 
104   r = bdev_opcl(BDEV_CLOSE, dev, 0);
105 
106   if (r == OK)
107 	bdev_minor_del(dev);
108 
109   return r;
110 }
111 
112 static int bdev_rdwt_setup(int req, dev_t dev, u64_t pos, char *buf,
113   size_t count, int flags, message *m)
114 {
115 /* Set up a single-buffer read/write request.
116  */
117   endpoint_t endpt;
118   cp_grant_id_t grant;
119   int access;
120 
121   assert((ssize_t) count >= 0);
122 
123   if ((endpt = bdev_driver_get(dev)) == NONE)
124 	return EDEADSRCDST;
125 
126   access = (req == BDEV_READ) ? CPF_WRITE : CPF_READ;
127 
128   grant = cpf_grant_direct(endpt, (vir_bytes) buf, count, access);
129 
130   if (!GRANT_VALID(grant)) {
131 	printf("bdev: unable to allocate grant!\n");
132 	return EINVAL;
133   }
134 
135   memset(m, 0, sizeof(*m));
136   m->m_type = req;
137   m->m_lbdev_lblockdriver_msg.minor = minor(dev);
138   m->m_lbdev_lblockdriver_msg.pos = pos;
139   m->m_lbdev_lblockdriver_msg.count = count;
140   m->m_lbdev_lblockdriver_msg.grant = grant;
141   m->m_lbdev_lblockdriver_msg.flags = flags;
142 
143   return OK;
144 }
145 
146 static void bdev_rdwt_cleanup(const message *m)
147 {
148 /* Clean up a single-buffer read/write request.
149  */
150 
151   cpf_revoke(m->m_lbdev_lblockdriver_msg.grant);
152 }
153 
154 static ssize_t bdev_rdwt(int req, dev_t dev, u64_t pos, char *buf,
155   size_t count, int flags)
156 {
157 /* Perform a synchronous read or write call using a single buffer.
158  */
159   message m;
160   int r, driver_tries = 0, transfer_tries = 0;
161 
162   do {
163 	if ((r = bdev_rdwt_setup(req, dev, pos, buf, count, flags, &m)) != OK)
164 		break;
165 
166 	r = bdev_sendrec(dev, &m);
167 
168 	bdev_rdwt_cleanup(&m);
169   } while (bdev_retry(&driver_tries, &transfer_tries, &r));
170 
171   return r;
172 }
173 
174 static int bdev_vrdwt_setup(int req, dev_t dev, u64_t pos, iovec_t *vec,
175   int count, int flags, message *m, iovec_s_t *gvec)
176 {
177 /* Set up a vectored read/write request.
178  */
179   ssize_t size;
180   endpoint_t endpt;
181   cp_grant_id_t grant;
182   int i, access;
183 
184   assert(count <= NR_IOREQS);
185 
186   if ((endpt = bdev_driver_get(dev)) == NONE)
187 	return EDEADSRCDST;
188 
189   access = (req == BDEV_GATHER) ? CPF_WRITE : CPF_READ;
190   size = 0;
191 
192   for (i = 0; i < count; i++) {
193 	grant = cpf_grant_direct(endpt, vec[i].iov_addr, vec[i].iov_size,
194 		access);
195 
196 	if (!GRANT_VALID(grant)) {
197 		printf("bdev: unable to allocate grant!\n");
198 
199 		for (i--; i >= 0; i--)
200 			cpf_revoke(gvec[i].iov_grant);
201 
202 		return EINVAL;
203 	}
204 
205 	gvec[i].iov_grant = grant;
206 	gvec[i].iov_size = vec[i].iov_size;
207 
208 	assert(vec[i].iov_size > 0);
209 	assert((ssize_t) (size + vec[i].iov_size) > size);
210 
211 	size += vec[i].iov_size;
212   }
213 
214   grant = cpf_grant_direct(endpt, (vir_bytes) gvec, sizeof(gvec[0]) * count,
215 	CPF_READ);
216 
217   if (!GRANT_VALID(grant)) {
218 	printf("bdev: unable to allocate grant!\n");
219 
220 	for (i = count - 1; i >= 0; i--)
221 		cpf_revoke(gvec[i].iov_grant);
222 
223 	return EINVAL;
224   }
225 
226   memset(m, 0, sizeof(*m));
227   m->m_type = req;
228   m->m_lbdev_lblockdriver_msg.minor = minor(dev);
229   m->m_lbdev_lblockdriver_msg.pos = pos;
230   m->m_lbdev_lblockdriver_msg.count = count;
231   m->m_lbdev_lblockdriver_msg.grant = grant;
232   m->m_lbdev_lblockdriver_msg.flags = flags;
233 
234   return OK;
235 }
236 
237 static void bdev_vrdwt_cleanup(const message *m, iovec_s_t *gvec)
238 {
239 /* Clean up a vectored read/write request.
240  */
241   cp_grant_id_t grant;
242   int i;
243 
244   grant = m->m_lbdev_lblockdriver_msg.grant;
245 
246   cpf_revoke(grant);
247 
248   for (i = m->m_lbdev_lblockdriver_msg.count - 1; i >= 0; i--)
249 	cpf_revoke(gvec[i].iov_grant);
250 }
251 
252 static ssize_t bdev_vrdwt(int req, dev_t dev, u64_t pos, iovec_t *vec,
253   int count, int flags)
254 {
255 /* Perform a synchronous read or write call using a vector of buffers.
256  */
257   iovec_s_t gvec[NR_IOREQS];
258   message m;
259   int r, driver_tries = 0, transfer_tries = 0;
260 
261   do {
262 	if ((r = bdev_vrdwt_setup(req, dev, pos, vec, count, flags, &m,
263 			gvec)) != OK)
264 		break;
265 
266 	r = bdev_sendrec(dev, &m);
267 
268 	bdev_vrdwt_cleanup(&m, gvec);
269   } while (bdev_retry(&driver_tries, &transfer_tries, &r));
270 
271   return r;
272 }
273 
274 ssize_t bdev_read(dev_t dev, u64_t pos, char *buf, size_t count, int flags)
275 {
276 /* Perform a synchronous read call into a single buffer.
277  */
278 
279   return bdev_rdwt(BDEV_READ, dev, pos, buf, count, flags);
280 }
281 
282 ssize_t bdev_write(dev_t dev, u64_t pos, char *buf, size_t count, int flags)
283 {
284 /* Perform a synchronous write call from a single buffer.
285  */
286 
287   return bdev_rdwt(BDEV_WRITE, dev, pos, buf, count, flags);
288 }
289 
290 ssize_t bdev_gather(dev_t dev, u64_t pos, iovec_t *vec, int count, int flags)
291 {
292 /* Perform a synchronous read call into a vector of buffers.
293  */
294 
295   return bdev_vrdwt(BDEV_GATHER, dev, pos, vec, count, flags);
296 }
297 
298 ssize_t bdev_scatter(dev_t dev, u64_t pos, iovec_t *vec, int count, int flags)
299 {
300 /* Perform a synchronous write call from a vector of buffers.
301  */
302 
303   return bdev_vrdwt(BDEV_SCATTER, dev, pos, vec, count, flags);
304 }
305 
306 static int bdev_ioctl_setup(dev_t dev, unsigned long request, void *buf,
307   endpoint_t user_endpt, message *m)
308 {
309 /* Set up an I/O control request.
310  */
311   endpoint_t endpt;
312   size_t size;
313   cp_grant_id_t grant;
314   int access;
315 
316   if ((endpt = bdev_driver_get(dev)) == NONE)
317 	return EDEADSRCDST;
318 
319   if (_MINIX_IOCTL_BIG(request))
320 	size = _MINIX_IOCTL_SIZE_BIG(request);
321   else
322 	size = _MINIX_IOCTL_SIZE(request);
323 
324   access = 0;
325   if (_MINIX_IOCTL_IOR(request)) access |= CPF_WRITE;
326   if (_MINIX_IOCTL_IOW(request)) access |= CPF_READ;
327 
328   /* The size may be 0, in which case 'buf' need not be a valid pointer. */
329   grant = cpf_grant_direct(endpt, (vir_bytes) buf, size, access);
330 
331   if (!GRANT_VALID(grant)) {
332 	printf("bdev: unable to allocate grant!\n");
333 	return EINVAL;
334   }
335 
336   memset(m, 0, sizeof(*m));
337   m->m_type = BDEV_IOCTL;
338   m->m_lbdev_lblockdriver_msg.minor = minor(dev);
339   m->m_lbdev_lblockdriver_msg.request = request;
340   m->m_lbdev_lblockdriver_msg.grant = grant;
341   m->m_lbdev_lblockdriver_msg.user = user_endpt;
342 
343   return OK;
344 }
345 
346 static void bdev_ioctl_cleanup(const message *m)
347 {
348 /* Clean up an I/O control request.
349  */
350 
351   cpf_revoke(m->m_lbdev_lblockdriver_msg.grant);
352 }
353 
354 int bdev_ioctl(dev_t dev, unsigned long request, void *buf,
355   endpoint_t user_endpt)
356 {
357 /* Perform a synchronous I/O control request.
358  */
359   message m;
360   int r, driver_tries = 0;
361 
362   do {
363 	if ((r = bdev_ioctl_setup(dev, request, buf, user_endpt, &m)) != OK)
364 		break;
365 
366 	r = bdev_sendrec(dev, &m);
367 
368 	bdev_ioctl_cleanup(&m);
369   } while (bdev_retry(&driver_tries, NULL, &r));
370 
371   return r;
372 }
373 
374 void bdev_flush_asyn(dev_t dev)
375 {
376 /* Flush all ongoing asynchronous requests to the given minor device. This
377  * involves blocking until all I/O for it has completed.
378  * File system usage note: typically called from flush.
379  */
380   bdev_call_t *call;
381 
382   while ((call = bdev_call_find(dev)) != NULL)
383 	(void) bdev_wait_asyn(call->id);
384 }
385 
386 static bdev_id_t bdev_rdwt_asyn(int req, dev_t dev, u64_t pos, char *buf,
387 	size_t count, int flags, bdev_callback_t callback, bdev_param_t param)
388 {
389 /* Perform an asynchronous read or write call using a single buffer.
390  */
391   bdev_call_t *call;
392   int r;
393 
394   if ((call = bdev_call_alloc(1)) == NULL)
395 	return ENOMEM;
396 
397   if ((r = bdev_rdwt_setup(req, dev, pos, buf, count, flags, &call->msg)) !=
398 		OK) {
399 	bdev_call_free(call);
400 
401 	return r;
402   }
403 
404   if ((r = bdev_senda(dev, &call->msg, call->id)) != OK) {
405 	bdev_rdwt_cleanup(&call->msg);
406 
407 	bdev_call_free(call);
408 
409 	return r;
410   }
411 
412   call->dev = dev;
413   call->callback = callback;
414   call->param = param;
415   call->driver_tries = 0;
416   call->transfer_tries = 0;
417   call->vec[0].iov_addr = (vir_bytes) buf;
418   call->vec[0].iov_size = count;
419 
420   return call->id;
421 }
422 
423 static bdev_id_t bdev_vrdwt_asyn(int req, dev_t dev, u64_t pos, iovec_t *vec,
424 	int count, int flags, bdev_callback_t callback, bdev_param_t param)
425 {
426 /* Perform an asynchronous read or write call using a vector of buffers.
427  */
428   bdev_call_t *call;
429   int r;
430 
431   if ((call = bdev_call_alloc(count)) == NULL)
432 	return ENOMEM;
433 
434   if ((r = bdev_vrdwt_setup(req, dev, pos, vec, count, flags, &call->msg,
435 		call->gvec)) != OK) {
436 	bdev_call_free(call);
437 
438 	return r;
439   }
440 
441   if ((r = bdev_senda(dev, &call->msg, call->id)) != OK) {
442 	bdev_vrdwt_cleanup(&call->msg, call->gvec);
443 
444 	bdev_call_free(call);
445 
446 	return r;
447   }
448 
449   call->dev = dev;
450   call->callback = callback;
451   call->param = param;
452   call->driver_tries = 0;
453   call->transfer_tries = 0;
454   memcpy(call->vec, vec, sizeof(vec[0]) * count);
455 
456   return call->id;
457 }
458 
459 bdev_id_t bdev_read_asyn(dev_t dev, u64_t pos, char *buf, size_t count,
460 	int flags, bdev_callback_t callback, bdev_param_t param)
461 {
462 /* Perform an asynchronous read call into a single buffer.
463  */
464 
465   return bdev_rdwt_asyn(BDEV_READ, dev, pos, buf, count, flags, callback,
466 	param);
467 }
468 
469 bdev_id_t bdev_write_asyn(dev_t dev, u64_t pos, char *buf, size_t count,
470 	int flags, bdev_callback_t callback, bdev_param_t param)
471 {
472 /* Perform an asynchronous write call from a single buffer.
473  */
474 
475   return bdev_rdwt_asyn(BDEV_WRITE, dev, pos, buf, count, flags, callback,
476 	param);
477 }
478 
479 bdev_id_t bdev_gather_asyn(dev_t dev, u64_t pos, iovec_t *vec, int count,
480 	int flags, bdev_callback_t callback, bdev_param_t param)
481 {
482 /* Perform an asynchronous read call into a vector of buffers.
483  */
484 
485   return bdev_vrdwt_asyn(BDEV_GATHER, dev, pos, vec, count, flags, callback,
486 	param);
487 }
488 
489 bdev_id_t bdev_scatter_asyn(dev_t dev, u64_t pos, iovec_t *vec, int count,
490 	int flags, bdev_callback_t callback, bdev_param_t param)
491 {
492 /* Perform an asynchronous write call into a vector of buffers.
493  */
494 
495   return bdev_vrdwt_asyn(BDEV_SCATTER, dev, pos, vec, count, flags, callback,
496 	param);
497 }
498 
499 bdev_id_t bdev_ioctl_asyn(dev_t dev, unsigned long request, void *buf,
500 	endpoint_t user_endpt, bdev_callback_t callback, bdev_param_t param)
501 {
502 /* Perform an asynchronous I/O control request.
503  */
504   bdev_call_t *call;
505   int r;
506 
507   if ((call = bdev_call_alloc(1)) == NULL)
508 	return ENOMEM;
509 
510   if ((r = bdev_ioctl_setup(dev, request, buf, user_endpt,
511 		&call->msg)) != OK) {
512 	bdev_call_free(call);
513 
514 	return r;
515   }
516 
517   if ((r = bdev_senda(dev, &call->msg, call->id)) != OK) {
518 	bdev_ioctl_cleanup(&call->msg);
519 
520 	bdev_call_free(call);
521 
522 	return r;
523   }
524 
525   call->dev = dev;
526   call->callback = callback;
527   call->param = param;
528   call->driver_tries = 0;
529   call->vec[0].iov_addr = (vir_bytes) buf;
530 
531   return call->id;
532 }
533 
534 void bdev_callback_asyn(bdev_call_t *call, int result)
535 {
536 /* Perform the callback for an asynchronous request, with the given result.
537  * Clean up the call structure afterwards.
538  */
539 
540   /* If this was a transfer request and the result is EIO, we may want to retry
541    * the request first.
542    */
543   switch (call->msg.m_type) {
544   case BDEV_READ:
545   case BDEV_WRITE:
546   case BDEV_GATHER:
547   case BDEV_SCATTER:
548 	if (result == EIO && ++call->transfer_tries < TRANSFER_TRIES) {
549 		result = bdev_senda(call->dev, &call->msg, call->id);
550 
551 		if (result == OK)
552 			return;
553 	}
554   }
555 
556   /* Clean up. */
557   switch (call->msg.m_type) {
558   case BDEV_READ:
559   case BDEV_WRITE:
560 	bdev_rdwt_cleanup(&call->msg);
561 
562 	break;
563 
564   case BDEV_GATHER:
565   case BDEV_SCATTER:
566 	bdev_vrdwt_cleanup(&call->msg, call->gvec);
567 
568 	break;
569 
570   case BDEV_IOCTL:
571 	bdev_ioctl_cleanup(&call->msg);
572 
573 	break;
574 
575   default:
576 	assert(0);
577   }
578 
579   /* Call the callback function. */
580   /* FIXME: we assume all reasonable ssize_t values can be stored in an int. */
581   call->callback(call->dev, call->id, call->param, result);
582 
583   /* Free up the call structure. */
584   bdev_call_free(call);
585 }
586 
587 int bdev_restart_asyn(bdev_call_t *call)
588 {
589 /* The driver for the given call has restarted, and may now have a new
590  * endpoint. Recreate and resend the request for the given call.
591  */
592   int type, r = OK;
593 
594   /* Update and check the retry limit for driver restarts first. */
595   if (++call->driver_tries >= DRIVER_TRIES)
596 	return EDEADSRCDST;
597 
598   /* Recreate all grants for the new endpoint. */
599   type = call->msg.m_type;
600 
601   switch (type) {
602   case BDEV_READ:
603   case BDEV_WRITE:
604 	bdev_rdwt_cleanup(&call->msg);
605 
606 	r = bdev_rdwt_setup(type, call->dev,
607 		call->msg.m_lbdev_lblockdriver_msg.pos,
608 		(char *) call->vec[0].iov_addr, call->msg.m_lbdev_lblockdriver_msg.count,
609 		call->msg.m_lbdev_lblockdriver_msg.flags, &call->msg);
610 
611 	break;
612 
613   case BDEV_GATHER:
614   case BDEV_SCATTER:
615 	bdev_vrdwt_cleanup(&call->msg, call->gvec);
616 
617 	r = bdev_vrdwt_setup(type, call->dev,
618 		call->msg.m_lbdev_lblockdriver_msg.pos,
619 		call->vec, call->msg.m_lbdev_lblockdriver_msg.count, call->msg.m_lbdev_lblockdriver_msg.flags,
620 		&call->msg, call->gvec);
621 
622 	break;
623 
624   case BDEV_IOCTL:
625 	bdev_ioctl_cleanup(&call->msg);
626 
627 	r = bdev_ioctl_setup(call->dev, call->msg.m_lbdev_lblockdriver_msg.request,
628 		(char *) call->vec[0].iov_addr, call->msg.m_lbdev_lblockdriver_msg.user,
629 		&call->msg);
630 
631 	break;
632 
633   default:
634 	assert(0);
635   }
636 
637   if (r != OK)
638 	return r;
639 
640   /* Try to resend the request. */
641   return bdev_senda(call->dev, &call->msg, call->id);
642 }
643