xref: /minix/minix/lib/libbdev/bdev.c (revision 7f5f010b)
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, int 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, int request, void *buf, endpoint_t user_endpt)
355 {
356 /* Perform a synchronous I/O control request.
357  */
358   message m;
359   int r, driver_tries = 0;
360 
361   do {
362 	if ((r = bdev_ioctl_setup(dev, request, buf, user_endpt, &m)) != OK)
363 		break;
364 
365 	r = bdev_sendrec(dev, &m);
366 
367 	bdev_ioctl_cleanup(&m);
368   } while (bdev_retry(&driver_tries, NULL, &r));
369 
370   return r;
371 }
372 
373 void bdev_flush_asyn(dev_t dev)
374 {
375 /* Flush all ongoing asynchronous requests to the given minor device. This
376  * involves blocking until all I/O for it has completed.
377  * File system usage note: typically called from flush.
378  */
379   bdev_call_t *call;
380 
381   while ((call = bdev_call_find(dev)) != NULL)
382 	(void) bdev_wait_asyn(call->id);
383 }
384 
385 static bdev_id_t bdev_rdwt_asyn(int req, dev_t dev, u64_t pos, char *buf,
386 	size_t count, int flags, bdev_callback_t callback, bdev_param_t param)
387 {
388 /* Perform an asynchronous read or write call using a single buffer.
389  */
390   bdev_call_t *call;
391   int r;
392 
393   if ((call = bdev_call_alloc(1)) == NULL)
394 	return ENOMEM;
395 
396   if ((r = bdev_rdwt_setup(req, dev, pos, buf, count, flags, &call->msg)) !=
397 		OK) {
398 	bdev_call_free(call);
399 
400 	return r;
401   }
402 
403   if ((r = bdev_senda(dev, &call->msg, call->id)) != OK) {
404 	bdev_rdwt_cleanup(&call->msg);
405 
406 	bdev_call_free(call);
407 
408 	return r;
409   }
410 
411   call->dev = dev;
412   call->callback = callback;
413   call->param = param;
414   call->driver_tries = 0;
415   call->transfer_tries = 0;
416   call->vec[0].iov_addr = (vir_bytes) buf;
417   call->vec[0].iov_size = count;
418 
419   return call->id;
420 }
421 
422 static bdev_id_t bdev_vrdwt_asyn(int req, dev_t dev, u64_t pos, iovec_t *vec,
423 	int count, int flags, bdev_callback_t callback, bdev_param_t param)
424 {
425 /* Perform an asynchronous read or write call using a vector of buffers.
426  */
427   bdev_call_t *call;
428   int r;
429 
430   if ((call = bdev_call_alloc(count)) == NULL)
431 	return ENOMEM;
432 
433   if ((r = bdev_vrdwt_setup(req, dev, pos, vec, count, flags, &call->msg,
434 		call->gvec)) != OK) {
435 	bdev_call_free(call);
436 
437 	return r;
438   }
439 
440   if ((r = bdev_senda(dev, &call->msg, call->id)) != OK) {
441 	bdev_vrdwt_cleanup(&call->msg, call->gvec);
442 
443 	bdev_call_free(call);
444 
445 	return r;
446   }
447 
448   call->dev = dev;
449   call->callback = callback;
450   call->param = param;
451   call->driver_tries = 0;
452   call->transfer_tries = 0;
453   memcpy(call->vec, vec, sizeof(vec[0]) * count);
454 
455   return call->id;
456 }
457 
458 bdev_id_t bdev_read_asyn(dev_t dev, u64_t pos, char *buf, size_t count,
459 	int flags, bdev_callback_t callback, bdev_param_t param)
460 {
461 /* Perform an asynchronous read call into a single buffer.
462  */
463 
464   return bdev_rdwt_asyn(BDEV_READ, dev, pos, buf, count, flags, callback,
465 	param);
466 }
467 
468 bdev_id_t bdev_write_asyn(dev_t dev, u64_t pos, char *buf, size_t count,
469 	int flags, bdev_callback_t callback, bdev_param_t param)
470 {
471 /* Perform an asynchronous write call from a single buffer.
472  */
473 
474   return bdev_rdwt_asyn(BDEV_WRITE, dev, pos, buf, count, flags, callback,
475 	param);
476 }
477 
478 bdev_id_t bdev_gather_asyn(dev_t dev, u64_t pos, iovec_t *vec, int count,
479 	int flags, bdev_callback_t callback, bdev_param_t param)
480 {
481 /* Perform an asynchronous read call into a vector of buffers.
482  */
483 
484   return bdev_vrdwt_asyn(BDEV_GATHER, dev, pos, vec, count, flags, callback,
485 	param);
486 }
487 
488 bdev_id_t bdev_scatter_asyn(dev_t dev, u64_t pos, iovec_t *vec, int count,
489 	int flags, bdev_callback_t callback, bdev_param_t param)
490 {
491 /* Perform an asynchronous write call into a vector of buffers.
492  */
493 
494   return bdev_vrdwt_asyn(BDEV_SCATTER, dev, pos, vec, count, flags, callback,
495 	param);
496 }
497 
498 bdev_id_t bdev_ioctl_asyn(dev_t dev, int request, void *buf,
499 	endpoint_t user_endpt, bdev_callback_t callback, bdev_param_t param)
500 {
501 /* Perform an asynchronous I/O control request.
502  */
503   bdev_call_t *call;
504   int r;
505 
506   if ((call = bdev_call_alloc(1)) == NULL)
507 	return ENOMEM;
508 
509   if ((r = bdev_ioctl_setup(dev, request, buf, user_endpt,
510 		&call->msg)) != OK) {
511 	bdev_call_free(call);
512 
513 	return r;
514   }
515 
516   if ((r = bdev_senda(dev, &call->msg, call->id)) != OK) {
517 	bdev_ioctl_cleanup(&call->msg);
518 
519 	bdev_call_free(call);
520 
521 	return r;
522   }
523 
524   call->dev = dev;
525   call->callback = callback;
526   call->param = param;
527   call->driver_tries = 0;
528   call->vec[0].iov_addr = (vir_bytes) buf;
529 
530   return call->id;
531 }
532 
533 void bdev_callback_asyn(bdev_call_t *call, int result)
534 {
535 /* Perform the callback for an asynchronous request, with the given result.
536  * Clean up the call structure afterwards.
537  */
538 
539   /* If this was a transfer request and the result is EIO, we may want to retry
540    * the request first.
541    */
542   switch (call->msg.m_type) {
543   case BDEV_READ:
544   case BDEV_WRITE:
545   case BDEV_GATHER:
546   case BDEV_SCATTER:
547 	if (result == EIO && ++call->transfer_tries < TRANSFER_TRIES) {
548 		result = bdev_senda(call->dev, &call->msg, call->id);
549 
550 		if (result == OK)
551 			return;
552 	}
553   }
554 
555   /* Clean up. */
556   switch (call->msg.m_type) {
557   case BDEV_READ:
558   case BDEV_WRITE:
559 	bdev_rdwt_cleanup(&call->msg);
560 
561 	break;
562 
563   case BDEV_GATHER:
564   case BDEV_SCATTER:
565 	bdev_vrdwt_cleanup(&call->msg, call->gvec);
566 
567 	break;
568 
569   case BDEV_IOCTL:
570 	bdev_ioctl_cleanup(&call->msg);
571 
572 	break;
573 
574   default:
575 	assert(0);
576   }
577 
578   /* Call the callback function. */
579   /* FIXME: we assume all reasonable ssize_t values can be stored in an int. */
580   call->callback(call->dev, call->id, call->param, result);
581 
582   /* Free up the call structure. */
583   bdev_call_free(call);
584 }
585 
586 int bdev_restart_asyn(bdev_call_t *call)
587 {
588 /* The driver for the given call has restarted, and may now have a new
589  * endpoint. Recreate and resend the request for the given call.
590  */
591   int type, r = OK;
592 
593   /* Update and check the retry limit for driver restarts first. */
594   if (++call->driver_tries >= DRIVER_TRIES)
595 	return EDEADSRCDST;
596 
597   /* Recreate all grants for the new endpoint. */
598   type = call->msg.m_type;
599 
600   switch (type) {
601   case BDEV_READ:
602   case BDEV_WRITE:
603 	bdev_rdwt_cleanup(&call->msg);
604 
605 	r = bdev_rdwt_setup(type, call->dev,
606 		call->msg.m_lbdev_lblockdriver_msg.pos,
607 		(char *) call->vec[0].iov_addr, call->msg.m_lbdev_lblockdriver_msg.count,
608 		call->msg.m_lbdev_lblockdriver_msg.flags, &call->msg);
609 
610 	break;
611 
612   case BDEV_GATHER:
613   case BDEV_SCATTER:
614 	bdev_vrdwt_cleanup(&call->msg, call->gvec);
615 
616 	r = bdev_vrdwt_setup(type, call->dev,
617 		call->msg.m_lbdev_lblockdriver_msg.pos,
618 		call->vec, call->msg.m_lbdev_lblockdriver_msg.count, call->msg.m_lbdev_lblockdriver_msg.flags,
619 		&call->msg, call->gvec);
620 
621 	break;
622 
623   case BDEV_IOCTL:
624 	bdev_ioctl_cleanup(&call->msg);
625 
626 	r = bdev_ioctl_setup(call->dev, call->msg.m_lbdev_lblockdriver_msg.request,
627 		(char *) call->vec[0].iov_addr, call->msg.m_lbdev_lblockdriver_msg.user,
628 		&call->msg);
629 
630 	break;
631 
632   default:
633 	assert(0);
634   }
635 
636   if (r != OK)
637 	return r;
638 
639   /* Try to resend the request. */
640   return bdev_senda(call->dev, &call->msg, call->id);
641 }
642