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 { |
