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>
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
;
20 /* Initialize the driver endpoint array. */
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.
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
)
43 *result
= EDEADSRCDST
;
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
)
61 static int bdev_opcl(int req
, dev_t dev
, int access
)
63 /* Open or close the given minor device.
66 int r
, driver_tries
= 0;
69 memset(&m
, 0, sizeof(m
));
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
));
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.
87 r
= bdev_opcl(BDEV_OPEN
, dev
, access
);
90 bdev_minor_add(dev
, access
);
95 int bdev_close(dev_t dev
)
97 /* Close the given minor device.
98 * File system usage note: typically called from unmount.
102 bdev_flush_asyn(dev
);
104 r
= bdev_opcl(BDEV_CLOSE
, dev
, 0);
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.
121 assert((ssize_t
) count
>= 0);
123 if ((endpt
= bdev_driver_get(dev
)) == NONE
)
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");
135 memset(m
, 0, sizeof(*m
));
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
;
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.
161 int r
, driver_tries
= 0, transfer_tries
= 0;
164 if ((r
= bdev_rdwt_setup(req
, dev
, pos
, buf
, count
, flags
, &m
)) != OK
)
167 r
= bdev_sendrec(dev
, &m
);
169 bdev_rdwt_cleanup(&m
);
170 } while (bdev_retry(&driver_tries
, &transfer_tries
, &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.
185 assert(count
<= NR_IOREQS
);
187 if ((endpt
= bdev_driver_get(dev
)) == NONE
)
190 access
= (req
== BDEV_GATHER
) ? CPF_WRITE
: CPF_READ
;
193 for (i
= 0; i
< count
; i
++) {
194 grant
= cpf_grant_direct(endpt
, vec
[i
].iov_addr
, vec
[i
].iov_size
,
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
);
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
,
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
);
226 memset(m
, 0, sizeof(*m
));
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
;
238 static void bdev_vrdwt_cleanup(const message
*m
, iovec_s_t
*gvec
)
240 /* Clean up a vectored read/write request.
245 grant
= m
->BDEV_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
];
260 int r
, driver_tries
= 0, transfer_tries
= 0;
263 if ((r
= bdev_vrdwt_setup(req
, dev
, pos
, vec
, count
, flags
, &m
,
267 r
= bdev_sendrec(dev
, &m
);
269 bdev_vrdwt_cleanup(&m
, gvec
);
270 } while (bdev_retry(&driver_tries
, &transfer_tries
, &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.
316 if ((endpt
= bdev_driver_get(dev
)) == NONE
)
319 if (_MINIX_IOCTL_BIG(request
))
320 size
= _MINIX_IOCTL_SIZE_BIG(request
);
322 size
= _MINIX_IOCTL_SIZE(request
);
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");
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
;
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.
358 int r
, driver_tries
= 0;
361 if ((r
= bdev_ioctl_setup(dev
, request
, buf
, &m
)) != OK
)
364 r
= bdev_sendrec(dev
, &m
);
366 bdev_ioctl_cleanup(&m
);
367 } while (bdev_retry(&driver_tries
, NULL
, &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.
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.
392 if ((call
= bdev_call_alloc(1)) == NULL
)
395 if ((r
= bdev_rdwt_setup(req
, dev
, pos
, buf
, count
, flags
, &call
->msg
)) !=
397 bdev_call_free(call
);
402 if ((r
= bdev_senda(dev
, &call
->msg
, call
->id
)) != OK
) {
403 bdev_rdwt_cleanup(&call
->msg
);
405 bdev_call_free(call
);
411 call
->callback
= callback
;
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
;
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.
429 if ((call
= bdev_call_alloc(count
)) == NULL
)
432 if ((r
= bdev_vrdwt_setup(req
, dev
, pos
, vec
, count
, flags
, &call
->msg
,
433 call
->gvec
)) != OK
) {
434 bdev_call_free(call
);
439 if ((r
= bdev_senda(dev
, &call
->msg
, call
->id
)) != OK
) {
440 bdev_vrdwt_cleanup(&call
->msg
, call
->gvec
);
442 bdev_call_free(call
);
448 call
->callback
= callback
;
450 call
->driver_tries
= 0;
451 call
->transfer_tries
= 0;
452 memcpy(call
->vec
, vec
, sizeof(vec
[0]) * count
);
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
,
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
,
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
,
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
,
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.
505 if ((call
= bdev_call_alloc(1)) == NULL
)
508 if ((r
= bdev_ioctl_setup(dev
, request
, buf
, &call
->msg
)) != OK
) {
509 bdev_call_free(call
);
514 if ((r
= bdev_senda(dev
, &call
->msg
, call
->id
)) != OK
) {
515 bdev_ioctl_cleanup(&call
->msg
);
517 bdev_call_free(call
);
523 call
->callback
= callback
;
525 call
->driver_tries
= 0;
526 call
->vec
[0].iov_addr
= (vir_bytes
) buf
;
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
540 switch (call
->msg
.m_type
) {
545 if (result
== EIO
&& ++call
->transfer_tries
< TRANSFER_TRIES
) {
546 result
= bdev_senda(call
->dev
, &call
->msg
, call
->id
);
554 switch (call
->msg
.m_type
) {
557 bdev_rdwt_cleanup(&call
->msg
);
563 bdev_vrdwt_cleanup(&call
->msg
, call
->gvec
);
568 bdev_ioctl_cleanup(&call
->msg
);
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.
591 /* Update and check the retry limit for driver restarts first. */
592 if (++call
->driver_tries
>= DRIVER_TRIES
)
595 /* Recreate all grants for the new endpoint. */
596 type
= call
->msg
.m_type
;
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
);
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
);
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
);
636 /* Try to resend the request. */
637 return bdev_senda(call
->dev
, &call
->msg
, call
->id
);