xref: /minix3/minix/lib/libbdev/bdev.c (revision 7c48de6cc4c6d56f2277d378dba01dbac8a8c3b9)
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