hammer2 - Increase bulkfree buffer size request and cap
[dragonfly.git] / sys / vfs / ufs / ufs_quota.c
blob513fc234cfc23dc6d9fe339b4780ae965bff0c79
1 /*
2 * Copyright (c) 1982, 1986, 1990, 1993, 1995
3 * The Regents of the University of California. All rights reserved.
5 * This code is derived from software contributed to Berkeley by
6 * Robert Elz at The University of Melbourne.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
32 * @(#)ufs_quota.c 8.5 (Berkeley) 5/20/95
33 * $FreeBSD: src/sys/ufs/ufs/ufs_quota.c,v 1.27.2.3 2002/01/15 10:33:32 phk Exp $
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/kernel.h>
39 #include <sys/malloc.h>
40 #include <sys/fcntl.h>
41 #include <sys/proc.h>
42 #include <sys/nlookup.h>
43 #include <sys/vnode.h>
44 #include <sys/mount.h>
45 #include <vm/vm_zone.h>
47 #include "quota.h"
48 #include "inode.h"
49 #include "ufsmount.h"
51 static MALLOC_DEFINE(M_DQUOT, "UFS quota", "UFS quota entries");
54 * Quota name to error message mapping.
56 static char *quotatypes[] = INITQFNAMES;
58 static int ufs_chkdqchg (struct inode *, long, struct ucred *, int);
59 static int ufs_chkiqchg (struct inode *, long, struct ucred *, int);
60 static int ufs_dqget (struct vnode *,
61 u_long, struct ufsmount *, int, struct ufs_dquot **);
62 static int ufs_dqsync (struct vnode *, struct ufs_dquot *);
63 static void ufs_dqflush (struct vnode *);
64 static void ufs_quotawarn(struct ufs_dquot *dq);
66 #ifdef DIAGNOSTIC
67 static void ufs_dqref (struct ufs_dquot *);
68 static void ufs_chkdquot (struct inode *);
69 #endif
72 * Set up the quotas for an inode.
74 * This routine completely defines the semantics of quotas.
75 * If other criterion want to be used to establish quotas, the
76 * MAXQUOTAS value in quotas.h should be increased, and the
77 * additional dquots set up here.
79 int
80 ufs_getinoquota(struct inode *ip)
82 struct ufsmount *ump;
83 struct vnode *vp = ITOV(ip);
84 int error;
86 ump = VFSTOUFS(vp->v_mount);
88 * Set up the user quota based on file uid.
89 * EINVAL means that quotas are not enabled.
91 if (ip->i_dquot[USRQUOTA] == NODQUOT &&
92 (error = ufs_dqget(vp, ip->i_uid, ump, USRQUOTA, &ip->i_dquot[USRQUOTA])) &&
93 error != EINVAL)
94 return (error);
96 * Set up the group quota based on file gid.
97 * EINVAL means that quotas are not enabled.
99 if (ip->i_dquot[GRPQUOTA] == NODQUOT &&
100 (error = ufs_dqget(vp, ip->i_gid, ump, GRPQUOTA, &ip->i_dquot[GRPQUOTA])) &&
101 error != EINVAL)
102 return (error);
103 return (0);
107 * Update disk usage, and take corrective action.
110 ufs_chkdq(struct inode *ip, long change, struct ucred *cred, int flags)
112 struct ufs_dquot *dq;
113 int i;
114 int ncurblocks, error;
116 #ifdef DIAGNOSTIC
117 if ((flags & CHOWN) == 0)
118 ufs_chkdquot(ip);
119 #endif
120 if (change == 0)
121 return (0);
122 if (change < 0) {
123 for (i = 0; i < MAXQUOTAS; i++) {
124 if ((dq = ip->i_dquot[i]) == NODQUOT)
125 continue;
126 if (dq->dq_ump->um_quotas[dq->dq_type] == ip->i_vnode) {
127 ufs_quotawarn(dq);
128 continue;
130 while (dq->dq_flags & DQ_LOCK) {
131 dq->dq_flags |= DQ_WANT;
132 (void) tsleep((caddr_t)dq, 0, "chkdq1", 0);
134 ncurblocks = dq->dq_curblocks + change;
135 if (ncurblocks >= 0)
136 dq->dq_curblocks = ncurblocks;
137 else
138 dq->dq_curblocks = 0;
139 dq->dq_flags &= ~DQ_BLKS;
140 dq->dq_flags |= DQ_MOD;
142 return (0);
144 if ((flags & FORCE) == 0 && cred->cr_uid != 0) {
145 for (i = 0; i < MAXQUOTAS; i++) {
146 if ((dq = ip->i_dquot[i]) == NODQUOT)
147 continue;
148 if (dq->dq_ump->um_quotas[dq->dq_type] == ip->i_vnode) {
149 ufs_quotawarn(dq);
150 continue;
152 error = ufs_chkdqchg(ip, change, cred, i);
153 if (error)
154 return (error);
157 for (i = 0; i < MAXQUOTAS; i++) {
158 if ((dq = ip->i_dquot[i]) == NODQUOT)
159 continue;
160 if (dq->dq_ump->um_quotas[dq->dq_type] == ip->i_vnode) {
161 ufs_quotawarn(dq);
162 continue;
164 while (dq->dq_flags & DQ_LOCK) {
165 dq->dq_flags |= DQ_WANT;
166 (void) tsleep((caddr_t)dq, 0, "chkdq2", 0);
168 /* Reset timer when crossing soft limit */
169 if (dq->dq_curblocks + change >= dq->dq_bsoftlimit &&
170 dq->dq_curblocks < dq->dq_bsoftlimit)
171 dq->dq_btime = time_second +
172 VFSTOUFS(ITOV(ip)->v_mount)->um_btime[i];
173 dq->dq_curblocks += change;
174 dq->dq_flags |= DQ_MOD;
176 return (0);
180 * Check for a valid change to a users allocation.
181 * Issue an error message if appropriate.
183 static int
184 ufs_chkdqchg(struct inode *ip, long change, struct ucred *cred, int type)
186 struct ufs_dquot *dq = ip->i_dquot[type];
187 long ncurblocks = dq->dq_curblocks + change;
190 * If user would exceed their hard limit, disallow space allocation.
192 if (ncurblocks >= dq->dq_bhardlimit && dq->dq_bhardlimit) {
193 if ((dq->dq_flags & DQ_BLKS) == 0 &&
194 ip->i_uid == cred->cr_uid) {
195 uprintf("\n%s: write failed, %s disk limit reached\n",
196 ITOV(ip)->v_mount->mnt_stat.f_mntfromname,
197 quotatypes[type]);
198 dq->dq_flags |= DQ_BLKS;
200 return (EDQUOT);
203 * If user is over their soft limit for too long, disallow space
204 * allocation. Reset time limit as they cross their soft limit.
206 if (ncurblocks >= dq->dq_bsoftlimit && dq->dq_bsoftlimit) {
207 if (dq->dq_curblocks < dq->dq_bsoftlimit) {
208 dq->dq_btime = time_second +
209 VFSTOUFS(ITOV(ip)->v_mount)->um_btime[type];
210 if (ip->i_uid == cred->cr_uid)
211 uprintf("\n%s: warning, %s %s\n",
212 ITOV(ip)->v_mount->mnt_stat.f_mntfromname,
213 quotatypes[type], "disk quota exceeded");
214 return (0);
216 if (time_second > dq->dq_btime) {
217 if ((dq->dq_flags & DQ_BLKS) == 0 &&
218 ip->i_uid == cred->cr_uid) {
219 uprintf("\n%s: write failed, %s %s\n",
220 ITOV(ip)->v_mount->mnt_stat.f_mntfromname,
221 quotatypes[type],
222 "disk quota exceeded for too long");
223 dq->dq_flags |= DQ_BLKS;
225 return (EDQUOT);
228 return (0);
232 * Check the inode limit, applying corrective action.
235 ufs_chkiq(struct inode *ip, long change, struct ucred *cred, int flags)
237 struct ufs_dquot *dq;
238 int i;
239 int ncurinodes, error;
241 #ifdef DIAGNOSTIC
242 if ((flags & CHOWN) == 0)
243 ufs_chkdquot(ip);
244 #endif
245 if (change == 0)
246 return (0);
247 if (change < 0) {
248 for (i = 0; i < MAXQUOTAS; i++) {
249 if ((dq = ip->i_dquot[i]) == NODQUOT)
250 continue;
251 if (dq->dq_ump->um_quotas[dq->dq_type] == ip->i_vnode) {
252 ufs_quotawarn(dq);
253 continue;
255 while (dq->dq_flags & DQ_LOCK) {
256 dq->dq_flags |= DQ_WANT;
257 (void) tsleep((caddr_t)dq, 0, "chkiq1", 0);
259 ncurinodes = dq->dq_curinodes + change;
260 if (ncurinodes >= 0)
261 dq->dq_curinodes = ncurinodes;
262 else
263 dq->dq_curinodes = 0;
264 dq->dq_flags &= ~DQ_INODS;
265 dq->dq_flags |= DQ_MOD;
267 return (0);
269 if ((flags & FORCE) == 0 && cred->cr_uid != 0) {
270 for (i = 0; i < MAXQUOTAS; i++) {
271 if ((dq = ip->i_dquot[i]) == NODQUOT)
272 continue;
273 if (dq->dq_ump->um_quotas[dq->dq_type] == ip->i_vnode) {
274 ufs_quotawarn(dq);
275 continue;
277 error = ufs_chkiqchg(ip, change, cred, i);
278 if (error)
279 return (error);
282 for (i = 0; i < MAXQUOTAS; i++) {
283 if ((dq = ip->i_dquot[i]) == NODQUOT)
284 continue;
285 if (dq->dq_ump->um_quotas[dq->dq_type] == ip->i_vnode) {
286 ufs_quotawarn(dq);
287 continue;
289 while (dq->dq_flags & DQ_LOCK) {
290 dq->dq_flags |= DQ_WANT;
291 (void) tsleep((caddr_t)dq, 0, "chkiq2", 0);
293 /* Reset timer when crossing soft limit */
294 if (dq->dq_curinodes + change >= dq->dq_isoftlimit &&
295 dq->dq_curinodes < dq->dq_isoftlimit)
296 dq->dq_itime = time_second +
297 VFSTOUFS(ITOV(ip)->v_mount)->um_itime[i];
298 dq->dq_curinodes += change;
299 dq->dq_flags |= DQ_MOD;
301 return (0);
305 * Check for a valid change to a users allocation.
306 * Issue an error message if appropriate.
308 static int
309 ufs_chkiqchg(struct inode *ip, long change, struct ucred *cred, int type)
311 struct ufs_dquot *dq = ip->i_dquot[type];
312 long ncurinodes = dq->dq_curinodes + change;
315 * If user would exceed their hard limit, disallow inode allocation.
317 if (ncurinodes >= dq->dq_ihardlimit && dq->dq_ihardlimit) {
318 if ((dq->dq_flags & DQ_INODS) == 0 &&
319 ip->i_uid == cred->cr_uid) {
320 uprintf("\n%s: write failed, %s inode limit reached\n",
321 ITOV(ip)->v_mount->mnt_stat.f_mntfromname,
322 quotatypes[type]);
323 dq->dq_flags |= DQ_INODS;
325 return (EDQUOT);
328 * If user is over their soft limit for too long, disallow inode
329 * allocation. Reset time limit as they cross their soft limit.
331 if (ncurinodes >= dq->dq_isoftlimit && dq->dq_isoftlimit) {
332 if (dq->dq_curinodes < dq->dq_isoftlimit) {
333 dq->dq_itime = time_second +
334 VFSTOUFS(ITOV(ip)->v_mount)->um_itime[type];
335 if (ip->i_uid == cred->cr_uid)
336 uprintf("\n%s: warning, %s %s\n",
337 ITOV(ip)->v_mount->mnt_stat.f_mntfromname,
338 quotatypes[type], "inode quota exceeded");
339 return (0);
341 if (time_second > dq->dq_itime) {
342 if ((dq->dq_flags & DQ_INODS) == 0 &&
343 ip->i_uid == cred->cr_uid) {
344 uprintf("\n%s: write failed, %s %s\n",
345 ITOV(ip)->v_mount->mnt_stat.f_mntfromname,
346 quotatypes[type],
347 "inode quota exceeded for too long");
348 dq->dq_flags |= DQ_INODS;
350 return (EDQUOT);
353 return (0);
357 * To avoid a deadlock we disallow quota operations on the quota file itself.
358 * This generally means that quotacheck was not run on the filesystem.
360 static
361 void
362 ufs_quotawarn(struct ufs_dquot *dq)
364 static int dqticks;
366 if (dqticks != ticks / hz) {
367 dqticks = ticks / hz;
368 uprintf("%s: warning, quota file expanded, quotacheck "
369 "was not run!\n",
370 dq->dq_ump->um_mountp->mnt_stat.f_mntfromname);
374 #ifdef DIAGNOSTIC
376 * On filesystems with quotas enabled, it is an error for a file to change
377 * size and not to have a dquot structure associated with it.
379 static void
380 ufs_chkdquot(struct inode *ip)
382 struct ufsmount *ump = VFSTOUFS(ITOV(ip)->v_mount);
383 int i;
385 for (i = 0; i < MAXQUOTAS; i++) {
386 if (ump->um_quotas[i] == NULLVP ||
387 (ump->um_qflags[i] & (QTF_OPENING|QTF_CLOSING)))
388 continue;
389 if (ip->i_dquot[i] == NODQUOT) {
390 vprint("chkdquot: missing dquot", ITOV(ip));
391 panic("chkdquot: missing dquot");
395 #endif
398 * Code to process quotactl commands.
401 struct scaninfo {
402 int rescan;
403 int type;
407 * Q_QUOTAON - set up a quota file for a particular filesystem.
409 static int ufs_quotaon_scan(struct mount *mp, struct vnode *vp, void *data);
412 ufs_quotaon(struct ucred *cred, struct mount *mp, int type, caddr_t fname)
414 struct ufsmount *ump = VFSTOUFS(mp);
415 struct vnode *vp, **vpp;
416 struct ufs_dquot *dq;
417 int error;
418 struct nlookupdata nd;
419 struct scaninfo scaninfo;
421 vpp = &ump->um_quotas[type];
422 error = nlookup_init(&nd, fname, UIO_USERSPACE, NLC_FOLLOW|NLC_LOCKVP);
423 if (error == 0)
424 error = vn_open(&nd, NULL, FREAD|FWRITE, 0);
425 if (error == 0 && nd.nl_open_vp->v_type != VREG)
426 error = EACCES;
427 if (error) {
428 nlookup_done(&nd);
429 return (error);
431 vp = nd.nl_open_vp;
432 nd.nl_open_vp = NULL;
433 nlookup_done(&nd);
435 vn_unlock(vp);
436 if (*vpp != vp)
437 ufs_quotaoff(mp, type);
438 ump->um_qflags[type] |= QTF_OPENING;
439 mp->mnt_flag |= MNT_QUOTA;
440 vsetflags(vp, VSYSTEM);
441 *vpp = vp;
442 /* XXX release duplicate vp if *vpp == vp? */
444 * Save the credential of the process that turned on quotas.
445 * Set up the time limits for this quota.
447 ump->um_cred[type] = crhold(cred);
448 ump->um_btime[type] = MAX_DQ_TIME;
449 ump->um_itime[type] = MAX_IQ_TIME;
450 if (ufs_dqget(NULLVP, 0, ump, type, &dq) == 0) {
451 if (dq->dq_btime > 0)
452 ump->um_btime[type] = dq->dq_btime;
453 if (dq->dq_itime > 0)
454 ump->um_itime[type] = dq->dq_itime;
455 ufs_dqrele(NULLVP, dq);
458 * Search vnodes associated with this mount point,
459 * adding references to quota file being opened.
460 * NB: only need to add dquot's for inodes being modified.
462 scaninfo.rescan = 1;
463 while (scaninfo.rescan) {
464 scaninfo.rescan = 0;
465 error = vmntvnodescan(mp, VMSC_GETVP,
466 NULL, ufs_quotaon_scan, &scaninfo);
467 if (error)
468 break;
470 ump->um_qflags[type] &= ~QTF_OPENING;
471 if (error)
472 ufs_quotaoff(mp, type);
473 return (error);
476 static int
477 ufs_quotaon_scan(struct mount *mp, struct vnode *vp, void *data)
479 int error;
480 /*struct scaninfo *info = data;*/
482 if (vp->v_writecount == 0)
483 return(0);
484 error = ufs_getinoquota(VTOI(vp));
485 return(error);
489 * Q_QUOTAOFF - turn off disk quotas for a filesystem.
492 static int ufs_quotaoff_scan(struct mount *mp, struct vnode *vp, void *data);
495 ufs_quotaoff(struct mount *mp, int type)
497 struct vnode *qvp;
498 struct ufsmount *ump = VFSTOUFS(mp);
499 int error;
500 struct scaninfo scaninfo;
502 if ((qvp = ump->um_quotas[type]) == NULLVP)
503 return (0);
504 ump->um_qflags[type] |= QTF_CLOSING;
507 * Search vnodes associated with this mount point,
508 * deleting any references to quota file being closed.
510 scaninfo.rescan = 1;
511 scaninfo.type = type;
512 while (scaninfo.rescan) {
513 scaninfo.rescan = 0;
514 vmntvnodescan(mp, VMSC_GETVP, NULL, ufs_quotaoff_scan, &scaninfo);
516 ufs_dqflush(qvp);
517 vclrflags(qvp, VSYSTEM);
518 error = vn_close(qvp, FREAD|FWRITE, NULL);
519 ump->um_quotas[type] = NULLVP;
520 crfree(ump->um_cred[type]);
521 ump->um_cred[type] = NOCRED;
522 ump->um_qflags[type] &= ~QTF_CLOSING;
523 for (type = 0; type < MAXQUOTAS; type++) {
524 if (ump->um_quotas[type] != NULLVP)
525 break;
527 if (type == MAXQUOTAS)
528 mp->mnt_flag &= ~MNT_QUOTA;
529 return (error);
532 static int
533 ufs_quotaoff_scan(struct mount *mp, struct vnode *vp, void *data)
535 struct scaninfo *info = data;
536 struct ufs_dquot *dq;
537 struct inode *ip;
539 if (vp->v_type == VNON) {
540 return(0);
542 ip = VTOI(vp);
543 dq = ip->i_dquot[info->type];
544 ip->i_dquot[info->type] = NODQUOT;
545 ufs_dqrele(vp, dq);
546 return(0);
550 * Q_GETQUOTA - return current values in a dqblk structure.
553 ufs_getquota(struct mount *mp, u_long id, int type, caddr_t addr)
555 struct ufs_dquot *dq;
556 int error;
558 error = ufs_dqget(NULLVP, id, VFSTOUFS(mp), type, &dq);
559 if (error)
560 return (error);
561 error = copyout((caddr_t)&dq->dq_dqb, addr, sizeof (struct ufs_dqblk));
562 ufs_dqrele(NULLVP, dq);
563 return (error);
567 * Q_SETQUOTA - assign an entire dqblk structure.
570 ufs_setquota(struct mount *mp, u_long id, int type, caddr_t addr)
572 struct ufs_dquot *dq;
573 struct ufs_dquot *ndq;
574 struct ufsmount *ump = VFSTOUFS(mp);
575 struct ufs_dqblk newlim;
576 int error;
578 error = copyin(addr, (caddr_t)&newlim, sizeof (struct ufs_dqblk));
579 if (error)
580 return (error);
581 error = ufs_dqget(NULLVP, id, ump, type, &ndq);
582 if (error)
583 return (error);
584 dq = ndq;
585 while (dq->dq_flags & DQ_LOCK) {
586 dq->dq_flags |= DQ_WANT;
587 (void) tsleep((caddr_t)dq, 0, "setqta", 0);
590 * Copy all but the current values.
591 * Reset time limit if previously had no soft limit or were
592 * under it, but now have a soft limit and are over it.
594 newlim.dqb_curblocks = dq->dq_curblocks;
595 newlim.dqb_curinodes = dq->dq_curinodes;
596 if (dq->dq_id != 0) {
597 newlim.dqb_btime = dq->dq_btime;
598 newlim.dqb_itime = dq->dq_itime;
600 if (newlim.dqb_bsoftlimit &&
601 dq->dq_curblocks >= newlim.dqb_bsoftlimit &&
602 (dq->dq_bsoftlimit == 0 || dq->dq_curblocks < dq->dq_bsoftlimit))
603 newlim.dqb_btime = time_second + ump->um_btime[type];
604 if (newlim.dqb_isoftlimit &&
605 dq->dq_curinodes >= newlim.dqb_isoftlimit &&
606 (dq->dq_isoftlimit == 0 || dq->dq_curinodes < dq->dq_isoftlimit))
607 newlim.dqb_itime = time_second + ump->um_itime[type];
608 dq->dq_dqb = newlim;
609 if (dq->dq_curblocks < dq->dq_bsoftlimit)
610 dq->dq_flags &= ~DQ_BLKS;
611 if (dq->dq_curinodes < dq->dq_isoftlimit)
612 dq->dq_flags &= ~DQ_INODS;
613 if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
614 dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
615 dq->dq_flags |= DQ_FAKE;
616 else
617 dq->dq_flags &= ~DQ_FAKE;
618 dq->dq_flags |= DQ_MOD;
619 ufs_dqrele(NULLVP, dq);
620 return (0);
624 * Q_SETUSE - set current inode and block usage.
627 ufs_setuse(struct mount *mp, u_long id, int type, caddr_t addr)
629 struct ufs_dquot *dq;
630 struct ufsmount *ump = VFSTOUFS(mp);
631 struct ufs_dquot *ndq;
632 struct ufs_dqblk usage;
633 int error;
635 error = copyin(addr, (caddr_t)&usage, sizeof (struct ufs_dqblk));
636 if (error)
637 return (error);
638 error = ufs_dqget(NULLVP, id, ump, type, &ndq);
639 if (error)
640 return (error);
641 dq = ndq;
642 while (dq->dq_flags & DQ_LOCK) {
643 dq->dq_flags |= DQ_WANT;
644 (void) tsleep((caddr_t)dq, 0, "setuse", 0);
647 * Reset time limit if have a soft limit and were
648 * previously under it, but are now over it.
650 if (dq->dq_bsoftlimit && dq->dq_curblocks < dq->dq_bsoftlimit &&
651 usage.dqb_curblocks >= dq->dq_bsoftlimit)
652 dq->dq_btime = time_second + ump->um_btime[type];
653 if (dq->dq_isoftlimit && dq->dq_curinodes < dq->dq_isoftlimit &&
654 usage.dqb_curinodes >= dq->dq_isoftlimit)
655 dq->dq_itime = time_second + ump->um_itime[type];
656 dq->dq_curblocks = usage.dqb_curblocks;
657 dq->dq_curinodes = usage.dqb_curinodes;
658 if (dq->dq_curblocks < dq->dq_bsoftlimit)
659 dq->dq_flags &= ~DQ_BLKS;
660 if (dq->dq_curinodes < dq->dq_isoftlimit)
661 dq->dq_flags &= ~DQ_INODS;
662 dq->dq_flags |= DQ_MOD;
663 ufs_dqrele(NULLVP, dq);
664 return (0);
668 * Q_SYNC - sync quota files to disk.
671 static int ufs_qsync_scan(struct mount *mp, struct vnode *vp, void *data);
674 ufs_qsync(struct mount *mp)
676 struct ufsmount *ump = VFSTOUFS(mp);
677 struct scaninfo scaninfo;
678 int i;
681 * Check if the mount point has any quotas.
682 * If not, simply return.
684 for (i = 0; i < MAXQUOTAS; i++)
685 if (ump->um_quotas[i] != NULLVP)
686 break;
687 if (i == MAXQUOTAS)
688 return (0);
690 * Search vnodes associated with this mount point,
691 * synchronizing any modified ufs_dquot structures.
693 scaninfo.rescan = 1;
694 while (scaninfo.rescan) {
695 scaninfo.rescan = 0;
696 vmntvnodescan(mp, VMSC_GETVP|VMSC_NOWAIT,
697 NULL, ufs_qsync_scan, &scaninfo);
699 return (0);
702 static int
703 ufs_qsync_scan(struct mount *mp, struct vnode *vp, void *data)
705 /*struct scaninfo *info = data;*/
706 struct ufs_dquot *dq;
707 /* int error;*/
708 int i;
710 for (i = 0; i < MAXQUOTAS; i++) {
711 dq = VTOI(vp)->i_dquot[i];
712 if (dq != NODQUOT && (dq->dq_flags & DQ_MOD))
713 ufs_dqsync(vp, dq);
715 return(0);
719 * Code pertaining to management of the in-core dquot data structures.
721 #define DQHASH(dqvp, id) \
722 (&ufs_dqhashtbl[((((intptr_t)(dqvp)) >> 8) + id) & ufs_dqhash])
723 static LIST_HEAD(ufs_dqhash, ufs_dquot) *ufs_dqhashtbl;
724 static u_long ufs_dqhash;
727 * Dquot free list.
729 #define DQUOTINC 5 /* minimum free dquots desired */
730 static TAILQ_HEAD(ufs_dqfreelist, ufs_dquot) ufs_dqfreelist;
731 static long ufs_numdquot, ufs_desireddquot = DQUOTINC;
734 * Initialize the quota system.
736 void
737 ufs_dqinit(void)
739 int hsize = vfs_inodehashsize();
741 ufs_dqhashtbl = hashinit(hsize, M_DQUOT, &ufs_dqhash);
742 TAILQ_INIT(&ufs_dqfreelist);
746 * Obtain a dquot structure for the specified identifier and quota file
747 * reading the information from the file if necessary.
749 static int
750 ufs_dqget(struct vnode *vp, u_long id, struct ufsmount *ump, int type,
751 struct ufs_dquot **dqp)
753 struct ufs_dquot *dq;
754 struct ufs_dqhash *dqh;
755 struct vnode *dqvp;
756 struct iovec aiov;
757 struct uio auio;
758 int error;
760 dqvp = ump->um_quotas[type];
761 if (dqvp == NULLVP || (ump->um_qflags[type] & QTF_CLOSING)) {
762 *dqp = NODQUOT;
763 return (EINVAL);
766 * Check the cache first.
768 dqh = DQHASH(dqvp, id);
769 LIST_FOREACH(dq, dqh, dq_hash) {
770 if (dq->dq_id != id ||
771 dq->dq_ump->um_quotas[dq->dq_type] != dqvp)
772 continue;
774 * Cache hit with no references. Take
775 * the structure off the free list.
777 if (dq->dq_cnt == 0)
778 TAILQ_REMOVE(&ufs_dqfreelist, dq, dq_freelist);
779 DQREF(dq);
780 *dqp = dq;
781 return (0);
785 * Not in cache, allocate a new one.
787 if (TAILQ_EMPTY(&ufs_dqfreelist) &&
788 ufs_numdquot < MAXQUOTAS * maxvnodes) {
789 ufs_desireddquot += DQUOTINC;
791 if (ufs_numdquot < ufs_desireddquot) {
792 dq = (struct ufs_dquot *)
793 kmalloc(sizeof *dq, M_DQUOT, M_WAITOK | M_ZERO);
794 ufs_numdquot++;
795 } else {
796 if ((dq = TAILQ_FIRST(&ufs_dqfreelist)) == NULL) {
797 tablefull("dquot");
798 *dqp = NODQUOT;
799 return (EUSERS);
801 if (dq->dq_cnt || (dq->dq_flags & DQ_MOD))
802 panic("dqget: free dquot isn't");
803 TAILQ_REMOVE(&ufs_dqfreelist, dq, dq_freelist);
804 if (dq->dq_ump != NULL)
805 LIST_REMOVE(dq, dq_hash);
808 * Initialize the contents of the dquot structure.
810 if (vp != dqvp)
811 vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY);
812 LIST_INSERT_HEAD(dqh, dq, dq_hash);
813 DQREF(dq);
814 dq->dq_flags = DQ_LOCK;
815 dq->dq_id = id;
816 dq->dq_ump = ump;
817 dq->dq_type = type;
818 auio.uio_iov = &aiov;
819 auio.uio_iovcnt = 1;
820 aiov.iov_base = (caddr_t)&dq->dq_dqb;
821 aiov.iov_len = sizeof (struct ufs_dqblk);
822 auio.uio_resid = sizeof (struct ufs_dqblk);
823 auio.uio_offset = (off_t)(id * sizeof (struct ufs_dqblk));
824 auio.uio_segflg = UIO_SYSSPACE;
825 auio.uio_rw = UIO_READ;
826 auio.uio_td = NULL;
827 error = VOP_READ(dqvp, &auio, 0, ump->um_cred[type]);
828 if (auio.uio_resid == sizeof(struct ufs_dqblk) && error == 0)
829 bzero((caddr_t)&dq->dq_dqb, sizeof(struct ufs_dqblk));
830 if (vp != dqvp)
831 vn_unlock(dqvp);
832 if (dq->dq_flags & DQ_WANT)
833 wakeup((caddr_t)dq);
834 dq->dq_flags = 0;
836 * I/O error in reading quota file, release
837 * quota structure and reflect problem to caller.
839 if (error) {
840 LIST_REMOVE(dq, dq_hash);
841 ufs_dqrele(vp, dq);
842 *dqp = NODQUOT;
843 return (error);
846 * Check for no limit to enforce.
847 * Initialize time values if necessary.
849 if (dq->dq_isoftlimit == 0 && dq->dq_bsoftlimit == 0 &&
850 dq->dq_ihardlimit == 0 && dq->dq_bhardlimit == 0)
851 dq->dq_flags |= DQ_FAKE;
852 if (dq->dq_id != 0) {
853 if (dq->dq_btime == 0)
854 dq->dq_btime = time_second + ump->um_btime[type];
855 if (dq->dq_itime == 0)
856 dq->dq_itime = time_second + ump->um_itime[type];
858 *dqp = dq;
859 return (0);
862 #ifdef DIAGNOSTIC
864 * Obtain a reference to a dquot.
866 static void
867 ufs_dqref(struct ufs_dquot *dq)
869 dq->dq_cnt++;
871 #endif
874 * Release a reference to a dquot.
876 void
877 ufs_dqrele(struct vnode *vp, struct ufs_dquot *dq)
879 if (dq == NODQUOT)
880 return;
881 if (dq->dq_cnt > 1) {
882 dq->dq_cnt--;
883 return;
885 if (dq->dq_flags & DQ_MOD)
886 (void)ufs_dqsync(vp, dq);
887 if (--dq->dq_cnt > 0)
888 return;
889 TAILQ_INSERT_TAIL(&ufs_dqfreelist, dq, dq_freelist);
893 * Update the disk quota in the quota file.
895 static int
896 ufs_dqsync(struct vnode *vp, struct ufs_dquot *dq)
898 struct vnode *dqvp;
899 struct iovec aiov;
900 struct uio auio;
901 int error;
903 if (dq == NODQUOT)
904 panic("dqsync: dquot");
905 if ((dq->dq_flags & DQ_MOD) == 0)
906 return (0);
907 if ((dqvp = dq->dq_ump->um_quotas[dq->dq_type]) == NULLVP)
908 panic("dqsync: file");
909 if (vp != dqvp)
910 vn_lock(dqvp, LK_EXCLUSIVE | LK_RETRY);
911 while (dq->dq_flags & DQ_LOCK) {
912 dq->dq_flags |= DQ_WANT;
913 (void) tsleep((caddr_t)dq, 0, "dqsync", 0);
914 if ((dq->dq_flags & DQ_MOD) == 0) {
915 if (vp != dqvp)
916 vn_unlock(dqvp);
917 return (0);
920 dq->dq_flags |= DQ_LOCK;
921 auio.uio_iov = &aiov;
922 auio.uio_iovcnt = 1;
923 aiov.iov_base = (caddr_t)&dq->dq_dqb;
924 aiov.iov_len = sizeof (struct ufs_dqblk);
925 auio.uio_resid = sizeof (struct ufs_dqblk);
926 auio.uio_offset = (off_t)(dq->dq_id * sizeof (struct ufs_dqblk));
927 auio.uio_segflg = UIO_SYSSPACE;
928 auio.uio_rw = UIO_WRITE;
929 auio.uio_td = NULL;
930 error = VOP_WRITE(dqvp, &auio, 0, dq->dq_ump->um_cred[dq->dq_type]);
931 if (auio.uio_resid && error == 0)
932 error = EIO;
933 if (dq->dq_flags & DQ_WANT)
934 wakeup((caddr_t)dq);
935 dq->dq_flags &= ~(DQ_MOD|DQ_LOCK|DQ_WANT);
936 if (vp != dqvp)
937 vn_unlock(dqvp);
938 return (error);
942 * Flush all entries from the cache for a particular vnode.
944 static void
945 ufs_dqflush(struct vnode *vp)
947 struct ufs_dquot *dq, *nextdq;
948 struct ufs_dqhash *dqh;
951 * Move all dquot's that used to refer to this quota
952 * file off their hash chains (they will eventually
953 * fall off the head of the free list and be re-used).
955 for (dqh = &ufs_dqhashtbl[ufs_dqhash]; dqh >= ufs_dqhashtbl; dqh--) {
956 for (dq = dqh->lh_first; dq; dq = nextdq) {
957 nextdq = dq->dq_hash.le_next;
958 if (dq->dq_ump->um_quotas[dq->dq_type] != vp)
959 continue;
960 if (dq->dq_cnt)
961 panic("dqflush: stray dquot");
962 LIST_REMOVE(dq, dq_hash);
963 dq->dq_ump = NULL;