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 | |
parent | b76769294ba400415fc44038c21cc2df86f9a28b (diff) | |
parent | 8cfcc3e565bf15870efe801368a25ca98092e6e7 (diff) |
Merge branch 'xfs-misc-fixes-1-for-3.16' into for-next
-rw-r--r-- | fs/quota/quota.c | 14 | ||||
-rw-r--r-- | fs/xfs/xfs_dir2_readdir.c | 3 | ||||
-rw-r--r-- | fs/xfs/xfs_dquot.c | 53 | ||||
-rw-r--r-- | fs/xfs/xfs_dquot.h | 2 | ||||
-rw-r--r-- | fs/xfs/xfs_ioctl.c | 5 | ||||
-rw-r--r-- | fs/xfs/xfs_ioctl32.c | 5 | ||||
-rw-r--r-- | fs/xfs/xfs_iops.c | 20 | ||||
-rw-r--r-- | fs/xfs/xfs_log.c | 9 | ||||
-rw-r--r-- | fs/xfs/xfs_log_cil.c | 50 | ||||
-rw-r--r-- | fs/xfs/xfs_qm.c | 214 | ||||
-rw-r--r-- | fs/xfs/xfs_qm_syscalls.c | 5 | ||||
-rw-r--r-- | fs/xfs/xfs_quotaops.c | 29 | ||||
-rw-r--r-- | include/linux/quota.h | 1 |
13 files changed, 142 insertions, 268 deletions
diff --git a/fs/quota/quota.c b/fs/quota/quota.c index 2b363e23f36e..ff3f0b3cfdb3 100644 --- a/fs/quota/quota.c +++ b/fs/quota/quota.c | |||
@@ -278,6 +278,17 @@ static int quota_getxquota(struct super_block *sb, int type, qid_t id, | |||
278 | return ret; | 278 | return ret; |
279 | } | 279 | } |
280 | 280 | ||
281 | static int quota_rmxquota(struct super_block *sb, void __user *addr) | ||
282 | { | ||
283 | __u32 flags; | ||
284 | |||
285 | if (copy_from_user(&flags, addr, sizeof(flags))) | ||
286 | return -EFAULT; | ||
287 | if (!sb->s_qcop->rm_xquota) | ||
288 | return -ENOSYS; | ||
289 | return sb->s_qcop->rm_xquota(sb, flags); | ||
290 | } | ||
291 | |||
281 | /* Copy parameters and call proper function */ | 292 | /* Copy parameters and call proper function */ |
282 | static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id, | 293 | static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id, |
283 | void __user *addr, struct path *path) | 294 | void __user *addr, struct path *path) |
@@ -316,8 +327,9 @@ static int do_quotactl(struct super_block *sb, int type, int cmd, qid_t id, | |||
316 | return sb->s_qcop->quota_sync(sb, type); | 327 | return sb->s_qcop->quota_sync(sb, type); |
317 | case Q_XQUOTAON: | 328 | case Q_XQUOTAON: |
318 | case Q_XQUOTAOFF: | 329 | case Q_XQUOTAOFF: |
319 | case Q_XQUOTARM: | ||
320 | return quota_setxstate(sb, cmd, addr); | 330 | return quota_setxstate(sb, cmd, addr); |
331 | case Q_XQUOTARM: | ||
332 | return quota_rmxquota(sb, addr); | ||
321 | case Q_XGETQSTAT: | 333 | case Q_XGETQSTAT: |
322 | return quota_getxstate(sb, addr); | 334 | return quota_getxstate(sb, addr); |
323 | case Q_XGETQSTATV: | 335 | case Q_XGETQSTATV: |
diff --git a/fs/xfs/xfs_dir2_readdir.c b/fs/xfs/xfs_dir2_readdir.c index 50b72f7b8787..bf7a5cee7adc 100644 --- a/fs/xfs/xfs_dir2_readdir.c +++ b/fs/xfs/xfs_dir2_readdir.c | |||
@@ -456,7 +456,7 @@ xfs_dir2_leaf_readbuf( | |||
456 | /* | 456 | /* |
457 | * Advance offset through the mapping table. | 457 | * Advance offset through the mapping table. |
458 | */ | 458 | */ |
459 | for (j = 0; j < mp->m_dirblkfsbs; j++) { | 459 | for (j = 0; j < mp->m_dirblkfsbs; j += length ) { |
460 | /* | 460 | /* |
461 | * The rest of this extent but not more than a dir | 461 | * The rest of this extent but not more than a dir |
462 | * block. | 462 | * block. |
@@ -464,7 +464,6 @@ xfs_dir2_leaf_readbuf( | |||
464 | length = min_t(int, mp->m_dirblkfsbs, | 464 | length = min_t(int, mp->m_dirblkfsbs, |
465 | map[mip->ra_index].br_blockcount - | 465 | map[mip->ra_index].br_blockcount - |
466 | mip->ra_offset); | 466 | mip->ra_offset); |
467 | j += length; | ||
468 | mip->ra_offset += length; | 467 | mip->ra_offset += length; |
469 | 468 | ||
470 | /* | 469 | /* |
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_ioctl.c b/fs/xfs/xfs_ioctl.c index 0b18776b075e..2d8f4fdf07f9 100644 --- a/fs/xfs/xfs_ioctl.c +++ b/fs/xfs/xfs_ioctl.c | |||
@@ -543,10 +543,11 @@ xfs_attrmulti_by_handle( | |||
543 | 543 | ||
544 | ops = memdup_user(am_hreq.ops, size); | 544 | ops = memdup_user(am_hreq.ops, size); |
545 | if (IS_ERR(ops)) { | 545 | if (IS_ERR(ops)) { |
546 | error = PTR_ERR(ops); | 546 | error = -PTR_ERR(ops); |
547 | goto out_dput; | 547 | goto out_dput; |
548 | } | 548 | } |
549 | 549 | ||
550 | error = ENOMEM; | ||
550 | attr_name = kmalloc(MAXNAMELEN, GFP_KERNEL); | 551 | attr_name = kmalloc(MAXNAMELEN, GFP_KERNEL); |
551 | if (!attr_name) | 552 | if (!attr_name) |
552 | goto out_kfree_ops; | 553 | goto out_kfree_ops; |
@@ -556,7 +557,7 @@ xfs_attrmulti_by_handle( | |||
556 | ops[i].am_error = strncpy_from_user((char *)attr_name, | 557 | ops[i].am_error = strncpy_from_user((char *)attr_name, |
557 | ops[i].am_attrname, MAXNAMELEN); | 558 | ops[i].am_attrname, MAXNAMELEN); |
558 | if (ops[i].am_error == 0 || ops[i].am_error == MAXNAMELEN) | 559 | if (ops[i].am_error == 0 || ops[i].am_error == MAXNAMELEN) |
559 | error = -ERANGE; | 560 | error = ERANGE; |
560 | if (ops[i].am_error < 0) | 561 | if (ops[i].am_error < 0) |
561 | break; | 562 | break; |
562 | 563 | ||
diff --git a/fs/xfs/xfs_ioctl32.c b/fs/xfs/xfs_ioctl32.c index a7992f8de9d3..944d5baa710a 100644 --- a/fs/xfs/xfs_ioctl32.c +++ b/fs/xfs/xfs_ioctl32.c | |||
@@ -424,10 +424,11 @@ xfs_compat_attrmulti_by_handle( | |||
424 | 424 | ||
425 | ops = memdup_user(compat_ptr(am_hreq.ops), size); | 425 | ops = memdup_user(compat_ptr(am_hreq.ops), size); |
426 | if (IS_ERR(ops)) { | 426 | if (IS_ERR(ops)) { |
427 | error = PTR_ERR(ops); | 427 | error = -PTR_ERR(ops); |
428 | goto out_dput; | 428 | goto out_dput; |
429 | } | 429 | } |
430 | 430 | ||
431 | error = ENOMEM; | ||
431 | attr_name = kmalloc(MAXNAMELEN, GFP_KERNEL); | 432 | attr_name = kmalloc(MAXNAMELEN, GFP_KERNEL); |
432 | if (!attr_name) | 433 | if (!attr_name) |
433 | goto out_kfree_ops; | 434 | goto out_kfree_ops; |
@@ -438,7 +439,7 @@ xfs_compat_attrmulti_by_handle( | |||
438 | compat_ptr(ops[i].am_attrname), | 439 | compat_ptr(ops[i].am_attrname), |
439 | MAXNAMELEN); | 440 | MAXNAMELEN); |
440 | if (ops[i].am_error == 0 || ops[i].am_error == MAXNAMELEN) | 441 | if (ops[i].am_error == 0 || ops[i].am_error == MAXNAMELEN) |
441 | error = -ERANGE; | 442 | error = ERANGE; |
442 | if (ops[i].am_error < 0) | 443 | if (ops[i].am_error < 0) |
443 | break; | 444 | break; |
444 | 445 | ||
diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c index 36d630319a27..205613a06068 100644 --- a/fs/xfs/xfs_iops.c +++ b/fs/xfs/xfs_iops.c | |||
@@ -829,22 +829,34 @@ xfs_setattr_size( | |||
829 | */ | 829 | */ |
830 | inode_dio_wait(inode); | 830 | inode_dio_wait(inode); |
831 | 831 | ||
832 | /* | ||
833 | * Do all the page cache truncate work outside the transaction context | ||
834 | * as the "lock" order is page lock->log space reservation. i.e. | ||
835 | * locking pages inside the transaction can ABBA deadlock with | ||
836 | * writeback. We have to do the VFS inode size update before we truncate | ||
837 | * the pagecache, however, to avoid racing with page faults beyond the | ||
838 | * new EOF they are not serialised against truncate operations except by | ||
839 | * page locks and size updates. | ||
840 | * | ||
841 | * Hence we are in a situation where a truncate can fail with ENOMEM | ||
842 | * from xfs_trans_reserve(), but having already truncated the in-memory | ||
843 | * version of the file (i.e. made user visible changes). There's not | ||
844 | * much we can do about this, except to hope that the caller sees ENOMEM | ||
845 | * and retries the truncate operation. | ||
846 | */ | ||
832 | error = -block_truncate_page(inode->i_mapping, newsize, xfs_get_blocks); | 847 | error = -block_truncate_page(inode->i_mapping, newsize, xfs_get_blocks); |
833 | if (error) | 848 | if (error) |
834 | return error; | 849 | return error; |
850 | truncate_setsize(inode, newsize); | ||
835 | 851 | ||
836 | tp = xfs_trans_alloc(mp, XFS_TRANS_SETATTR_SIZE); | 852 | tp = xfs_trans_alloc(mp, XFS_TRANS_SETATTR_SIZE); |
837 | error = xfs_trans_reserve(tp, &M_RES(mp)->tr_itruncate, 0, 0); | 853 | error = xfs_trans_reserve(tp, &M_RES(mp)->tr_itruncate, 0, 0); |
838 | if (error) | 854 | if (error) |
839 | goto out_trans_cancel; | 855 | goto out_trans_cancel; |
840 | 856 | ||
841 | truncate_setsize(inode, newsize); | ||
842 | |||
843 | commit_flags = XFS_TRANS_RELEASE_LOG_RES; | 857 | commit_flags = XFS_TRANS_RELEASE_LOG_RES; |
844 | lock_flags |= XFS_ILOCK_EXCL; | 858 | lock_flags |= XFS_ILOCK_EXCL; |
845 | |||
846 | xfs_ilock(ip, XFS_ILOCK_EXCL); | 859 | xfs_ilock(ip, XFS_ILOCK_EXCL); |
847 | |||
848 | xfs_trans_ijoin(tp, ip, 0); | 860 | xfs_trans_ijoin(tp, ip, 0); |
849 | 861 | ||
850 | /* | 862 | /* |
diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c index a5f8bd9899d3..3554098692d8 100644 --- a/fs/xfs/xfs_log.c +++ b/fs/xfs/xfs_log.c | |||
@@ -3952,11 +3952,14 @@ xfs_log_force_umount( | |||
3952 | retval = xlog_state_ioerror(log); | 3952 | retval = xlog_state_ioerror(log); |
3953 | spin_unlock(&log->l_icloglock); | 3953 | spin_unlock(&log->l_icloglock); |
3954 | } | 3954 | } |
3955 | |||
3955 | /* | 3956 | /* |
3956 | * Wake up everybody waiting on xfs_log_force. | 3957 | * Wake up everybody waiting on xfs_log_force. Wake the CIL push first |
3957 | * Callback all log item committed functions as if the | 3958 | * as if the log writes were completed. The abort handling in the log |
3958 | * log writes were completed. | 3959 | * item committed callback functions will do this again under lock to |
3960 | * avoid races. | ||
3959 | */ | 3961 | */ |
3962 | wake_up_all(&log->l_cilp->xc_commit_wait); | ||
3960 | xlog_state_do_callback(log, XFS_LI_ABORTED, NULL); | 3963 | xlog_state_do_callback(log, XFS_LI_ABORTED, NULL); |
3961 | 3964 | ||
3962 | #ifdef XFSERRORDEBUG | 3965 | #ifdef XFSERRORDEBUG |
diff --git a/fs/xfs/xfs_log_cil.c b/fs/xfs/xfs_log_cil.c index 7e5455391176..039c873e6fb2 100644 --- a/fs/xfs/xfs_log_cil.c +++ b/fs/xfs/xfs_log_cil.c | |||
@@ -385,7 +385,15 @@ xlog_cil_committed( | |||
385 | xfs_extent_busy_clear(mp, &ctx->busy_extents, | 385 | xfs_extent_busy_clear(mp, &ctx->busy_extents, |
386 | (mp->m_flags & XFS_MOUNT_DISCARD) && !abort); | 386 | (mp->m_flags & XFS_MOUNT_DISCARD) && !abort); |
387 | 387 | ||
388 | /* | ||
389 | * If we are aborting the commit, wake up anyone waiting on the | ||
390 | * committing list. If we don't, then a shutdown we can leave processes | ||
391 | * waiting in xlog_cil_force_lsn() waiting on a sequence commit that | ||
392 | * will never happen because we aborted it. | ||
393 | */ | ||
388 | spin_lock(&ctx->cil->xc_push_lock); | 394 | spin_lock(&ctx->cil->xc_push_lock); |
395 | if (abort) | ||
396 | wake_up_all(&ctx->cil->xc_commit_wait); | ||
389 | list_del(&ctx->committing); | 397 | list_del(&ctx->committing); |
390 | spin_unlock(&ctx->cil->xc_push_lock); | 398 | spin_unlock(&ctx->cil->xc_push_lock); |
391 | 399 | ||
@@ -564,8 +572,18 @@ restart: | |||
564 | spin_lock(&cil->xc_push_lock); | 572 | spin_lock(&cil->xc_push_lock); |
565 | list_for_each_entry(new_ctx, &cil->xc_committing, committing) { | 573 | list_for_each_entry(new_ctx, &cil->xc_committing, committing) { |
566 | /* | 574 | /* |
575 | * Avoid getting stuck in this loop because we were woken by the | ||
576 | * shutdown, but then went back to sleep once already in the | ||
577 | * shutdown state. | ||
578 | */ | ||
579 | if (XLOG_FORCED_SHUTDOWN(log)) { | ||
580 | spin_unlock(&cil->xc_push_lock); | ||
581 | goto out_abort_free_ticket; | ||
582 | } | ||
583 | |||
584 | /* | ||
567 | * Higher sequences will wait for this one so skip them. | 585 | * Higher sequences will wait for this one so skip them. |
568 | * Don't wait for own own sequence, either. | 586 | * Don't wait for our own sequence, either. |
569 | */ | 587 | */ |
570 | if (new_ctx->sequence >= ctx->sequence) | 588 | if (new_ctx->sequence >= ctx->sequence) |
571 | continue; | 589 | continue; |
@@ -810,6 +828,13 @@ restart: | |||
810 | */ | 828 | */ |
811 | spin_lock(&cil->xc_push_lock); | 829 | spin_lock(&cil->xc_push_lock); |
812 | list_for_each_entry(ctx, &cil->xc_committing, committing) { | 830 | list_for_each_entry(ctx, &cil->xc_committing, committing) { |
831 | /* | ||
832 | * Avoid getting stuck in this loop because we were woken by the | ||
833 | * shutdown, but then went back to sleep once already in the | ||
834 | * shutdown state. | ||
835 | */ | ||
836 | if (XLOG_FORCED_SHUTDOWN(log)) | ||
837 | goto out_shutdown; | ||
813 | if (ctx->sequence > sequence) | 838 | if (ctx->sequence > sequence) |
814 | continue; | 839 | continue; |
815 | if (!ctx->commit_lsn) { | 840 | if (!ctx->commit_lsn) { |
@@ -833,14 +858,12 @@ restart: | |||
833 | * push sequence after the above wait loop and the CIL still contains | 858 | * push sequence after the above wait loop and the CIL still contains |
834 | * dirty objects. | 859 | * dirty objects. |
835 | * | 860 | * |
836 | * When the push occurs, it will empty the CIL and | 861 | * When the push occurs, it will empty the CIL and atomically increment |
837 | * atomically increment the currect sequence past the push sequence and | 862 | * the currect sequence past the push sequence and move it into the |
838 | * move it into the committing list. Of course, if the CIL is clean at | 863 | * committing list. Of course, if the CIL is clean at the time of the |
839 | * the time of the push, it won't have pushed the CIL at all, so in that | 864 | * push, it won't have pushed the CIL at all, so in that case we should |
840 | * case we should try the push for this sequence again from the start | 865 | * try the push for this sequence again from the start just in case. |
841 | * just in case. | ||
842 | */ | 866 | */ |
843 | |||
844 | if (sequence == cil->xc_current_sequence && | 867 | if (sequence == cil->xc_current_sequence && |
845 | !list_empty(&cil->xc_cil)) { | 868 | !list_empty(&cil->xc_cil)) { |
846 | spin_unlock(&cil->xc_push_lock); | 869 | spin_unlock(&cil->xc_push_lock); |
@@ -849,6 +872,17 @@ restart: | |||
849 | 872 | ||
850 | spin_unlock(&cil->xc_push_lock); | 873 | spin_unlock(&cil->xc_push_lock); |
851 | return commit_lsn; | 874 | return commit_lsn; |
875 | |||
876 | /* | ||
877 | * We detected a shutdown in progress. We need to trigger the log force | ||
878 | * to pass through it's iclog state machine error handling, even though | ||
879 | * we are already in a shutdown state. Hence we can't return | ||
880 | * NULLCOMMITLSN here as that has special meaning to log forces (i.e. | ||
881 | * LSN is already stable), so we return a zero LSN instead. | ||
882 | */ | ||
883 | out_shutdown: | ||
884 | spin_unlock(&cil->xc_push_lock); | ||
885 | return 0; | ||
852 | } | 886 | } |
853 | 887 | ||
854 | /* | 888 | /* |
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 | ||
diff --git a/fs/xfs/xfs_qm_syscalls.c b/fs/xfs/xfs_qm_syscalls.c index 72cbe65e5e75..bbc813caba4c 100644 --- a/fs/xfs/xfs_qm_syscalls.c +++ b/fs/xfs/xfs_qm_syscalls.c | |||
@@ -278,9 +278,10 @@ xfs_qm_scall_trunc_qfiles( | |||
278 | xfs_mount_t *mp, | 278 | xfs_mount_t *mp, |
279 | uint flags) | 279 | uint flags) |
280 | { | 280 | { |
281 | int error; | 281 | int error = EINVAL; |
282 | 282 | ||
283 | if (!xfs_sb_version_hasquota(&mp->m_sb) || flags == 0) { | 283 | if (!xfs_sb_version_hasquota(&mp->m_sb) || flags == 0 || |
284 | (flags & ~XFS_DQ_ALLTYPES)) { | ||
284 | xfs_debug(mp, "%s: flags=%x m_qflags=%x", | 285 | xfs_debug(mp, "%s: flags=%x m_qflags=%x", |
285 | __func__, flags, mp->m_qflags); | 286 | __func__, flags, mp->m_qflags); |
286 | return XFS_ERROR(EINVAL); | 287 | return XFS_ERROR(EINVAL); |
diff --git a/fs/xfs/xfs_quotaops.c b/fs/xfs/xfs_quotaops.c index af33cafe69b6..2ad1b9822e92 100644 --- a/fs/xfs/xfs_quotaops.c +++ b/fs/xfs/xfs_quotaops.c | |||
@@ -100,16 +100,36 @@ xfs_fs_set_xstate( | |||
100 | if (!XFS_IS_QUOTA_ON(mp)) | 100 | if (!XFS_IS_QUOTA_ON(mp)) |
101 | return -EINVAL; | 101 | return -EINVAL; |
102 | return -xfs_qm_scall_quotaoff(mp, flags); | 102 | return -xfs_qm_scall_quotaoff(mp, flags); |
103 | case Q_XQUOTARM: | ||
104 | if (XFS_IS_QUOTA_ON(mp)) | ||
105 | return -EINVAL; | ||
106 | return -xfs_qm_scall_trunc_qfiles(mp, flags); | ||
107 | } | 103 | } |
108 | 104 | ||
109 | return -EINVAL; | 105 | return -EINVAL; |
110 | } | 106 | } |
111 | 107 | ||
112 | STATIC int | 108 | STATIC int |
109 | xfs_fs_rm_xquota( | ||
110 | struct super_block *sb, | ||
111 | unsigned int uflags) | ||
112 | { | ||
113 | struct xfs_mount *mp = XFS_M(sb); | ||
114 | unsigned int flags = 0; | ||
115 | |||
116 | if (sb->s_flags & MS_RDONLY) | ||
117 | return -EROFS; | ||
118 | |||
119 | if (XFS_IS_QUOTA_ON(mp)) | ||
120 | return -EINVAL; | ||
121 | |||
122 | if (uflags & FS_USER_QUOTA) | ||
123 | flags |= XFS_DQ_USER; | ||
124 | if (uflags & FS_GROUP_QUOTA) | ||
125 | flags |= XFS_DQ_GROUP; | ||
126 | if (uflags & FS_USER_QUOTA) | ||
127 | flags |= XFS_DQ_PROJ; | ||
128 | |||
129 | return -xfs_qm_scall_trunc_qfiles(mp, flags); | ||
130 | } | ||
131 | |||
132 | STATIC int | ||
113 | xfs_fs_get_dqblk( | 133 | xfs_fs_get_dqblk( |
114 | struct super_block *sb, | 134 | struct super_block *sb, |
115 | struct kqid qid, | 135 | struct kqid qid, |
@@ -149,6 +169,7 @@ const struct quotactl_ops xfs_quotactl_operations = { | |||
149 | .get_xstatev = xfs_fs_get_xstatev, | 169 | .get_xstatev = xfs_fs_get_xstatev, |
150 | .get_xstate = xfs_fs_get_xstate, | 170 | .get_xstate = xfs_fs_get_xstate, |
151 | .set_xstate = xfs_fs_set_xstate, | 171 | .set_xstate = xfs_fs_set_xstate, |
172 | .rm_xquota = xfs_fs_rm_xquota, | ||
152 | .get_dqblk = xfs_fs_get_dqblk, | 173 | .get_dqblk = xfs_fs_get_dqblk, |
153 | .set_dqblk = xfs_fs_set_dqblk, | 174 | .set_dqblk = xfs_fs_set_dqblk, |
154 | }; | 175 | }; |
diff --git a/include/linux/quota.h b/include/linux/quota.h index cc7494a35429..0f3c5d38da1f 100644 --- a/include/linux/quota.h +++ b/include/linux/quota.h | |||
@@ -329,6 +329,7 @@ struct quotactl_ops { | |||
329 | int (*get_xstate)(struct super_block *, struct fs_quota_stat *); | 329 | int (*get_xstate)(struct super_block *, struct fs_quota_stat *); |
330 | int (*set_xstate)(struct super_block *, unsigned int, int); | 330 | int (*set_xstate)(struct super_block *, unsigned int, int); |
331 | int (*get_xstatev)(struct super_block *, struct fs_quota_statv *); | 331 | int (*get_xstatev)(struct super_block *, struct fs_quota_statv *); |
332 | int (*rm_xquota)(struct super_block *, unsigned int); | ||
332 | }; | 333 | }; |
333 | 334 | ||
334 | struct quota_format_type { | 335 | struct quota_format_type { |