summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorQu Wenruo <wqu@suse.com>2017-10-17 13:00:41 +0800
committerDavid Sterba <dsterba@suse.com>2017-11-14 15:59:00 +0100
commitea9cd9df2be56041e2d62b0e72c77695b03261f7 (patch)
treebaddacddd209a34ef9e79369a4d7f17f1b5a046f
parent7a060ea02662a3cbf4b2707de830c835f43658ef (diff)
btrfs-progs: Introduce function to fix unaligned device size
Recent kernel introduced alignment check for dev item, however older kernel doesn't align device size when adding new device or shrinking existing device. This makes noisy kernel warning every time when any DEV_ITEM gets updated. Introduce function to fix device size on an unmounted filesystem. Reported-by: Asif Youssuff <yoasif@gmail.com> Reported-by: Rich Rauenzahn <rrauenza@gmail.com> Signed-off-by: Qu Wenruo <wqu@suse.com> Reviewed-by: Nikolay Borisov <nborisov@suse.com> Signed-off-by: David Sterba <dsterba@suse.com>
-rw-r--r--volumes.c69
-rw-r--r--volumes.h2
2 files changed, 71 insertions, 0 deletions
diff --git a/volumes.c b/volumes.c
index e3654a4c..652f0087 100644
--- a/volumes.c
+++ b/volumes.c
@@ -2356,3 +2356,72 @@ u64 btrfs_stripe_length(struct btrfs_fs_info *fs_info,
}
return stripe_len;
}
+
+/*
+ * Return 0 if size of @device is already good
+ * Return >0 if size of @device is not aligned but fixed without problems
+ * Return <0 if something wrong happened when aligning the size of @device
+ */
+int btrfs_fix_device_size(struct btrfs_fs_info *fs_info,
+ struct btrfs_device *device)
+{
+ struct btrfs_trans_handle *trans;
+ struct btrfs_key key;
+ struct btrfs_path path;
+ struct btrfs_root *chunk_root = fs_info->chunk_root;
+ struct btrfs_dev_item *di;
+ u64 old_bytes = device->total_bytes;
+ int ret;
+
+ if (IS_ALIGNED(old_bytes, fs_info->sectorsize))
+ return 0;
+
+ /* Align the in-memory total_bytes first, and use it as correct size */
+ device->total_bytes = round_down(device->total_bytes,
+ fs_info->sectorsize);
+
+ key.objectid = BTRFS_DEV_ITEMS_OBJECTID;
+ key.type = BTRFS_DEV_ITEM_KEY;
+ key.offset = device->devid;
+
+ trans = btrfs_start_transaction(chunk_root, 1);
+ if (IS_ERR(trans)) {
+ ret = PTR_ERR(trans);
+ error("error starting transaction: %d (%s)",
+ ret, strerror(-ret));
+ return ret;
+ }
+
+ btrfs_init_path(&path);
+ ret = btrfs_search_slot(trans, chunk_root, &key, &path, 0, 1);
+ if (ret > 0) {
+ error("failed to find DEV_ITEM for devid %llu", device->devid);
+ ret = -ENOENT;
+ goto err;
+ }
+ if (ret < 0) {
+ error("failed to search chunk root: %d (%s)",
+ ret, strerror(-ret));
+ goto err;
+ }
+ di = btrfs_item_ptr(path.nodes[0], path.slots[0], struct btrfs_dev_item);
+ btrfs_set_device_total_bytes(path.nodes[0], di, device->total_bytes);
+ btrfs_mark_buffer_dirty(path.nodes[0]);
+ ret = btrfs_commit_transaction(trans, chunk_root);
+ if (ret < 0) {
+ error("failed to commit current transaction: %d (%s)",
+ ret, strerror(-ret));
+ btrfs_release_path(&path);
+ return ret;
+ }
+ btrfs_release_path(&path);
+ printf("Fixed device size for devid %llu, old size: %llu new size: %llu\n",
+ device->devid, old_bytes, device->total_bytes);
+ return 1;
+
+err:
+ /* We haven't modified anything, it's OK to commit current trans */
+ btrfs_commit_transaction(trans, chunk_root);
+ btrfs_release_path(&path);
+ return ret;
+}
diff --git a/volumes.h b/volumes.h
index d35a4e65..89bd4aa8 100644
--- a/volumes.h
+++ b/volumes.h
@@ -245,4 +245,6 @@ int btrfs_check_chunk_valid(struct btrfs_fs_info *fs_info,
u64 btrfs_stripe_length(struct btrfs_fs_info *fs_info,
struct extent_buffer *leaf,
struct btrfs_chunk *chunk);
+int btrfs_fix_device_size(struct btrfs_fs_info *fs_info,
+ struct btrfs_device *device);
#endif