Introduce the function iovec_copyin() and it's friend iovec_free().
[dragonfly.git] / sys / kern / kern_subr.c
1 /*
2  * Copyright (c) 1982, 1986, 1991, 1993
3  *      The Regents of the University of California.  All rights reserved.
4  * (c) UNIX System Laboratories, Inc.
5  * All or some portions of this file are derived from material licensed
6  * to the University of California by American Telephone and Telegraph
7  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
8  * the permission of UNIX System Laboratories, Inc.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *      This product includes software developed by the University of
21  *      California, Berkeley and its contributors.
22  * 4. Neither the name of the University nor the names of its contributors
23  *    may be used to endorse or promote products derived from this software
24  *    without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36  * SUCH DAMAGE.
37  *
38  *      @(#)kern_subr.c 8.3 (Berkeley) 1/21/94
39  * $FreeBSD: src/sys/kern/kern_subr.c,v 1.31.2.2 2002/04/21 08:09:37 bde Exp $
40  * $DragonFly: src/sys/kern/kern_subr.c,v 1.12 2003/10/08 01:30:32 daver Exp $
41  */
42
43 #include "opt_ddb.h"
44
45 #include <sys/param.h>
46 #include <sys/systm.h>
47 #include <sys/kernel.h>
48 #include <sys/proc.h>
49 #include <sys/malloc.h>
50 #include <sys/lock.h>
51 #include <sys/resourcevar.h>
52 #include <sys/vnode.h>
53 #include <machine/limits.h>
54
55 #include <vm/vm.h>
56 #include <vm/vm_page.h>
57 #include <vm/vm_map.h>
58
59 int
60 uiomove(cp, n, uio)
61         caddr_t cp;
62         int n;
63         struct uio *uio;
64 {
65         struct iovec *iov;
66         u_int cnt;
67         int error = 0;
68         int save = 0;
69         int baseticks = ticks;
70
71         KASSERT(uio->uio_rw == UIO_READ || uio->uio_rw == UIO_WRITE,
72             ("uiomove: mode"));
73         KASSERT(uio->uio_segflg != UIO_USERSPACE || uio->uio_td == curthread,
74             ("uiomove proc"));
75
76         if (curproc) {
77                 save = curproc->p_flag & P_DEADLKTREAT;
78                 curproc->p_flag |= P_DEADLKTREAT;
79         }
80
81         while (n > 0 && uio->uio_resid) {
82                 iov = uio->uio_iov;
83                 cnt = iov->iov_len;
84                 if (cnt == 0) {
85                         uio->uio_iov++;
86                         uio->uio_iovcnt--;
87                         continue;
88                 }
89                 if (cnt > n)
90                         cnt = n;
91
92                 switch (uio->uio_segflg) {
93
94                 case UIO_USERSPACE:
95                 case UIO_USERISPACE:
96                         if (ticks - baseticks >= hogticks) {
97                                 uio_yield();
98                                 baseticks = ticks;
99                         }
100                         if (uio->uio_rw == UIO_READ)
101                                 error = copyout(cp, iov->iov_base, cnt);
102                         else
103                                 error = copyin(iov->iov_base, cp, cnt);
104                         if (error)
105                                 break;
106                         break;
107
108                 case UIO_SYSSPACE:
109                         if (uio->uio_rw == UIO_READ)
110                                 bcopy((caddr_t)cp, iov->iov_base, cnt);
111                         else
112                                 bcopy(iov->iov_base, (caddr_t)cp, cnt);
113                         break;
114                 case UIO_NOCOPY:
115                         break;
116                 }
117                 iov->iov_base += cnt;
118                 iov->iov_len -= cnt;
119                 uio->uio_resid -= cnt;
120                 uio->uio_offset += cnt;
121                 cp += cnt;
122                 n -= cnt;
123         }
124         if (curproc)
125                 curproc->p_flag = (curproc->p_flag & ~P_DEADLKTREAT) | save;
126         return (error);
127 }
128 /*
129  * Wrapper for uiomove() that validates the arguments against a known-good
130  * kernel buffer.  Currently, uiomove accepts a signed (n) argument, which
131  * is almost definitely a bad thing, so we catch that here as well.  We
132  * return a runtime failure, but it might be desirable to generate a runtime
133  * assertion failure instead.
134  */
135 int
136 uiomove_frombuf(void *buf, int buflen, struct uio *uio)
137 {
138         unsigned int offset, n;
139
140         if (uio->uio_offset < 0 || uio->uio_resid < 0 ||
141             (offset = uio->uio_offset) != uio->uio_offset)
142                 return (EINVAL);
143         if (buflen <= 0 || offset >= buflen)
144                 return (0);
145         if ((n = buflen - offset) > INT_MAX)
146                 return (EINVAL);
147         return (uiomove((char *)buf + offset, n, uio));
148 }
149
150
151 int
152 uiomoveco(cp, n, uio, obj)
153         caddr_t cp;
154         int n;
155         struct uio *uio;
156         struct vm_object *obj;
157 {
158         struct iovec *iov;
159         u_int cnt;
160         int error;
161         int baseticks = ticks;
162
163         KASSERT(uio->uio_rw == UIO_READ || uio->uio_rw == UIO_WRITE,
164             ("uiomoveco: mode"));
165         KASSERT(uio->uio_segflg != UIO_USERSPACE || uio->uio_td == curthread,
166             ("uiomoveco proc"));
167
168         while (n > 0 && uio->uio_resid) {
169                 iov = uio->uio_iov;
170                 cnt = iov->iov_len;
171                 if (cnt == 0) {
172                         uio->uio_iov++;
173                         uio->uio_iovcnt--;
174                         continue;
175                 }
176                 if (cnt > n)
177                         cnt = n;
178
179                 switch (uio->uio_segflg) {
180
181                 case UIO_USERSPACE:
182                 case UIO_USERISPACE:
183                         if (ticks - baseticks >= hogticks) {
184                                 uio_yield();
185                                 baseticks = ticks;
186                         }
187                         if (uio->uio_rw == UIO_READ) {
188 #ifdef ENABLE_VFS_IOOPT
189                                 if (vfs_ioopt && ((cnt & PAGE_MASK) == 0) &&
190                                         ((((intptr_t) iov->iov_base) & PAGE_MASK) == 0) &&
191                                         ((uio->uio_offset & PAGE_MASK) == 0) &&
192                                         ((((intptr_t) cp) & PAGE_MASK) == 0)) {
193                                                 error = vm_uiomove(&curproc->p_vmspace->vm_map, obj,
194                                                                 uio->uio_offset, cnt,
195                                                                 (vm_offset_t) iov->iov_base, NULL);
196                                 } else
197 #endif
198                                 {
199                                         error = copyout(cp, iov->iov_base, cnt);
200                                 }
201                         } else {
202                                 error = copyin(iov->iov_base, cp, cnt);
203                         }
204                         if (error)
205                                 return (error);
206                         break;
207
208                 case UIO_SYSSPACE:
209                         if (uio->uio_rw == UIO_READ)
210                                 bcopy((caddr_t)cp, iov->iov_base, cnt);
211                         else
212                                 bcopy(iov->iov_base, (caddr_t)cp, cnt);
213                         break;
214                 case UIO_NOCOPY:
215                         break;
216                 }
217                 iov->iov_base += cnt;
218                 iov->iov_len -= cnt;
219                 uio->uio_resid -= cnt;
220                 uio->uio_offset += cnt;
221                 cp += cnt;
222                 n -= cnt;
223         }
224         return (0);
225 }
226
227 #ifdef ENABLE_VFS_IOOPT
228
229 int
230 uioread(n, uio, obj, nread)
231         int n;
232         struct uio *uio;
233         struct vm_object *obj;
234         int *nread;
235 {
236         int npagesmoved;
237         struct iovec *iov;
238         u_int cnt, tcnt;
239         int error;
240         int baseticks = ticks;
241
242         *nread = 0;
243         if (vfs_ioopt < 2)
244                 return 0;
245
246         error = 0;
247
248         while (n > 0 && uio->uio_resid) {
249                 iov = uio->uio_iov;
250                 cnt = iov->iov_len;
251                 if (cnt == 0) {
252                         uio->uio_iov++;
253                         uio->uio_iovcnt--;
254                         continue;
255                 }
256                 if (cnt > n)
257                         cnt = n;
258
259                 if ((uio->uio_segflg == UIO_USERSPACE) &&
260                         ((((intptr_t) iov->iov_base) & PAGE_MASK) == 0) &&
261                                  ((uio->uio_offset & PAGE_MASK) == 0) ) {
262
263                         if (cnt < PAGE_SIZE)
264                                 break;
265
266                         cnt &= ~PAGE_MASK;
267
268                         if (ticks - baseticks >= hogticks) {
269                                 uio_yield();
270                                 baseticks = ticks;
271                         }
272                         error = vm_uiomove(&curproc->p_vmspace->vm_map, obj,
273                                                 uio->uio_offset, cnt,
274                                                 (vm_offset_t) iov->iov_base, &npagesmoved);
275
276                         if (npagesmoved == 0)
277                                 break;
278
279                         tcnt = npagesmoved * PAGE_SIZE;
280                         cnt = tcnt;
281
282                         if (error)
283                                 break;
284
285                         iov->iov_base += cnt;
286                         iov->iov_len -= cnt;
287                         uio->uio_resid -= cnt;
288                         uio->uio_offset += cnt;
289                         *nread += cnt;
290                         n -= cnt;
291                 } else {
292                         break;
293                 }
294         }
295         return error;
296 }
297
298 #endif
299
300 /*
301  * Give next character to user as result of read.
302  */
303 int
304 ureadc(c, uio)
305         int c;
306         struct uio *uio;
307 {
308         struct iovec *iov;
309
310 again:
311         if (uio->uio_iovcnt == 0 || uio->uio_resid == 0)
312                 panic("ureadc");
313         iov = uio->uio_iov;
314         if (iov->iov_len == 0) {
315                 uio->uio_iovcnt--;
316                 uio->uio_iov++;
317                 goto again;
318         }
319         switch (uio->uio_segflg) {
320
321         case UIO_USERSPACE:
322                 if (subyte(iov->iov_base, c) < 0)
323                         return (EFAULT);
324                 break;
325
326         case UIO_SYSSPACE:
327                 *iov->iov_base = c;
328                 break;
329
330         case UIO_USERISPACE:
331                 if (suibyte(iov->iov_base, c) < 0)
332                         return (EFAULT);
333                 break;
334         case UIO_NOCOPY:
335                 break;
336         }
337         iov->iov_base++;
338         iov->iov_len--;
339         uio->uio_resid--;
340         uio->uio_offset++;
341         return (0);
342 }
343
344 #ifdef vax      /* unused except by ct.c, other oddities XXX */
345 /*
346  * Get next character written in by user from uio.
347  */
348 int
349 uwritec(uio)
350         struct uio *uio;
351 {
352         struct iovec *iov;
353         int c;
354
355         if (uio->uio_resid <= 0)
356                 return (-1);
357 again:
358         if (uio->uio_iovcnt <= 0)
359                 panic("uwritec");
360         iov = uio->uio_iov;
361         if (iov->iov_len == 0) {
362                 uio->uio_iov++;
363                 if (--uio->uio_iovcnt == 0)
364                         return (-1);
365                 goto again;
366         }
367         switch (uio->uio_segflg) {
368
369         case UIO_USERSPACE:
370                 c = fubyte(iov->iov_base);
371                 break;
372
373         case UIO_SYSSPACE:
374                 c = *(u_char *) iov->iov_base;
375                 break;
376
377         case UIO_USERISPACE:
378                 c = fuibyte(iov->iov_base);
379                 break;
380         }
381         if (c < 0)
382                 return (-1);
383         iov->iov_base++;
384         iov->iov_len--;
385         uio->uio_resid--;
386         uio->uio_offset++;
387         return (c);
388 }
389 #endif /* vax */
390
391 /*
392  * General routine to allocate a hash table.
393  */
394 void *
395 hashinit(elements, type, hashmask)
396         int elements;
397         struct malloc_type *type;
398         u_long *hashmask;
399 {
400         long hashsize;
401         LIST_HEAD(generic, generic) *hashtbl;
402         int i;
403
404         if (elements <= 0)
405                 panic("hashinit: bad elements");
406         for (hashsize = 1; hashsize <= elements; hashsize <<= 1)
407                 continue;
408         hashsize >>= 1;
409         hashtbl = malloc((u_long)hashsize * sizeof(*hashtbl), type, M_WAITOK);
410         for (i = 0; i < hashsize; i++)
411                 LIST_INIT(&hashtbl[i]);
412         *hashmask = hashsize - 1;
413         return (hashtbl);
414 }
415
416 static int primes[] = { 1, 13, 31, 61, 127, 251, 509, 761, 1021, 1531, 2039,
417                         2557, 3067, 3583, 4093, 4603, 5119, 5623, 6143, 6653,
418                         7159, 7673, 8191, 12281, 16381, 24571, 32749 };
419 #define NPRIMES (sizeof(primes) / sizeof(primes[0]))
420
421 /*
422  * General routine to allocate a prime number sized hash table.
423  */
424 void *
425 phashinit(elements, type, nentries)
426         int elements;
427         struct malloc_type *type;
428         u_long *nentries;
429 {
430         long hashsize;
431         LIST_HEAD(generic, generic) *hashtbl;
432         int i;
433
434         if (elements <= 0)
435                 panic("phashinit: bad elements");
436         for (i = 1, hashsize = primes[1]; hashsize <= elements;) {
437                 i++;
438                 if (i == NPRIMES)
439                         break;
440                 hashsize = primes[i];
441         }
442         hashsize = primes[i - 1];
443         hashtbl = malloc((u_long)hashsize * sizeof(*hashtbl), type, M_WAITOK);
444         for (i = 0; i < hashsize; i++)
445                 LIST_INIT(&hashtbl[i]);
446         *nentries = hashsize;
447         return (hashtbl);
448 }
449
450 /*
451  * Copyin an iovec.  If the iovec array fits, use the preallocated small
452  * iovec structure.  If it is too big, dynamically allocate an iovec array
453  * of sufficient size.
454  */
455 int
456 iovec_copyin(struct iovec *uiov, struct iovec **kiov, struct iovec *siov,
457     size_t iov_cnt, size_t *iov_len)
458 {
459         struct iovec *iovp;
460         int error, i;
461
462         if (iov_cnt >= UIO_MAXIOV)
463                 return EMSGSIZE;
464         if (iov_cnt >= UIO_SMALLIOV) {
465                 MALLOC(*kiov, struct iovec *, sizeof(struct iovec) * iov_cnt,
466                     M_IOV, M_WAITOK);
467         } else {
468                 *kiov = siov;
469         }
470         error = copyin(uiov, *kiov, iov_cnt * sizeof(struct iovec));
471         if (error)
472                 goto cleanup;
473         *iov_len = 0;
474         for (i = 0, iovp = *kiov; i < iov_cnt; i++, iovp++) {
475                 *iov_len += iovp->iov_len;
476                 if (iov_len < 0) {
477                         error = EINVAL;
478                         goto cleanup;
479                 }
480         }
481
482 cleanup:
483         if (error)
484                 iovec_free(kiov, siov);
485         return (error);
486 }