btrfs: add support for processing pending changes
authorDavid Sterba <dsterba@suse.cz>
Wed, 5 Feb 2014 14:26:17 +0000 (15:26 +0100)
committerDavid Sterba <dsterba@suse.cz>
Wed, 12 Nov 2014 15:53:12 +0000 (16:53 +0100)
There are some actions that modify global filesystem state but cannot be
performed at the time of request, but later at the transaction commit
time when the filesystem is in a known state.

For example enabling new incompat features on-the-fly or issuing
transaction commit from unsafe contexts (sysfs handlers).

Signed-off-by: David Sterba <dsterba@suse.cz>
fs/btrfs/ctree.h
fs/btrfs/disk-io.c
fs/btrfs/transaction.c
fs/btrfs/transaction.h

index fe69edda11fbad5201dffc3d4848af5b49fab1c6..f30b061ef77d0c2a66eb7ce4d46a7583f7fc9e83 100644 (file)
@@ -1402,6 +1402,11 @@ struct btrfs_fs_info {
         */
        u64 last_trans_log_full_commit;
        unsigned long mount_opt;
+       /*
+        * Track requests for actions that need to be done during transaction
+        * commit (like for some mount options).
+        */
+       unsigned long pending_changes;
        unsigned long compress_type:4;
        int commit_interval;
        /*
@@ -2103,6 +2108,7 @@ struct btrfs_ioctl_defrag_range_args {
 #define btrfs_raw_test_opt(o, opt)     ((o) & BTRFS_MOUNT_##opt)
 #define btrfs_test_opt(root, opt)      ((root)->fs_info->mount_opt & \
                                         BTRFS_MOUNT_##opt)
+
 #define btrfs_set_and_info(root, opt, fmt, args...)                    \
 {                                                                      \
        if (!btrfs_test_opt(root, opt))                                 \
@@ -2117,6 +2123,45 @@ struct btrfs_ioctl_defrag_range_args {
        btrfs_clear_opt(root->fs_info->mount_opt, opt);                 \
 }
 
+/*
+ * Requests for changes that need to be done during transaction commit.
+ *
+ * Internal mount options that are used for special handling of the real
+ * mount options (eg. cannot be set during remount and have to be set during
+ * transaction commit)
+ */
+
+#define btrfs_test_pending(info, opt)  \
+       test_bit(BTRFS_PENDING_##opt, &(info)->pending_changes)
+#define btrfs_set_pending(info, opt)   \
+       set_bit(BTRFS_PENDING_##opt, &(info)->pending_changes)
+#define btrfs_clear_pending(info, opt) \
+       clear_bit(BTRFS_PENDING_##opt, &(info)->pending_changes)
+
+/*
+ * Helpers for setting pending mount option changes.
+ *
+ * Expects corresponding macros
+ * BTRFS_PENDING_SET_ and CLEAR_ + short mount option name
+ */
+#define btrfs_set_pending_and_info(info, opt, fmt, args...)            \
+do {                                                                   \
+       if (!btrfs_raw_test_opt((info)->mount_opt, opt)) {              \
+               btrfs_info((info), fmt, ##args);                        \
+               btrfs_set_pending((info), SET_##opt);                   \
+               btrfs_clear_pending((info), CLEAR_##opt);               \
+       }                                                               \
+} while(0)
+
+#define btrfs_clear_pending_and_info(info, opt, fmt, args...)          \
+do {                                                                   \
+       if (btrfs_raw_test_opt((info)->mount_opt, opt)) {               \
+               btrfs_info((info), fmt, ##args);                        \
+               btrfs_set_pending((info), CLEAR_##opt);                 \
+               btrfs_clear_pending((info), SET_##opt);                 \
+       }                                                               \
+} while(0)
+
 /*
  * Inode flags
  */
index 1bf9f897065dc886ce4463b3d63f5e507c2b953c..fd80c0d984216fd5bb9e9f557c85729913d65c46 100644 (file)
@@ -2834,6 +2834,12 @@ retry_root_backup:
        if (btrfs_test_opt(tree_root, CHANGE_INODE_CACHE))
                btrfs_set_opt(tree_root->fs_info->mount_opt, INODE_MAP_CACHE);
 
+       /*
+        * Mount does not set all options immediatelly, we can do it now and do
+        * not have to wait for transaction commit
+        */
+       btrfs_apply_pending_changes(fs_info);
+
 #ifdef CONFIG_BTRFS_FS_CHECK_INTEGRITY
        if (btrfs_test_opt(tree_root, CHECK_INTEGRITY)) {
                ret = btrfsic_mount(tree_root, fs_devices,
index dcaae36167288aff33fe679c99b18fc14130188f..7a4024a55e5c5b90298b924934b5292aa1425186 100644 (file)
@@ -1850,6 +1850,8 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans,
        else
                btrfs_clear_opt(root->fs_info->mount_opt, INODE_MAP_CACHE);
 
+       btrfs_apply_pending_changes(root->fs_info);
+
        /* commit_fs_roots gets rid of all the tree log roots, it is now
         * safe to free the root of tree log roots
         */
@@ -2019,3 +2021,17 @@ int btrfs_clean_one_deleted_snapshot(struct btrfs_root *root)
 
        return (ret < 0) ? 0 : 1;
 }
+
+void btrfs_apply_pending_changes(struct btrfs_fs_info *fs_info)
+{
+       unsigned long prev;
+       unsigned long bit;
+
+       prev = cmpxchg(&fs_info->pending_changes, 0, 0);
+       if (!prev)
+               return;
+
+       if (prev)
+               btrfs_warn(fs_info,
+                       "unknown pending changes left 0x%lx, ignoring", prev);
+}
index d8f40e1a5d2dc1b6dfd9ca24526eb7de2bd189f0..75ebcfce9d579f4ed9851725963de856125e8474 100644 (file)
@@ -170,4 +170,6 @@ int btrfs_wait_marked_extents(struct btrfs_root *root,
 int btrfs_transaction_blocked(struct btrfs_fs_info *info);
 int btrfs_transaction_in_commit(struct btrfs_fs_info *info);
 void btrfs_put_transaction(struct btrfs_transaction *transaction);
+void btrfs_apply_pending_changes(struct btrfs_fs_info *fs_info);
+
 #endif