worldstone: add -s for statistical profiling
[minix.git] / lib / libbdev / bdev.c
blobfb4dfc239916ee8f6158d77efffdba626d9c78e9
1 /* libbdev - block device interfacing library, by D.C. van Moolenbroek */
3 #include <minix/drivers.h>
4 #include <minix/bdev.h>
5 #include <minix/ioctl.h>
6 #include <assert.h>
8 #include "const.h"
9 #include "type.h"
10 #include "proto.h"
12 void bdev_driver(dev_t dev, char *label)
14 /* Associate a driver with the given (major) device, using its endpoint.
15 * File system usage note: typically called from mount and newdriver.
17 static int first = TRUE;
19 if (first) {
20 /* Initialize the driver endpoint array. */
21 bdev_driver_init();
23 first = FALSE;
26 bdev_update(dev, label);
29 static int bdev_retry(int *driver_tries, int *transfer_tries, int *result)
31 /* Return TRUE iff the call result implies that we should retry the operation.
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.
40 if (++*driver_tries < DRIVER_TRIES)
41 return TRUE;
43 *result = EDEADSRCDST;
45 break;
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.
52 if (transfer_tries != NULL && ++*transfer_tries < TRANSFER_TRIES)
53 return TRUE;
55 break;
58 return FALSE;
61 static int bdev_opcl(int req, dev_t dev, int access)
63 /* Open or close the given minor device.
65 message m;
66 int r, driver_tries = 0;
68 do {
69 memset(&m, 0, sizeof(m));
70 m.m_type = req;
71 m.BDEV_MINOR = minor(dev);
72 m.BDEV_ACCESS = access;
74 r = bdev_sendrec(dev, &m);
75 } while (bdev_retry(&driver_tries, NULL, &r));
77 return r;
80 int bdev_open(dev_t dev, int access)
82 /* Open the given minor device.
83 * File system usage note: typically called from mount, after bdev_driver.
85 int r;
87 r = bdev_opcl(BDEV_OPEN, dev, access);
89 if (r == OK)
90 bdev_minor_add(dev, access);
92 return r;
95 int bdev_close(dev_t dev)
97 /* Close the given minor device.
98 * File system usage note: typically called from unmount.
100 int r;
102 bdev_flush_asyn(dev);
104 r = bdev_opcl(BDEV_CLOSE, dev, 0);
106 if (r == OK)
107 bdev_minor_del(dev);
109 return r;
112 static int bdev_rdwt_setup(int req, dev_t dev, u64_t pos, char *buf,
113 size_t count, int flags, message *m)
115 /* Set up a single-buffer read/write request.
117 endpoint_t endpt;
118 cp_grant_id_t grant;
119 int access;
121 assert((ssize_t) count >= 0);
123 if ((endpt = bdev_driver_get(dev)) == NONE)
124 return EDEADSRCDST;
126 access = (req == BDEV_READ) ? CPF_WRITE : CPF_READ;
128 grant = cpf_grant_direct(endpt, (vir_bytes) buf, count, access);
130 if (!GRANT_VALID(grant)) {
131 printf("bdev: unable to allocate grant!\n");
132 return EINVAL;
135 memset(m, 0, sizeof(*m));
136 m->m_type = req;
137 m->BDEV_MINOR = minor(dev);
138 m->BDEV_POS_LO = ex64lo(pos);
139 m->BDEV_POS_HI = ex64hi(pos);
140 m->BDEV_COUNT = count;
141 m->BDEV_GRANT = grant;
142 m->BDEV_FLAGS = flags;
144 return OK;
147 static void bdev_rdwt_cleanup(const message *m)
149 /* Clean up a single-buffer read/write request.
152 cpf_revoke(m->BDEV_GRANT);
155 static ssize_t bdev_rdwt(int req, dev_t dev, u64_t pos, char *buf,
156 size_t count, int flags)
158 /* Perform a synchronous read or write call using a single buffer.
160 message m;
161 int r, driver_tries = 0, transfer_tries = 0;
163 do {
164 if ((r = bdev_rdwt_setup(req, dev, pos, buf, count, flags, &m)) != OK)
165 break;
167 r = bdev_sendrec(dev, &m);
169 bdev_rdwt_cleanup(&m);
170 } while (bdev_retry(&driver_tries, &transfer_tries, &r));
172 return r;
175 static int bdev_vrdwt_setup(int req, dev_t dev, u64_t pos, iovec_t *vec,
176 int count, int flags, message *m, iovec_s_t *gvec)
178 /* Set up a vectored read/write request.
180 ssize_t size;
181 endpoint_t endpt;
182 cp_grant_id_t grant;
183 int i, access;
185 assert(count <= NR_IOREQS);
187 if ((endpt = bdev_driver_get(dev)) == NONE)
188 return EDEADSRCDST;
190 access = (req == BDEV_GATHER) ? CPF_WRITE : CPF_READ;
191 size = 0;
193 for (i = 0; i < count; i++) {
194 grant = cpf_grant_direct(endpt, vec[i].iov_addr, vec[i].iov_size,
195 access);
197 if (!GRANT_VALID(grant)) {
198 printf("bdev: unable to allocate grant!\n");
200 for (i--; i >= 0; i--)
201 cpf_revoke(gvec[i].iov_grant);
203 return EINVAL;
206 gvec[i].iov_grant = grant;
207 gvec[i].iov_size = vec[i].iov_size;
209 assert((ssize_t) (size + vec[i].iov_size) > size);
211 size += vec[i].iov_size;
214 grant = cpf_grant_direct(endpt, (vir_bytes) gvec, sizeof(gvec[0]) * count,
215 CPF_READ);
217 if (!GRANT_VALID(grant)) {
218 printf("bdev: unable to allocate grant!\n");
220 for (i = count - 1; i >= 0; i--)
221 cpf_revoke(gvec[i].iov_grant);
223 return EINVAL;
226 memset(m, 0, sizeof(*m));
227 m->m_type = req;
228 m->BDEV_MINOR = minor(dev);
229 m->BDEV_POS_LO = ex64lo(pos);
230 m->BDEV_POS_HI = ex64hi(pos);
231 m->BDEV_COUNT = count;
232 m->BDEV_GRANT = grant;
233 m->BDEV_FLAGS = flags;
235 return OK;
238 static void bdev_vrdwt_cleanup(const message *m, iovec_s_t *gvec)
240 /* Clean up a vectored read/write request.
242 cp_grant_id_t grant;
243 int i;
245 grant = m->BDEV_GRANT;
247 cpf_revoke(grant);
249 for (i = m->BDEV_COUNT - 1; i >= 0; i--)
250 cpf_revoke(gvec[i].iov_grant);
253 static ssize_t bdev_vrdwt(int req, dev_t dev, u64_t pos, iovec_t *vec,
254 int count, int flags)
256 /* Perform a synchronous read or write call using a vector of buffers.
258 iovec_s_t gvec[NR_IOREQS];
259 message m;
260 int r, driver_tries = 0, transfer_tries = 0;
262 do {
263 if ((r = bdev_vrdwt_setup(req, dev, pos, vec, count, flags, &m,
264 gvec)) != OK)
265 break;
267 r = bdev_sendrec(dev, &m);
269 bdev_vrdwt_cleanup(&m, gvec);
270 } while (bdev_retry(&driver_tries, &transfer_tries, &r));
272 return r;
275 ssize_t bdev_read(dev_t dev, u64_t pos, char *buf, size_t count, int flags)
277 /* Perform a synchronous read call into a single buffer.
280 return bdev_rdwt(BDEV_READ, dev, pos, buf, count, flags);
283 ssize_t bdev_write(dev_t dev, u64_t pos, char *buf, size_t count, int flags)
285 /* Perform a synchronous write call from a single buffer.
288 return bdev_rdwt(BDEV_WRITE, dev, pos, buf, count, flags);
291 ssize_t bdev_gather(dev_t dev, u64_t pos, iovec_t *vec, int count, int flags)
293 /* Perform a synchronous read call into a vector of buffers.
296 return bdev_vrdwt(BDEV_GATHER, dev, pos, vec, count, flags);
299 ssize_t bdev_scatter(dev_t dev, u64_t pos, iovec_t *vec, int count, int flags)
301 /* Perform a synchronous write call from a vector of buffers.
304 return bdev_vrdwt(BDEV_SCATTER, dev, pos, vec, count, flags);
307 static int bdev_ioctl_setup(dev_t dev, int request, void *buf, message *m)
309 /* Set up an I/O control request.
311 endpoint_t endpt;
312 size_t size;
313 cp_grant_id_t grant;
314 int access;
316 if ((endpt = bdev_driver_get(dev)) == NONE)
317 return EDEADSRCDST;
319 if (_MINIX_IOCTL_BIG(request))
320 size = _MINIX_IOCTL_SIZE_BIG(request);
321 else
322 size = _MINIX_IOCTL_SIZE(request);
324 access = 0;
325 if (_MINIX_IOCTL_IOR(access)) access |= CPF_WRITE;
326 if (_MINIX_IOCTL_IOW(access)) access |= CPF_READ;
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);
331 if (!GRANT_VALID(grant)) {
332 printf("bdev: unable to allocate grant!\n");
333 return EINVAL;
336 memset(m, 0, sizeof(*m));
337 m->m_type = BDEV_IOCTL;
338 m->BDEV_MINOR = minor(dev);
339 m->BDEV_REQUEST = request;
340 m->BDEV_GRANT = grant;
342 return OK;
345 static void bdev_ioctl_cleanup(const message *m)
347 /* Clean up an I/O control request.
350 cpf_revoke(m->BDEV_GRANT);
353 int bdev_ioctl(dev_t dev, int request, void *buf)
355 /* Perform a synchronous I/O control request.
357 message m;
358 int r, driver_tries = 0;
360 do {
361 if ((r = bdev_ioctl_setup(dev, request, buf, &m)) != OK)
362 break;
364 r = bdev_sendrec(dev, &m);
366 bdev_ioctl_cleanup(&m);
367 } while (bdev_retry(&driver_tries, NULL, &r));
369 return r;
372 void bdev_flush_asyn(dev_t dev)
374 /* Flush all ongoing asynchronous requests to the given minor device. This
375 * involves blocking until all I/O for it has completed.
376 * File system usage note: typically called from flush.
378 bdev_call_t *call;
380 while ((call = bdev_call_find(dev)) != NULL)
381 (void) bdev_wait_asyn(call->id);
384 static bdev_id_t bdev_rdwt_asyn(int req, dev_t dev, u64_t pos, char *buf,
385 size_t count, int flags, bdev_callback_t callback, bdev_param_t param)
387 /* Perform an asynchronous read or write call using a single buffer.
389 bdev_call_t *call;
390 int r;
392 if ((call = bdev_call_alloc(1)) == NULL)
393 return ENOMEM;
395 if ((r = bdev_rdwt_setup(req, dev, pos, buf, count, flags, &call->msg)) !=
396 OK) {
397 bdev_call_free(call);
399 return r;
402 if ((r = bdev_senda(dev, &call->msg, call->id)) != OK) {
403 bdev_rdwt_cleanup(&call->msg);
405 bdev_call_free(call);
407 return r;
410 call->dev = dev;
411 call->callback = callback;
412 call->param = param;
413 call->driver_tries = 0;
414 call->transfer_tries = 0;
415 call->vec[0].iov_addr = (vir_bytes) buf;
416 call->vec[0].iov_size = count;
418 return call->id;
421 static bdev_id_t bdev_vrdwt_asyn(int req, dev_t dev, u64_t pos, iovec_t *vec,
422 int count, int flags, bdev_callback_t callback, bdev_param_t param)
424 /* Perform an asynchronous read or write call using a vector of buffers.
426 bdev_call_t *call;
427 int r;
429 if ((call = bdev_call_alloc(count)) == NULL)
430 return ENOMEM;
432 if ((r = bdev_vrdwt_setup(req, dev, pos, vec, count, flags, &call->msg,
433 call->gvec)) != OK) {
434 bdev_call_free(call);
436 return r;
439 if ((r = bdev_senda(dev, &call->msg, call->id)) != OK) {
440 bdev_vrdwt_cleanup(&call->msg, call->gvec);
442 bdev_call_free(call);
444 return r;
447 call->dev = dev;
448 call->callback = callback;
449 call->param = param;
450 call->driver_tries = 0;
451 call->transfer_tries = 0;
452 memcpy(call->vec, vec, sizeof(vec[0]) * count);
454 return call->id;
457 bdev_id_t bdev_read_asyn(dev_t dev, u64_t pos, char *buf, size_t count,
458 int flags, bdev_callback_t callback, bdev_param_t param)
460 /* Perform an asynchronous read call into a single buffer.
463 return bdev_rdwt_asyn(BDEV_READ, dev, pos, buf, count, flags, callback,
464 param);
467 bdev_id_t bdev_write_asyn(dev_t dev, u64_t pos, char *buf, size_t count,
468 int flags, bdev_callback_t callback, bdev_param_t param)
470 /* Perform an asynchronous write call from a single buffer.
473 return bdev_rdwt_asyn(BDEV_WRITE, dev, pos, buf, count, flags, callback,
474 param);
477 bdev_id_t bdev_gather_asyn(dev_t dev, u64_t pos, iovec_t *vec, int count,
478 int flags, bdev_callback_t callback, bdev_param_t param)
480 /* Perform an asynchronous read call into a vector of buffers.
483 return bdev_vrdwt_asyn(BDEV_GATHER, dev, pos, vec, count, flags, callback,
484 param);
487 bdev_id_t bdev_scatter_asyn(dev_t dev, u64_t pos, iovec_t *vec, int count,
488 int flags, bdev_callback_t callback, bdev_param_t param)
490 /* Perform an asynchronous write call into a vector of buffers.
493 return bdev_vrdwt_asyn(BDEV_SCATTER, dev, pos, vec, count, flags, callback,
494 param);
497 bdev_id_t bdev_ioctl_asyn(dev_t dev, int request, void *buf,
498 bdev_callback_t callback, bdev_param_t param)
500 /* Perform an asynchronous I/O control request.
502 bdev_call_t *call;
503 int r;
505 if ((call = bdev_call_alloc(1)) == NULL)
506 return ENOMEM;
508 if ((r = bdev_ioctl_setup(dev, request, buf, &call->msg)) != OK) {
509 bdev_call_free(call);
511 return r;
514 if ((r = bdev_senda(dev, &call->msg, call->id)) != OK) {
515 bdev_ioctl_cleanup(&call->msg);
517 bdev_call_free(call);
519 return r;
522 call->dev = dev;
523 call->callback = callback;
524 call->param = param;
525 call->driver_tries = 0;
526 call->vec[0].iov_addr = (vir_bytes) buf;
528 return call->id;
531 void bdev_callback_asyn(bdev_call_t *call, int result)
533 /* Perform the callback for an asynchronous request, with the given result.
534 * Clean up the call structure afterwards.
537 /* If this was a transfer request and the result is EIO, we may want to retry
538 * the request first.
540 switch (call->msg.m_type) {
541 case BDEV_READ:
542 case BDEV_WRITE:
543 case BDEV_GATHER:
544 case BDEV_SCATTER:
545 if (result == EIO && ++call->transfer_tries < TRANSFER_TRIES) {
546 result = bdev_senda(call->dev, &call->msg, call->id);
548 if (result == OK)
549 return;
553 /* Clean up. */
554 switch (call->msg.m_type) {
555 case BDEV_READ:
556 case BDEV_WRITE:
557 bdev_rdwt_cleanup(&call->msg);
559 break;
561 case BDEV_GATHER:
562 case BDEV_SCATTER:
563 bdev_vrdwt_cleanup(&call->msg, call->gvec);
565 break;
567 case BDEV_IOCTL:
568 bdev_ioctl_cleanup(&call->msg);
570 break;
572 default:
573 assert(0);
576 /* Call the callback function. */
577 /* FIXME: we assume all reasonable ssize_t values can be stored in an int. */
578 call->callback(call->dev, call->id, call->param, result);
580 /* Free up the call structure. */
581 bdev_call_free(call);
584 int bdev_restart_asyn(bdev_call_t *call)
586 /* The driver for the given call has restarted, and may now have a new
587 * endpoint. Recreate and resend the request for the given call.
589 int type, r = OK;
591 /* Update and check the retry limit for driver restarts first. */
592 if (++call->driver_tries >= DRIVER_TRIES)
593 return EDEADSRCDST;
595 /* Recreate all grants for the new endpoint. */
596 type = call->msg.m_type;
598 switch (type) {
599 case BDEV_READ:
600 case BDEV_WRITE:
601 bdev_rdwt_cleanup(&call->msg);
603 r = bdev_rdwt_setup(type, call->dev,
604 make64(call->msg.BDEV_POS_LO, call->msg.BDEV_POS_HI),
605 (char *) call->vec[0].iov_addr, call->msg.BDEV_COUNT,
606 call->msg.BDEV_FLAGS, &call->msg);
608 break;
610 case BDEV_GATHER:
611 case BDEV_SCATTER:
612 bdev_vrdwt_cleanup(&call->msg, call->gvec);
614 r = bdev_vrdwt_setup(type, call->dev,
615 make64(call->msg.BDEV_POS_LO, call->msg.BDEV_POS_HI),
616 call->vec, call->msg.BDEV_COUNT, call->msg.BDEV_FLAGS,
617 &call->msg, call->gvec);
619 break;
621 case BDEV_IOCTL:
622 bdev_ioctl_cleanup(&call->msg);
624 r = bdev_ioctl_setup(call->dev, call->msg.BDEV_REQUEST,
625 (char *) call->vec[0].iov_addr, &call->msg);
627 break;
629 default:
630 assert(0);
633 if (r != OK)
634 return r;
636 /* Try to resend the request. */
637 return bdev_senda(call->dev, &call->msg, call->id);