2 * This Cplant(TM) source code is the property of Sandia National
5 * This Cplant(TM) source code is copyrighted by Sandia National
8 * The redistribution of this Cplant(TM) source code is subject to the
9 * terms of the GNU Lesser General Public License
10 * (see cit/LGPL or http://www.gnu.org/licenses/lgpl.html)
12 * Cplant(TM) Copyright 1998-2004 Sandia Corporation.
13 * Under the terms of Contract DE-AC04-94AL85000, there is a non-exclusive
14 * license for use of this work by or on behalf of the US Government.
15 * Export of this program may require a license from the United States
20 * This library is free software; you can redistribute it and/or
21 * modify it under the terms of the GNU Lesser General Public
22 * License as published by the Free Software Foundation; either
23 * version 2.1 of the License, or (at your option) any later version.
25 * This library is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28 * Lesser General Public License for more details.
30 * You should have received a copy of the GNU Lesser General Public
31 * License along with this library; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
34 * Questions or comments about this library should be sent to:
37 * Sandia National Laboratories, New Mexico
39 * Albuquerque, NM 87185-1110
48 #include <sys/types.h>
50 #include <sys/queue.h>
57 * Asynchronous IO context support.
61 * Arguments to IO vector enumerator callback when used by _sysio_doio().
63 struct doio_helper_args {
64 ssize_t (*f)(void *, size_t, _SYSIO_OFF_T, void *); /* base func */
65 void *arg; /* caller arg */
69 * List of all outstanding (in-flight) asynch IO requests tracked
72 static LIST_HEAD( ,ioctx) aioq;
75 * Free callback entry.
77 #define cb_free(cb) free(cb)
80 * Initialization. Must be called before using any other routine in this
92 * Enter an IO context onto the async IO events queue.
95 _sysio_ioctx_enter(struct ioctx *ioctx)
98 LIST_INSERT_HEAD(&aioq, ioctx, ioctx_link);
102 * Allocate and initialize a new IO context.
105 _sysio_ioctx_new(struct inode *ino,
107 const struct iovec *iov,
109 const struct intnl_xtvec *xtv,
114 ioctx = malloc(sizeof(struct ioctx));
129 * Link request onto the outstanding requests queue.
131 _sysio_ioctx_enter(ioctx);
137 * Add an IO completion call-back to the end of the context call-back queue.
138 * These are called in iowait() as the last thing, right before the context
141 * They are called in order. Beware.
144 _sysio_ioctx_cb(struct ioctx *ioctx,
145 void (*f)(struct ioctx *, void *),
148 struct ioctx_callback *entry;
150 entry = malloc(sizeof(struct ioctx_callback));
155 entry->iocb_data = data;
157 TAILQ_INSERT_TAIL(&ioctx->ioctx_cbq, entry, iocb_next);
163 * Find an IO context given it's identifier.
165 * NB: This is dog-slow. If there are alot of these, we will need to change
166 * this implementation.
169 _sysio_ioctx_find(ioid_t id)
173 for (ioctx = aioq.lh_first; ioctx; ioctx = ioctx->ioctx_link.le_next)
174 if (ioctx->ioctx_id == id)
181 * Wait for asynchronous IO operation to complete, return status
182 * and dispose of the context.
185 * The context is no longer valid after return.
188 _sysio_ioctx_wait(struct ioctx *ioctx)
193 * Wait for async operation to complete.
195 while (!(ioctx->ioctx_done ||
196 (*ioctx->ioctx_ino->i_ops.inop_iodone)(ioctx)))
202 cc = ioctx->ioctx_cc;
204 cc = -ioctx->ioctx_errno;
209 _sysio_ioctx_complete(ioctx);
215 * Free callback entry.
218 _sysio_ioctx_cb_free(struct ioctx_callback *cb)
225 * Complete an asynchronous IO request.
228 _sysio_ioctx_complete(struct ioctx *ioctx)
230 struct ioctx_callback *entry;
233 * Run the call-back queue.
235 while ((entry = ioctx->ioctx_cbq.tqh_first)) {
236 TAILQ_REMOVE(&ioctx->ioctx_cbq, entry, iocb_next);
237 (*entry->iocb_f)(ioctx, entry->iocb_data);
242 * Unlink from the file record's outstanding request queue.
244 LIST_REMOVE(ioctx, ioctx_link);
246 if (ioctx->ioctx_fast)
249 I_RELE(ioctx->ioctx_ino);
255 * General help validating strided-IO vectors.
257 * A driver may call this to make sure underflow/overflow of an off_t can't
258 * occur and overflow of a ssize_t can't occur when writing. The sum
259 * of the reconciled transfer length is returned or some appropriate
260 * error depending on underflow/overflow.
262 * The following algorithm assumes:
264 * a) sizeof(size_t) >= sizeof(ssize_t)
265 * b) 2's complement arithmetic
266 * c) The compiler won't optimize away code because it's developers
267 * believed that something with an undefined result in `C' can't happen.
270 _sysio_validx(const struct intnl_xtvec *xtv, size_t xtvlen,
271 const struct iovec *iov, size_t iovlen,
276 struct intnl_xtvec xtvec;
279 if (!(xtvlen && iovlen))
283 xtvec.xtv_len = iovec.iov_len = 0;
285 while (!xtvec.xtv_len) {
293 if (xtvec.xtv_off < 0)
299 while (!iovec.iov_len) {
313 if ((size_t )cc > xtvec.xtv_len)
317 off = xtvec.xtv_off + cc;
318 if (xtvec.xtv_off && off <= xtvec.xtv_off)
319 return off < 0 ? -EINVAL : -EOVERFLOW;
324 if (acc && (cc <= acc))
327 } while (xtvec.xtv_len && iovlen);
328 } while ((xtvlen || xtvec.xtv_len) && iovlen);
335 _sysio_enumerate_extents(const struct intnl_xtvec *xtv, size_t xtvlen,
336 const struct iovec *iov, size_t iovlen,
337 ssize_t (*f)(const struct iovec *, int,
343 ssize_t acc, tmp, cc;
345 struct intnl_xtvec xtvec;
346 const struct iovec *start;
355 * Coalesce contiguous extent vector entries.
357 off = xtvec.xtv_off = xtv->xtv_off;
358 off += xtvec.xtv_len = xtv->xtv_len;
359 while (++xtv, --xtvlen) {
360 if (off != xtv->xtv_off) {
373 xtvec.xtv_len += xtv->xtv_len;
375 while (xtvec.xtv_len) {
378 if (iovec.iov_len > xtvec.xtv_len) {
379 iovec.iov_len = xtvec.xtv_len;
391 iovec.iov_base = (char *)iovec.iov_base + cc;
392 iovec.iov_len = tmp - cc;
394 if (acc && tmp <= acc)
395 abort(); /* paranoia */
401 if (iov->iov_len > n) {
413 if (iovec.iov_len > n) {
419 remain = xtvec.xtv_len - n;
421 (*f)(start, iov - start,
432 if (acc && tmp <= acc)
433 abort(); /* paranoia */
436 if (remain && !iovlen)
441 return acc; /* short */
451 _sysio_enumerate_iovec(const struct iovec *iov, size_t count,
454 ssize_t (*f)(void *, size_t, _SYSIO_OFF_T, void *),
467 for (indx = 0; n && indx < count; indx++) {
468 if (iov[indx].iov_len < n) {
469 cc = (ssize_t )iov[indx].iov_len;
478 if (acc && cc <= acc)
491 iov->iov_len < (size_t )limit
494 cc = (*f)(iov->iov_base, n, off, arg);
502 remain = iov->iov_len - cc;
504 if (acc && cc <= acc)
505 abort(); /* bad driver! */
507 if (remain || !limit)
508 break; /* short/limited read */
515 _sysio_doio_helper(const struct iovec *iov, int count,
518 struct doio_helper_args *args)
521 return _sysio_enumerate_iovec(iov, count,
528 * A meta-driver for the whole strided-io process. Appropriate when
529 * the driver can't handle anything but simple p{read,write}-like
533 _sysio_doio(const struct intnl_xtvec *xtv, size_t xtvlen,
534 const struct iovec *iov, size_t iovlen,
535 ssize_t (*f)(void *, size_t, _SYSIO_OFF_T, void *),
538 struct doio_helper_args arguments;
542 return _sysio_enumerate_extents(xtv, xtvlen,
544 (ssize_t (*)(const struct iovec *, int,
547 void *))_sysio_doio_helper,