diff options
author | Dave Chinner <david@fromorbit.com> | 2014-05-14 19:38:15 -0400 |
---|---|---|
committer | Dave Chinner <david@fromorbit.com> | 2014-05-14 19:38:15 -0400 |
commit | ff14ee42a038cf48263ac8d2eca5d30196554b82 (patch) | |
tree | cee5ba7a2b08d7e4f48d974d08ecfc40c8bd1308 /fs/xfs/xfs_qm.c | |
parent | b76769294ba400415fc44038c21cc2df86f9a28b (diff) | |
parent | 8cfcc3e565bf15870efe801368a25ca98092e6e7 (diff) |
Merge branch 'xfs-misc-fixes-1-for-3.16' into for-next
Diffstat (limited to 'fs/xfs/xfs_qm.c')
-rw-r--r-- | fs/xfs/xfs_qm.c | 214 |
1 files changed, 21 insertions, 193 deletions
diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index 061fad7ca820..6d26759c779a 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c | |||
@@ -193,47 +193,6 @@ xfs_qm_dqpurge( | |||
193 | } | 193 | } |
194 | 194 | ||
195 | /* | 195 | /* |
196 | * Release the group or project dquot pointers the user dquots maybe carrying | ||
197 | * around as a hint, and proceed to purge the user dquot cache if requested. | ||
198 | */ | ||
199 | STATIC int | ||
200 | xfs_qm_dqpurge_hints( | ||
201 | struct xfs_dquot *dqp, | ||
202 | void *data) | ||
203 | { | ||
204 | struct xfs_dquot *gdqp = NULL; | ||
205 | struct xfs_dquot *pdqp = NULL; | ||
206 | uint flags = *((uint *)data); | ||
207 | |||
208 | xfs_dqlock(dqp); | ||
209 | if (dqp->dq_flags & XFS_DQ_FREEING) { | ||
210 | xfs_dqunlock(dqp); | ||
211 | return EAGAIN; | ||
212 | } | ||
213 | |||
214 | /* If this quota has a hint attached, prepare for releasing it now */ | ||
215 | gdqp = dqp->q_gdquot; | ||
216 | if (gdqp) | ||
217 | dqp->q_gdquot = NULL; | ||
218 | |||
219 | pdqp = dqp->q_pdquot; | ||
220 | if (pdqp) | ||
221 | dqp->q_pdquot = NULL; | ||
222 | |||
223 | xfs_dqunlock(dqp); | ||
224 | |||
225 | if (gdqp) | ||
226 | xfs_qm_dqrele(gdqp); | ||
227 | if (pdqp) | ||
228 | xfs_qm_dqrele(pdqp); | ||
229 | |||
230 | if (flags & XFS_QMOPT_UQUOTA) | ||
231 | return xfs_qm_dqpurge(dqp, NULL); | ||
232 | |||
233 | return 0; | ||
234 | } | ||
235 | |||
236 | /* | ||
237 | * Purge the dquot cache. | 196 | * Purge the dquot cache. |
238 | */ | 197 | */ |
239 | void | 198 | void |
@@ -241,18 +200,8 @@ xfs_qm_dqpurge_all( | |||
241 | struct xfs_mount *mp, | 200 | struct xfs_mount *mp, |
242 | uint flags) | 201 | uint flags) |
243 | { | 202 | { |
244 | /* | 203 | if (flags & XFS_QMOPT_UQUOTA) |
245 | * We have to release group/project dquot hint(s) from the user dquot | 204 | xfs_qm_dquot_walk(mp, XFS_DQ_USER, xfs_qm_dqpurge, NULL); |
246 | * at first if they are there, otherwise we would run into an infinite | ||
247 | * loop while walking through radix tree to purge other type of dquots | ||
248 | * since their refcount is not zero if the user dquot refers to them | ||
249 | * as hint. | ||
250 | * | ||
251 | * Call the special xfs_qm_dqpurge_hints() will end up go through the | ||
252 | * general xfs_qm_dqpurge() against user dquot cache if requested. | ||
253 | */ | ||
254 | xfs_qm_dquot_walk(mp, XFS_DQ_USER, xfs_qm_dqpurge_hints, &flags); | ||
255 | |||
256 | if (flags & XFS_QMOPT_GQUOTA) | 205 | if (flags & XFS_QMOPT_GQUOTA) |
257 | xfs_qm_dquot_walk(mp, XFS_DQ_GROUP, xfs_qm_dqpurge, NULL); | 206 | xfs_qm_dquot_walk(mp, XFS_DQ_GROUP, xfs_qm_dqpurge, NULL); |
258 | if (flags & XFS_QMOPT_PQUOTA) | 207 | if (flags & XFS_QMOPT_PQUOTA) |
@@ -409,7 +358,6 @@ xfs_qm_dqattach_one( | |||
409 | xfs_dqid_t id, | 358 | xfs_dqid_t id, |
410 | uint type, | 359 | uint type, |
411 | uint doalloc, | 360 | uint doalloc, |
412 | xfs_dquot_t *udqhint, /* hint */ | ||
413 | xfs_dquot_t **IO_idqpp) | 361 | xfs_dquot_t **IO_idqpp) |
414 | { | 362 | { |
415 | xfs_dquot_t *dqp; | 363 | xfs_dquot_t *dqp; |
@@ -419,9 +367,9 @@ xfs_qm_dqattach_one( | |||
419 | error = 0; | 367 | error = 0; |
420 | 368 | ||
421 | /* | 369 | /* |
422 | * See if we already have it in the inode itself. IO_idqpp is | 370 | * See if we already have it in the inode itself. IO_idqpp is &i_udquot |
423 | * &i_udquot or &i_gdquot. This made the code look weird, but | 371 | * or &i_gdquot. This made the code look weird, but made the logic a lot |
424 | * made the logic a lot simpler. | 372 | * simpler. |
425 | */ | 373 | */ |
426 | dqp = *IO_idqpp; | 374 | dqp = *IO_idqpp; |
427 | if (dqp) { | 375 | if (dqp) { |
@@ -430,49 +378,10 @@ xfs_qm_dqattach_one( | |||
430 | } | 378 | } |
431 | 379 | ||
432 | /* | 380 | /* |
433 | * udqhint is the i_udquot field in inode, and is non-NULL only | 381 | * Find the dquot from somewhere. This bumps the reference count of |
434 | * when the type arg is group/project. Its purpose is to save a | 382 | * dquot and returns it locked. This can return ENOENT if dquot didn't |
435 | * lookup by dqid (xfs_qm_dqget) by caching a group dquot inside | 383 | * exist on disk and we didn't ask it to allocate; ESRCH if quotas got |
436 | * the user dquot. | 384 | * turned off suddenly. |
437 | */ | ||
438 | if (udqhint) { | ||
439 | ASSERT(type == XFS_DQ_GROUP || type == XFS_DQ_PROJ); | ||
440 | xfs_dqlock(udqhint); | ||
441 | |||
442 | /* | ||
443 | * No need to take dqlock to look at the id. | ||
444 | * | ||
445 | * The ID can't change until it gets reclaimed, and it won't | ||
446 | * be reclaimed as long as we have a ref from inode and we | ||
447 | * hold the ilock. | ||
448 | */ | ||
449 | if (type == XFS_DQ_GROUP) | ||
450 | dqp = udqhint->q_gdquot; | ||
451 | else | ||
452 | dqp = udqhint->q_pdquot; | ||
453 | if (dqp && be32_to_cpu(dqp->q_core.d_id) == id) { | ||
454 | ASSERT(*IO_idqpp == NULL); | ||
455 | |||
456 | *IO_idqpp = xfs_qm_dqhold(dqp); | ||
457 | xfs_dqunlock(udqhint); | ||
458 | return 0; | ||
459 | } | ||
460 | |||
461 | /* | ||
462 | * We can't hold a dquot lock when we call the dqget code. | ||
463 | * We'll deadlock in no time, because of (not conforming to) | ||
464 | * lock ordering - the inodelock comes before any dquot lock, | ||
465 | * and we may drop and reacquire the ilock in xfs_qm_dqget(). | ||
466 | */ | ||
467 | xfs_dqunlock(udqhint); | ||
468 | } | ||
469 | |||
470 | /* | ||
471 | * Find the dquot from somewhere. This bumps the | ||
472 | * reference count of dquot and returns it locked. | ||
473 | * This can return ENOENT if dquot didn't exist on | ||
474 | * disk and we didn't ask it to allocate; | ||
475 | * ESRCH if quotas got turned off suddenly. | ||
476 | */ | 385 | */ |
477 | error = xfs_qm_dqget(ip->i_mount, ip, id, type, | 386 | error = xfs_qm_dqget(ip->i_mount, ip, id, type, |
478 | doalloc | XFS_QMOPT_DOWARN, &dqp); | 387 | doalloc | XFS_QMOPT_DOWARN, &dqp); |
@@ -490,48 +399,6 @@ xfs_qm_dqattach_one( | |||
490 | return 0; | 399 | return 0; |
491 | } | 400 | } |
492 | 401 | ||
493 | |||
494 | /* | ||
495 | * Given a udquot and group/project type, attach the group/project | ||
496 | * dquot pointer to the udquot as a hint for future lookups. | ||
497 | */ | ||
498 | STATIC void | ||
499 | xfs_qm_dqattach_hint( | ||
500 | struct xfs_inode *ip, | ||
501 | int type) | ||
502 | { | ||
503 | struct xfs_dquot **dqhintp; | ||
504 | struct xfs_dquot *dqp; | ||
505 | struct xfs_dquot *udq = ip->i_udquot; | ||
506 | |||
507 | ASSERT(type == XFS_DQ_GROUP || type == XFS_DQ_PROJ); | ||
508 | |||
509 | xfs_dqlock(udq); | ||
510 | |||
511 | if (type == XFS_DQ_GROUP) { | ||
512 | dqp = ip->i_gdquot; | ||
513 | dqhintp = &udq->q_gdquot; | ||
514 | } else { | ||
515 | dqp = ip->i_pdquot; | ||
516 | dqhintp = &udq->q_pdquot; | ||
517 | } | ||
518 | |||
519 | if (*dqhintp) { | ||
520 | struct xfs_dquot *tmp; | ||
521 | |||
522 | if (*dqhintp == dqp) | ||
523 | goto done; | ||
524 | |||
525 | tmp = *dqhintp; | ||
526 | *dqhintp = NULL; | ||
527 | xfs_qm_dqrele(tmp); | ||
528 | } | ||
529 | |||
530 | *dqhintp = xfs_qm_dqhold(dqp); | ||
531 | done: | ||
532 | xfs_dqunlock(udq); | ||
533 | } | ||
534 | |||
535 | static bool | 402 | static bool |
536 | xfs_qm_need_dqattach( | 403 | xfs_qm_need_dqattach( |
537 | struct xfs_inode *ip) | 404 | struct xfs_inode *ip) |
@@ -562,7 +429,6 @@ xfs_qm_dqattach_locked( | |||
562 | uint flags) | 429 | uint flags) |
563 | { | 430 | { |
564 | xfs_mount_t *mp = ip->i_mount; | 431 | xfs_mount_t *mp = ip->i_mount; |
565 | uint nquotas = 0; | ||
566 | int error = 0; | 432 | int error = 0; |
567 | 433 | ||
568 | if (!xfs_qm_need_dqattach(ip)) | 434 | if (!xfs_qm_need_dqattach(ip)) |
@@ -570,77 +436,39 @@ xfs_qm_dqattach_locked( | |||
570 | 436 | ||
571 | ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); | 437 | ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); |
572 | 438 | ||
573 | if (XFS_IS_UQUOTA_ON(mp)) { | 439 | if (XFS_IS_UQUOTA_ON(mp) && !ip->i_udquot) { |
574 | error = xfs_qm_dqattach_one(ip, ip->i_d.di_uid, XFS_DQ_USER, | 440 | error = xfs_qm_dqattach_one(ip, ip->i_d.di_uid, XFS_DQ_USER, |
575 | flags & XFS_QMOPT_DQALLOC, | 441 | flags & XFS_QMOPT_DQALLOC, |
576 | NULL, &ip->i_udquot); | 442 | &ip->i_udquot); |
577 | if (error) | 443 | if (error) |
578 | goto done; | 444 | goto done; |
579 | nquotas++; | 445 | ASSERT(ip->i_udquot); |
580 | } | 446 | } |
581 | 447 | ||
582 | ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); | 448 | if (XFS_IS_GQUOTA_ON(mp) && !ip->i_gdquot) { |
583 | if (XFS_IS_GQUOTA_ON(mp)) { | ||
584 | error = xfs_qm_dqattach_one(ip, ip->i_d.di_gid, XFS_DQ_GROUP, | 449 | error = xfs_qm_dqattach_one(ip, ip->i_d.di_gid, XFS_DQ_GROUP, |
585 | flags & XFS_QMOPT_DQALLOC, | 450 | flags & XFS_QMOPT_DQALLOC, |
586 | ip->i_udquot, &ip->i_gdquot); | 451 | &ip->i_gdquot); |
587 | /* | ||
588 | * Don't worry about the udquot that we may have | ||
589 | * attached above. It'll get detached, if not already. | ||
590 | */ | ||
591 | if (error) | 452 | if (error) |
592 | goto done; | 453 | goto done; |
593 | nquotas++; | 454 | ASSERT(ip->i_gdquot); |
594 | } | 455 | } |
595 | 456 | ||
596 | ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); | 457 | if (XFS_IS_PQUOTA_ON(mp) && !ip->i_pdquot) { |
597 | if (XFS_IS_PQUOTA_ON(mp)) { | ||
598 | error = xfs_qm_dqattach_one(ip, xfs_get_projid(ip), XFS_DQ_PROJ, | 458 | error = xfs_qm_dqattach_one(ip, xfs_get_projid(ip), XFS_DQ_PROJ, |
599 | flags & XFS_QMOPT_DQALLOC, | 459 | flags & XFS_QMOPT_DQALLOC, |
600 | ip->i_udquot, &ip->i_pdquot); | 460 | &ip->i_pdquot); |
601 | /* | ||
602 | * Don't worry about the udquot that we may have | ||
603 | * attached above. It'll get detached, if not already. | ||
604 | */ | ||
605 | if (error) | 461 | if (error) |
606 | goto done; | 462 | goto done; |
607 | nquotas++; | 463 | ASSERT(ip->i_pdquot); |
608 | } | 464 | } |
609 | 465 | ||
466 | done: | ||
610 | /* | 467 | /* |
611 | * Attach this group/project quota to the user quota as a hint. | 468 | * Don't worry about the dquots that we may have attached before any |
612 | * This WON'T, in general, result in a thrash. | 469 | * error - they'll get detached later if it has not already been done. |
613 | */ | 470 | */ |
614 | if (nquotas > 1 && ip->i_udquot) { | ||
615 | ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); | ||
616 | ASSERT(ip->i_gdquot || !XFS_IS_GQUOTA_ON(mp)); | ||
617 | ASSERT(ip->i_pdquot || !XFS_IS_PQUOTA_ON(mp)); | ||
618 | |||
619 | /* | ||
620 | * We do not have i_udquot locked at this point, but this check | ||
621 | * is OK since we don't depend on the i_gdquot to be accurate | ||
622 | * 100% all the time. It is just a hint, and this will | ||
623 | * succeed in general. | ||
624 | */ | ||
625 | if (ip->i_udquot->q_gdquot != ip->i_gdquot) | ||
626 | xfs_qm_dqattach_hint(ip, XFS_DQ_GROUP); | ||
627 | |||
628 | if (ip->i_udquot->q_pdquot != ip->i_pdquot) | ||
629 | xfs_qm_dqattach_hint(ip, XFS_DQ_PROJ); | ||
630 | } | ||
631 | |||
632 | done: | ||
633 | #ifdef DEBUG | ||
634 | if (!error) { | ||
635 | if (XFS_IS_UQUOTA_ON(mp)) | ||
636 | ASSERT(ip->i_udquot); | ||
637 | if (XFS_IS_GQUOTA_ON(mp)) | ||
638 | ASSERT(ip->i_gdquot); | ||
639 | if (XFS_IS_PQUOTA_ON(mp)) | ||
640 | ASSERT(ip->i_pdquot); | ||
641 | } | ||
642 | ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); | 471 | ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); |
643 | #endif | ||
644 | return error; | 472 | return error; |
645 | } | 473 | } |
646 | 474 | ||