Whamcloud - gitweb
This commit was generated by cvs2svn to compensate for changes in r46154,
[fs/lustre-release.git] / libsysio / src / reconcile.c
1 /*
2  *    This Cplant(TM) source code is the property of Sandia National
3  *    Laboratories.
4  *
5  *    This Cplant(TM) source code is copyrighted by Sandia National
6  *    Laboratories.
7  *
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)
11  *
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
16  *    Government.
17  */
18
19 /*
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.
24  * 
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.
29  * 
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
33  *
34  * Questions or comments about this library should be sent to:
35  *
36  * Lee Ward
37  * Sandia National Laboratories, New Mexico
38  * P.O. Box 5800
39  * Albuquerque, NM 87185-1110
40  *
41  * lee@sandia.gov
42  */
43
44 #include <stdlib.h>
45 #include <string.h>
46 #include <errno.h>
47 #include <assert.h>
48 #include <sys/uio.h>
49 #include <sys/types.h>
50 #include <sys/stat.h>
51 #include <sys/queue.h>
52
53 #include "sysio.h"
54 #include "xtio.h"
55
56 /*
57  * Extent-vector IO support.
58  */
59
60 /*
61  * Arguments to IO vector enumerator callback when used by _sysio_doio().
62  */
63 struct doio_helper_args {
64         ssize_t (*f)(void *, size_t, _SYSIO_OFF_T, void *);     /* base func */
65         void    *arg;                                           /* caller arg */
66 };
67
68 /*
69  * General help validating strided-IO vectors.
70  *
71  * A driver may call this to make sure underflow/overflow of an off_t can't
72  * occur and overflow of a ssize_t can't occur when writing. The sum
73  * of the reconciled transfer length is returned or some appropriate
74  * error depending on underflow/overflow.
75  *
76  * The following algorithm assumes:
77  *
78  * a) sizeof(size_t) >= sizeof(ssize_t)
79  * b) 2's complement arithmetic
80  * c) The compiler won't optimize away code because it's developers
81  *      believed that something with an undefined result in `C' can't happen.
82  */
83 ssize_t
84 _sysio_validx(const struct intnl_xtvec *xtv, size_t xtvlen,
85               const struct iovec *iov, size_t iovlen,
86               _SYSIO_OFF_T limit)
87 {
88         ssize_t acc, cc;
89         struct iovec iovec;
90         struct intnl_xtvec xtvec;
91         _SYSIO_OFF_T off;
92
93         if (!(xtvlen && iovlen))
94                 return -EINVAL;
95
96         acc = 0;
97         xtvec.xtv_len = iovec.iov_len = 0;
98         do {
99                 while (!xtvec.xtv_len) {
100                         if (!xtvlen--)
101                                 break;
102                         if (!xtv->xtv_len) {
103                                 xtv++;
104                                 continue;
105                         }
106                         xtvec = *xtv++;
107                         if (xtvec.xtv_off < 0)
108                                 return -EINVAL;
109                 }
110                 if (!xtvec.xtv_len)
111                         break;
112                 do {
113                         while (!iovec.iov_len) {
114                                 if (!iovlen--)
115                                         break;
116                                 if (!iov->iov_len) {
117                                         iov++;
118                                         continue;
119                                 }
120                                 iovec = *iov++;
121                         }
122                         if (!iovec.iov_len)
123                                 break;
124                         cc = iovec.iov_len;
125                         if (cc < 0)
126                                 return -EINVAL;
127                         if ((size_t )cc > xtvec.xtv_len)
128                                 cc = xtvec.xtv_len;
129                         xtvec.xtv_len -= cc;
130                         iovec.iov_len -= cc;
131                         off = xtvec.xtv_off + cc;
132                         if (xtvec.xtv_off && off <= xtvec.xtv_off)
133                                 return off < 0 ? -EINVAL : -EOVERFLOW;
134                         if (off > limit)
135                                 return -EFBIG;
136                         xtvec.xtv_off = off;
137                         cc += acc;
138                         if (acc && (cc <= acc))
139                                 return -EINVAL;
140                         acc = cc;
141                 } while (xtvec.xtv_len && iovlen);
142         } while ((xtvlen || xtvec.xtv_len) && iovlen);
143         return acc;
144 }
145
146 /*
147  */
148 ssize_t
149 _sysio_enumerate_extents(const struct intnl_xtvec *xtv, size_t xtvlen,
150                          const struct iovec *iov, size_t iovlen,
151                          ssize_t (*f)(const struct iovec *, int,
152                                       _SYSIO_OFF_T,
153                                       ssize_t,
154                                       void *),
155                          void *arg)
156 {
157         ssize_t acc, tmp, cc;
158         struct iovec iovec;
159         struct intnl_xtvec xtvec;
160         const struct iovec *start;
161         _SYSIO_OFF_T off;
162         size_t  n;
163         size_t  remain;
164         
165         acc = 0;
166         iovec.iov_len = 0;
167         while (xtvlen) {
168                 /*
169                  * Coalesce contiguous extent vector entries.
170                  */
171                 off = xtvec.xtv_off = xtv->xtv_off;
172                 off += xtvec.xtv_len = xtv->xtv_len;
173                 while (++xtv, --xtvlen) {
174                         if (off != xtv->xtv_off) {
175                                 /*
176                                  * Not contiguous.
177                                  */
178                                 break;
179                         }
180                         if (!xtv->xtv_len) {
181                                 /*
182                                  * Zero length.
183                                  */
184                                 continue;
185                         }
186                         off += xtv->xtv_len;
187                         xtvec.xtv_len += xtv->xtv_len;
188                 }
189                 while (xtvec.xtv_len) {
190                         if (iovec.iov_len) {
191                                 tmp = iovec.iov_len; 
192                                 if (iovec.iov_len > xtvec.xtv_len)
193                                         iovec.iov_len = xtvec.xtv_len;
194                                 cc =
195                                     (*f)(&iovec, 1,
196                                          xtvec.xtv_off,
197                                          xtvec.xtv_len,
198                                          arg);
199                                 if (cc <= 0) {
200                                         if (acc)
201                                                 return acc;
202                                         return cc;
203                                 }
204                                 iovec.iov_base = (char *)iovec.iov_base + cc;
205                                 iovec.iov_len = tmp - cc; 
206                                 tmp = cc + acc;
207                                 if (acc && tmp <= acc)
208                                         abort();                /* paranoia */
209                                 acc = tmp;
210                         } else if (iovlen) {
211                                 start = iov;
212                                 n = xtvec.xtv_len;
213                                 do {
214                                         if (iov->iov_len > n) {
215                                                 /*
216                                                  * That'll do.
217                                                  */
218                                                 break;
219                                         }
220                                         n -= iov->iov_len;
221                                         iov++;
222                                 } while (--iovlen);
223                                 if (iov == start) {
224                                         iovec = *iov++;
225                                         iovlen--;
226                                         continue;
227                                 }
228                                 remain = xtvec.xtv_len - n;
229                                 cc =
230                                     (*f)(start, iov - start,
231                                          xtvec.xtv_off,
232                                          remain,
233                                          arg);
234                                 if (cc <= 0) {
235                                         if (acc)
236                                                 return acc;
237                                         return cc;
238                                 }
239                                                                 
240                                 tmp = cc + acc;
241                                 if (acc && tmp <= acc)
242                                         abort();                /* paranoia */
243                                 acc = tmp;
244
245                                 remain -= cc;
246                                 if (remain)
247                                         return acc;             /* short */
248                         } else
249                                 return acc;                     /* short out */
250                         xtvec.xtv_off += cc;
251                         xtvec.xtv_len -= cc;
252                 }
253         }
254         return acc;
255 }
256
257 ssize_t
258 _sysio_enumerate_iovec(const struct iovec *iov, size_t count,
259                        _SYSIO_OFF_T off,
260                        ssize_t limit,
261                        ssize_t (*f)(void *, size_t, _SYSIO_OFF_T, void *),
262                        void *arg)
263 {
264         ssize_t acc, cc;
265         size_t  n;
266         unsigned indx;
267         size_t  remain;
268
269         if (!count)
270                 return -EINVAL;
271         assert(limit >= 0);
272         acc = 0;
273         n = limit;
274         for (indx = 0; n && indx < count; indx++) {
275                 if (iov[indx].iov_len < n) {
276                         cc = (ssize_t )iov[indx].iov_len;
277                         if (cc < 0)
278                                 return -EINVAL;
279                 } else
280                         cc = (ssize_t )n;
281                 if (!cc)
282                         continue;
283                 n -= cc;
284                 cc += acc;
285                 if (acc && cc <= acc)
286                         return -EINVAL;
287                 acc = cc;
288         }
289         if (!acc)
290                 return 0;
291         acc = 0;
292         do {
293                 if (!iov->iov_len) {
294                         iov++;
295                         continue;
296                 }
297                 n =
298                     iov->iov_len < (size_t )limit
299                       ? iov->iov_len
300                       : (size_t )limit;
301                 cc = (*f)(iov->iov_base, n, off, arg);
302                 if (cc <= 0) {
303                         if (acc)
304                                 return acc;
305                         return cc;
306                 }
307                 off += cc;
308                 limit -= cc;
309                 remain = iov->iov_len - cc;
310                 cc += acc;
311                 if (acc && cc <= acc)
312                         abort();                        /* bad driver! */
313                 acc = cc;
314                 if (remain || !limit)
315                         break;                          /* short/limited read */
316                 iov++;
317         } while (--count);
318         return acc;
319 }
320
321 static ssize_t
322 _sysio_doio_helper(const struct iovec *iov, int count,
323                    _SYSIO_OFF_T off,
324                    ssize_t limit,
325                    struct doio_helper_args *args)
326 {
327
328         return _sysio_enumerate_iovec(iov, count,
329                                       off, limit,
330                                       args->f,
331                                       args->arg);
332 }
333
334 /*
335  * A meta-driver for the whole strided-io process. Appropriate when
336  * the driver can't handle anything but simple p{read,write}-like
337  * interface.
338  */
339 ssize_t
340 _sysio_doio(const struct intnl_xtvec *xtv, size_t xtvlen,
341             const struct iovec *iov, size_t iovlen,
342             ssize_t (*f)(void *, size_t, _SYSIO_OFF_T, void *),
343             void *arg)
344 {
345         struct doio_helper_args arguments;
346
347         arguments.f = f;
348         arguments.arg = arg;
349         return _sysio_enumerate_extents(xtv, xtvlen,
350                                         iov, iovlen,
351                                         (ssize_t (*)(const struct iovec *, int,
352                                                      _SYSIO_OFF_T,
353                                                      ssize_t,
354                                                      void *))_sysio_doio_helper,
355                                         &arguments);
356 }