aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Chinner <dchinner@redhat.com>2014-05-05 03:30:15 -0400
committerDave Chinner <david@fromorbit.com>2014-05-05 03:30:15 -0400
commit3c353375761d81abfb66eb054aacceef31658e24 (patch)
tree709ca6211697a56c57dd58a21127582d581be32d
parentf58522c5a47a1862c6b3fad97ea9285c5d68199d (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.c53
-rw-r--r--fs/xfs/xfs_dquot.h2
-rw-r--r--fs/xfs/xfs_qm.c214
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
836STATIC void
837xfs_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*/
199STATIC int
200xfs_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 */
239void 198void
@@ -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 */
498STATIC void
499xfs_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);
531done:
532 xfs_dqunlock(udq);
533}
534
535static bool 402static bool
536xfs_qm_need_dqattach( 403xfs_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
466done:
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