diff options
author | Dave Chinner <dchinner@redhat.com> | 2014-05-05 03:30:15 -0400 |
---|---|---|
committer | Dave Chinner <david@fromorbit.com> | 2014-05-05 03:30:15 -0400 |
commit | 3c353375761d81abfb66eb054aacceef31658e24 (patch) | |
tree | 709ca6211697a56c57dd58a21127582d581be32d | |
parent | f58522c5a47a1862c6b3fad97ea9285c5d68199d (diff) |
xfs: remove dquot hints
group and project quota hints are currently stored on the user
dquot. If we are attaching quotas to the inode, then the group and
project dquots are stored as hints on the user dquot to save having
to look them up again later.
The thing is, the hints are not used for that inode for the rest of
the life of the inode - the dquots are attached directly to the
inode itself - so the only time the hints are used is when an inode
first has dquots attached.
When the hints on the user dquot don't match the dquots being
attache dto the inode, they are then removed and replaced with the
new hints. If a user is concurrently modifying files in different
group and/or project contexts, then this leads to thrashing of the
hints attached to user dquot.
If user quotas are not enabled, then hints are never even used.
So, if the hints are used to avoid the cost of the lookup, is the
cost of the lookup significant enough to justify the hint
infrstructure? Maybe it was once, when there was a global quota
manager shared between all XFS filesystems and was hash table based.
However, lookups are now much simpler, requiring only a single lock and
radix tree lookup local to the filesystem and no hash or LRU
manipulations to be made. Hence the cost of lookup is much lower
than when hints were implemented. Turns out that benchmarks show
that, too, with thir being no differnce in performance when doing
file creation workloads as a single user with user, group and
project quotas enabled - the hints do not make the code go any
faster. In fact, removing the hints shows a 2-3% reduction in the
time it takes to create 50 million inodes....
So, let's just get rid of the hints and the complexity around them.
Signed-off-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Signed-off-by: Dave Chinner <david@fromorbit.com>
-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 | ||