1433d6423SLionel Sambuc /* libbdev - block device interfacing library, by D.C. van Moolenbroek */
2433d6423SLionel Sambuc
3433d6423SLionel Sambuc #include <minix/drivers.h>
4433d6423SLionel Sambuc #include <minix/bdev.h>
5433d6423SLionel Sambuc #include <minix/ioctl.h>
6433d6423SLionel Sambuc #include <assert.h>
7433d6423SLionel Sambuc
8433d6423SLionel Sambuc #include "const.h"
9433d6423SLionel Sambuc #include "type.h"
10433d6423SLionel Sambuc #include "proto.h"
11433d6423SLionel Sambuc
bdev_driver(dev_t dev,char * label)12433d6423SLionel Sambuc void bdev_driver(dev_t dev, char *label)
13433d6423SLionel Sambuc {
14433d6423SLionel Sambuc /* Associate a driver with the given (major) device, using its endpoint.
15433d6423SLionel Sambuc * File system usage note: typically called from mount and newdriver.
16433d6423SLionel Sambuc */
17433d6423SLionel Sambuc static int first = TRUE;
18433d6423SLionel Sambuc
19433d6423SLionel Sambuc if (first) {
20433d6423SLionel Sambuc /* Initialize the driver endpoint array. */
21433d6423SLionel Sambuc bdev_driver_init();
22433d6423SLionel Sambuc
23433d6423SLionel Sambuc first = FALSE;
24433d6423SLionel Sambuc }
25433d6423SLionel Sambuc
26433d6423SLionel Sambuc bdev_update(dev, label);
27433d6423SLionel Sambuc }
28433d6423SLionel Sambuc
bdev_retry(int * driver_tries,int * transfer_tries,int * result)29433d6423SLionel Sambuc static int bdev_retry(int *driver_tries, int *transfer_tries, int *result)
30433d6423SLionel Sambuc {
31433d6423SLionel Sambuc /* Return TRUE iff the call result implies that we should retry the operation.
32433d6423SLionel Sambuc */
33433d6423SLionel Sambuc
34433d6423SLionel Sambuc switch (*result) {
35433d6423SLionel Sambuc case ERESTART:
36433d6423SLionel Sambuc /* We get this error internally if the driver has restarted and the
37433d6423SLionel Sambuc * current operation may now go through. Check the retry count for
38433d6423SLionel Sambuc * driver restarts first, as we don't want to keep trying forever.
39433d6423SLionel Sambuc */
40433d6423SLionel Sambuc if (++*driver_tries < DRIVER_TRIES)
41433d6423SLionel Sambuc return TRUE;
42433d6423SLionel Sambuc
43433d6423SLionel Sambuc *result = EDEADSRCDST;
44433d6423SLionel Sambuc
45433d6423SLionel Sambuc break;
46433d6423SLionel Sambuc
47433d6423SLionel Sambuc case EIO:
48433d6423SLionel Sambuc /* The 'transfer_tries' pointer is non-NULL if this was a transfer
49433d6423SLionel Sambuc * request. If we get back an I/O failure, keep retrying the request
50433d6423SLionel Sambuc * until we hit the transfer retry limit.
51433d6423SLionel Sambuc */
52433d6423SLionel Sambuc if (transfer_tries != NULL && ++*transfer_tries < TRANSFER_TRIES)
53433d6423SLionel Sambuc return TRUE;
54433d6423SLionel Sambuc
55433d6423SLionel Sambuc break;
56433d6423SLionel Sambuc }
57433d6423SLionel Sambuc
58433d6423SLionel Sambuc return FALSE;
59433d6423SLionel Sambuc }
60433d6423SLionel Sambuc
bdev_opcl(int req,dev_t dev,int bits)61*7c48de6cSDavid van Moolenbroek static int bdev_opcl(int req, dev_t dev, int bits)
62433d6423SLionel Sambuc {
63433d6423SLionel Sambuc /* Open or close the given minor device.
64433d6423SLionel Sambuc */
65433d6423SLionel Sambuc message m;
66433d6423SLionel Sambuc int r, driver_tries = 0;
67433d6423SLionel Sambuc
68433d6423SLionel Sambuc do {
69433d6423SLionel Sambuc memset(&m, 0, sizeof(m));
70433d6423SLionel Sambuc m.m_type = req;
71433d6423SLionel Sambuc m.m_lbdev_lblockdriver_msg.minor = minor(dev);
72*7c48de6cSDavid van Moolenbroek m.m_lbdev_lblockdriver_msg.access = bits;
73433d6423SLionel Sambuc
74433d6423SLionel Sambuc r = bdev_sendrec(dev, &m);
75433d6423SLionel Sambuc } while (bdev_retry(&driver_tries, NULL, &r));
76433d6423SLionel Sambuc
77433d6423SLionel Sambuc return r;
78433d6423SLionel Sambuc }
79433d6423SLionel Sambuc
bdev_open(dev_t dev,int bits)80*7c48de6cSDavid van Moolenbroek int bdev_open(dev_t dev, int bits)
81433d6423SLionel Sambuc {
82433d6423SLionel Sambuc /* Open the given minor device.
83433d6423SLionel Sambuc * File system usage note: typically called from mount, after bdev_driver.
84433d6423SLionel Sambuc */
85433d6423SLionel Sambuc int r;
86433d6423SLionel Sambuc
87*7c48de6cSDavid van Moolenbroek r = bdev_opcl(BDEV_OPEN, dev, bits);
88433d6423SLionel Sambuc
89433d6423SLionel Sambuc if (r == OK)
90*7c48de6cSDavid van Moolenbroek bdev_minor_add(dev, bits);
91433d6423SLionel Sambuc
92433d6423SLionel Sambuc return r;
93433d6423SLionel Sambuc }
94433d6423SLionel Sambuc
bdev_close(dev_t dev)95433d6423SLionel Sambuc int bdev_close(dev_t dev)
96433d6423SLionel Sambuc {
97433d6423SLionel Sambuc /* Close the given minor device.
98433d6423SLionel Sambuc * File system usage note: typically called from unmount.
99433d6423SLionel Sambuc */
100433d6423SLionel Sambuc int r;
101433d6423SLionel Sambuc
102433d6423SLionel Sambuc bdev_flush_asyn(dev);
103433d6423SLionel Sambuc
104433d6423SLionel Sambuc r = bdev_opcl(BDEV_CLOSE, dev, 0);
105433d6423SLionel Sambuc
106433d6423SLionel Sambuc if (r == OK)
107433d6423SLionel Sambuc bdev_minor_del(dev);
108433d6423SLionel Sambuc
109433d6423SLionel Sambuc return r;
110433d6423SLionel Sambuc }
111433d6423SLionel Sambuc
bdev_rdwt_setup(int req,dev_t dev,u64_t pos,char * buf,size_t count,int flags,message * m)112433d6423SLionel Sambuc static int bdev_rdwt_setup(int req, dev_t dev, u64_t pos, char *buf,
113433d6423SLionel Sambuc size_t count, int flags, message *m)
114433d6423SLionel Sambuc {
115433d6423SLionel Sambuc /* Set up a single-buffer read/write request.
116433d6423SLionel Sambuc */
117433d6423SLionel Sambuc endpoint_t endpt;
118433d6423SLionel Sambuc cp_grant_id_t grant;
119*7c48de6cSDavid van Moolenbroek int perm;
120433d6423SLionel Sambuc
121433d6423SLionel Sambuc assert((ssize_t) count >= 0);
122433d6423SLionel Sambuc
123433d6423SLionel Sambuc if ((endpt = bdev_driver_get(dev)) == NONE)
124433d6423SLionel Sambuc return EDEADSRCDST;
125433d6423SLionel Sambuc
126*7c48de6cSDavid van Moolenbroek perm = (req == BDEV_READ) ? CPF_WRITE : CPF_READ;
127433d6423SLionel Sambuc
128*7c48de6cSDavid van Moolenbroek grant = cpf_grant_direct(endpt, (vir_bytes) buf, count, perm);
129433d6423SLionel Sambuc
130433d6423SLionel Sambuc if (!GRANT_VALID(grant)) {
131433d6423SLionel Sambuc printf("bdev: unable to allocate grant!\n");
132433d6423SLionel Sambuc return EINVAL;
133433d6423SLionel Sambuc }
134433d6423SLionel Sambuc
135433d6423SLionel Sambuc memset(m, 0, sizeof(*m));
136433d6423SLionel Sambuc m->m_type = req;
137433d6423SLionel Sambuc m->m_lbdev_lblockdriver_msg.minor = minor(dev);
138433d6423SLionel Sambuc m->m_lbdev_lblockdriver_msg.pos = pos;
139433d6423SLionel Sambuc m->m_lbdev_lblockdriver_msg.count = count;
140433d6423SLionel Sambuc m->m_lbdev_lblockdriver_msg.grant = grant;
141433d6423SLionel Sambuc m->m_lbdev_lblockdriver_msg.flags = flags;
142433d6423SLionel Sambuc
143433d6423SLionel Sambuc return OK;
144433d6423SLionel Sambuc }
145433d6423SLionel Sambuc
bdev_rdwt_cleanup(const message * m)146433d6423SLionel Sambuc static void bdev_rdwt_cleanup(const message *m)
147433d6423SLionel Sambuc {
148433d6423SLionel Sambuc /* Clean up a single-buffer read/write request.
149433d6423SLionel Sambuc */
150433d6423SLionel Sambuc
151433d6423SLionel Sambuc cpf_revoke(m->m_lbdev_lblockdriver_msg.grant);
152433d6423SLionel Sambuc }
153433d6423SLionel Sambuc
bdev_rdwt(int req,dev_t dev,u64_t pos,char * buf,size_t count,int flags)154433d6423SLionel Sambuc static ssize_t bdev_rdwt(int req, dev_t dev, u64_t pos, char *buf,
155433d6423SLionel Sambuc size_t count, int flags)
156433d6423SLionel Sambuc {
157433d6423SLionel Sambuc /* Perform a synchronous read or write call using a single buffer.
158433d6423SLionel Sambuc */
159433d6423SLionel Sambuc message m;
160433d6423SLionel Sambuc int r, driver_tries = 0, transfer_tries = 0;
161433d6423SLionel Sambuc
162433d6423SLionel Sambuc do {
163433d6423SLionel Sambuc if ((r = bdev_rdwt_setup(req, dev, pos, buf, count, flags, &m)) != OK)
164433d6423SLionel Sambuc break;
165433d6423SLionel Sambuc
166433d6423SLionel Sambuc r = bdev_sendrec(dev, &m);
167433d6423SLionel Sambuc
168433d6423SLionel Sambuc bdev_rdwt_cleanup(&m);
169433d6423SLionel Sambuc } while (bdev_retry(&driver_tries, &transfer_tries, &r));
170433d6423SLionel Sambuc
171433d6423SLionel Sambuc return r;
172433d6423SLionel Sambuc }
173433d6423SLionel Sambuc
bdev_vrdwt_setup(int req,dev_t dev,u64_t pos,iovec_t * vec,int count,int flags,message * m,iovec_s_t * gvec)174433d6423SLionel Sambuc static int bdev_vrdwt_setup(int req, dev_t dev, u64_t pos, iovec_t *vec,
175433d6423SLionel Sambuc int count, int flags, message *m, iovec_s_t *gvec)
176433d6423SLionel Sambuc {
177433d6423SLionel Sambuc /* Set up a vectored read/write request.
178433d6423SLionel Sambuc */
179433d6423SLionel Sambuc ssize_t size;
180433d6423SLionel Sambuc endpoint_t endpt;
181433d6423SLionel Sambuc cp_grant_id_t grant;
182*7c48de6cSDavid van Moolenbroek int i, perm;
183433d6423SLionel Sambuc
184433d6423SLionel Sambuc assert(count <= NR_IOREQS);
185433d6423SLionel Sambuc
186433d6423SLionel Sambuc if ((endpt = bdev_driver_get(dev)) == NONE)
187433d6423SLionel Sambuc return EDEADSRCDST;
188433d6423SLionel Sambuc
189*7c48de6cSDavid van Moolenbroek perm = (req == BDEV_GATHER) ? CPF_WRITE : CPF_READ;
190433d6423SLionel Sambuc size = 0;
191433d6423SLionel Sambuc
192433d6423SLionel Sambuc for (i = 0; i < count; i++) {
193433d6423SLionel Sambuc grant = cpf_grant_direct(endpt, vec[i].iov_addr, vec[i].iov_size,
194*7c48de6cSDavid van Moolenbroek perm);
195433d6423SLionel Sambuc
196433d6423SLionel Sambuc if (!GRANT_VALID(grant)) {
197433d6423SLionel Sambuc printf("bdev: unable to allocate grant!\n");
198433d6423SLionel Sambuc
199433d6423SLionel Sambuc for (i--; i >= 0; i--)
200433d6423SLionel Sambuc cpf_revoke(gvec[i].iov_grant);
201433d6423SLionel Sambuc
202433d6423SLionel Sambuc return EINVAL;
203433d6423SLionel Sambuc }
204433d6423SLionel Sambuc
205433d6423SLionel Sambuc gvec[i].iov_grant = grant;
206433d6423SLionel Sambuc gvec[i].iov_size = vec[i].iov_size;
207433d6423SLionel Sambuc
208433d6423SLionel Sambuc assert(vec[i].iov_size > 0);
209433d6423SLionel Sambuc assert((ssize_t) (size + vec[i].iov_size) > size);
210433d6423SLionel Sambuc
211433d6423SLionel Sambuc size += vec[i].iov_size;
212433d6423SLionel Sambuc }
213433d6423SLionel Sambuc
214433d6423SLionel Sambuc grant = cpf_grant_direct(endpt, (vir_bytes) gvec, sizeof(gvec[0]) * count,
215433d6423SLionel Sambuc CPF_READ);
216433d6423SLionel Sambuc
217433d6423SLionel Sambuc if (!GRANT_VALID(grant)) {
218433d6423SLionel Sambuc printf("bdev: unable to allocate grant!\n");
219433d6423SLionel Sambuc
220433d6423SLionel Sambuc for (i = count - 1; i >= 0; i--)
221433d6423SLionel Sambuc cpf_revoke(gvec[i].iov_grant);
222433d6423SLionel Sambuc
223433d6423SLionel Sambuc return EINVAL;
224433d6423SLionel Sambuc }
225433d6423SLionel Sambuc
226433d6423SLionel Sambuc memset(m, 0, sizeof(*m));
227433d6423SLionel Sambuc m->m_type = req;
228433d6423SLionel Sambuc m->m_lbdev_lblockdriver_msg.minor = minor(dev);
229433d6423SLionel Sambuc m->m_lbdev_lblockdriver_msg.pos = pos;
230433d6423SLionel Sambuc m->m_lbdev_lblockdriver_msg.count = count;
231433d6423SLionel Sambuc m->m_lbdev_lblockdriver_msg.grant = grant;
232433d6423SLionel Sambuc m->m_lbdev_lblockdriver_msg.flags = flags;
233433d6423SLionel Sambuc
234433d6423SLionel Sambuc return OK;
235433d6423SLionel Sambuc }
236433d6423SLionel Sambuc
bdev_vrdwt_cleanup(const message * m,iovec_s_t * gvec)237433d6423SLionel Sambuc static void bdev_vrdwt_cleanup(const message *m, iovec_s_t *gvec)
238433d6423SLionel Sambuc {
239433d6423SLionel Sambuc /* Clean up a vectored read/write request.
240433d6423SLionel Sambuc */
241433d6423SLionel Sambuc cp_grant_id_t grant;
242433d6423SLionel Sambuc int i;
243433d6423SLionel Sambuc
244433d6423SLionel Sambuc grant = m->m_lbdev_lblockdriver_msg.grant;
245433d6423SLionel Sambuc
246433d6423SLionel Sambuc cpf_revoke(grant);
247433d6423SLionel Sambuc
248433d6423SLionel Sambuc for (i = m->m_lbdev_lblockdriver_msg.count - 1; i >= 0; i--)
249433d6423SLionel Sambuc cpf_revoke(gvec[i].iov_grant);
250433d6423SLionel Sambuc }
251433d6423SLionel Sambuc
bdev_vrdwt(int req,dev_t dev,u64_t pos,iovec_t * vec,int count,int flags)252433d6423SLionel Sambuc static ssize_t bdev_vrdwt(int req, dev_t dev, u64_t pos, iovec_t *vec,
253433d6423SLionel Sambuc int count, int flags)
254433d6423SLionel Sambuc {
255433d6423SLionel Sambuc /* Perform a synchronous read or write call using a vector of buffers.
256433d6423SLionel Sambuc */
257433d6423SLionel Sambuc iovec_s_t gvec[NR_IOREQS];
258433d6423SLionel Sambuc message m;
259433d6423SLionel Sambuc int r, driver_tries = 0, transfer_tries = 0;
260433d6423SLionel Sambuc
261433d6423SLionel Sambuc do {
262433d6423SLionel Sambuc if ((r = bdev_vrdwt_setup(req, dev, pos, vec, count, flags, &m,
263433d6423SLionel Sambuc gvec)) != OK)
264433d6423SLionel Sambuc break;
265433d6423SLionel Sambuc
266433d6423SLionel Sambuc r = bdev_sendrec(dev, &m);
267433d6423SLionel Sambuc
268433d6423SLionel Sambuc bdev_vrdwt_cleanup(&m, gvec);
269433d6423SLionel Sambuc } while (bdev_retry(&driver_tries, &transfer_tries, &r));
270433d6423SLionel Sambuc
271433d6423SLionel Sambuc return r;
272433d6423SLionel Sambuc }
273433d6423SLionel Sambuc
bdev_read(dev_t dev,u64_t pos,char * buf,size_t count,int flags)274433d6423SLionel Sambuc ssize_t bdev_read(dev_t dev, u64_t pos, char *buf, size_t count, int flags)
275433d6423SLionel Sambuc {
276433d6423SLionel Sambuc /* Perform a synchronous read call into a single buffer.
277433d6423SLionel Sambuc */
278433d6423SLionel Sambuc
279433d6423SLionel Sambuc return bdev_rdwt(BDEV_READ, dev, pos, buf, count, flags);
280433d6423SLionel Sambuc }
281433d6423SLionel Sambuc
bdev_write(dev_t dev,u64_t pos,char * buf,size_t count,int flags)282433d6423SLionel Sambuc ssize_t bdev_write(dev_t dev, u64_t pos, char *buf, size_t count, int flags)
283433d6423SLionel Sambuc {
284433d6423SLionel Sambuc /* Perform a synchronous write call from a single buffer.
285433d6423SLionel Sambuc */
286433d6423SLionel Sambuc
287433d6423SLionel Sambuc return bdev_rdwt(BDEV_WRITE, dev, pos, buf, count, flags);
288433d6423SLionel Sambuc }
289433d6423SLionel Sambuc
bdev_gather(dev_t dev,u64_t pos,iovec_t * vec,int count,int flags)290433d6423SLionel Sambuc ssize_t bdev_gather(dev_t dev, u64_t pos, iovec_t *vec, int count, int flags)
291433d6423SLionel Sambuc {
292433d6423SLionel Sambuc /* Perform a synchronous read call into a vector of buffers.
293433d6423SLionel Sambuc */
294433d6423SLionel Sambuc
295433d6423SLionel Sambuc return bdev_vrdwt(BDEV_GATHER, dev, pos, vec, count, flags);
296433d6423SLionel Sambuc }
297433d6423SLionel Sambuc
bdev_scatter(dev_t dev,u64_t pos,iovec_t * vec,int count,int flags)298433d6423SLionel Sambuc ssize_t bdev_scatter(dev_t dev, u64_t pos, iovec_t *vec, int count, int flags)
299433d6423SLionel Sambuc {
300433d6423SLionel Sambuc /* Perform a synchronous write call from a vector of buffers.
301433d6423SLionel Sambuc */
302433d6423SLionel Sambuc
303433d6423SLionel Sambuc return bdev_vrdwt(BDEV_SCATTER, dev, pos, vec, count, flags);
304433d6423SLionel Sambuc }
305433d6423SLionel Sambuc
bdev_ioctl_setup(dev_t dev,unsigned long request,void * buf,endpoint_t user_endpt,message * m)30663ce03dbSDavid van Moolenbroek static int bdev_ioctl_setup(dev_t dev, unsigned long request, void *buf,
307433d6423SLionel Sambuc endpoint_t user_endpt, message *m)
308433d6423SLionel Sambuc {
309433d6423SLionel Sambuc /* Set up an I/O control request.
310433d6423SLionel Sambuc */
311433d6423SLionel Sambuc endpoint_t endpt;
312433d6423SLionel Sambuc size_t size;
313433d6423SLionel Sambuc cp_grant_id_t grant;
314*7c48de6cSDavid van Moolenbroek int perm;
315433d6423SLionel Sambuc
316433d6423SLionel Sambuc if ((endpt = bdev_driver_get(dev)) == NONE)
317433d6423SLionel Sambuc return EDEADSRCDST;
318433d6423SLionel Sambuc
319433d6423SLionel Sambuc if (_MINIX_IOCTL_BIG(request))
320433d6423SLionel Sambuc size = _MINIX_IOCTL_SIZE_BIG(request);
321433d6423SLionel Sambuc else
322433d6423SLionel Sambuc size = _MINIX_IOCTL_SIZE(request);
323433d6423SLionel Sambuc
324*7c48de6cSDavid van Moolenbroek perm = 0;
325*7c48de6cSDavid van Moolenbroek if (_MINIX_IOCTL_IOR(request)) perm |= CPF_WRITE;
326*7c48de6cSDavid van Moolenbroek if (_MINIX_IOCTL_IOW(request)) perm |= CPF_READ;
327433d6423SLionel Sambuc
328433d6423SLionel Sambuc /* The size may be 0, in which case 'buf' need not be a valid pointer. */
329*7c48de6cSDavid van Moolenbroek grant = cpf_grant_direct(endpt, (vir_bytes) buf, size, perm);
330433d6423SLionel Sambuc
331433d6423SLionel Sambuc if (!GRANT_VALID(grant)) {
332433d6423SLionel Sambuc printf("bdev: unable to allocate grant!\n");
333433d6423SLionel Sambuc return EINVAL;
334433d6423SLionel Sambuc }
335433d6423SLionel Sambuc
336433d6423SLionel Sambuc memset(m, 0, sizeof(*m));
337433d6423SLionel Sambuc m->m_type = BDEV_IOCTL;
338433d6423SLionel Sambuc m->m_lbdev_lblockdriver_msg.minor = minor(dev);
339433d6423SLionel Sambuc m->m_lbdev_lblockdriver_msg.request = request;
340433d6423SLionel Sambuc m->m_lbdev_lblockdriver_msg.grant = grant;
341433d6423SLionel Sambuc m->m_lbdev_lblockdriver_msg.user = user_endpt;
342433d6423SLionel Sambuc
343433d6423SLionel Sambuc return OK;
344433d6423SLionel Sambuc }
345433d6423SLionel Sambuc
bdev_ioctl_cleanup(const message * m)346433d6423SLionel Sambuc static void bdev_ioctl_cleanup(const message *m)
347433d6423SLionel Sambuc {
348433d6423SLionel Sambuc /* Clean up an I/O control request.
349433d6423SLionel Sambuc */
350433d6423SLionel Sambuc
351433d6423SLionel Sambuc cpf_revoke(m->m_lbdev_lblockdriver_msg.grant);
352433d6423SLionel Sambuc }
353433d6423SLionel Sambuc
bdev_ioctl(dev_t dev,unsigned long request,void * buf,endpoint_t user_endpt)35463ce03dbSDavid van Moolenbroek int bdev_ioctl(dev_t dev, unsigned long request, void *buf,
35563ce03dbSDavid van Moolenbroek endpoint_t user_endpt)
356433d6423SLionel Sambuc {
357433d6423SLionel Sambuc /* Perform a synchronous I/O control request.
358433d6423SLionel Sambuc */
359433d6423SLionel Sambuc message m;
360433d6423SLionel Sambuc int r, driver_tries = 0;
361433d6423SLionel Sambuc
362433d6423SLionel Sambuc do {
363433d6423SLionel Sambuc if ((r = bdev_ioctl_setup(dev, request, buf, user_endpt, &m)) != OK)
364433d6423SLionel Sambuc break;
365433d6423SLionel Sambuc
366433d6423SLionel Sambuc r = bdev_sendrec(dev, &m);
367433d6423SLionel Sambuc
368433d6423SLionel Sambuc bdev_ioctl_cleanup(&m);
369433d6423SLionel Sambuc } while (bdev_retry(&driver_tries, NULL, &r));
370433d6423SLionel Sambuc
371433d6423SLionel Sambuc return r;
372433d6423SLionel Sambuc }
373433d6423SLionel Sambuc
bdev_flush_asyn(dev_t dev)374433d6423SLionel Sambuc void bdev_flush_asyn(dev_t dev)
375433d6423SLionel Sambuc {
376433d6423SLionel Sambuc /* Flush all ongoing asynchronous requests to the given minor device. This
377433d6423SLionel Sambuc * involves blocking until all I/O for it has completed.
378433d6423SLionel Sambuc * File system usage note: typically called from flush.
379433d6423SLionel Sambuc */
380433d6423SLionel Sambuc bdev_call_t *call;
381433d6423SLionel Sambuc
382433d6423SLionel Sambuc while ((call = bdev_call_find(dev)) != NULL)
383433d6423SLionel Sambuc (void) bdev_wait_asyn(call->id);
384433d6423SLionel Sambuc }
385433d6423SLionel Sambuc
bdev_rdwt_asyn(int req,dev_t dev,u64_t pos,char * buf,size_t count,int flags,bdev_callback_t callback,bdev_param_t param)386433d6423SLionel Sambuc static bdev_id_t bdev_rdwt_asyn(int req, dev_t dev, u64_t pos, char *buf,
387433d6423SLionel Sambuc size_t count, int flags, bdev_callback_t callback, bdev_param_t param)
388433d6423SLionel Sambuc {
389433d6423SLionel Sambuc /* Perform an asynchronous read or write call using a single buffer.
390433d6423SLionel Sambuc */
391433d6423SLionel Sambuc bdev_call_t *call;
392433d6423SLionel Sambuc int r;
393433d6423SLionel Sambuc
394433d6423SLionel Sambuc if ((call = bdev_call_alloc(1)) == NULL)
395433d6423SLionel Sambuc return ENOMEM;
396433d6423SLionel Sambuc
397433d6423SLionel Sambuc if ((r = bdev_rdwt_setup(req, dev, pos, buf, count, flags, &call->msg)) !=
398433d6423SLionel Sambuc OK) {
399433d6423SLionel Sambuc bdev_call_free(call);
400433d6423SLionel Sambuc
401433d6423SLionel Sambuc return r;
402433d6423SLionel Sambuc }
403433d6423SLionel Sambuc
404433d6423SLionel Sambuc if ((r = bdev_senda(dev, &call->msg, call->id)) != OK) {
405433d6423SLionel Sambuc bdev_rdwt_cleanup(&call->msg);
406433d6423SLionel Sambuc
407433d6423SLionel Sambuc bdev_call_free(call);
408433d6423SLionel Sambuc
409433d6423SLionel Sambuc return r;
410433d6423SLionel Sambuc }
411433d6423SLionel Sambuc
412433d6423SLionel Sambuc call->dev = dev;
413433d6423SLionel Sambuc call->callback = callback;
414433d6423SLionel Sambuc call->param = param;
415433d6423SLionel Sambuc call->driver_tries = 0;
416433d6423SLionel Sambuc call->transfer_tries = 0;
417433d6423SLionel Sambuc call->vec[0].iov_addr = (vir_bytes) buf;
418433d6423SLionel Sambuc call->vec[0].iov_size = count;
419433d6423SLionel Sambuc
420433d6423SLionel Sambuc return call->id;
421433d6423SLionel Sambuc }
422433d6423SLionel Sambuc
bdev_vrdwt_asyn(int req,dev_t dev,u64_t pos,iovec_t * vec,int count,int flags,bdev_callback_t callback,bdev_param_t param)423433d6423SLionel Sambuc static bdev_id_t bdev_vrdwt_asyn(int req, dev_t dev, u64_t pos, iovec_t *vec,
424433d6423SLionel Sambuc int count, int flags, bdev_callback_t callback, bdev_param_t param)
425433d6423SLionel Sambuc {
426433d6423SLionel Sambuc /* Perform an asynchronous read or write call using a vector of buffers.
427433d6423SLionel Sambuc */
428433d6423SLionel Sambuc bdev_call_t *call;
429433d6423SLionel Sambuc int r;
430433d6423SLionel Sambuc
431433d6423SLionel Sambuc if ((call = bdev_call_alloc(count)) == NULL)
432433d6423SLionel Sambuc return ENOMEM;
433433d6423SLionel Sambuc
434433d6423SLionel Sambuc if ((r = bdev_vrdwt_setup(req, dev, pos, vec, count, flags, &call->msg,
435433d6423SLionel Sambuc call->gvec)) != OK) {
436433d6423SLionel Sambuc bdev_call_free(call);
437433d6423SLionel Sambuc
438433d6423SLionel Sambuc return r;
439433d6423SLionel Sambuc }
440433d6423SLionel Sambuc
441433d6423SLionel Sambuc if ((r = bdev_senda(dev, &call->msg, call->id)) != OK) {
442433d6423SLionel Sambuc bdev_vrdwt_cleanup(&call->msg, call->gvec);
443433d6423SLionel Sambuc
444433d6423SLionel Sambuc bdev_call_free(call);
445433d6423SLionel Sambuc
446433d6423SLionel Sambuc return r;
447433d6423SLionel Sambuc }
448433d6423SLionel Sambuc
449433d6423SLionel Sambuc call->dev = dev;
450433d6423SLionel Sambuc call->callback = callback;
451433d6423SLionel Sambuc call->param = param;
452433d6423SLionel Sambuc call->driver_tries = 0;
453433d6423SLionel Sambuc call->transfer_tries = 0;
454433d6423SLionel Sambuc memcpy(call->vec, vec, sizeof(vec[0]) * count);
455433d6423SLionel Sambuc
456433d6423SLionel Sambuc return call->id;
457433d6423SLionel Sambuc }
458433d6423SLionel Sambuc
bdev_read_asyn(dev_t dev,u64_t pos,char * buf,size_t count,int flags,bdev_callback_t callback,bdev_param_t param)459433d6423SLionel Sambuc bdev_id_t bdev_read_asyn(dev_t dev, u64_t pos, char *buf, size_t count,
460433d6423SLionel Sambuc int flags, bdev_callback_t callback, bdev_param_t param)
461433d6423SLionel Sambuc {
462433d6423SLionel Sambuc /* Perform an asynchronous read call into a single buffer.
463433d6423SLionel Sambuc */
464433d6423SLionel Sambuc
465433d6423SLionel Sambuc return bdev_rdwt_asyn(BDEV_READ, dev, pos, buf, count, flags, callback,
466433d6423SLionel Sambuc param);
467433d6423SLionel Sambuc }
468433d6423SLionel Sambuc
bdev_write_asyn(dev_t dev,u64_t pos,char * buf,size_t count,int flags,bdev_callback_t callback,bdev_param_t param)469433d6423SLionel Sambuc bdev_id_t bdev_write_asyn(dev_t dev, u64_t pos, char *buf, size_t count,
470433d6423SLionel Sambuc int flags, bdev_callback_t callback, bdev_param_t param)
471433d6423SLionel Sambuc {
472433d6423SLionel Sambuc /* Perform an asynchronous write call from a single buffer.
473433d6423SLionel Sambuc */
474433d6423SLionel Sambuc
475433d6423SLionel Sambuc return bdev_rdwt_asyn(BDEV_WRITE, dev, pos, buf, count, flags, callback,
476433d6423SLionel Sambuc param);
477433d6423SLionel Sambuc }
478433d6423SLionel Sambuc
bdev_gather_asyn(dev_t dev,u64_t pos,iovec_t * vec,int count,int flags,bdev_callback_t callback,bdev_param_t param)479433d6423SLionel Sambuc bdev_id_t bdev_gather_asyn(dev_t dev, u64_t pos, iovec_t *vec, int count,
480433d6423SLionel Sambuc int flags, bdev_callback_t callback, bdev_param_t param)
481433d6423SLionel Sambuc {
482433d6423SLionel Sambuc /* Perform an asynchronous read call into a vector of buffers.
483433d6423SLionel Sambuc */
484433d6423SLionel Sambuc
485433d6423SLionel Sambuc return bdev_vrdwt_asyn(BDEV_GATHER, dev, pos, vec, count, flags, callback,
486433d6423SLionel Sambuc param);
487433d6423SLionel Sambuc }
488433d6423SLionel Sambuc
bdev_scatter_asyn(dev_t dev,u64_t pos,iovec_t * vec,int count,int flags,bdev_callback_t callback,bdev_param_t param)489433d6423SLionel Sambuc bdev_id_t bdev_scatter_asyn(dev_t dev, u64_t pos, iovec_t *vec, int count,
490433d6423SLionel Sambuc int flags, bdev_callback_t callback, bdev_param_t param)
491433d6423SLionel Sambuc {
492433d6423SLionel Sambuc /* Perform an asynchronous write call into a vector of buffers.
493433d6423SLionel Sambuc */
494433d6423SLionel Sambuc
495433d6423SLionel Sambuc return bdev_vrdwt_asyn(BDEV_SCATTER, dev, pos, vec, count, flags, callback,
496433d6423SLionel Sambuc param);
497433d6423SLionel Sambuc }
498433d6423SLionel Sambuc
bdev_ioctl_asyn(dev_t dev,unsigned long request,void * buf,endpoint_t user_endpt,bdev_callback_t callback,bdev_param_t param)49963ce03dbSDavid van Moolenbroek bdev_id_t bdev_ioctl_asyn(dev_t dev, unsigned long request, void *buf,
500433d6423SLionel Sambuc endpoint_t user_endpt, bdev_callback_t callback, bdev_param_t param)
501433d6423SLionel Sambuc {
502433d6423SLionel Sambuc /* Perform an asynchronous I/O control request.
503433d6423SLionel Sambuc */
504433d6423SLionel Sambuc bdev_call_t *call;
505433d6423SLionel Sambuc int r;
506433d6423SLionel Sambuc
507433d6423SLionel Sambuc if ((call = bdev_call_alloc(1)) == NULL)
508433d6423SLionel Sambuc return ENOMEM;
509433d6423SLionel Sambuc
510433d6423SLionel Sambuc if ((r = bdev_ioctl_setup(dev, request, buf, user_endpt,
511433d6423SLionel Sambuc &call->msg)) != OK) {
512433d6423SLionel Sambuc bdev_call_free(call);
513433d6423SLionel Sambuc
514433d6423SLionel Sambuc return r;
515433d6423SLionel Sambuc }
516433d6423SLionel Sambuc
517433d6423SLionel Sambuc if ((r = bdev_senda(dev, &call->msg, call->id)) != OK) {
518433d6423SLionel Sambuc bdev_ioctl_cleanup(&call->msg);
519433d6423SLionel Sambuc
520433d6423SLionel Sambuc bdev_call_free(call);
521433d6423SLionel Sambuc
522433d6423SLionel Sambuc return r;
523433d6423SLionel Sambuc }
524433d6423SLionel Sambuc
525433d6423SLionel Sambuc call->dev = dev;
526433d6423SLionel Sambuc call->callback = callback;
527433d6423SLionel Sambuc call->param = param;
528433d6423SLionel Sambuc call->driver_tries = 0;
529433d6423SLionel Sambuc call->vec[0].iov_addr = (vir_bytes) buf;
530433d6423SLionel Sambuc
531433d6423SLionel Sambuc return call->id;
532433d6423SLionel Sambuc }
533433d6423SLionel Sambuc
bdev_callback_asyn(bdev_call_t * call,int result)534433d6423SLionel Sambuc void bdev_callback_asyn(bdev_call_t *call, int result)
535433d6423SLionel Sambuc {
536433d6423SLionel Sambuc /* Perform the callback for an asynchronous request, with the given result.
537433d6423SLionel Sambuc * Clean up the call structure afterwards.
538433d6423SLionel Sambuc */
539433d6423SLionel Sambuc
540433d6423SLionel Sambuc /* If this was a transfer request and the result is EIO, we may want to retry
541433d6423SLionel Sambuc * the request first.
542433d6423SLionel Sambuc */
543433d6423SLionel Sambuc switch (call->msg.m_type) {
544433d6423SLionel Sambuc case BDEV_READ:
545433d6423SLionel Sambuc case BDEV_WRITE:
546433d6423SLionel Sambuc case BDEV_GATHER:
547433d6423SLionel Sambuc case BDEV_SCATTER:
548433d6423SLionel Sambuc if (result == EIO && ++call->transfer_tries < TRANSFER_TRIES) {
549433d6423SLionel Sambuc result = bdev_senda(call->dev, &call->msg, call->id);
550433d6423SLionel Sambuc
551433d6423SLionel Sambuc if (result == OK)
552433d6423SLionel Sambuc return;
553433d6423SLionel Sambuc }
554433d6423SLionel Sambuc }
555433d6423SLionel Sambuc
556433d6423SLionel Sambuc /* Clean up. */
557433d6423SLionel Sambuc switch (call->msg.m_type) {
558433d6423SLionel Sambuc case BDEV_READ:
559433d6423SLionel Sambuc case BDEV_WRITE:
560433d6423SLionel Sambuc bdev_rdwt_cleanup(&call->msg);
561433d6423SLionel Sambuc
562433d6423SLionel Sambuc break;
563433d6423SLionel Sambuc
564433d6423SLionel Sambuc case BDEV_GATHER:
565433d6423SLionel Sambuc case BDEV_SCATTER:
566433d6423SLionel Sambuc bdev_vrdwt_cleanup(&call->msg, call->gvec);
567433d6423SLionel Sambuc
568433d6423SLionel Sambuc break;
569433d6423SLionel Sambuc
570433d6423SLionel Sambuc case BDEV_IOCTL:
571433d6423SLionel Sambuc bdev_ioctl_cleanup(&call->msg);
572433d6423SLionel Sambuc
573433d6423SLionel Sambuc break;
574433d6423SLionel Sambuc
575433d6423SLionel Sambuc default:
576433d6423SLionel Sambuc assert(0);
577433d6423SLionel Sambuc }
578433d6423SLionel Sambuc
579433d6423SLionel Sambuc /* Call the callback function. */
580433d6423SLionel Sambuc /* FIXME: we assume all reasonable ssize_t values can be stored in an int. */
581433d6423SLionel Sambuc call->callback(call->dev, call->id, call->param, result);
582433d6423SLionel Sambuc
583433d6423SLionel Sambuc /* Free up the call structure. */
584433d6423SLionel Sambuc bdev_call_free(call);
585433d6423SLionel Sambuc }
586433d6423SLionel Sambuc
bdev_restart_asyn(bdev_call_t * call)587433d6423SLionel Sambuc int bdev_restart_asyn(bdev_call_t *call)
588433d6423SLionel Sambuc {
589433d6423SLionel Sambuc /* The driver for the given call has restarted, and may now have a new
590433d6423SLionel Sambuc * endpoint. Recreate and resend the request for the given call.
591433d6423SLionel Sambuc */
592433d6423SLionel Sambuc int type, r = OK;
593433d6423SLionel Sambuc
594433d6423SLionel Sambuc /* Update and check the retry limit for driver restarts first. */
595433d6423SLionel Sambuc if (++call->driver_tries >= DRIVER_TRIES)
596433d6423SLionel Sambuc return EDEADSRCDST;
597433d6423SLionel Sambuc
598433d6423SLionel Sambuc /* Recreate all grants for the new endpoint. */
599433d6423SLionel Sambuc type = call->msg.m_type;
600433d6423SLionel Sambuc
601433d6423SLionel Sambuc switch (type) {
602433d6423SLionel Sambuc case BDEV_READ:
603433d6423SLionel Sambuc case BDEV_WRITE:
604433d6423SLionel Sambuc bdev_rdwt_cleanup(&call->msg);
605433d6423SLionel Sambuc
606433d6423SLionel Sambuc r = bdev_rdwt_setup(type, call->dev,
607433d6423SLionel Sambuc call->msg.m_lbdev_lblockdriver_msg.pos,
608433d6423SLionel Sambuc (char *) call->vec[0].iov_addr, call->msg.m_lbdev_lblockdriver_msg.count,
609433d6423SLionel Sambuc call->msg.m_lbdev_lblockdriver_msg.flags, &call->msg);
610433d6423SLionel Sambuc
611433d6423SLionel Sambuc break;
612433d6423SLionel Sambuc
613433d6423SLionel Sambuc case BDEV_GATHER:
614433d6423SLionel Sambuc case BDEV_SCATTER:
615433d6423SLionel Sambuc bdev_vrdwt_cleanup(&call->msg, call->gvec);
616433d6423SLionel Sambuc
617433d6423SLionel Sambuc r = bdev_vrdwt_setup(type, call->dev,
618433d6423SLionel Sambuc call->msg.m_lbdev_lblockdriver_msg.pos,
619433d6423SLionel Sambuc call->vec, call->msg.m_lbdev_lblockdriver_msg.count, call->msg.m_lbdev_lblockdriver_msg.flags,
620433d6423SLionel Sambuc &call->msg, call->gvec);
621433d6423SLionel Sambuc
622433d6423SLionel Sambuc break;
623433d6423SLionel Sambuc
624433d6423SLionel Sambuc case BDEV_IOCTL:
625433d6423SLionel Sambuc bdev_ioctl_cleanup(&call->msg);
626433d6423SLionel Sambuc
627433d6423SLionel Sambuc r = bdev_ioctl_setup(call->dev, call->msg.m_lbdev_lblockdriver_msg.request,
628433d6423SLionel Sambuc (char *) call->vec[0].iov_addr, call->msg.m_lbdev_lblockdriver_msg.user,
629433d6423SLionel Sambuc &call->msg);
630433d6423SLionel Sambuc
631433d6423SLionel Sambuc break;
632433d6423SLionel Sambuc
633433d6423SLionel Sambuc default:
634433d6423SLionel Sambuc assert(0);
635433d6423SLionel Sambuc }
636433d6423SLionel Sambuc
637433d6423SLionel Sambuc if (r != OK)
638433d6423SLionel Sambuc return r;
639433d6423SLionel Sambuc
640433d6423SLionel Sambuc /* Try to resend the request. */
641433d6423SLionel Sambuc return bdev_senda(call->dev, &call->msg, call->id);
642433d6423SLionel Sambuc }
643