diff options
| -rw-r--r-- | fs/xfs/xfs_dquot.c | 53 | ||||
| -rw-r--r-- | fs/xfs/xfs_dquot.h | 2 | ||||
| -rw-r--r-- | fs/xfs/xfs_qm.c | 214 |
3 files changed, 29 insertions, 240 deletions
diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index 868b19f096bf..5fec738f1f2e 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c | |||
| @@ -832,47 +832,6 @@ restart: | |||
| 832 | return (0); | 832 | return (0); |
| 833 | } | 833 | } |
| 834 | 834 | ||
| 835 | |||
| 836 | STATIC void | ||
| 837 | xfs_qm_dqput_final( | ||
| 838 | struct xfs_dquot *dqp) | ||
| 839 | { | ||
| 840 | struct xfs_quotainfo *qi = dqp->q_mount->m_quotainfo; | ||
| 841 | struct xfs_dquot *gdqp; | ||
| 842 | struct xfs_dquot *pdqp; | ||
| 843 | |||
| 844 | trace_xfs_dqput_free(dqp); | ||
| 845 | |||
| 846 | if (list_lru_add(&qi->qi_lru, &dqp->q_lru)) | ||
| 847 | XFS_STATS_INC(xs_qm_dquot_unused); | ||
| 848 | |||
| 849 | /* | ||
| 850 | * If we just added a udquot to the freelist, then we want to release | ||
| 851 | * the gdquot/pdquot reference that it (probably) has. Otherwise it'll | ||
| 852 | * keep the gdquot/pdquot from getting reclaimed. | ||
| 853 | */ | ||
| 854 | gdqp = dqp->q_gdquot; | ||
| 855 | if (gdqp) { | ||
| 856 | xfs_dqlock(gdqp); | ||
| 857 | dqp->q_gdquot = NULL; | ||
| 858 | } | ||
| 859 | |||
| 860 | pdqp = dqp->q_pdquot; | ||
| 861 | if (pdqp) { | ||
| 862 | xfs_dqlock(pdqp); | ||
| 863 | dqp->q_pdquot = NULL; | ||
| 864 | } | ||
| 865 | xfs_dqunlock(dqp); | ||
| 866 | |||
| 867 | /* | ||
| 868 | * If we had a group/project quota hint, release it now. | ||
| 869 | */ | ||
| 870 | if (gdqp) | ||
| 871 | xfs_qm_dqput(gdqp); | ||
| 872 | if (pdqp) | ||
| 873 | xfs_qm_dqput(pdqp); | ||
| 874 | } | ||
| 875 | |||
| 876 | /* | 835 | /* |
| 877 | * Release a reference to the dquot (decrement ref-count) and unlock it. | 836 | * Release a reference to the dquot (decrement ref-count) and unlock it. |
| 878 | * | 837 | * |
| @@ -888,10 +847,14 @@ xfs_qm_dqput( | |||
| 888 | 847 | ||
| 889 | trace_xfs_dqput(dqp); | 848 | trace_xfs_dqput(dqp); |
| 890 | 849 | ||
| 891 | if (--dqp->q_nrefs > 0) | 850 | if (--dqp->q_nrefs == 0) { |
| 892 | xfs_dqunlock(dqp); | 851 | struct xfs_quotainfo *qi = dqp->q_mount->m_quotainfo; |
| 893 | else | 852 | trace_xfs_dqput_free(dqp); |
| 894 | xfs_qm_dqput_final(dqp); | 853 | |
| 854 | if (list_lru_add(&qi->qi_lru, &dqp->q_lru)) | ||
| 855 | XFS_STATS_INC(xs_qm_dquot_unused); | ||
| 856 | } | ||
| 857 | xfs_dqunlock(dqp); | ||
| 895 | } | 858 | } |
| 896 | 859 | ||
| 897 | /* | 860 | /* |
diff --git a/fs/xfs/xfs_dquot.h b/fs/xfs/xfs_dquot.h index d22ed0053c32..68a68f704837 100644 --- a/fs/xfs/xfs_dquot.h +++ b/fs/xfs/xfs_dquot.h | |||
| @@ -52,8 +52,6 @@ typedef struct xfs_dquot { | |||
| 52 | int q_bufoffset; /* off of dq in buffer (# dquots) */ | 52 | int q_bufoffset; /* off of dq in buffer (# dquots) */ |
| 53 | xfs_fileoff_t q_fileoffset; /* offset in quotas file */ | 53 | xfs_fileoff_t q_fileoffset; /* offset in quotas file */ |
| 54 | 54 | ||
| 55 | struct xfs_dquot*q_gdquot; /* group dquot, hint only */ | ||
| 56 | struct xfs_dquot*q_pdquot; /* project dquot, hint only */ | ||
| 57 | xfs_disk_dquot_t q_core; /* actual usage & quotas */ | 55 | xfs_disk_dquot_t q_core; /* actual usage & quotas */ |
| 58 | xfs_dq_logitem_t q_logitem; /* dquot log item */ | 56 | xfs_dq_logitem_t q_logitem; /* dquot log item */ |
| 59 | xfs_qcnt_t q_res_bcount; /* total regular nblks used+reserved */ | 57 | xfs_qcnt_t q_res_bcount; /* total regular nblks used+reserved */ |
diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index 348e4d2ed6e6..72bd0e8a5954 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 | ||
