diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /fs/xfs/quota |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'fs/xfs/quota')
-rw-r--r-- | fs/xfs/quota/xfs_dquot.c | 1648 | ||||
-rw-r--r-- | fs/xfs/quota/xfs_dquot.h | 224 | ||||
-rw-r--r-- | fs/xfs/quota/xfs_dquot_item.c | 715 | ||||
-rw-r--r-- | fs/xfs/quota/xfs_dquot_item.h | 66 | ||||
-rw-r--r-- | fs/xfs/quota/xfs_qm.c | 2848 | ||||
-rw-r--r-- | fs/xfs/quota/xfs_qm.h | 236 | ||||
-rw-r--r-- | fs/xfs/quota/xfs_qm_bhv.c | 410 | ||||
-rw-r--r-- | fs/xfs/quota/xfs_qm_stats.c | 149 | ||||
-rw-r--r-- | fs/xfs/quota/xfs_qm_stats.h | 68 | ||||
-rw-r--r-- | fs/xfs/quota/xfs_qm_syscalls.c | 1458 | ||||
-rw-r--r-- | fs/xfs/quota/xfs_quota_priv.h | 192 | ||||
-rw-r--r-- | fs/xfs/quota/xfs_trans_dquot.c | 941 |
12 files changed, 8955 insertions, 0 deletions
diff --git a/fs/xfs/quota/xfs_dquot.c b/fs/xfs/quota/xfs_dquot.c new file mode 100644 index 000000000000..740d20d33187 --- /dev/null +++ b/fs/xfs/quota/xfs_dquot.c | |||
@@ -0,0 +1,1648 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms of version 2 of the GNU General Public License as | ||
6 | * published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope that it would be useful, but | ||
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | * | ||
12 | * Further, this software is distributed without any warranty that it is | ||
13 | * free of the rightful claim of any third person regarding infringement | ||
14 | * or the like. Any license provided herein, whether implied or | ||
15 | * otherwise, applies only to this software file. Patent licenses, if | ||
16 | * any, provided herein do not apply to combinations of this program with | ||
17 | * other software, or any other product whatsoever. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License along | ||
20 | * with this program; if not, write the Free Software Foundation, Inc., 59 | ||
21 | * Temple Place - Suite 330, Boston MA 02111-1307, USA. | ||
22 | * | ||
23 | * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, | ||
24 | * Mountain View, CA 94043, or: | ||
25 | * | ||
26 | * http://www.sgi.com | ||
27 | * | ||
28 | * For further information regarding this notice, see: | ||
29 | * | ||
30 | * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ | ||
31 | */ | ||
32 | |||
33 | #include "xfs.h" | ||
34 | #include "xfs_fs.h" | ||
35 | #include "xfs_inum.h" | ||
36 | #include "xfs_log.h" | ||
37 | #include "xfs_trans.h" | ||
38 | #include "xfs_sb.h" | ||
39 | #include "xfs_ag.h" | ||
40 | #include "xfs_dir.h" | ||
41 | #include "xfs_dir2.h" | ||
42 | #include "xfs_alloc.h" | ||
43 | #include "xfs_dmapi.h" | ||
44 | #include "xfs_quota.h" | ||
45 | #include "xfs_mount.h" | ||
46 | #include "xfs_alloc_btree.h" | ||
47 | #include "xfs_bmap_btree.h" | ||
48 | #include "xfs_ialloc_btree.h" | ||
49 | #include "xfs_btree.h" | ||
50 | #include "xfs_ialloc.h" | ||
51 | #include "xfs_attr_sf.h" | ||
52 | #include "xfs_dir_sf.h" | ||
53 | #include "xfs_dir2_sf.h" | ||
54 | #include "xfs_dinode.h" | ||
55 | #include "xfs_inode.h" | ||
56 | #include "xfs_bmap.h" | ||
57 | #include "xfs_bit.h" | ||
58 | #include "xfs_rtalloc.h" | ||
59 | #include "xfs_error.h" | ||
60 | #include "xfs_itable.h" | ||
61 | #include "xfs_rw.h" | ||
62 | #include "xfs_acl.h" | ||
63 | #include "xfs_cap.h" | ||
64 | #include "xfs_mac.h" | ||
65 | #include "xfs_attr.h" | ||
66 | #include "xfs_buf_item.h" | ||
67 | #include "xfs_trans_space.h" | ||
68 | #include "xfs_trans_priv.h" | ||
69 | |||
70 | #include "xfs_qm.h" | ||
71 | |||
72 | |||
73 | /* | ||
74 | LOCK ORDER | ||
75 | |||
76 | inode lock (ilock) | ||
77 | dquot hash-chain lock (hashlock) | ||
78 | xqm dquot freelist lock (freelistlock | ||
79 | mount's dquot list lock (mplistlock) | ||
80 | user dquot lock - lock ordering among dquots is based on the uid or gid | ||
81 | group dquot lock - similar to udquots. Between the two dquots, the udquot | ||
82 | has to be locked first. | ||
83 | pin lock - the dquot lock must be held to take this lock. | ||
84 | flush lock - ditto. | ||
85 | */ | ||
86 | |||
87 | STATIC void xfs_qm_dqflush_done(xfs_buf_t *, xfs_dq_logitem_t *); | ||
88 | |||
89 | #ifdef DEBUG | ||
90 | xfs_buftarg_t *xfs_dqerror_target; | ||
91 | int xfs_do_dqerror; | ||
92 | int xfs_dqreq_num; | ||
93 | int xfs_dqerror_mod = 33; | ||
94 | #endif | ||
95 | |||
96 | /* | ||
97 | * Allocate and initialize a dquot. We don't always allocate fresh memory; | ||
98 | * we try to reclaim a free dquot if the number of incore dquots are above | ||
99 | * a threshold. | ||
100 | * The only field inside the core that gets initialized at this point | ||
101 | * is the d_id field. The idea is to fill in the entire q_core | ||
102 | * when we read in the on disk dquot. | ||
103 | */ | ||
104 | xfs_dquot_t * | ||
105 | xfs_qm_dqinit( | ||
106 | xfs_mount_t *mp, | ||
107 | xfs_dqid_t id, | ||
108 | uint type) | ||
109 | { | ||
110 | xfs_dquot_t *dqp; | ||
111 | boolean_t brandnewdquot; | ||
112 | |||
113 | brandnewdquot = xfs_qm_dqalloc_incore(&dqp); | ||
114 | dqp->dq_flags = type; | ||
115 | INT_SET(dqp->q_core.d_id, ARCH_CONVERT, id); | ||
116 | dqp->q_mount = mp; | ||
117 | |||
118 | /* | ||
119 | * No need to re-initialize these if this is a reclaimed dquot. | ||
120 | */ | ||
121 | if (brandnewdquot) { | ||
122 | dqp->dq_flnext = dqp->dq_flprev = dqp; | ||
123 | mutex_init(&dqp->q_qlock, MUTEX_DEFAULT, "xdq"); | ||
124 | initnsema(&dqp->q_flock, 1, "fdq"); | ||
125 | sv_init(&dqp->q_pinwait, SV_DEFAULT, "pdq"); | ||
126 | |||
127 | #ifdef XFS_DQUOT_TRACE | ||
128 | dqp->q_trace = ktrace_alloc(DQUOT_TRACE_SIZE, KM_SLEEP); | ||
129 | xfs_dqtrace_entry(dqp, "DQINIT"); | ||
130 | #endif | ||
131 | } else { | ||
132 | /* | ||
133 | * Only the q_core portion was zeroed in dqreclaim_one(). | ||
134 | * So, we need to reset others. | ||
135 | */ | ||
136 | dqp->q_nrefs = 0; | ||
137 | dqp->q_blkno = 0; | ||
138 | dqp->MPL_NEXT = dqp->HL_NEXT = NULL; | ||
139 | dqp->HL_PREVP = dqp->MPL_PREVP = NULL; | ||
140 | dqp->q_bufoffset = 0; | ||
141 | dqp->q_fileoffset = 0; | ||
142 | dqp->q_transp = NULL; | ||
143 | dqp->q_gdquot = NULL; | ||
144 | dqp->q_res_bcount = 0; | ||
145 | dqp->q_res_icount = 0; | ||
146 | dqp->q_res_rtbcount = 0; | ||
147 | dqp->q_pincount = 0; | ||
148 | dqp->q_hash = NULL; | ||
149 | ASSERT(dqp->dq_flnext == dqp->dq_flprev); | ||
150 | |||
151 | #ifdef XFS_DQUOT_TRACE | ||
152 | ASSERT(dqp->q_trace); | ||
153 | xfs_dqtrace_entry(dqp, "DQRECLAIMED_INIT"); | ||
154 | #endif | ||
155 | } | ||
156 | |||
157 | /* | ||
158 | * log item gets initialized later | ||
159 | */ | ||
160 | return (dqp); | ||
161 | } | ||
162 | |||
163 | /* | ||
164 | * This is called to free all the memory associated with a dquot | ||
165 | */ | ||
166 | void | ||
167 | xfs_qm_dqdestroy( | ||
168 | xfs_dquot_t *dqp) | ||
169 | { | ||
170 | ASSERT(! XFS_DQ_IS_ON_FREELIST(dqp)); | ||
171 | |||
172 | mutex_destroy(&dqp->q_qlock); | ||
173 | freesema(&dqp->q_flock); | ||
174 | sv_destroy(&dqp->q_pinwait); | ||
175 | |||
176 | #ifdef XFS_DQUOT_TRACE | ||
177 | if (dqp->q_trace) | ||
178 | ktrace_free(dqp->q_trace); | ||
179 | dqp->q_trace = NULL; | ||
180 | #endif | ||
181 | kmem_zone_free(xfs_Gqm->qm_dqzone, dqp); | ||
182 | atomic_dec(&xfs_Gqm->qm_totaldquots); | ||
183 | } | ||
184 | |||
185 | /* | ||
186 | * This is what a 'fresh' dquot inside a dquot chunk looks like on disk. | ||
187 | */ | ||
188 | STATIC void | ||
189 | xfs_qm_dqinit_core( | ||
190 | xfs_dqid_t id, | ||
191 | uint type, | ||
192 | xfs_dqblk_t *d) | ||
193 | { | ||
194 | /* | ||
195 | * Caller has zero'd the entire dquot 'chunk' already. | ||
196 | */ | ||
197 | INT_SET(d->dd_diskdq.d_magic, ARCH_CONVERT, XFS_DQUOT_MAGIC); | ||
198 | INT_SET(d->dd_diskdq.d_version, ARCH_CONVERT, XFS_DQUOT_VERSION); | ||
199 | INT_SET(d->dd_diskdq.d_id, ARCH_CONVERT, id); | ||
200 | INT_SET(d->dd_diskdq.d_flags, ARCH_CONVERT, type); | ||
201 | } | ||
202 | |||
203 | |||
204 | #ifdef XFS_DQUOT_TRACE | ||
205 | /* | ||
206 | * Dquot tracing for debugging. | ||
207 | */ | ||
208 | /* ARGSUSED */ | ||
209 | void | ||
210 | __xfs_dqtrace_entry( | ||
211 | xfs_dquot_t *dqp, | ||
212 | char *func, | ||
213 | void *retaddr, | ||
214 | xfs_inode_t *ip) | ||
215 | { | ||
216 | xfs_dquot_t *udqp = NULL; | ||
217 | xfs_ino_t ino = 0; | ||
218 | |||
219 | ASSERT(dqp->q_trace); | ||
220 | if (ip) { | ||
221 | ino = ip->i_ino; | ||
222 | udqp = ip->i_udquot; | ||
223 | } | ||
224 | ktrace_enter(dqp->q_trace, | ||
225 | (void *)(__psint_t)DQUOT_KTRACE_ENTRY, | ||
226 | (void *)func, | ||
227 | (void *)(__psint_t)dqp->q_nrefs, | ||
228 | (void *)(__psint_t)dqp->dq_flags, | ||
229 | (void *)(__psint_t)dqp->q_res_bcount, | ||
230 | (void *)(__psint_t)INT_GET(dqp->q_core.d_bcount, | ||
231 | ARCH_CONVERT), | ||
232 | (void *)(__psint_t)INT_GET(dqp->q_core.d_icount, | ||
233 | ARCH_CONVERT), | ||
234 | (void *)(__psint_t)INT_GET(dqp->q_core.d_blk_hardlimit, | ||
235 | ARCH_CONVERT), | ||
236 | (void *)(__psint_t)INT_GET(dqp->q_core.d_blk_softlimit, | ||
237 | ARCH_CONVERT), | ||
238 | (void *)(__psint_t)INT_GET(dqp->q_core.d_ino_hardlimit, | ||
239 | ARCH_CONVERT), | ||
240 | (void *)(__psint_t)INT_GET(dqp->q_core.d_ino_softlimit, | ||
241 | ARCH_CONVERT), | ||
242 | (void *)(__psint_t)INT_GET(dqp->q_core.d_id, ARCH_CONVERT), | ||
243 | (void *)(__psint_t)current_pid(), | ||
244 | (void *)(__psint_t)ino, | ||
245 | (void *)(__psint_t)retaddr, | ||
246 | (void *)(__psint_t)udqp); | ||
247 | return; | ||
248 | } | ||
249 | #endif | ||
250 | |||
251 | |||
252 | /* | ||
253 | * If default limits are in force, push them into the dquot now. | ||
254 | * We overwrite the dquot limits only if they are zero and this | ||
255 | * is not the root dquot. | ||
256 | */ | ||
257 | void | ||
258 | xfs_qm_adjust_dqlimits( | ||
259 | xfs_mount_t *mp, | ||
260 | xfs_disk_dquot_t *d) | ||
261 | { | ||
262 | xfs_quotainfo_t *q = mp->m_quotainfo; | ||
263 | |||
264 | ASSERT(d->d_id); | ||
265 | |||
266 | if (q->qi_bsoftlimit && !d->d_blk_softlimit) | ||
267 | INT_SET(d->d_blk_softlimit, ARCH_CONVERT, q->qi_bsoftlimit); | ||
268 | if (q->qi_bhardlimit && !d->d_blk_hardlimit) | ||
269 | INT_SET(d->d_blk_hardlimit, ARCH_CONVERT, q->qi_bhardlimit); | ||
270 | if (q->qi_isoftlimit && !d->d_ino_softlimit) | ||
271 | INT_SET(d->d_ino_softlimit, ARCH_CONVERT, q->qi_isoftlimit); | ||
272 | if (q->qi_ihardlimit && !d->d_ino_hardlimit) | ||
273 | INT_SET(d->d_ino_hardlimit, ARCH_CONVERT, q->qi_ihardlimit); | ||
274 | if (q->qi_rtbsoftlimit && !d->d_rtb_softlimit) | ||
275 | INT_SET(d->d_rtb_softlimit, ARCH_CONVERT, q->qi_rtbsoftlimit); | ||
276 | if (q->qi_rtbhardlimit && !d->d_rtb_hardlimit) | ||
277 | INT_SET(d->d_rtb_hardlimit, ARCH_CONVERT, q->qi_rtbhardlimit); | ||
278 | } | ||
279 | |||
280 | /* | ||
281 | * Check the limits and timers of a dquot and start or reset timers | ||
282 | * if necessary. | ||
283 | * This gets called even when quota enforcement is OFF, which makes our | ||
284 | * life a little less complicated. (We just don't reject any quota | ||
285 | * reservations in that case, when enforcement is off). | ||
286 | * We also return 0 as the values of the timers in Q_GETQUOTA calls, when | ||
287 | * enforcement's off. | ||
288 | * In contrast, warnings are a little different in that they don't | ||
289 | * 'automatically' get started when limits get exceeded. | ||
290 | */ | ||
291 | void | ||
292 | xfs_qm_adjust_dqtimers( | ||
293 | xfs_mount_t *mp, | ||
294 | xfs_disk_dquot_t *d) | ||
295 | { | ||
296 | ASSERT(d->d_id); | ||
297 | |||
298 | #ifdef QUOTADEBUG | ||
299 | if (INT_GET(d->d_blk_hardlimit, ARCH_CONVERT)) | ||
300 | ASSERT(INT_GET(d->d_blk_softlimit, ARCH_CONVERT) <= | ||
301 | INT_GET(d->d_blk_hardlimit, ARCH_CONVERT)); | ||
302 | if (INT_GET(d->d_ino_hardlimit, ARCH_CONVERT)) | ||
303 | ASSERT(INT_GET(d->d_ino_softlimit, ARCH_CONVERT) <= | ||
304 | INT_GET(d->d_ino_hardlimit, ARCH_CONVERT)); | ||
305 | if (INT_GET(d->d_rtb_hardlimit, ARCH_CONVERT)) | ||
306 | ASSERT(INT_GET(d->d_rtb_softlimit, ARCH_CONVERT) <= | ||
307 | INT_GET(d->d_rtb_hardlimit, ARCH_CONVERT)); | ||
308 | #endif | ||
309 | if (!d->d_btimer) { | ||
310 | if ((INT_GET(d->d_blk_softlimit, ARCH_CONVERT) && | ||
311 | (INT_GET(d->d_bcount, ARCH_CONVERT) >= | ||
312 | INT_GET(d->d_blk_softlimit, ARCH_CONVERT))) || | ||
313 | (INT_GET(d->d_blk_hardlimit, ARCH_CONVERT) && | ||
314 | (INT_GET(d->d_bcount, ARCH_CONVERT) >= | ||
315 | INT_GET(d->d_blk_hardlimit, ARCH_CONVERT)))) { | ||
316 | INT_SET(d->d_btimer, ARCH_CONVERT, | ||
317 | get_seconds() + XFS_QI_BTIMELIMIT(mp)); | ||
318 | } | ||
319 | } else { | ||
320 | if ((!d->d_blk_softlimit || | ||
321 | (INT_GET(d->d_bcount, ARCH_CONVERT) < | ||
322 | INT_GET(d->d_blk_softlimit, ARCH_CONVERT))) && | ||
323 | (!d->d_blk_hardlimit || | ||
324 | (INT_GET(d->d_bcount, ARCH_CONVERT) < | ||
325 | INT_GET(d->d_blk_hardlimit, ARCH_CONVERT)))) { | ||
326 | d->d_btimer = 0; | ||
327 | } | ||
328 | } | ||
329 | |||
330 | if (!d->d_itimer) { | ||
331 | if ((INT_GET(d->d_ino_softlimit, ARCH_CONVERT) && | ||
332 | (INT_GET(d->d_icount, ARCH_CONVERT) >= | ||
333 | INT_GET(d->d_ino_softlimit, ARCH_CONVERT))) || | ||
334 | (INT_GET(d->d_ino_hardlimit, ARCH_CONVERT) && | ||
335 | (INT_GET(d->d_icount, ARCH_CONVERT) >= | ||
336 | INT_GET(d->d_ino_hardlimit, ARCH_CONVERT)))) { | ||
337 | INT_SET(d->d_itimer, ARCH_CONVERT, | ||
338 | get_seconds() + XFS_QI_ITIMELIMIT(mp)); | ||
339 | } | ||
340 | } else { | ||
341 | if ((!d->d_ino_softlimit || | ||
342 | (INT_GET(d->d_icount, ARCH_CONVERT) < | ||
343 | INT_GET(d->d_ino_softlimit, ARCH_CONVERT))) && | ||
344 | (!d->d_ino_hardlimit || | ||
345 | (INT_GET(d->d_icount, ARCH_CONVERT) < | ||
346 | INT_GET(d->d_ino_hardlimit, ARCH_CONVERT)))) { | ||
347 | d->d_itimer = 0; | ||
348 | } | ||
349 | } | ||
350 | |||
351 | if (!d->d_rtbtimer) { | ||
352 | if ((INT_GET(d->d_rtb_softlimit, ARCH_CONVERT) && | ||
353 | (INT_GET(d->d_rtbcount, ARCH_CONVERT) >= | ||
354 | INT_GET(d->d_rtb_softlimit, ARCH_CONVERT))) || | ||
355 | (INT_GET(d->d_rtb_hardlimit, ARCH_CONVERT) && | ||
356 | (INT_GET(d->d_rtbcount, ARCH_CONVERT) >= | ||
357 | INT_GET(d->d_rtb_hardlimit, ARCH_CONVERT)))) { | ||
358 | INT_SET(d->d_rtbtimer, ARCH_CONVERT, | ||
359 | get_seconds() + XFS_QI_RTBTIMELIMIT(mp)); | ||
360 | } | ||
361 | } else { | ||
362 | if ((!d->d_rtb_softlimit || | ||
363 | (INT_GET(d->d_rtbcount, ARCH_CONVERT) < | ||
364 | INT_GET(d->d_rtb_softlimit, ARCH_CONVERT))) && | ||
365 | (!d->d_rtb_hardlimit || | ||
366 | (INT_GET(d->d_rtbcount, ARCH_CONVERT) < | ||
367 | INT_GET(d->d_rtb_hardlimit, ARCH_CONVERT)))) { | ||
368 | d->d_rtbtimer = 0; | ||
369 | } | ||
370 | } | ||
371 | } | ||
372 | |||
373 | /* | ||
374 | * Increment or reset warnings of a given dquot. | ||
375 | */ | ||
376 | int | ||
377 | xfs_qm_dqwarn( | ||
378 | xfs_disk_dquot_t *d, | ||
379 | uint flags) | ||
380 | { | ||
381 | int warned; | ||
382 | |||
383 | /* | ||
384 | * root's limits are not real limits. | ||
385 | */ | ||
386 | if (!d->d_id) | ||
387 | return (0); | ||
388 | |||
389 | warned = 0; | ||
390 | if (INT_GET(d->d_blk_softlimit, ARCH_CONVERT) && | ||
391 | (INT_GET(d->d_bcount, ARCH_CONVERT) >= | ||
392 | INT_GET(d->d_blk_softlimit, ARCH_CONVERT))) { | ||
393 | if (flags & XFS_QMOPT_DOWARN) { | ||
394 | INT_MOD(d->d_bwarns, ARCH_CONVERT, +1); | ||
395 | warned++; | ||
396 | } | ||
397 | } else { | ||
398 | if (!d->d_blk_softlimit || | ||
399 | (INT_GET(d->d_bcount, ARCH_CONVERT) < | ||
400 | INT_GET(d->d_blk_softlimit, ARCH_CONVERT))) { | ||
401 | d->d_bwarns = 0; | ||
402 | } | ||
403 | } | ||
404 | |||
405 | if (INT_GET(d->d_ino_softlimit, ARCH_CONVERT) > 0 && | ||
406 | (INT_GET(d->d_icount, ARCH_CONVERT) >= | ||
407 | INT_GET(d->d_ino_softlimit, ARCH_CONVERT))) { | ||
408 | if (flags & XFS_QMOPT_DOWARN) { | ||
409 | INT_MOD(d->d_iwarns, ARCH_CONVERT, +1); | ||
410 | warned++; | ||
411 | } | ||
412 | } else { | ||
413 | if (!d->d_ino_softlimit || | ||
414 | (INT_GET(d->d_icount, ARCH_CONVERT) < | ||
415 | INT_GET(d->d_ino_softlimit, ARCH_CONVERT))) { | ||
416 | d->d_iwarns = 0; | ||
417 | } | ||
418 | } | ||
419 | #ifdef QUOTADEBUG | ||
420 | if (INT_GET(d->d_iwarns, ARCH_CONVERT)) | ||
421 | cmn_err(CE_DEBUG, | ||
422 | "--------@@Inode warnings running : %Lu >= %Lu", | ||
423 | INT_GET(d->d_icount, ARCH_CONVERT), | ||
424 | INT_GET(d->d_ino_softlimit, ARCH_CONVERT)); | ||
425 | if (INT_GET(d->d_bwarns, ARCH_CONVERT)) | ||
426 | cmn_err(CE_DEBUG, | ||
427 | "--------@@Blks warnings running : %Lu >= %Lu", | ||
428 | INT_GET(d->d_bcount, ARCH_CONVERT), | ||
429 | INT_GET(d->d_blk_softlimit, ARCH_CONVERT)); | ||
430 | #endif | ||
431 | return (warned); | ||
432 | } | ||
433 | |||
434 | |||
435 | /* | ||
436 | * initialize a buffer full of dquots and log the whole thing | ||
437 | */ | ||
438 | STATIC void | ||
439 | xfs_qm_init_dquot_blk( | ||
440 | xfs_trans_t *tp, | ||
441 | xfs_mount_t *mp, | ||
442 | xfs_dqid_t id, | ||
443 | uint type, | ||
444 | xfs_buf_t *bp) | ||
445 | { | ||
446 | xfs_dqblk_t *d; | ||
447 | int curid, i; | ||
448 | |||
449 | ASSERT(tp); | ||
450 | ASSERT(XFS_BUF_ISBUSY(bp)); | ||
451 | ASSERT(XFS_BUF_VALUSEMA(bp) <= 0); | ||
452 | |||
453 | d = (xfs_dqblk_t *)XFS_BUF_PTR(bp); | ||
454 | |||
455 | /* | ||
456 | * ID of the first dquot in the block - id's are zero based. | ||
457 | */ | ||
458 | curid = id - (id % XFS_QM_DQPERBLK(mp)); | ||
459 | ASSERT(curid >= 0); | ||
460 | memset(d, 0, BBTOB(XFS_QI_DQCHUNKLEN(mp))); | ||
461 | for (i = 0; i < XFS_QM_DQPERBLK(mp); i++, d++, curid++) | ||
462 | xfs_qm_dqinit_core(curid, type, d); | ||
463 | xfs_trans_dquot_buf(tp, bp, | ||
464 | type & XFS_DQ_USER ? | ||
465 | XFS_BLI_UDQUOT_BUF : | ||
466 | XFS_BLI_GDQUOT_BUF); | ||
467 | xfs_trans_log_buf(tp, bp, 0, BBTOB(XFS_QI_DQCHUNKLEN(mp)) - 1); | ||
468 | } | ||
469 | |||
470 | |||
471 | |||
472 | /* | ||
473 | * Allocate a block and fill it with dquots. | ||
474 | * This is called when the bmapi finds a hole. | ||
475 | */ | ||
476 | STATIC int | ||
477 | xfs_qm_dqalloc( | ||
478 | xfs_trans_t *tp, | ||
479 | xfs_mount_t *mp, | ||
480 | xfs_dquot_t *dqp, | ||
481 | xfs_inode_t *quotip, | ||
482 | xfs_fileoff_t offset_fsb, | ||
483 | xfs_buf_t **O_bpp) | ||
484 | { | ||
485 | xfs_fsblock_t firstblock; | ||
486 | xfs_bmap_free_t flist; | ||
487 | xfs_bmbt_irec_t map; | ||
488 | int nmaps, error, committed; | ||
489 | xfs_buf_t *bp; | ||
490 | |||
491 | ASSERT(tp != NULL); | ||
492 | xfs_dqtrace_entry(dqp, "DQALLOC"); | ||
493 | |||
494 | /* | ||
495 | * Initialize the bmap freelist prior to calling bmapi code. | ||
496 | */ | ||
497 | XFS_BMAP_INIT(&flist, &firstblock); | ||
498 | xfs_ilock(quotip, XFS_ILOCK_EXCL); | ||
499 | /* | ||
500 | * Return if this type of quotas is turned off while we didn't | ||
501 | * have an inode lock | ||
502 | */ | ||
503 | if (XFS_IS_THIS_QUOTA_OFF(dqp)) { | ||
504 | xfs_iunlock(quotip, XFS_ILOCK_EXCL); | ||
505 | return (ESRCH); | ||
506 | } | ||
507 | |||
508 | /* | ||
509 | * xfs_trans_commit normally decrements the vnode ref count | ||
510 | * when it unlocks the inode. Since we want to keep the quota | ||
511 | * inode around, we bump the vnode ref count now. | ||
512 | */ | ||
513 | VN_HOLD(XFS_ITOV(quotip)); | ||
514 | |||
515 | xfs_trans_ijoin(tp, quotip, XFS_ILOCK_EXCL); | ||
516 | nmaps = 1; | ||
517 | if ((error = xfs_bmapi(tp, quotip, | ||
518 | offset_fsb, XFS_DQUOT_CLUSTER_SIZE_FSB, | ||
519 | XFS_BMAPI_METADATA | XFS_BMAPI_WRITE, | ||
520 | &firstblock, | ||
521 | XFS_QM_DQALLOC_SPACE_RES(mp), | ||
522 | &map, &nmaps, &flist))) { | ||
523 | goto error0; | ||
524 | } | ||
525 | ASSERT(map.br_blockcount == XFS_DQUOT_CLUSTER_SIZE_FSB); | ||
526 | ASSERT(nmaps == 1); | ||
527 | ASSERT((map.br_startblock != DELAYSTARTBLOCK) && | ||
528 | (map.br_startblock != HOLESTARTBLOCK)); | ||
529 | |||
530 | /* | ||
531 | * Keep track of the blkno to save a lookup later | ||
532 | */ | ||
533 | dqp->q_blkno = XFS_FSB_TO_DADDR(mp, map.br_startblock); | ||
534 | |||
535 | /* now we can just get the buffer (there's nothing to read yet) */ | ||
536 | bp = xfs_trans_get_buf(tp, mp->m_ddev_targp, | ||
537 | dqp->q_blkno, | ||
538 | XFS_QI_DQCHUNKLEN(mp), | ||
539 | 0); | ||
540 | if (!bp || (error = XFS_BUF_GETERROR(bp))) | ||
541 | goto error1; | ||
542 | /* | ||
543 | * Make a chunk of dquots out of this buffer and log | ||
544 | * the entire thing. | ||
545 | */ | ||
546 | xfs_qm_init_dquot_blk(tp, mp, INT_GET(dqp->q_core.d_id, ARCH_CONVERT), | ||
547 | dqp->dq_flags & (XFS_DQ_USER|XFS_DQ_GROUP), | ||
548 | bp); | ||
549 | |||
550 | if ((error = xfs_bmap_finish(&tp, &flist, firstblock, &committed))) { | ||
551 | goto error1; | ||
552 | } | ||
553 | |||
554 | *O_bpp = bp; | ||
555 | return 0; | ||
556 | |||
557 | error1: | ||
558 | xfs_bmap_cancel(&flist); | ||
559 | error0: | ||
560 | xfs_iunlock(quotip, XFS_ILOCK_EXCL); | ||
561 | |||
562 | return (error); | ||
563 | } | ||
564 | |||
565 | /* | ||
566 | * Maps a dquot to the buffer containing its on-disk version. | ||
567 | * This returns a ptr to the buffer containing the on-disk dquot | ||
568 | * in the bpp param, and a ptr to the on-disk dquot within that buffer | ||
569 | */ | ||
570 | STATIC int | ||
571 | xfs_qm_dqtobp( | ||
572 | xfs_trans_t *tp, | ||
573 | xfs_dquot_t *dqp, | ||
574 | xfs_disk_dquot_t **O_ddpp, | ||
575 | xfs_buf_t **O_bpp, | ||
576 | uint flags) | ||
577 | { | ||
578 | xfs_bmbt_irec_t map; | ||
579 | int nmaps, error; | ||
580 | xfs_buf_t *bp; | ||
581 | xfs_inode_t *quotip; | ||
582 | xfs_mount_t *mp; | ||
583 | xfs_disk_dquot_t *ddq; | ||
584 | xfs_dqid_t id; | ||
585 | boolean_t newdquot; | ||
586 | |||
587 | mp = dqp->q_mount; | ||
588 | id = INT_GET(dqp->q_core.d_id, ARCH_CONVERT); | ||
589 | nmaps = 1; | ||
590 | newdquot = B_FALSE; | ||
591 | |||
592 | /* | ||
593 | * If we don't know where the dquot lives, find out. | ||
594 | */ | ||
595 | if (dqp->q_blkno == (xfs_daddr_t) 0) { | ||
596 | /* We use the id as an index */ | ||
597 | dqp->q_fileoffset = (xfs_fileoff_t) ((uint)id / | ||
598 | XFS_QM_DQPERBLK(mp)); | ||
599 | nmaps = 1; | ||
600 | quotip = XFS_DQ_TO_QIP(dqp); | ||
601 | xfs_ilock(quotip, XFS_ILOCK_SHARED); | ||
602 | /* | ||
603 | * Return if this type of quotas is turned off while we didn't | ||
604 | * have an inode lock | ||
605 | */ | ||
606 | if (XFS_IS_THIS_QUOTA_OFF(dqp)) { | ||
607 | xfs_iunlock(quotip, XFS_ILOCK_SHARED); | ||
608 | return (ESRCH); | ||
609 | } | ||
610 | /* | ||
611 | * Find the block map; no allocations yet | ||
612 | */ | ||
613 | error = xfs_bmapi(NULL, quotip, dqp->q_fileoffset, | ||
614 | XFS_DQUOT_CLUSTER_SIZE_FSB, | ||
615 | XFS_BMAPI_METADATA, | ||
616 | NULL, 0, &map, &nmaps, NULL); | ||
617 | |||
618 | xfs_iunlock(quotip, XFS_ILOCK_SHARED); | ||
619 | if (error) | ||
620 | return (error); | ||
621 | ASSERT(nmaps == 1); | ||
622 | ASSERT(map.br_blockcount == 1); | ||
623 | |||
624 | /* | ||
625 | * offset of dquot in the (fixed sized) dquot chunk. | ||
626 | */ | ||
627 | dqp->q_bufoffset = (id % XFS_QM_DQPERBLK(mp)) * | ||
628 | sizeof(xfs_dqblk_t); | ||
629 | if (map.br_startblock == HOLESTARTBLOCK) { | ||
630 | /* | ||
631 | * We don't allocate unless we're asked to | ||
632 | */ | ||
633 | if (!(flags & XFS_QMOPT_DQALLOC)) | ||
634 | return (ENOENT); | ||
635 | |||
636 | ASSERT(tp); | ||
637 | if ((error = xfs_qm_dqalloc(tp, mp, dqp, quotip, | ||
638 | dqp->q_fileoffset, &bp))) | ||
639 | return (error); | ||
640 | newdquot = B_TRUE; | ||
641 | } else { | ||
642 | /* | ||
643 | * store the blkno etc so that we don't have to do the | ||
644 | * mapping all the time | ||
645 | */ | ||
646 | dqp->q_blkno = XFS_FSB_TO_DADDR(mp, map.br_startblock); | ||
647 | } | ||
648 | } | ||
649 | ASSERT(dqp->q_blkno != DELAYSTARTBLOCK); | ||
650 | ASSERT(dqp->q_blkno != HOLESTARTBLOCK); | ||
651 | |||
652 | /* | ||
653 | * Read in the buffer, unless we've just done the allocation | ||
654 | * (in which case we already have the buf). | ||
655 | */ | ||
656 | if (! newdquot) { | ||
657 | xfs_dqtrace_entry(dqp, "DQTOBP READBUF"); | ||
658 | if ((error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp, | ||
659 | dqp->q_blkno, | ||
660 | XFS_QI_DQCHUNKLEN(mp), | ||
661 | 0, &bp))) { | ||
662 | return (error); | ||
663 | } | ||
664 | if (error || !bp) | ||
665 | return XFS_ERROR(error); | ||
666 | } | ||
667 | ASSERT(XFS_BUF_ISBUSY(bp)); | ||
668 | ASSERT(XFS_BUF_VALUSEMA(bp) <= 0); | ||
669 | |||
670 | /* | ||
671 | * calculate the location of the dquot inside the buffer. | ||
672 | */ | ||
673 | ddq = (xfs_disk_dquot_t *)((char *)XFS_BUF_PTR(bp) + dqp->q_bufoffset); | ||
674 | |||
675 | /* | ||
676 | * A simple sanity check in case we got a corrupted dquot... | ||
677 | */ | ||
678 | if (xfs_qm_dqcheck(ddq, id, | ||
679 | dqp->dq_flags & (XFS_DQ_USER|XFS_DQ_GROUP), | ||
680 | flags & (XFS_QMOPT_DQREPAIR|XFS_QMOPT_DOWARN), | ||
681 | "dqtobp")) { | ||
682 | if (!(flags & XFS_QMOPT_DQREPAIR)) { | ||
683 | xfs_trans_brelse(tp, bp); | ||
684 | return XFS_ERROR(EIO); | ||
685 | } | ||
686 | XFS_BUF_BUSY(bp); /* We dirtied this */ | ||
687 | } | ||
688 | |||
689 | *O_bpp = bp; | ||
690 | *O_ddpp = ddq; | ||
691 | |||
692 | return (0); | ||
693 | } | ||
694 | |||
695 | |||
696 | /* | ||
697 | * Read in the ondisk dquot using dqtobp() then copy it to an incore version, | ||
698 | * and release the buffer immediately. | ||
699 | * | ||
700 | */ | ||
701 | /* ARGSUSED */ | ||
702 | STATIC int | ||
703 | xfs_qm_dqread( | ||
704 | xfs_trans_t *tp, | ||
705 | xfs_dqid_t id, | ||
706 | xfs_dquot_t *dqp, /* dquot to get filled in */ | ||
707 | uint flags) | ||
708 | { | ||
709 | xfs_disk_dquot_t *ddqp; | ||
710 | xfs_buf_t *bp; | ||
711 | int error; | ||
712 | |||
713 | /* | ||
714 | * get a pointer to the on-disk dquot and the buffer containing it | ||
715 | * dqp already knows its own type (GROUP/USER). | ||
716 | */ | ||
717 | xfs_dqtrace_entry(dqp, "DQREAD"); | ||
718 | if ((error = xfs_qm_dqtobp(tp, dqp, &ddqp, &bp, flags))) { | ||
719 | return (error); | ||
720 | } | ||
721 | |||
722 | /* copy everything from disk dquot to the incore dquot */ | ||
723 | memcpy(&dqp->q_core, ddqp, sizeof(xfs_disk_dquot_t)); | ||
724 | ASSERT(INT_GET(dqp->q_core.d_id, ARCH_CONVERT) == id); | ||
725 | xfs_qm_dquot_logitem_init(dqp); | ||
726 | |||
727 | /* | ||
728 | * Reservation counters are defined as reservation plus current usage | ||
729 | * to avoid having to add everytime. | ||
730 | */ | ||
731 | dqp->q_res_bcount = INT_GET(ddqp->d_bcount, ARCH_CONVERT); | ||
732 | dqp->q_res_icount = INT_GET(ddqp->d_icount, ARCH_CONVERT); | ||
733 | dqp->q_res_rtbcount = INT_GET(ddqp->d_rtbcount, ARCH_CONVERT); | ||
734 | |||
735 | /* Mark the buf so that this will stay incore a little longer */ | ||
736 | XFS_BUF_SET_VTYPE_REF(bp, B_FS_DQUOT, XFS_DQUOT_REF); | ||
737 | |||
738 | /* | ||
739 | * We got the buffer with a xfs_trans_read_buf() (in dqtobp()) | ||
740 | * So we need to release with xfs_trans_brelse(). | ||
741 | * The strategy here is identical to that of inodes; we lock | ||
742 | * the dquot in xfs_qm_dqget() before making it accessible to | ||
743 | * others. This is because dquots, like inodes, need a good level of | ||
744 | * concurrency, and we don't want to take locks on the entire buffers | ||
745 | * for dquot accesses. | ||
746 | * Note also that the dquot buffer may even be dirty at this point, if | ||
747 | * this particular dquot was repaired. We still aren't afraid to | ||
748 | * brelse it because we have the changes incore. | ||
749 | */ | ||
750 | ASSERT(XFS_BUF_ISBUSY(bp)); | ||
751 | ASSERT(XFS_BUF_VALUSEMA(bp) <= 0); | ||
752 | xfs_trans_brelse(tp, bp); | ||
753 | |||
754 | return (error); | ||
755 | } | ||
756 | |||
757 | |||
758 | /* | ||
759 | * allocate an incore dquot from the kernel heap, | ||
760 | * and fill its core with quota information kept on disk. | ||
761 | * If XFS_QMOPT_DQALLOC is set, it'll allocate a dquot on disk | ||
762 | * if it wasn't already allocated. | ||
763 | */ | ||
764 | STATIC int | ||
765 | xfs_qm_idtodq( | ||
766 | xfs_mount_t *mp, | ||
767 | xfs_dqid_t id, /* gid or uid, depending on type */ | ||
768 | uint type, /* UDQUOT or GDQUOT */ | ||
769 | uint flags, /* DQALLOC, DQREPAIR */ | ||
770 | xfs_dquot_t **O_dqpp)/* OUT : incore dquot, not locked */ | ||
771 | { | ||
772 | xfs_dquot_t *dqp; | ||
773 | int error; | ||
774 | xfs_trans_t *tp; | ||
775 | int cancelflags=0; | ||
776 | |||
777 | dqp = xfs_qm_dqinit(mp, id, type); | ||
778 | tp = NULL; | ||
779 | if (flags & XFS_QMOPT_DQALLOC) { | ||
780 | tp = xfs_trans_alloc(mp, XFS_TRANS_QM_DQALLOC); | ||
781 | if ((error = xfs_trans_reserve(tp, | ||
782 | XFS_QM_DQALLOC_SPACE_RES(mp), | ||
783 | XFS_WRITE_LOG_RES(mp) + | ||
784 | BBTOB(XFS_QI_DQCHUNKLEN(mp)) - 1 + | ||
785 | 128, | ||
786 | 0, | ||
787 | XFS_TRANS_PERM_LOG_RES, | ||
788 | XFS_WRITE_LOG_COUNT))) { | ||
789 | cancelflags = 0; | ||
790 | goto error0; | ||
791 | } | ||
792 | cancelflags = XFS_TRANS_RELEASE_LOG_RES; | ||
793 | } | ||
794 | |||
795 | /* | ||
796 | * Read it from disk; xfs_dqread() takes care of | ||
797 | * all the necessary initialization of dquot's fields (locks, etc) | ||
798 | */ | ||
799 | if ((error = xfs_qm_dqread(tp, id, dqp, flags))) { | ||
800 | /* | ||
801 | * This can happen if quotas got turned off (ESRCH), | ||
802 | * or if the dquot didn't exist on disk and we ask to | ||
803 | * allocate (ENOENT). | ||
804 | */ | ||
805 | xfs_dqtrace_entry(dqp, "DQREAD FAIL"); | ||
806 | cancelflags |= XFS_TRANS_ABORT; | ||
807 | goto error0; | ||
808 | } | ||
809 | if (tp) { | ||
810 | if ((error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES, | ||
811 | NULL))) | ||
812 | goto error1; | ||
813 | } | ||
814 | |||
815 | *O_dqpp = dqp; | ||
816 | return (0); | ||
817 | |||
818 | error0: | ||
819 | ASSERT(error); | ||
820 | if (tp) | ||
821 | xfs_trans_cancel(tp, cancelflags); | ||
822 | error1: | ||
823 | xfs_qm_dqdestroy(dqp); | ||
824 | *O_dqpp = NULL; | ||
825 | return (error); | ||
826 | } | ||
827 | |||
828 | /* | ||
829 | * Lookup a dquot in the incore dquot hashtable. We keep two separate | ||
830 | * hashtables for user and group dquots; and, these are global tables | ||
831 | * inside the XQM, not per-filesystem tables. | ||
832 | * The hash chain must be locked by caller, and it is left locked | ||
833 | * on return. Returning dquot is locked. | ||
834 | */ | ||
835 | STATIC int | ||
836 | xfs_qm_dqlookup( | ||
837 | xfs_mount_t *mp, | ||
838 | xfs_dqid_t id, | ||
839 | xfs_dqhash_t *qh, | ||
840 | xfs_dquot_t **O_dqpp) | ||
841 | { | ||
842 | xfs_dquot_t *dqp; | ||
843 | uint flist_locked; | ||
844 | xfs_dquot_t *d; | ||
845 | |||
846 | ASSERT(XFS_DQ_IS_HASH_LOCKED(qh)); | ||
847 | |||
848 | flist_locked = B_FALSE; | ||
849 | |||
850 | /* | ||
851 | * Traverse the hashchain looking for a match | ||
852 | */ | ||
853 | for (dqp = qh->qh_next; dqp != NULL; dqp = dqp->HL_NEXT) { | ||
854 | /* | ||
855 | * We already have the hashlock. We don't need the | ||
856 | * dqlock to look at the id field of the dquot, since the | ||
857 | * id can't be modified without the hashlock anyway. | ||
858 | */ | ||
859 | if (INT_GET(dqp->q_core.d_id, ARCH_CONVERT) == id && dqp->q_mount == mp) { | ||
860 | xfs_dqtrace_entry(dqp, "DQFOUND BY LOOKUP"); | ||
861 | /* | ||
862 | * All in core dquots must be on the dqlist of mp | ||
863 | */ | ||
864 | ASSERT(dqp->MPL_PREVP != NULL); | ||
865 | |||
866 | xfs_dqlock(dqp); | ||
867 | if (dqp->q_nrefs == 0) { | ||
868 | ASSERT (XFS_DQ_IS_ON_FREELIST(dqp)); | ||
869 | if (! xfs_qm_freelist_lock_nowait(xfs_Gqm)) { | ||
870 | xfs_dqtrace_entry(dqp, "DQLOOKUP: WANT"); | ||
871 | |||
872 | /* | ||
873 | * We may have raced with dqreclaim_one() | ||
874 | * (and lost). So, flag that we don't | ||
875 | * want the dquot to be reclaimed. | ||
876 | */ | ||
877 | dqp->dq_flags |= XFS_DQ_WANT; | ||
878 | xfs_dqunlock(dqp); | ||
879 | xfs_qm_freelist_lock(xfs_Gqm); | ||
880 | xfs_dqlock(dqp); | ||
881 | dqp->dq_flags &= ~(XFS_DQ_WANT); | ||
882 | } | ||
883 | flist_locked = B_TRUE; | ||
884 | } | ||
885 | |||
886 | /* | ||
887 | * id couldn't have changed; we had the hashlock all | ||
888 | * along | ||
889 | */ | ||
890 | ASSERT(INT_GET(dqp->q_core.d_id, ARCH_CONVERT) == id); | ||
891 | |||
892 | if (flist_locked) { | ||
893 | if (dqp->q_nrefs != 0) { | ||
894 | xfs_qm_freelist_unlock(xfs_Gqm); | ||
895 | flist_locked = B_FALSE; | ||
896 | } else { | ||
897 | /* | ||
898 | * take it off the freelist | ||
899 | */ | ||
900 | xfs_dqtrace_entry(dqp, | ||
901 | "DQLOOKUP: TAKEOFF FL"); | ||
902 | XQM_FREELIST_REMOVE(dqp); | ||
903 | /* xfs_qm_freelist_print(&(xfs_Gqm-> | ||
904 | qm_dqfreelist), | ||
905 | "after removal"); */ | ||
906 | } | ||
907 | } | ||
908 | |||
909 | /* | ||
910 | * grab a reference | ||
911 | */ | ||
912 | XFS_DQHOLD(dqp); | ||
913 | |||
914 | if (flist_locked) | ||
915 | xfs_qm_freelist_unlock(xfs_Gqm); | ||
916 | /* | ||
917 | * move the dquot to the front of the hashchain | ||
918 | */ | ||
919 | ASSERT(XFS_DQ_IS_HASH_LOCKED(qh)); | ||
920 | if (dqp->HL_PREVP != &qh->qh_next) { | ||
921 | xfs_dqtrace_entry(dqp, | ||
922 | "DQLOOKUP: HASH MOVETOFRONT"); | ||
923 | if ((d = dqp->HL_NEXT)) | ||
924 | d->HL_PREVP = dqp->HL_PREVP; | ||
925 | *(dqp->HL_PREVP) = d; | ||
926 | d = qh->qh_next; | ||
927 | d->HL_PREVP = &dqp->HL_NEXT; | ||
928 | dqp->HL_NEXT = d; | ||
929 | dqp->HL_PREVP = &qh->qh_next; | ||
930 | qh->qh_next = dqp; | ||
931 | } | ||
932 | xfs_dqtrace_entry(dqp, "LOOKUP END"); | ||
933 | *O_dqpp = dqp; | ||
934 | ASSERT(XFS_DQ_IS_HASH_LOCKED(qh)); | ||
935 | return (0); | ||
936 | } | ||
937 | } | ||
938 | |||
939 | *O_dqpp = NULL; | ||
940 | ASSERT(XFS_DQ_IS_HASH_LOCKED(qh)); | ||
941 | return (1); | ||
942 | } | ||
943 | |||
944 | /* | ||
945 | * Given the file system, inode OR id, and type (UDQUOT/GDQUOT), return a | ||
946 | * a locked dquot, doing an allocation (if requested) as needed. | ||
947 | * When both an inode and an id are given, the inode's id takes precedence. | ||
948 | * That is, if the id changes while we don't hold the ilock inside this | ||
949 | * function, the new dquot is returned, not necessarily the one requested | ||
950 | * in the id argument. | ||
951 | */ | ||
952 | int | ||
953 | xfs_qm_dqget( | ||
954 | xfs_mount_t *mp, | ||
955 | xfs_inode_t *ip, /* locked inode (optional) */ | ||
956 | xfs_dqid_t id, /* gid or uid, depending on type */ | ||
957 | uint type, /* UDQUOT or GDQUOT */ | ||
958 | uint flags, /* DQALLOC, DQSUSER, DQREPAIR, DOWARN */ | ||
959 | xfs_dquot_t **O_dqpp) /* OUT : locked incore dquot */ | ||
960 | { | ||
961 | xfs_dquot_t *dqp; | ||
962 | xfs_dqhash_t *h; | ||
963 | uint version; | ||
964 | int error; | ||
965 | |||
966 | ASSERT(XFS_IS_QUOTA_RUNNING(mp)); | ||
967 | if ((! XFS_IS_UQUOTA_ON(mp) && type == XFS_DQ_USER) || | ||
968 | (! XFS_IS_GQUOTA_ON(mp) && type == XFS_DQ_GROUP)) { | ||
969 | return (ESRCH); | ||
970 | } | ||
971 | h = XFS_DQ_HASH(mp, id, type); | ||
972 | |||
973 | #ifdef DEBUG | ||
974 | if (xfs_do_dqerror) { | ||
975 | if ((xfs_dqerror_target == mp->m_ddev_targp) && | ||
976 | (xfs_dqreq_num++ % xfs_dqerror_mod) == 0) { | ||
977 | cmn_err(CE_DEBUG, "Returning error in dqget"); | ||
978 | return (EIO); | ||
979 | } | ||
980 | } | ||
981 | #endif | ||
982 | |||
983 | again: | ||
984 | |||
985 | #ifdef DEBUG | ||
986 | ASSERT(type == XFS_DQ_USER || type == XFS_DQ_GROUP); | ||
987 | if (ip) { | ||
988 | ASSERT(XFS_ISLOCKED_INODE_EXCL(ip)); | ||
989 | if (type == XFS_DQ_USER) | ||
990 | ASSERT(ip->i_udquot == NULL); | ||
991 | else | ||
992 | ASSERT(ip->i_gdquot == NULL); | ||
993 | } | ||
994 | #endif | ||
995 | XFS_DQ_HASH_LOCK(h); | ||
996 | |||
997 | /* | ||
998 | * Look in the cache (hashtable). | ||
999 | * The chain is kept locked during lookup. | ||
1000 | */ | ||
1001 | if (xfs_qm_dqlookup(mp, id, h, O_dqpp) == 0) { | ||
1002 | XQM_STATS_INC(xqmstats.xs_qm_dqcachehits); | ||
1003 | /* | ||
1004 | * The dquot was found, moved to the front of the chain, | ||
1005 | * taken off the freelist if it was on it, and locked | ||
1006 | * at this point. Just unlock the hashchain and return. | ||
1007 | */ | ||
1008 | ASSERT(*O_dqpp); | ||
1009 | ASSERT(XFS_DQ_IS_LOCKED(*O_dqpp)); | ||
1010 | XFS_DQ_HASH_UNLOCK(h); | ||
1011 | xfs_dqtrace_entry(*O_dqpp, "DQGET DONE (FROM CACHE)"); | ||
1012 | return (0); /* success */ | ||
1013 | } | ||
1014 | XQM_STATS_INC(xqmstats.xs_qm_dqcachemisses); | ||
1015 | |||
1016 | /* | ||
1017 | * Dquot cache miss. We don't want to keep the inode lock across | ||
1018 | * a (potential) disk read. Also we don't want to deal with the lock | ||
1019 | * ordering between quotainode and this inode. OTOH, dropping the inode | ||
1020 | * lock here means dealing with a chown that can happen before | ||
1021 | * we re-acquire the lock. | ||
1022 | */ | ||
1023 | if (ip) | ||
1024 | xfs_iunlock(ip, XFS_ILOCK_EXCL); | ||
1025 | /* | ||
1026 | * Save the hashchain version stamp, and unlock the chain, so that | ||
1027 | * we don't keep the lock across a disk read | ||
1028 | */ | ||
1029 | version = h->qh_version; | ||
1030 | XFS_DQ_HASH_UNLOCK(h); | ||
1031 | |||
1032 | /* | ||
1033 | * Allocate the dquot on the kernel heap, and read the ondisk | ||
1034 | * portion off the disk. Also, do all the necessary initialization | ||
1035 | * This can return ENOENT if dquot didn't exist on disk and we didn't | ||
1036 | * ask it to allocate; ESRCH if quotas got turned off suddenly. | ||
1037 | */ | ||
1038 | if ((error = xfs_qm_idtodq(mp, id, type, | ||
1039 | flags & (XFS_QMOPT_DQALLOC|XFS_QMOPT_DQREPAIR| | ||
1040 | XFS_QMOPT_DOWARN), | ||
1041 | &dqp))) { | ||
1042 | if (ip) | ||
1043 | xfs_ilock(ip, XFS_ILOCK_EXCL); | ||
1044 | return (error); | ||
1045 | } | ||
1046 | |||
1047 | /* | ||
1048 | * See if this is mount code calling to look at the overall quota limits | ||
1049 | * which are stored in the id == 0 user or group's dquot. | ||
1050 | * Since we may not have done a quotacheck by this point, just return | ||
1051 | * the dquot without attaching it to any hashtables, lists, etc, or even | ||
1052 | * taking a reference. | ||
1053 | * The caller must dqdestroy this once done. | ||
1054 | */ | ||
1055 | if (flags & XFS_QMOPT_DQSUSER) { | ||
1056 | ASSERT(id == 0); | ||
1057 | ASSERT(! ip); | ||
1058 | goto dqret; | ||
1059 | } | ||
1060 | |||
1061 | /* | ||
1062 | * Dquot lock comes after hashlock in the lock ordering | ||
1063 | */ | ||
1064 | if (ip) { | ||
1065 | xfs_ilock(ip, XFS_ILOCK_EXCL); | ||
1066 | if (! XFS_IS_DQTYPE_ON(mp, type)) { | ||
1067 | /* inode stays locked on return */ | ||
1068 | xfs_qm_dqdestroy(dqp); | ||
1069 | return XFS_ERROR(ESRCH); | ||
1070 | } | ||
1071 | /* | ||
1072 | * A dquot could be attached to this inode by now, since | ||
1073 | * we had dropped the ilock. | ||
1074 | */ | ||
1075 | if (type == XFS_DQ_USER) { | ||
1076 | if (ip->i_udquot) { | ||
1077 | xfs_qm_dqdestroy(dqp); | ||
1078 | dqp = ip->i_udquot; | ||
1079 | xfs_dqlock(dqp); | ||
1080 | goto dqret; | ||
1081 | } | ||
1082 | } else { | ||
1083 | if (ip->i_gdquot) { | ||
1084 | xfs_qm_dqdestroy(dqp); | ||
1085 | dqp = ip->i_gdquot; | ||
1086 | xfs_dqlock(dqp); | ||
1087 | goto dqret; | ||
1088 | } | ||
1089 | } | ||
1090 | } | ||
1091 | |||
1092 | /* | ||
1093 | * Hashlock comes after ilock in lock order | ||
1094 | */ | ||
1095 | XFS_DQ_HASH_LOCK(h); | ||
1096 | if (version != h->qh_version) { | ||
1097 | xfs_dquot_t *tmpdqp; | ||
1098 | /* | ||
1099 | * Now, see if somebody else put the dquot in the | ||
1100 | * hashtable before us. This can happen because we didn't | ||
1101 | * keep the hashchain lock. We don't have to worry about | ||
1102 | * lock order between the two dquots here since dqp isn't | ||
1103 | * on any findable lists yet. | ||
1104 | */ | ||
1105 | if (xfs_qm_dqlookup(mp, id, h, &tmpdqp) == 0) { | ||
1106 | /* | ||
1107 | * Duplicate found. Just throw away the new dquot | ||
1108 | * and start over. | ||
1109 | */ | ||
1110 | xfs_qm_dqput(tmpdqp); | ||
1111 | XFS_DQ_HASH_UNLOCK(h); | ||
1112 | xfs_qm_dqdestroy(dqp); | ||
1113 | XQM_STATS_INC(xqmstats.xs_qm_dquot_dups); | ||
1114 | goto again; | ||
1115 | } | ||
1116 | } | ||
1117 | |||
1118 | /* | ||
1119 | * Put the dquot at the beginning of the hash-chain and mp's list | ||
1120 | * LOCK ORDER: hashlock, freelistlock, mplistlock, udqlock, gdqlock .. | ||
1121 | */ | ||
1122 | ASSERT(XFS_DQ_IS_HASH_LOCKED(h)); | ||
1123 | dqp->q_hash = h; | ||
1124 | XQM_HASHLIST_INSERT(h, dqp); | ||
1125 | |||
1126 | /* | ||
1127 | * Attach this dquot to this filesystem's list of all dquots, | ||
1128 | * kept inside the mount structure in m_quotainfo field | ||
1129 | */ | ||
1130 | xfs_qm_mplist_lock(mp); | ||
1131 | |||
1132 | /* | ||
1133 | * We return a locked dquot to the caller, with a reference taken | ||
1134 | */ | ||
1135 | xfs_dqlock(dqp); | ||
1136 | dqp->q_nrefs = 1; | ||
1137 | |||
1138 | XQM_MPLIST_INSERT(&(XFS_QI_MPL_LIST(mp)), dqp); | ||
1139 | |||
1140 | xfs_qm_mplist_unlock(mp); | ||
1141 | XFS_DQ_HASH_UNLOCK(h); | ||
1142 | dqret: | ||
1143 | ASSERT((ip == NULL) || XFS_ISLOCKED_INODE_EXCL(ip)); | ||
1144 | xfs_dqtrace_entry(dqp, "DQGET DONE"); | ||
1145 | *O_dqpp = dqp; | ||
1146 | return (0); | ||
1147 | } | ||
1148 | |||
1149 | |||
1150 | /* | ||
1151 | * Release a reference to the dquot (decrement ref-count) | ||
1152 | * and unlock it. If there is a group quota attached to this | ||
1153 | * dquot, carefully release that too without tripping over | ||
1154 | * deadlocks'n'stuff. | ||
1155 | */ | ||
1156 | void | ||
1157 | xfs_qm_dqput( | ||
1158 | xfs_dquot_t *dqp) | ||
1159 | { | ||
1160 | xfs_dquot_t *gdqp; | ||
1161 | |||
1162 | ASSERT(dqp->q_nrefs > 0); | ||
1163 | ASSERT(XFS_DQ_IS_LOCKED(dqp)); | ||
1164 | xfs_dqtrace_entry(dqp, "DQPUT"); | ||
1165 | |||
1166 | if (dqp->q_nrefs != 1) { | ||
1167 | dqp->q_nrefs--; | ||
1168 | xfs_dqunlock(dqp); | ||
1169 | return; | ||
1170 | } | ||
1171 | |||
1172 | /* | ||
1173 | * drop the dqlock and acquire the freelist and dqlock | ||
1174 | * in the right order; but try to get it out-of-order first | ||
1175 | */ | ||
1176 | if (! xfs_qm_freelist_lock_nowait(xfs_Gqm)) { | ||
1177 | xfs_dqtrace_entry(dqp, "DQPUT: FLLOCK-WAIT"); | ||
1178 | xfs_dqunlock(dqp); | ||
1179 | xfs_qm_freelist_lock(xfs_Gqm); | ||
1180 | xfs_dqlock(dqp); | ||
1181 | } | ||
1182 | |||
1183 | while (1) { | ||
1184 | gdqp = NULL; | ||
1185 | |||
1186 | /* We can't depend on nrefs being == 1 here */ | ||
1187 | if (--dqp->q_nrefs == 0) { | ||
1188 | xfs_dqtrace_entry(dqp, "DQPUT: ON FREELIST"); | ||
1189 | /* | ||
1190 | * insert at end of the freelist. | ||
1191 | */ | ||
1192 | XQM_FREELIST_INSERT(&(xfs_Gqm->qm_dqfreelist), dqp); | ||
1193 | |||
1194 | /* | ||
1195 | * If we just added a udquot to the freelist, then | ||
1196 | * we want to release the gdquot reference that | ||
1197 | * it (probably) has. Otherwise it'll keep the | ||
1198 | * gdquot from getting reclaimed. | ||
1199 | */ | ||
1200 | if ((gdqp = dqp->q_gdquot)) { | ||
1201 | /* | ||
1202 | * Avoid a recursive dqput call | ||
1203 | */ | ||
1204 | xfs_dqlock(gdqp); | ||
1205 | dqp->q_gdquot = NULL; | ||
1206 | } | ||
1207 | |||
1208 | /* xfs_qm_freelist_print(&(xfs_Gqm->qm_dqfreelist), | ||
1209 | "@@@@@++ Free list (after append) @@@@@+"); | ||
1210 | */ | ||
1211 | } | ||
1212 | xfs_dqunlock(dqp); | ||
1213 | |||
1214 | /* | ||
1215 | * If we had a group quota inside the user quota as a hint, | ||
1216 | * release it now. | ||
1217 | */ | ||
1218 | if (! gdqp) | ||
1219 | break; | ||
1220 | dqp = gdqp; | ||
1221 | } | ||
1222 | xfs_qm_freelist_unlock(xfs_Gqm); | ||
1223 | } | ||
1224 | |||
1225 | /* | ||
1226 | * Release a dquot. Flush it if dirty, then dqput() it. | ||
1227 | * dquot must not be locked. | ||
1228 | */ | ||
1229 | void | ||
1230 | xfs_qm_dqrele( | ||
1231 | xfs_dquot_t *dqp) | ||
1232 | { | ||
1233 | ASSERT(dqp); | ||
1234 | xfs_dqtrace_entry(dqp, "DQRELE"); | ||
1235 | |||
1236 | xfs_dqlock(dqp); | ||
1237 | /* | ||
1238 | * We don't care to flush it if the dquot is dirty here. | ||
1239 | * That will create stutters that we want to avoid. | ||
1240 | * Instead we do a delayed write when we try to reclaim | ||
1241 | * a dirty dquot. Also xfs_sync will take part of the burden... | ||
1242 | */ | ||
1243 | xfs_qm_dqput(dqp); | ||
1244 | } | ||
1245 | |||
1246 | |||
1247 | /* | ||
1248 | * Write a modified dquot to disk. | ||
1249 | * The dquot must be locked and the flush lock too taken by caller. | ||
1250 | * The flush lock will not be unlocked until the dquot reaches the disk, | ||
1251 | * but the dquot is free to be unlocked and modified by the caller | ||
1252 | * in the interim. Dquot is still locked on return. This behavior is | ||
1253 | * identical to that of inodes. | ||
1254 | */ | ||
1255 | int | ||
1256 | xfs_qm_dqflush( | ||
1257 | xfs_dquot_t *dqp, | ||
1258 | uint flags) | ||
1259 | { | ||
1260 | xfs_mount_t *mp; | ||
1261 | xfs_buf_t *bp; | ||
1262 | xfs_disk_dquot_t *ddqp; | ||
1263 | int error; | ||
1264 | SPLDECL(s); | ||
1265 | |||
1266 | ASSERT(XFS_DQ_IS_LOCKED(dqp)); | ||
1267 | ASSERT(XFS_DQ_IS_FLUSH_LOCKED(dqp)); | ||
1268 | xfs_dqtrace_entry(dqp, "DQFLUSH"); | ||
1269 | |||
1270 | /* | ||
1271 | * If not dirty, nada. | ||
1272 | */ | ||
1273 | if (!XFS_DQ_IS_DIRTY(dqp)) { | ||
1274 | xfs_dqfunlock(dqp); | ||
1275 | return (0); | ||
1276 | } | ||
1277 | |||
1278 | /* | ||
1279 | * Cant flush a pinned dquot. Wait for it. | ||
1280 | */ | ||
1281 | xfs_qm_dqunpin_wait(dqp); | ||
1282 | |||
1283 | /* | ||
1284 | * This may have been unpinned because the filesystem is shutting | ||
1285 | * down forcibly. If that's the case we must not write this dquot | ||
1286 | * to disk, because the log record didn't make it to disk! | ||
1287 | */ | ||
1288 | if (XFS_FORCED_SHUTDOWN(dqp->q_mount)) { | ||
1289 | dqp->dq_flags &= ~(XFS_DQ_DIRTY); | ||
1290 | xfs_dqfunlock(dqp); | ||
1291 | return XFS_ERROR(EIO); | ||
1292 | } | ||
1293 | |||
1294 | /* | ||
1295 | * Get the buffer containing the on-disk dquot | ||
1296 | * We don't need a transaction envelope because we know that the | ||
1297 | * the ondisk-dquot has already been allocated for. | ||
1298 | */ | ||
1299 | if ((error = xfs_qm_dqtobp(NULL, dqp, &ddqp, &bp, XFS_QMOPT_DOWARN))) { | ||
1300 | xfs_dqtrace_entry(dqp, "DQTOBP FAIL"); | ||
1301 | ASSERT(error != ENOENT); | ||
1302 | /* | ||
1303 | * Quotas could have gotten turned off (ESRCH) | ||
1304 | */ | ||
1305 | xfs_dqfunlock(dqp); | ||
1306 | return (error); | ||
1307 | } | ||
1308 | |||
1309 | if (xfs_qm_dqcheck(&dqp->q_core, INT_GET(ddqp->d_id, ARCH_CONVERT), 0, XFS_QMOPT_DOWARN, | ||
1310 | "dqflush (incore copy)")) { | ||
1311 | xfs_force_shutdown(dqp->q_mount, XFS_CORRUPT_INCORE); | ||
1312 | return XFS_ERROR(EIO); | ||
1313 | } | ||
1314 | |||
1315 | /* This is the only portion of data that needs to persist */ | ||
1316 | memcpy(ddqp, &(dqp->q_core), sizeof(xfs_disk_dquot_t)); | ||
1317 | |||
1318 | /* | ||
1319 | * Clear the dirty field and remember the flush lsn for later use. | ||
1320 | */ | ||
1321 | dqp->dq_flags &= ~(XFS_DQ_DIRTY); | ||
1322 | mp = dqp->q_mount; | ||
1323 | |||
1324 | /* lsn is 64 bits */ | ||
1325 | AIL_LOCK(mp, s); | ||
1326 | dqp->q_logitem.qli_flush_lsn = dqp->q_logitem.qli_item.li_lsn; | ||
1327 | AIL_UNLOCK(mp, s); | ||
1328 | |||
1329 | /* | ||
1330 | * Attach an iodone routine so that we can remove this dquot from the | ||
1331 | * AIL and release the flush lock once the dquot is synced to disk. | ||
1332 | */ | ||
1333 | xfs_buf_attach_iodone(bp, (void(*)(xfs_buf_t *, xfs_log_item_t *)) | ||
1334 | xfs_qm_dqflush_done, &(dqp->q_logitem.qli_item)); | ||
1335 | /* | ||
1336 | * If the buffer is pinned then push on the log so we won't | ||
1337 | * get stuck waiting in the write for too long. | ||
1338 | */ | ||
1339 | if (XFS_BUF_ISPINNED(bp)) { | ||
1340 | xfs_dqtrace_entry(dqp, "DQFLUSH LOG FORCE"); | ||
1341 | xfs_log_force(mp, (xfs_lsn_t)0, XFS_LOG_FORCE); | ||
1342 | } | ||
1343 | |||
1344 | if (flags & XFS_QMOPT_DELWRI) { | ||
1345 | xfs_bdwrite(mp, bp); | ||
1346 | } else if (flags & XFS_QMOPT_ASYNC) { | ||
1347 | xfs_bawrite(mp, bp); | ||
1348 | } else { | ||
1349 | error = xfs_bwrite(mp, bp); | ||
1350 | } | ||
1351 | xfs_dqtrace_entry(dqp, "DQFLUSH END"); | ||
1352 | /* | ||
1353 | * dqp is still locked, but caller is free to unlock it now. | ||
1354 | */ | ||
1355 | return (error); | ||
1356 | |||
1357 | } | ||
1358 | |||
1359 | /* | ||
1360 | * This is the dquot flushing I/O completion routine. It is called | ||
1361 | * from interrupt level when the buffer containing the dquot is | ||
1362 | * flushed to disk. It is responsible for removing the dquot logitem | ||
1363 | * from the AIL if it has not been re-logged, and unlocking the dquot's | ||
1364 | * flush lock. This behavior is very similar to that of inodes.. | ||
1365 | */ | ||
1366 | /*ARGSUSED*/ | ||
1367 | STATIC void | ||
1368 | xfs_qm_dqflush_done( | ||
1369 | xfs_buf_t *bp, | ||
1370 | xfs_dq_logitem_t *qip) | ||
1371 | { | ||
1372 | xfs_dquot_t *dqp; | ||
1373 | SPLDECL(s); | ||
1374 | |||
1375 | dqp = qip->qli_dquot; | ||
1376 | |||
1377 | /* | ||
1378 | * We only want to pull the item from the AIL if its | ||
1379 | * location in the log has not changed since we started the flush. | ||
1380 | * Thus, we only bother if the dquot's lsn has | ||
1381 | * not changed. First we check the lsn outside the lock | ||
1382 | * since it's cheaper, and then we recheck while | ||
1383 | * holding the lock before removing the dquot from the AIL. | ||
1384 | */ | ||
1385 | if ((qip->qli_item.li_flags & XFS_LI_IN_AIL) && | ||
1386 | qip->qli_item.li_lsn == qip->qli_flush_lsn) { | ||
1387 | |||
1388 | AIL_LOCK(dqp->q_mount, s); | ||
1389 | /* | ||
1390 | * xfs_trans_delete_ail() drops the AIL lock. | ||
1391 | */ | ||
1392 | if (qip->qli_item.li_lsn == qip->qli_flush_lsn) | ||
1393 | xfs_trans_delete_ail(dqp->q_mount, | ||
1394 | (xfs_log_item_t*)qip, s); | ||
1395 | else | ||
1396 | AIL_UNLOCK(dqp->q_mount, s); | ||
1397 | } | ||
1398 | |||
1399 | /* | ||
1400 | * Release the dq's flush lock since we're done with it. | ||
1401 | */ | ||
1402 | xfs_dqfunlock(dqp); | ||
1403 | } | ||
1404 | |||
1405 | |||
1406 | int | ||
1407 | xfs_qm_dqflock_nowait( | ||
1408 | xfs_dquot_t *dqp) | ||
1409 | { | ||
1410 | int locked; | ||
1411 | |||
1412 | locked = cpsema(&((dqp)->q_flock)); | ||
1413 | |||
1414 | /* XXX ifdef these out */ | ||
1415 | if (locked) | ||
1416 | (dqp)->dq_flags |= XFS_DQ_FLOCKED; | ||
1417 | return (locked); | ||
1418 | } | ||
1419 | |||
1420 | |||
1421 | int | ||
1422 | xfs_qm_dqlock_nowait( | ||
1423 | xfs_dquot_t *dqp) | ||
1424 | { | ||
1425 | return (mutex_trylock(&((dqp)->q_qlock))); | ||
1426 | } | ||
1427 | |||
1428 | void | ||
1429 | xfs_dqlock( | ||
1430 | xfs_dquot_t *dqp) | ||
1431 | { | ||
1432 | mutex_lock(&(dqp->q_qlock), PINOD); | ||
1433 | } | ||
1434 | |||
1435 | void | ||
1436 | xfs_dqunlock( | ||
1437 | xfs_dquot_t *dqp) | ||
1438 | { | ||
1439 | mutex_unlock(&(dqp->q_qlock)); | ||
1440 | if (dqp->q_logitem.qli_dquot == dqp) { | ||
1441 | /* Once was dqp->q_mount, but might just have been cleared */ | ||
1442 | xfs_trans_unlocked_item(dqp->q_logitem.qli_item.li_mountp, | ||
1443 | (xfs_log_item_t*)&(dqp->q_logitem)); | ||
1444 | } | ||
1445 | } | ||
1446 | |||
1447 | |||
1448 | void | ||
1449 | xfs_dqunlock_nonotify( | ||
1450 | xfs_dquot_t *dqp) | ||
1451 | { | ||
1452 | mutex_unlock(&(dqp->q_qlock)); | ||
1453 | } | ||
1454 | |||
1455 | void | ||
1456 | xfs_dqlock2( | ||
1457 | xfs_dquot_t *d1, | ||
1458 | xfs_dquot_t *d2) | ||
1459 | { | ||
1460 | if (d1 && d2) { | ||
1461 | ASSERT(d1 != d2); | ||
1462 | if (INT_GET(d1->q_core.d_id, ARCH_CONVERT) > INT_GET(d2->q_core.d_id, ARCH_CONVERT)) { | ||
1463 | xfs_dqlock(d2); | ||
1464 | xfs_dqlock(d1); | ||
1465 | } else { | ||
1466 | xfs_dqlock(d1); | ||
1467 | xfs_dqlock(d2); | ||
1468 | } | ||
1469 | } else { | ||
1470 | if (d1) { | ||
1471 | xfs_dqlock(d1); | ||
1472 | } else if (d2) { | ||
1473 | xfs_dqlock(d2); | ||
1474 | } | ||
1475 | } | ||
1476 | } | ||
1477 | |||
1478 | |||
1479 | /* | ||
1480 | * Take a dquot out of the mount's dqlist as well as the hashlist. | ||
1481 | * This is called via unmount as well as quotaoff, and the purge | ||
1482 | * will always succeed unless there are soft (temp) references | ||
1483 | * outstanding. | ||
1484 | * | ||
1485 | * This returns 0 if it was purged, 1 if it wasn't. It's not an error code | ||
1486 | * that we're returning! XXXsup - not cool. | ||
1487 | */ | ||
1488 | /* ARGSUSED */ | ||
1489 | int | ||
1490 | xfs_qm_dqpurge( | ||
1491 | xfs_dquot_t *dqp, | ||
1492 | uint flags) | ||
1493 | { | ||
1494 | xfs_dqhash_t *thishash; | ||
1495 | xfs_mount_t *mp; | ||
1496 | |||
1497 | mp = dqp->q_mount; | ||
1498 | |||
1499 | ASSERT(XFS_QM_IS_MPLIST_LOCKED(mp)); | ||
1500 | ASSERT(XFS_DQ_IS_HASH_LOCKED(dqp->q_hash)); | ||
1501 | |||
1502 | xfs_dqlock(dqp); | ||
1503 | /* | ||
1504 | * We really can't afford to purge a dquot that is | ||
1505 | * referenced, because these are hard refs. | ||
1506 | * It shouldn't happen in general because we went thru _all_ inodes in | ||
1507 | * dqrele_all_inodes before calling this and didn't let the mountlock go. | ||
1508 | * However it is possible that we have dquots with temporary | ||
1509 | * references that are not attached to an inode. e.g. see xfs_setattr(). | ||
1510 | */ | ||
1511 | if (dqp->q_nrefs != 0) { | ||
1512 | xfs_dqunlock(dqp); | ||
1513 | XFS_DQ_HASH_UNLOCK(dqp->q_hash); | ||
1514 | return (1); | ||
1515 | } | ||
1516 | |||
1517 | ASSERT(XFS_DQ_IS_ON_FREELIST(dqp)); | ||
1518 | |||
1519 | /* | ||
1520 | * If we're turning off quotas, we have to make sure that, for | ||
1521 | * example, we don't delete quota disk blocks while dquots are | ||
1522 | * in the process of getting written to those disk blocks. | ||
1523 | * This dquot might well be on AIL, and we can't leave it there | ||
1524 | * if we're turning off quotas. Basically, we need this flush | ||
1525 | * lock, and are willing to block on it. | ||
1526 | */ | ||
1527 | if (! xfs_qm_dqflock_nowait(dqp)) { | ||
1528 | /* | ||
1529 | * Block on the flush lock after nudging dquot buffer, | ||
1530 | * if it is incore. | ||
1531 | */ | ||
1532 | xfs_qm_dqflock_pushbuf_wait(dqp); | ||
1533 | } | ||
1534 | |||
1535 | /* | ||
1536 | * XXXIf we're turning this type of quotas off, we don't care | ||
1537 | * about the dirty metadata sitting in this dquot. OTOH, if | ||
1538 | * we're unmounting, we do care, so we flush it and wait. | ||
1539 | */ | ||
1540 | if (XFS_DQ_IS_DIRTY(dqp)) { | ||
1541 | xfs_dqtrace_entry(dqp, "DQPURGE ->DQFLUSH: DQDIRTY"); | ||
1542 | /* dqflush unlocks dqflock */ | ||
1543 | /* | ||
1544 | * Given that dqpurge is a very rare occurrence, it is OK | ||
1545 | * that we're holding the hashlist and mplist locks | ||
1546 | * across the disk write. But, ... XXXsup | ||
1547 | * | ||
1548 | * We don't care about getting disk errors here. We need | ||
1549 | * to purge this dquot anyway, so we go ahead regardless. | ||
1550 | */ | ||
1551 | (void) xfs_qm_dqflush(dqp, XFS_QMOPT_SYNC); | ||
1552 | xfs_dqflock(dqp); | ||
1553 | } | ||
1554 | ASSERT(dqp->q_pincount == 0); | ||
1555 | ASSERT(XFS_FORCED_SHUTDOWN(mp) || | ||
1556 | !(dqp->q_logitem.qli_item.li_flags & XFS_LI_IN_AIL)); | ||
1557 | |||
1558 | thishash = dqp->q_hash; | ||
1559 | XQM_HASHLIST_REMOVE(thishash, dqp); | ||
1560 | XQM_MPLIST_REMOVE(&(XFS_QI_MPL_LIST(mp)), dqp); | ||
1561 | /* | ||
1562 | * XXX Move this to the front of the freelist, if we can get the | ||
1563 | * freelist lock. | ||
1564 | */ | ||
1565 | ASSERT(XFS_DQ_IS_ON_FREELIST(dqp)); | ||
1566 | |||
1567 | dqp->q_mount = NULL; | ||
1568 | dqp->q_hash = NULL; | ||
1569 | dqp->dq_flags = XFS_DQ_INACTIVE; | ||
1570 | memset(&dqp->q_core, 0, sizeof(dqp->q_core)); | ||
1571 | xfs_dqfunlock(dqp); | ||
1572 | xfs_dqunlock(dqp); | ||
1573 | XFS_DQ_HASH_UNLOCK(thishash); | ||
1574 | return (0); | ||
1575 | } | ||
1576 | |||
1577 | |||
1578 | #ifdef QUOTADEBUG | ||
1579 | void | ||
1580 | xfs_qm_dqprint(xfs_dquot_t *dqp) | ||
1581 | { | ||
1582 | cmn_err(CE_DEBUG, "-----------KERNEL DQUOT----------------"); | ||
1583 | cmn_err(CE_DEBUG, "---- dquotID = %d", | ||
1584 | (int)INT_GET(dqp->q_core.d_id, ARCH_CONVERT)); | ||
1585 | cmn_err(CE_DEBUG, "---- type = %s", | ||
1586 | XFS_QM_ISUDQ(dqp) ? "USR" : "GRP"); | ||
1587 | cmn_err(CE_DEBUG, "---- fs = 0x%p", dqp->q_mount); | ||
1588 | cmn_err(CE_DEBUG, "---- blkno = 0x%x", (int) dqp->q_blkno); | ||
1589 | cmn_err(CE_DEBUG, "---- boffset = 0x%x", (int) dqp->q_bufoffset); | ||
1590 | cmn_err(CE_DEBUG, "---- blkhlimit = %Lu (0x%x)", | ||
1591 | INT_GET(dqp->q_core.d_blk_hardlimit, ARCH_CONVERT), | ||
1592 | (int) INT_GET(dqp->q_core.d_blk_hardlimit, ARCH_CONVERT)); | ||
1593 | cmn_err(CE_DEBUG, "---- blkslimit = %Lu (0x%x)", | ||
1594 | INT_GET(dqp->q_core.d_blk_softlimit, ARCH_CONVERT), | ||
1595 | (int)INT_GET(dqp->q_core.d_blk_softlimit, ARCH_CONVERT)); | ||
1596 | cmn_err(CE_DEBUG, "---- inohlimit = %Lu (0x%x)", | ||
1597 | INT_GET(dqp->q_core.d_ino_hardlimit, ARCH_CONVERT), | ||
1598 | (int)INT_GET(dqp->q_core.d_ino_hardlimit, ARCH_CONVERT)); | ||
1599 | cmn_err(CE_DEBUG, "---- inoslimit = %Lu (0x%x)", | ||
1600 | INT_GET(dqp->q_core.d_ino_softlimit, ARCH_CONVERT), | ||
1601 | (int)INT_GET(dqp->q_core.d_ino_softlimit, ARCH_CONVERT)); | ||
1602 | cmn_err(CE_DEBUG, "---- bcount = %Lu (0x%x)", | ||
1603 | INT_GET(dqp->q_core.d_bcount, ARCH_CONVERT), | ||
1604 | (int)INT_GET(dqp->q_core.d_bcount, ARCH_CONVERT)); | ||
1605 | cmn_err(CE_DEBUG, "---- icount = %Lu (0x%x)", | ||
1606 | INT_GET(dqp->q_core.d_icount, ARCH_CONVERT), | ||
1607 | (int)INT_GET(dqp->q_core.d_icount, ARCH_CONVERT)); | ||
1608 | cmn_err(CE_DEBUG, "---- btimer = %d", | ||
1609 | (int)INT_GET(dqp->q_core.d_btimer, ARCH_CONVERT)); | ||
1610 | cmn_err(CE_DEBUG, "---- itimer = %d", | ||
1611 | (int)INT_GET(dqp->q_core.d_itimer, ARCH_CONVERT)); | ||
1612 | cmn_err(CE_DEBUG, "---------------------------"); | ||
1613 | } | ||
1614 | #endif | ||
1615 | |||
1616 | /* | ||
1617 | * Give the buffer a little push if it is incore and | ||
1618 | * wait on the flush lock. | ||
1619 | */ | ||
1620 | void | ||
1621 | xfs_qm_dqflock_pushbuf_wait( | ||
1622 | xfs_dquot_t *dqp) | ||
1623 | { | ||
1624 | xfs_buf_t *bp; | ||
1625 | |||
1626 | /* | ||
1627 | * Check to see if the dquot has been flushed delayed | ||
1628 | * write. If so, grab its buffer and send it | ||
1629 | * out immediately. We'll be able to acquire | ||
1630 | * the flush lock when the I/O completes. | ||
1631 | */ | ||
1632 | bp = xfs_incore(dqp->q_mount->m_ddev_targp, dqp->q_blkno, | ||
1633 | XFS_QI_DQCHUNKLEN(dqp->q_mount), | ||
1634 | XFS_INCORE_TRYLOCK); | ||
1635 | if (bp != NULL) { | ||
1636 | if (XFS_BUF_ISDELAYWRITE(bp)) { | ||
1637 | if (XFS_BUF_ISPINNED(bp)) { | ||
1638 | xfs_log_force(dqp->q_mount, | ||
1639 | (xfs_lsn_t)0, | ||
1640 | XFS_LOG_FORCE); | ||
1641 | } | ||
1642 | xfs_bawrite(dqp->q_mount, bp); | ||
1643 | } else { | ||
1644 | xfs_buf_relse(bp); | ||
1645 | } | ||
1646 | } | ||
1647 | xfs_dqflock(dqp); | ||
1648 | } | ||
diff --git a/fs/xfs/quota/xfs_dquot.h b/fs/xfs/quota/xfs_dquot.h new file mode 100644 index 000000000000..0c3fe3175baa --- /dev/null +++ b/fs/xfs/quota/xfs_dquot.h | |||
@@ -0,0 +1,224 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms of version 2 of the GNU General Public License as | ||
6 | * published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope that it would be useful, but | ||
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | * | ||
12 | * Further, this software is distributed without any warranty that it is | ||
13 | * free of the rightful claim of any third person regarding infringement | ||
14 | * or the like. Any license provided herein, whether implied or | ||
15 | * otherwise, applies only to this software file. Patent licenses, if | ||
16 | * any, provided herein do not apply to combinations of this program with | ||
17 | * other software, or any other product whatsoever. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License along | ||
20 | * with this program; if not, write the Free Software Foundation, Inc., 59 | ||
21 | * Temple Place - Suite 330, Boston MA 02111-1307, USA. | ||
22 | * | ||
23 | * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, | ||
24 | * Mountain View, CA 94043, or: | ||
25 | * | ||
26 | * http://www.sgi.com | ||
27 | * | ||
28 | * For further information regarding this notice, see: | ||
29 | * | ||
30 | * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ | ||
31 | */ | ||
32 | #ifndef __XFS_DQUOT_H__ | ||
33 | #define __XFS_DQUOT_H__ | ||
34 | |||
35 | /* | ||
36 | * Dquots are structures that hold quota information about a user or a group, | ||
37 | * much like inodes are for files. In fact, dquots share many characteristics | ||
38 | * with inodes. However, dquots can also be a centralized resource, relative | ||
39 | * to a collection of inodes. In this respect, dquots share some characteristics | ||
40 | * of the superblock. | ||
41 | * XFS dquots exploit both those in its algorithms. They make every attempt | ||
42 | * to not be a bottleneck when quotas are on and have minimal impact, if any, | ||
43 | * when quotas are off. | ||
44 | */ | ||
45 | |||
46 | /* | ||
47 | * The hash chain headers (hash buckets) | ||
48 | */ | ||
49 | typedef struct xfs_dqhash { | ||
50 | struct xfs_dquot *qh_next; | ||
51 | mutex_t qh_lock; | ||
52 | uint qh_version; /* ever increasing version */ | ||
53 | uint qh_nelems; /* number of dquots on the list */ | ||
54 | } xfs_dqhash_t; | ||
55 | |||
56 | typedef struct xfs_dqlink { | ||
57 | struct xfs_dquot *ql_next; /* forward link */ | ||
58 | struct xfs_dquot **ql_prevp; /* pointer to prev ql_next */ | ||
59 | } xfs_dqlink_t; | ||
60 | |||
61 | struct xfs_mount; | ||
62 | struct xfs_trans; | ||
63 | |||
64 | /* | ||
65 | * This is the marker which is designed to occupy the first few | ||
66 | * bytes of the xfs_dquot_t structure. Even inside this, the freelist pointers | ||
67 | * must come first. | ||
68 | * This serves as the marker ("sentinel") when we have to restart list | ||
69 | * iterations because of locking considerations. | ||
70 | */ | ||
71 | typedef struct xfs_dqmarker { | ||
72 | struct xfs_dquot*dqm_flnext; /* link to freelist: must be first */ | ||
73 | struct xfs_dquot*dqm_flprev; | ||
74 | xfs_dqlink_t dqm_mplist; /* link to mount's list of dquots */ | ||
75 | xfs_dqlink_t dqm_hashlist; /* link to the hash chain */ | ||
76 | uint dqm_flags; /* various flags (XFS_DQ_*) */ | ||
77 | } xfs_dqmarker_t; | ||
78 | |||
79 | /* | ||
80 | * The incore dquot structure | ||
81 | */ | ||
82 | typedef struct xfs_dquot { | ||
83 | xfs_dqmarker_t q_lists; /* list ptrs, q_flags (marker) */ | ||
84 | xfs_dqhash_t *q_hash; /* the hashchain header */ | ||
85 | struct xfs_mount*q_mount; /* filesystem this relates to */ | ||
86 | struct xfs_trans*q_transp; /* trans this belongs to currently */ | ||
87 | uint q_nrefs; /* # active refs from inodes */ | ||
88 | xfs_daddr_t q_blkno; /* blkno of dquot buffer */ | ||
89 | int q_bufoffset; /* off of dq in buffer (# dquots) */ | ||
90 | xfs_fileoff_t q_fileoffset; /* offset in quotas file */ | ||
91 | |||
92 | struct xfs_dquot*q_gdquot; /* group dquot, hint only */ | ||
93 | xfs_disk_dquot_t q_core; /* actual usage & quotas */ | ||
94 | xfs_dq_logitem_t q_logitem; /* dquot log item */ | ||
95 | xfs_qcnt_t q_res_bcount; /* total regular nblks used+reserved */ | ||
96 | xfs_qcnt_t q_res_icount; /* total inos allocd+reserved */ | ||
97 | xfs_qcnt_t q_res_rtbcount;/* total realtime blks used+reserved */ | ||
98 | mutex_t q_qlock; /* quota lock */ | ||
99 | sema_t q_flock; /* flush lock */ | ||
100 | uint q_pincount; /* pin count for this dquot */ | ||
101 | sv_t q_pinwait; /* sync var for pinning */ | ||
102 | #ifdef XFS_DQUOT_TRACE | ||
103 | struct ktrace *q_trace; /* trace header structure */ | ||
104 | #endif | ||
105 | } xfs_dquot_t; | ||
106 | |||
107 | |||
108 | #define dq_flnext q_lists.dqm_flnext | ||
109 | #define dq_flprev q_lists.dqm_flprev | ||
110 | #define dq_mplist q_lists.dqm_mplist | ||
111 | #define dq_hashlist q_lists.dqm_hashlist | ||
112 | #define dq_flags q_lists.dqm_flags | ||
113 | |||
114 | #define XFS_DQHOLD(dqp) ((dqp)->q_nrefs++) | ||
115 | |||
116 | /* | ||
117 | * Quota Accounting flags | ||
118 | */ | ||
119 | #define XFS_ALL_QUOTA_ACCT (XFS_UQUOTA_ACCT | XFS_GQUOTA_ACCT) | ||
120 | #define XFS_ALL_QUOTA_ENFD (XFS_UQUOTA_ENFD | XFS_GQUOTA_ENFD) | ||
121 | #define XFS_ALL_QUOTA_CHKD (XFS_UQUOTA_CHKD | XFS_GQUOTA_CHKD) | ||
122 | #define XFS_ALL_QUOTA_ACTV (XFS_UQUOTA_ACTIVE | XFS_GQUOTA_ACTIVE) | ||
123 | #define XFS_ALL_QUOTA_ACCT_ENFD (XFS_UQUOTA_ACCT|XFS_UQUOTA_ENFD|\ | ||
124 | XFS_GQUOTA_ACCT|XFS_GQUOTA_ENFD) | ||
125 | |||
126 | #define XFS_IS_QUOTA_RUNNING(mp) ((mp)->m_qflags & XFS_ALL_QUOTA_ACCT) | ||
127 | #define XFS_IS_UQUOTA_RUNNING(mp) ((mp)->m_qflags & XFS_UQUOTA_ACCT) | ||
128 | #define XFS_IS_GQUOTA_RUNNING(mp) ((mp)->m_qflags & XFS_GQUOTA_ACCT) | ||
129 | |||
130 | /* | ||
131 | * Quota Limit Enforcement flags | ||
132 | */ | ||
133 | #define XFS_IS_QUOTA_ENFORCED(mp) ((mp)->m_qflags & XFS_ALL_QUOTA_ENFD) | ||
134 | #define XFS_IS_UQUOTA_ENFORCED(mp) ((mp)->m_qflags & XFS_UQUOTA_ENFD) | ||
135 | #define XFS_IS_GQUOTA_ENFORCED(mp) ((mp)->m_qflags & XFS_GQUOTA_ENFD) | ||
136 | |||
137 | #ifdef DEBUG | ||
138 | static inline int | ||
139 | XFS_DQ_IS_LOCKED(xfs_dquot_t *dqp) | ||
140 | { | ||
141 | if (mutex_trylock(&dqp->q_qlock)) { | ||
142 | mutex_unlock(&dqp->q_qlock); | ||
143 | return 0; | ||
144 | } | ||
145 | return 1; | ||
146 | } | ||
147 | #endif | ||
148 | |||
149 | |||
150 | /* | ||
151 | * The following three routines simply manage the q_flock | ||
152 | * semaphore embedded in the dquot. This semaphore synchronizes | ||
153 | * processes attempting to flush the in-core dquot back to disk. | ||
154 | */ | ||
155 | #define xfs_dqflock(dqp) { psema(&((dqp)->q_flock), PINOD | PRECALC);\ | ||
156 | (dqp)->dq_flags |= XFS_DQ_FLOCKED; } | ||
157 | #define xfs_dqfunlock(dqp) { ASSERT(valusema(&((dqp)->q_flock)) <= 0); \ | ||
158 | vsema(&((dqp)->q_flock)); \ | ||
159 | (dqp)->dq_flags &= ~(XFS_DQ_FLOCKED); } | ||
160 | |||
161 | #define XFS_DQ_PINLOCK(dqp) mutex_spinlock( \ | ||
162 | &(XFS_DQ_TO_QINF(dqp)->qi_pinlock)) | ||
163 | #define XFS_DQ_PINUNLOCK(dqp, s) mutex_spinunlock( \ | ||
164 | &(XFS_DQ_TO_QINF(dqp)->qi_pinlock), s) | ||
165 | |||
166 | #define XFS_DQ_IS_FLUSH_LOCKED(dqp) (valusema(&((dqp)->q_flock)) <= 0) | ||
167 | #define XFS_DQ_IS_ON_FREELIST(dqp) ((dqp)->dq_flnext != (dqp)) | ||
168 | #define XFS_DQ_IS_DIRTY(dqp) ((dqp)->dq_flags & XFS_DQ_DIRTY) | ||
169 | #define XFS_QM_ISUDQ(dqp) ((dqp)->dq_flags & XFS_DQ_USER) | ||
170 | #define XFS_DQ_TO_QINF(dqp) ((dqp)->q_mount->m_quotainfo) | ||
171 | #define XFS_DQ_TO_QIP(dqp) (XFS_QM_ISUDQ(dqp) ? \ | ||
172 | XFS_DQ_TO_QINF(dqp)->qi_uquotaip : \ | ||
173 | XFS_DQ_TO_QINF(dqp)->qi_gquotaip) | ||
174 | |||
175 | #define XFS_IS_THIS_QUOTA_OFF(d) (! (XFS_QM_ISUDQ(d) ? \ | ||
176 | (XFS_IS_UQUOTA_ON((d)->q_mount)) : \ | ||
177 | (XFS_IS_GQUOTA_ON((d)->q_mount)))) | ||
178 | |||
179 | #ifdef XFS_DQUOT_TRACE | ||
180 | /* | ||
181 | * Dquot Tracing stuff. | ||
182 | */ | ||
183 | #define DQUOT_TRACE_SIZE 64 | ||
184 | #define DQUOT_KTRACE_ENTRY 1 | ||
185 | |||
186 | extern void __xfs_dqtrace_entry(xfs_dquot_t *dqp, char *func, | ||
187 | void *, xfs_inode_t *); | ||
188 | #define xfs_dqtrace_entry_ino(a,b,ip) \ | ||
189 | __xfs_dqtrace_entry((a), (b), (void*)__return_address, (ip)) | ||
190 | #define xfs_dqtrace_entry(a,b) \ | ||
191 | __xfs_dqtrace_entry((a), (b), (void*)__return_address, NULL) | ||
192 | #else | ||
193 | #define xfs_dqtrace_entry(a,b) | ||
194 | #define xfs_dqtrace_entry_ino(a,b,ip) | ||
195 | #endif | ||
196 | |||
197 | #ifdef QUOTADEBUG | ||
198 | extern void xfs_qm_dqprint(xfs_dquot_t *); | ||
199 | #else | ||
200 | #define xfs_qm_dqprint(a) | ||
201 | #endif | ||
202 | |||
203 | extern void xfs_qm_dqdestroy(xfs_dquot_t *); | ||
204 | extern int xfs_qm_dqflush(xfs_dquot_t *, uint); | ||
205 | extern int xfs_qm_dqpurge(xfs_dquot_t *, uint); | ||
206 | extern void xfs_qm_dqunpin_wait(xfs_dquot_t *); | ||
207 | extern int xfs_qm_dqlock_nowait(xfs_dquot_t *); | ||
208 | extern int xfs_qm_dqflock_nowait(xfs_dquot_t *); | ||
209 | extern void xfs_qm_dqflock_pushbuf_wait(xfs_dquot_t *dqp); | ||
210 | extern void xfs_qm_adjust_dqtimers(xfs_mount_t *, | ||
211 | xfs_disk_dquot_t *); | ||
212 | extern void xfs_qm_adjust_dqlimits(xfs_mount_t *, | ||
213 | xfs_disk_dquot_t *); | ||
214 | extern int xfs_qm_dqwarn(xfs_disk_dquot_t *, uint); | ||
215 | extern int xfs_qm_dqget(xfs_mount_t *, xfs_inode_t *, | ||
216 | xfs_dqid_t, uint, uint, xfs_dquot_t **); | ||
217 | extern void xfs_qm_dqput(xfs_dquot_t *); | ||
218 | extern void xfs_qm_dqrele(xfs_dquot_t *); | ||
219 | extern void xfs_dqlock(xfs_dquot_t *); | ||
220 | extern void xfs_dqlock2(xfs_dquot_t *, xfs_dquot_t *); | ||
221 | extern void xfs_dqunlock(xfs_dquot_t *); | ||
222 | extern void xfs_dqunlock_nonotify(xfs_dquot_t *); | ||
223 | |||
224 | #endif /* __XFS_DQUOT_H__ */ | ||
diff --git a/fs/xfs/quota/xfs_dquot_item.c b/fs/xfs/quota/xfs_dquot_item.c new file mode 100644 index 000000000000..a5425ee6e7bd --- /dev/null +++ b/fs/xfs/quota/xfs_dquot_item.c | |||
@@ -0,0 +1,715 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms of version 2 of the GNU General Public License as | ||
6 | * published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope that it would be useful, but | ||
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | * | ||
12 | * Further, this software is distributed without any warranty that it is | ||
13 | * free of the rightful claim of any third person regarding infringement | ||
14 | * or the like. Any license provided herein, whether implied or | ||
15 | * otherwise, applies only to this software file. Patent licenses, if | ||
16 | * any, provided herein do not apply to combinations of this program with | ||
17 | * other software, or any other product whatsoever. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License along | ||
20 | * with this program; if not, write the Free Software Foundation, Inc., 59 | ||
21 | * Temple Place - Suite 330, Boston MA 02111-1307, USA. | ||
22 | * | ||
23 | * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, | ||
24 | * Mountain View, CA 94043, or: | ||
25 | * | ||
26 | * http://www.sgi.com | ||
27 | * | ||
28 | * For further information regarding this notice, see: | ||
29 | * | ||
30 | * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ | ||
31 | */ | ||
32 | |||
33 | #include "xfs.h" | ||
34 | #include "xfs_fs.h" | ||
35 | #include "xfs_inum.h" | ||
36 | #include "xfs_log.h" | ||
37 | #include "xfs_trans.h" | ||
38 | #include "xfs_sb.h" | ||
39 | #include "xfs_ag.h" | ||
40 | #include "xfs_dir.h" | ||
41 | #include "xfs_dir2.h" | ||
42 | #include "xfs_alloc.h" | ||
43 | #include "xfs_dmapi.h" | ||
44 | #include "xfs_quota.h" | ||
45 | #include "xfs_mount.h" | ||
46 | #include "xfs_alloc_btree.h" | ||
47 | #include "xfs_bmap_btree.h" | ||
48 | #include "xfs_ialloc_btree.h" | ||
49 | #include "xfs_btree.h" | ||
50 | #include "xfs_ialloc.h" | ||
51 | #include "xfs_attr_sf.h" | ||
52 | #include "xfs_dir_sf.h" | ||
53 | #include "xfs_dir2_sf.h" | ||
54 | #include "xfs_dinode.h" | ||
55 | #include "xfs_inode.h" | ||
56 | #include "xfs_bmap.h" | ||
57 | #include "xfs_bit.h" | ||
58 | #include "xfs_rtalloc.h" | ||
59 | #include "xfs_error.h" | ||
60 | #include "xfs_itable.h" | ||
61 | #include "xfs_rw.h" | ||
62 | #include "xfs_acl.h" | ||
63 | #include "xfs_cap.h" | ||
64 | #include "xfs_mac.h" | ||
65 | #include "xfs_attr.h" | ||
66 | #include "xfs_buf_item.h" | ||
67 | #include "xfs_trans_priv.h" | ||
68 | |||
69 | #include "xfs_qm.h" | ||
70 | |||
71 | |||
72 | /* | ||
73 | * returns the number of iovecs needed to log the given dquot item. | ||
74 | */ | ||
75 | /* ARGSUSED */ | ||
76 | STATIC uint | ||
77 | xfs_qm_dquot_logitem_size( | ||
78 | xfs_dq_logitem_t *logitem) | ||
79 | { | ||
80 | /* | ||
81 | * we need only two iovecs, one for the format, one for the real thing | ||
82 | */ | ||
83 | return (2); | ||
84 | } | ||
85 | |||
86 | /* | ||
87 | * fills in the vector of log iovecs for the given dquot log item. | ||
88 | */ | ||
89 | STATIC void | ||
90 | xfs_qm_dquot_logitem_format( | ||
91 | xfs_dq_logitem_t *logitem, | ||
92 | xfs_log_iovec_t *logvec) | ||
93 | { | ||
94 | ASSERT(logitem); | ||
95 | ASSERT(logitem->qli_dquot); | ||
96 | |||
97 | logvec->i_addr = (xfs_caddr_t)&logitem->qli_format; | ||
98 | logvec->i_len = sizeof(xfs_dq_logformat_t); | ||
99 | logvec++; | ||
100 | logvec->i_addr = (xfs_caddr_t)&logitem->qli_dquot->q_core; | ||
101 | logvec->i_len = sizeof(xfs_disk_dquot_t); | ||
102 | |||
103 | ASSERT(2 == logitem->qli_item.li_desc->lid_size); | ||
104 | logitem->qli_format.qlf_size = 2; | ||
105 | |||
106 | } | ||
107 | |||
108 | /* | ||
109 | * Increment the pin count of the given dquot. | ||
110 | * This value is protected by pinlock spinlock in the xQM structure. | ||
111 | */ | ||
112 | STATIC void | ||
113 | xfs_qm_dquot_logitem_pin( | ||
114 | xfs_dq_logitem_t *logitem) | ||
115 | { | ||
116 | unsigned long s; | ||
117 | xfs_dquot_t *dqp; | ||
118 | |||
119 | dqp = logitem->qli_dquot; | ||
120 | ASSERT(XFS_DQ_IS_LOCKED(dqp)); | ||
121 | s = XFS_DQ_PINLOCK(dqp); | ||
122 | dqp->q_pincount++; | ||
123 | XFS_DQ_PINUNLOCK(dqp, s); | ||
124 | } | ||
125 | |||
126 | /* | ||
127 | * Decrement the pin count of the given dquot, and wake up | ||
128 | * anyone in xfs_dqwait_unpin() if the count goes to 0. The | ||
129 | * dquot must have been previously pinned with a call to xfs_dqpin(). | ||
130 | */ | ||
131 | /* ARGSUSED */ | ||
132 | STATIC void | ||
133 | xfs_qm_dquot_logitem_unpin( | ||
134 | xfs_dq_logitem_t *logitem, | ||
135 | int stale) | ||
136 | { | ||
137 | unsigned long s; | ||
138 | xfs_dquot_t *dqp; | ||
139 | |||
140 | dqp = logitem->qli_dquot; | ||
141 | ASSERT(dqp->q_pincount > 0); | ||
142 | s = XFS_DQ_PINLOCK(dqp); | ||
143 | dqp->q_pincount--; | ||
144 | if (dqp->q_pincount == 0) { | ||
145 | sv_broadcast(&dqp->q_pinwait); | ||
146 | } | ||
147 | XFS_DQ_PINUNLOCK(dqp, s); | ||
148 | } | ||
149 | |||
150 | /* ARGSUSED */ | ||
151 | STATIC void | ||
152 | xfs_qm_dquot_logitem_unpin_remove( | ||
153 | xfs_dq_logitem_t *logitem, | ||
154 | xfs_trans_t *tp) | ||
155 | { | ||
156 | xfs_qm_dquot_logitem_unpin(logitem, 0); | ||
157 | } | ||
158 | |||
159 | /* | ||
160 | * Given the logitem, this writes the corresponding dquot entry to disk | ||
161 | * asynchronously. This is called with the dquot entry securely locked; | ||
162 | * we simply get xfs_qm_dqflush() to do the work, and unlock the dquot | ||
163 | * at the end. | ||
164 | */ | ||
165 | STATIC void | ||
166 | xfs_qm_dquot_logitem_push( | ||
167 | xfs_dq_logitem_t *logitem) | ||
168 | { | ||
169 | xfs_dquot_t *dqp; | ||
170 | |||
171 | dqp = logitem->qli_dquot; | ||
172 | |||
173 | ASSERT(XFS_DQ_IS_LOCKED(dqp)); | ||
174 | ASSERT(XFS_DQ_IS_FLUSH_LOCKED(dqp)); | ||
175 | |||
176 | /* | ||
177 | * Since we were able to lock the dquot's flush lock and | ||
178 | * we found it on the AIL, the dquot must be dirty. This | ||
179 | * is because the dquot is removed from the AIL while still | ||
180 | * holding the flush lock in xfs_dqflush_done(). Thus, if | ||
181 | * we found it in the AIL and were able to obtain the flush | ||
182 | * lock without sleeping, then there must not have been | ||
183 | * anyone in the process of flushing the dquot. | ||
184 | */ | ||
185 | xfs_qm_dqflush(dqp, XFS_B_DELWRI); | ||
186 | xfs_dqunlock(dqp); | ||
187 | } | ||
188 | |||
189 | /*ARGSUSED*/ | ||
190 | STATIC xfs_lsn_t | ||
191 | xfs_qm_dquot_logitem_committed( | ||
192 | xfs_dq_logitem_t *l, | ||
193 | xfs_lsn_t lsn) | ||
194 | { | ||
195 | /* | ||
196 | * We always re-log the entire dquot when it becomes dirty, | ||
197 | * so, the latest copy _is_ the only one that matters. | ||
198 | */ | ||
199 | return (lsn); | ||
200 | } | ||
201 | |||
202 | |||
203 | /* | ||
204 | * This is called to wait for the given dquot to be unpinned. | ||
205 | * Most of these pin/unpin routines are plagiarized from inode code. | ||
206 | */ | ||
207 | void | ||
208 | xfs_qm_dqunpin_wait( | ||
209 | xfs_dquot_t *dqp) | ||
210 | { | ||
211 | SPLDECL(s); | ||
212 | |||
213 | ASSERT(XFS_DQ_IS_LOCKED(dqp)); | ||
214 | if (dqp->q_pincount == 0) { | ||
215 | return; | ||
216 | } | ||
217 | |||
218 | /* | ||
219 | * Give the log a push so we don't wait here too long. | ||
220 | */ | ||
221 | xfs_log_force(dqp->q_mount, (xfs_lsn_t)0, XFS_LOG_FORCE); | ||
222 | s = XFS_DQ_PINLOCK(dqp); | ||
223 | if (dqp->q_pincount == 0) { | ||
224 | XFS_DQ_PINUNLOCK(dqp, s); | ||
225 | return; | ||
226 | } | ||
227 | sv_wait(&(dqp->q_pinwait), PINOD, | ||
228 | &(XFS_DQ_TO_QINF(dqp)->qi_pinlock), s); | ||
229 | } | ||
230 | |||
231 | /* | ||
232 | * This is called when IOP_TRYLOCK returns XFS_ITEM_PUSHBUF to indicate that | ||
233 | * the dquot is locked by us, but the flush lock isn't. So, here we are | ||
234 | * going to see if the relevant dquot buffer is incore, waiting on DELWRI. | ||
235 | * If so, we want to push it out to help us take this item off the AIL as soon | ||
236 | * as possible. | ||
237 | * | ||
238 | * We must not be holding the AIL_LOCK at this point. Calling incore() to | ||
239 | * search the buffercache can be a time consuming thing, and AIL_LOCK is a | ||
240 | * spinlock. | ||
241 | */ | ||
242 | STATIC void | ||
243 | xfs_qm_dquot_logitem_pushbuf( | ||
244 | xfs_dq_logitem_t *qip) | ||
245 | { | ||
246 | xfs_dquot_t *dqp; | ||
247 | xfs_mount_t *mp; | ||
248 | xfs_buf_t *bp; | ||
249 | uint dopush; | ||
250 | |||
251 | dqp = qip->qli_dquot; | ||
252 | ASSERT(XFS_DQ_IS_LOCKED(dqp)); | ||
253 | |||
254 | /* | ||
255 | * The qli_pushbuf_flag keeps others from | ||
256 | * trying to duplicate our effort. | ||
257 | */ | ||
258 | ASSERT(qip->qli_pushbuf_flag != 0); | ||
259 | ASSERT(qip->qli_push_owner == get_thread_id()); | ||
260 | |||
261 | /* | ||
262 | * If flushlock isn't locked anymore, chances are that the | ||
263 | * inode flush completed and the inode was taken off the AIL. | ||
264 | * So, just get out. | ||
265 | */ | ||
266 | if ((valusema(&(dqp->q_flock)) > 0) || | ||
267 | ((qip->qli_item.li_flags & XFS_LI_IN_AIL) == 0)) { | ||
268 | qip->qli_pushbuf_flag = 0; | ||
269 | xfs_dqunlock(dqp); | ||
270 | return; | ||
271 | } | ||
272 | mp = dqp->q_mount; | ||
273 | bp = xfs_incore(mp->m_ddev_targp, qip->qli_format.qlf_blkno, | ||
274 | XFS_QI_DQCHUNKLEN(mp), | ||
275 | XFS_INCORE_TRYLOCK); | ||
276 | if (bp != NULL) { | ||
277 | if (XFS_BUF_ISDELAYWRITE(bp)) { | ||
278 | dopush = ((qip->qli_item.li_flags & XFS_LI_IN_AIL) && | ||
279 | (valusema(&(dqp->q_flock)) <= 0)); | ||
280 | qip->qli_pushbuf_flag = 0; | ||
281 | xfs_dqunlock(dqp); | ||
282 | |||
283 | if (XFS_BUF_ISPINNED(bp)) { | ||
284 | xfs_log_force(mp, (xfs_lsn_t)0, | ||
285 | XFS_LOG_FORCE); | ||
286 | } | ||
287 | if (dopush) { | ||
288 | #ifdef XFSRACEDEBUG | ||
289 | delay_for_intr(); | ||
290 | delay(300); | ||
291 | #endif | ||
292 | xfs_bawrite(mp, bp); | ||
293 | } else { | ||
294 | xfs_buf_relse(bp); | ||
295 | } | ||
296 | } else { | ||
297 | qip->qli_pushbuf_flag = 0; | ||
298 | xfs_dqunlock(dqp); | ||
299 | xfs_buf_relse(bp); | ||
300 | } | ||
301 | return; | ||
302 | } | ||
303 | |||
304 | qip->qli_pushbuf_flag = 0; | ||
305 | xfs_dqunlock(dqp); | ||
306 | } | ||
307 | |||
308 | /* | ||
309 | * This is called to attempt to lock the dquot associated with this | ||
310 | * dquot log item. Don't sleep on the dquot lock or the flush lock. | ||
311 | * If the flush lock is already held, indicating that the dquot has | ||
312 | * been or is in the process of being flushed, then see if we can | ||
313 | * find the dquot's buffer in the buffer cache without sleeping. If | ||
314 | * we can and it is marked delayed write, then we want to send it out. | ||
315 | * We delay doing so until the push routine, though, to avoid sleeping | ||
316 | * in any device strategy routines. | ||
317 | */ | ||
318 | STATIC uint | ||
319 | xfs_qm_dquot_logitem_trylock( | ||
320 | xfs_dq_logitem_t *qip) | ||
321 | { | ||
322 | xfs_dquot_t *dqp; | ||
323 | uint retval; | ||
324 | |||
325 | dqp = qip->qli_dquot; | ||
326 | if (dqp->q_pincount > 0) | ||
327 | return (XFS_ITEM_PINNED); | ||
328 | |||
329 | if (! xfs_qm_dqlock_nowait(dqp)) | ||
330 | return (XFS_ITEM_LOCKED); | ||
331 | |||
332 | retval = XFS_ITEM_SUCCESS; | ||
333 | if (! xfs_qm_dqflock_nowait(dqp)) { | ||
334 | /* | ||
335 | * The dquot is already being flushed. It may have been | ||
336 | * flushed delayed write, however, and we don't want to | ||
337 | * get stuck waiting for that to complete. So, we want to check | ||
338 | * to see if we can lock the dquot's buffer without sleeping. | ||
339 | * If we can and it is marked for delayed write, then we | ||
340 | * hold it and send it out from the push routine. We don't | ||
341 | * want to do that now since we might sleep in the device | ||
342 | * strategy routine. We also don't want to grab the buffer lock | ||
343 | * here because we'd like not to call into the buffer cache | ||
344 | * while holding the AIL_LOCK. | ||
345 | * Make sure to only return PUSHBUF if we set pushbuf_flag | ||
346 | * ourselves. If someone else is doing it then we don't | ||
347 | * want to go to the push routine and duplicate their efforts. | ||
348 | */ | ||
349 | if (qip->qli_pushbuf_flag == 0) { | ||
350 | qip->qli_pushbuf_flag = 1; | ||
351 | ASSERT(qip->qli_format.qlf_blkno == dqp->q_blkno); | ||
352 | #ifdef DEBUG | ||
353 | qip->qli_push_owner = get_thread_id(); | ||
354 | #endif | ||
355 | /* | ||
356 | * The dquot is left locked. | ||
357 | */ | ||
358 | retval = XFS_ITEM_PUSHBUF; | ||
359 | } else { | ||
360 | retval = XFS_ITEM_FLUSHING; | ||
361 | xfs_dqunlock_nonotify(dqp); | ||
362 | } | ||
363 | } | ||
364 | |||
365 | ASSERT(qip->qli_item.li_flags & XFS_LI_IN_AIL); | ||
366 | return (retval); | ||
367 | } | ||
368 | |||
369 | |||
370 | /* | ||
371 | * Unlock the dquot associated with the log item. | ||
372 | * Clear the fields of the dquot and dquot log item that | ||
373 | * are specific to the current transaction. If the | ||
374 | * hold flags is set, do not unlock the dquot. | ||
375 | */ | ||
376 | STATIC void | ||
377 | xfs_qm_dquot_logitem_unlock( | ||
378 | xfs_dq_logitem_t *ql) | ||
379 | { | ||
380 | xfs_dquot_t *dqp; | ||
381 | |||
382 | ASSERT(ql != NULL); | ||
383 | dqp = ql->qli_dquot; | ||
384 | ASSERT(XFS_DQ_IS_LOCKED(dqp)); | ||
385 | |||
386 | /* | ||
387 | * Clear the transaction pointer in the dquot | ||
388 | */ | ||
389 | dqp->q_transp = NULL; | ||
390 | |||
391 | /* | ||
392 | * dquots are never 'held' from getting unlocked at the end of | ||
393 | * a transaction. Their locking and unlocking is hidden inside the | ||
394 | * transaction layer, within trans_commit. Hence, no LI_HOLD flag | ||
395 | * for the logitem. | ||
396 | */ | ||
397 | xfs_dqunlock(dqp); | ||
398 | } | ||
399 | |||
400 | |||
401 | /* | ||
402 | * The transaction with the dquot locked has aborted. The dquot | ||
403 | * must not be dirty within the transaction. We simply unlock just | ||
404 | * as if the transaction had been cancelled. | ||
405 | */ | ||
406 | STATIC void | ||
407 | xfs_qm_dquot_logitem_abort( | ||
408 | xfs_dq_logitem_t *ql) | ||
409 | { | ||
410 | xfs_qm_dquot_logitem_unlock(ql); | ||
411 | } | ||
412 | |||
413 | /* | ||
414 | * this needs to stamp an lsn into the dquot, I think. | ||
415 | * rpc's that look at user dquot's would then have to | ||
416 | * push on the dependency recorded in the dquot | ||
417 | */ | ||
418 | /* ARGSUSED */ | ||
419 | STATIC void | ||
420 | xfs_qm_dquot_logitem_committing( | ||
421 | xfs_dq_logitem_t *l, | ||
422 | xfs_lsn_t lsn) | ||
423 | { | ||
424 | return; | ||
425 | } | ||
426 | |||
427 | |||
428 | /* | ||
429 | * This is the ops vector for dquots | ||
430 | */ | ||
431 | struct xfs_item_ops xfs_dquot_item_ops = { | ||
432 | .iop_size = (uint(*)(xfs_log_item_t*))xfs_qm_dquot_logitem_size, | ||
433 | .iop_format = (void(*)(xfs_log_item_t*, xfs_log_iovec_t*)) | ||
434 | xfs_qm_dquot_logitem_format, | ||
435 | .iop_pin = (void(*)(xfs_log_item_t*))xfs_qm_dquot_logitem_pin, | ||
436 | .iop_unpin = (void(*)(xfs_log_item_t*, int)) | ||
437 | xfs_qm_dquot_logitem_unpin, | ||
438 | .iop_unpin_remove = (void(*)(xfs_log_item_t*, xfs_trans_t*)) | ||
439 | xfs_qm_dquot_logitem_unpin_remove, | ||
440 | .iop_trylock = (uint(*)(xfs_log_item_t*)) | ||
441 | xfs_qm_dquot_logitem_trylock, | ||
442 | .iop_unlock = (void(*)(xfs_log_item_t*))xfs_qm_dquot_logitem_unlock, | ||
443 | .iop_committed = (xfs_lsn_t(*)(xfs_log_item_t*, xfs_lsn_t)) | ||
444 | xfs_qm_dquot_logitem_committed, | ||
445 | .iop_push = (void(*)(xfs_log_item_t*))xfs_qm_dquot_logitem_push, | ||
446 | .iop_abort = (void(*)(xfs_log_item_t*))xfs_qm_dquot_logitem_abort, | ||
447 | .iop_pushbuf = (void(*)(xfs_log_item_t*)) | ||
448 | xfs_qm_dquot_logitem_pushbuf, | ||
449 | .iop_committing = (void(*)(xfs_log_item_t*, xfs_lsn_t)) | ||
450 | xfs_qm_dquot_logitem_committing | ||
451 | }; | ||
452 | |||
453 | /* | ||
454 | * Initialize the dquot log item for a newly allocated dquot. | ||
455 | * The dquot isn't locked at this point, but it isn't on any of the lists | ||
456 | * either, so we don't care. | ||
457 | */ | ||
458 | void | ||
459 | xfs_qm_dquot_logitem_init( | ||
460 | struct xfs_dquot *dqp) | ||
461 | { | ||
462 | xfs_dq_logitem_t *lp; | ||
463 | lp = &dqp->q_logitem; | ||
464 | |||
465 | lp->qli_item.li_type = XFS_LI_DQUOT; | ||
466 | lp->qli_item.li_ops = &xfs_dquot_item_ops; | ||
467 | lp->qli_item.li_mountp = dqp->q_mount; | ||
468 | lp->qli_dquot = dqp; | ||
469 | lp->qli_format.qlf_type = XFS_LI_DQUOT; | ||
470 | lp->qli_format.qlf_id = INT_GET(dqp->q_core.d_id, ARCH_CONVERT); | ||
471 | lp->qli_format.qlf_blkno = dqp->q_blkno; | ||
472 | lp->qli_format.qlf_len = 1; | ||
473 | /* | ||
474 | * This is just the offset of this dquot within its buffer | ||
475 | * (which is currently 1 FSB and probably won't change). | ||
476 | * Hence 32 bits for this offset should be just fine. | ||
477 | * Alternatively, we can store (bufoffset / sizeof(xfs_dqblk_t)) | ||
478 | * here, and recompute it at recovery time. | ||
479 | */ | ||
480 | lp->qli_format.qlf_boffset = (__uint32_t)dqp->q_bufoffset; | ||
481 | } | ||
482 | |||
483 | /*------------------ QUOTAOFF LOG ITEMS -------------------*/ | ||
484 | |||
485 | /* | ||
486 | * This returns the number of iovecs needed to log the given quotaoff item. | ||
487 | * We only need 1 iovec for an quotaoff item. It just logs the | ||
488 | * quotaoff_log_format structure. | ||
489 | */ | ||
490 | /*ARGSUSED*/ | ||
491 | STATIC uint | ||
492 | xfs_qm_qoff_logitem_size(xfs_qoff_logitem_t *qf) | ||
493 | { | ||
494 | return (1); | ||
495 | } | ||
496 | |||
497 | /* | ||
498 | * This is called to fill in the vector of log iovecs for the | ||
499 | * given quotaoff log item. We use only 1 iovec, and we point that | ||
500 | * at the quotaoff_log_format structure embedded in the quotaoff item. | ||
501 | * It is at this point that we assert that all of the extent | ||
502 | * slots in the quotaoff item have been filled. | ||
503 | */ | ||
504 | STATIC void | ||
505 | xfs_qm_qoff_logitem_format(xfs_qoff_logitem_t *qf, | ||
506 | xfs_log_iovec_t *log_vector) | ||
507 | { | ||
508 | ASSERT(qf->qql_format.qf_type == XFS_LI_QUOTAOFF); | ||
509 | |||
510 | log_vector->i_addr = (xfs_caddr_t)&(qf->qql_format); | ||
511 | log_vector->i_len = sizeof(xfs_qoff_logitem_t); | ||
512 | qf->qql_format.qf_size = 1; | ||
513 | } | ||
514 | |||
515 | |||
516 | /* | ||
517 | * Pinning has no meaning for an quotaoff item, so just return. | ||
518 | */ | ||
519 | /*ARGSUSED*/ | ||
520 | STATIC void | ||
521 | xfs_qm_qoff_logitem_pin(xfs_qoff_logitem_t *qf) | ||
522 | { | ||
523 | return; | ||
524 | } | ||
525 | |||
526 | |||
527 | /* | ||
528 | * Since pinning has no meaning for an quotaoff item, unpinning does | ||
529 | * not either. | ||
530 | */ | ||
531 | /*ARGSUSED*/ | ||
532 | STATIC void | ||
533 | xfs_qm_qoff_logitem_unpin(xfs_qoff_logitem_t *qf, int stale) | ||
534 | { | ||
535 | return; | ||
536 | } | ||
537 | |||
538 | /*ARGSUSED*/ | ||
539 | STATIC void | ||
540 | xfs_qm_qoff_logitem_unpin_remove(xfs_qoff_logitem_t *qf, xfs_trans_t *tp) | ||
541 | { | ||
542 | return; | ||
543 | } | ||
544 | |||
545 | /* | ||
546 | * Quotaoff items have no locking, so just return success. | ||
547 | */ | ||
548 | /*ARGSUSED*/ | ||
549 | STATIC uint | ||
550 | xfs_qm_qoff_logitem_trylock(xfs_qoff_logitem_t *qf) | ||
551 | { | ||
552 | return XFS_ITEM_LOCKED; | ||
553 | } | ||
554 | |||
555 | /* | ||
556 | * Quotaoff items have no locking or pushing, so return failure | ||
557 | * so that the caller doesn't bother with us. | ||
558 | */ | ||
559 | /*ARGSUSED*/ | ||
560 | STATIC void | ||
561 | xfs_qm_qoff_logitem_unlock(xfs_qoff_logitem_t *qf) | ||
562 | { | ||
563 | return; | ||
564 | } | ||
565 | |||
566 | /* | ||
567 | * The quotaoff-start-item is logged only once and cannot be moved in the log, | ||
568 | * so simply return the lsn at which it's been logged. | ||
569 | */ | ||
570 | /*ARGSUSED*/ | ||
571 | STATIC xfs_lsn_t | ||
572 | xfs_qm_qoff_logitem_committed(xfs_qoff_logitem_t *qf, xfs_lsn_t lsn) | ||
573 | { | ||
574 | return (lsn); | ||
575 | } | ||
576 | |||
577 | /* | ||
578 | * The transaction of which this QUOTAOFF is a part has been aborted. | ||
579 | * Just clean up after ourselves. | ||
580 | * Shouldn't this never happen in the case of qoffend logitems? XXX | ||
581 | */ | ||
582 | STATIC void | ||
583 | xfs_qm_qoff_logitem_abort(xfs_qoff_logitem_t *qf) | ||
584 | { | ||
585 | kmem_free(qf, sizeof(xfs_qoff_logitem_t)); | ||
586 | } | ||
587 | |||
588 | /* | ||
589 | * There isn't much you can do to push on an quotaoff item. It is simply | ||
590 | * stuck waiting for the log to be flushed to disk. | ||
591 | */ | ||
592 | /*ARGSUSED*/ | ||
593 | STATIC void | ||
594 | xfs_qm_qoff_logitem_push(xfs_qoff_logitem_t *qf) | ||
595 | { | ||
596 | return; | ||
597 | } | ||
598 | |||
599 | |||
600 | /*ARGSUSED*/ | ||
601 | STATIC xfs_lsn_t | ||
602 | xfs_qm_qoffend_logitem_committed( | ||
603 | xfs_qoff_logitem_t *qfe, | ||
604 | xfs_lsn_t lsn) | ||
605 | { | ||
606 | xfs_qoff_logitem_t *qfs; | ||
607 | SPLDECL(s); | ||
608 | |||
609 | qfs = qfe->qql_start_lip; | ||
610 | AIL_LOCK(qfs->qql_item.li_mountp,s); | ||
611 | /* | ||
612 | * Delete the qoff-start logitem from the AIL. | ||
613 | * xfs_trans_delete_ail() drops the AIL lock. | ||
614 | */ | ||
615 | xfs_trans_delete_ail(qfs->qql_item.li_mountp, (xfs_log_item_t *)qfs, s); | ||
616 | kmem_free(qfs, sizeof(xfs_qoff_logitem_t)); | ||
617 | kmem_free(qfe, sizeof(xfs_qoff_logitem_t)); | ||
618 | return (xfs_lsn_t)-1; | ||
619 | } | ||
620 | |||
621 | /* | ||
622 | * XXX rcc - don't know quite what to do with this. I think we can | ||
623 | * just ignore it. The only time that isn't the case is if we allow | ||
624 | * the client to somehow see that quotas have been turned off in which | ||
625 | * we can't allow that to get back until the quotaoff hits the disk. | ||
626 | * So how would that happen? Also, do we need different routines for | ||
627 | * quotaoff start and quotaoff end? I suspect the answer is yes but | ||
628 | * to be sure, I need to look at the recovery code and see how quota off | ||
629 | * recovery is handled (do we roll forward or back or do something else). | ||
630 | * If we roll forwards or backwards, then we need two separate routines, | ||
631 | * one that does nothing and one that stamps in the lsn that matters | ||
632 | * (truly makes the quotaoff irrevocable). If we do something else, | ||
633 | * then maybe we don't need two. | ||
634 | */ | ||
635 | /* ARGSUSED */ | ||
636 | STATIC void | ||
637 | xfs_qm_qoff_logitem_committing(xfs_qoff_logitem_t *qip, xfs_lsn_t commit_lsn) | ||
638 | { | ||
639 | return; | ||
640 | } | ||
641 | |||
642 | /* ARGSUSED */ | ||
643 | STATIC void | ||
644 | xfs_qm_qoffend_logitem_committing(xfs_qoff_logitem_t *qip, xfs_lsn_t commit_lsn) | ||
645 | { | ||
646 | return; | ||
647 | } | ||
648 | |||
649 | struct xfs_item_ops xfs_qm_qoffend_logitem_ops = { | ||
650 | .iop_size = (uint(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_size, | ||
651 | .iop_format = (void(*)(xfs_log_item_t*, xfs_log_iovec_t*)) | ||
652 | xfs_qm_qoff_logitem_format, | ||
653 | .iop_pin = (void(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_pin, | ||
654 | .iop_unpin = (void(*)(xfs_log_item_t* ,int)) | ||
655 | xfs_qm_qoff_logitem_unpin, | ||
656 | .iop_unpin_remove = (void(*)(xfs_log_item_t*,xfs_trans_t*)) | ||
657 | xfs_qm_qoff_logitem_unpin_remove, | ||
658 | .iop_trylock = (uint(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_trylock, | ||
659 | .iop_unlock = (void(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_unlock, | ||
660 | .iop_committed = (xfs_lsn_t(*)(xfs_log_item_t*, xfs_lsn_t)) | ||
661 | xfs_qm_qoffend_logitem_committed, | ||
662 | .iop_push = (void(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_push, | ||
663 | .iop_abort = (void(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_abort, | ||
664 | .iop_pushbuf = NULL, | ||
665 | .iop_committing = (void(*)(xfs_log_item_t*, xfs_lsn_t)) | ||
666 | xfs_qm_qoffend_logitem_committing | ||
667 | }; | ||
668 | |||
669 | /* | ||
670 | * This is the ops vector shared by all quotaoff-start log items. | ||
671 | */ | ||
672 | struct xfs_item_ops xfs_qm_qoff_logitem_ops = { | ||
673 | .iop_size = (uint(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_size, | ||
674 | .iop_format = (void(*)(xfs_log_item_t*, xfs_log_iovec_t*)) | ||
675 | xfs_qm_qoff_logitem_format, | ||
676 | .iop_pin = (void(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_pin, | ||
677 | .iop_unpin = (void(*)(xfs_log_item_t*, int)) | ||
678 | xfs_qm_qoff_logitem_unpin, | ||
679 | .iop_unpin_remove = (void(*)(xfs_log_item_t*,xfs_trans_t*)) | ||
680 | xfs_qm_qoff_logitem_unpin_remove, | ||
681 | .iop_trylock = (uint(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_trylock, | ||
682 | .iop_unlock = (void(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_unlock, | ||
683 | .iop_committed = (xfs_lsn_t(*)(xfs_log_item_t*, xfs_lsn_t)) | ||
684 | xfs_qm_qoff_logitem_committed, | ||
685 | .iop_push = (void(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_push, | ||
686 | .iop_abort = (void(*)(xfs_log_item_t*))xfs_qm_qoff_logitem_abort, | ||
687 | .iop_pushbuf = NULL, | ||
688 | .iop_committing = (void(*)(xfs_log_item_t*, xfs_lsn_t)) | ||
689 | xfs_qm_qoff_logitem_committing | ||
690 | }; | ||
691 | |||
692 | /* | ||
693 | * Allocate and initialize an quotaoff item of the correct quota type(s). | ||
694 | */ | ||
695 | xfs_qoff_logitem_t * | ||
696 | xfs_qm_qoff_logitem_init( | ||
697 | struct xfs_mount *mp, | ||
698 | xfs_qoff_logitem_t *start, | ||
699 | uint flags) | ||
700 | { | ||
701 | xfs_qoff_logitem_t *qf; | ||
702 | |||
703 | qf = (xfs_qoff_logitem_t*) kmem_zalloc(sizeof(xfs_qoff_logitem_t), KM_SLEEP); | ||
704 | |||
705 | qf->qql_item.li_type = XFS_LI_QUOTAOFF; | ||
706 | if (start) | ||
707 | qf->qql_item.li_ops = &xfs_qm_qoffend_logitem_ops; | ||
708 | else | ||
709 | qf->qql_item.li_ops = &xfs_qm_qoff_logitem_ops; | ||
710 | qf->qql_item.li_mountp = mp; | ||
711 | qf->qql_format.qf_type = XFS_LI_QUOTAOFF; | ||
712 | qf->qql_format.qf_flags = flags; | ||
713 | qf->qql_start_lip = start; | ||
714 | return (qf); | ||
715 | } | ||
diff --git a/fs/xfs/quota/xfs_dquot_item.h b/fs/xfs/quota/xfs_dquot_item.h new file mode 100644 index 000000000000..9c6500dabcaa --- /dev/null +++ b/fs/xfs/quota/xfs_dquot_item.h | |||
@@ -0,0 +1,66 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms of version 2 of the GNU General Public License as | ||
6 | * published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope that it would be useful, but | ||
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | * | ||
12 | * Further, this software is distributed without any warranty that it is | ||
13 | * free of the rightful claim of any third person regarding infringement | ||
14 | * or the like. Any license provided herein, whether implied or | ||
15 | * otherwise, applies only to this software file. Patent licenses, if | ||
16 | * any, provided herein do not apply to combinations of this program with | ||
17 | * other software, or any other product whatsoever. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License along | ||
20 | * with this program; if not, write the Free Software Foundation, Inc., 59 | ||
21 | * Temple Place - Suite 330, Boston MA 02111-1307, USA. | ||
22 | * | ||
23 | * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, | ||
24 | * Mountain View, CA 94043, or: | ||
25 | * | ||
26 | * http://www.sgi.com | ||
27 | * | ||
28 | * For further information regarding this notice, see: | ||
29 | * | ||
30 | * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ | ||
31 | */ | ||
32 | #ifndef __XFS_DQUOT_ITEM_H__ | ||
33 | #define __XFS_DQUOT_ITEM_H__ | ||
34 | |||
35 | struct xfs_dquot; | ||
36 | struct xfs_trans; | ||
37 | struct xfs_mount; | ||
38 | struct xfs_qoff_logitem; | ||
39 | |||
40 | typedef struct xfs_dq_logitem { | ||
41 | xfs_log_item_t qli_item; /* common portion */ | ||
42 | struct xfs_dquot *qli_dquot; /* dquot ptr */ | ||
43 | xfs_lsn_t qli_flush_lsn; /* lsn at last flush */ | ||
44 | unsigned short qli_pushbuf_flag; /* 1 bit used in push_ail */ | ||
45 | #ifdef DEBUG | ||
46 | uint64_t qli_push_owner; | ||
47 | #endif | ||
48 | xfs_dq_logformat_t qli_format; /* logged structure */ | ||
49 | } xfs_dq_logitem_t; | ||
50 | |||
51 | typedef struct xfs_qoff_logitem { | ||
52 | xfs_log_item_t qql_item; /* common portion */ | ||
53 | struct xfs_qoff_logitem *qql_start_lip; /* qoff-start logitem, if any */ | ||
54 | xfs_qoff_logformat_t qql_format; /* logged structure */ | ||
55 | } xfs_qoff_logitem_t; | ||
56 | |||
57 | |||
58 | extern void xfs_qm_dquot_logitem_init(struct xfs_dquot *); | ||
59 | extern xfs_qoff_logitem_t *xfs_qm_qoff_logitem_init(struct xfs_mount *, | ||
60 | struct xfs_qoff_logitem *, uint); | ||
61 | extern xfs_qoff_logitem_t *xfs_trans_get_qoff_item(struct xfs_trans *, | ||
62 | struct xfs_qoff_logitem *, uint); | ||
63 | extern void xfs_trans_log_quotaoff_item(struct xfs_trans *, | ||
64 | struct xfs_qoff_logitem *); | ||
65 | |||
66 | #endif /* __XFS_DQUOT_ITEM_H__ */ | ||
diff --git a/fs/xfs/quota/xfs_qm.c b/fs/xfs/quota/xfs_qm.c new file mode 100644 index 000000000000..89f2cd656ebf --- /dev/null +++ b/fs/xfs/quota/xfs_qm.c | |||
@@ -0,0 +1,2848 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms of version 2 of the GNU General Public License as | ||
6 | * published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope that it would be useful, but | ||
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | * | ||
12 | * Further, this software is distributed without any warranty that it is | ||
13 | * free of the rightful claim of any third person regarding infringement | ||
14 | * or the like. Any license provided herein, whether implied or | ||
15 | * otherwise, applies only to this software file. Patent licenses, if | ||
16 | * any, provided herein do not apply to combinations of this program with | ||
17 | * other software, or any other product whatsoever. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License along | ||
20 | * with this program; if not, write the Free Software Foundation, Inc., 59 | ||
21 | * Temple Place - Suite 330, Boston MA 02111-1307, USA. | ||
22 | * | ||
23 | * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, | ||
24 | * Mountain View, CA 94043, or: | ||
25 | * | ||
26 | * http://www.sgi.com | ||
27 | * | ||
28 | * For further information regarding this notice, see: | ||
29 | * | ||
30 | * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ | ||
31 | */ | ||
32 | |||
33 | #include "xfs.h" | ||
34 | #include "xfs_fs.h" | ||
35 | #include "xfs_inum.h" | ||
36 | #include "xfs_log.h" | ||
37 | #include "xfs_clnt.h" | ||
38 | #include "xfs_trans.h" | ||
39 | #include "xfs_sb.h" | ||
40 | #include "xfs_ag.h" | ||
41 | #include "xfs_dir.h" | ||
42 | #include "xfs_dir2.h" | ||
43 | #include "xfs_alloc.h" | ||
44 | #include "xfs_dmapi.h" | ||
45 | #include "xfs_quota.h" | ||
46 | #include "xfs_mount.h" | ||
47 | #include "xfs_alloc_btree.h" | ||
48 | #include "xfs_bmap_btree.h" | ||
49 | #include "xfs_ialloc_btree.h" | ||
50 | #include "xfs_btree.h" | ||
51 | #include "xfs_ialloc.h" | ||
52 | #include "xfs_attr_sf.h" | ||
53 | #include "xfs_dir_sf.h" | ||
54 | #include "xfs_dir2_sf.h" | ||
55 | #include "xfs_dinode.h" | ||
56 | #include "xfs_inode.h" | ||
57 | #include "xfs_bmap.h" | ||
58 | #include "xfs_bit.h" | ||
59 | #include "xfs_rtalloc.h" | ||
60 | #include "xfs_error.h" | ||
61 | #include "xfs_itable.h" | ||
62 | #include "xfs_rw.h" | ||
63 | #include "xfs_acl.h" | ||
64 | #include "xfs_cap.h" | ||
65 | #include "xfs_mac.h" | ||
66 | #include "xfs_attr.h" | ||
67 | #include "xfs_buf_item.h" | ||
68 | #include "xfs_trans_space.h" | ||
69 | #include "xfs_utils.h" | ||
70 | |||
71 | #include "xfs_qm.h" | ||
72 | |||
73 | /* | ||
74 | * The global quota manager. There is only one of these for the entire | ||
75 | * system, _not_ one per file system. XQM keeps track of the overall | ||
76 | * quota functionality, including maintaining the freelist and hash | ||
77 | * tables of dquots. | ||
78 | */ | ||
79 | mutex_t xfs_Gqm_lock; | ||
80 | struct xfs_qm *xfs_Gqm; | ||
81 | |||
82 | kmem_zone_t *qm_dqzone; | ||
83 | kmem_zone_t *qm_dqtrxzone; | ||
84 | kmem_shaker_t xfs_qm_shaker; | ||
85 | |||
86 | STATIC void xfs_qm_list_init(xfs_dqlist_t *, char *, int); | ||
87 | STATIC void xfs_qm_list_destroy(xfs_dqlist_t *); | ||
88 | |||
89 | STATIC int xfs_qm_init_quotainos(xfs_mount_t *); | ||
90 | STATIC int xfs_qm_shake(int, unsigned int); | ||
91 | |||
92 | #ifdef DEBUG | ||
93 | extern mutex_t qcheck_lock; | ||
94 | #endif | ||
95 | |||
96 | #ifdef QUOTADEBUG | ||
97 | #define XQM_LIST_PRINT(l, NXT, title) \ | ||
98 | { \ | ||
99 | xfs_dquot_t *dqp; int i = 0; \ | ||
100 | cmn_err(CE_DEBUG, "%s (#%d)", title, (int) (l)->qh_nelems); \ | ||
101 | for (dqp = (l)->qh_next; dqp != NULL; dqp = dqp->NXT) { \ | ||
102 | cmn_err(CE_DEBUG, " %d. \"%d (%s)\" " \ | ||
103 | "bcnt = %d, icnt = %d, refs = %d", \ | ||
104 | ++i, (int) INT_GET(dqp->q_core.d_id, ARCH_CONVERT), \ | ||
105 | DQFLAGTO_TYPESTR(dqp), \ | ||
106 | (int) INT_GET(dqp->q_core.d_bcount, ARCH_CONVERT), \ | ||
107 | (int) INT_GET(dqp->q_core.d_icount, ARCH_CONVERT), \ | ||
108 | (int) dqp->q_nrefs); } \ | ||
109 | } | ||
110 | #else | ||
111 | #define XQM_LIST_PRINT(l, NXT, title) do { } while (0) | ||
112 | #endif | ||
113 | |||
114 | /* | ||
115 | * Initialize the XQM structure. | ||
116 | * Note that there is not one quota manager per file system. | ||
117 | */ | ||
118 | STATIC struct xfs_qm * | ||
119 | xfs_Gqm_init(void) | ||
120 | { | ||
121 | xfs_qm_t *xqm; | ||
122 | int hsize, i; | ||
123 | |||
124 | xqm = kmem_zalloc(sizeof(xfs_qm_t), KM_SLEEP); | ||
125 | ASSERT(xqm); | ||
126 | |||
127 | /* | ||
128 | * Initialize the dquot hash tables. | ||
129 | */ | ||
130 | hsize = (DQUOT_HASH_HEURISTIC < XFS_QM_NCSIZE_THRESHOLD) ? | ||
131 | XFS_QM_HASHSIZE_LOW : XFS_QM_HASHSIZE_HIGH; | ||
132 | xqm->qm_dqhashmask = hsize - 1; | ||
133 | |||
134 | xqm->qm_usr_dqhtable = (xfs_dqhash_t *)kmem_zalloc(hsize * | ||
135 | sizeof(xfs_dqhash_t), | ||
136 | KM_SLEEP); | ||
137 | xqm->qm_grp_dqhtable = (xfs_dqhash_t *)kmem_zalloc(hsize * | ||
138 | sizeof(xfs_dqhash_t), | ||
139 | KM_SLEEP); | ||
140 | ASSERT(xqm->qm_usr_dqhtable != NULL); | ||
141 | ASSERT(xqm->qm_grp_dqhtable != NULL); | ||
142 | |||
143 | for (i = 0; i < hsize; i++) { | ||
144 | xfs_qm_list_init(&(xqm->qm_usr_dqhtable[i]), "uxdqh", i); | ||
145 | xfs_qm_list_init(&(xqm->qm_grp_dqhtable[i]), "gxdqh", i); | ||
146 | } | ||
147 | |||
148 | /* | ||
149 | * Freelist of all dquots of all file systems | ||
150 | */ | ||
151 | xfs_qm_freelist_init(&(xqm->qm_dqfreelist)); | ||
152 | |||
153 | /* | ||
154 | * dquot zone. we register our own low-memory callback. | ||
155 | */ | ||
156 | if (!qm_dqzone) { | ||
157 | xqm->qm_dqzone = kmem_zone_init(sizeof(xfs_dquot_t), | ||
158 | "xfs_dquots"); | ||
159 | qm_dqzone = xqm->qm_dqzone; | ||
160 | } else | ||
161 | xqm->qm_dqzone = qm_dqzone; | ||
162 | |||
163 | xfs_qm_shaker = kmem_shake_register(xfs_qm_shake); | ||
164 | |||
165 | /* | ||
166 | * The t_dqinfo portion of transactions. | ||
167 | */ | ||
168 | if (!qm_dqtrxzone) { | ||
169 | xqm->qm_dqtrxzone = kmem_zone_init(sizeof(xfs_dquot_acct_t), | ||
170 | "xfs_dqtrx"); | ||
171 | qm_dqtrxzone = xqm->qm_dqtrxzone; | ||
172 | } else | ||
173 | xqm->qm_dqtrxzone = qm_dqtrxzone; | ||
174 | |||
175 | atomic_set(&xqm->qm_totaldquots, 0); | ||
176 | xqm->qm_dqfree_ratio = XFS_QM_DQFREE_RATIO; | ||
177 | xqm->qm_nrefs = 0; | ||
178 | #ifdef DEBUG | ||
179 | mutex_init(&qcheck_lock, MUTEX_DEFAULT, "qchk"); | ||
180 | #endif | ||
181 | return xqm; | ||
182 | } | ||
183 | |||
184 | /* | ||
185 | * Destroy the global quota manager when its reference count goes to zero. | ||
186 | */ | ||
187 | void | ||
188 | xfs_qm_destroy( | ||
189 | struct xfs_qm *xqm) | ||
190 | { | ||
191 | int hsize, i; | ||
192 | |||
193 | ASSERT(xqm != NULL); | ||
194 | ASSERT(xqm->qm_nrefs == 0); | ||
195 | kmem_shake_deregister(xfs_qm_shaker); | ||
196 | hsize = xqm->qm_dqhashmask + 1; | ||
197 | for (i = 0; i < hsize; i++) { | ||
198 | xfs_qm_list_destroy(&(xqm->qm_usr_dqhtable[i])); | ||
199 | xfs_qm_list_destroy(&(xqm->qm_grp_dqhtable[i])); | ||
200 | } | ||
201 | kmem_free(xqm->qm_usr_dqhtable, hsize * sizeof(xfs_dqhash_t)); | ||
202 | kmem_free(xqm->qm_grp_dqhtable, hsize * sizeof(xfs_dqhash_t)); | ||
203 | xqm->qm_usr_dqhtable = NULL; | ||
204 | xqm->qm_grp_dqhtable = NULL; | ||
205 | xqm->qm_dqhashmask = 0; | ||
206 | xfs_qm_freelist_destroy(&(xqm->qm_dqfreelist)); | ||
207 | #ifdef DEBUG | ||
208 | mutex_destroy(&qcheck_lock); | ||
209 | #endif | ||
210 | kmem_free(xqm, sizeof(xfs_qm_t)); | ||
211 | } | ||
212 | |||
213 | /* | ||
214 | * Called at mount time to let XQM know that another file system is | ||
215 | * starting quotas. This isn't crucial information as the individual mount | ||
216 | * structures are pretty independent, but it helps the XQM keep a | ||
217 | * global view of what's going on. | ||
218 | */ | ||
219 | /* ARGSUSED */ | ||
220 | STATIC int | ||
221 | xfs_qm_hold_quotafs_ref( | ||
222 | struct xfs_mount *mp) | ||
223 | { | ||
224 | /* | ||
225 | * Need to lock the xfs_Gqm structure for things like this. For example, | ||
226 | * the structure could disappear between the entry to this routine and | ||
227 | * a HOLD operation if not locked. | ||
228 | */ | ||
229 | XFS_QM_LOCK(xfs_Gqm); | ||
230 | |||
231 | if (xfs_Gqm == NULL) | ||
232 | xfs_Gqm = xfs_Gqm_init(); | ||
233 | /* | ||
234 | * We can keep a list of all filesystems with quotas mounted for | ||
235 | * debugging and statistical purposes, but ... | ||
236 | * Just take a reference and get out. | ||
237 | */ | ||
238 | XFS_QM_HOLD(xfs_Gqm); | ||
239 | XFS_QM_UNLOCK(xfs_Gqm); | ||
240 | |||
241 | return 0; | ||
242 | } | ||
243 | |||
244 | |||
245 | /* | ||
246 | * Release the reference that a filesystem took at mount time, | ||
247 | * so that we know when we need to destroy the entire quota manager. | ||
248 | */ | ||
249 | /* ARGSUSED */ | ||
250 | STATIC void | ||
251 | xfs_qm_rele_quotafs_ref( | ||
252 | struct xfs_mount *mp) | ||
253 | { | ||
254 | xfs_dquot_t *dqp, *nextdqp; | ||
255 | |||
256 | ASSERT(xfs_Gqm); | ||
257 | ASSERT(xfs_Gqm->qm_nrefs > 0); | ||
258 | |||
259 | /* | ||
260 | * Go thru the freelist and destroy all inactive dquots. | ||
261 | */ | ||
262 | xfs_qm_freelist_lock(xfs_Gqm); | ||
263 | |||
264 | for (dqp = xfs_Gqm->qm_dqfreelist.qh_next; | ||
265 | dqp != (xfs_dquot_t *)&(xfs_Gqm->qm_dqfreelist); ) { | ||
266 | xfs_dqlock(dqp); | ||
267 | nextdqp = dqp->dq_flnext; | ||
268 | if (dqp->dq_flags & XFS_DQ_INACTIVE) { | ||
269 | ASSERT(dqp->q_mount == NULL); | ||
270 | ASSERT(! XFS_DQ_IS_DIRTY(dqp)); | ||
271 | ASSERT(dqp->HL_PREVP == NULL); | ||
272 | ASSERT(dqp->MPL_PREVP == NULL); | ||
273 | XQM_FREELIST_REMOVE(dqp); | ||
274 | xfs_dqunlock(dqp); | ||
275 | xfs_qm_dqdestroy(dqp); | ||
276 | } else { | ||
277 | xfs_dqunlock(dqp); | ||
278 | } | ||
279 | dqp = nextdqp; | ||
280 | } | ||
281 | xfs_qm_freelist_unlock(xfs_Gqm); | ||
282 | |||
283 | /* | ||
284 | * Destroy the entire XQM. If somebody mounts with quotaon, this'll | ||
285 | * be restarted. | ||
286 | */ | ||
287 | XFS_QM_LOCK(xfs_Gqm); | ||
288 | XFS_QM_RELE(xfs_Gqm); | ||
289 | if (xfs_Gqm->qm_nrefs == 0) { | ||
290 | xfs_qm_destroy(xfs_Gqm); | ||
291 | xfs_Gqm = NULL; | ||
292 | } | ||
293 | XFS_QM_UNLOCK(xfs_Gqm); | ||
294 | } | ||
295 | |||
296 | /* | ||
297 | * This is called at mount time from xfs_mountfs to initialize the quotainfo | ||
298 | * structure and start the global quotamanager (xfs_Gqm) if it hasn't done | ||
299 | * so already. Note that the superblock has not been read in yet. | ||
300 | */ | ||
301 | void | ||
302 | xfs_qm_mount_quotainit( | ||
303 | xfs_mount_t *mp, | ||
304 | uint flags) | ||
305 | { | ||
306 | /* | ||
307 | * User or group quotas has to be on. | ||
308 | */ | ||
309 | ASSERT(flags & (XFSMNT_UQUOTA | XFSMNT_GQUOTA)); | ||
310 | |||
311 | /* | ||
312 | * Initialize the flags in the mount structure. From this point | ||
313 | * onwards we look at m_qflags to figure out if quotas's ON/OFF, etc. | ||
314 | * Note that we enforce nothing if accounting is off. | ||
315 | * ie. XFSMNT_*QUOTA must be ON for XFSMNT_*QUOTAENF. | ||
316 | * It isn't necessary to take the quotaoff lock to do this; this is | ||
317 | * called from mount. | ||
318 | */ | ||
319 | if (flags & XFSMNT_UQUOTA) { | ||
320 | mp->m_qflags |= (XFS_UQUOTA_ACCT | XFS_UQUOTA_ACTIVE); | ||
321 | if (flags & XFSMNT_UQUOTAENF) | ||
322 | mp->m_qflags |= XFS_UQUOTA_ENFD; | ||
323 | } | ||
324 | if (flags & XFSMNT_GQUOTA) { | ||
325 | mp->m_qflags |= (XFS_GQUOTA_ACCT | XFS_GQUOTA_ACTIVE); | ||
326 | if (flags & XFSMNT_GQUOTAENF) | ||
327 | mp->m_qflags |= XFS_GQUOTA_ENFD; | ||
328 | } | ||
329 | } | ||
330 | |||
331 | /* | ||
332 | * Just destroy the quotainfo structure. | ||
333 | */ | ||
334 | void | ||
335 | xfs_qm_unmount_quotadestroy( | ||
336 | xfs_mount_t *mp) | ||
337 | { | ||
338 | if (mp->m_quotainfo) | ||
339 | xfs_qm_destroy_quotainfo(mp); | ||
340 | } | ||
341 | |||
342 | |||
343 | /* | ||
344 | * This is called from xfs_mountfs to start quotas and initialize all | ||
345 | * necessary data structures like quotainfo. This is also responsible for | ||
346 | * running a quotacheck as necessary. We are guaranteed that the superblock | ||
347 | * is consistently read in at this point. | ||
348 | */ | ||
349 | int | ||
350 | xfs_qm_mount_quotas( | ||
351 | xfs_mount_t *mp, | ||
352 | int mfsi_flags) | ||
353 | { | ||
354 | unsigned long s; | ||
355 | int error = 0; | ||
356 | uint sbf; | ||
357 | |||
358 | /* | ||
359 | * If a file system had quotas running earlier, but decided to | ||
360 | * mount without -o quota/uquota/gquota options, revoke the | ||
361 | * quotachecked license, and bail out. | ||
362 | */ | ||
363 | if (! XFS_IS_QUOTA_ON(mp) && | ||
364 | (mp->m_sb.sb_qflags & (XFS_UQUOTA_ACCT|XFS_GQUOTA_ACCT))) { | ||
365 | mp->m_qflags = 0; | ||
366 | goto write_changes; | ||
367 | } | ||
368 | |||
369 | /* | ||
370 | * If quotas on realtime volumes is not supported, we disable | ||
371 | * quotas immediately. | ||
372 | */ | ||
373 | if (mp->m_sb.sb_rextents) { | ||
374 | cmn_err(CE_NOTE, | ||
375 | "Cannot turn on quotas for realtime filesystem %s", | ||
376 | mp->m_fsname); | ||
377 | mp->m_qflags = 0; | ||
378 | goto write_changes; | ||
379 | } | ||
380 | |||
381 | #if defined(DEBUG) && defined(XFS_LOUD_RECOVERY) | ||
382 | cmn_err(CE_NOTE, "Attempting to turn on disk quotas."); | ||
383 | #endif | ||
384 | |||
385 | ASSERT(XFS_IS_QUOTA_RUNNING(mp)); | ||
386 | /* | ||
387 | * Allocate the quotainfo structure inside the mount struct, and | ||
388 | * create quotainode(s), and change/rev superblock if necessary. | ||
389 | */ | ||
390 | if ((error = xfs_qm_init_quotainfo(mp))) { | ||
391 | /* | ||
392 | * We must turn off quotas. | ||
393 | */ | ||
394 | ASSERT(mp->m_quotainfo == NULL); | ||
395 | mp->m_qflags = 0; | ||
396 | goto write_changes; | ||
397 | } | ||
398 | /* | ||
399 | * If any of the quotas are not consistent, do a quotacheck. | ||
400 | */ | ||
401 | if (XFS_QM_NEED_QUOTACHECK(mp) && | ||
402 | !(mfsi_flags & XFS_MFSI_NO_QUOTACHECK)) { | ||
403 | #ifdef DEBUG | ||
404 | cmn_err(CE_NOTE, "Doing a quotacheck. Please wait."); | ||
405 | #endif | ||
406 | if ((error = xfs_qm_quotacheck(mp))) { | ||
407 | /* Quotacheck has failed and quotas have | ||
408 | * been disabled. | ||
409 | */ | ||
410 | return XFS_ERROR(error); | ||
411 | } | ||
412 | #ifdef DEBUG | ||
413 | cmn_err(CE_NOTE, "Done quotacheck."); | ||
414 | #endif | ||
415 | } | ||
416 | write_changes: | ||
417 | /* | ||
418 | * We actually don't have to acquire the SB_LOCK at all. | ||
419 | * This can only be called from mount, and that's single threaded. XXX | ||
420 | */ | ||
421 | s = XFS_SB_LOCK(mp); | ||
422 | sbf = mp->m_sb.sb_qflags; | ||
423 | mp->m_sb.sb_qflags = mp->m_qflags & XFS_MOUNT_QUOTA_ALL; | ||
424 | XFS_SB_UNLOCK(mp, s); | ||
425 | |||
426 | if (sbf != (mp->m_qflags & XFS_MOUNT_QUOTA_ALL)) { | ||
427 | if (xfs_qm_write_sb_changes(mp, XFS_SB_QFLAGS)) { | ||
428 | /* | ||
429 | * We could only have been turning quotas off. | ||
430 | * We aren't in very good shape actually because | ||
431 | * the incore structures are convinced that quotas are | ||
432 | * off, but the on disk superblock doesn't know that ! | ||
433 | */ | ||
434 | ASSERT(!(XFS_IS_QUOTA_RUNNING(mp))); | ||
435 | xfs_fs_cmn_err(CE_ALERT, mp, | ||
436 | "XFS mount_quotas: Superblock update failed!"); | ||
437 | } | ||
438 | } | ||
439 | |||
440 | if (error) { | ||
441 | xfs_fs_cmn_err(CE_WARN, mp, | ||
442 | "Failed to initialize disk quotas."); | ||
443 | } | ||
444 | return XFS_ERROR(error); | ||
445 | } | ||
446 | |||
447 | /* | ||
448 | * Called from the vfsops layer. | ||
449 | */ | ||
450 | int | ||
451 | xfs_qm_unmount_quotas( | ||
452 | xfs_mount_t *mp) | ||
453 | { | ||
454 | xfs_inode_t *uqp, *gqp; | ||
455 | int error = 0; | ||
456 | |||
457 | /* | ||
458 | * Release the dquots that root inode, et al might be holding, | ||
459 | * before we flush quotas and blow away the quotainfo structure. | ||
460 | */ | ||
461 | ASSERT(mp->m_rootip); | ||
462 | xfs_qm_dqdetach(mp->m_rootip); | ||
463 | if (mp->m_rbmip) | ||
464 | xfs_qm_dqdetach(mp->m_rbmip); | ||
465 | if (mp->m_rsumip) | ||
466 | xfs_qm_dqdetach(mp->m_rsumip); | ||
467 | |||
468 | /* | ||
469 | * Flush out the quota inodes. | ||
470 | */ | ||
471 | uqp = gqp = NULL; | ||
472 | if (mp->m_quotainfo) { | ||
473 | if ((uqp = mp->m_quotainfo->qi_uquotaip) != NULL) { | ||
474 | xfs_ilock(uqp, XFS_ILOCK_EXCL); | ||
475 | xfs_iflock(uqp); | ||
476 | error = xfs_iflush(uqp, XFS_IFLUSH_SYNC); | ||
477 | xfs_iunlock(uqp, XFS_ILOCK_EXCL); | ||
478 | if (unlikely(error == EFSCORRUPTED)) { | ||
479 | XFS_ERROR_REPORT("xfs_qm_unmount_quotas(1)", | ||
480 | XFS_ERRLEVEL_LOW, mp); | ||
481 | goto out; | ||
482 | } | ||
483 | } | ||
484 | if ((gqp = mp->m_quotainfo->qi_gquotaip) != NULL) { | ||
485 | xfs_ilock(gqp, XFS_ILOCK_EXCL); | ||
486 | xfs_iflock(gqp); | ||
487 | error = xfs_iflush(gqp, XFS_IFLUSH_SYNC); | ||
488 | xfs_iunlock(gqp, XFS_ILOCK_EXCL); | ||
489 | if (unlikely(error == EFSCORRUPTED)) { | ||
490 | XFS_ERROR_REPORT("xfs_qm_unmount_quotas(2)", | ||
491 | XFS_ERRLEVEL_LOW, mp); | ||
492 | goto out; | ||
493 | } | ||
494 | } | ||
495 | } | ||
496 | if (uqp) { | ||
497 | XFS_PURGE_INODE(uqp); | ||
498 | mp->m_quotainfo->qi_uquotaip = NULL; | ||
499 | } | ||
500 | if (gqp) { | ||
501 | XFS_PURGE_INODE(gqp); | ||
502 | mp->m_quotainfo->qi_gquotaip = NULL; | ||
503 | } | ||
504 | out: | ||
505 | return XFS_ERROR(error); | ||
506 | } | ||
507 | |||
508 | /* | ||
509 | * Flush all dquots of the given file system to disk. The dquots are | ||
510 | * _not_ purged from memory here, just their data written to disk. | ||
511 | */ | ||
512 | int | ||
513 | xfs_qm_dqflush_all( | ||
514 | xfs_mount_t *mp, | ||
515 | int flags) | ||
516 | { | ||
517 | int recl; | ||
518 | xfs_dquot_t *dqp; | ||
519 | int niters; | ||
520 | int error; | ||
521 | |||
522 | if (mp->m_quotainfo == NULL) | ||
523 | return (0); | ||
524 | niters = 0; | ||
525 | again: | ||
526 | xfs_qm_mplist_lock(mp); | ||
527 | FOREACH_DQUOT_IN_MP(dqp, mp) { | ||
528 | xfs_dqlock(dqp); | ||
529 | if (! XFS_DQ_IS_DIRTY(dqp)) { | ||
530 | xfs_dqunlock(dqp); | ||
531 | continue; | ||
532 | } | ||
533 | xfs_dqtrace_entry(dqp, "FLUSHALL: DQDIRTY"); | ||
534 | /* XXX a sentinel would be better */ | ||
535 | recl = XFS_QI_MPLRECLAIMS(mp); | ||
536 | if (! xfs_qm_dqflock_nowait(dqp)) { | ||
537 | /* | ||
538 | * If we can't grab the flush lock then check | ||
539 | * to see if the dquot has been flushed delayed | ||
540 | * write. If so, grab its buffer and send it | ||
541 | * out immediately. We'll be able to acquire | ||
542 | * the flush lock when the I/O completes. | ||
543 | */ | ||
544 | xfs_qm_dqflock_pushbuf_wait(dqp); | ||
545 | } | ||
546 | /* | ||
547 | * Let go of the mplist lock. We don't want to hold it | ||
548 | * across a disk write. | ||
549 | */ | ||
550 | xfs_qm_mplist_unlock(mp); | ||
551 | error = xfs_qm_dqflush(dqp, flags); | ||
552 | xfs_dqunlock(dqp); | ||
553 | if (error) | ||
554 | return (error); | ||
555 | |||
556 | xfs_qm_mplist_lock(mp); | ||
557 | if (recl != XFS_QI_MPLRECLAIMS(mp)) { | ||
558 | xfs_qm_mplist_unlock(mp); | ||
559 | /* XXX restart limit */ | ||
560 | goto again; | ||
561 | } | ||
562 | } | ||
563 | |||
564 | xfs_qm_mplist_unlock(mp); | ||
565 | /* return ! busy */ | ||
566 | return (0); | ||
567 | } | ||
568 | /* | ||
569 | * Release the group dquot pointers the user dquots may be | ||
570 | * carrying around as a hint. mplist is locked on entry and exit. | ||
571 | */ | ||
572 | STATIC void | ||
573 | xfs_qm_detach_gdquots( | ||
574 | xfs_mount_t *mp) | ||
575 | { | ||
576 | xfs_dquot_t *dqp, *gdqp; | ||
577 | int nrecl; | ||
578 | |||
579 | again: | ||
580 | ASSERT(XFS_QM_IS_MPLIST_LOCKED(mp)); | ||
581 | dqp = XFS_QI_MPLNEXT(mp); | ||
582 | while (dqp) { | ||
583 | xfs_dqlock(dqp); | ||
584 | if ((gdqp = dqp->q_gdquot)) { | ||
585 | xfs_dqlock(gdqp); | ||
586 | dqp->q_gdquot = NULL; | ||
587 | } | ||
588 | xfs_dqunlock(dqp); | ||
589 | |||
590 | if (gdqp) { | ||
591 | /* | ||
592 | * Can't hold the mplist lock across a dqput. | ||
593 | * XXXmust convert to marker based iterations here. | ||
594 | */ | ||
595 | nrecl = XFS_QI_MPLRECLAIMS(mp); | ||
596 | xfs_qm_mplist_unlock(mp); | ||
597 | xfs_qm_dqput(gdqp); | ||
598 | |||
599 | xfs_qm_mplist_lock(mp); | ||
600 | if (nrecl != XFS_QI_MPLRECLAIMS(mp)) | ||
601 | goto again; | ||
602 | } | ||
603 | dqp = dqp->MPL_NEXT; | ||
604 | } | ||
605 | } | ||
606 | |||
607 | /* | ||
608 | * Go through all the incore dquots of this file system and take them | ||
609 | * off the mplist and hashlist, if the dquot type matches the dqtype | ||
610 | * parameter. This is used when turning off quota accounting for | ||
611 | * users and/or groups, as well as when the filesystem is unmounting. | ||
612 | */ | ||
613 | STATIC int | ||
614 | xfs_qm_dqpurge_int( | ||
615 | xfs_mount_t *mp, | ||
616 | uint flags) /* QUOTAOFF/UMOUNTING/UQUOTA/GQUOTA */ | ||
617 | { | ||
618 | xfs_dquot_t *dqp; | ||
619 | uint dqtype; | ||
620 | int nrecl; | ||
621 | xfs_dquot_t *nextdqp; | ||
622 | int nmisses; | ||
623 | |||
624 | if (mp->m_quotainfo == NULL) | ||
625 | return (0); | ||
626 | |||
627 | dqtype = (flags & XFS_QMOPT_UQUOTA) ? XFS_DQ_USER : 0; | ||
628 | dqtype |= (flags & XFS_QMOPT_GQUOTA) ? XFS_DQ_GROUP : 0; | ||
629 | |||
630 | xfs_qm_mplist_lock(mp); | ||
631 | |||
632 | /* | ||
633 | * In the first pass through all incore dquots of this filesystem, | ||
634 | * we release the group dquot pointers the user dquots may be | ||
635 | * carrying around as a hint. We need to do this irrespective of | ||
636 | * what's being turned off. | ||
637 | */ | ||
638 | xfs_qm_detach_gdquots(mp); | ||
639 | |||
640 | again: | ||
641 | nmisses = 0; | ||
642 | ASSERT(XFS_QM_IS_MPLIST_LOCKED(mp)); | ||
643 | /* | ||
644 | * Try to get rid of all of the unwanted dquots. The idea is to | ||
645 | * get them off mplist and hashlist, but leave them on freelist. | ||
646 | */ | ||
647 | dqp = XFS_QI_MPLNEXT(mp); | ||
648 | while (dqp) { | ||
649 | /* | ||
650 | * It's OK to look at the type without taking dqlock here. | ||
651 | * We're holding the mplist lock here, and that's needed for | ||
652 | * a dqreclaim. | ||
653 | */ | ||
654 | if ((dqp->dq_flags & dqtype) == 0) { | ||
655 | dqp = dqp->MPL_NEXT; | ||
656 | continue; | ||
657 | } | ||
658 | |||
659 | if (! xfs_qm_dqhashlock_nowait(dqp)) { | ||
660 | nrecl = XFS_QI_MPLRECLAIMS(mp); | ||
661 | xfs_qm_mplist_unlock(mp); | ||
662 | XFS_DQ_HASH_LOCK(dqp->q_hash); | ||
663 | xfs_qm_mplist_lock(mp); | ||
664 | |||
665 | /* | ||
666 | * XXXTheoretically, we can get into a very long | ||
667 | * ping pong game here. | ||
668 | * No one can be adding dquots to the mplist at | ||
669 | * this point, but somebody might be taking things off. | ||
670 | */ | ||
671 | if (nrecl != XFS_QI_MPLRECLAIMS(mp)) { | ||
672 | XFS_DQ_HASH_UNLOCK(dqp->q_hash); | ||
673 | goto again; | ||
674 | } | ||
675 | } | ||
676 | |||
677 | /* | ||
678 | * Take the dquot off the mplist and hashlist. It may remain on | ||
679 | * freelist in INACTIVE state. | ||
680 | */ | ||
681 | nextdqp = dqp->MPL_NEXT; | ||
682 | nmisses += xfs_qm_dqpurge(dqp, flags); | ||
683 | dqp = nextdqp; | ||
684 | } | ||
685 | xfs_qm_mplist_unlock(mp); | ||
686 | return nmisses; | ||
687 | } | ||
688 | |||
689 | int | ||
690 | xfs_qm_dqpurge_all( | ||
691 | xfs_mount_t *mp, | ||
692 | uint flags) | ||
693 | { | ||
694 | int ndquots; | ||
695 | |||
696 | /* | ||
697 | * Purge the dquot cache. | ||
698 | * None of the dquots should really be busy at this point. | ||
699 | */ | ||
700 | if (mp->m_quotainfo) { | ||
701 | while ((ndquots = xfs_qm_dqpurge_int(mp, flags))) { | ||
702 | delay(ndquots * 10); | ||
703 | } | ||
704 | } | ||
705 | return 0; | ||
706 | } | ||
707 | |||
708 | STATIC int | ||
709 | xfs_qm_dqattach_one( | ||
710 | xfs_inode_t *ip, | ||
711 | xfs_dqid_t id, | ||
712 | uint type, | ||
713 | uint doalloc, | ||
714 | uint dolock, | ||
715 | xfs_dquot_t *udqhint, /* hint */ | ||
716 | xfs_dquot_t **IO_idqpp) | ||
717 | { | ||
718 | xfs_dquot_t *dqp; | ||
719 | int error; | ||
720 | |||
721 | ASSERT(XFS_ISLOCKED_INODE_EXCL(ip)); | ||
722 | error = 0; | ||
723 | /* | ||
724 | * See if we already have it in the inode itself. IO_idqpp is | ||
725 | * &i_udquot or &i_gdquot. This made the code look weird, but | ||
726 | * made the logic a lot simpler. | ||
727 | */ | ||
728 | if ((dqp = *IO_idqpp)) { | ||
729 | if (dolock) | ||
730 | xfs_dqlock(dqp); | ||
731 | xfs_dqtrace_entry(dqp, "DQATTACH: found in ip"); | ||
732 | goto done; | ||
733 | } | ||
734 | |||
735 | /* | ||
736 | * udqhint is the i_udquot field in inode, and is non-NULL only | ||
737 | * when the type arg is XFS_DQ_GROUP. Its purpose is to save a | ||
738 | * lookup by dqid (xfs_qm_dqget) by caching a group dquot inside | ||
739 | * the user dquot. | ||
740 | */ | ||
741 | ASSERT(!udqhint || type == XFS_DQ_GROUP); | ||
742 | if (udqhint && !dolock) | ||
743 | xfs_dqlock(udqhint); | ||
744 | |||
745 | /* | ||
746 | * No need to take dqlock to look at the id. | ||
747 | * The ID can't change until it gets reclaimed, and it won't | ||
748 | * be reclaimed as long as we have a ref from inode and we hold | ||
749 | * the ilock. | ||
750 | */ | ||
751 | if (udqhint && | ||
752 | (dqp = udqhint->q_gdquot) && | ||
753 | (INT_GET(dqp->q_core.d_id, ARCH_CONVERT) == id)) { | ||
754 | ASSERT(XFS_DQ_IS_LOCKED(udqhint)); | ||
755 | xfs_dqlock(dqp); | ||
756 | XFS_DQHOLD(dqp); | ||
757 | ASSERT(*IO_idqpp == NULL); | ||
758 | *IO_idqpp = dqp; | ||
759 | if (!dolock) { | ||
760 | xfs_dqunlock(dqp); | ||
761 | xfs_dqunlock(udqhint); | ||
762 | } | ||
763 | goto done; | ||
764 | } | ||
765 | /* | ||
766 | * We can't hold a dquot lock when we call the dqget code. | ||
767 | * We'll deadlock in no time, because of (not conforming to) | ||
768 | * lock ordering - the inodelock comes before any dquot lock, | ||
769 | * and we may drop and reacquire the ilock in xfs_qm_dqget(). | ||
770 | */ | ||
771 | if (udqhint) | ||
772 | xfs_dqunlock(udqhint); | ||
773 | /* | ||
774 | * Find the dquot from somewhere. This bumps the | ||
775 | * reference count of dquot and returns it locked. | ||
776 | * This can return ENOENT if dquot didn't exist on | ||
777 | * disk and we didn't ask it to allocate; | ||
778 | * ESRCH if quotas got turned off suddenly. | ||
779 | */ | ||
780 | if ((error = xfs_qm_dqget(ip->i_mount, ip, id, type, | ||
781 | doalloc|XFS_QMOPT_DOWARN, &dqp))) { | ||
782 | if (udqhint && dolock) | ||
783 | xfs_dqlock(udqhint); | ||
784 | goto done; | ||
785 | } | ||
786 | |||
787 | xfs_dqtrace_entry(dqp, "DQATTACH: found by dqget"); | ||
788 | /* | ||
789 | * dqget may have dropped and re-acquired the ilock, but it guarantees | ||
790 | * that the dquot returned is the one that should go in the inode. | ||
791 | */ | ||
792 | *IO_idqpp = dqp; | ||
793 | ASSERT(dqp); | ||
794 | ASSERT(XFS_DQ_IS_LOCKED(dqp)); | ||
795 | if (! dolock) { | ||
796 | xfs_dqunlock(dqp); | ||
797 | goto done; | ||
798 | } | ||
799 | if (! udqhint) | ||
800 | goto done; | ||
801 | |||
802 | ASSERT(udqhint); | ||
803 | ASSERT(dolock); | ||
804 | ASSERT(XFS_DQ_IS_LOCKED(dqp)); | ||
805 | if (! xfs_qm_dqlock_nowait(udqhint)) { | ||
806 | xfs_dqunlock(dqp); | ||
807 | xfs_dqlock(udqhint); | ||
808 | xfs_dqlock(dqp); | ||
809 | } | ||
810 | done: | ||
811 | #ifdef QUOTADEBUG | ||
812 | if (udqhint) { | ||
813 | if (dolock) | ||
814 | ASSERT(XFS_DQ_IS_LOCKED(udqhint)); | ||
815 | } | ||
816 | if (! error) { | ||
817 | if (dolock) | ||
818 | ASSERT(XFS_DQ_IS_LOCKED(dqp)); | ||
819 | } | ||
820 | #endif | ||
821 | return (error); | ||
822 | } | ||
823 | |||
824 | |||
825 | /* | ||
826 | * Given a udquot and gdquot, attach a ptr to the group dquot in the | ||
827 | * udquot as a hint for future lookups. The idea sounds simple, but the | ||
828 | * execution isn't, because the udquot might have a group dquot attached | ||
829 | * already and getting rid of that gets us into lock ordering contraints. | ||
830 | * The process is complicated more by the fact that the dquots may or may not | ||
831 | * be locked on entry. | ||
832 | */ | ||
833 | STATIC void | ||
834 | xfs_qm_dqattach_grouphint( | ||
835 | xfs_dquot_t *udq, | ||
836 | xfs_dquot_t *gdq, | ||
837 | uint locked) | ||
838 | { | ||
839 | xfs_dquot_t *tmp; | ||
840 | |||
841 | #ifdef QUOTADEBUG | ||
842 | if (locked) { | ||
843 | ASSERT(XFS_DQ_IS_LOCKED(udq)); | ||
844 | ASSERT(XFS_DQ_IS_LOCKED(gdq)); | ||
845 | } | ||
846 | #endif | ||
847 | if (! locked) | ||
848 | xfs_dqlock(udq); | ||
849 | |||
850 | if ((tmp = udq->q_gdquot)) { | ||
851 | if (tmp == gdq) { | ||
852 | if (! locked) | ||
853 | xfs_dqunlock(udq); | ||
854 | return; | ||
855 | } | ||
856 | |||
857 | udq->q_gdquot = NULL; | ||
858 | /* | ||
859 | * We can't keep any dqlocks when calling dqrele, | ||
860 | * because the freelist lock comes before dqlocks. | ||
861 | */ | ||
862 | xfs_dqunlock(udq); | ||
863 | if (locked) | ||
864 | xfs_dqunlock(gdq); | ||
865 | /* | ||
866 | * we took a hard reference once upon a time in dqget, | ||
867 | * so give it back when the udquot no longer points at it | ||
868 | * dqput() does the unlocking of the dquot. | ||
869 | */ | ||
870 | xfs_qm_dqrele(tmp); | ||
871 | |||
872 | xfs_dqlock(udq); | ||
873 | xfs_dqlock(gdq); | ||
874 | |||
875 | } else { | ||
876 | ASSERT(XFS_DQ_IS_LOCKED(udq)); | ||
877 | if (! locked) { | ||
878 | xfs_dqlock(gdq); | ||
879 | } | ||
880 | } | ||
881 | |||
882 | ASSERT(XFS_DQ_IS_LOCKED(udq)); | ||
883 | ASSERT(XFS_DQ_IS_LOCKED(gdq)); | ||
884 | /* | ||
885 | * Somebody could have attached a gdquot here, | ||
886 | * when we dropped the uqlock. If so, just do nothing. | ||
887 | */ | ||
888 | if (udq->q_gdquot == NULL) { | ||
889 | XFS_DQHOLD(gdq); | ||
890 | udq->q_gdquot = gdq; | ||
891 | } | ||
892 | if (! locked) { | ||
893 | xfs_dqunlock(gdq); | ||
894 | xfs_dqunlock(udq); | ||
895 | } | ||
896 | } | ||
897 | |||
898 | |||
899 | /* | ||
900 | * Given a locked inode, attach dquot(s) to it, taking UQUOTAON / GQUOTAON | ||
901 | * in to account. | ||
902 | * If XFS_QMOPT_DQALLOC, the dquot(s) will be allocated if needed. | ||
903 | * If XFS_QMOPT_DQLOCK, the dquot(s) will be returned locked. This option pretty | ||
904 | * much made this code a complete mess, but it has been pretty useful. | ||
905 | * If XFS_QMOPT_ILOCKED, then inode sent is already locked EXCL. | ||
906 | * Inode may get unlocked and relocked in here, and the caller must deal with | ||
907 | * the consequences. | ||
908 | */ | ||
909 | int | ||
910 | xfs_qm_dqattach( | ||
911 | xfs_inode_t *ip, | ||
912 | uint flags) | ||
913 | { | ||
914 | xfs_mount_t *mp = ip->i_mount; | ||
915 | uint nquotas = 0; | ||
916 | int error = 0; | ||
917 | |||
918 | if ((! XFS_IS_QUOTA_ON(mp)) || | ||
919 | (! XFS_NOT_DQATTACHED(mp, ip)) || | ||
920 | (ip->i_ino == mp->m_sb.sb_uquotino) || | ||
921 | (ip->i_ino == mp->m_sb.sb_gquotino)) | ||
922 | return (0); | ||
923 | |||
924 | ASSERT((flags & XFS_QMOPT_ILOCKED) == 0 || | ||
925 | XFS_ISLOCKED_INODE_EXCL(ip)); | ||
926 | |||
927 | if (! (flags & XFS_QMOPT_ILOCKED)) | ||
928 | xfs_ilock(ip, XFS_ILOCK_EXCL); | ||
929 | |||
930 | if (XFS_IS_UQUOTA_ON(mp)) { | ||
931 | error = xfs_qm_dqattach_one(ip, ip->i_d.di_uid, XFS_DQ_USER, | ||
932 | flags & XFS_QMOPT_DQALLOC, | ||
933 | flags & XFS_QMOPT_DQLOCK, | ||
934 | NULL, &ip->i_udquot); | ||
935 | if (error) | ||
936 | goto done; | ||
937 | nquotas++; | ||
938 | } | ||
939 | ASSERT(XFS_ISLOCKED_INODE_EXCL(ip)); | ||
940 | if (XFS_IS_GQUOTA_ON(mp)) { | ||
941 | error = xfs_qm_dqattach_one(ip, ip->i_d.di_gid, XFS_DQ_GROUP, | ||
942 | flags & XFS_QMOPT_DQALLOC, | ||
943 | flags & XFS_QMOPT_DQLOCK, | ||
944 | ip->i_udquot, &ip->i_gdquot); | ||
945 | /* | ||
946 | * Don't worry about the udquot that we may have | ||
947 | * attached above. It'll get detached, if not already. | ||
948 | */ | ||
949 | if (error) | ||
950 | goto done; | ||
951 | nquotas++; | ||
952 | } | ||
953 | |||
954 | /* | ||
955 | * Attach this group quota to the user quota as a hint. | ||
956 | * This WON'T, in general, result in a thrash. | ||
957 | */ | ||
958 | if (nquotas == 2) { | ||
959 | ASSERT(XFS_ISLOCKED_INODE_EXCL(ip)); | ||
960 | ASSERT(ip->i_udquot); | ||
961 | ASSERT(ip->i_gdquot); | ||
962 | |||
963 | /* | ||
964 | * We may or may not have the i_udquot locked at this point, | ||
965 | * but this check is OK since we don't depend on the i_gdquot to | ||
966 | * be accurate 100% all the time. It is just a hint, and this | ||
967 | * will succeed in general. | ||
968 | */ | ||
969 | if (ip->i_udquot->q_gdquot == ip->i_gdquot) | ||
970 | goto done; | ||
971 | /* | ||
972 | * Attach i_gdquot to the gdquot hint inside the i_udquot. | ||
973 | */ | ||
974 | xfs_qm_dqattach_grouphint(ip->i_udquot, ip->i_gdquot, | ||
975 | flags & XFS_QMOPT_DQLOCK); | ||
976 | } | ||
977 | |||
978 | done: | ||
979 | |||
980 | #ifdef QUOTADEBUG | ||
981 | if (! error) { | ||
982 | if (ip->i_udquot) { | ||
983 | if (flags & XFS_QMOPT_DQLOCK) | ||
984 | ASSERT(XFS_DQ_IS_LOCKED(ip->i_udquot)); | ||
985 | } | ||
986 | if (ip->i_gdquot) { | ||
987 | if (flags & XFS_QMOPT_DQLOCK) | ||
988 | ASSERT(XFS_DQ_IS_LOCKED(ip->i_gdquot)); | ||
989 | } | ||
990 | if (XFS_IS_UQUOTA_ON(mp)) | ||
991 | ASSERT(ip->i_udquot); | ||
992 | if (XFS_IS_GQUOTA_ON(mp)) | ||
993 | ASSERT(ip->i_gdquot); | ||
994 | } | ||
995 | #endif | ||
996 | |||
997 | if (! (flags & XFS_QMOPT_ILOCKED)) | ||
998 | xfs_iunlock(ip, XFS_ILOCK_EXCL); | ||
999 | |||
1000 | #ifdef QUOTADEBUG | ||
1001 | else | ||
1002 | ASSERT(XFS_ISLOCKED_INODE_EXCL(ip)); | ||
1003 | #endif | ||
1004 | return (error); | ||
1005 | } | ||
1006 | |||
1007 | /* | ||
1008 | * Release dquots (and their references) if any. | ||
1009 | * The inode should be locked EXCL except when this's called by | ||
1010 | * xfs_ireclaim. | ||
1011 | */ | ||
1012 | void | ||
1013 | xfs_qm_dqdetach( | ||
1014 | xfs_inode_t *ip) | ||
1015 | { | ||
1016 | if (!(ip->i_udquot || ip->i_gdquot)) | ||
1017 | return; | ||
1018 | |||
1019 | ASSERT(ip->i_ino != ip->i_mount->m_sb.sb_uquotino); | ||
1020 | ASSERT(ip->i_ino != ip->i_mount->m_sb.sb_gquotino); | ||
1021 | if (ip->i_udquot) | ||
1022 | xfs_dqtrace_entry_ino(ip->i_udquot, "DQDETTACH", ip); | ||
1023 | if (ip->i_udquot) { | ||
1024 | xfs_qm_dqrele(ip->i_udquot); | ||
1025 | ip->i_udquot = NULL; | ||
1026 | } | ||
1027 | if (ip->i_gdquot) { | ||
1028 | xfs_qm_dqrele(ip->i_gdquot); | ||
1029 | ip->i_gdquot = NULL; | ||
1030 | } | ||
1031 | } | ||
1032 | |||
1033 | /* | ||
1034 | * This is called by VFS_SYNC and flags arg determines the caller, | ||
1035 | * and its motives, as done in xfs_sync. | ||
1036 | * | ||
1037 | * vfs_sync: SYNC_FSDATA|SYNC_ATTR|SYNC_BDFLUSH 0x31 | ||
1038 | * syscall sync: SYNC_FSDATA|SYNC_ATTR|SYNC_DELWRI 0x25 | ||
1039 | * umountroot : SYNC_WAIT | SYNC_CLOSE | SYNC_ATTR | SYNC_FSDATA | ||
1040 | */ | ||
1041 | |||
1042 | int | ||
1043 | xfs_qm_sync( | ||
1044 | xfs_mount_t *mp, | ||
1045 | short flags) | ||
1046 | { | ||
1047 | int recl, restarts; | ||
1048 | xfs_dquot_t *dqp; | ||
1049 | uint flush_flags; | ||
1050 | boolean_t nowait; | ||
1051 | int error; | ||
1052 | |||
1053 | restarts = 0; | ||
1054 | /* | ||
1055 | * We won't block unless we are asked to. | ||
1056 | */ | ||
1057 | nowait = (boolean_t)(flags & SYNC_BDFLUSH || (flags & SYNC_WAIT) == 0); | ||
1058 | |||
1059 | again: | ||
1060 | xfs_qm_mplist_lock(mp); | ||
1061 | /* | ||
1062 | * dqpurge_all() also takes the mplist lock and iterate thru all dquots | ||
1063 | * in quotaoff. However, if the QUOTA_ACTIVE bits are not cleared | ||
1064 | * when we have the mplist lock, we know that dquots will be consistent | ||
1065 | * as long as we have it locked. | ||
1066 | */ | ||
1067 | if (! XFS_IS_QUOTA_ON(mp)) { | ||
1068 | xfs_qm_mplist_unlock(mp); | ||
1069 | return (0); | ||
1070 | } | ||
1071 | FOREACH_DQUOT_IN_MP(dqp, mp) { | ||
1072 | /* | ||
1073 | * If this is vfs_sync calling, then skip the dquots that | ||
1074 | * don't 'seem' to be dirty. ie. don't acquire dqlock. | ||
1075 | * This is very similar to what xfs_sync does with inodes. | ||
1076 | */ | ||
1077 | if (flags & SYNC_BDFLUSH) { | ||
1078 | if (! XFS_DQ_IS_DIRTY(dqp)) | ||
1079 | continue; | ||
1080 | } | ||
1081 | |||
1082 | if (nowait) { | ||
1083 | /* | ||
1084 | * Try to acquire the dquot lock. We are NOT out of | ||
1085 | * lock order, but we just don't want to wait for this | ||
1086 | * lock, unless somebody wanted us to. | ||
1087 | */ | ||
1088 | if (! xfs_qm_dqlock_nowait(dqp)) | ||
1089 | continue; | ||
1090 | } else { | ||
1091 | xfs_dqlock(dqp); | ||
1092 | } | ||
1093 | |||
1094 | /* | ||
1095 | * Now, find out for sure if this dquot is dirty or not. | ||
1096 | */ | ||
1097 | if (! XFS_DQ_IS_DIRTY(dqp)) { | ||
1098 | xfs_dqunlock(dqp); | ||
1099 | continue; | ||
1100 | } | ||
1101 | |||
1102 | /* XXX a sentinel would be better */ | ||
1103 | recl = XFS_QI_MPLRECLAIMS(mp); | ||
1104 | if (! xfs_qm_dqflock_nowait(dqp)) { | ||
1105 | if (nowait) { | ||
1106 | xfs_dqunlock(dqp); | ||
1107 | continue; | ||
1108 | } | ||
1109 | /* | ||
1110 | * If we can't grab the flush lock then if the caller | ||
1111 | * really wanted us to give this our best shot, | ||
1112 | * see if we can give a push to the buffer before we wait | ||
1113 | * on the flush lock. At this point, we know that | ||
1114 | * eventhough the dquot is being flushed, | ||
1115 | * it has (new) dirty data. | ||
1116 | */ | ||
1117 | xfs_qm_dqflock_pushbuf_wait(dqp); | ||
1118 | } | ||
1119 | /* | ||
1120 | * Let go of the mplist lock. We don't want to hold it | ||
1121 | * across a disk write | ||
1122 | */ | ||
1123 | flush_flags = (nowait) ? XFS_QMOPT_DELWRI : XFS_QMOPT_SYNC; | ||
1124 | xfs_qm_mplist_unlock(mp); | ||
1125 | xfs_dqtrace_entry(dqp, "XQM_SYNC: DQFLUSH"); | ||
1126 | error = xfs_qm_dqflush(dqp, flush_flags); | ||
1127 | xfs_dqunlock(dqp); | ||
1128 | if (error && XFS_FORCED_SHUTDOWN(mp)) | ||
1129 | return(0); /* Need to prevent umount failure */ | ||
1130 | else if (error) | ||
1131 | return (error); | ||
1132 | |||
1133 | xfs_qm_mplist_lock(mp); | ||
1134 | if (recl != XFS_QI_MPLRECLAIMS(mp)) { | ||
1135 | if (++restarts >= XFS_QM_SYNC_MAX_RESTARTS) | ||
1136 | break; | ||
1137 | |||
1138 | xfs_qm_mplist_unlock(mp); | ||
1139 | goto again; | ||
1140 | } | ||
1141 | } | ||
1142 | |||
1143 | xfs_qm_mplist_unlock(mp); | ||
1144 | return (0); | ||
1145 | } | ||
1146 | |||
1147 | |||
1148 | /* | ||
1149 | * This initializes all the quota information that's kept in the | ||
1150 | * mount structure | ||
1151 | */ | ||
1152 | int | ||
1153 | xfs_qm_init_quotainfo( | ||
1154 | xfs_mount_t *mp) | ||
1155 | { | ||
1156 | xfs_quotainfo_t *qinf; | ||
1157 | int error; | ||
1158 | xfs_dquot_t *dqp; | ||
1159 | |||
1160 | ASSERT(XFS_IS_QUOTA_RUNNING(mp)); | ||
1161 | |||
1162 | /* | ||
1163 | * Tell XQM that we exist as soon as possible. | ||
1164 | */ | ||
1165 | if ((error = xfs_qm_hold_quotafs_ref(mp))) { | ||
1166 | return (error); | ||
1167 | } | ||
1168 | |||
1169 | qinf = mp->m_quotainfo = kmem_zalloc(sizeof(xfs_quotainfo_t), KM_SLEEP); | ||
1170 | |||
1171 | /* | ||
1172 | * See if quotainodes are setup, and if not, allocate them, | ||
1173 | * and change the superblock accordingly. | ||
1174 | */ | ||
1175 | if ((error = xfs_qm_init_quotainos(mp))) { | ||
1176 | kmem_free(qinf, sizeof(xfs_quotainfo_t)); | ||
1177 | mp->m_quotainfo = NULL; | ||
1178 | return (error); | ||
1179 | } | ||
1180 | |||
1181 | spinlock_init(&qinf->qi_pinlock, "xfs_qinf_pin"); | ||
1182 | xfs_qm_list_init(&qinf->qi_dqlist, "mpdqlist", 0); | ||
1183 | qinf->qi_dqreclaims = 0; | ||
1184 | |||
1185 | /* mutex used to serialize quotaoffs */ | ||
1186 | mutex_init(&qinf->qi_quotaofflock, MUTEX_DEFAULT, "qoff"); | ||
1187 | |||
1188 | /* Precalc some constants */ | ||
1189 | qinf->qi_dqchunklen = XFS_FSB_TO_BB(mp, XFS_DQUOT_CLUSTER_SIZE_FSB); | ||
1190 | ASSERT(qinf->qi_dqchunklen); | ||
1191 | qinf->qi_dqperchunk = BBTOB(qinf->qi_dqchunklen); | ||
1192 | do_div(qinf->qi_dqperchunk, sizeof(xfs_dqblk_t)); | ||
1193 | |||
1194 | mp->m_qflags |= (mp->m_sb.sb_qflags & XFS_ALL_QUOTA_CHKD); | ||
1195 | |||
1196 | /* | ||
1197 | * We try to get the limits from the superuser's limits fields. | ||
1198 | * This is quite hacky, but it is standard quota practice. | ||
1199 | * We look at the USR dquot with id == 0 first, but if user quotas | ||
1200 | * are not enabled we goto the GRP dquot with id == 0. | ||
1201 | * We don't really care to keep separate default limits for user | ||
1202 | * and group quotas, at least not at this point. | ||
1203 | */ | ||
1204 | error = xfs_qm_dqget(mp, NULL, (xfs_dqid_t)0, | ||
1205 | (XFS_IS_UQUOTA_RUNNING(mp)) ? | ||
1206 | XFS_DQ_USER : XFS_DQ_GROUP, | ||
1207 | XFS_QMOPT_DQSUSER|XFS_QMOPT_DOWARN, | ||
1208 | &dqp); | ||
1209 | if (! error) { | ||
1210 | xfs_disk_dquot_t *ddqp = &dqp->q_core; | ||
1211 | |||
1212 | /* | ||
1213 | * The warnings and timers set the grace period given to | ||
1214 | * a user or group before he or she can not perform any | ||
1215 | * more writing. If it is zero, a default is used. | ||
1216 | */ | ||
1217 | qinf->qi_btimelimit = | ||
1218 | INT_GET(ddqp->d_btimer, ARCH_CONVERT) ? | ||
1219 | INT_GET(ddqp->d_btimer, ARCH_CONVERT) : | ||
1220 | XFS_QM_BTIMELIMIT; | ||
1221 | qinf->qi_itimelimit = | ||
1222 | INT_GET(ddqp->d_itimer, ARCH_CONVERT) ? | ||
1223 | INT_GET(ddqp->d_itimer, ARCH_CONVERT) : | ||
1224 | XFS_QM_ITIMELIMIT; | ||
1225 | qinf->qi_rtbtimelimit = | ||
1226 | INT_GET(ddqp->d_rtbtimer, ARCH_CONVERT) ? | ||
1227 | INT_GET(ddqp->d_rtbtimer, ARCH_CONVERT) : | ||
1228 | XFS_QM_RTBTIMELIMIT; | ||
1229 | qinf->qi_bwarnlimit = | ||
1230 | INT_GET(ddqp->d_bwarns, ARCH_CONVERT) ? | ||
1231 | INT_GET(ddqp->d_bwarns, ARCH_CONVERT) : | ||
1232 | XFS_QM_BWARNLIMIT; | ||
1233 | qinf->qi_iwarnlimit = | ||
1234 | INT_GET(ddqp->d_iwarns, ARCH_CONVERT) ? | ||
1235 | INT_GET(ddqp->d_iwarns, ARCH_CONVERT) : | ||
1236 | XFS_QM_IWARNLIMIT; | ||
1237 | qinf->qi_bhardlimit = | ||
1238 | INT_GET(ddqp->d_blk_hardlimit, ARCH_CONVERT); | ||
1239 | qinf->qi_bsoftlimit = | ||
1240 | INT_GET(ddqp->d_blk_softlimit, ARCH_CONVERT); | ||
1241 | qinf->qi_ihardlimit = | ||
1242 | INT_GET(ddqp->d_ino_hardlimit, ARCH_CONVERT); | ||
1243 | qinf->qi_isoftlimit = | ||
1244 | INT_GET(ddqp->d_ino_softlimit, ARCH_CONVERT); | ||
1245 | qinf->qi_rtbhardlimit = | ||
1246 | INT_GET(ddqp->d_rtb_hardlimit, ARCH_CONVERT); | ||
1247 | qinf->qi_rtbsoftlimit = | ||
1248 | INT_GET(ddqp->d_rtb_softlimit, ARCH_CONVERT); | ||
1249 | |||
1250 | /* | ||
1251 | * We sent the XFS_QMOPT_DQSUSER flag to dqget because | ||
1252 | * we don't want this dquot cached. We haven't done a | ||
1253 | * quotacheck yet, and quotacheck doesn't like incore dquots. | ||
1254 | */ | ||
1255 | xfs_qm_dqdestroy(dqp); | ||
1256 | } else { | ||
1257 | qinf->qi_btimelimit = XFS_QM_BTIMELIMIT; | ||
1258 | qinf->qi_itimelimit = XFS_QM_ITIMELIMIT; | ||
1259 | qinf->qi_rtbtimelimit = XFS_QM_RTBTIMELIMIT; | ||
1260 | qinf->qi_bwarnlimit = XFS_QM_BWARNLIMIT; | ||
1261 | qinf->qi_iwarnlimit = XFS_QM_IWARNLIMIT; | ||
1262 | } | ||
1263 | |||
1264 | return (0); | ||
1265 | } | ||
1266 | |||
1267 | |||
1268 | /* | ||
1269 | * Gets called when unmounting a filesystem or when all quotas get | ||
1270 | * turned off. | ||
1271 | * This purges the quota inodes, destroys locks and frees itself. | ||
1272 | */ | ||
1273 | void | ||
1274 | xfs_qm_destroy_quotainfo( | ||
1275 | xfs_mount_t *mp) | ||
1276 | { | ||
1277 | xfs_quotainfo_t *qi; | ||
1278 | |||
1279 | qi = mp->m_quotainfo; | ||
1280 | ASSERT(qi != NULL); | ||
1281 | ASSERT(xfs_Gqm != NULL); | ||
1282 | |||
1283 | /* | ||
1284 | * Release the reference that XQM kept, so that we know | ||
1285 | * when the XQM structure should be freed. We cannot assume | ||
1286 | * that xfs_Gqm is non-null after this point. | ||
1287 | */ | ||
1288 | xfs_qm_rele_quotafs_ref(mp); | ||
1289 | |||
1290 | spinlock_destroy(&qi->qi_pinlock); | ||
1291 | xfs_qm_list_destroy(&qi->qi_dqlist); | ||
1292 | |||
1293 | if (qi->qi_uquotaip) { | ||
1294 | XFS_PURGE_INODE(qi->qi_uquotaip); | ||
1295 | qi->qi_uquotaip = NULL; /* paranoia */ | ||
1296 | } | ||
1297 | if (qi->qi_gquotaip) { | ||
1298 | XFS_PURGE_INODE(qi->qi_gquotaip); | ||
1299 | qi->qi_gquotaip = NULL; | ||
1300 | } | ||
1301 | mutex_destroy(&qi->qi_quotaofflock); | ||
1302 | kmem_free(qi, sizeof(xfs_quotainfo_t)); | ||
1303 | mp->m_quotainfo = NULL; | ||
1304 | } | ||
1305 | |||
1306 | |||
1307 | |||
1308 | /* ------------------- PRIVATE STATIC FUNCTIONS ----------------------- */ | ||
1309 | |||
1310 | /* ARGSUSED */ | ||
1311 | STATIC void | ||
1312 | xfs_qm_list_init( | ||
1313 | xfs_dqlist_t *list, | ||
1314 | char *str, | ||
1315 | int n) | ||
1316 | { | ||
1317 | mutex_init(&list->qh_lock, MUTEX_DEFAULT, str); | ||
1318 | list->qh_next = NULL; | ||
1319 | list->qh_version = 0; | ||
1320 | list->qh_nelems = 0; | ||
1321 | } | ||
1322 | |||
1323 | STATIC void | ||
1324 | xfs_qm_list_destroy( | ||
1325 | xfs_dqlist_t *list) | ||
1326 | { | ||
1327 | mutex_destroy(&(list->qh_lock)); | ||
1328 | } | ||
1329 | |||
1330 | |||
1331 | /* | ||
1332 | * Stripped down version of dqattach. This doesn't attach, or even look at the | ||
1333 | * dquots attached to the inode. The rationale is that there won't be any | ||
1334 | * attached at the time this is called from quotacheck. | ||
1335 | */ | ||
1336 | STATIC int | ||
1337 | xfs_qm_dqget_noattach( | ||
1338 | xfs_inode_t *ip, | ||
1339 | xfs_dquot_t **O_udqpp, | ||
1340 | xfs_dquot_t **O_gdqpp) | ||
1341 | { | ||
1342 | int error; | ||
1343 | xfs_mount_t *mp; | ||
1344 | xfs_dquot_t *udqp, *gdqp; | ||
1345 | |||
1346 | ASSERT(XFS_ISLOCKED_INODE_EXCL(ip)); | ||
1347 | mp = ip->i_mount; | ||
1348 | udqp = NULL; | ||
1349 | gdqp = NULL; | ||
1350 | |||
1351 | if (XFS_IS_UQUOTA_ON(mp)) { | ||
1352 | ASSERT(ip->i_udquot == NULL); | ||
1353 | /* | ||
1354 | * We want the dquot allocated if it doesn't exist. | ||
1355 | */ | ||
1356 | if ((error = xfs_qm_dqget(mp, ip, ip->i_d.di_uid, XFS_DQ_USER, | ||
1357 | XFS_QMOPT_DQALLOC | XFS_QMOPT_DOWARN, | ||
1358 | &udqp))) { | ||
1359 | /* | ||
1360 | * Shouldn't be able to turn off quotas here. | ||
1361 | */ | ||
1362 | ASSERT(error != ESRCH); | ||
1363 | ASSERT(error != ENOENT); | ||
1364 | return (error); | ||
1365 | } | ||
1366 | ASSERT(udqp); | ||
1367 | } | ||
1368 | |||
1369 | if (XFS_IS_GQUOTA_ON(mp)) { | ||
1370 | ASSERT(ip->i_gdquot == NULL); | ||
1371 | if (udqp) | ||
1372 | xfs_dqunlock(udqp); | ||
1373 | if ((error = xfs_qm_dqget(mp, ip, ip->i_d.di_gid, XFS_DQ_GROUP, | ||
1374 | XFS_QMOPT_DQALLOC|XFS_QMOPT_DOWARN, | ||
1375 | &gdqp))) { | ||
1376 | if (udqp) | ||
1377 | xfs_qm_dqrele(udqp); | ||
1378 | ASSERT(error != ESRCH); | ||
1379 | ASSERT(error != ENOENT); | ||
1380 | return (error); | ||
1381 | } | ||
1382 | ASSERT(gdqp); | ||
1383 | |||
1384 | /* Reacquire the locks in the right order */ | ||
1385 | if (udqp) { | ||
1386 | if (! xfs_qm_dqlock_nowait(udqp)) { | ||
1387 | xfs_dqunlock(gdqp); | ||
1388 | xfs_dqlock(udqp); | ||
1389 | xfs_dqlock(gdqp); | ||
1390 | } | ||
1391 | } | ||
1392 | } | ||
1393 | |||
1394 | *O_udqpp = udqp; | ||
1395 | *O_gdqpp = gdqp; | ||
1396 | |||
1397 | #ifdef QUOTADEBUG | ||
1398 | if (udqp) ASSERT(XFS_DQ_IS_LOCKED(udqp)); | ||
1399 | if (gdqp) ASSERT(XFS_DQ_IS_LOCKED(gdqp)); | ||
1400 | #endif | ||
1401 | return (0); | ||
1402 | } | ||
1403 | |||
1404 | /* | ||
1405 | * Create an inode and return with a reference already taken, but unlocked | ||
1406 | * This is how we create quota inodes | ||
1407 | */ | ||
1408 | STATIC int | ||
1409 | xfs_qm_qino_alloc( | ||
1410 | xfs_mount_t *mp, | ||
1411 | xfs_inode_t **ip, | ||
1412 | __int64_t sbfields, | ||
1413 | uint flags) | ||
1414 | { | ||
1415 | xfs_trans_t *tp; | ||
1416 | int error; | ||
1417 | unsigned long s; | ||
1418 | cred_t zerocr; | ||
1419 | int committed; | ||
1420 | |||
1421 | tp = xfs_trans_alloc(mp,XFS_TRANS_QM_QINOCREATE); | ||
1422 | if ((error = xfs_trans_reserve(tp, | ||
1423 | XFS_QM_QINOCREATE_SPACE_RES(mp), | ||
1424 | XFS_CREATE_LOG_RES(mp), 0, | ||
1425 | XFS_TRANS_PERM_LOG_RES, | ||
1426 | XFS_CREATE_LOG_COUNT))) { | ||
1427 | xfs_trans_cancel(tp, 0); | ||
1428 | return (error); | ||
1429 | } | ||
1430 | memset(&zerocr, 0, sizeof(zerocr)); | ||
1431 | |||
1432 | if ((error = xfs_dir_ialloc(&tp, mp->m_rootip, S_IFREG, 1, 0, | ||
1433 | &zerocr, 0, 1, ip, &committed))) { | ||
1434 | xfs_trans_cancel(tp, XFS_TRANS_RELEASE_LOG_RES | | ||
1435 | XFS_TRANS_ABORT); | ||
1436 | return (error); | ||
1437 | } | ||
1438 | |||
1439 | /* | ||
1440 | * Keep an extra reference to this quota inode. This inode is | ||
1441 | * locked exclusively and joined to the transaction already. | ||
1442 | */ | ||
1443 | ASSERT(XFS_ISLOCKED_INODE_EXCL(*ip)); | ||
1444 | VN_HOLD(XFS_ITOV((*ip))); | ||
1445 | |||
1446 | /* | ||
1447 | * Make the changes in the superblock, and log those too. | ||
1448 | * sbfields arg may contain fields other than *QUOTINO; | ||
1449 | * VERSIONNUM for example. | ||
1450 | */ | ||
1451 | s = XFS_SB_LOCK(mp); | ||
1452 | if (flags & XFS_QMOPT_SBVERSION) { | ||
1453 | #if defined(DEBUG) && defined(XFS_LOUD_RECOVERY) | ||
1454 | unsigned oldv = mp->m_sb.sb_versionnum; | ||
1455 | #endif | ||
1456 | ASSERT(!XFS_SB_VERSION_HASQUOTA(&mp->m_sb)); | ||
1457 | ASSERT((sbfields & (XFS_SB_VERSIONNUM | XFS_SB_UQUOTINO | | ||
1458 | XFS_SB_GQUOTINO | XFS_SB_QFLAGS)) == | ||
1459 | (XFS_SB_VERSIONNUM | XFS_SB_UQUOTINO | | ||
1460 | XFS_SB_GQUOTINO | XFS_SB_QFLAGS)); | ||
1461 | |||
1462 | XFS_SB_VERSION_ADDQUOTA(&mp->m_sb); | ||
1463 | mp->m_sb.sb_uquotino = NULLFSINO; | ||
1464 | mp->m_sb.sb_gquotino = NULLFSINO; | ||
1465 | |||
1466 | /* qflags will get updated _after_ quotacheck */ | ||
1467 | mp->m_sb.sb_qflags = 0; | ||
1468 | #if defined(DEBUG) && defined(XFS_LOUD_RECOVERY) | ||
1469 | cmn_err(CE_NOTE, | ||
1470 | "Old superblock version %x, converting to %x.", | ||
1471 | oldv, mp->m_sb.sb_versionnum); | ||
1472 | #endif | ||
1473 | } | ||
1474 | if (flags & XFS_QMOPT_UQUOTA) | ||
1475 | mp->m_sb.sb_uquotino = (*ip)->i_ino; | ||
1476 | else | ||
1477 | mp->m_sb.sb_gquotino = (*ip)->i_ino; | ||
1478 | XFS_SB_UNLOCK(mp, s); | ||
1479 | xfs_mod_sb(tp, sbfields); | ||
1480 | |||
1481 | if ((error = xfs_trans_commit(tp, XFS_TRANS_RELEASE_LOG_RES, | ||
1482 | NULL))) { | ||
1483 | xfs_fs_cmn_err(CE_ALERT, mp, "XFS qino_alloc failed!"); | ||
1484 | return (error); | ||
1485 | } | ||
1486 | return (0); | ||
1487 | } | ||
1488 | |||
1489 | |||
1490 | STATIC int | ||
1491 | xfs_qm_reset_dqcounts( | ||
1492 | xfs_mount_t *mp, | ||
1493 | xfs_buf_t *bp, | ||
1494 | xfs_dqid_t id, | ||
1495 | uint type) | ||
1496 | { | ||
1497 | xfs_disk_dquot_t *ddq; | ||
1498 | int j; | ||
1499 | |||
1500 | xfs_buftrace("RESET DQUOTS", bp); | ||
1501 | /* | ||
1502 | * Reset all counters and timers. They'll be | ||
1503 | * started afresh by xfs_qm_quotacheck. | ||
1504 | */ | ||
1505 | #ifdef DEBUG | ||
1506 | j = XFS_FSB_TO_B(mp, XFS_DQUOT_CLUSTER_SIZE_FSB); | ||
1507 | do_div(j, sizeof(xfs_dqblk_t)); | ||
1508 | ASSERT(XFS_QM_DQPERBLK(mp) == j); | ||
1509 | #endif | ||
1510 | ddq = (xfs_disk_dquot_t *)XFS_BUF_PTR(bp); | ||
1511 | for (j = 0; j < XFS_QM_DQPERBLK(mp); j++) { | ||
1512 | /* | ||
1513 | * Do a sanity check, and if needed, repair the dqblk. Don't | ||
1514 | * output any warnings because it's perfectly possible to | ||
1515 | * find unitialized dquot blks. See comment in xfs_qm_dqcheck. | ||
1516 | */ | ||
1517 | (void) xfs_qm_dqcheck(ddq, id+j, type, XFS_QMOPT_DQREPAIR, | ||
1518 | "xfs_quotacheck"); | ||
1519 | INT_SET(ddq->d_bcount, ARCH_CONVERT, 0ULL); | ||
1520 | INT_SET(ddq->d_icount, ARCH_CONVERT, 0ULL); | ||
1521 | INT_SET(ddq->d_rtbcount, ARCH_CONVERT, 0ULL); | ||
1522 | INT_SET(ddq->d_btimer, ARCH_CONVERT, (time_t)0); | ||
1523 | INT_SET(ddq->d_itimer, ARCH_CONVERT, (time_t)0); | ||
1524 | INT_SET(ddq->d_bwarns, ARCH_CONVERT, 0UL); | ||
1525 | INT_SET(ddq->d_iwarns, ARCH_CONVERT, 0UL); | ||
1526 | ddq = (xfs_disk_dquot_t *) ((xfs_dqblk_t *)ddq + 1); | ||
1527 | } | ||
1528 | |||
1529 | return (0); | ||
1530 | } | ||
1531 | |||
1532 | STATIC int | ||
1533 | xfs_qm_dqiter_bufs( | ||
1534 | xfs_mount_t *mp, | ||
1535 | xfs_dqid_t firstid, | ||
1536 | xfs_fsblock_t bno, | ||
1537 | xfs_filblks_t blkcnt, | ||
1538 | uint flags) | ||
1539 | { | ||
1540 | xfs_buf_t *bp; | ||
1541 | int error; | ||
1542 | int notcommitted; | ||
1543 | int incr; | ||
1544 | |||
1545 | ASSERT(blkcnt > 0); | ||
1546 | notcommitted = 0; | ||
1547 | incr = (blkcnt > XFS_QM_MAX_DQCLUSTER_LOGSZ) ? | ||
1548 | XFS_QM_MAX_DQCLUSTER_LOGSZ : blkcnt; | ||
1549 | error = 0; | ||
1550 | |||
1551 | /* | ||
1552 | * Blkcnt arg can be a very big number, and might even be | ||
1553 | * larger than the log itself. So, we have to break it up into | ||
1554 | * manageable-sized transactions. | ||
1555 | * Note that we don't start a permanent transaction here; we might | ||
1556 | * not be able to get a log reservation for the whole thing up front, | ||
1557 | * and we don't really care to either, because we just discard | ||
1558 | * everything if we were to crash in the middle of this loop. | ||
1559 | */ | ||
1560 | while (blkcnt--) { | ||
1561 | error = xfs_trans_read_buf(mp, NULL, mp->m_ddev_targp, | ||
1562 | XFS_FSB_TO_DADDR(mp, bno), | ||
1563 | (int)XFS_QI_DQCHUNKLEN(mp), 0, &bp); | ||
1564 | if (error) | ||
1565 | break; | ||
1566 | |||
1567 | (void) xfs_qm_reset_dqcounts(mp, bp, firstid, | ||
1568 | flags & XFS_QMOPT_UQUOTA ? | ||
1569 | XFS_DQ_USER : XFS_DQ_GROUP); | ||
1570 | xfs_bdwrite(mp, bp); | ||
1571 | /* | ||
1572 | * goto the next block. | ||
1573 | */ | ||
1574 | bno++; | ||
1575 | firstid += XFS_QM_DQPERBLK(mp); | ||
1576 | } | ||
1577 | return (error); | ||
1578 | } | ||
1579 | |||
1580 | /* | ||
1581 | * Iterate over all allocated USR/GRP dquots in the system, calling a | ||
1582 | * caller supplied function for every chunk of dquots that we find. | ||
1583 | */ | ||
1584 | STATIC int | ||
1585 | xfs_qm_dqiterate( | ||
1586 | xfs_mount_t *mp, | ||
1587 | xfs_inode_t *qip, | ||
1588 | uint flags) | ||
1589 | { | ||
1590 | xfs_bmbt_irec_t *map; | ||
1591 | int i, nmaps; /* number of map entries */ | ||
1592 | int error; /* return value */ | ||
1593 | xfs_fileoff_t lblkno; | ||
1594 | xfs_filblks_t maxlblkcnt; | ||
1595 | xfs_dqid_t firstid; | ||
1596 | xfs_fsblock_t rablkno; | ||
1597 | xfs_filblks_t rablkcnt; | ||
1598 | |||
1599 | error = 0; | ||
1600 | /* | ||
1601 | * This looks racey, but we can't keep an inode lock across a | ||
1602 | * trans_reserve. But, this gets called during quotacheck, and that | ||
1603 | * happens only at mount time which is single threaded. | ||
1604 | */ | ||
1605 | if (qip->i_d.di_nblocks == 0) | ||
1606 | return (0); | ||
1607 | |||
1608 | map = kmem_alloc(XFS_DQITER_MAP_SIZE * sizeof(*map), KM_SLEEP); | ||
1609 | |||
1610 | lblkno = 0; | ||
1611 | maxlblkcnt = XFS_B_TO_FSB(mp, (xfs_ufsize_t)XFS_MAXIOFFSET(mp)); | ||
1612 | do { | ||
1613 | nmaps = XFS_DQITER_MAP_SIZE; | ||
1614 | /* | ||
1615 | * We aren't changing the inode itself. Just changing | ||
1616 | * some of its data. No new blocks are added here, and | ||
1617 | * the inode is never added to the transaction. | ||
1618 | */ | ||
1619 | xfs_ilock(qip, XFS_ILOCK_SHARED); | ||
1620 | error = xfs_bmapi(NULL, qip, lblkno, | ||
1621 | maxlblkcnt - lblkno, | ||
1622 | XFS_BMAPI_METADATA, | ||
1623 | NULL, | ||
1624 | 0, map, &nmaps, NULL); | ||
1625 | xfs_iunlock(qip, XFS_ILOCK_SHARED); | ||
1626 | if (error) | ||
1627 | break; | ||
1628 | |||
1629 | ASSERT(nmaps <= XFS_DQITER_MAP_SIZE); | ||
1630 | for (i = 0; i < nmaps; i++) { | ||
1631 | ASSERT(map[i].br_startblock != DELAYSTARTBLOCK); | ||
1632 | ASSERT(map[i].br_blockcount); | ||
1633 | |||
1634 | |||
1635 | lblkno += map[i].br_blockcount; | ||
1636 | |||
1637 | if (map[i].br_startblock == HOLESTARTBLOCK) | ||
1638 | continue; | ||
1639 | |||
1640 | firstid = (xfs_dqid_t) map[i].br_startoff * | ||
1641 | XFS_QM_DQPERBLK(mp); | ||
1642 | /* | ||
1643 | * Do a read-ahead on the next extent. | ||
1644 | */ | ||
1645 | if ((i+1 < nmaps) && | ||
1646 | (map[i+1].br_startblock != HOLESTARTBLOCK)) { | ||
1647 | rablkcnt = map[i+1].br_blockcount; | ||
1648 | rablkno = map[i+1].br_startblock; | ||
1649 | while (rablkcnt--) { | ||
1650 | xfs_baread(mp->m_ddev_targp, | ||
1651 | XFS_FSB_TO_DADDR(mp, rablkno), | ||
1652 | (int)XFS_QI_DQCHUNKLEN(mp)); | ||
1653 | rablkno++; | ||
1654 | } | ||
1655 | } | ||
1656 | /* | ||
1657 | * Iterate thru all the blks in the extent and | ||
1658 | * reset the counters of all the dquots inside them. | ||
1659 | */ | ||
1660 | if ((error = xfs_qm_dqiter_bufs(mp, | ||
1661 | firstid, | ||
1662 | map[i].br_startblock, | ||
1663 | map[i].br_blockcount, | ||
1664 | flags))) { | ||
1665 | break; | ||
1666 | } | ||
1667 | } | ||
1668 | |||
1669 | if (error) | ||
1670 | break; | ||
1671 | } while (nmaps > 0); | ||
1672 | |||
1673 | kmem_free(map, XFS_DQITER_MAP_SIZE * sizeof(*map)); | ||
1674 | |||
1675 | return (error); | ||
1676 | } | ||
1677 | |||
1678 | /* | ||
1679 | * Called by dqusage_adjust in doing a quotacheck. | ||
1680 | * Given the inode, and a dquot (either USR or GRP, doesn't matter), | ||
1681 | * this updates its incore copy as well as the buffer copy. This is | ||
1682 | * so that once the quotacheck is done, we can just log all the buffers, | ||
1683 | * as opposed to logging numerous updates to individual dquots. | ||
1684 | */ | ||
1685 | STATIC void | ||
1686 | xfs_qm_quotacheck_dqadjust( | ||
1687 | xfs_dquot_t *dqp, | ||
1688 | xfs_qcnt_t nblks, | ||
1689 | xfs_qcnt_t rtblks) | ||
1690 | { | ||
1691 | ASSERT(XFS_DQ_IS_LOCKED(dqp)); | ||
1692 | xfs_dqtrace_entry(dqp, "QCHECK DQADJUST"); | ||
1693 | /* | ||
1694 | * Adjust the inode count and the block count to reflect this inode's | ||
1695 | * resource usage. | ||
1696 | */ | ||
1697 | INT_MOD(dqp->q_core.d_icount, ARCH_CONVERT, +1); | ||
1698 | dqp->q_res_icount++; | ||
1699 | if (nblks) { | ||
1700 | INT_MOD(dqp->q_core.d_bcount, ARCH_CONVERT, nblks); | ||
1701 | dqp->q_res_bcount += nblks; | ||
1702 | } | ||
1703 | if (rtblks) { | ||
1704 | INT_MOD(dqp->q_core.d_rtbcount, ARCH_CONVERT, rtblks); | ||
1705 | dqp->q_res_rtbcount += rtblks; | ||
1706 | } | ||
1707 | |||
1708 | /* | ||
1709 | * Set default limits, adjust timers (since we changed usages) | ||
1710 | */ | ||
1711 | if (! XFS_IS_SUSER_DQUOT(dqp)) { | ||
1712 | xfs_qm_adjust_dqlimits(dqp->q_mount, &dqp->q_core); | ||
1713 | xfs_qm_adjust_dqtimers(dqp->q_mount, &dqp->q_core); | ||
1714 | } | ||
1715 | |||
1716 | dqp->dq_flags |= XFS_DQ_DIRTY; | ||
1717 | } | ||
1718 | |||
1719 | STATIC int | ||
1720 | xfs_qm_get_rtblks( | ||
1721 | xfs_inode_t *ip, | ||
1722 | xfs_qcnt_t *O_rtblks) | ||
1723 | { | ||
1724 | xfs_filblks_t rtblks; /* total rt blks */ | ||
1725 | xfs_ifork_t *ifp; /* inode fork pointer */ | ||
1726 | xfs_extnum_t nextents; /* number of extent entries */ | ||
1727 | xfs_bmbt_rec_t *base; /* base of extent array */ | ||
1728 | xfs_bmbt_rec_t *ep; /* pointer to an extent entry */ | ||
1729 | int error; | ||
1730 | |||
1731 | ASSERT(XFS_IS_REALTIME_INODE(ip)); | ||
1732 | ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK); | ||
1733 | if (!(ifp->if_flags & XFS_IFEXTENTS)) { | ||
1734 | if ((error = xfs_iread_extents(NULL, ip, XFS_DATA_FORK))) | ||
1735 | return (error); | ||
1736 | } | ||
1737 | rtblks = 0; | ||
1738 | nextents = ifp->if_bytes / sizeof(xfs_bmbt_rec_t); | ||
1739 | base = &ifp->if_u1.if_extents[0]; | ||
1740 | for (ep = base; ep < &base[nextents]; ep++) | ||
1741 | rtblks += xfs_bmbt_get_blockcount(ep); | ||
1742 | *O_rtblks = (xfs_qcnt_t)rtblks; | ||
1743 | return (0); | ||
1744 | } | ||
1745 | |||
1746 | /* | ||
1747 | * callback routine supplied to bulkstat(). Given an inumber, find its | ||
1748 | * dquots and update them to account for resources taken by that inode. | ||
1749 | */ | ||
1750 | /* ARGSUSED */ | ||
1751 | STATIC int | ||
1752 | xfs_qm_dqusage_adjust( | ||
1753 | xfs_mount_t *mp, /* mount point for filesystem */ | ||
1754 | xfs_ino_t ino, /* inode number to get data for */ | ||
1755 | void __user *buffer, /* not used */ | ||
1756 | int ubsize, /* not used */ | ||
1757 | void *private_data, /* not used */ | ||
1758 | xfs_daddr_t bno, /* starting block of inode cluster */ | ||
1759 | int *ubused, /* not used */ | ||
1760 | void *dip, /* on-disk inode pointer (not used) */ | ||
1761 | int *res) /* result code value */ | ||
1762 | { | ||
1763 | xfs_inode_t *ip; | ||
1764 | xfs_dquot_t *udqp, *gdqp; | ||
1765 | xfs_qcnt_t nblks, rtblks; | ||
1766 | int error; | ||
1767 | |||
1768 | ASSERT(XFS_IS_QUOTA_RUNNING(mp)); | ||
1769 | |||
1770 | /* | ||
1771 | * rootino must have its resources accounted for, not so with the quota | ||
1772 | * inodes. | ||
1773 | */ | ||
1774 | if (ino == mp->m_sb.sb_uquotino || ino == mp->m_sb.sb_gquotino) { | ||
1775 | *res = BULKSTAT_RV_NOTHING; | ||
1776 | return XFS_ERROR(EINVAL); | ||
1777 | } | ||
1778 | |||
1779 | /* | ||
1780 | * We don't _need_ to take the ilock EXCL. However, the xfs_qm_dqget | ||
1781 | * interface expects the inode to be exclusively locked because that's | ||
1782 | * the case in all other instances. It's OK that we do this because | ||
1783 | * quotacheck is done only at mount time. | ||
1784 | */ | ||
1785 | if ((error = xfs_iget(mp, NULL, ino, 0, XFS_ILOCK_EXCL, &ip, bno))) { | ||
1786 | *res = BULKSTAT_RV_NOTHING; | ||
1787 | return (error); | ||
1788 | } | ||
1789 | |||
1790 | if (ip->i_d.di_mode == 0) { | ||
1791 | xfs_iput_new(ip, XFS_ILOCK_EXCL); | ||
1792 | *res = BULKSTAT_RV_NOTHING; | ||
1793 | return XFS_ERROR(ENOENT); | ||
1794 | } | ||
1795 | |||
1796 | /* | ||
1797 | * Obtain the locked dquots. In case of an error (eg. allocation | ||
1798 | * fails for ENOSPC), we return the negative of the error number | ||
1799 | * to bulkstat, so that it can get propagated to quotacheck() and | ||
1800 | * making us disable quotas for the file system. | ||
1801 | */ | ||
1802 | if ((error = xfs_qm_dqget_noattach(ip, &udqp, &gdqp))) { | ||
1803 | xfs_iput(ip, XFS_ILOCK_EXCL); | ||
1804 | *res = BULKSTAT_RV_GIVEUP; | ||
1805 | return (error); | ||
1806 | } | ||
1807 | |||
1808 | rtblks = 0; | ||
1809 | if (! XFS_IS_REALTIME_INODE(ip)) { | ||
1810 | nblks = (xfs_qcnt_t)ip->i_d.di_nblocks; | ||
1811 | } else { | ||
1812 | /* | ||
1813 | * Walk thru the extent list and count the realtime blocks. | ||
1814 | */ | ||
1815 | if ((error = xfs_qm_get_rtblks(ip, &rtblks))) { | ||
1816 | xfs_iput(ip, XFS_ILOCK_EXCL); | ||
1817 | if (udqp) | ||
1818 | xfs_qm_dqput(udqp); | ||
1819 | if (gdqp) | ||
1820 | xfs_qm_dqput(gdqp); | ||
1821 | *res = BULKSTAT_RV_GIVEUP; | ||
1822 | return (error); | ||
1823 | } | ||
1824 | nblks = (xfs_qcnt_t)ip->i_d.di_nblocks - rtblks; | ||
1825 | } | ||
1826 | ASSERT(ip->i_delayed_blks == 0); | ||
1827 | |||
1828 | /* | ||
1829 | * We can't release the inode while holding its dquot locks. | ||
1830 | * The inode can go into inactive and might try to acquire the dquotlocks. | ||
1831 | * So, just unlock here and do a vn_rele at the end. | ||
1832 | */ | ||
1833 | xfs_iunlock(ip, XFS_ILOCK_EXCL); | ||
1834 | |||
1835 | /* | ||
1836 | * Add the (disk blocks and inode) resources occupied by this | ||
1837 | * inode to its dquots. We do this adjustment in the incore dquot, | ||
1838 | * and also copy the changes to its buffer. | ||
1839 | * We don't care about putting these changes in a transaction | ||
1840 | * envelope because if we crash in the middle of a 'quotacheck' | ||
1841 | * we have to start from the beginning anyway. | ||
1842 | * Once we're done, we'll log all the dquot bufs. | ||
1843 | * | ||
1844 | * The *QUOTA_ON checks below may look pretty racey, but quotachecks | ||
1845 | * and quotaoffs don't race. (Quotachecks happen at mount time only). | ||
1846 | */ | ||
1847 | if (XFS_IS_UQUOTA_ON(mp)) { | ||
1848 | ASSERT(udqp); | ||
1849 | xfs_qm_quotacheck_dqadjust(udqp, nblks, rtblks); | ||
1850 | xfs_qm_dqput(udqp); | ||
1851 | } | ||
1852 | if (XFS_IS_GQUOTA_ON(mp)) { | ||
1853 | ASSERT(gdqp); | ||
1854 | xfs_qm_quotacheck_dqadjust(gdqp, nblks, rtblks); | ||
1855 | xfs_qm_dqput(gdqp); | ||
1856 | } | ||
1857 | /* | ||
1858 | * Now release the inode. This will send it to 'inactive', and | ||
1859 | * possibly even free blocks. | ||
1860 | */ | ||
1861 | VN_RELE(XFS_ITOV(ip)); | ||
1862 | |||
1863 | /* | ||
1864 | * Goto next inode. | ||
1865 | */ | ||
1866 | *res = BULKSTAT_RV_DIDONE; | ||
1867 | return (0); | ||
1868 | } | ||
1869 | |||
1870 | /* | ||
1871 | * Walk thru all the filesystem inodes and construct a consistent view | ||
1872 | * of the disk quota world. If the quotacheck fails, disable quotas. | ||
1873 | */ | ||
1874 | int | ||
1875 | xfs_qm_quotacheck( | ||
1876 | xfs_mount_t *mp) | ||
1877 | { | ||
1878 | int done, count, error; | ||
1879 | xfs_ino_t lastino; | ||
1880 | size_t structsz; | ||
1881 | xfs_inode_t *uip, *gip; | ||
1882 | uint flags; | ||
1883 | |||
1884 | count = INT_MAX; | ||
1885 | structsz = 1; | ||
1886 | lastino = 0; | ||
1887 | flags = 0; | ||
1888 | |||
1889 | ASSERT(XFS_QI_UQIP(mp) || XFS_QI_GQIP(mp)); | ||
1890 | ASSERT(XFS_IS_QUOTA_RUNNING(mp)); | ||
1891 | |||
1892 | /* | ||
1893 | * There should be no cached dquots. The (simplistic) quotacheck | ||
1894 | * algorithm doesn't like that. | ||
1895 | */ | ||
1896 | ASSERT(XFS_QI_MPLNDQUOTS(mp) == 0); | ||
1897 | |||
1898 | cmn_err(CE_NOTE, "XFS quotacheck %s: Please wait.", mp->m_fsname); | ||
1899 | |||
1900 | /* | ||
1901 | * First we go thru all the dquots on disk, USR and GRP, and reset | ||
1902 | * their counters to zero. We need a clean slate. | ||
1903 | * We don't log our changes till later. | ||
1904 | */ | ||
1905 | if ((uip = XFS_QI_UQIP(mp))) { | ||
1906 | if ((error = xfs_qm_dqiterate(mp, uip, XFS_QMOPT_UQUOTA))) | ||
1907 | goto error_return; | ||
1908 | flags |= XFS_UQUOTA_CHKD; | ||
1909 | } | ||
1910 | |||
1911 | if ((gip = XFS_QI_GQIP(mp))) { | ||
1912 | if ((error = xfs_qm_dqiterate(mp, gip, XFS_QMOPT_GQUOTA))) | ||
1913 | goto error_return; | ||
1914 | flags |= XFS_GQUOTA_CHKD; | ||
1915 | } | ||
1916 | |||
1917 | do { | ||
1918 | /* | ||
1919 | * Iterate thru all the inodes in the file system, | ||
1920 | * adjusting the corresponding dquot counters in core. | ||
1921 | */ | ||
1922 | if ((error = xfs_bulkstat(mp, &lastino, &count, | ||
1923 | xfs_qm_dqusage_adjust, NULL, | ||
1924 | structsz, NULL, | ||
1925 | BULKSTAT_FG_IGET|BULKSTAT_FG_VFSLOCKED, | ||
1926 | &done))) | ||
1927 | break; | ||
1928 | |||
1929 | } while (! done); | ||
1930 | |||
1931 | /* | ||
1932 | * We can get this error if we couldn't do a dquot allocation inside | ||
1933 | * xfs_qm_dqusage_adjust (via bulkstat). We don't care about the | ||
1934 | * dirty dquots that might be cached, we just want to get rid of them | ||
1935 | * and turn quotaoff. The dquots won't be attached to any of the inodes | ||
1936 | * at this point (because we intentionally didn't in dqget_noattach). | ||
1937 | */ | ||
1938 | if (error) { | ||
1939 | xfs_qm_dqpurge_all(mp, | ||
1940 | XFS_QMOPT_UQUOTA|XFS_QMOPT_GQUOTA| | ||
1941 | XFS_QMOPT_QUOTAOFF); | ||
1942 | goto error_return; | ||
1943 | } | ||
1944 | /* | ||
1945 | * We've made all the changes that we need to make incore. | ||
1946 | * Now flush_them down to disk buffers. | ||
1947 | */ | ||
1948 | xfs_qm_dqflush_all(mp, XFS_QMOPT_DELWRI); | ||
1949 | |||
1950 | /* | ||
1951 | * We didn't log anything, because if we crashed, we'll have to | ||
1952 | * start the quotacheck from scratch anyway. However, we must make | ||
1953 | * sure that our dquot changes are secure before we put the | ||
1954 | * quotacheck'd stamp on the superblock. So, here we do a synchronous | ||
1955 | * flush. | ||
1956 | */ | ||
1957 | XFS_bflush(mp->m_ddev_targp); | ||
1958 | |||
1959 | /* | ||
1960 | * If one type of quotas is off, then it will lose its | ||
1961 | * quotachecked status, since we won't be doing accounting for | ||
1962 | * that type anymore. | ||
1963 | */ | ||
1964 | mp->m_qflags &= ~(XFS_GQUOTA_CHKD | XFS_UQUOTA_CHKD); | ||
1965 | mp->m_qflags |= flags; | ||
1966 | |||
1967 | XQM_LIST_PRINT(&(XFS_QI_MPL_LIST(mp)), MPL_NEXT, "++++ Mp list +++"); | ||
1968 | |||
1969 | error_return: | ||
1970 | if (error) { | ||
1971 | cmn_err(CE_WARN, "XFS quotacheck %s: Unsuccessful (Error %d): " | ||
1972 | "Disabling quotas.", | ||
1973 | mp->m_fsname, error); | ||
1974 | /* | ||
1975 | * We must turn off quotas. | ||
1976 | */ | ||
1977 | ASSERT(mp->m_quotainfo != NULL); | ||
1978 | ASSERT(xfs_Gqm != NULL); | ||
1979 | xfs_qm_destroy_quotainfo(mp); | ||
1980 | xfs_mount_reset_sbqflags(mp); | ||
1981 | } else { | ||
1982 | cmn_err(CE_NOTE, "XFS quotacheck %s: Done.", mp->m_fsname); | ||
1983 | } | ||
1984 | return (error); | ||
1985 | } | ||
1986 | |||
1987 | /* | ||
1988 | * This is called after the superblock has been read in and we're ready to | ||
1989 | * iget the quota inodes. | ||
1990 | */ | ||
1991 | STATIC int | ||
1992 | xfs_qm_init_quotainos( | ||
1993 | xfs_mount_t *mp) | ||
1994 | { | ||
1995 | xfs_inode_t *uip, *gip; | ||
1996 | int error; | ||
1997 | __int64_t sbflags; | ||
1998 | uint flags; | ||
1999 | |||
2000 | ASSERT(mp->m_quotainfo); | ||
2001 | uip = gip = NULL; | ||
2002 | sbflags = 0; | ||
2003 | flags = 0; | ||
2004 | |||
2005 | /* | ||
2006 | * Get the uquota and gquota inodes | ||
2007 | */ | ||
2008 | if (XFS_SB_VERSION_HASQUOTA(&mp->m_sb)) { | ||
2009 | if (XFS_IS_UQUOTA_ON(mp) && | ||
2010 | mp->m_sb.sb_uquotino != NULLFSINO) { | ||
2011 | ASSERT(mp->m_sb.sb_uquotino > 0); | ||
2012 | if ((error = xfs_iget(mp, NULL, mp->m_sb.sb_uquotino, | ||
2013 | 0, 0, &uip, 0))) | ||
2014 | return XFS_ERROR(error); | ||
2015 | } | ||
2016 | if (XFS_IS_GQUOTA_ON(mp) && | ||
2017 | mp->m_sb.sb_gquotino != NULLFSINO) { | ||
2018 | ASSERT(mp->m_sb.sb_gquotino > 0); | ||
2019 | if ((error = xfs_iget(mp, NULL, mp->m_sb.sb_gquotino, | ||
2020 | 0, 0, &gip, 0))) { | ||
2021 | if (uip) | ||
2022 | VN_RELE(XFS_ITOV(uip)); | ||
2023 | return XFS_ERROR(error); | ||
2024 | } | ||
2025 | } | ||
2026 | } else { | ||
2027 | flags |= XFS_QMOPT_SBVERSION; | ||
2028 | sbflags |= (XFS_SB_VERSIONNUM | XFS_SB_UQUOTINO | | ||
2029 | XFS_SB_GQUOTINO | XFS_SB_QFLAGS); | ||
2030 | } | ||
2031 | |||
2032 | /* | ||
2033 | * Create the two inodes, if they don't exist already. The changes | ||
2034 | * made above will get added to a transaction and logged in one of | ||
2035 | * the qino_alloc calls below. If the device is readonly, | ||
2036 | * temporarily switch to read-write to do this. | ||
2037 | */ | ||
2038 | if (XFS_IS_UQUOTA_ON(mp) && uip == NULL) { | ||
2039 | if ((error = xfs_qm_qino_alloc(mp, &uip, | ||
2040 | sbflags | XFS_SB_UQUOTINO, | ||
2041 | flags | XFS_QMOPT_UQUOTA))) | ||
2042 | return XFS_ERROR(error); | ||
2043 | |||
2044 | flags &= ~XFS_QMOPT_SBVERSION; | ||
2045 | } | ||
2046 | if (XFS_IS_GQUOTA_ON(mp) && gip == NULL) { | ||
2047 | if ((error = xfs_qm_qino_alloc(mp, &gip, | ||
2048 | sbflags | XFS_SB_GQUOTINO, | ||
2049 | flags | XFS_QMOPT_GQUOTA))) { | ||
2050 | if (uip) | ||
2051 | VN_RELE(XFS_ITOV(uip)); | ||
2052 | |||
2053 | return XFS_ERROR(error); | ||
2054 | } | ||
2055 | } | ||
2056 | |||
2057 | XFS_QI_UQIP(mp) = uip; | ||
2058 | XFS_QI_GQIP(mp) = gip; | ||
2059 | |||
2060 | return (0); | ||
2061 | } | ||
2062 | |||
2063 | |||
2064 | /* | ||
2065 | * Traverse the freelist of dquots and attempt to reclaim a maximum of | ||
2066 | * 'howmany' dquots. This operation races with dqlookup(), and attempts to | ||
2067 | * favor the lookup function ... | ||
2068 | * XXXsup merge this with qm_reclaim_one(). | ||
2069 | */ | ||
2070 | STATIC int | ||
2071 | xfs_qm_shake_freelist( | ||
2072 | int howmany) | ||
2073 | { | ||
2074 | int nreclaimed; | ||
2075 | xfs_dqhash_t *hash; | ||
2076 | xfs_dquot_t *dqp, *nextdqp; | ||
2077 | int restarts; | ||
2078 | int nflushes; | ||
2079 | |||
2080 | if (howmany <= 0) | ||
2081 | return (0); | ||
2082 | |||
2083 | nreclaimed = 0; | ||
2084 | restarts = 0; | ||
2085 | nflushes = 0; | ||
2086 | |||
2087 | #ifdef QUOTADEBUG | ||
2088 | cmn_err(CE_DEBUG, "Shake free 0x%x", howmany); | ||
2089 | #endif | ||
2090 | /* lock order is : hashchainlock, freelistlock, mplistlock */ | ||
2091 | tryagain: | ||
2092 | xfs_qm_freelist_lock(xfs_Gqm); | ||
2093 | |||
2094 | for (dqp = xfs_Gqm->qm_dqfreelist.qh_next; | ||
2095 | ((dqp != (xfs_dquot_t *) &xfs_Gqm->qm_dqfreelist) && | ||
2096 | nreclaimed < howmany); ) { | ||
2097 | xfs_dqlock(dqp); | ||
2098 | |||
2099 | /* | ||
2100 | * We are racing with dqlookup here. Naturally we don't | ||
2101 | * want to reclaim a dquot that lookup wants. | ||
2102 | */ | ||
2103 | if (dqp->dq_flags & XFS_DQ_WANT) { | ||
2104 | xfs_dqunlock(dqp); | ||
2105 | xfs_qm_freelist_unlock(xfs_Gqm); | ||
2106 | if (++restarts >= XFS_QM_RECLAIM_MAX_RESTARTS) | ||
2107 | return (nreclaimed); | ||
2108 | XQM_STATS_INC(xqmstats.xs_qm_dqwants); | ||
2109 | goto tryagain; | ||
2110 | } | ||
2111 | |||
2112 | /* | ||
2113 | * If the dquot is inactive, we are assured that it is | ||
2114 | * not on the mplist or the hashlist, and that makes our | ||
2115 | * life easier. | ||
2116 | */ | ||
2117 | if (dqp->dq_flags & XFS_DQ_INACTIVE) { | ||
2118 | ASSERT(dqp->q_mount == NULL); | ||
2119 | ASSERT(! XFS_DQ_IS_DIRTY(dqp)); | ||
2120 | ASSERT(dqp->HL_PREVP == NULL); | ||
2121 | ASSERT(dqp->MPL_PREVP == NULL); | ||
2122 | XQM_STATS_INC(xqmstats.xs_qm_dqinact_reclaims); | ||
2123 | nextdqp = dqp->dq_flnext; | ||
2124 | goto off_freelist; | ||
2125 | } | ||
2126 | |||
2127 | ASSERT(dqp->MPL_PREVP); | ||
2128 | /* | ||
2129 | * Try to grab the flush lock. If this dquot is in the process of | ||
2130 | * getting flushed to disk, we don't want to reclaim it. | ||
2131 | */ | ||
2132 | if (! xfs_qm_dqflock_nowait(dqp)) { | ||
2133 | xfs_dqunlock(dqp); | ||
2134 | dqp = dqp->dq_flnext; | ||
2135 | continue; | ||
2136 | } | ||
2137 | |||
2138 | /* | ||
2139 | * We have the flush lock so we know that this is not in the | ||
2140 | * process of being flushed. So, if this is dirty, flush it | ||
2141 | * DELWRI so that we don't get a freelist infested with | ||
2142 | * dirty dquots. | ||
2143 | */ | ||
2144 | if (XFS_DQ_IS_DIRTY(dqp)) { | ||
2145 | xfs_dqtrace_entry(dqp, "DQSHAKE: DQDIRTY"); | ||
2146 | /* | ||
2147 | * We flush it delayed write, so don't bother | ||
2148 | * releasing the mplock. | ||
2149 | */ | ||
2150 | (void) xfs_qm_dqflush(dqp, XFS_QMOPT_DELWRI); | ||
2151 | xfs_dqunlock(dqp); /* dqflush unlocks dqflock */ | ||
2152 | dqp = dqp->dq_flnext; | ||
2153 | continue; | ||
2154 | } | ||
2155 | /* | ||
2156 | * We're trying to get the hashlock out of order. This races | ||
2157 | * with dqlookup; so, we giveup and goto the next dquot if | ||
2158 | * we couldn't get the hashlock. This way, we won't starve | ||
2159 | * a dqlookup process that holds the hashlock that is | ||
2160 | * waiting for the freelist lock. | ||
2161 | */ | ||
2162 | if (! xfs_qm_dqhashlock_nowait(dqp)) { | ||
2163 | xfs_dqfunlock(dqp); | ||
2164 | xfs_dqunlock(dqp); | ||
2165 | dqp = dqp->dq_flnext; | ||
2166 | continue; | ||
2167 | } | ||
2168 | /* | ||
2169 | * This races with dquot allocation code as well as dqflush_all | ||
2170 | * and reclaim code. So, if we failed to grab the mplist lock, | ||
2171 | * giveup everything and start over. | ||
2172 | */ | ||
2173 | hash = dqp->q_hash; | ||
2174 | ASSERT(hash); | ||
2175 | if (! xfs_qm_mplist_nowait(dqp->q_mount)) { | ||
2176 | /* XXX put a sentinel so that we can come back here */ | ||
2177 | xfs_dqfunlock(dqp); | ||
2178 | xfs_dqunlock(dqp); | ||
2179 | XFS_DQ_HASH_UNLOCK(hash); | ||
2180 | xfs_qm_freelist_unlock(xfs_Gqm); | ||
2181 | if (++restarts >= XFS_QM_RECLAIM_MAX_RESTARTS) | ||
2182 | return (nreclaimed); | ||
2183 | goto tryagain; | ||
2184 | } | ||
2185 | xfs_dqtrace_entry(dqp, "DQSHAKE: UNLINKING"); | ||
2186 | #ifdef QUOTADEBUG | ||
2187 | cmn_err(CE_DEBUG, "Shake 0x%p, ID 0x%x\n", | ||
2188 | dqp, INT_GET(dqp->q_core.d_id, ARCH_CONVERT)); | ||
2189 | #endif | ||
2190 | ASSERT(dqp->q_nrefs == 0); | ||
2191 | nextdqp = dqp->dq_flnext; | ||
2192 | XQM_MPLIST_REMOVE(&(XFS_QI_MPL_LIST(dqp->q_mount)), dqp); | ||
2193 | XQM_HASHLIST_REMOVE(hash, dqp); | ||
2194 | xfs_dqfunlock(dqp); | ||
2195 | xfs_qm_mplist_unlock(dqp->q_mount); | ||
2196 | XFS_DQ_HASH_UNLOCK(hash); | ||
2197 | |||
2198 | off_freelist: | ||
2199 | XQM_FREELIST_REMOVE(dqp); | ||
2200 | xfs_dqunlock(dqp); | ||
2201 | nreclaimed++; | ||
2202 | XQM_STATS_INC(xqmstats.xs_qm_dqshake_reclaims); | ||
2203 | xfs_qm_dqdestroy(dqp); | ||
2204 | dqp = nextdqp; | ||
2205 | } | ||
2206 | xfs_qm_freelist_unlock(xfs_Gqm); | ||
2207 | return (nreclaimed); | ||
2208 | } | ||
2209 | |||
2210 | |||
2211 | /* | ||
2212 | * The kmem_shake interface is invoked when memory is running low. | ||
2213 | */ | ||
2214 | /* ARGSUSED */ | ||
2215 | STATIC int | ||
2216 | xfs_qm_shake(int nr_to_scan, unsigned int gfp_mask) | ||
2217 | { | ||
2218 | int ndqused, nfree, n; | ||
2219 | |||
2220 | if (!kmem_shake_allow(gfp_mask)) | ||
2221 | return (0); | ||
2222 | if (!xfs_Gqm) | ||
2223 | return (0); | ||
2224 | |||
2225 | nfree = xfs_Gqm->qm_dqfreelist.qh_nelems; /* free dquots */ | ||
2226 | /* incore dquots in all f/s's */ | ||
2227 | ndqused = atomic_read(&xfs_Gqm->qm_totaldquots) - nfree; | ||
2228 | |||
2229 | ASSERT(ndqused >= 0); | ||
2230 | |||
2231 | if (nfree <= ndqused && nfree < ndquot) | ||
2232 | return (0); | ||
2233 | |||
2234 | ndqused *= xfs_Gqm->qm_dqfree_ratio; /* target # of free dquots */ | ||
2235 | n = nfree - ndqused - ndquot; /* # over target */ | ||
2236 | |||
2237 | return xfs_qm_shake_freelist(MAX(nfree, n)); | ||
2238 | } | ||
2239 | |||
2240 | |||
2241 | /* | ||
2242 | * Just pop the least recently used dquot off the freelist and | ||
2243 | * recycle it. The returned dquot is locked. | ||
2244 | */ | ||
2245 | STATIC xfs_dquot_t * | ||
2246 | xfs_qm_dqreclaim_one(void) | ||
2247 | { | ||
2248 | xfs_dquot_t *dqpout; | ||
2249 | xfs_dquot_t *dqp; | ||
2250 | int restarts; | ||
2251 | int nflushes; | ||
2252 | |||
2253 | restarts = 0; | ||
2254 | dqpout = NULL; | ||
2255 | nflushes = 0; | ||
2256 | |||
2257 | /* lockorder: hashchainlock, freelistlock, mplistlock, dqlock, dqflock */ | ||
2258 | startagain: | ||
2259 | xfs_qm_freelist_lock(xfs_Gqm); | ||
2260 | |||
2261 | FOREACH_DQUOT_IN_FREELIST(dqp, &(xfs_Gqm->qm_dqfreelist)) { | ||
2262 | xfs_dqlock(dqp); | ||
2263 | |||
2264 | /* | ||
2265 | * We are racing with dqlookup here. Naturally we don't | ||
2266 | * want to reclaim a dquot that lookup wants. We release the | ||
2267 | * freelist lock and start over, so that lookup will grab | ||
2268 | * both the dquot and the freelistlock. | ||
2269 | */ | ||
2270 | if (dqp->dq_flags & XFS_DQ_WANT) { | ||
2271 | ASSERT(! (dqp->dq_flags & XFS_DQ_INACTIVE)); | ||
2272 | xfs_dqtrace_entry(dqp, "DQRECLAIM: DQWANT"); | ||
2273 | xfs_dqunlock(dqp); | ||
2274 | xfs_qm_freelist_unlock(xfs_Gqm); | ||
2275 | if (++restarts >= XFS_QM_RECLAIM_MAX_RESTARTS) | ||
2276 | return (NULL); | ||
2277 | XQM_STATS_INC(xqmstats.xs_qm_dqwants); | ||
2278 | goto startagain; | ||
2279 | } | ||
2280 | |||
2281 | /* | ||
2282 | * If the dquot is inactive, we are assured that it is | ||
2283 | * not on the mplist or the hashlist, and that makes our | ||
2284 | * life easier. | ||
2285 | */ | ||
2286 | if (dqp->dq_flags & XFS_DQ_INACTIVE) { | ||
2287 | ASSERT(dqp->q_mount == NULL); | ||
2288 | ASSERT(! XFS_DQ_IS_DIRTY(dqp)); | ||
2289 | ASSERT(dqp->HL_PREVP == NULL); | ||
2290 | ASSERT(dqp->MPL_PREVP == NULL); | ||
2291 | XQM_FREELIST_REMOVE(dqp); | ||
2292 | xfs_dqunlock(dqp); | ||
2293 | dqpout = dqp; | ||
2294 | XQM_STATS_INC(xqmstats.xs_qm_dqinact_reclaims); | ||
2295 | break; | ||
2296 | } | ||
2297 | |||
2298 | ASSERT(dqp->q_hash); | ||
2299 | ASSERT(dqp->MPL_PREVP); | ||
2300 | |||
2301 | /* | ||
2302 | * Try to grab the flush lock. If this dquot is in the process of | ||
2303 | * getting flushed to disk, we don't want to reclaim it. | ||
2304 | */ | ||
2305 | if (! xfs_qm_dqflock_nowait(dqp)) { | ||
2306 | xfs_dqunlock(dqp); | ||
2307 | continue; | ||
2308 | } | ||
2309 | |||
2310 | /* | ||
2311 | * We have the flush lock so we know that this is not in the | ||
2312 | * process of being flushed. So, if this is dirty, flush it | ||
2313 | * DELWRI so that we don't get a freelist infested with | ||
2314 | * dirty dquots. | ||
2315 | */ | ||
2316 | if (XFS_DQ_IS_DIRTY(dqp)) { | ||
2317 | xfs_dqtrace_entry(dqp, "DQRECLAIM: DQDIRTY"); | ||
2318 | /* | ||
2319 | * We flush it delayed write, so don't bother | ||
2320 | * releasing the freelist lock. | ||
2321 | */ | ||
2322 | (void) xfs_qm_dqflush(dqp, XFS_QMOPT_DELWRI); | ||
2323 | xfs_dqunlock(dqp); /* dqflush unlocks dqflock */ | ||
2324 | continue; | ||
2325 | } | ||
2326 | |||
2327 | if (! xfs_qm_mplist_nowait(dqp->q_mount)) { | ||
2328 | xfs_dqfunlock(dqp); | ||
2329 | xfs_dqunlock(dqp); | ||
2330 | continue; | ||
2331 | } | ||
2332 | |||
2333 | if (! xfs_qm_dqhashlock_nowait(dqp)) | ||
2334 | goto mplistunlock; | ||
2335 | |||
2336 | ASSERT(dqp->q_nrefs == 0); | ||
2337 | xfs_dqtrace_entry(dqp, "DQRECLAIM: UNLINKING"); | ||
2338 | XQM_MPLIST_REMOVE(&(XFS_QI_MPL_LIST(dqp->q_mount)), dqp); | ||
2339 | XQM_HASHLIST_REMOVE(dqp->q_hash, dqp); | ||
2340 | XQM_FREELIST_REMOVE(dqp); | ||
2341 | dqpout = dqp; | ||
2342 | XFS_DQ_HASH_UNLOCK(dqp->q_hash); | ||
2343 | mplistunlock: | ||
2344 | xfs_qm_mplist_unlock(dqp->q_mount); | ||
2345 | xfs_dqfunlock(dqp); | ||
2346 | xfs_dqunlock(dqp); | ||
2347 | if (dqpout) | ||
2348 | break; | ||
2349 | } | ||
2350 | |||
2351 | xfs_qm_freelist_unlock(xfs_Gqm); | ||
2352 | return (dqpout); | ||
2353 | } | ||
2354 | |||
2355 | |||
2356 | /*------------------------------------------------------------------*/ | ||
2357 | |||
2358 | /* | ||
2359 | * Return a new incore dquot. Depending on the number of | ||
2360 | * dquots in the system, we either allocate a new one on the kernel heap, | ||
2361 | * or reclaim a free one. | ||
2362 | * Return value is B_TRUE if we allocated a new dquot, B_FALSE if we managed | ||
2363 | * to reclaim an existing one from the freelist. | ||
2364 | */ | ||
2365 | boolean_t | ||
2366 | xfs_qm_dqalloc_incore( | ||
2367 | xfs_dquot_t **O_dqpp) | ||
2368 | { | ||
2369 | xfs_dquot_t *dqp; | ||
2370 | |||
2371 | /* | ||
2372 | * Check against high water mark to see if we want to pop | ||
2373 | * a nincompoop dquot off the freelist. | ||
2374 | */ | ||
2375 | if (atomic_read(&xfs_Gqm->qm_totaldquots) >= ndquot) { | ||
2376 | /* | ||
2377 | * Try to recycle a dquot from the freelist. | ||
2378 | */ | ||
2379 | if ((dqp = xfs_qm_dqreclaim_one())) { | ||
2380 | XQM_STATS_INC(xqmstats.xs_qm_dqreclaims); | ||
2381 | /* | ||
2382 | * Just zero the core here. The rest will get | ||
2383 | * reinitialized by caller. XXX we shouldn't even | ||
2384 | * do this zero ... | ||
2385 | */ | ||
2386 | memset(&dqp->q_core, 0, sizeof(dqp->q_core)); | ||
2387 | *O_dqpp = dqp; | ||
2388 | return (B_FALSE); | ||
2389 | } | ||
2390 | XQM_STATS_INC(xqmstats.xs_qm_dqreclaim_misses); | ||
2391 | } | ||
2392 | |||
2393 | /* | ||
2394 | * Allocate a brand new dquot on the kernel heap and return it | ||
2395 | * to the caller to initialize. | ||
2396 | */ | ||
2397 | ASSERT(xfs_Gqm->qm_dqzone != NULL); | ||
2398 | *O_dqpp = kmem_zone_zalloc(xfs_Gqm->qm_dqzone, KM_SLEEP); | ||
2399 | atomic_inc(&xfs_Gqm->qm_totaldquots); | ||
2400 | |||
2401 | return (B_TRUE); | ||
2402 | } | ||
2403 | |||
2404 | |||
2405 | /* | ||
2406 | * Start a transaction and write the incore superblock changes to | ||
2407 | * disk. flags parameter indicates which fields have changed. | ||
2408 | */ | ||
2409 | int | ||
2410 | xfs_qm_write_sb_changes( | ||
2411 | xfs_mount_t *mp, | ||
2412 | __int64_t flags) | ||
2413 | { | ||
2414 | xfs_trans_t *tp; | ||
2415 | int error; | ||
2416 | |||
2417 | #ifdef QUOTADEBUG | ||
2418 | cmn_err(CE_NOTE, "Writing superblock quota changes :%s", mp->m_fsname); | ||
2419 | #endif | ||
2420 | tp = xfs_trans_alloc(mp, XFS_TRANS_QM_SBCHANGE); | ||
2421 | if ((error = xfs_trans_reserve(tp, 0, | ||
2422 | mp->m_sb.sb_sectsize + 128, 0, | ||
2423 | 0, | ||
2424 | XFS_DEFAULT_LOG_COUNT))) { | ||
2425 | xfs_trans_cancel(tp, 0); | ||
2426 | return (error); | ||
2427 | } | ||
2428 | |||
2429 | xfs_mod_sb(tp, flags); | ||
2430 | (void) xfs_trans_commit(tp, 0, NULL); | ||
2431 | |||
2432 | return (0); | ||
2433 | } | ||
2434 | |||
2435 | |||
2436 | /* --------------- utility functions for vnodeops ---------------- */ | ||
2437 | |||
2438 | |||
2439 | /* | ||
2440 | * Given an inode, a uid and gid (from cred_t) make sure that we have | ||
2441 | * allocated relevant dquot(s) on disk, and that we won't exceed inode | ||
2442 | * quotas by creating this file. | ||
2443 | * This also attaches dquot(s) to the given inode after locking it, | ||
2444 | * and returns the dquots corresponding to the uid and/or gid. | ||
2445 | * | ||
2446 | * in : inode (unlocked) | ||
2447 | * out : udquot, gdquot with references taken and unlocked | ||
2448 | */ | ||
2449 | int | ||
2450 | xfs_qm_vop_dqalloc( | ||
2451 | xfs_mount_t *mp, | ||
2452 | xfs_inode_t *ip, | ||
2453 | uid_t uid, | ||
2454 | gid_t gid, | ||
2455 | uint flags, | ||
2456 | xfs_dquot_t **O_udqpp, | ||
2457 | xfs_dquot_t **O_gdqpp) | ||
2458 | { | ||
2459 | int error; | ||
2460 | xfs_dquot_t *uq, *gq; | ||
2461 | uint lockflags; | ||
2462 | |||
2463 | if (!XFS_IS_QUOTA_ON(mp)) | ||
2464 | return 0; | ||
2465 | |||
2466 | lockflags = XFS_ILOCK_EXCL; | ||
2467 | xfs_ilock(ip, lockflags); | ||
2468 | |||
2469 | if ((flags & XFS_QMOPT_INHERIT) && | ||
2470 | XFS_INHERIT_GID(ip, XFS_MTOVFS(mp))) | ||
2471 | gid = ip->i_d.di_gid; | ||
2472 | |||
2473 | /* | ||
2474 | * Attach the dquot(s) to this inode, doing a dquot allocation | ||
2475 | * if necessary. The dquot(s) will not be locked. | ||
2476 | */ | ||
2477 | if (XFS_NOT_DQATTACHED(mp, ip)) { | ||
2478 | if ((error = xfs_qm_dqattach(ip, XFS_QMOPT_DQALLOC | | ||
2479 | XFS_QMOPT_ILOCKED))) { | ||
2480 | xfs_iunlock(ip, lockflags); | ||
2481 | return (error); | ||
2482 | } | ||
2483 | } | ||
2484 | |||
2485 | uq = gq = NULL; | ||
2486 | if ((flags & XFS_QMOPT_UQUOTA) && | ||
2487 | XFS_IS_UQUOTA_ON(mp)) { | ||
2488 | if (ip->i_d.di_uid != uid) { | ||
2489 | /* | ||
2490 | * What we need is the dquot that has this uid, and | ||
2491 | * if we send the inode to dqget, the uid of the inode | ||
2492 | * takes priority over what's sent in the uid argument. | ||
2493 | * We must unlock inode here before calling dqget if | ||
2494 | * we're not sending the inode, because otherwise | ||
2495 | * we'll deadlock by doing trans_reserve while | ||
2496 | * holding ilock. | ||
2497 | */ | ||
2498 | xfs_iunlock(ip, lockflags); | ||
2499 | if ((error = xfs_qm_dqget(mp, NULL, (xfs_dqid_t) uid, | ||
2500 | XFS_DQ_USER, | ||
2501 | XFS_QMOPT_DQALLOC | | ||
2502 | XFS_QMOPT_DOWARN, | ||
2503 | &uq))) { | ||
2504 | ASSERT(error != ENOENT); | ||
2505 | return (error); | ||
2506 | } | ||
2507 | /* | ||
2508 | * Get the ilock in the right order. | ||
2509 | */ | ||
2510 | xfs_dqunlock(uq); | ||
2511 | lockflags = XFS_ILOCK_SHARED; | ||
2512 | xfs_ilock(ip, lockflags); | ||
2513 | } else { | ||
2514 | /* | ||
2515 | * Take an extra reference, because we'll return | ||
2516 | * this to caller | ||
2517 | */ | ||
2518 | ASSERT(ip->i_udquot); | ||
2519 | uq = ip->i_udquot; | ||
2520 | xfs_dqlock(uq); | ||
2521 | XFS_DQHOLD(uq); | ||
2522 | xfs_dqunlock(uq); | ||
2523 | } | ||
2524 | } | ||
2525 | if ((flags & XFS_QMOPT_GQUOTA) && | ||
2526 | XFS_IS_GQUOTA_ON(mp)) { | ||
2527 | if (ip->i_d.di_gid != gid) { | ||
2528 | xfs_iunlock(ip, lockflags); | ||
2529 | if ((error = xfs_qm_dqget(mp, NULL, (xfs_dqid_t)gid, | ||
2530 | XFS_DQ_GROUP, | ||
2531 | XFS_QMOPT_DQALLOC | | ||
2532 | XFS_QMOPT_DOWARN, | ||
2533 | &gq))) { | ||
2534 | if (uq) | ||
2535 | xfs_qm_dqrele(uq); | ||
2536 | ASSERT(error != ENOENT); | ||
2537 | return (error); | ||
2538 | } | ||
2539 | xfs_dqunlock(gq); | ||
2540 | lockflags = XFS_ILOCK_SHARED; | ||
2541 | xfs_ilock(ip, lockflags); | ||
2542 | } else { | ||
2543 | ASSERT(ip->i_gdquot); | ||
2544 | gq = ip->i_gdquot; | ||
2545 | xfs_dqlock(gq); | ||
2546 | XFS_DQHOLD(gq); | ||
2547 | xfs_dqunlock(gq); | ||
2548 | } | ||
2549 | } | ||
2550 | if (uq) | ||
2551 | xfs_dqtrace_entry_ino(uq, "DQALLOC", ip); | ||
2552 | |||
2553 | xfs_iunlock(ip, lockflags); | ||
2554 | if (O_udqpp) | ||
2555 | *O_udqpp = uq; | ||
2556 | else if (uq) | ||
2557 | xfs_qm_dqrele(uq); | ||
2558 | if (O_gdqpp) | ||
2559 | *O_gdqpp = gq; | ||
2560 | else if (gq) | ||
2561 | xfs_qm_dqrele(gq); | ||
2562 | return (0); | ||
2563 | } | ||
2564 | |||
2565 | /* | ||
2566 | * Actually transfer ownership, and do dquot modifications. | ||
2567 | * These were already reserved. | ||
2568 | */ | ||
2569 | xfs_dquot_t * | ||
2570 | xfs_qm_vop_chown( | ||
2571 | xfs_trans_t *tp, | ||
2572 | xfs_inode_t *ip, | ||
2573 | xfs_dquot_t **IO_olddq, | ||
2574 | xfs_dquot_t *newdq) | ||
2575 | { | ||
2576 | xfs_dquot_t *prevdq; | ||
2577 | ASSERT(XFS_ISLOCKED_INODE_EXCL(ip)); | ||
2578 | ASSERT(XFS_IS_QUOTA_RUNNING(ip->i_mount)); | ||
2579 | |||
2580 | /* old dquot */ | ||
2581 | prevdq = *IO_olddq; | ||
2582 | ASSERT(prevdq); | ||
2583 | ASSERT(prevdq != newdq); | ||
2584 | |||
2585 | xfs_trans_mod_dquot(tp, prevdq, | ||
2586 | XFS_TRANS_DQ_BCOUNT, | ||
2587 | -(ip->i_d.di_nblocks)); | ||
2588 | xfs_trans_mod_dquot(tp, prevdq, | ||
2589 | XFS_TRANS_DQ_ICOUNT, | ||
2590 | -1); | ||
2591 | |||
2592 | /* the sparkling new dquot */ | ||
2593 | xfs_trans_mod_dquot(tp, newdq, | ||
2594 | XFS_TRANS_DQ_BCOUNT, | ||
2595 | ip->i_d.di_nblocks); | ||
2596 | xfs_trans_mod_dquot(tp, newdq, | ||
2597 | XFS_TRANS_DQ_ICOUNT, | ||
2598 | 1); | ||
2599 | |||
2600 | /* | ||
2601 | * Take an extra reference, because the inode | ||
2602 | * is going to keep this dquot pointer even | ||
2603 | * after the trans_commit. | ||
2604 | */ | ||
2605 | xfs_dqlock(newdq); | ||
2606 | XFS_DQHOLD(newdq); | ||
2607 | xfs_dqunlock(newdq); | ||
2608 | *IO_olddq = newdq; | ||
2609 | |||
2610 | return (prevdq); | ||
2611 | } | ||
2612 | |||
2613 | /* | ||
2614 | * Quota reservations for setattr(AT_UID|AT_GID). | ||
2615 | */ | ||
2616 | int | ||
2617 | xfs_qm_vop_chown_reserve( | ||
2618 | xfs_trans_t *tp, | ||
2619 | xfs_inode_t *ip, | ||
2620 | xfs_dquot_t *udqp, | ||
2621 | xfs_dquot_t *gdqp, | ||
2622 | uint flags) | ||
2623 | { | ||
2624 | int error; | ||
2625 | xfs_mount_t *mp; | ||
2626 | uint delblks; | ||
2627 | xfs_dquot_t *unresudq, *unresgdq, *delblksudq, *delblksgdq; | ||
2628 | |||
2629 | ASSERT(XFS_ISLOCKED_INODE(ip)); | ||
2630 | mp = ip->i_mount; | ||
2631 | ASSERT(XFS_IS_QUOTA_RUNNING(mp)); | ||
2632 | |||
2633 | delblks = ip->i_delayed_blks; | ||
2634 | delblksudq = delblksgdq = unresudq = unresgdq = NULL; | ||
2635 | |||
2636 | if (XFS_IS_UQUOTA_ON(mp) && udqp && | ||
2637 | ip->i_d.di_uid != (uid_t)INT_GET(udqp->q_core.d_id, ARCH_CONVERT)) { | ||
2638 | delblksudq = udqp; | ||
2639 | /* | ||
2640 | * If there are delayed allocation blocks, then we have to | ||
2641 | * unreserve those from the old dquot, and add them to the | ||
2642 | * new dquot. | ||
2643 | */ | ||
2644 | if (delblks) { | ||
2645 | ASSERT(ip->i_udquot); | ||
2646 | unresudq = ip->i_udquot; | ||
2647 | } | ||
2648 | } | ||
2649 | if (XFS_IS_GQUOTA_ON(ip->i_mount) && gdqp && | ||
2650 | ip->i_d.di_gid != INT_GET(gdqp->q_core.d_id, ARCH_CONVERT)) { | ||
2651 | delblksgdq = gdqp; | ||
2652 | if (delblks) { | ||
2653 | ASSERT(ip->i_gdquot); | ||
2654 | unresgdq = ip->i_gdquot; | ||
2655 | } | ||
2656 | } | ||
2657 | |||
2658 | if ((error = xfs_trans_reserve_quota_bydquots(tp, ip->i_mount, | ||
2659 | delblksudq, delblksgdq, ip->i_d.di_nblocks, 1, | ||
2660 | flags | XFS_QMOPT_RES_REGBLKS))) | ||
2661 | return (error); | ||
2662 | |||
2663 | /* | ||
2664 | * Do the delayed blks reservations/unreservations now. Since, these | ||
2665 | * are done without the help of a transaction, if a reservation fails | ||
2666 | * its previous reservations won't be automatically undone by trans | ||
2667 | * code. So, we have to do it manually here. | ||
2668 | */ | ||
2669 | if (delblks) { | ||
2670 | /* | ||
2671 | * Do the reservations first. Unreservation can't fail. | ||
2672 | */ | ||
2673 | ASSERT(delblksudq || delblksgdq); | ||
2674 | ASSERT(unresudq || unresgdq); | ||
2675 | if ((error = xfs_trans_reserve_quota_bydquots(NULL, ip->i_mount, | ||
2676 | delblksudq, delblksgdq, (xfs_qcnt_t)delblks, 0, | ||
2677 | flags | XFS_QMOPT_RES_REGBLKS))) | ||
2678 | return (error); | ||
2679 | xfs_trans_reserve_quota_bydquots(NULL, ip->i_mount, | ||
2680 | unresudq, unresgdq, -((xfs_qcnt_t)delblks), 0, | ||
2681 | XFS_QMOPT_RES_REGBLKS); | ||
2682 | } | ||
2683 | |||
2684 | return (0); | ||
2685 | } | ||
2686 | |||
2687 | int | ||
2688 | xfs_qm_vop_rename_dqattach( | ||
2689 | xfs_inode_t **i_tab) | ||
2690 | { | ||
2691 | xfs_inode_t *ip; | ||
2692 | int i; | ||
2693 | int error; | ||
2694 | |||
2695 | ip = i_tab[0]; | ||
2696 | |||
2697 | if (! XFS_IS_QUOTA_ON(ip->i_mount)) | ||
2698 | return (0); | ||
2699 | |||
2700 | if (XFS_NOT_DQATTACHED(ip->i_mount, ip)) { | ||
2701 | error = xfs_qm_dqattach(ip, 0); | ||
2702 | if (error) | ||
2703 | return (error); | ||
2704 | } | ||
2705 | for (i = 1; (i < 4 && i_tab[i]); i++) { | ||
2706 | /* | ||
2707 | * Watch out for duplicate entries in the table. | ||
2708 | */ | ||
2709 | if ((ip = i_tab[i]) != i_tab[i-1]) { | ||
2710 | if (XFS_NOT_DQATTACHED(ip->i_mount, ip)) { | ||
2711 | error = xfs_qm_dqattach(ip, 0); | ||
2712 | if (error) | ||
2713 | return (error); | ||
2714 | } | ||
2715 | } | ||
2716 | } | ||
2717 | return (0); | ||
2718 | } | ||
2719 | |||
2720 | void | ||
2721 | xfs_qm_vop_dqattach_and_dqmod_newinode( | ||
2722 | xfs_trans_t *tp, | ||
2723 | xfs_inode_t *ip, | ||
2724 | xfs_dquot_t *udqp, | ||
2725 | xfs_dquot_t *gdqp) | ||
2726 | { | ||
2727 | if (!XFS_IS_QUOTA_ON(tp->t_mountp)) | ||
2728 | return; | ||
2729 | |||
2730 | ASSERT(XFS_ISLOCKED_INODE_EXCL(ip)); | ||
2731 | ASSERT(XFS_IS_QUOTA_RUNNING(tp->t_mountp)); | ||
2732 | |||
2733 | if (udqp) { | ||
2734 | xfs_dqlock(udqp); | ||
2735 | XFS_DQHOLD(udqp); | ||
2736 | xfs_dqunlock(udqp); | ||
2737 | ASSERT(ip->i_udquot == NULL); | ||
2738 | ip->i_udquot = udqp; | ||
2739 | ASSERT(ip->i_d.di_uid == INT_GET(udqp->q_core.d_id, ARCH_CONVERT)); | ||
2740 | xfs_trans_mod_dquot(tp, udqp, XFS_TRANS_DQ_ICOUNT, 1); | ||
2741 | } | ||
2742 | if (gdqp) { | ||
2743 | xfs_dqlock(gdqp); | ||
2744 | XFS_DQHOLD(gdqp); | ||
2745 | xfs_dqunlock(gdqp); | ||
2746 | ASSERT(ip->i_gdquot == NULL); | ||
2747 | ip->i_gdquot = gdqp; | ||
2748 | ASSERT(ip->i_d.di_gid == INT_GET(gdqp->q_core.d_id, ARCH_CONVERT)); | ||
2749 | xfs_trans_mod_dquot(tp, gdqp, XFS_TRANS_DQ_ICOUNT, 1); | ||
2750 | } | ||
2751 | } | ||
2752 | |||
2753 | /* ------------- list stuff -----------------*/ | ||
2754 | void | ||
2755 | xfs_qm_freelist_init(xfs_frlist_t *ql) | ||
2756 | { | ||
2757 | ql->qh_next = ql->qh_prev = (xfs_dquot_t *) ql; | ||
2758 | mutex_init(&ql->qh_lock, MUTEX_DEFAULT, "dqf"); | ||
2759 | ql->qh_version = 0; | ||
2760 | ql->qh_nelems = 0; | ||
2761 | } | ||
2762 | |||
2763 | void | ||
2764 | xfs_qm_freelist_destroy(xfs_frlist_t *ql) | ||
2765 | { | ||
2766 | xfs_dquot_t *dqp, *nextdqp; | ||
2767 | |||
2768 | mutex_lock(&ql->qh_lock, PINOD); | ||
2769 | for (dqp = ql->qh_next; | ||
2770 | dqp != (xfs_dquot_t *)ql; ) { | ||
2771 | xfs_dqlock(dqp); | ||
2772 | nextdqp = dqp->dq_flnext; | ||
2773 | #ifdef QUOTADEBUG | ||
2774 | cmn_err(CE_DEBUG, "FREELIST destroy 0x%p", dqp); | ||
2775 | #endif | ||
2776 | XQM_FREELIST_REMOVE(dqp); | ||
2777 | xfs_dqunlock(dqp); | ||
2778 | xfs_qm_dqdestroy(dqp); | ||
2779 | dqp = nextdqp; | ||
2780 | } | ||
2781 | /* | ||
2782 | * Don't bother about unlocking. | ||
2783 | */ | ||
2784 | mutex_destroy(&ql->qh_lock); | ||
2785 | |||
2786 | ASSERT(ql->qh_nelems == 0); | ||
2787 | } | ||
2788 | |||
2789 | void | ||
2790 | xfs_qm_freelist_insert(xfs_frlist_t *ql, xfs_dquot_t *dq) | ||
2791 | { | ||
2792 | dq->dq_flnext = ql->qh_next; | ||
2793 | dq->dq_flprev = (xfs_dquot_t *)ql; | ||
2794 | ql->qh_next = dq; | ||
2795 | dq->dq_flnext->dq_flprev = dq; | ||
2796 | xfs_Gqm->qm_dqfreelist.qh_nelems++; | ||
2797 | xfs_Gqm->qm_dqfreelist.qh_version++; | ||
2798 | } | ||
2799 | |||
2800 | void | ||
2801 | xfs_qm_freelist_unlink(xfs_dquot_t *dq) | ||
2802 | { | ||
2803 | xfs_dquot_t *next = dq->dq_flnext; | ||
2804 | xfs_dquot_t *prev = dq->dq_flprev; | ||
2805 | |||
2806 | next->dq_flprev = prev; | ||
2807 | prev->dq_flnext = next; | ||
2808 | dq->dq_flnext = dq->dq_flprev = dq; | ||
2809 | xfs_Gqm->qm_dqfreelist.qh_nelems--; | ||
2810 | xfs_Gqm->qm_dqfreelist.qh_version++; | ||
2811 | } | ||
2812 | |||
2813 | void | ||
2814 | xfs_qm_freelist_append(xfs_frlist_t *ql, xfs_dquot_t *dq) | ||
2815 | { | ||
2816 | xfs_qm_freelist_insert((xfs_frlist_t *)ql->qh_prev, dq); | ||
2817 | } | ||
2818 | |||
2819 | int | ||
2820 | xfs_qm_dqhashlock_nowait( | ||
2821 | xfs_dquot_t *dqp) | ||
2822 | { | ||
2823 | int locked; | ||
2824 | |||
2825 | locked = mutex_trylock(&((dqp)->q_hash->qh_lock)); | ||
2826 | return (locked); | ||
2827 | } | ||
2828 | |||
2829 | int | ||
2830 | xfs_qm_freelist_lock_nowait( | ||
2831 | xfs_qm_t *xqm) | ||
2832 | { | ||
2833 | int locked; | ||
2834 | |||
2835 | locked = mutex_trylock(&(xqm->qm_dqfreelist.qh_lock)); | ||
2836 | return (locked); | ||
2837 | } | ||
2838 | |||
2839 | int | ||
2840 | xfs_qm_mplist_nowait( | ||
2841 | xfs_mount_t *mp) | ||
2842 | { | ||
2843 | int locked; | ||
2844 | |||
2845 | ASSERT(mp->m_quotainfo); | ||
2846 | locked = mutex_trylock(&(XFS_QI_MPLLOCK(mp))); | ||
2847 | return (locked); | ||
2848 | } | ||
diff --git a/fs/xfs/quota/xfs_qm.h b/fs/xfs/quota/xfs_qm.h new file mode 100644 index 000000000000..dcf1a7a831d8 --- /dev/null +++ b/fs/xfs/quota/xfs_qm.h | |||
@@ -0,0 +1,236 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms of version 2 of the GNU General Public License as | ||
6 | * published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope that it would be useful, but | ||
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | * | ||
12 | * Further, this software is distributed without any warranty that it is | ||
13 | * free of the rightful claim of any third person regarding infringement | ||
14 | * or the like. Any license provided herein, whether implied or | ||
15 | * otherwise, applies only to this software file. Patent licenses, if | ||
16 | * any, provided herein do not apply to combinations of this program with | ||
17 | * other software, or any other product whatsoever. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License along | ||
20 | * with this program; if not, write the Free Software Foundation, Inc., 59 | ||
21 | * Temple Place - Suite 330, Boston MA 02111-1307, USA. | ||
22 | * | ||
23 | * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, | ||
24 | * Mountain View, CA 94043, or: | ||
25 | * | ||
26 | * http://www.sgi.com | ||
27 | * | ||
28 | * For further information regarding this notice, see: | ||
29 | * | ||
30 | * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ | ||
31 | */ | ||
32 | #ifndef __XFS_QM_H__ | ||
33 | #define __XFS_QM_H__ | ||
34 | |||
35 | #include "xfs_dquot_item.h" | ||
36 | #include "xfs_dquot.h" | ||
37 | #include "xfs_quota_priv.h" | ||
38 | #include "xfs_qm_stats.h" | ||
39 | |||
40 | struct xfs_qm; | ||
41 | struct xfs_inode; | ||
42 | |||
43 | extern mutex_t xfs_Gqm_lock; | ||
44 | extern struct xfs_qm *xfs_Gqm; | ||
45 | extern kmem_zone_t *qm_dqzone; | ||
46 | extern kmem_zone_t *qm_dqtrxzone; | ||
47 | |||
48 | /* | ||
49 | * Used in xfs_qm_sync called by xfs_sync to count the max times that it can | ||
50 | * iterate over the mountpt's dquot list in one call. | ||
51 | */ | ||
52 | #define XFS_QM_SYNC_MAX_RESTARTS 7 | ||
53 | |||
54 | /* | ||
55 | * Ditto, for xfs_qm_dqreclaim_one. | ||
56 | */ | ||
57 | #define XFS_QM_RECLAIM_MAX_RESTARTS 4 | ||
58 | |||
59 | /* | ||
60 | * Ideal ratio of free to in use dquots. Quota manager makes an attempt | ||
61 | * to keep this balance. | ||
62 | */ | ||
63 | #define XFS_QM_DQFREE_RATIO 2 | ||
64 | |||
65 | /* | ||
66 | * Dquot hashtable constants/threshold values. | ||
67 | */ | ||
68 | #define XFS_QM_NCSIZE_THRESHOLD 5000 | ||
69 | #define XFS_QM_HASHSIZE_LOW 32 | ||
70 | #define XFS_QM_HASHSIZE_HIGH 64 | ||
71 | |||
72 | /* | ||
73 | * We output a cmn_err when quotachecking a quota file with more than | ||
74 | * this many fsbs. | ||
75 | */ | ||
76 | #define XFS_QM_BIG_QCHECK_NBLKS 500 | ||
77 | |||
78 | /* | ||
79 | * This defines the unit of allocation of dquots. | ||
80 | * Currently, it is just one file system block, and a 4K blk contains 30 | ||
81 | * (136 * 30 = 4080) dquots. It's probably not worth trying to make | ||
82 | * this more dynamic. | ||
83 | * XXXsup However, if this number is changed, we have to make sure that we don't | ||
84 | * implicitly assume that we do allocations in chunks of a single filesystem | ||
85 | * block in the dquot/xqm code. | ||
86 | */ | ||
87 | #define XFS_DQUOT_CLUSTER_SIZE_FSB (xfs_filblks_t)1 | ||
88 | /* | ||
89 | * When doing a quotacheck, we log dquot clusters of this many FSBs at most | ||
90 | * in a single transaction. We don't want to ask for too huge a log reservation. | ||
91 | */ | ||
92 | #define XFS_QM_MAX_DQCLUSTER_LOGSZ 3 | ||
93 | |||
94 | typedef xfs_dqhash_t xfs_dqlist_t; | ||
95 | /* | ||
96 | * The freelist head. The first two fields match the first two in the | ||
97 | * xfs_dquot_t structure (in xfs_dqmarker_t) | ||
98 | */ | ||
99 | typedef struct xfs_frlist { | ||
100 | struct xfs_dquot *qh_next; | ||
101 | struct xfs_dquot *qh_prev; | ||
102 | mutex_t qh_lock; | ||
103 | uint qh_version; | ||
104 | uint qh_nelems; | ||
105 | } xfs_frlist_t; | ||
106 | |||
107 | /* | ||
108 | * Quota Manager (global) structure. Lives only in core. | ||
109 | */ | ||
110 | typedef struct xfs_qm { | ||
111 | xfs_dqlist_t *qm_usr_dqhtable;/* udquot hash table */ | ||
112 | xfs_dqlist_t *qm_grp_dqhtable;/* gdquot hash table */ | ||
113 | uint qm_dqhashmask; /* # buckets in dq hashtab - 1 */ | ||
114 | xfs_frlist_t qm_dqfreelist; /* freelist of dquots */ | ||
115 | atomic_t qm_totaldquots; /* total incore dquots */ | ||
116 | uint qm_nrefs; /* file systems with quota on */ | ||
117 | int qm_dqfree_ratio;/* ratio of free to inuse dquots */ | ||
118 | kmem_zone_t *qm_dqzone; /* dquot mem-alloc zone */ | ||
119 | kmem_zone_t *qm_dqtrxzone; /* t_dqinfo of transactions */ | ||
120 | } xfs_qm_t; | ||
121 | |||
122 | /* | ||
123 | * Various quota information for individual filesystems. | ||
124 | * The mount structure keeps a pointer to this. | ||
125 | */ | ||
126 | typedef struct xfs_quotainfo { | ||
127 | xfs_inode_t *qi_uquotaip; /* user quota inode */ | ||
128 | xfs_inode_t *qi_gquotaip; /* group quota inode */ | ||
129 | lock_t qi_pinlock; /* dquot pinning mutex */ | ||
130 | xfs_dqlist_t qi_dqlist; /* all dquots in filesys */ | ||
131 | int qi_dqreclaims; /* a change here indicates | ||
132 | a removal in the dqlist */ | ||
133 | time_t qi_btimelimit; /* limit for blks timer */ | ||
134 | time_t qi_itimelimit; /* limit for inodes timer */ | ||
135 | time_t qi_rtbtimelimit;/* limit for rt blks timer */ | ||
136 | xfs_qwarncnt_t qi_bwarnlimit; /* limit for num warnings */ | ||
137 | xfs_qwarncnt_t qi_iwarnlimit; /* limit for num warnings */ | ||
138 | mutex_t qi_quotaofflock;/* to serialize quotaoff */ | ||
139 | xfs_filblks_t qi_dqchunklen; /* # BBs in a chunk of dqs */ | ||
140 | uint qi_dqperchunk; /* # ondisk dqs in above chunk */ | ||
141 | xfs_qcnt_t qi_bhardlimit; /* default data blk hard limit */ | ||
142 | xfs_qcnt_t qi_bsoftlimit; /* default data blk soft limit */ | ||
143 | xfs_qcnt_t qi_ihardlimit; /* default inode count hard limit */ | ||
144 | xfs_qcnt_t qi_isoftlimit; /* default inode count soft limit */ | ||
145 | xfs_qcnt_t qi_rtbhardlimit;/* default realtime blk hard limit */ | ||
146 | xfs_qcnt_t qi_rtbsoftlimit;/* default realtime blk soft limit */ | ||
147 | } xfs_quotainfo_t; | ||
148 | |||
149 | |||
150 | extern xfs_dqtrxops_t xfs_trans_dquot_ops; | ||
151 | |||
152 | extern void xfs_trans_mod_dquot(xfs_trans_t *, xfs_dquot_t *, uint, long); | ||
153 | extern int xfs_trans_reserve_quota_bydquots(xfs_trans_t *, xfs_mount_t *, | ||
154 | xfs_dquot_t *, xfs_dquot_t *, long, long, uint); | ||
155 | extern void xfs_trans_dqjoin(xfs_trans_t *, xfs_dquot_t *); | ||
156 | extern void xfs_trans_log_dquot(xfs_trans_t *, xfs_dquot_t *); | ||
157 | |||
158 | /* | ||
159 | * We keep the usr and grp dquots separately so that locking will be easier | ||
160 | * to do at commit time. All transactions that we know of at this point | ||
161 | * affect no more than two dquots of one type. Hence, the TRANS_MAXDQS value. | ||
162 | */ | ||
163 | #define XFS_QM_TRANS_MAXDQS 2 | ||
164 | typedef struct xfs_dquot_acct { | ||
165 | xfs_dqtrx_t dqa_usrdquots[XFS_QM_TRANS_MAXDQS]; | ||
166 | xfs_dqtrx_t dqa_grpdquots[XFS_QM_TRANS_MAXDQS]; | ||
167 | } xfs_dquot_acct_t; | ||
168 | |||
169 | /* | ||
170 | * Users are allowed to have a usage exceeding their softlimit for | ||
171 | * a period this long. | ||
172 | */ | ||
173 | #define XFS_QM_BTIMELIMIT (7 * 24*60*60) /* 1 week */ | ||
174 | #define XFS_QM_RTBTIMELIMIT (7 * 24*60*60) /* 1 week */ | ||
175 | #define XFS_QM_ITIMELIMIT (7 * 24*60*60) /* 1 week */ | ||
176 | |||
177 | #define XFS_QM_BWARNLIMIT 5 | ||
178 | #define XFS_QM_IWARNLIMIT 5 | ||
179 | |||
180 | #define XFS_QM_LOCK(xqm) (mutex_lock(&xqm##_lock, PINOD)) | ||
181 | #define XFS_QM_UNLOCK(xqm) (mutex_unlock(&xqm##_lock)) | ||
182 | #define XFS_QM_HOLD(xqm) ((xqm)->qm_nrefs++) | ||
183 | #define XFS_QM_RELE(xqm) ((xqm)->qm_nrefs--) | ||
184 | |||
185 | extern void xfs_mount_reset_sbqflags(xfs_mount_t *); | ||
186 | |||
187 | extern int xfs_qm_init_quotainfo(xfs_mount_t *); | ||
188 | extern void xfs_qm_destroy_quotainfo(xfs_mount_t *); | ||
189 | extern int xfs_qm_mount_quotas(xfs_mount_t *, int); | ||
190 | extern void xfs_qm_mount_quotainit(xfs_mount_t *, uint); | ||
191 | extern int xfs_qm_quotacheck(xfs_mount_t *); | ||
192 | extern void xfs_qm_unmount_quotadestroy(xfs_mount_t *); | ||
193 | extern int xfs_qm_unmount_quotas(xfs_mount_t *); | ||
194 | extern int xfs_qm_write_sb_changes(xfs_mount_t *, __int64_t); | ||
195 | extern int xfs_qm_sync(xfs_mount_t *, short); | ||
196 | |||
197 | /* dquot stuff */ | ||
198 | extern boolean_t xfs_qm_dqalloc_incore(xfs_dquot_t **); | ||
199 | extern int xfs_qm_dqattach(xfs_inode_t *, uint); | ||
200 | extern void xfs_qm_dqdetach(xfs_inode_t *); | ||
201 | extern int xfs_qm_dqpurge_all(xfs_mount_t *, uint); | ||
202 | extern void xfs_qm_dqrele_all_inodes(xfs_mount_t *, uint); | ||
203 | |||
204 | /* vop stuff */ | ||
205 | extern int xfs_qm_vop_dqalloc(xfs_mount_t *, xfs_inode_t *, | ||
206 | uid_t, gid_t, uint, | ||
207 | xfs_dquot_t **, xfs_dquot_t **); | ||
208 | extern void xfs_qm_vop_dqattach_and_dqmod_newinode( | ||
209 | xfs_trans_t *, xfs_inode_t *, | ||
210 | xfs_dquot_t *, xfs_dquot_t *); | ||
211 | extern int xfs_qm_vop_rename_dqattach(xfs_inode_t **); | ||
212 | extern xfs_dquot_t * xfs_qm_vop_chown(xfs_trans_t *, xfs_inode_t *, | ||
213 | xfs_dquot_t **, xfs_dquot_t *); | ||
214 | extern int xfs_qm_vop_chown_reserve(xfs_trans_t *, xfs_inode_t *, | ||
215 | xfs_dquot_t *, xfs_dquot_t *, uint); | ||
216 | |||
217 | /* list stuff */ | ||
218 | extern void xfs_qm_freelist_init(xfs_frlist_t *); | ||
219 | extern void xfs_qm_freelist_destroy(xfs_frlist_t *); | ||
220 | extern void xfs_qm_freelist_insert(xfs_frlist_t *, xfs_dquot_t *); | ||
221 | extern void xfs_qm_freelist_append(xfs_frlist_t *, xfs_dquot_t *); | ||
222 | extern void xfs_qm_freelist_unlink(xfs_dquot_t *); | ||
223 | extern int xfs_qm_freelist_lock_nowait(xfs_qm_t *); | ||
224 | extern int xfs_qm_mplist_nowait(xfs_mount_t *); | ||
225 | extern int xfs_qm_dqhashlock_nowait(xfs_dquot_t *); | ||
226 | |||
227 | /* system call interface */ | ||
228 | extern int xfs_qm_quotactl(bhv_desc_t *, int, int, xfs_caddr_t); | ||
229 | |||
230 | #ifdef DEBUG | ||
231 | extern int xfs_qm_internalqcheck(xfs_mount_t *); | ||
232 | #else | ||
233 | #define xfs_qm_internalqcheck(mp) (0) | ||
234 | #endif | ||
235 | |||
236 | #endif /* __XFS_QM_H__ */ | ||
diff --git a/fs/xfs/quota/xfs_qm_bhv.c b/fs/xfs/quota/xfs_qm_bhv.c new file mode 100644 index 000000000000..be67d9c265f8 --- /dev/null +++ b/fs/xfs/quota/xfs_qm_bhv.c | |||
@@ -0,0 +1,410 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2000-2004 Silicon Graphics, Inc. All Rights Reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms of version 2 of the GNU General Public License as | ||
6 | * published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope that it would be useful, but | ||
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | * | ||
12 | * Further, this software is distributed without any warranty that it is | ||
13 | * free of the rightful claim of any third person regarding infringement | ||
14 | * or the like. Any license provided herein, whether implied or | ||
15 | * otherwise, applies only to this software file. Patent licenses, if | ||
16 | * any, provided herein do not apply to combinations of this program with | ||
17 | * other software, or any other product whatsoever. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License along | ||
20 | * with this program; if not, write the Free Software Foundation, Inc., 59 | ||
21 | * Temple Place - Suite 330, Boston MA 02111-1307, USA. | ||
22 | * | ||
23 | * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, | ||
24 | * Mountain View, CA 94043, or: | ||
25 | * | ||
26 | * http://www.sgi.com | ||
27 | * | ||
28 | * For further information regarding this notice, see: | ||
29 | * | ||
30 | * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ | ||
31 | */ | ||
32 | |||
33 | #include "xfs.h" | ||
34 | #include "xfs_fs.h" | ||
35 | #include "xfs_inum.h" | ||
36 | #include "xfs_log.h" | ||
37 | #include "xfs_clnt.h" | ||
38 | #include "xfs_trans.h" | ||
39 | #include "xfs_sb.h" | ||
40 | #include "xfs_dir.h" | ||
41 | #include "xfs_dir2.h" | ||
42 | #include "xfs_alloc.h" | ||
43 | #include "xfs_dmapi.h" | ||
44 | #include "xfs_quota.h" | ||
45 | #include "xfs_mount.h" | ||
46 | #include "xfs_alloc_btree.h" | ||
47 | #include "xfs_bmap_btree.h" | ||
48 | #include "xfs_ialloc_btree.h" | ||
49 | #include "xfs_btree.h" | ||
50 | #include "xfs_ialloc.h" | ||
51 | #include "xfs_attr_sf.h" | ||
52 | #include "xfs_dir_sf.h" | ||
53 | #include "xfs_dir2_sf.h" | ||
54 | #include "xfs_dinode.h" | ||
55 | #include "xfs_inode.h" | ||
56 | #include "xfs_bmap.h" | ||
57 | #include "xfs_bit.h" | ||
58 | #include "xfs_rtalloc.h" | ||
59 | #include "xfs_error.h" | ||
60 | #include "xfs_itable.h" | ||
61 | #include "xfs_rw.h" | ||
62 | #include "xfs_acl.h" | ||
63 | #include "xfs_cap.h" | ||
64 | #include "xfs_mac.h" | ||
65 | #include "xfs_attr.h" | ||
66 | #include "xfs_buf_item.h" | ||
67 | |||
68 | #include "xfs_qm.h" | ||
69 | |||
70 | #define MNTOPT_QUOTA "quota" /* disk quotas (user) */ | ||
71 | #define MNTOPT_NOQUOTA "noquota" /* no quotas */ | ||
72 | #define MNTOPT_USRQUOTA "usrquota" /* user quota enabled */ | ||
73 | #define MNTOPT_GRPQUOTA "grpquota" /* group quota enabled */ | ||
74 | #define MNTOPT_UQUOTA "uquota" /* user quota (IRIX variant) */ | ||
75 | #define MNTOPT_GQUOTA "gquota" /* group quota (IRIX variant) */ | ||
76 | #define MNTOPT_UQUOTANOENF "uqnoenforce"/* user quota limit enforcement */ | ||
77 | #define MNTOPT_GQUOTANOENF "gqnoenforce"/* group quota limit enforcement */ | ||
78 | #define MNTOPT_QUOTANOENF "qnoenforce" /* same as uqnoenforce */ | ||
79 | |||
80 | STATIC int | ||
81 | xfs_qm_parseargs( | ||
82 | struct bhv_desc *bhv, | ||
83 | char *options, | ||
84 | struct xfs_mount_args *args, | ||
85 | int update) | ||
86 | { | ||
87 | size_t length; | ||
88 | char *local_options = options; | ||
89 | char *this_char; | ||
90 | int error; | ||
91 | int referenced = update; | ||
92 | |||
93 | while ((this_char = strsep(&local_options, ",")) != NULL) { | ||
94 | length = strlen(this_char); | ||
95 | if (local_options) | ||
96 | length++; | ||
97 | |||
98 | if (!strcmp(this_char, MNTOPT_NOQUOTA)) { | ||
99 | args->flags &= ~(XFSMNT_UQUOTAENF|XFSMNT_UQUOTA); | ||
100 | args->flags &= ~(XFSMNT_GQUOTAENF|XFSMNT_GQUOTA); | ||
101 | referenced = update; | ||
102 | } else if (!strcmp(this_char, MNTOPT_QUOTA) || | ||
103 | !strcmp(this_char, MNTOPT_UQUOTA) || | ||
104 | !strcmp(this_char, MNTOPT_USRQUOTA)) { | ||
105 | args->flags |= XFSMNT_UQUOTA | XFSMNT_UQUOTAENF; | ||
106 | referenced = 1; | ||
107 | } else if (!strcmp(this_char, MNTOPT_QUOTANOENF) || | ||
108 | !strcmp(this_char, MNTOPT_UQUOTANOENF)) { | ||
109 | args->flags |= XFSMNT_UQUOTA; | ||
110 | args->flags &= ~XFSMNT_UQUOTAENF; | ||
111 | referenced = 1; | ||
112 | } else if (!strcmp(this_char, MNTOPT_GQUOTA) || | ||
113 | !strcmp(this_char, MNTOPT_GRPQUOTA)) { | ||
114 | args->flags |= XFSMNT_GQUOTA | XFSMNT_GQUOTAENF; | ||
115 | referenced = 1; | ||
116 | } else if (!strcmp(this_char, MNTOPT_GQUOTANOENF)) { | ||
117 | args->flags |= XFSMNT_GQUOTA; | ||
118 | args->flags &= ~XFSMNT_GQUOTAENF; | ||
119 | referenced = 1; | ||
120 | } else { | ||
121 | if (local_options) | ||
122 | *(local_options-1) = ','; | ||
123 | continue; | ||
124 | } | ||
125 | |||
126 | while (length--) | ||
127 | *this_char++ = ','; | ||
128 | } | ||
129 | |||
130 | PVFS_PARSEARGS(BHV_NEXT(bhv), options, args, update, error); | ||
131 | if (!error && !referenced) | ||
132 | bhv_remove_vfsops(bhvtovfs(bhv), VFS_POSITION_QM); | ||
133 | return error; | ||
134 | } | ||
135 | |||
136 | STATIC int | ||
137 | xfs_qm_showargs( | ||
138 | struct bhv_desc *bhv, | ||
139 | struct seq_file *m) | ||
140 | { | ||
141 | struct vfs *vfsp = bhvtovfs(bhv); | ||
142 | struct xfs_mount *mp = XFS_VFSTOM(vfsp); | ||
143 | int error; | ||
144 | |||
145 | if (mp->m_qflags & XFS_UQUOTA_ACCT) { | ||
146 | (mp->m_qflags & XFS_UQUOTA_ENFD) ? | ||
147 | seq_puts(m, "," MNTOPT_USRQUOTA) : | ||
148 | seq_puts(m, "," MNTOPT_UQUOTANOENF); | ||
149 | } | ||
150 | |||
151 | if (mp->m_qflags & XFS_GQUOTA_ACCT) { | ||
152 | (mp->m_qflags & XFS_GQUOTA_ENFD) ? | ||
153 | seq_puts(m, "," MNTOPT_GRPQUOTA) : | ||
154 | seq_puts(m, "," MNTOPT_GQUOTANOENF); | ||
155 | } | ||
156 | |||
157 | if (!(mp->m_qflags & (XFS_UQUOTA_ACCT|XFS_GQUOTA_ACCT))) | ||
158 | seq_puts(m, "," MNTOPT_NOQUOTA); | ||
159 | |||
160 | PVFS_SHOWARGS(BHV_NEXT(bhv), m, error); | ||
161 | return error; | ||
162 | } | ||
163 | |||
164 | STATIC int | ||
165 | xfs_qm_mount( | ||
166 | struct bhv_desc *bhv, | ||
167 | struct xfs_mount_args *args, | ||
168 | struct cred *cr) | ||
169 | { | ||
170 | struct vfs *vfsp = bhvtovfs(bhv); | ||
171 | struct xfs_mount *mp = XFS_VFSTOM(vfsp); | ||
172 | int error; | ||
173 | |||
174 | if (args->flags & (XFSMNT_UQUOTA | XFSMNT_GQUOTA)) | ||
175 | xfs_qm_mount_quotainit(mp, args->flags); | ||
176 | PVFS_MOUNT(BHV_NEXT(bhv), args, cr, error); | ||
177 | return error; | ||
178 | } | ||
179 | |||
180 | STATIC int | ||
181 | xfs_qm_syncall( | ||
182 | struct bhv_desc *bhv, | ||
183 | int flags, | ||
184 | cred_t *credp) | ||
185 | { | ||
186 | struct vfs *vfsp = bhvtovfs(bhv); | ||
187 | struct xfs_mount *mp = XFS_VFSTOM(vfsp); | ||
188 | int error; | ||
189 | |||
190 | /* | ||
191 | * Get the Quota Manager to flush the dquots. | ||
192 | */ | ||
193 | if (XFS_IS_QUOTA_ON(mp)) { | ||
194 | if ((error = xfs_qm_sync(mp, flags))) { | ||
195 | /* | ||
196 | * If we got an IO error, we will be shutting down. | ||
197 | * So, there's nothing more for us to do here. | ||
198 | */ | ||
199 | ASSERT(error != EIO || XFS_FORCED_SHUTDOWN(mp)); | ||
200 | if (XFS_FORCED_SHUTDOWN(mp)) { | ||
201 | return XFS_ERROR(error); | ||
202 | } | ||
203 | } | ||
204 | } | ||
205 | PVFS_SYNC(BHV_NEXT(bhv), flags, credp, error); | ||
206 | return error; | ||
207 | } | ||
208 | |||
209 | /* | ||
210 | * Clear the quotaflags in memory and in the superblock. | ||
211 | */ | ||
212 | void | ||
213 | xfs_mount_reset_sbqflags( | ||
214 | xfs_mount_t *mp) | ||
215 | { | ||
216 | xfs_trans_t *tp; | ||
217 | unsigned long s; | ||
218 | |||
219 | mp->m_qflags = 0; | ||
220 | /* | ||
221 | * It is OK to look at sb_qflags here in mount path, | ||
222 | * without SB_LOCK. | ||
223 | */ | ||
224 | if (mp->m_sb.sb_qflags == 0) | ||
225 | return; | ||
226 | s = XFS_SB_LOCK(mp); | ||
227 | mp->m_sb.sb_qflags = 0; | ||
228 | XFS_SB_UNLOCK(mp, s); | ||
229 | |||
230 | /* | ||
231 | * if the fs is readonly, let the incore superblock run | ||
232 | * with quotas off but don't flush the update out to disk | ||
233 | */ | ||
234 | if (XFS_MTOVFS(mp)->vfs_flag & VFS_RDONLY) | ||
235 | return; | ||
236 | #ifdef QUOTADEBUG | ||
237 | xfs_fs_cmn_err(CE_NOTE, mp, "Writing superblock quota changes"); | ||
238 | #endif | ||
239 | tp = xfs_trans_alloc(mp, XFS_TRANS_QM_SBCHANGE); | ||
240 | if (xfs_trans_reserve(tp, 0, mp->m_sb.sb_sectsize + 128, 0, 0, | ||
241 | XFS_DEFAULT_LOG_COUNT)) { | ||
242 | xfs_trans_cancel(tp, 0); | ||
243 | xfs_fs_cmn_err(CE_ALERT, mp, | ||
244 | "xfs_mount_reset_sbqflags: Superblock update failed!"); | ||
245 | return; | ||
246 | } | ||
247 | xfs_mod_sb(tp, XFS_SB_QFLAGS); | ||
248 | xfs_trans_commit(tp, 0, NULL); | ||
249 | } | ||
250 | |||
251 | STATIC int | ||
252 | xfs_qm_newmount( | ||
253 | xfs_mount_t *mp, | ||
254 | uint *needquotamount, | ||
255 | uint *quotaflags) | ||
256 | { | ||
257 | uint quotaondisk; | ||
258 | uint uquotaondisk = 0, gquotaondisk = 0; | ||
259 | |||
260 | *quotaflags = 0; | ||
261 | *needquotamount = B_FALSE; | ||
262 | |||
263 | quotaondisk = XFS_SB_VERSION_HASQUOTA(&mp->m_sb) && | ||
264 | mp->m_sb.sb_qflags & (XFS_UQUOTA_ACCT|XFS_GQUOTA_ACCT); | ||
265 | |||
266 | if (quotaondisk) { | ||
267 | uquotaondisk = mp->m_sb.sb_qflags & XFS_UQUOTA_ACCT; | ||
268 | gquotaondisk = mp->m_sb.sb_qflags & XFS_GQUOTA_ACCT; | ||
269 | } | ||
270 | |||
271 | /* | ||
272 | * If the device itself is read-only, we can't allow | ||
273 | * the user to change the state of quota on the mount - | ||
274 | * this would generate a transaction on the ro device, | ||
275 | * which would lead to an I/O error and shutdown | ||
276 | */ | ||
277 | |||
278 | if (((uquotaondisk && !XFS_IS_UQUOTA_ON(mp)) || | ||
279 | (!uquotaondisk && XFS_IS_UQUOTA_ON(mp)) || | ||
280 | (gquotaondisk && !XFS_IS_GQUOTA_ON(mp)) || | ||
281 | (!gquotaondisk && XFS_IS_GQUOTA_ON(mp))) && | ||
282 | xfs_dev_is_read_only(mp, "changing quota state")) { | ||
283 | cmn_err(CE_WARN, | ||
284 | "XFS: please mount with%s%s%s.", | ||
285 | (!quotaondisk ? "out quota" : ""), | ||
286 | (uquotaondisk ? " usrquota" : ""), | ||
287 | (gquotaondisk ? " grpquota" : "")); | ||
288 | return XFS_ERROR(EPERM); | ||
289 | } | ||
290 | |||
291 | if (XFS_IS_QUOTA_ON(mp) || quotaondisk) { | ||
292 | /* | ||
293 | * Call mount_quotas at this point only if we won't have to do | ||
294 | * a quotacheck. | ||
295 | */ | ||
296 | if (quotaondisk && !XFS_QM_NEED_QUOTACHECK(mp)) { | ||
297 | /* | ||
298 | * If an error occured, qm_mount_quotas code | ||
299 | * has already disabled quotas. So, just finish | ||
300 | * mounting, and get on with the boring life | ||
301 | * without disk quotas. | ||
302 | */ | ||
303 | xfs_qm_mount_quotas(mp, 0); | ||
304 | } else { | ||
305 | /* | ||
306 | * Clear the quota flags, but remember them. This | ||
307 | * is so that the quota code doesn't get invoked | ||
308 | * before we're ready. This can happen when an | ||
309 | * inode goes inactive and wants to free blocks, | ||
310 | * or via xfs_log_mount_finish. | ||
311 | */ | ||
312 | *needquotamount = B_TRUE; | ||
313 | *quotaflags = mp->m_qflags; | ||
314 | mp->m_qflags = 0; | ||
315 | } | ||
316 | } | ||
317 | |||
318 | return 0; | ||
319 | } | ||
320 | |||
321 | STATIC int | ||
322 | xfs_qm_endmount( | ||
323 | xfs_mount_t *mp, | ||
324 | uint needquotamount, | ||
325 | uint quotaflags, | ||
326 | int mfsi_flags) | ||
327 | { | ||
328 | if (needquotamount) { | ||
329 | ASSERT(mp->m_qflags == 0); | ||
330 | mp->m_qflags = quotaflags; | ||
331 | xfs_qm_mount_quotas(mp, mfsi_flags); | ||
332 | } | ||
333 | |||
334 | #if defined(DEBUG) && defined(XFS_LOUD_RECOVERY) | ||
335 | if (! (XFS_IS_QUOTA_ON(mp))) | ||
336 | xfs_fs_cmn_err(CE_NOTE, mp, "Disk quotas not turned on"); | ||
337 | else | ||
338 | xfs_fs_cmn_err(CE_NOTE, mp, "Disk quotas turned on"); | ||
339 | #endif | ||
340 | |||
341 | #ifdef QUOTADEBUG | ||
342 | if (XFS_IS_QUOTA_ON(mp) && xfs_qm_internalqcheck(mp)) | ||
343 | cmn_err(CE_WARN, "XFS: mount internalqcheck failed"); | ||
344 | #endif | ||
345 | |||
346 | return 0; | ||
347 | } | ||
348 | |||
349 | STATIC void | ||
350 | xfs_qm_dqrele_null( | ||
351 | xfs_dquot_t *dq) | ||
352 | { | ||
353 | /* | ||
354 | * Called from XFS, where we always check first for a NULL dquot. | ||
355 | */ | ||
356 | if (!dq) | ||
357 | return; | ||
358 | xfs_qm_dqrele(dq); | ||
359 | } | ||
360 | |||
361 | |||
362 | struct xfs_qmops xfs_qmcore_xfs = { | ||
363 | .xfs_qminit = xfs_qm_newmount, | ||
364 | .xfs_qmdone = xfs_qm_unmount_quotadestroy, | ||
365 | .xfs_qmmount = xfs_qm_endmount, | ||
366 | .xfs_qmunmount = xfs_qm_unmount_quotas, | ||
367 | .xfs_dqrele = xfs_qm_dqrele_null, | ||
368 | .xfs_dqattach = xfs_qm_dqattach, | ||
369 | .xfs_dqdetach = xfs_qm_dqdetach, | ||
370 | .xfs_dqpurgeall = xfs_qm_dqpurge_all, | ||
371 | .xfs_dqvopalloc = xfs_qm_vop_dqalloc, | ||
372 | .xfs_dqvopcreate = xfs_qm_vop_dqattach_and_dqmod_newinode, | ||
373 | .xfs_dqvoprename = xfs_qm_vop_rename_dqattach, | ||
374 | .xfs_dqvopchown = xfs_qm_vop_chown, | ||
375 | .xfs_dqvopchownresv = xfs_qm_vop_chown_reserve, | ||
376 | .xfs_dqtrxops = &xfs_trans_dquot_ops, | ||
377 | }; | ||
378 | |||
379 | struct bhv_vfsops xfs_qmops = { { | ||
380 | BHV_IDENTITY_INIT(VFS_BHV_QM, VFS_POSITION_QM), | ||
381 | .vfs_parseargs = xfs_qm_parseargs, | ||
382 | .vfs_showargs = xfs_qm_showargs, | ||
383 | .vfs_mount = xfs_qm_mount, | ||
384 | .vfs_sync = xfs_qm_syncall, | ||
385 | .vfs_quotactl = xfs_qm_quotactl, }, | ||
386 | }; | ||
387 | |||
388 | |||
389 | void __init | ||
390 | xfs_qm_init(void) | ||
391 | { | ||
392 | static char message[] __initdata = | ||
393 | KERN_INFO "SGI XFS Quota Management subsystem\n"; | ||
394 | |||
395 | printk(message); | ||
396 | mutex_init(&xfs_Gqm_lock, MUTEX_DEFAULT, "xfs_qmlock"); | ||
397 | vfs_bhv_set_custom(&xfs_qmops, &xfs_qmcore_xfs); | ||
398 | xfs_qm_init_procfs(); | ||
399 | } | ||
400 | |||
401 | void __exit | ||
402 | xfs_qm_exit(void) | ||
403 | { | ||
404 | vfs_bhv_clr_custom(&xfs_qmops); | ||
405 | xfs_qm_cleanup_procfs(); | ||
406 | if (qm_dqzone) | ||
407 | kmem_cache_destroy(qm_dqzone); | ||
408 | if (qm_dqtrxzone) | ||
409 | kmem_cache_destroy(qm_dqtrxzone); | ||
410 | } | ||
diff --git a/fs/xfs/quota/xfs_qm_stats.c b/fs/xfs/quota/xfs_qm_stats.c new file mode 100644 index 000000000000..29978e037fee --- /dev/null +++ b/fs/xfs/quota/xfs_qm_stats.c | |||
@@ -0,0 +1,149 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms of version 2 of the GNU General Public License as | ||
6 | * published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope that it would be useful, but | ||
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | * | ||
12 | * Further, this software is distributed without any warranty that it is | ||
13 | * free of the rightful claim of any third person regarding infringement | ||
14 | * or the like. Any license provided herein, whether implied or | ||
15 | * otherwise, applies only to this software file. Patent licenses, if | ||
16 | * any, provided herein do not apply to combinations of this program with | ||
17 | * other software, or any other product whatsoever. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License along | ||
20 | * with this program; if not, write the Free Software Foundation, Inc., 59 | ||
21 | * Temple Place - Suite 330, Boston MA 02111-1307, USA. | ||
22 | * | ||
23 | * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, | ||
24 | * Mountain View, CA 94043, or: | ||
25 | * | ||
26 | * http://www.sgi.com | ||
27 | * | ||
28 | * For further information regarding this notice, see: | ||
29 | * | ||
30 | * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ | ||
31 | */ | ||
32 | |||
33 | #include "xfs.h" | ||
34 | #include "xfs_fs.h" | ||
35 | #include "xfs_inum.h" | ||
36 | #include "xfs_log.h" | ||
37 | #include "xfs_trans.h" | ||
38 | #include "xfs_sb.h" | ||
39 | #include "xfs_dir.h" | ||
40 | #include "xfs_dir2.h" | ||
41 | #include "xfs_alloc.h" | ||
42 | #include "xfs_dmapi.h" | ||
43 | #include "xfs_quota.h" | ||
44 | #include "xfs_mount.h" | ||
45 | #include "xfs_alloc_btree.h" | ||
46 | #include "xfs_bmap_btree.h" | ||
47 | #include "xfs_ialloc_btree.h" | ||
48 | #include "xfs_btree.h" | ||
49 | #include "xfs_ialloc.h" | ||
50 | #include "xfs_attr_sf.h" | ||
51 | #include "xfs_dir_sf.h" | ||
52 | #include "xfs_dir2_sf.h" | ||
53 | #include "xfs_dinode.h" | ||
54 | #include "xfs_inode.h" | ||
55 | #include "xfs_bmap.h" | ||
56 | #include "xfs_bit.h" | ||
57 | #include "xfs_rtalloc.h" | ||
58 | #include "xfs_error.h" | ||
59 | #include "xfs_itable.h" | ||
60 | #include "xfs_rw.h" | ||
61 | #include "xfs_acl.h" | ||
62 | #include "xfs_cap.h" | ||
63 | #include "xfs_mac.h" | ||
64 | #include "xfs_attr.h" | ||
65 | #include "xfs_buf_item.h" | ||
66 | |||
67 | #include "xfs_qm.h" | ||
68 | |||
69 | struct xqmstats xqmstats; | ||
70 | |||
71 | STATIC int | ||
72 | xfs_qm_read_xfsquota( | ||
73 | char *buffer, | ||
74 | char **start, | ||
75 | off_t offset, | ||
76 | int count, | ||
77 | int *eof, | ||
78 | void *data) | ||
79 | { | ||
80 | int len; | ||
81 | |||
82 | /* maximum; incore; ratio free to inuse; freelist */ | ||
83 | len = sprintf(buffer, "%d\t%d\t%d\t%u\n", | ||
84 | ndquot, | ||
85 | xfs_Gqm? atomic_read(&xfs_Gqm->qm_totaldquots) : 0, | ||
86 | xfs_Gqm? xfs_Gqm->qm_dqfree_ratio : 0, | ||
87 | xfs_Gqm? xfs_Gqm->qm_dqfreelist.qh_nelems : 0); | ||
88 | |||
89 | if (offset >= len) { | ||
90 | *start = buffer; | ||
91 | *eof = 1; | ||
92 | return 0; | ||
93 | } | ||
94 | *start = buffer + offset; | ||
95 | if ((len -= offset) > count) | ||
96 | return count; | ||
97 | *eof = 1; | ||
98 | |||
99 | return len; | ||
100 | } | ||
101 | |||
102 | STATIC int | ||
103 | xfs_qm_read_stats( | ||
104 | char *buffer, | ||
105 | char **start, | ||
106 | off_t offset, | ||
107 | int count, | ||
108 | int *eof, | ||
109 | void *data) | ||
110 | { | ||
111 | int len; | ||
112 | |||
113 | /* quota performance statistics */ | ||
114 | len = sprintf(buffer, "qm %u %u %u %u %u %u %u %u\n", | ||
115 | xqmstats.xs_qm_dqreclaims, | ||
116 | xqmstats.xs_qm_dqreclaim_misses, | ||
117 | xqmstats.xs_qm_dquot_dups, | ||
118 | xqmstats.xs_qm_dqcachemisses, | ||
119 | xqmstats.xs_qm_dqcachehits, | ||
120 | xqmstats.xs_qm_dqwants, | ||
121 | xqmstats.xs_qm_dqshake_reclaims, | ||
122 | xqmstats.xs_qm_dqinact_reclaims); | ||
123 | |||
124 | if (offset >= len) { | ||
125 | *start = buffer; | ||
126 | *eof = 1; | ||
127 | return 0; | ||
128 | } | ||
129 | *start = buffer + offset; | ||
130 | if ((len -= offset) > count) | ||
131 | return count; | ||
132 | *eof = 1; | ||
133 | |||
134 | return len; | ||
135 | } | ||
136 | |||
137 | void | ||
138 | xfs_qm_init_procfs(void) | ||
139 | { | ||
140 | create_proc_read_entry("fs/xfs/xqmstat", 0, NULL, xfs_qm_read_stats, NULL); | ||
141 | create_proc_read_entry("fs/xfs/xqm", 0, NULL, xfs_qm_read_xfsquota, NULL); | ||
142 | } | ||
143 | |||
144 | void | ||
145 | xfs_qm_cleanup_procfs(void) | ||
146 | { | ||
147 | remove_proc_entry("fs/xfs/xqm", NULL); | ||
148 | remove_proc_entry("fs/xfs/xqmstat", NULL); | ||
149 | } | ||
diff --git a/fs/xfs/quota/xfs_qm_stats.h b/fs/xfs/quota/xfs_qm_stats.h new file mode 100644 index 000000000000..8093c5c284ec --- /dev/null +++ b/fs/xfs/quota/xfs_qm_stats.h | |||
@@ -0,0 +1,68 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2002 Silicon Graphics, Inc. All Rights Reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms of version 2 of the GNU General Public License as | ||
6 | * published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope that it would be useful, but | ||
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | * | ||
12 | * Further, this software is distributed without any warranty that it is | ||
13 | * free of the rightful claim of any third person regarding infringement | ||
14 | * or the like. Any license provided herein, whether implied or | ||
15 | * otherwise, applies only to this software file. Patent licenses, if | ||
16 | * any, provided herein do not apply to combinations of this program with | ||
17 | * other software, or any other product whatsoever. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License along | ||
20 | * with this program; if not, write the Free Software Foundation, Inc., 59 | ||
21 | * Temple Place - Suite 330, Boston MA 02111-1307, USA. | ||
22 | * | ||
23 | * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, | ||
24 | * Mountain View, CA 94043, or: | ||
25 | * | ||
26 | * http://www.sgi.com | ||
27 | * | ||
28 | * For further information regarding this notice, see: | ||
29 | * | ||
30 | * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ | ||
31 | */ | ||
32 | #ifndef __XFS_QM_STATS_H__ | ||
33 | #define __XFS_QM_STATS_H__ | ||
34 | |||
35 | |||
36 | #if defined(CONFIG_PROC_FS) && !defined(XFS_STATS_OFF) | ||
37 | |||
38 | /* | ||
39 | * XQM global statistics | ||
40 | */ | ||
41 | struct xqmstats { | ||
42 | __uint32_t xs_qm_dqreclaims; | ||
43 | __uint32_t xs_qm_dqreclaim_misses; | ||
44 | __uint32_t xs_qm_dquot_dups; | ||
45 | __uint32_t xs_qm_dqcachemisses; | ||
46 | __uint32_t xs_qm_dqcachehits; | ||
47 | __uint32_t xs_qm_dqwants; | ||
48 | __uint32_t xs_qm_dqshake_reclaims; | ||
49 | __uint32_t xs_qm_dqinact_reclaims; | ||
50 | }; | ||
51 | |||
52 | extern struct xqmstats xqmstats; | ||
53 | |||
54 | # define XQM_STATS_INC(count) ( (count)++ ) | ||
55 | |||
56 | extern void xfs_qm_init_procfs(void); | ||
57 | extern void xfs_qm_cleanup_procfs(void); | ||
58 | |||
59 | #else | ||
60 | |||
61 | # define XQM_STATS_INC(count) do { } while (0) | ||
62 | |||
63 | static __inline void xfs_qm_init_procfs(void) { }; | ||
64 | static __inline void xfs_qm_cleanup_procfs(void) { }; | ||
65 | |||
66 | #endif | ||
67 | |||
68 | #endif /* __XFS_QM_STATS_H__ */ | ||
diff --git a/fs/xfs/quota/xfs_qm_syscalls.c b/fs/xfs/quota/xfs_qm_syscalls.c new file mode 100644 index 000000000000..229f5b5a2d25 --- /dev/null +++ b/fs/xfs/quota/xfs_qm_syscalls.c | |||
@@ -0,0 +1,1458 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms of version 2 of the GNU General Public License as | ||
6 | * published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope that it would be useful, but | ||
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | * | ||
12 | * Further, this software is distributed without any warranty that it is | ||
13 | * free of the rightful claim of any third person regarding infringement | ||
14 | * or the like. Any license provided herein, whether implied or | ||
15 | * otherwise, applies only to this software file. Patent licenses, if | ||
16 | * any, provided herein do not apply to combinations of this program with | ||
17 | * other software, or any other product whatsoever. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License along | ||
20 | * with this program; if not, write the Free Software Foundation, Inc., 59 | ||
21 | * Temple Place - Suite 330, Boston MA 02111-1307, USA. | ||
22 | * | ||
23 | * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, | ||
24 | * Mountain View, CA 94043, or: | ||
25 | * | ||
26 | * http://www.sgi.com | ||
27 | * | ||
28 | * For further information regarding this notice, see: | ||
29 | * | ||
30 | * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ | ||
31 | */ | ||
32 | |||
33 | #include "xfs.h" | ||
34 | #include "xfs_fs.h" | ||
35 | #include "xfs_inum.h" | ||
36 | #include "xfs_log.h" | ||
37 | #include "xfs_trans.h" | ||
38 | #include "xfs_sb.h" | ||
39 | #include "xfs_dir.h" | ||
40 | #include "xfs_dir2.h" | ||
41 | #include "xfs_alloc.h" | ||
42 | #include "xfs_dmapi.h" | ||
43 | #include "xfs_quota.h" | ||
44 | #include "xfs_mount.h" | ||
45 | #include "xfs_alloc_btree.h" | ||
46 | #include "xfs_bmap_btree.h" | ||
47 | #include "xfs_ialloc_btree.h" | ||
48 | #include "xfs_btree.h" | ||
49 | #include "xfs_ialloc.h" | ||
50 | #include "xfs_attr_sf.h" | ||
51 | #include "xfs_dir_sf.h" | ||
52 | #include "xfs_dir2_sf.h" | ||
53 | #include "xfs_dinode.h" | ||
54 | #include "xfs_inode.h" | ||
55 | #include "xfs_bmap.h" | ||
56 | #include "xfs_bit.h" | ||
57 | #include "xfs_rtalloc.h" | ||
58 | #include "xfs_error.h" | ||
59 | #include "xfs_itable.h" | ||
60 | #include "xfs_rw.h" | ||
61 | #include "xfs_acl.h" | ||
62 | #include "xfs_cap.h" | ||
63 | #include "xfs_mac.h" | ||
64 | #include "xfs_attr.h" | ||
65 | #include "xfs_buf_item.h" | ||
66 | #include "xfs_utils.h" | ||
67 | |||
68 | #include "xfs_qm.h" | ||
69 | |||
70 | #ifdef DEBUG | ||
71 | # define qdprintk(s, args...) cmn_err(CE_DEBUG, s, ## args) | ||
72 | #else | ||
73 | # define qdprintk(s, args...) do { } while (0) | ||
74 | #endif | ||
75 | |||
76 | STATIC int xfs_qm_scall_trunc_qfiles(xfs_mount_t *, uint); | ||
77 | STATIC int xfs_qm_scall_getquota(xfs_mount_t *, xfs_dqid_t, uint, | ||
78 | fs_disk_quota_t *); | ||
79 | STATIC int xfs_qm_scall_getqstat(xfs_mount_t *, fs_quota_stat_t *); | ||
80 | STATIC int xfs_qm_scall_setqlim(xfs_mount_t *, xfs_dqid_t, uint, | ||
81 | fs_disk_quota_t *); | ||
82 | STATIC int xfs_qm_scall_quotaon(xfs_mount_t *, uint); | ||
83 | STATIC int xfs_qm_scall_quotaoff(xfs_mount_t *, uint, boolean_t); | ||
84 | STATIC int xfs_qm_log_quotaoff(xfs_mount_t *, xfs_qoff_logitem_t **, uint); | ||
85 | STATIC int xfs_qm_log_quotaoff_end(xfs_mount_t *, xfs_qoff_logitem_t *, | ||
86 | uint); | ||
87 | STATIC uint xfs_qm_import_flags(uint); | ||
88 | STATIC uint xfs_qm_export_flags(uint); | ||
89 | STATIC uint xfs_qm_import_qtype_flags(uint); | ||
90 | STATIC uint xfs_qm_export_qtype_flags(uint); | ||
91 | STATIC void xfs_qm_export_dquot(xfs_mount_t *, xfs_disk_dquot_t *, | ||
92 | fs_disk_quota_t *); | ||
93 | |||
94 | |||
95 | /* | ||
96 | * The main distribution switch of all XFS quotactl system calls. | ||
97 | */ | ||
98 | int | ||
99 | xfs_qm_quotactl( | ||
100 | struct bhv_desc *bdp, | ||
101 | int cmd, | ||
102 | int id, | ||
103 | xfs_caddr_t addr) | ||
104 | { | ||
105 | xfs_mount_t *mp; | ||
106 | int error; | ||
107 | struct vfs *vfsp; | ||
108 | |||
109 | vfsp = bhvtovfs(bdp); | ||
110 | mp = XFS_VFSTOM(vfsp); | ||
111 | |||
112 | if (addr == NULL && cmd != Q_SYNC) | ||
113 | return XFS_ERROR(EINVAL); | ||
114 | if (id < 0 && cmd != Q_SYNC) | ||
115 | return XFS_ERROR(EINVAL); | ||
116 | |||
117 | /* | ||
118 | * The following commands are valid even when quotaoff. | ||
119 | */ | ||
120 | switch (cmd) { | ||
121 | /* | ||
122 | * truncate quota files. quota must be off. | ||
123 | */ | ||
124 | case Q_XQUOTARM: | ||
125 | if (XFS_IS_QUOTA_ON(mp) || addr == NULL) | ||
126 | return XFS_ERROR(EINVAL); | ||
127 | if (vfsp->vfs_flag & VFS_RDONLY) | ||
128 | return XFS_ERROR(EROFS); | ||
129 | return (xfs_qm_scall_trunc_qfiles(mp, | ||
130 | xfs_qm_import_qtype_flags(*(uint *)addr))); | ||
131 | /* | ||
132 | * Get quota status information. | ||
133 | */ | ||
134 | case Q_XGETQSTAT: | ||
135 | return (xfs_qm_scall_getqstat(mp, (fs_quota_stat_t *)addr)); | ||
136 | |||
137 | /* | ||
138 | * QUOTAON for root f/s and quota enforcement on others.. | ||
139 | * Quota accounting for non-root f/s's must be turned on | ||
140 | * at mount time. | ||
141 | */ | ||
142 | case Q_XQUOTAON: | ||
143 | if (addr == NULL) | ||
144 | return XFS_ERROR(EINVAL); | ||
145 | if (vfsp->vfs_flag & VFS_RDONLY) | ||
146 | return XFS_ERROR(EROFS); | ||
147 | return (xfs_qm_scall_quotaon(mp, | ||
148 | xfs_qm_import_flags(*(uint *)addr))); | ||
149 | case Q_XQUOTAOFF: | ||
150 | if (vfsp->vfs_flag & VFS_RDONLY) | ||
151 | return XFS_ERROR(EROFS); | ||
152 | break; | ||
153 | |||
154 | default: | ||
155 | break; | ||
156 | } | ||
157 | |||
158 | if (! XFS_IS_QUOTA_ON(mp)) | ||
159 | return XFS_ERROR(ESRCH); | ||
160 | |||
161 | switch (cmd) { | ||
162 | case Q_XQUOTAOFF: | ||
163 | if (vfsp->vfs_flag & VFS_RDONLY) | ||
164 | return XFS_ERROR(EROFS); | ||
165 | error = xfs_qm_scall_quotaoff(mp, | ||
166 | xfs_qm_import_flags(*(uint *)addr), | ||
167 | B_FALSE); | ||
168 | break; | ||
169 | |||
170 | /* | ||
171 | * Defaults to XFS_GETUQUOTA. | ||
172 | */ | ||
173 | case Q_XGETQUOTA: | ||
174 | error = xfs_qm_scall_getquota(mp, (xfs_dqid_t)id, XFS_DQ_USER, | ||
175 | (fs_disk_quota_t *)addr); | ||
176 | break; | ||
177 | /* | ||
178 | * Set limits, both hard and soft. Defaults to Q_SETUQLIM. | ||
179 | */ | ||
180 | case Q_XSETQLIM: | ||
181 | if (vfsp->vfs_flag & VFS_RDONLY) | ||
182 | return XFS_ERROR(EROFS); | ||
183 | error = xfs_qm_scall_setqlim(mp, (xfs_dqid_t)id, XFS_DQ_USER, | ||
184 | (fs_disk_quota_t *)addr); | ||
185 | break; | ||
186 | |||
187 | case Q_XSETGQLIM: | ||
188 | if (vfsp->vfs_flag & VFS_RDONLY) | ||
189 | return XFS_ERROR(EROFS); | ||
190 | error = xfs_qm_scall_setqlim(mp, (xfs_dqid_t)id, XFS_DQ_GROUP, | ||
191 | (fs_disk_quota_t *)addr); | ||
192 | break; | ||
193 | |||
194 | |||
195 | case Q_XGETGQUOTA: | ||
196 | error = xfs_qm_scall_getquota(mp, (xfs_dqid_t)id, XFS_DQ_GROUP, | ||
197 | (fs_disk_quota_t *)addr); | ||
198 | break; | ||
199 | |||
200 | /* | ||
201 | * Quotas are entirely undefined after quotaoff in XFS quotas. | ||
202 | * For instance, there's no way to set limits when quotaoff. | ||
203 | */ | ||
204 | |||
205 | default: | ||
206 | error = XFS_ERROR(EINVAL); | ||
207 | break; | ||
208 | } | ||
209 | |||
210 | return (error); | ||
211 | } | ||
212 | |||
213 | /* | ||
214 | * Turn off quota accounting and/or enforcement for all udquots and/or | ||
215 | * gdquots. Called only at unmount time. | ||
216 | * | ||
217 | * This assumes that there are no dquots of this file system cached | ||
218 | * incore, and modifies the ondisk dquot directly. Therefore, for example, | ||
219 | * it is an error to call this twice, without purging the cache. | ||
220 | */ | ||
221 | STATIC int | ||
222 | xfs_qm_scall_quotaoff( | ||
223 | xfs_mount_t *mp, | ||
224 | uint flags, | ||
225 | boolean_t force) | ||
226 | { | ||
227 | uint dqtype; | ||
228 | unsigned long s; | ||
229 | int error; | ||
230 | uint inactivate_flags; | ||
231 | xfs_qoff_logitem_t *qoffstart; | ||
232 | int nculprits; | ||
233 | |||
234 | if (!force && !capable(CAP_SYS_ADMIN)) | ||
235 | return XFS_ERROR(EPERM); | ||
236 | /* | ||
237 | * No file system can have quotas enabled on disk but not in core. | ||
238 | * Note that quota utilities (like quotaoff) _expect_ | ||
239 | * errno == EEXIST here. | ||
240 | */ | ||
241 | if ((mp->m_qflags & flags) == 0) | ||
242 | return XFS_ERROR(EEXIST); | ||
243 | error = 0; | ||
244 | |||
245 | flags &= (XFS_ALL_QUOTA_ACCT | XFS_ALL_QUOTA_ENFD); | ||
246 | |||
247 | /* | ||
248 | * We don't want to deal with two quotaoffs messing up each other, | ||
249 | * so we're going to serialize it. quotaoff isn't exactly a performance | ||
250 | * critical thing. | ||
251 | * If quotaoff, then we must be dealing with the root filesystem. | ||
252 | */ | ||
253 | ASSERT(mp->m_quotainfo); | ||
254 | if (mp->m_quotainfo) | ||
255 | mutex_lock(&(XFS_QI_QOFFLOCK(mp)), PINOD); | ||
256 | |||
257 | ASSERT(mp->m_quotainfo); | ||
258 | |||
259 | /* | ||
260 | * If we're just turning off quota enforcement, change mp and go. | ||
261 | */ | ||
262 | if ((flags & XFS_ALL_QUOTA_ACCT) == 0) { | ||
263 | mp->m_qflags &= ~(flags); | ||
264 | |||
265 | s = XFS_SB_LOCK(mp); | ||
266 | mp->m_sb.sb_qflags = mp->m_qflags; | ||
267 | XFS_SB_UNLOCK(mp, s); | ||
268 | mutex_unlock(&(XFS_QI_QOFFLOCK(mp))); | ||
269 | |||
270 | /* XXX what to do if error ? Revert back to old vals incore ? */ | ||
271 | error = xfs_qm_write_sb_changes(mp, XFS_SB_QFLAGS); | ||
272 | return (error); | ||
273 | } | ||
274 | |||
275 | dqtype = 0; | ||
276 | inactivate_flags = 0; | ||
277 | /* | ||
278 | * If accounting is off, we must turn enforcement off, clear the | ||
279 | * quota 'CHKD' certificate to make it known that we have to | ||
280 | * do a quotacheck the next time this quota is turned on. | ||
281 | */ | ||
282 | if (flags & XFS_UQUOTA_ACCT) { | ||
283 | dqtype |= XFS_QMOPT_UQUOTA; | ||
284 | flags |= (XFS_UQUOTA_CHKD | XFS_UQUOTA_ENFD); | ||
285 | inactivate_flags |= XFS_UQUOTA_ACTIVE; | ||
286 | } | ||
287 | if (flags & XFS_GQUOTA_ACCT) { | ||
288 | dqtype |= XFS_QMOPT_GQUOTA; | ||
289 | flags |= (XFS_GQUOTA_CHKD | XFS_GQUOTA_ENFD); | ||
290 | inactivate_flags |= XFS_GQUOTA_ACTIVE; | ||
291 | } | ||
292 | |||
293 | /* | ||
294 | * Nothing to do? Don't complain. This happens when we're just | ||
295 | * turning off quota enforcement. | ||
296 | */ | ||
297 | if ((mp->m_qflags & flags) == 0) { | ||
298 | mutex_unlock(&(XFS_QI_QOFFLOCK(mp))); | ||
299 | return (0); | ||
300 | } | ||
301 | |||
302 | /* | ||
303 | * Write the LI_QUOTAOFF log record, and do SB changes atomically, | ||
304 | * and synchronously. | ||
305 | */ | ||
306 | xfs_qm_log_quotaoff(mp, &qoffstart, flags); | ||
307 | |||
308 | /* | ||
309 | * Next we clear the XFS_MOUNT_*DQ_ACTIVE bit(s) in the mount struct | ||
310 | * to take care of the race between dqget and quotaoff. We don't take | ||
311 | * any special locks to reset these bits. All processes need to check | ||
312 | * these bits *after* taking inode lock(s) to see if the particular | ||
313 | * quota type is in the process of being turned off. If *ACTIVE, it is | ||
314 | * guaranteed that all dquot structures and all quotainode ptrs will all | ||
315 | * stay valid as long as that inode is kept locked. | ||
316 | * | ||
317 | * There is no turning back after this. | ||
318 | */ | ||
319 | mp->m_qflags &= ~inactivate_flags; | ||
320 | |||
321 | /* | ||
322 | * Give back all the dquot reference(s) held by inodes. | ||
323 | * Here we go thru every single incore inode in this file system, and | ||
324 | * do a dqrele on the i_udquot/i_gdquot that it may have. | ||
325 | * Essentially, as long as somebody has an inode locked, this guarantees | ||
326 | * that quotas will not be turned off. This is handy because in a | ||
327 | * transaction once we lock the inode(s) and check for quotaon, we can | ||
328 | * depend on the quota inodes (and other things) being valid as long as | ||
329 | * we keep the lock(s). | ||
330 | */ | ||
331 | xfs_qm_dqrele_all_inodes(mp, flags); | ||
332 | |||
333 | /* | ||
334 | * Next we make the changes in the quota flag in the mount struct. | ||
335 | * This isn't protected by a particular lock directly, because we | ||
336 | * don't want to take a mrlock everytime we depend on quotas being on. | ||
337 | */ | ||
338 | mp->m_qflags &= ~(flags); | ||
339 | |||
340 | /* | ||
341 | * Go through all the dquots of this file system and purge them, | ||
342 | * according to what was turned off. We may not be able to get rid | ||
343 | * of all dquots, because dquots can have temporary references that | ||
344 | * are not attached to inodes. eg. xfs_setattr, xfs_create. | ||
345 | * So, if we couldn't purge all the dquots from the filesystem, | ||
346 | * we can't get rid of the incore data structures. | ||
347 | */ | ||
348 | while ((nculprits = xfs_qm_dqpurge_all(mp, dqtype|XFS_QMOPT_QUOTAOFF))) | ||
349 | delay(10 * nculprits); | ||
350 | |||
351 | /* | ||
352 | * Transactions that had started before ACTIVE state bit was cleared | ||
353 | * could have logged many dquots, so they'd have higher LSNs than | ||
354 | * the first QUOTAOFF log record does. If we happen to crash when | ||
355 | * the tail of the log has gone past the QUOTAOFF record, but | ||
356 | * before the last dquot modification, those dquots __will__ | ||
357 | * recover, and that's not good. | ||
358 | * | ||
359 | * So, we have QUOTAOFF start and end logitems; the start | ||
360 | * logitem won't get overwritten until the end logitem appears... | ||
361 | */ | ||
362 | xfs_qm_log_quotaoff_end(mp, qoffstart, flags); | ||
363 | |||
364 | /* | ||
365 | * If quotas is completely disabled, close shop. | ||
366 | */ | ||
367 | if ((flags & XFS_MOUNT_QUOTA_ALL) == XFS_MOUNT_QUOTA_ALL) { | ||
368 | mutex_unlock(&(XFS_QI_QOFFLOCK(mp))); | ||
369 | xfs_qm_destroy_quotainfo(mp); | ||
370 | return (0); | ||
371 | } | ||
372 | |||
373 | /* | ||
374 | * Release our quotainode references, and vn_purge them, | ||
375 | * if we don't need them anymore. | ||
376 | */ | ||
377 | if ((dqtype & XFS_QMOPT_UQUOTA) && XFS_QI_UQIP(mp)) { | ||
378 | XFS_PURGE_INODE(XFS_QI_UQIP(mp)); | ||
379 | XFS_QI_UQIP(mp) = NULL; | ||
380 | } | ||
381 | if ((dqtype & XFS_QMOPT_GQUOTA) && XFS_QI_GQIP(mp)) { | ||
382 | XFS_PURGE_INODE(XFS_QI_GQIP(mp)); | ||
383 | XFS_QI_GQIP(mp) = NULL; | ||
384 | } | ||
385 | mutex_unlock(&(XFS_QI_QOFFLOCK(mp))); | ||
386 | |||
387 | return (error); | ||
388 | } | ||
389 | |||
390 | STATIC int | ||
391 | xfs_qm_scall_trunc_qfiles( | ||
392 | xfs_mount_t *mp, | ||
393 | uint flags) | ||
394 | { | ||
395 | int error; | ||
396 | xfs_inode_t *qip; | ||
397 | |||
398 | if (!capable(CAP_SYS_ADMIN)) | ||
399 | return XFS_ERROR(EPERM); | ||
400 | error = 0; | ||
401 | if (!XFS_SB_VERSION_HASQUOTA(&mp->m_sb) || flags == 0) { | ||
402 | qdprintk("qtrunc flags=%x m_qflags=%x\n", flags, mp->m_qflags); | ||
403 | return XFS_ERROR(EINVAL); | ||
404 | } | ||
405 | |||
406 | if ((flags & XFS_DQ_USER) && mp->m_sb.sb_uquotino != NULLFSINO) { | ||
407 | error = xfs_iget(mp, NULL, mp->m_sb.sb_uquotino, 0, 0, &qip, 0); | ||
408 | if (! error) { | ||
409 | (void) xfs_truncate_file(mp, qip); | ||
410 | VN_RELE(XFS_ITOV(qip)); | ||
411 | } | ||
412 | } | ||
413 | |||
414 | if ((flags & XFS_DQ_GROUP) && mp->m_sb.sb_gquotino != NULLFSINO) { | ||
415 | error = xfs_iget(mp, NULL, mp->m_sb.sb_gquotino, 0, 0, &qip, 0); | ||
416 | if (! error) { | ||
417 | (void) xfs_truncate_file(mp, qip); | ||
418 | VN_RELE(XFS_ITOV(qip)); | ||
419 | } | ||
420 | } | ||
421 | |||
422 | return (error); | ||
423 | } | ||
424 | |||
425 | |||
426 | /* | ||
427 | * Switch on (a given) quota enforcement for a filesystem. This takes | ||
428 | * effect immediately. | ||
429 | * (Switching on quota accounting must be done at mount time.) | ||
430 | */ | ||
431 | STATIC int | ||
432 | xfs_qm_scall_quotaon( | ||
433 | xfs_mount_t *mp, | ||
434 | uint flags) | ||
435 | { | ||
436 | int error; | ||
437 | unsigned long s; | ||
438 | uint qf; | ||
439 | uint accflags; | ||
440 | __int64_t sbflags; | ||
441 | |||
442 | if (!capable(CAP_SYS_ADMIN)) | ||
443 | return XFS_ERROR(EPERM); | ||
444 | |||
445 | flags &= (XFS_ALL_QUOTA_ACCT | XFS_ALL_QUOTA_ENFD); | ||
446 | /* | ||
447 | * Switching on quota accounting must be done at mount time. | ||
448 | */ | ||
449 | accflags = flags & XFS_ALL_QUOTA_ACCT; | ||
450 | flags &= ~(XFS_ALL_QUOTA_ACCT); | ||
451 | |||
452 | sbflags = 0; | ||
453 | |||
454 | if (flags == 0) { | ||
455 | qdprintk("quotaon: zero flags, m_qflags=%x\n", mp->m_qflags); | ||
456 | return XFS_ERROR(EINVAL); | ||
457 | } | ||
458 | |||
459 | /* No fs can turn on quotas with a delayed effect */ | ||
460 | ASSERT((flags & XFS_ALL_QUOTA_ACCT) == 0); | ||
461 | |||
462 | /* | ||
463 | * Can't enforce without accounting. We check the superblock | ||
464 | * qflags here instead of m_qflags because rootfs can have | ||
465 | * quota acct on ondisk without m_qflags' knowing. | ||
466 | */ | ||
467 | if (((flags & XFS_UQUOTA_ACCT) == 0 && | ||
468 | (mp->m_sb.sb_qflags & XFS_UQUOTA_ACCT) == 0 && | ||
469 | (flags & XFS_UQUOTA_ENFD)) | ||
470 | || | ||
471 | ((flags & XFS_GQUOTA_ACCT) == 0 && | ||
472 | (mp->m_sb.sb_qflags & XFS_GQUOTA_ACCT) == 0 && | ||
473 | (flags & XFS_GQUOTA_ENFD))) { | ||
474 | qdprintk("Can't enforce without acct, flags=%x sbflags=%x\n", | ||
475 | flags, mp->m_sb.sb_qflags); | ||
476 | return XFS_ERROR(EINVAL); | ||
477 | } | ||
478 | /* | ||
479 | * If everything's upto-date incore, then don't waste time. | ||
480 | */ | ||
481 | if ((mp->m_qflags & flags) == flags) | ||
482 | return XFS_ERROR(EEXIST); | ||
483 | |||
484 | /* | ||
485 | * Change sb_qflags on disk but not incore mp->qflags | ||
486 | * if this is the root filesystem. | ||
487 | */ | ||
488 | s = XFS_SB_LOCK(mp); | ||
489 | qf = mp->m_sb.sb_qflags; | ||
490 | mp->m_sb.sb_qflags = qf | flags; | ||
491 | XFS_SB_UNLOCK(mp, s); | ||
492 | |||
493 | /* | ||
494 | * There's nothing to change if it's the same. | ||
495 | */ | ||
496 | if ((qf & flags) == flags && sbflags == 0) | ||
497 | return XFS_ERROR(EEXIST); | ||
498 | sbflags |= XFS_SB_QFLAGS; | ||
499 | |||
500 | if ((error = xfs_qm_write_sb_changes(mp, sbflags))) | ||
501 | return (error); | ||
502 | /* | ||
503 | * If we aren't trying to switch on quota enforcement, we are done. | ||
504 | */ | ||
505 | if (((mp->m_sb.sb_qflags & XFS_UQUOTA_ACCT) != | ||
506 | (mp->m_qflags & XFS_UQUOTA_ACCT)) || | ||
507 | (flags & XFS_ALL_QUOTA_ENFD) == 0) | ||
508 | return (0); | ||
509 | |||
510 | if (! XFS_IS_QUOTA_RUNNING(mp)) | ||
511 | return XFS_ERROR(ESRCH); | ||
512 | |||
513 | /* | ||
514 | * Switch on quota enforcement in core. | ||
515 | */ | ||
516 | mutex_lock(&(XFS_QI_QOFFLOCK(mp)), PINOD); | ||
517 | mp->m_qflags |= (flags & XFS_ALL_QUOTA_ENFD); | ||
518 | mutex_unlock(&(XFS_QI_QOFFLOCK(mp))); | ||
519 | |||
520 | return (0); | ||
521 | } | ||
522 | |||
523 | |||
524 | |||
525 | /* | ||
526 | * Return quota status information, such as uquota-off, enforcements, etc. | ||
527 | */ | ||
528 | STATIC int | ||
529 | xfs_qm_scall_getqstat( | ||
530 | xfs_mount_t *mp, | ||
531 | fs_quota_stat_t *out) | ||
532 | { | ||
533 | xfs_inode_t *uip, *gip; | ||
534 | boolean_t tempuqip, tempgqip; | ||
535 | |||
536 | uip = gip = NULL; | ||
537 | tempuqip = tempgqip = B_FALSE; | ||
538 | memset(out, 0, sizeof(fs_quota_stat_t)); | ||
539 | |||
540 | out->qs_version = FS_QSTAT_VERSION; | ||
541 | if (! XFS_SB_VERSION_HASQUOTA(&mp->m_sb)) { | ||
542 | out->qs_uquota.qfs_ino = NULLFSINO; | ||
543 | out->qs_gquota.qfs_ino = NULLFSINO; | ||
544 | return (0); | ||
545 | } | ||
546 | out->qs_flags = (__uint16_t) xfs_qm_export_flags(mp->m_qflags & | ||
547 | (XFS_ALL_QUOTA_ACCT| | ||
548 | XFS_ALL_QUOTA_ENFD)); | ||
549 | out->qs_pad = 0; | ||
550 | out->qs_uquota.qfs_ino = mp->m_sb.sb_uquotino; | ||
551 | out->qs_gquota.qfs_ino = mp->m_sb.sb_gquotino; | ||
552 | |||
553 | if (mp->m_quotainfo) { | ||
554 | uip = mp->m_quotainfo->qi_uquotaip; | ||
555 | gip = mp->m_quotainfo->qi_gquotaip; | ||
556 | } | ||
557 | if (!uip && mp->m_sb.sb_uquotino != NULLFSINO) { | ||
558 | if (xfs_iget(mp, NULL, mp->m_sb.sb_uquotino, | ||
559 | 0, 0, &uip, 0) == 0) | ||
560 | tempuqip = B_TRUE; | ||
561 | } | ||
562 | if (!gip && mp->m_sb.sb_gquotino != NULLFSINO) { | ||
563 | if (xfs_iget(mp, NULL, mp->m_sb.sb_gquotino, | ||
564 | 0, 0, &gip, 0) == 0) | ||
565 | tempgqip = B_TRUE; | ||
566 | } | ||
567 | if (uip) { | ||
568 | out->qs_uquota.qfs_nblks = uip->i_d.di_nblocks; | ||
569 | out->qs_uquota.qfs_nextents = uip->i_d.di_nextents; | ||
570 | if (tempuqip) | ||
571 | VN_RELE(XFS_ITOV(uip)); | ||
572 | } | ||
573 | if (gip) { | ||
574 | out->qs_gquota.qfs_nblks = gip->i_d.di_nblocks; | ||
575 | out->qs_gquota.qfs_nextents = gip->i_d.di_nextents; | ||
576 | if (tempgqip) | ||
577 | VN_RELE(XFS_ITOV(gip)); | ||
578 | } | ||
579 | if (mp->m_quotainfo) { | ||
580 | out->qs_incoredqs = XFS_QI_MPLNDQUOTS(mp); | ||
581 | out->qs_btimelimit = XFS_QI_BTIMELIMIT(mp); | ||
582 | out->qs_itimelimit = XFS_QI_ITIMELIMIT(mp); | ||
583 | out->qs_rtbtimelimit = XFS_QI_RTBTIMELIMIT(mp); | ||
584 | out->qs_bwarnlimit = XFS_QI_BWARNLIMIT(mp); | ||
585 | out->qs_iwarnlimit = XFS_QI_IWARNLIMIT(mp); | ||
586 | } | ||
587 | return (0); | ||
588 | } | ||
589 | |||
590 | /* | ||
591 | * Adjust quota limits, and start/stop timers accordingly. | ||
592 | */ | ||
593 | STATIC int | ||
594 | xfs_qm_scall_setqlim( | ||
595 | xfs_mount_t *mp, | ||
596 | xfs_dqid_t id, | ||
597 | uint type, | ||
598 | fs_disk_quota_t *newlim) | ||
599 | { | ||
600 | xfs_disk_dquot_t *ddq; | ||
601 | xfs_dquot_t *dqp; | ||
602 | xfs_trans_t *tp; | ||
603 | int error; | ||
604 | xfs_qcnt_t hard, soft; | ||
605 | |||
606 | if (!capable(CAP_SYS_ADMIN)) | ||
607 | return XFS_ERROR(EPERM); | ||
608 | |||
609 | if ((newlim->d_fieldmask & (FS_DQ_LIMIT_MASK|FS_DQ_TIMER_MASK)) == 0) | ||
610 | return (0); | ||
611 | |||
612 | tp = xfs_trans_alloc(mp, XFS_TRANS_QM_SETQLIM); | ||
613 | if ((error = xfs_trans_reserve(tp, 0, sizeof(xfs_disk_dquot_t) + 128, | ||
614 | 0, 0, XFS_DEFAULT_LOG_COUNT))) { | ||
615 | xfs_trans_cancel(tp, 0); | ||
616 | return (error); | ||
617 | } | ||
618 | |||
619 | /* | ||
620 | * We don't want to race with a quotaoff so take the quotaoff lock. | ||
621 | * (We don't hold an inode lock, so there's nothing else to stop | ||
622 | * a quotaoff from happening). (XXXThis doesn't currently happen | ||
623 | * because we take the vfslock before calling xfs_qm_sysent). | ||
624 | */ | ||
625 | mutex_lock(&(XFS_QI_QOFFLOCK(mp)), PINOD); | ||
626 | |||
627 | /* | ||
628 | * Get the dquot (locked), and join it to the transaction. | ||
629 | * Allocate the dquot if this doesn't exist. | ||
630 | */ | ||
631 | if ((error = xfs_qm_dqget(mp, NULL, id, type, XFS_QMOPT_DQALLOC, &dqp))) { | ||
632 | xfs_trans_cancel(tp, XFS_TRANS_ABORT); | ||
633 | mutex_unlock(&(XFS_QI_QOFFLOCK(mp))); | ||
634 | ASSERT(error != ENOENT); | ||
635 | return (error); | ||
636 | } | ||
637 | xfs_dqtrace_entry(dqp, "Q_SETQLIM: AFT DQGET"); | ||
638 | xfs_trans_dqjoin(tp, dqp); | ||
639 | ddq = &dqp->q_core; | ||
640 | |||
641 | /* | ||
642 | * Make sure that hardlimits are >= soft limits before changing. | ||
643 | */ | ||
644 | hard = (newlim->d_fieldmask & FS_DQ_BHARD) ? | ||
645 | (xfs_qcnt_t) XFS_BB_TO_FSB(mp, newlim->d_blk_hardlimit) : | ||
646 | INT_GET(ddq->d_blk_hardlimit, ARCH_CONVERT); | ||
647 | soft = (newlim->d_fieldmask & FS_DQ_BSOFT) ? | ||
648 | (xfs_qcnt_t) XFS_BB_TO_FSB(mp, newlim->d_blk_softlimit) : | ||
649 | INT_GET(ddq->d_blk_softlimit, ARCH_CONVERT); | ||
650 | if (hard == 0 || hard >= soft) { | ||
651 | INT_SET(ddq->d_blk_hardlimit, ARCH_CONVERT, hard); | ||
652 | INT_SET(ddq->d_blk_softlimit, ARCH_CONVERT, soft); | ||
653 | if (id == 0) { | ||
654 | mp->m_quotainfo->qi_bhardlimit = hard; | ||
655 | mp->m_quotainfo->qi_bsoftlimit = soft; | ||
656 | } | ||
657 | } else { | ||
658 | qdprintk("blkhard %Ld < blksoft %Ld\n", hard, soft); | ||
659 | } | ||
660 | hard = (newlim->d_fieldmask & FS_DQ_RTBHARD) ? | ||
661 | (xfs_qcnt_t) XFS_BB_TO_FSB(mp, newlim->d_rtb_hardlimit) : | ||
662 | INT_GET(ddq->d_rtb_hardlimit, ARCH_CONVERT); | ||
663 | soft = (newlim->d_fieldmask & FS_DQ_RTBSOFT) ? | ||
664 | (xfs_qcnt_t) XFS_BB_TO_FSB(mp, newlim->d_rtb_softlimit) : | ||
665 | INT_GET(ddq->d_rtb_softlimit, ARCH_CONVERT); | ||
666 | if (hard == 0 || hard >= soft) { | ||
667 | INT_SET(ddq->d_rtb_hardlimit, ARCH_CONVERT, hard); | ||
668 | INT_SET(ddq->d_rtb_softlimit, ARCH_CONVERT, soft); | ||
669 | if (id == 0) { | ||
670 | mp->m_quotainfo->qi_rtbhardlimit = hard; | ||
671 | mp->m_quotainfo->qi_rtbsoftlimit = soft; | ||
672 | } | ||
673 | } else { | ||
674 | qdprintk("rtbhard %Ld < rtbsoft %Ld\n", hard, soft); | ||
675 | } | ||
676 | |||
677 | hard = (newlim->d_fieldmask & FS_DQ_IHARD) ? | ||
678 | (xfs_qcnt_t) newlim->d_ino_hardlimit : | ||
679 | INT_GET(ddq->d_ino_hardlimit, ARCH_CONVERT); | ||
680 | soft = (newlim->d_fieldmask & FS_DQ_ISOFT) ? | ||
681 | (xfs_qcnt_t) newlim->d_ino_softlimit : | ||
682 | INT_GET(ddq->d_ino_softlimit, ARCH_CONVERT); | ||
683 | if (hard == 0 || hard >= soft) { | ||
684 | INT_SET(ddq->d_ino_hardlimit, ARCH_CONVERT, hard); | ||
685 | INT_SET(ddq->d_ino_softlimit, ARCH_CONVERT, soft); | ||
686 | if (id == 0) { | ||
687 | mp->m_quotainfo->qi_ihardlimit = hard; | ||
688 | mp->m_quotainfo->qi_isoftlimit = soft; | ||
689 | } | ||
690 | } else { | ||
691 | qdprintk("ihard %Ld < isoft %Ld\n", hard, soft); | ||
692 | } | ||
693 | |||
694 | if (id == 0) { | ||
695 | /* | ||
696 | * Timelimits for the super user set the relative time | ||
697 | * the other users can be over quota for this file system. | ||
698 | * If it is zero a default is used. Ditto for the default | ||
699 | * soft and hard limit values (already done, above). | ||
700 | */ | ||
701 | if (newlim->d_fieldmask & FS_DQ_BTIMER) { | ||
702 | mp->m_quotainfo->qi_btimelimit = newlim->d_btimer; | ||
703 | INT_SET(ddq->d_btimer, ARCH_CONVERT, newlim->d_btimer); | ||
704 | } | ||
705 | if (newlim->d_fieldmask & FS_DQ_ITIMER) { | ||
706 | mp->m_quotainfo->qi_itimelimit = newlim->d_itimer; | ||
707 | INT_SET(ddq->d_itimer, ARCH_CONVERT, newlim->d_itimer); | ||
708 | } | ||
709 | if (newlim->d_fieldmask & FS_DQ_RTBTIMER) { | ||
710 | mp->m_quotainfo->qi_rtbtimelimit = newlim->d_rtbtimer; | ||
711 | INT_SET(ddq->d_rtbtimer, ARCH_CONVERT, newlim->d_rtbtimer); | ||
712 | } | ||
713 | } else /* if (XFS_IS_QUOTA_ENFORCED(mp)) */ { | ||
714 | /* | ||
715 | * If the user is now over quota, start the timelimit. | ||
716 | * The user will not be 'warned'. | ||
717 | * Note that we keep the timers ticking, whether enforcement | ||
718 | * is on or off. We don't really want to bother with iterating | ||
719 | * over all ondisk dquots and turning the timers on/off. | ||
720 | */ | ||
721 | xfs_qm_adjust_dqtimers(mp, ddq); | ||
722 | } | ||
723 | dqp->dq_flags |= XFS_DQ_DIRTY; | ||
724 | xfs_trans_log_dquot(tp, dqp); | ||
725 | |||
726 | xfs_dqtrace_entry(dqp, "Q_SETQLIM: COMMIT"); | ||
727 | xfs_trans_commit(tp, 0, NULL); | ||
728 | xfs_qm_dqprint(dqp); | ||
729 | xfs_qm_dqrele(dqp); | ||
730 | mutex_unlock(&(XFS_QI_QOFFLOCK(mp))); | ||
731 | |||
732 | return (0); | ||
733 | } | ||
734 | |||
735 | STATIC int | ||
736 | xfs_qm_scall_getquota( | ||
737 | xfs_mount_t *mp, | ||
738 | xfs_dqid_t id, | ||
739 | uint type, | ||
740 | fs_disk_quota_t *out) | ||
741 | { | ||
742 | xfs_dquot_t *dqp; | ||
743 | int error; | ||
744 | |||
745 | /* | ||
746 | * Try to get the dquot. We don't want it allocated on disk, so | ||
747 | * we aren't passing the XFS_QMOPT_DOALLOC flag. If it doesn't | ||
748 | * exist, we'll get ENOENT back. | ||
749 | */ | ||
750 | if ((error = xfs_qm_dqget(mp, NULL, id, type, 0, &dqp))) { | ||
751 | return (error); | ||
752 | } | ||
753 | |||
754 | xfs_dqtrace_entry(dqp, "Q_GETQUOTA SUCCESS"); | ||
755 | /* | ||
756 | * If everything's NULL, this dquot doesn't quite exist as far as | ||
757 | * our utility programs are concerned. | ||
758 | */ | ||
759 | if (XFS_IS_DQUOT_UNINITIALIZED(dqp)) { | ||
760 | xfs_qm_dqput(dqp); | ||
761 | return XFS_ERROR(ENOENT); | ||
762 | } | ||
763 | /* xfs_qm_dqprint(dqp); */ | ||
764 | /* | ||
765 | * Convert the disk dquot to the exportable format | ||
766 | */ | ||
767 | xfs_qm_export_dquot(mp, &dqp->q_core, out); | ||
768 | xfs_qm_dqput(dqp); | ||
769 | return (error ? XFS_ERROR(EFAULT) : 0); | ||
770 | } | ||
771 | |||
772 | |||
773 | STATIC int | ||
774 | xfs_qm_log_quotaoff_end( | ||
775 | xfs_mount_t *mp, | ||
776 | xfs_qoff_logitem_t *startqoff, | ||
777 | uint flags) | ||
778 | { | ||
779 | xfs_trans_t *tp; | ||
780 | int error; | ||
781 | xfs_qoff_logitem_t *qoffi; | ||
782 | |||
783 | tp = xfs_trans_alloc(mp, XFS_TRANS_QM_QUOTAOFF_END); | ||
784 | |||
785 | if ((error = xfs_trans_reserve(tp, 0, sizeof(xfs_qoff_logitem_t) * 2, | ||
786 | 0, 0, XFS_DEFAULT_LOG_COUNT))) { | ||
787 | xfs_trans_cancel(tp, 0); | ||
788 | return (error); | ||
789 | } | ||
790 | |||
791 | qoffi = xfs_trans_get_qoff_item(tp, startqoff, | ||
792 | flags & XFS_ALL_QUOTA_ACCT); | ||
793 | xfs_trans_log_quotaoff_item(tp, qoffi); | ||
794 | |||
795 | /* | ||
796 | * We have to make sure that the transaction is secure on disk before we | ||
797 | * return and actually stop quota accounting. So, make it synchronous. | ||
798 | * We don't care about quotoff's performance. | ||
799 | */ | ||
800 | xfs_trans_set_sync(tp); | ||
801 | error = xfs_trans_commit(tp, 0, NULL); | ||
802 | return (error); | ||
803 | } | ||
804 | |||
805 | |||
806 | STATIC int | ||
807 | xfs_qm_log_quotaoff( | ||
808 | xfs_mount_t *mp, | ||
809 | xfs_qoff_logitem_t **qoffstartp, | ||
810 | uint flags) | ||
811 | { | ||
812 | xfs_trans_t *tp; | ||
813 | int error; | ||
814 | unsigned long s; | ||
815 | xfs_qoff_logitem_t *qoffi=NULL; | ||
816 | uint oldsbqflag=0; | ||
817 | |||
818 | tp = xfs_trans_alloc(mp, XFS_TRANS_QM_QUOTAOFF); | ||
819 | if ((error = xfs_trans_reserve(tp, 0, | ||
820 | sizeof(xfs_qoff_logitem_t) * 2 + | ||
821 | mp->m_sb.sb_sectsize + 128, | ||
822 | 0, | ||
823 | 0, | ||
824 | XFS_DEFAULT_LOG_COUNT))) { | ||
825 | goto error0; | ||
826 | } | ||
827 | |||
828 | qoffi = xfs_trans_get_qoff_item(tp, NULL, flags & XFS_ALL_QUOTA_ACCT); | ||
829 | xfs_trans_log_quotaoff_item(tp, qoffi); | ||
830 | |||
831 | s = XFS_SB_LOCK(mp); | ||
832 | oldsbqflag = mp->m_sb.sb_qflags; | ||
833 | mp->m_sb.sb_qflags = (mp->m_qflags & ~(flags)) & XFS_MOUNT_QUOTA_ALL; | ||
834 | XFS_SB_UNLOCK(mp, s); | ||
835 | |||
836 | xfs_mod_sb(tp, XFS_SB_QFLAGS); | ||
837 | |||
838 | /* | ||
839 | * We have to make sure that the transaction is secure on disk before we | ||
840 | * return and actually stop quota accounting. So, make it synchronous. | ||
841 | * We don't care about quotoff's performance. | ||
842 | */ | ||
843 | xfs_trans_set_sync(tp); | ||
844 | error = xfs_trans_commit(tp, 0, NULL); | ||
845 | |||
846 | error0: | ||
847 | if (error) { | ||
848 | xfs_trans_cancel(tp, 0); | ||
849 | /* | ||
850 | * No one else is modifying sb_qflags, so this is OK. | ||
851 | * We still hold the quotaofflock. | ||
852 | */ | ||
853 | s = XFS_SB_LOCK(mp); | ||
854 | mp->m_sb.sb_qflags = oldsbqflag; | ||
855 | XFS_SB_UNLOCK(mp, s); | ||
856 | } | ||
857 | *qoffstartp = qoffi; | ||
858 | return (error); | ||
859 | } | ||
860 | |||
861 | |||
862 | /* | ||
863 | * Translate an internal style on-disk-dquot to the exportable format. | ||
864 | * The main differences are that the counters/limits are all in Basic | ||
865 | * Blocks (BBs) instead of the internal FSBs, and all on-disk data has | ||
866 | * to be converted to the native endianness. | ||
867 | */ | ||
868 | STATIC void | ||
869 | xfs_qm_export_dquot( | ||
870 | xfs_mount_t *mp, | ||
871 | xfs_disk_dquot_t *src, | ||
872 | struct fs_disk_quota *dst) | ||
873 | { | ||
874 | memset(dst, 0, sizeof(*dst)); | ||
875 | dst->d_version = FS_DQUOT_VERSION; /* different from src->d_version */ | ||
876 | dst->d_flags = | ||
877 | xfs_qm_export_qtype_flags(INT_GET(src->d_flags, ARCH_CONVERT)); | ||
878 | dst->d_id = INT_GET(src->d_id, ARCH_CONVERT); | ||
879 | dst->d_blk_hardlimit = (__uint64_t) | ||
880 | XFS_FSB_TO_BB(mp, INT_GET(src->d_blk_hardlimit, ARCH_CONVERT)); | ||
881 | dst->d_blk_softlimit = (__uint64_t) | ||
882 | XFS_FSB_TO_BB(mp, INT_GET(src->d_blk_softlimit, ARCH_CONVERT)); | ||
883 | dst->d_ino_hardlimit = (__uint64_t) | ||
884 | INT_GET(src->d_ino_hardlimit, ARCH_CONVERT); | ||
885 | dst->d_ino_softlimit = (__uint64_t) | ||
886 | INT_GET(src->d_ino_softlimit, ARCH_CONVERT); | ||
887 | dst->d_bcount = (__uint64_t) | ||
888 | XFS_FSB_TO_BB(mp, INT_GET(src->d_bcount, ARCH_CONVERT)); | ||
889 | dst->d_icount = (__uint64_t) INT_GET(src->d_icount, ARCH_CONVERT); | ||
890 | dst->d_btimer = (__uint32_t) INT_GET(src->d_btimer, ARCH_CONVERT); | ||
891 | dst->d_itimer = (__uint32_t) INT_GET(src->d_itimer, ARCH_CONVERT); | ||
892 | dst->d_iwarns = INT_GET(src->d_iwarns, ARCH_CONVERT); | ||
893 | dst->d_bwarns = INT_GET(src->d_bwarns, ARCH_CONVERT); | ||
894 | |||
895 | dst->d_rtb_hardlimit = (__uint64_t) | ||
896 | XFS_FSB_TO_BB(mp, INT_GET(src->d_rtb_hardlimit, ARCH_CONVERT)); | ||
897 | dst->d_rtb_softlimit = (__uint64_t) | ||
898 | XFS_FSB_TO_BB(mp, INT_GET(src->d_rtb_softlimit, ARCH_CONVERT)); | ||
899 | dst->d_rtbcount = (__uint64_t) | ||
900 | XFS_FSB_TO_BB(mp, INT_GET(src->d_rtbcount, ARCH_CONVERT)); | ||
901 | dst->d_rtbtimer = (__uint32_t) INT_GET(src->d_rtbtimer, ARCH_CONVERT); | ||
902 | dst->d_rtbwarns = INT_GET(src->d_rtbwarns, ARCH_CONVERT); | ||
903 | |||
904 | /* | ||
905 | * Internally, we don't reset all the timers when quota enforcement | ||
906 | * gets turned off. No need to confuse the userlevel code, | ||
907 | * so return zeroes in that case. | ||
908 | */ | ||
909 | if (! XFS_IS_QUOTA_ENFORCED(mp)) { | ||
910 | dst->d_btimer = 0; | ||
911 | dst->d_itimer = 0; | ||
912 | dst->d_rtbtimer = 0; | ||
913 | } | ||
914 | |||
915 | #ifdef DEBUG | ||
916 | if (XFS_IS_QUOTA_ENFORCED(mp) && dst->d_id != 0) { | ||
917 | if (((int) dst->d_bcount >= (int) dst->d_blk_softlimit) && | ||
918 | (dst->d_blk_softlimit > 0)) { | ||
919 | ASSERT(dst->d_btimer != 0); | ||
920 | } | ||
921 | if (((int) dst->d_icount >= (int) dst->d_ino_softlimit) && | ||
922 | (dst->d_ino_softlimit > 0)) { | ||
923 | ASSERT(dst->d_itimer != 0); | ||
924 | } | ||
925 | } | ||
926 | #endif | ||
927 | } | ||
928 | |||
929 | STATIC uint | ||
930 | xfs_qm_import_qtype_flags( | ||
931 | uint uflags) | ||
932 | { | ||
933 | /* | ||
934 | * Can't be both at the same time. | ||
935 | */ | ||
936 | if (((uflags & (XFS_GROUP_QUOTA | XFS_USER_QUOTA)) == | ||
937 | (XFS_GROUP_QUOTA | XFS_USER_QUOTA)) || | ||
938 | ((uflags & (XFS_GROUP_QUOTA | XFS_USER_QUOTA)) == 0)) | ||
939 | return (0); | ||
940 | |||
941 | return (uflags & XFS_USER_QUOTA) ? | ||
942 | XFS_DQ_USER : XFS_DQ_GROUP; | ||
943 | } | ||
944 | |||
945 | STATIC uint | ||
946 | xfs_qm_export_qtype_flags( | ||
947 | uint flags) | ||
948 | { | ||
949 | /* | ||
950 | * Can't be both at the same time. | ||
951 | */ | ||
952 | ASSERT((flags & (XFS_GROUP_QUOTA | XFS_USER_QUOTA)) != | ||
953 | (XFS_GROUP_QUOTA | XFS_USER_QUOTA)); | ||
954 | ASSERT((flags & (XFS_GROUP_QUOTA | XFS_USER_QUOTA)) != 0); | ||
955 | |||
956 | return (flags & XFS_DQ_USER) ? | ||
957 | XFS_USER_QUOTA : XFS_GROUP_QUOTA; | ||
958 | } | ||
959 | |||
960 | STATIC uint | ||
961 | xfs_qm_import_flags( | ||
962 | uint uflags) | ||
963 | { | ||
964 | uint flags = 0; | ||
965 | |||
966 | if (uflags & XFS_QUOTA_UDQ_ACCT) | ||
967 | flags |= XFS_UQUOTA_ACCT; | ||
968 | if (uflags & XFS_QUOTA_GDQ_ACCT) | ||
969 | flags |= XFS_GQUOTA_ACCT; | ||
970 | if (uflags & XFS_QUOTA_UDQ_ENFD) | ||
971 | flags |= XFS_UQUOTA_ENFD; | ||
972 | if (uflags & XFS_QUOTA_GDQ_ENFD) | ||
973 | flags |= XFS_GQUOTA_ENFD; | ||
974 | return (flags); | ||
975 | } | ||
976 | |||
977 | |||
978 | STATIC uint | ||
979 | xfs_qm_export_flags( | ||
980 | uint flags) | ||
981 | { | ||
982 | uint uflags; | ||
983 | |||
984 | uflags = 0; | ||
985 | if (flags & XFS_UQUOTA_ACCT) | ||
986 | uflags |= XFS_QUOTA_UDQ_ACCT; | ||
987 | if (flags & XFS_GQUOTA_ACCT) | ||
988 | uflags |= XFS_QUOTA_GDQ_ACCT; | ||
989 | if (flags & XFS_UQUOTA_ENFD) | ||
990 | uflags |= XFS_QUOTA_UDQ_ENFD; | ||
991 | if (flags & XFS_GQUOTA_ENFD) | ||
992 | uflags |= XFS_QUOTA_GDQ_ENFD; | ||
993 | return (uflags); | ||
994 | } | ||
995 | |||
996 | |||
997 | /* | ||
998 | * Go thru all the inodes in the file system, releasing their dquots. | ||
999 | * Note that the mount structure gets modified to indicate that quotas are off | ||
1000 | * AFTER this, in the case of quotaoff. This also gets called from | ||
1001 | * xfs_rootumount. | ||
1002 | */ | ||
1003 | void | ||
1004 | xfs_qm_dqrele_all_inodes( | ||
1005 | struct xfs_mount *mp, | ||
1006 | uint flags) | ||
1007 | { | ||
1008 | vmap_t vmap; | ||
1009 | xfs_inode_t *ip, *topino; | ||
1010 | uint ireclaims; | ||
1011 | vnode_t *vp; | ||
1012 | boolean_t vnode_refd; | ||
1013 | |||
1014 | ASSERT(mp->m_quotainfo); | ||
1015 | |||
1016 | again: | ||
1017 | XFS_MOUNT_ILOCK(mp); | ||
1018 | ip = mp->m_inodes; | ||
1019 | if (ip == NULL) { | ||
1020 | XFS_MOUNT_IUNLOCK(mp); | ||
1021 | return; | ||
1022 | } | ||
1023 | do { | ||
1024 | /* Skip markers inserted by xfs_sync */ | ||
1025 | if (ip->i_mount == NULL) { | ||
1026 | ip = ip->i_mnext; | ||
1027 | continue; | ||
1028 | } | ||
1029 | /* Root inode, rbmip and rsumip have associated blocks */ | ||
1030 | if (ip == XFS_QI_UQIP(mp) || ip == XFS_QI_GQIP(mp)) { | ||
1031 | ASSERT(ip->i_udquot == NULL); | ||
1032 | ASSERT(ip->i_gdquot == NULL); | ||
1033 | ip = ip->i_mnext; | ||
1034 | continue; | ||
1035 | } | ||
1036 | vp = XFS_ITOV_NULL(ip); | ||
1037 | if (!vp) { | ||
1038 | ASSERT(ip->i_udquot == NULL); | ||
1039 | ASSERT(ip->i_gdquot == NULL); | ||
1040 | ip = ip->i_mnext; | ||
1041 | continue; | ||
1042 | } | ||
1043 | vnode_refd = B_FALSE; | ||
1044 | if (xfs_ilock_nowait(ip, XFS_ILOCK_EXCL) == 0) { | ||
1045 | /* | ||
1046 | * Sample vp mapping while holding the mplock, lest | ||
1047 | * we come across a non-existent vnode. | ||
1048 | */ | ||
1049 | VMAP(vp, vmap); | ||
1050 | ireclaims = mp->m_ireclaims; | ||
1051 | topino = mp->m_inodes; | ||
1052 | XFS_MOUNT_IUNLOCK(mp); | ||
1053 | |||
1054 | /* XXX restart limit ? */ | ||
1055 | if ( ! (vp = vn_get(vp, &vmap))) | ||
1056 | goto again; | ||
1057 | xfs_ilock(ip, XFS_ILOCK_EXCL); | ||
1058 | vnode_refd = B_TRUE; | ||
1059 | } else { | ||
1060 | ireclaims = mp->m_ireclaims; | ||
1061 | topino = mp->m_inodes; | ||
1062 | XFS_MOUNT_IUNLOCK(mp); | ||
1063 | } | ||
1064 | |||
1065 | /* | ||
1066 | * We don't keep the mountlock across the dqrele() call, | ||
1067 | * since it can take a while.. | ||
1068 | */ | ||
1069 | if ((flags & XFS_UQUOTA_ACCT) && ip->i_udquot) { | ||
1070 | xfs_qm_dqrele(ip->i_udquot); | ||
1071 | ip->i_udquot = NULL; | ||
1072 | } | ||
1073 | if ((flags & XFS_GQUOTA_ACCT) && ip->i_gdquot) { | ||
1074 | xfs_qm_dqrele(ip->i_gdquot); | ||
1075 | ip->i_gdquot = NULL; | ||
1076 | } | ||
1077 | xfs_iunlock(ip, XFS_ILOCK_EXCL); | ||
1078 | /* | ||
1079 | * Wait until we've dropped the ilock and mountlock to | ||
1080 | * do the vn_rele. Or be condemned to an eternity in the | ||
1081 | * inactive code in hell. | ||
1082 | */ | ||
1083 | if (vnode_refd) | ||
1084 | VN_RELE(vp); | ||
1085 | XFS_MOUNT_ILOCK(mp); | ||
1086 | /* | ||
1087 | * If an inode was inserted or removed, we gotta | ||
1088 | * start over again. | ||
1089 | */ | ||
1090 | if (topino != mp->m_inodes || mp->m_ireclaims != ireclaims) { | ||
1091 | /* XXX use a sentinel */ | ||
1092 | XFS_MOUNT_IUNLOCK(mp); | ||
1093 | goto again; | ||
1094 | } | ||
1095 | ip = ip->i_mnext; | ||
1096 | } while (ip != mp->m_inodes); | ||
1097 | |||
1098 | XFS_MOUNT_IUNLOCK(mp); | ||
1099 | } | ||
1100 | |||
1101 | /*------------------------------------------------------------------------*/ | ||
1102 | #ifdef DEBUG | ||
1103 | /* | ||
1104 | * This contains all the test functions for XFS disk quotas. | ||
1105 | * Currently it does a quota accounting check. ie. it walks through | ||
1106 | * all inodes in the file system, calculating the dquot accounting fields, | ||
1107 | * and prints out any inconsistencies. | ||
1108 | */ | ||
1109 | xfs_dqhash_t *qmtest_udqtab; | ||
1110 | xfs_dqhash_t *qmtest_gdqtab; | ||
1111 | int qmtest_hashmask; | ||
1112 | int qmtest_nfails; | ||
1113 | mutex_t qcheck_lock; | ||
1114 | |||
1115 | #define DQTEST_HASHVAL(mp, id) (((__psunsigned_t)(mp) + \ | ||
1116 | (__psunsigned_t)(id)) & \ | ||
1117 | (qmtest_hashmask - 1)) | ||
1118 | |||
1119 | #define DQTEST_HASH(mp, id, type) ((type & XFS_DQ_USER) ? \ | ||
1120 | (qmtest_udqtab + \ | ||
1121 | DQTEST_HASHVAL(mp, id)) : \ | ||
1122 | (qmtest_gdqtab + \ | ||
1123 | DQTEST_HASHVAL(mp, id))) | ||
1124 | |||
1125 | #define DQTEST_LIST_PRINT(l, NXT, title) \ | ||
1126 | { \ | ||
1127 | xfs_dqtest_t *dqp; int i = 0;\ | ||
1128 | cmn_err(CE_DEBUG, "%s (#%d)", title, (int) (l)->qh_nelems); \ | ||
1129 | for (dqp = (xfs_dqtest_t *)(l)->qh_next; dqp != NULL; \ | ||
1130 | dqp = (xfs_dqtest_t *)dqp->NXT) { \ | ||
1131 | cmn_err(CE_DEBUG, " %d. \"%d (%s)\" bcnt = %d, icnt = %d", \ | ||
1132 | ++i, dqp->d_id, DQFLAGTO_TYPESTR(dqp), \ | ||
1133 | dqp->d_bcount, dqp->d_icount); } \ | ||
1134 | } | ||
1135 | |||
1136 | typedef struct dqtest { | ||
1137 | xfs_dqmarker_t q_lists; | ||
1138 | xfs_dqhash_t *q_hash; /* the hashchain header */ | ||
1139 | xfs_mount_t *q_mount; /* filesystem this relates to */ | ||
1140 | xfs_dqid_t d_id; /* user id or group id */ | ||
1141 | xfs_qcnt_t d_bcount; /* # disk blocks owned by the user */ | ||
1142 | xfs_qcnt_t d_icount; /* # inodes owned by the user */ | ||
1143 | } xfs_dqtest_t; | ||
1144 | |||
1145 | STATIC void | ||
1146 | xfs_qm_hashinsert(xfs_dqhash_t *h, xfs_dqtest_t *dqp) | ||
1147 | { | ||
1148 | xfs_dquot_t *d; | ||
1149 | if (((d) = (h)->qh_next)) | ||
1150 | (d)->HL_PREVP = &((dqp)->HL_NEXT); | ||
1151 | (dqp)->HL_NEXT = d; | ||
1152 | (dqp)->HL_PREVP = &((h)->qh_next); | ||
1153 | (h)->qh_next = (xfs_dquot_t *)dqp; | ||
1154 | (h)->qh_version++; | ||
1155 | (h)->qh_nelems++; | ||
1156 | } | ||
1157 | STATIC void | ||
1158 | xfs_qm_dqtest_print( | ||
1159 | xfs_dqtest_t *d) | ||
1160 | { | ||
1161 | cmn_err(CE_DEBUG, "-----------DQTEST DQUOT----------------"); | ||
1162 | cmn_err(CE_DEBUG, "---- dquot ID = %d", d->d_id); | ||
1163 | cmn_err(CE_DEBUG, "---- type = %s", XFS_QM_ISUDQ(d)? "USR" : "GRP"); | ||
1164 | cmn_err(CE_DEBUG, "---- fs = 0x%p", d->q_mount); | ||
1165 | cmn_err(CE_DEBUG, "---- bcount = %Lu (0x%x)", | ||
1166 | d->d_bcount, (int)d->d_bcount); | ||
1167 | cmn_err(CE_DEBUG, "---- icount = %Lu (0x%x)", | ||
1168 | d->d_icount, (int)d->d_icount); | ||
1169 | cmn_err(CE_DEBUG, "---------------------------"); | ||
1170 | } | ||
1171 | |||
1172 | STATIC void | ||
1173 | xfs_qm_dqtest_failed( | ||
1174 | xfs_dqtest_t *d, | ||
1175 | xfs_dquot_t *dqp, | ||
1176 | char *reason, | ||
1177 | xfs_qcnt_t a, | ||
1178 | xfs_qcnt_t b, | ||
1179 | int error) | ||
1180 | { | ||
1181 | qmtest_nfails++; | ||
1182 | if (error) | ||
1183 | cmn_err(CE_DEBUG, "quotacheck failed id=%d, err=%d\nreason: %s", | ||
1184 | INT_GET(d->d_id, ARCH_CONVERT), error, reason); | ||
1185 | else | ||
1186 | cmn_err(CE_DEBUG, "quotacheck failed id=%d (%s) [%d != %d]", | ||
1187 | INT_GET(d->d_id, ARCH_CONVERT), reason, (int)a, (int)b); | ||
1188 | xfs_qm_dqtest_print(d); | ||
1189 | if (dqp) | ||
1190 | xfs_qm_dqprint(dqp); | ||
1191 | } | ||
1192 | |||
1193 | STATIC int | ||
1194 | xfs_dqtest_cmp2( | ||
1195 | xfs_dqtest_t *d, | ||
1196 | xfs_dquot_t *dqp) | ||
1197 | { | ||
1198 | int err = 0; | ||
1199 | if (INT_GET(dqp->q_core.d_icount, ARCH_CONVERT) != d->d_icount) { | ||
1200 | xfs_qm_dqtest_failed(d, dqp, "icount mismatch", | ||
1201 | INT_GET(dqp->q_core.d_icount, ARCH_CONVERT), | ||
1202 | d->d_icount, 0); | ||
1203 | err++; | ||
1204 | } | ||
1205 | if (INT_GET(dqp->q_core.d_bcount, ARCH_CONVERT) != d->d_bcount) { | ||
1206 | xfs_qm_dqtest_failed(d, dqp, "bcount mismatch", | ||
1207 | INT_GET(dqp->q_core.d_bcount, ARCH_CONVERT), | ||
1208 | d->d_bcount, 0); | ||
1209 | err++; | ||
1210 | } | ||
1211 | if (INT_GET(dqp->q_core.d_blk_softlimit, ARCH_CONVERT) && | ||
1212 | INT_GET(dqp->q_core.d_bcount, ARCH_CONVERT) >= | ||
1213 | INT_GET(dqp->q_core.d_blk_softlimit, ARCH_CONVERT)) { | ||
1214 | if (!dqp->q_core.d_btimer && dqp->q_core.d_id) { | ||
1215 | cmn_err(CE_DEBUG, | ||
1216 | "%d [%s] [0x%p] BLK TIMER NOT STARTED", | ||
1217 | d->d_id, DQFLAGTO_TYPESTR(d), d->q_mount); | ||
1218 | err++; | ||
1219 | } | ||
1220 | } | ||
1221 | if (INT_GET(dqp->q_core.d_ino_softlimit, ARCH_CONVERT) && | ||
1222 | INT_GET(dqp->q_core.d_icount, ARCH_CONVERT) >= | ||
1223 | INT_GET(dqp->q_core.d_ino_softlimit, ARCH_CONVERT)) { | ||
1224 | if (!dqp->q_core.d_itimer && dqp->q_core.d_id) { | ||
1225 | cmn_err(CE_DEBUG, | ||
1226 | "%d [%s] [0x%p] INO TIMER NOT STARTED", | ||
1227 | d->d_id, DQFLAGTO_TYPESTR(d), d->q_mount); | ||
1228 | err++; | ||
1229 | } | ||
1230 | } | ||
1231 | #ifdef QUOTADEBUG | ||
1232 | if (!err) { | ||
1233 | cmn_err(CE_DEBUG, "%d [%s] [0x%p] qchecked", | ||
1234 | d->d_id, XFS_QM_ISUDQ(d) ? "USR" : "GRP", d->q_mount); | ||
1235 | } | ||
1236 | #endif | ||
1237 | return (err); | ||
1238 | } | ||
1239 | |||
1240 | STATIC void | ||
1241 | xfs_dqtest_cmp( | ||
1242 | xfs_dqtest_t *d) | ||
1243 | { | ||
1244 | xfs_dquot_t *dqp; | ||
1245 | int error; | ||
1246 | |||
1247 | /* xfs_qm_dqtest_print(d); */ | ||
1248 | if ((error = xfs_qm_dqget(d->q_mount, NULL, d->d_id, d->dq_flags, 0, | ||
1249 | &dqp))) { | ||
1250 | xfs_qm_dqtest_failed(d, NULL, "dqget failed", 0, 0, error); | ||
1251 | return; | ||
1252 | } | ||
1253 | xfs_dqtest_cmp2(d, dqp); | ||
1254 | xfs_qm_dqput(dqp); | ||
1255 | } | ||
1256 | |||
1257 | STATIC int | ||
1258 | xfs_qm_internalqcheck_dqget( | ||
1259 | xfs_mount_t *mp, | ||
1260 | xfs_dqid_t id, | ||
1261 | uint type, | ||
1262 | xfs_dqtest_t **O_dq) | ||
1263 | { | ||
1264 | xfs_dqtest_t *d; | ||
1265 | xfs_dqhash_t *h; | ||
1266 | |||
1267 | h = DQTEST_HASH(mp, id, type); | ||
1268 | for (d = (xfs_dqtest_t *) h->qh_next; d != NULL; | ||
1269 | d = (xfs_dqtest_t *) d->HL_NEXT) { | ||
1270 | /* DQTEST_LIST_PRINT(h, HL_NEXT, "@@@@@ dqtestlist @@@@@"); */ | ||
1271 | if (d->d_id == id && mp == d->q_mount) { | ||
1272 | *O_dq = d; | ||
1273 | return (0); | ||
1274 | } | ||
1275 | } | ||
1276 | d = kmem_zalloc(sizeof(xfs_dqtest_t), KM_SLEEP); | ||
1277 | d->dq_flags = type; | ||
1278 | d->d_id = id; | ||
1279 | d->q_mount = mp; | ||
1280 | d->q_hash = h; | ||
1281 | xfs_qm_hashinsert(h, d); | ||
1282 | *O_dq = d; | ||
1283 | return (0); | ||
1284 | } | ||
1285 | |||
1286 | STATIC void | ||
1287 | xfs_qm_internalqcheck_get_dquots( | ||
1288 | xfs_mount_t *mp, | ||
1289 | xfs_dqid_t uid, | ||
1290 | xfs_dqid_t gid, | ||
1291 | xfs_dqtest_t **ud, | ||
1292 | xfs_dqtest_t **gd) | ||
1293 | { | ||
1294 | if (XFS_IS_UQUOTA_ON(mp)) | ||
1295 | xfs_qm_internalqcheck_dqget(mp, uid, XFS_DQ_USER, ud); | ||
1296 | if (XFS_IS_GQUOTA_ON(mp)) | ||
1297 | xfs_qm_internalqcheck_dqget(mp, gid, XFS_DQ_GROUP, gd); | ||
1298 | } | ||
1299 | |||
1300 | |||
1301 | STATIC void | ||
1302 | xfs_qm_internalqcheck_dqadjust( | ||
1303 | xfs_inode_t *ip, | ||
1304 | xfs_dqtest_t *d) | ||
1305 | { | ||
1306 | d->d_icount++; | ||
1307 | d->d_bcount += (xfs_qcnt_t)ip->i_d.di_nblocks; | ||
1308 | } | ||
1309 | |||
1310 | STATIC int | ||
1311 | xfs_qm_internalqcheck_adjust( | ||
1312 | xfs_mount_t *mp, /* mount point for filesystem */ | ||
1313 | xfs_ino_t ino, /* inode number to get data for */ | ||
1314 | void __user *buffer, /* not used */ | ||
1315 | int ubsize, /* not used */ | ||
1316 | void *private_data, /* not used */ | ||
1317 | xfs_daddr_t bno, /* starting block of inode cluster */ | ||
1318 | int *ubused, /* not used */ | ||
1319 | void *dip, /* not used */ | ||
1320 | int *res) /* bulkstat result code */ | ||
1321 | { | ||
1322 | xfs_inode_t *ip; | ||
1323 | xfs_dqtest_t *ud, *gd; | ||
1324 | uint lock_flags; | ||
1325 | boolean_t ipreleased; | ||
1326 | int error; | ||
1327 | |||
1328 | ASSERT(XFS_IS_QUOTA_RUNNING(mp)); | ||
1329 | |||
1330 | if (ino == mp->m_sb.sb_uquotino || ino == mp->m_sb.sb_gquotino) { | ||
1331 | *res = BULKSTAT_RV_NOTHING; | ||
1332 | qdprintk("internalqcheck: ino=%llu, uqino=%llu, gqino=%llu\n", | ||
1333 | (unsigned long long) ino, | ||
1334 | (unsigned long long) mp->m_sb.sb_uquotino, | ||
1335 | (unsigned long long) mp->m_sb.sb_gquotino); | ||
1336 | return XFS_ERROR(EINVAL); | ||
1337 | } | ||
1338 | ipreleased = B_FALSE; | ||
1339 | again: | ||
1340 | lock_flags = XFS_ILOCK_SHARED; | ||
1341 | if ((error = xfs_iget(mp, NULL, ino, 0, lock_flags, &ip, bno))) { | ||
1342 | *res = BULKSTAT_RV_NOTHING; | ||
1343 | return (error); | ||
1344 | } | ||
1345 | |||
1346 | if (ip->i_d.di_mode == 0) { | ||
1347 | xfs_iput_new(ip, lock_flags); | ||
1348 | *res = BULKSTAT_RV_NOTHING; | ||
1349 | return XFS_ERROR(ENOENT); | ||
1350 | } | ||
1351 | |||
1352 | /* | ||
1353 | * This inode can have blocks after eof which can get released | ||
1354 | * when we send it to inactive. Since we don't check the dquot | ||
1355 | * until the after all our calculations are done, we must get rid | ||
1356 | * of those now. | ||
1357 | */ | ||
1358 | if (! ipreleased) { | ||
1359 | xfs_iput(ip, lock_flags); | ||
1360 | ipreleased = B_TRUE; | ||
1361 | goto again; | ||
1362 | } | ||
1363 | xfs_qm_internalqcheck_get_dquots(mp, | ||
1364 | (xfs_dqid_t) ip->i_d.di_uid, | ||
1365 | (xfs_dqid_t) ip->i_d.di_gid, | ||
1366 | &ud, &gd); | ||
1367 | if (XFS_IS_UQUOTA_ON(mp)) { | ||
1368 | ASSERT(ud); | ||
1369 | xfs_qm_internalqcheck_dqadjust(ip, ud); | ||
1370 | } | ||
1371 | if (XFS_IS_GQUOTA_ON(mp)) { | ||
1372 | ASSERT(gd); | ||
1373 | xfs_qm_internalqcheck_dqadjust(ip, gd); | ||
1374 | } | ||
1375 | xfs_iput(ip, lock_flags); | ||
1376 | *res = BULKSTAT_RV_DIDONE; | ||
1377 | return (0); | ||
1378 | } | ||
1379 | |||
1380 | |||
1381 | /* PRIVATE, debugging */ | ||
1382 | int | ||
1383 | xfs_qm_internalqcheck( | ||
1384 | xfs_mount_t *mp) | ||
1385 | { | ||
1386 | xfs_ino_t lastino; | ||
1387 | int done, count; | ||
1388 | int i; | ||
1389 | xfs_dqtest_t *d, *e; | ||
1390 | xfs_dqhash_t *h1; | ||
1391 | int error; | ||
1392 | |||
1393 | lastino = 0; | ||
1394 | qmtest_hashmask = 32; | ||
1395 | count = 5; | ||
1396 | done = 0; | ||
1397 | qmtest_nfails = 0; | ||
1398 | |||
1399 | if (! XFS_IS_QUOTA_ON(mp)) | ||
1400 | return XFS_ERROR(ESRCH); | ||
1401 | |||
1402 | xfs_log_force(mp, (xfs_lsn_t)0, XFS_LOG_FORCE | XFS_LOG_SYNC); | ||
1403 | XFS_bflush(mp->m_ddev_targp); | ||
1404 | xfs_log_force(mp, (xfs_lsn_t)0, XFS_LOG_FORCE | XFS_LOG_SYNC); | ||
1405 | XFS_bflush(mp->m_ddev_targp); | ||
1406 | |||
1407 | mutex_lock(&qcheck_lock, PINOD); | ||
1408 | /* There should be absolutely no quota activity while this | ||
1409 | is going on. */ | ||
1410 | qmtest_udqtab = kmem_zalloc(qmtest_hashmask * | ||
1411 | sizeof(xfs_dqhash_t), KM_SLEEP); | ||
1412 | qmtest_gdqtab = kmem_zalloc(qmtest_hashmask * | ||
1413 | sizeof(xfs_dqhash_t), KM_SLEEP); | ||
1414 | do { | ||
1415 | /* | ||
1416 | * Iterate thru all the inodes in the file system, | ||
1417 | * adjusting the corresponding dquot counters | ||
1418 | */ | ||
1419 | if ((error = xfs_bulkstat(mp, &lastino, &count, | ||
1420 | xfs_qm_internalqcheck_adjust, NULL, | ||
1421 | 0, NULL, BULKSTAT_FG_IGET, &done))) { | ||
1422 | break; | ||
1423 | } | ||
1424 | } while (! done); | ||
1425 | if (error) { | ||
1426 | cmn_err(CE_DEBUG, "Bulkstat returned error 0x%x", error); | ||
1427 | } | ||
1428 | cmn_err(CE_DEBUG, "Checking results against system dquots"); | ||
1429 | for (i = 0; i < qmtest_hashmask; i++) { | ||
1430 | h1 = &qmtest_udqtab[i]; | ||
1431 | for (d = (xfs_dqtest_t *) h1->qh_next; d != NULL; ) { | ||
1432 | xfs_dqtest_cmp(d); | ||
1433 | e = (xfs_dqtest_t *) d->HL_NEXT; | ||
1434 | kmem_free(d, sizeof(xfs_dqtest_t)); | ||
1435 | d = e; | ||
1436 | } | ||
1437 | h1 = &qmtest_gdqtab[i]; | ||
1438 | for (d = (xfs_dqtest_t *) h1->qh_next; d != NULL; ) { | ||
1439 | xfs_dqtest_cmp(d); | ||
1440 | e = (xfs_dqtest_t *) d->HL_NEXT; | ||
1441 | kmem_free(d, sizeof(xfs_dqtest_t)); | ||
1442 | d = e; | ||
1443 | } | ||
1444 | } | ||
1445 | |||
1446 | if (qmtest_nfails) { | ||
1447 | cmn_err(CE_DEBUG, "******** quotacheck failed ********"); | ||
1448 | cmn_err(CE_DEBUG, "failures = %d", qmtest_nfails); | ||
1449 | } else { | ||
1450 | cmn_err(CE_DEBUG, "******** quotacheck successful! ********"); | ||
1451 | } | ||
1452 | kmem_free(qmtest_udqtab, qmtest_hashmask * sizeof(xfs_dqhash_t)); | ||
1453 | kmem_free(qmtest_gdqtab, qmtest_hashmask * sizeof(xfs_dqhash_t)); | ||
1454 | mutex_unlock(&qcheck_lock); | ||
1455 | return (qmtest_nfails); | ||
1456 | } | ||
1457 | |||
1458 | #endif /* DEBUG */ | ||
diff --git a/fs/xfs/quota/xfs_quota_priv.h b/fs/xfs/quota/xfs_quota_priv.h new file mode 100644 index 000000000000..414b6004af21 --- /dev/null +++ b/fs/xfs/quota/xfs_quota_priv.h | |||
@@ -0,0 +1,192 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2000-2003 Silicon Graphics, Inc. All Rights Reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms of version 2 of the GNU General Public License as | ||
6 | * published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope that it would be useful, but | ||
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | * | ||
12 | * Further, this software is distributed without any warranty that it is | ||
13 | * free of the rightful claim of any third person regarding infringement | ||
14 | * or the like. Any license provided herein, whether implied or | ||
15 | * otherwise, applies only to this software file. Patent licenses, if | ||
16 | * any, provided herein do not apply to combinations of this program with | ||
17 | * other software, or any other product whatsoever. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License along | ||
20 | * with this program; if not, write the Free Software Foundation, Inc., 59 | ||
21 | * Temple Place - Suite 330, Boston MA 02111-1307, USA. | ||
22 | * | ||
23 | * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, | ||
24 | * Mountain View, CA 94043, or: | ||
25 | * | ||
26 | * http://www.sgi.com | ||
27 | * | ||
28 | * For further information regarding this notice, see: | ||
29 | * | ||
30 | * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ | ||
31 | */ | ||
32 | #ifndef __XFS_QUOTA_PRIV_H__ | ||
33 | #define __XFS_QUOTA_PRIV_H__ | ||
34 | |||
35 | /* | ||
36 | * Number of bmaps that we ask from bmapi when doing a quotacheck. | ||
37 | * We make this restriction to keep the memory usage to a minimum. | ||
38 | */ | ||
39 | #define XFS_DQITER_MAP_SIZE 10 | ||
40 | |||
41 | /* Number of dquots that fit in to a dquot block */ | ||
42 | #define XFS_QM_DQPERBLK(mp) ((mp)->m_quotainfo->qi_dqperchunk) | ||
43 | |||
44 | #define XFS_ISLOCKED_INODE(ip) (ismrlocked(&(ip)->i_lock, \ | ||
45 | MR_UPDATE | MR_ACCESS) != 0) | ||
46 | #define XFS_ISLOCKED_INODE_EXCL(ip) (ismrlocked(&(ip)->i_lock, \ | ||
47 | MR_UPDATE) != 0) | ||
48 | |||
49 | #define XFS_DQ_IS_ADDEDTO_TRX(t, d) ((d)->q_transp == (t)) | ||
50 | |||
51 | #define XFS_QI_MPLRECLAIMS(mp) ((mp)->m_quotainfo->qi_dqreclaims) | ||
52 | #define XFS_QI_UQIP(mp) ((mp)->m_quotainfo->qi_uquotaip) | ||
53 | #define XFS_QI_GQIP(mp) ((mp)->m_quotainfo->qi_gquotaip) | ||
54 | #define XFS_QI_DQCHUNKLEN(mp) ((mp)->m_quotainfo->qi_dqchunklen) | ||
55 | #define XFS_QI_BTIMELIMIT(mp) ((mp)->m_quotainfo->qi_btimelimit) | ||
56 | #define XFS_QI_RTBTIMELIMIT(mp) ((mp)->m_quotainfo->qi_rtbtimelimit) | ||
57 | #define XFS_QI_ITIMELIMIT(mp) ((mp)->m_quotainfo->qi_itimelimit) | ||
58 | #define XFS_QI_BWARNLIMIT(mp) ((mp)->m_quotainfo->qi_bwarnlimit) | ||
59 | #define XFS_QI_IWARNLIMIT(mp) ((mp)->m_quotainfo->qi_iwarnlimit) | ||
60 | #define XFS_QI_QOFFLOCK(mp) ((mp)->m_quotainfo->qi_quotaofflock) | ||
61 | |||
62 | #define XFS_QI_MPL_LIST(mp) ((mp)->m_quotainfo->qi_dqlist) | ||
63 | #define XFS_QI_MPLLOCK(mp) ((mp)->m_quotainfo->qi_dqlist.qh_lock) | ||
64 | #define XFS_QI_MPLNEXT(mp) ((mp)->m_quotainfo->qi_dqlist.qh_next) | ||
65 | #define XFS_QI_MPLNDQUOTS(mp) ((mp)->m_quotainfo->qi_dqlist.qh_nelems) | ||
66 | |||
67 | #define XQMLCK(h) (mutex_lock(&((h)->qh_lock), PINOD)) | ||
68 | #define XQMUNLCK(h) (mutex_unlock(&((h)->qh_lock))) | ||
69 | #ifdef DEBUG | ||
70 | struct xfs_dqhash; | ||
71 | static inline int XQMISLCKD(struct xfs_dqhash *h) | ||
72 | { | ||
73 | if (mutex_trylock(&h->qh_lock)) { | ||
74 | mutex_unlock(&h->qh_lock); | ||
75 | return 0; | ||
76 | } | ||
77 | return 1; | ||
78 | } | ||
79 | #endif | ||
80 | |||
81 | #define XFS_DQ_HASH_LOCK(h) XQMLCK(h) | ||
82 | #define XFS_DQ_HASH_UNLOCK(h) XQMUNLCK(h) | ||
83 | #define XFS_DQ_IS_HASH_LOCKED(h) XQMISLCKD(h) | ||
84 | |||
85 | #define xfs_qm_mplist_lock(mp) XQMLCK(&(XFS_QI_MPL_LIST(mp))) | ||
86 | #define xfs_qm_mplist_unlock(mp) XQMUNLCK(&(XFS_QI_MPL_LIST(mp))) | ||
87 | #define XFS_QM_IS_MPLIST_LOCKED(mp) XQMISLCKD(&(XFS_QI_MPL_LIST(mp))) | ||
88 | |||
89 | #define xfs_qm_freelist_lock(qm) XQMLCK(&((qm)->qm_dqfreelist)) | ||
90 | #define xfs_qm_freelist_unlock(qm) XQMUNLCK(&((qm)->qm_dqfreelist)) | ||
91 | #define XFS_QM_IS_FREELIST_LOCKED(qm) XQMISLCKD(&((qm)->qm_dqfreelist)) | ||
92 | |||
93 | /* | ||
94 | * Hash into a bucket in the dquot hash table, based on <mp, id>. | ||
95 | */ | ||
96 | #define XFS_DQ_HASHVAL(mp, id) (((__psunsigned_t)(mp) + \ | ||
97 | (__psunsigned_t)(id)) & \ | ||
98 | (xfs_Gqm->qm_dqhashmask - 1)) | ||
99 | #define XFS_DQ_HASH(mp, id, type) (type == XFS_DQ_USER ? \ | ||
100 | (xfs_Gqm->qm_usr_dqhtable + \ | ||
101 | XFS_DQ_HASHVAL(mp, id)) : \ | ||
102 | (xfs_Gqm->qm_grp_dqhtable + \ | ||
103 | XFS_DQ_HASHVAL(mp, id))) | ||
104 | #define XFS_IS_DQTYPE_ON(mp, type) (type == XFS_DQ_USER ? \ | ||
105 | XFS_IS_UQUOTA_ON(mp):XFS_IS_GQUOTA_ON(mp)) | ||
106 | #define XFS_IS_DQUOT_UNINITIALIZED(dqp) ( \ | ||
107 | !dqp->q_core.d_blk_hardlimit && \ | ||
108 | !dqp->q_core.d_blk_softlimit && \ | ||
109 | !dqp->q_core.d_rtb_hardlimit && \ | ||
110 | !dqp->q_core.d_rtb_softlimit && \ | ||
111 | !dqp->q_core.d_ino_hardlimit && \ | ||
112 | !dqp->q_core.d_ino_softlimit && \ | ||
113 | !dqp->q_core.d_bcount && \ | ||
114 | !dqp->q_core.d_rtbcount && \ | ||
115 | !dqp->q_core.d_icount) | ||
116 | |||
117 | #define HL_PREVP dq_hashlist.ql_prevp | ||
118 | #define HL_NEXT dq_hashlist.ql_next | ||
119 | #define MPL_PREVP dq_mplist.ql_prevp | ||
120 | #define MPL_NEXT dq_mplist.ql_next | ||
121 | |||
122 | |||
123 | #define _LIST_REMOVE(h, dqp, PVP, NXT) \ | ||
124 | { \ | ||
125 | xfs_dquot_t *d; \ | ||
126 | if (((d) = (dqp)->NXT)) \ | ||
127 | (d)->PVP = (dqp)->PVP; \ | ||
128 | *((dqp)->PVP) = d; \ | ||
129 | (dqp)->NXT = NULL; \ | ||
130 | (dqp)->PVP = NULL; \ | ||
131 | (h)->qh_version++; \ | ||
132 | (h)->qh_nelems--; \ | ||
133 | } | ||
134 | |||
135 | #define _LIST_INSERT(h, dqp, PVP, NXT) \ | ||
136 | { \ | ||
137 | xfs_dquot_t *d; \ | ||
138 | if (((d) = (h)->qh_next)) \ | ||
139 | (d)->PVP = &((dqp)->NXT); \ | ||
140 | (dqp)->NXT = d; \ | ||
141 | (dqp)->PVP = &((h)->qh_next); \ | ||
142 | (h)->qh_next = dqp; \ | ||
143 | (h)->qh_version++; \ | ||
144 | (h)->qh_nelems++; \ | ||
145 | } | ||
146 | |||
147 | #define FOREACH_DQUOT_IN_MP(dqp, mp) \ | ||
148 | for ((dqp) = XFS_QI_MPLNEXT(mp); (dqp) != NULL; (dqp) = (dqp)->MPL_NEXT) | ||
149 | |||
150 | #define FOREACH_DQUOT_IN_FREELIST(dqp, qlist) \ | ||
151 | for ((dqp) = (qlist)->qh_next; (dqp) != (xfs_dquot_t *)(qlist); \ | ||
152 | (dqp) = (dqp)->dq_flnext) | ||
153 | |||
154 | #define XQM_HASHLIST_INSERT(h, dqp) \ | ||
155 | _LIST_INSERT(h, dqp, HL_PREVP, HL_NEXT) | ||
156 | |||
157 | #define XQM_FREELIST_INSERT(h, dqp) \ | ||
158 | xfs_qm_freelist_append(h, dqp) | ||
159 | |||
160 | #define XQM_MPLIST_INSERT(h, dqp) \ | ||
161 | _LIST_INSERT(h, dqp, MPL_PREVP, MPL_NEXT) | ||
162 | |||
163 | #define XQM_HASHLIST_REMOVE(h, dqp) \ | ||
164 | _LIST_REMOVE(h, dqp, HL_PREVP, HL_NEXT) | ||
165 | #define XQM_FREELIST_REMOVE(dqp) \ | ||
166 | xfs_qm_freelist_unlink(dqp) | ||
167 | #define XQM_MPLIST_REMOVE(h, dqp) \ | ||
168 | { _LIST_REMOVE(h, dqp, MPL_PREVP, MPL_NEXT); \ | ||
169 | XFS_QI_MPLRECLAIMS((dqp)->q_mount)++; } | ||
170 | |||
171 | #define XFS_DQ_IS_LOGITEM_INITD(dqp) ((dqp)->q_logitem.qli_dquot == (dqp)) | ||
172 | |||
173 | #define XFS_QM_DQP_TO_DQACCT(tp, dqp) (XFS_QM_ISUDQ(dqp) ? \ | ||
174 | (tp)->t_dqinfo->dqa_usrdquots : \ | ||
175 | (tp)->t_dqinfo->dqa_grpdquots) | ||
176 | #define XFS_IS_SUSER_DQUOT(dqp) \ | ||
177 | (!((dqp)->q_core.d_id)) | ||
178 | |||
179 | #define XFS_PURGE_INODE(ip) \ | ||
180 | { \ | ||
181 | vmap_t dqvmap; \ | ||
182 | vnode_t *dqvp; \ | ||
183 | dqvp = XFS_ITOV(ip); \ | ||
184 | VMAP(dqvp, dqvmap); \ | ||
185 | VN_RELE(dqvp); \ | ||
186 | } | ||
187 | |||
188 | #define DQFLAGTO_TYPESTR(d) (((d)->dq_flags & XFS_DQ_USER) ? "USR" : \ | ||
189 | (((d)->dq_flags & XFS_DQ_GROUP) ? "GRP" : "???")) | ||
190 | #define DQFLAGTO_DIRTYSTR(d) (XFS_DQ_IS_DIRTY(d) ? "DIRTY" : "NOTDIRTY") | ||
191 | |||
192 | #endif /* __XFS_QUOTA_PRIV_H__ */ | ||
diff --git a/fs/xfs/quota/xfs_trans_dquot.c b/fs/xfs/quota/xfs_trans_dquot.c new file mode 100644 index 000000000000..149b2a1fd949 --- /dev/null +++ b/fs/xfs/quota/xfs_trans_dquot.c | |||
@@ -0,0 +1,941 @@ | |||
1 | /* | ||
2 | * Copyright (c) 2000-2002 Silicon Graphics, Inc. All Rights Reserved. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify it | ||
5 | * under the terms of version 2 of the GNU General Public License as | ||
6 | * published by the Free Software Foundation. | ||
7 | * | ||
8 | * This program is distributed in the hope that it would be useful, but | ||
9 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||
11 | * | ||
12 | * Further, this software is distributed without any warranty that it is | ||
13 | * free of the rightful claim of any third person regarding infringement | ||
14 | * or the like. Any license provided herein, whether implied or | ||
15 | * otherwise, applies only to this software file. Patent licenses, if | ||
16 | * any, provided herein do not apply to combinations of this program with | ||
17 | * other software, or any other product whatsoever. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License along | ||
20 | * with this program; if not, write the Free Software Foundation, Inc., 59 | ||
21 | * Temple Place - Suite 330, Boston MA 02111-1307, USA. | ||
22 | * | ||
23 | * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, | ||
24 | * Mountain View, CA 94043, or: | ||
25 | * | ||
26 | * http://www.sgi.com | ||
27 | * | ||
28 | * For further information regarding this notice, see: | ||
29 | * | ||
30 | * http://oss.sgi.com/projects/GenInfo/SGIGPLNoticeExplan/ | ||
31 | */ | ||
32 | |||
33 | #include "xfs.h" | ||
34 | #include "xfs_fs.h" | ||
35 | #include "xfs_inum.h" | ||
36 | #include "xfs_log.h" | ||
37 | #include "xfs_trans.h" | ||
38 | #include "xfs_sb.h" | ||
39 | #include "xfs_ag.h" | ||
40 | #include "xfs_dir.h" | ||
41 | #include "xfs_dir2.h" | ||
42 | #include "xfs_alloc.h" | ||
43 | #include "xfs_dmapi.h" | ||
44 | #include "xfs_quota.h" | ||
45 | #include "xfs_mount.h" | ||
46 | #include "xfs_alloc_btree.h" | ||
47 | #include "xfs_bmap_btree.h" | ||
48 | #include "xfs_ialloc_btree.h" | ||
49 | #include "xfs_btree.h" | ||
50 | #include "xfs_ialloc.h" | ||
51 | #include "xfs_attr_sf.h" | ||
52 | #include "xfs_dir_sf.h" | ||
53 | #include "xfs_dir2_sf.h" | ||
54 | #include "xfs_dinode.h" | ||
55 | #include "xfs_inode.h" | ||
56 | #include "xfs_bmap.h" | ||
57 | #include "xfs_bit.h" | ||
58 | #include "xfs_rtalloc.h" | ||
59 | #include "xfs_error.h" | ||
60 | #include "xfs_itable.h" | ||
61 | #include "xfs_rw.h" | ||
62 | #include "xfs_acl.h" | ||
63 | #include "xfs_cap.h" | ||
64 | #include "xfs_mac.h" | ||
65 | #include "xfs_attr.h" | ||
66 | #include "xfs_buf_item.h" | ||
67 | #include "xfs_trans_priv.h" | ||
68 | |||
69 | #include "xfs_qm.h" | ||
70 | |||
71 | STATIC void xfs_trans_alloc_dqinfo(xfs_trans_t *); | ||
72 | |||
73 | /* | ||
74 | * Add the locked dquot to the transaction. | ||
75 | * The dquot must be locked, and it cannot be associated with any | ||
76 | * transaction. | ||
77 | */ | ||
78 | void | ||
79 | xfs_trans_dqjoin( | ||
80 | xfs_trans_t *tp, | ||
81 | xfs_dquot_t *dqp) | ||
82 | { | ||
83 | xfs_dq_logitem_t *lp; | ||
84 | |||
85 | ASSERT(! XFS_DQ_IS_ADDEDTO_TRX(tp, dqp)); | ||
86 | ASSERT(XFS_DQ_IS_LOCKED(dqp)); | ||
87 | ASSERT(XFS_DQ_IS_LOGITEM_INITD(dqp)); | ||
88 | lp = &dqp->q_logitem; | ||
89 | |||
90 | /* | ||
91 | * Get a log_item_desc to point at the new item. | ||
92 | */ | ||
93 | (void) xfs_trans_add_item(tp, (xfs_log_item_t*)(lp)); | ||
94 | |||
95 | /* | ||
96 | * Initialize i_transp so we can later determine if this dquot is | ||
97 | * associated with this transaction. | ||
98 | */ | ||
99 | dqp->q_transp = tp; | ||
100 | } | ||
101 | |||
102 | |||
103 | /* | ||
104 | * This is called to mark the dquot as needing | ||
105 | * to be logged when the transaction is committed. The dquot must | ||
106 | * already be associated with the given transaction. | ||
107 | * Note that it marks the entire transaction as dirty. In the ordinary | ||
108 | * case, this gets called via xfs_trans_commit, after the transaction | ||
109 | * is already dirty. However, there's nothing stop this from getting | ||
110 | * called directly, as done by xfs_qm_scall_setqlim. Hence, the TRANS_DIRTY | ||
111 | * flag. | ||
112 | */ | ||
113 | void | ||
114 | xfs_trans_log_dquot( | ||
115 | xfs_trans_t *tp, | ||
116 | xfs_dquot_t *dqp) | ||
117 | { | ||
118 | xfs_log_item_desc_t *lidp; | ||
119 | |||
120 | ASSERT(XFS_DQ_IS_ADDEDTO_TRX(tp, dqp)); | ||
121 | ASSERT(XFS_DQ_IS_LOCKED(dqp)); | ||
122 | |||
123 | lidp = xfs_trans_find_item(tp, (xfs_log_item_t*)(&dqp->q_logitem)); | ||
124 | ASSERT(lidp != NULL); | ||
125 | |||
126 | tp->t_flags |= XFS_TRANS_DIRTY; | ||
127 | lidp->lid_flags |= XFS_LID_DIRTY; | ||
128 | } | ||
129 | |||
130 | /* | ||
131 | * Carry forward whatever is left of the quota blk reservation to | ||
132 | * the spanky new transaction | ||
133 | */ | ||
134 | STATIC void | ||
135 | xfs_trans_dup_dqinfo( | ||
136 | xfs_trans_t *otp, | ||
137 | xfs_trans_t *ntp) | ||
138 | { | ||
139 | xfs_dqtrx_t *oq, *nq; | ||
140 | int i,j; | ||
141 | xfs_dqtrx_t *oqa, *nqa; | ||
142 | |||
143 | if (!otp->t_dqinfo) | ||
144 | return; | ||
145 | |||
146 | xfs_trans_alloc_dqinfo(ntp); | ||
147 | oqa = otp->t_dqinfo->dqa_usrdquots; | ||
148 | nqa = ntp->t_dqinfo->dqa_usrdquots; | ||
149 | |||
150 | /* | ||
151 | * Because the quota blk reservation is carried forward, | ||
152 | * it is also necessary to carry forward the DQ_DIRTY flag. | ||
153 | */ | ||
154 | if(otp->t_flags & XFS_TRANS_DQ_DIRTY) | ||
155 | ntp->t_flags |= XFS_TRANS_DQ_DIRTY; | ||
156 | |||
157 | for (j = 0; j < 2; j++) { | ||
158 | for (i = 0; i < XFS_QM_TRANS_MAXDQS; i++) { | ||
159 | if (oqa[i].qt_dquot == NULL) | ||
160 | break; | ||
161 | oq = &oqa[i]; | ||
162 | nq = &nqa[i]; | ||
163 | |||
164 | nq->qt_dquot = oq->qt_dquot; | ||
165 | nq->qt_bcount_delta = nq->qt_icount_delta = 0; | ||
166 | nq->qt_rtbcount_delta = 0; | ||
167 | |||
168 | /* | ||
169 | * Transfer whatever is left of the reservations. | ||
170 | */ | ||
171 | nq->qt_blk_res = oq->qt_blk_res - oq->qt_blk_res_used; | ||
172 | oq->qt_blk_res = oq->qt_blk_res_used; | ||
173 | |||
174 | nq->qt_rtblk_res = oq->qt_rtblk_res - | ||
175 | oq->qt_rtblk_res_used; | ||
176 | oq->qt_rtblk_res = oq->qt_rtblk_res_used; | ||
177 | |||
178 | nq->qt_ino_res = oq->qt_ino_res - oq->qt_ino_res_used; | ||
179 | oq->qt_ino_res = oq->qt_ino_res_used; | ||
180 | |||
181 | } | ||
182 | oqa = otp->t_dqinfo->dqa_grpdquots; | ||
183 | nqa = ntp->t_dqinfo->dqa_grpdquots; | ||
184 | } | ||
185 | } | ||
186 | |||
187 | /* | ||
188 | * Wrap around mod_dquot to account for both user and group quotas. | ||
189 | */ | ||
190 | void | ||
191 | xfs_trans_mod_dquot_byino( | ||
192 | xfs_trans_t *tp, | ||
193 | xfs_inode_t *ip, | ||
194 | uint field, | ||
195 | long delta) | ||
196 | { | ||
197 | xfs_mount_t *mp; | ||
198 | |||
199 | ASSERT(tp); | ||
200 | mp = tp->t_mountp; | ||
201 | |||
202 | if (!XFS_IS_QUOTA_ON(mp) || | ||
203 | ip->i_ino == mp->m_sb.sb_uquotino || | ||
204 | ip->i_ino == mp->m_sb.sb_gquotino) | ||
205 | return; | ||
206 | |||
207 | if (tp->t_dqinfo == NULL) | ||
208 | xfs_trans_alloc_dqinfo(tp); | ||
209 | |||
210 | if (XFS_IS_UQUOTA_ON(mp) && ip->i_udquot) { | ||
211 | (void) xfs_trans_mod_dquot(tp, ip->i_udquot, field, delta); | ||
212 | } | ||
213 | if (XFS_IS_GQUOTA_ON(mp) && ip->i_gdquot) { | ||
214 | (void) xfs_trans_mod_dquot(tp, ip->i_gdquot, field, delta); | ||
215 | } | ||
216 | } | ||
217 | |||
218 | STATIC xfs_dqtrx_t * | ||
219 | xfs_trans_get_dqtrx( | ||
220 | xfs_trans_t *tp, | ||
221 | xfs_dquot_t *dqp) | ||
222 | { | ||
223 | int i; | ||
224 | xfs_dqtrx_t *qa; | ||
225 | |||
226 | for (i = 0; i < XFS_QM_TRANS_MAXDQS; i++) { | ||
227 | qa = XFS_QM_DQP_TO_DQACCT(tp, dqp); | ||
228 | |||
229 | if (qa[i].qt_dquot == NULL || | ||
230 | qa[i].qt_dquot == dqp) { | ||
231 | return (&qa[i]); | ||
232 | } | ||
233 | } | ||
234 | |||
235 | return (NULL); | ||
236 | } | ||
237 | |||
238 | /* | ||
239 | * Make the changes in the transaction structure. | ||
240 | * The moral equivalent to xfs_trans_mod_sb(). | ||
241 | * We don't touch any fields in the dquot, so we don't care | ||
242 | * if it's locked or not (most of the time it won't be). | ||
243 | */ | ||
244 | void | ||
245 | xfs_trans_mod_dquot( | ||
246 | xfs_trans_t *tp, | ||
247 | xfs_dquot_t *dqp, | ||
248 | uint field, | ||
249 | long delta) | ||
250 | { | ||
251 | xfs_dqtrx_t *qtrx; | ||
252 | |||
253 | ASSERT(tp); | ||
254 | qtrx = NULL; | ||
255 | |||
256 | if (tp->t_dqinfo == NULL) | ||
257 | xfs_trans_alloc_dqinfo(tp); | ||
258 | /* | ||
259 | * Find either the first free slot or the slot that belongs | ||
260 | * to this dquot. | ||
261 | */ | ||
262 | qtrx = xfs_trans_get_dqtrx(tp, dqp); | ||
263 | ASSERT(qtrx); | ||
264 | if (qtrx->qt_dquot == NULL) | ||
265 | qtrx->qt_dquot = dqp; | ||
266 | |||
267 | switch (field) { | ||
268 | |||
269 | /* | ||
270 | * regular disk blk reservation | ||
271 | */ | ||
272 | case XFS_TRANS_DQ_RES_BLKS: | ||
273 | qtrx->qt_blk_res += (ulong)delta; | ||
274 | break; | ||
275 | |||
276 | /* | ||
277 | * inode reservation | ||
278 | */ | ||
279 | case XFS_TRANS_DQ_RES_INOS: | ||
280 | qtrx->qt_ino_res += (ulong)delta; | ||
281 | break; | ||
282 | |||
283 | /* | ||
284 | * disk blocks used. | ||
285 | */ | ||
286 | case XFS_TRANS_DQ_BCOUNT: | ||
287 | if (qtrx->qt_blk_res && delta > 0) { | ||
288 | qtrx->qt_blk_res_used += (ulong)delta; | ||
289 | ASSERT(qtrx->qt_blk_res >= qtrx->qt_blk_res_used); | ||
290 | } | ||
291 | qtrx->qt_bcount_delta += delta; | ||
292 | break; | ||
293 | |||
294 | case XFS_TRANS_DQ_DELBCOUNT: | ||
295 | qtrx->qt_delbcnt_delta += delta; | ||
296 | break; | ||
297 | |||
298 | /* | ||
299 | * Inode Count | ||
300 | */ | ||
301 | case XFS_TRANS_DQ_ICOUNT: | ||
302 | if (qtrx->qt_ino_res && delta > 0) { | ||
303 | qtrx->qt_ino_res_used += (ulong)delta; | ||
304 | ASSERT(qtrx->qt_ino_res >= qtrx->qt_ino_res_used); | ||
305 | } | ||
306 | qtrx->qt_icount_delta += delta; | ||
307 | break; | ||
308 | |||
309 | /* | ||
310 | * rtblk reservation | ||
311 | */ | ||
312 | case XFS_TRANS_DQ_RES_RTBLKS: | ||
313 | qtrx->qt_rtblk_res += (ulong)delta; | ||
314 | break; | ||
315 | |||
316 | /* | ||
317 | * rtblk count | ||
318 | */ | ||
319 | case XFS_TRANS_DQ_RTBCOUNT: | ||
320 | if (qtrx->qt_rtblk_res && delta > 0) { | ||
321 | qtrx->qt_rtblk_res_used += (ulong)delta; | ||
322 | ASSERT(qtrx->qt_rtblk_res >= qtrx->qt_rtblk_res_used); | ||
323 | } | ||
324 | qtrx->qt_rtbcount_delta += delta; | ||
325 | break; | ||
326 | |||
327 | case XFS_TRANS_DQ_DELRTBCOUNT: | ||
328 | qtrx->qt_delrtb_delta += delta; | ||
329 | break; | ||
330 | |||
331 | default: | ||
332 | ASSERT(0); | ||
333 | } | ||
334 | tp->t_flags |= XFS_TRANS_DQ_DIRTY; | ||
335 | } | ||
336 | |||
337 | |||
338 | /* | ||
339 | * Given an array of dqtrx structures, lock all the dquots associated | ||
340 | * and join them to the transaction, provided they have been modified. | ||
341 | * We know that the highest number of dquots (of one type - usr OR grp), | ||
342 | * involved in a transaction is 2 and that both usr and grp combined - 3. | ||
343 | * So, we don't attempt to make this very generic. | ||
344 | */ | ||
345 | STATIC void | ||
346 | xfs_trans_dqlockedjoin( | ||
347 | xfs_trans_t *tp, | ||
348 | xfs_dqtrx_t *q) | ||
349 | { | ||
350 | ASSERT(q[0].qt_dquot != NULL); | ||
351 | if (q[1].qt_dquot == NULL) { | ||
352 | xfs_dqlock(q[0].qt_dquot); | ||
353 | xfs_trans_dqjoin(tp, q[0].qt_dquot); | ||
354 | } else { | ||
355 | ASSERT(XFS_QM_TRANS_MAXDQS == 2); | ||
356 | xfs_dqlock2(q[0].qt_dquot, q[1].qt_dquot); | ||
357 | xfs_trans_dqjoin(tp, q[0].qt_dquot); | ||
358 | xfs_trans_dqjoin(tp, q[1].qt_dquot); | ||
359 | } | ||
360 | } | ||
361 | |||
362 | |||
363 | /* | ||
364 | * Called by xfs_trans_commit() and similar in spirit to | ||
365 | * xfs_trans_apply_sb_deltas(). | ||
366 | * Go thru all the dquots belonging to this transaction and modify the | ||
367 | * INCORE dquot to reflect the actual usages. | ||
368 | * Unreserve just the reservations done by this transaction. | ||
369 | * dquot is still left locked at exit. | ||
370 | */ | ||
371 | void | ||
372 | xfs_trans_apply_dquot_deltas( | ||
373 | xfs_trans_t *tp) | ||
374 | { | ||
375 | int i, j; | ||
376 | xfs_dquot_t *dqp; | ||
377 | xfs_dqtrx_t *qtrx, *qa; | ||
378 | xfs_disk_dquot_t *d; | ||
379 | long totalbdelta; | ||
380 | long totalrtbdelta; | ||
381 | |||
382 | if (! (tp->t_flags & XFS_TRANS_DQ_DIRTY)) | ||
383 | return; | ||
384 | |||
385 | ASSERT(tp->t_dqinfo); | ||
386 | qa = tp->t_dqinfo->dqa_usrdquots; | ||
387 | for (j = 0; j < 2; j++) { | ||
388 | if (qa[0].qt_dquot == NULL) { | ||
389 | qa = tp->t_dqinfo->dqa_grpdquots; | ||
390 | continue; | ||
391 | } | ||
392 | |||
393 | /* | ||
394 | * Lock all of the dquots and join them to the transaction. | ||
395 | */ | ||
396 | xfs_trans_dqlockedjoin(tp, qa); | ||
397 | |||
398 | for (i = 0; i < XFS_QM_TRANS_MAXDQS; i++) { | ||
399 | qtrx = &qa[i]; | ||
400 | /* | ||
401 | * The array of dquots is filled | ||
402 | * sequentially, not sparsely. | ||
403 | */ | ||
404 | if ((dqp = qtrx->qt_dquot) == NULL) | ||
405 | break; | ||
406 | |||
407 | ASSERT(XFS_DQ_IS_LOCKED(dqp)); | ||
408 | ASSERT(XFS_DQ_IS_ADDEDTO_TRX(tp, dqp)); | ||
409 | |||
410 | /* | ||
411 | * adjust the actual number of blocks used | ||
412 | */ | ||
413 | d = &dqp->q_core; | ||
414 | |||
415 | /* | ||
416 | * The issue here is - sometimes we don't make a blkquota | ||
417 | * reservation intentionally to be fair to users | ||
418 | * (when the amount is small). On the other hand, | ||
419 | * delayed allocs do make reservations, but that's | ||
420 | * outside of a transaction, so we have no | ||
421 | * idea how much was really reserved. | ||
422 | * So, here we've accumulated delayed allocation blks and | ||
423 | * non-delay blks. The assumption is that the | ||
424 | * delayed ones are always reserved (outside of a | ||
425 | * transaction), and the others may or may not have | ||
426 | * quota reservations. | ||
427 | */ | ||
428 | totalbdelta = qtrx->qt_bcount_delta + | ||
429 | qtrx->qt_delbcnt_delta; | ||
430 | totalrtbdelta = qtrx->qt_rtbcount_delta + | ||
431 | qtrx->qt_delrtb_delta; | ||
432 | #ifdef QUOTADEBUG | ||
433 | if (totalbdelta < 0) | ||
434 | ASSERT(INT_GET(d->d_bcount, ARCH_CONVERT) >= | ||
435 | (xfs_qcnt_t) -totalbdelta); | ||
436 | |||
437 | if (totalrtbdelta < 0) | ||
438 | ASSERT(INT_GET(d->d_rtbcount, ARCH_CONVERT) >= | ||
439 | (xfs_qcnt_t) -totalrtbdelta); | ||
440 | |||
441 | if (qtrx->qt_icount_delta < 0) | ||
442 | ASSERT(INT_GET(d->d_icount, ARCH_CONVERT) >= | ||
443 | (xfs_qcnt_t) -qtrx->qt_icount_delta); | ||
444 | #endif | ||
445 | if (totalbdelta) | ||
446 | INT_MOD(d->d_bcount, ARCH_CONVERT, (xfs_qcnt_t)totalbdelta); | ||
447 | |||
448 | if (qtrx->qt_icount_delta) | ||
449 | INT_MOD(d->d_icount, ARCH_CONVERT, (xfs_qcnt_t)qtrx->qt_icount_delta); | ||
450 | |||
451 | if (totalrtbdelta) | ||
452 | INT_MOD(d->d_rtbcount, ARCH_CONVERT, (xfs_qcnt_t)totalrtbdelta); | ||
453 | |||
454 | /* | ||
455 | * Get any default limits in use. | ||
456 | * Start/reset the timer(s) if needed. | ||
457 | */ | ||
458 | if (d->d_id) { | ||
459 | xfs_qm_adjust_dqlimits(tp->t_mountp, d); | ||
460 | xfs_qm_adjust_dqtimers(tp->t_mountp, d); | ||
461 | } | ||
462 | |||
463 | dqp->dq_flags |= XFS_DQ_DIRTY; | ||
464 | /* | ||
465 | * add this to the list of items to get logged | ||
466 | */ | ||
467 | xfs_trans_log_dquot(tp, dqp); | ||
468 | /* | ||
469 | * Take off what's left of the original reservation. | ||
470 | * In case of delayed allocations, there's no | ||
471 | * reservation that a transaction structure knows of. | ||
472 | */ | ||
473 | if (qtrx->qt_blk_res != 0) { | ||
474 | if (qtrx->qt_blk_res != qtrx->qt_blk_res_used) { | ||
475 | if (qtrx->qt_blk_res > | ||
476 | qtrx->qt_blk_res_used) | ||
477 | dqp->q_res_bcount -= (xfs_qcnt_t) | ||
478 | (qtrx->qt_blk_res - | ||
479 | qtrx->qt_blk_res_used); | ||
480 | else | ||
481 | dqp->q_res_bcount -= (xfs_qcnt_t) | ||
482 | (qtrx->qt_blk_res_used - | ||
483 | qtrx->qt_blk_res); | ||
484 | } | ||
485 | } else { | ||
486 | /* | ||
487 | * These blks were never reserved, either inside | ||
488 | * a transaction or outside one (in a delayed | ||
489 | * allocation). Also, this isn't always a | ||
490 | * negative number since we sometimes | ||
491 | * deliberately skip quota reservations. | ||
492 | */ | ||
493 | if (qtrx->qt_bcount_delta) { | ||
494 | dqp->q_res_bcount += | ||
495 | (xfs_qcnt_t)qtrx->qt_bcount_delta; | ||
496 | } | ||
497 | } | ||
498 | /* | ||
499 | * Adjust the RT reservation. | ||
500 | */ | ||
501 | if (qtrx->qt_rtblk_res != 0) { | ||
502 | if (qtrx->qt_blk_res != qtrx->qt_blk_res_used) { | ||
503 | if (qtrx->qt_rtblk_res > | ||
504 | qtrx->qt_rtblk_res_used) | ||
505 | dqp->q_res_rtbcount -= (xfs_qcnt_t) | ||
506 | (qtrx->qt_rtblk_res - | ||
507 | qtrx->qt_rtblk_res_used); | ||
508 | else | ||
509 | dqp->q_res_rtbcount -= (xfs_qcnt_t) | ||
510 | (qtrx->qt_rtblk_res_used - | ||
511 | qtrx->qt_rtblk_res); | ||
512 | } | ||
513 | } else { | ||
514 | if (qtrx->qt_rtbcount_delta) | ||
515 | dqp->q_res_rtbcount += | ||
516 | (xfs_qcnt_t)qtrx->qt_rtbcount_delta; | ||
517 | } | ||
518 | |||
519 | /* | ||
520 | * Adjust the inode reservation. | ||
521 | */ | ||
522 | if (qtrx->qt_ino_res != 0) { | ||
523 | ASSERT(qtrx->qt_ino_res >= | ||
524 | qtrx->qt_ino_res_used); | ||
525 | if (qtrx->qt_ino_res > qtrx->qt_ino_res_used) | ||
526 | dqp->q_res_icount -= (xfs_qcnt_t) | ||
527 | (qtrx->qt_ino_res - | ||
528 | qtrx->qt_ino_res_used); | ||
529 | } else { | ||
530 | if (qtrx->qt_icount_delta) | ||
531 | dqp->q_res_icount += | ||
532 | (xfs_qcnt_t)qtrx->qt_icount_delta; | ||
533 | } | ||
534 | |||
535 | |||
536 | #ifdef QUOTADEBUG | ||
537 | if (qtrx->qt_rtblk_res != 0) | ||
538 | cmn_err(CE_DEBUG, "RT res %d for 0x%p\n", | ||
539 | (int) qtrx->qt_rtblk_res, dqp); | ||
540 | #endif | ||
541 | ASSERT(dqp->q_res_bcount >= | ||
542 | INT_GET(dqp->q_core.d_bcount, ARCH_CONVERT)); | ||
543 | ASSERT(dqp->q_res_icount >= | ||
544 | INT_GET(dqp->q_core.d_icount, ARCH_CONVERT)); | ||
545 | ASSERT(dqp->q_res_rtbcount >= | ||
546 | INT_GET(dqp->q_core.d_rtbcount, ARCH_CONVERT)); | ||
547 | } | ||
548 | /* | ||
549 | * Do the group quotas next | ||
550 | */ | ||
551 | qa = tp->t_dqinfo->dqa_grpdquots; | ||
552 | } | ||
553 | } | ||
554 | |||
555 | /* | ||
556 | * Release the reservations, and adjust the dquots accordingly. | ||
557 | * This is called only when the transaction is being aborted. If by | ||
558 | * any chance we have done dquot modifications incore (ie. deltas) already, | ||
559 | * we simply throw those away, since that's the expected behavior | ||
560 | * when a transaction is curtailed without a commit. | ||
561 | */ | ||
562 | STATIC void | ||
563 | xfs_trans_unreserve_and_mod_dquots( | ||
564 | xfs_trans_t *tp) | ||
565 | { | ||
566 | int i, j; | ||
567 | xfs_dquot_t *dqp; | ||
568 | xfs_dqtrx_t *qtrx, *qa; | ||
569 | boolean_t locked; | ||
570 | |||
571 | if (!tp->t_dqinfo || !(tp->t_flags & XFS_TRANS_DQ_DIRTY)) | ||
572 | return; | ||
573 | |||
574 | qa = tp->t_dqinfo->dqa_usrdquots; | ||
575 | |||
576 | for (j = 0; j < 2; j++) { | ||
577 | for (i = 0; i < XFS_QM_TRANS_MAXDQS; i++) { | ||
578 | qtrx = &qa[i]; | ||
579 | /* | ||
580 | * We assume that the array of dquots is filled | ||
581 | * sequentially, not sparsely. | ||
582 | */ | ||
583 | if ((dqp = qtrx->qt_dquot) == NULL) | ||
584 | break; | ||
585 | /* | ||
586 | * Unreserve the original reservation. We don't care | ||
587 | * about the number of blocks used field, or deltas. | ||
588 | * Also we don't bother to zero the fields. | ||
589 | */ | ||
590 | locked = B_FALSE; | ||
591 | if (qtrx->qt_blk_res) { | ||
592 | xfs_dqlock(dqp); | ||
593 | locked = B_TRUE; | ||
594 | dqp->q_res_bcount -= | ||
595 | (xfs_qcnt_t)qtrx->qt_blk_res; | ||
596 | } | ||
597 | if (qtrx->qt_ino_res) { | ||
598 | if (!locked) { | ||
599 | xfs_dqlock(dqp); | ||
600 | locked = B_TRUE; | ||
601 | } | ||
602 | dqp->q_res_icount -= | ||
603 | (xfs_qcnt_t)qtrx->qt_ino_res; | ||
604 | } | ||
605 | |||
606 | if (qtrx->qt_rtblk_res) { | ||
607 | if (!locked) { | ||
608 | xfs_dqlock(dqp); | ||
609 | locked = B_TRUE; | ||
610 | } | ||
611 | dqp->q_res_rtbcount -= | ||
612 | (xfs_qcnt_t)qtrx->qt_rtblk_res; | ||
613 | } | ||
614 | if (locked) | ||
615 | xfs_dqunlock(dqp); | ||
616 | |||
617 | } | ||
618 | qa = tp->t_dqinfo->dqa_grpdquots; | ||
619 | } | ||
620 | } | ||
621 | |||
622 | /* | ||
623 | * This reserves disk blocks and inodes against a dquot. | ||
624 | * Flags indicate if the dquot is to be locked here and also | ||
625 | * if the blk reservation is for RT or regular blocks. | ||
626 | * Sending in XFS_QMOPT_FORCE_RES flag skips the quota check. | ||
627 | * Returns EDQUOT if quota is exceeded. | ||
628 | */ | ||
629 | STATIC int | ||
630 | xfs_trans_dqresv( | ||
631 | xfs_trans_t *tp, | ||
632 | xfs_mount_t *mp, | ||
633 | xfs_dquot_t *dqp, | ||
634 | long nblks, | ||
635 | long ninos, | ||
636 | uint flags) | ||
637 | { | ||
638 | int error; | ||
639 | xfs_qcnt_t hardlimit; | ||
640 | xfs_qcnt_t softlimit; | ||
641 | time_t btimer; | ||
642 | xfs_qcnt_t *resbcountp; | ||
643 | xfs_quotainfo_t *q = mp->m_quotainfo; | ||
644 | |||
645 | if (! (flags & XFS_QMOPT_DQLOCK)) { | ||
646 | xfs_dqlock(dqp); | ||
647 | } | ||
648 | ASSERT(XFS_DQ_IS_LOCKED(dqp)); | ||
649 | if (flags & XFS_TRANS_DQ_RES_BLKS) { | ||
650 | hardlimit = INT_GET(dqp->q_core.d_blk_hardlimit, ARCH_CONVERT); | ||
651 | if (!hardlimit) | ||
652 | hardlimit = q->qi_bhardlimit; | ||
653 | softlimit = INT_GET(dqp->q_core.d_blk_softlimit, ARCH_CONVERT); | ||
654 | if (!softlimit) | ||
655 | softlimit = q->qi_bsoftlimit; | ||
656 | btimer = INT_GET(dqp->q_core.d_btimer, ARCH_CONVERT); | ||
657 | resbcountp = &dqp->q_res_bcount; | ||
658 | } else { | ||
659 | ASSERT(flags & XFS_TRANS_DQ_RES_RTBLKS); | ||
660 | hardlimit = INT_GET(dqp->q_core.d_rtb_hardlimit, ARCH_CONVERT); | ||
661 | if (!hardlimit) | ||
662 | hardlimit = q->qi_rtbhardlimit; | ||
663 | softlimit = INT_GET(dqp->q_core.d_rtb_softlimit, ARCH_CONVERT); | ||
664 | if (!softlimit) | ||
665 | softlimit = q->qi_rtbsoftlimit; | ||
666 | btimer = INT_GET(dqp->q_core.d_rtbtimer, ARCH_CONVERT); | ||
667 | resbcountp = &dqp->q_res_rtbcount; | ||
668 | } | ||
669 | error = 0; | ||
670 | |||
671 | if ((flags & XFS_QMOPT_FORCE_RES) == 0 && | ||
672 | dqp->q_core.d_id && | ||
673 | XFS_IS_QUOTA_ENFORCED(dqp->q_mount)) { | ||
674 | #ifdef QUOTADEBUG | ||
675 | cmn_err(CE_DEBUG, "BLK Res: nblks=%ld + resbcount=%Ld" | ||
676 | " > hardlimit=%Ld?", nblks, *resbcountp, hardlimit); | ||
677 | #endif | ||
678 | if (nblks > 0) { | ||
679 | /* | ||
680 | * dquot is locked already. See if we'd go over the | ||
681 | * hardlimit or exceed the timelimit if we allocate | ||
682 | * nblks. | ||
683 | */ | ||
684 | if (hardlimit > 0ULL && | ||
685 | (hardlimit <= nblks + *resbcountp)) { | ||
686 | error = EDQUOT; | ||
687 | goto error_return; | ||
688 | } | ||
689 | |||
690 | if (softlimit > 0ULL && | ||
691 | (softlimit <= nblks + *resbcountp)) { | ||
692 | /* | ||
693 | * If timer or warnings has expired, | ||
694 | * return EDQUOT | ||
695 | */ | ||
696 | if ((btimer != 0 && get_seconds() > btimer) || | ||
697 | (dqp->q_core.d_bwarns && | ||
698 | INT_GET(dqp->q_core.d_bwarns, ARCH_CONVERT) >= | ||
699 | XFS_QI_BWARNLIMIT(dqp->q_mount))) { | ||
700 | error = EDQUOT; | ||
701 | goto error_return; | ||
702 | } | ||
703 | } | ||
704 | } | ||
705 | if (ninos > 0) { | ||
706 | hardlimit = INT_GET(dqp->q_core.d_ino_hardlimit, ARCH_CONVERT); | ||
707 | if (!hardlimit) | ||
708 | hardlimit = q->qi_ihardlimit; | ||
709 | softlimit = INT_GET(dqp->q_core.d_ino_softlimit, ARCH_CONVERT); | ||
710 | if (!softlimit) | ||
711 | softlimit = q->qi_isoftlimit; | ||
712 | if (hardlimit > 0ULL && | ||
713 | INT_GET(dqp->q_core.d_icount, ARCH_CONVERT) >= hardlimit) { | ||
714 | error = EDQUOT; | ||
715 | goto error_return; | ||
716 | } else if (softlimit > 0ULL && | ||
717 | INT_GET(dqp->q_core.d_icount, ARCH_CONVERT) >= softlimit) { | ||
718 | /* | ||
719 | * If timer or warnings has expired, | ||
720 | * return EDQUOT | ||
721 | */ | ||
722 | if ((dqp->q_core.d_itimer && | ||
723 | get_seconds() > INT_GET(dqp->q_core.d_itimer, ARCH_CONVERT)) || | ||
724 | (dqp->q_core.d_iwarns && | ||
725 | INT_GET(dqp->q_core.d_iwarns, ARCH_CONVERT) >= | ||
726 | XFS_QI_IWARNLIMIT(dqp->q_mount))) { | ||
727 | error = EDQUOT; | ||
728 | goto error_return; | ||
729 | } | ||
730 | } | ||
731 | } | ||
732 | } | ||
733 | |||
734 | /* | ||
735 | * Change the reservation, but not the actual usage. | ||
736 | * Note that q_res_bcount = q_core.d_bcount + resv | ||
737 | */ | ||
738 | (*resbcountp) += (xfs_qcnt_t)nblks; | ||
739 | if (ninos != 0) | ||
740 | dqp->q_res_icount += (xfs_qcnt_t)ninos; | ||
741 | |||
742 | /* | ||
743 | * note the reservation amt in the trans struct too, | ||
744 | * so that the transaction knows how much was reserved by | ||
745 | * it against this particular dquot. | ||
746 | * We don't do this when we are reserving for a delayed allocation, | ||
747 | * because we don't have the luxury of a transaction envelope then. | ||
748 | */ | ||
749 | if (tp) { | ||
750 | ASSERT(tp->t_dqinfo); | ||
751 | ASSERT(flags & XFS_QMOPT_RESBLK_MASK); | ||
752 | if (nblks != 0) | ||
753 | xfs_trans_mod_dquot(tp, dqp, | ||
754 | flags & XFS_QMOPT_RESBLK_MASK, | ||
755 | nblks); | ||
756 | if (ninos != 0) | ||
757 | xfs_trans_mod_dquot(tp, dqp, | ||
758 | XFS_TRANS_DQ_RES_INOS, | ||
759 | ninos); | ||
760 | } | ||
761 | ASSERT(dqp->q_res_bcount >= INT_GET(dqp->q_core.d_bcount, ARCH_CONVERT)); | ||
762 | ASSERT(dqp->q_res_rtbcount >= INT_GET(dqp->q_core.d_rtbcount, ARCH_CONVERT)); | ||
763 | ASSERT(dqp->q_res_icount >= INT_GET(dqp->q_core.d_icount, ARCH_CONVERT)); | ||
764 | |||
765 | error_return: | ||
766 | if (! (flags & XFS_QMOPT_DQLOCK)) { | ||
767 | xfs_dqunlock(dqp); | ||
768 | } | ||
769 | return (error); | ||
770 | } | ||
771 | |||
772 | |||
773 | /* | ||
774 | * Given a dquot(s), make disk block and/or inode reservations against them. | ||
775 | * The fact that this does the reservation against both the usr and | ||
776 | * grp quotas is important, because this follows a both-or-nothing | ||
777 | * approach. | ||
778 | * | ||
779 | * flags = XFS_QMOPT_DQLOCK indicate if dquot(s) need to be locked. | ||
780 | * XFS_QMOPT_FORCE_RES evades limit enforcement. Used by chown. | ||
781 | * XFS_TRANS_DQ_RES_BLKS reserves regular disk blocks | ||
782 | * XFS_TRANS_DQ_RES_RTBLKS reserves realtime disk blocks | ||
783 | * dquots are unlocked on return, if they were not locked by caller. | ||
784 | */ | ||
785 | int | ||
786 | xfs_trans_reserve_quota_bydquots( | ||
787 | xfs_trans_t *tp, | ||
788 | xfs_mount_t *mp, | ||
789 | xfs_dquot_t *udqp, | ||
790 | xfs_dquot_t *gdqp, | ||
791 | long nblks, | ||
792 | long ninos, | ||
793 | uint flags) | ||
794 | { | ||
795 | int resvd; | ||
796 | |||
797 | if (! XFS_IS_QUOTA_ON(mp)) | ||
798 | return (0); | ||
799 | |||
800 | if (tp && tp->t_dqinfo == NULL) | ||
801 | xfs_trans_alloc_dqinfo(tp); | ||
802 | |||
803 | ASSERT(flags & XFS_QMOPT_RESBLK_MASK); | ||
804 | resvd = 0; | ||
805 | |||
806 | if (udqp) { | ||
807 | if (xfs_trans_dqresv(tp, mp, udqp, nblks, ninos, flags)) | ||
808 | return (EDQUOT); | ||
809 | resvd = 1; | ||
810 | } | ||
811 | |||
812 | if (gdqp) { | ||
813 | if (xfs_trans_dqresv(tp, mp, gdqp, nblks, ninos, flags)) { | ||
814 | /* | ||
815 | * can't do it, so backout previous reservation | ||
816 | */ | ||
817 | if (resvd) { | ||
818 | flags |= XFS_QMOPT_FORCE_RES; | ||
819 | xfs_trans_dqresv(tp, mp, udqp, | ||
820 | -nblks, -ninos, flags); | ||
821 | } | ||
822 | return (EDQUOT); | ||
823 | } | ||
824 | } | ||
825 | |||
826 | /* | ||
827 | * Didnt change anything critical, so, no need to log | ||
828 | */ | ||
829 | return (0); | ||
830 | } | ||
831 | |||
832 | |||
833 | /* | ||
834 | * Lock the dquot and change the reservation if we can. | ||
835 | * This doesn't change the actual usage, just the reservation. | ||
836 | * The inode sent in is locked. | ||
837 | * | ||
838 | * Returns 0 on success, EDQUOT or other errors otherwise | ||
839 | */ | ||
840 | STATIC int | ||
841 | xfs_trans_reserve_quota_nblks( | ||
842 | xfs_trans_t *tp, | ||
843 | xfs_mount_t *mp, | ||
844 | xfs_inode_t *ip, | ||
845 | long nblks, | ||
846 | long ninos, | ||
847 | uint type) | ||
848 | { | ||
849 | int error; | ||
850 | |||
851 | if (!XFS_IS_QUOTA_ON(mp)) | ||
852 | return (0); | ||
853 | |||
854 | ASSERT(ip->i_ino != mp->m_sb.sb_uquotino); | ||
855 | ASSERT(ip->i_ino != mp->m_sb.sb_gquotino); | ||
856 | |||
857 | ASSERT(XFS_ISLOCKED_INODE_EXCL(ip)); | ||
858 | ASSERT(XFS_IS_QUOTA_RUNNING(ip->i_mount)); | ||
859 | ASSERT((type & ~XFS_QMOPT_FORCE_RES) == XFS_TRANS_DQ_RES_RTBLKS || | ||
860 | (type & ~XFS_QMOPT_FORCE_RES) == XFS_TRANS_DQ_RES_BLKS); | ||
861 | |||
862 | /* | ||
863 | * Reserve nblks against these dquots, with trans as the mediator. | ||
864 | */ | ||
865 | error = xfs_trans_reserve_quota_bydquots(tp, mp, | ||
866 | ip->i_udquot, ip->i_gdquot, | ||
867 | nblks, ninos, | ||
868 | type); | ||
869 | return (error); | ||
870 | } | ||
871 | |||
872 | /* | ||
873 | * This routine is called to allocate a quotaoff log item. | ||
874 | */ | ||
875 | xfs_qoff_logitem_t * | ||
876 | xfs_trans_get_qoff_item( | ||
877 | xfs_trans_t *tp, | ||
878 | xfs_qoff_logitem_t *startqoff, | ||
879 | uint flags) | ||
880 | { | ||
881 | xfs_qoff_logitem_t *q; | ||
882 | |||
883 | ASSERT(tp != NULL); | ||
884 | |||
885 | q = xfs_qm_qoff_logitem_init(tp->t_mountp, startqoff, flags); | ||
886 | ASSERT(q != NULL); | ||
887 | |||
888 | /* | ||
889 | * Get a log_item_desc to point at the new item. | ||
890 | */ | ||
891 | (void) xfs_trans_add_item(tp, (xfs_log_item_t*)q); | ||
892 | |||
893 | return (q); | ||
894 | } | ||
895 | |||
896 | |||
897 | /* | ||
898 | * This is called to mark the quotaoff logitem as needing | ||
899 | * to be logged when the transaction is committed. The logitem must | ||
900 | * already be associated with the given transaction. | ||
901 | */ | ||
902 | void | ||
903 | xfs_trans_log_quotaoff_item( | ||
904 | xfs_trans_t *tp, | ||
905 | xfs_qoff_logitem_t *qlp) | ||
906 | { | ||
907 | xfs_log_item_desc_t *lidp; | ||
908 | |||
909 | lidp = xfs_trans_find_item(tp, (xfs_log_item_t *)qlp); | ||
910 | ASSERT(lidp != NULL); | ||
911 | |||
912 | tp->t_flags |= XFS_TRANS_DIRTY; | ||
913 | lidp->lid_flags |= XFS_LID_DIRTY; | ||
914 | } | ||
915 | |||
916 | STATIC void | ||
917 | xfs_trans_alloc_dqinfo( | ||
918 | xfs_trans_t *tp) | ||
919 | { | ||
920 | (tp)->t_dqinfo = kmem_zone_zalloc(xfs_Gqm->qm_dqtrxzone, KM_SLEEP); | ||
921 | } | ||
922 | |||
923 | STATIC void | ||
924 | xfs_trans_free_dqinfo( | ||
925 | xfs_trans_t *tp) | ||
926 | { | ||
927 | if (!tp->t_dqinfo) | ||
928 | return; | ||
929 | kmem_zone_free(xfs_Gqm->qm_dqtrxzone, (tp)->t_dqinfo); | ||
930 | (tp)->t_dqinfo = NULL; | ||
931 | } | ||
932 | |||
933 | xfs_dqtrxops_t xfs_trans_dquot_ops = { | ||
934 | .qo_dup_dqinfo = xfs_trans_dup_dqinfo, | ||
935 | .qo_free_dqinfo = xfs_trans_free_dqinfo, | ||
936 | .qo_mod_dquot_byino = xfs_trans_mod_dquot_byino, | ||
937 | .qo_apply_dquot_deltas = xfs_trans_apply_dquot_deltas, | ||
938 | .qo_reserve_quota_nblks = xfs_trans_reserve_quota_nblks, | ||
939 | .qo_reserve_quota_bydquots = xfs_trans_reserve_quota_bydquots, | ||
940 | .qo_unreserve_and_mod_dquots = xfs_trans_unreserve_and_mod_dquots, | ||
941 | }; | ||