@@ -28,6 +28,9 @@
#include "cmds/commands.h"
#include "cmds/props.h"
#include "kernel-shared/ctree.h"
+#include "kernel-shared/volumes.h"
+#include "kernel-shared/disk-io.h"
+#include "kernel-shared/transaction.h"
#include "common/open-utils.h"
#include "common/utils.h"
#include "common/help.h"
@@ -377,6 +380,115 @@ static struct ull_charp_pair_t {
{0, NULL}
};
+static int find_device(const char *object, struct btrfs_device **device_ret)
+{
+ struct btrfs_fs_devices *fs_devices;
+ struct list_head *fs_uuids;
+ struct stat sttarget, st;
+
+ if (stat(object, &sttarget) < 0)
+ return -EACCES;
+
+ fs_uuids = btrfs_scanned_uuids();
+
+ list_for_each_entry(fs_devices, fs_uuids, list) {
+ struct btrfs_device *device;
+
+ list_for_each_entry(device, &fs_devices->devices, dev_list) {
+
+ if (stat(device->name, &st) < 0)
+ return -EACCES;
+
+ if (st.st_rdev == sttarget.st_rdev) {
+ *device_ret = device;
+ return 0;
+ }
+ }
+ }
+
+ return -ENODEV;
+}
+
+static int prop_allocation_hint_unmounted(const char *object,
+ const char *name,
+ const char *val)
+{
+
+ struct btrfs_device *device;
+ int ret;
+ struct btrfs_root *root = NULL;
+
+ root = open_ctree(object, btrfs_sb_offset(0), 0);
+ if (!root)
+ return -ENODEV;
+
+ ret = find_device(object, &device);
+ if (ret)
+ goto out;
+
+ if (!val) {
+ int i;
+ u64 v = device->flags & BTRFS_DEV_ALLOCATION_HINT_MASK;
+
+ for (i = 0 ; allocation_hint_description[i].descr ; i++)
+ if (v == allocation_hint_description[i].value)
+ break;
+
+ if (allocation_hint_description[i].descr)
+ printf("allocation_hint=%s\n",
+ allocation_hint_description[i].descr);
+ else
+ printf("allocation_hint=unknown:%llu\n", v);
+ ret = 0;
+ } else {
+ struct btrfs_trans_handle *trans;
+ int i;
+ u64 v;
+
+ for (i = 0 ; allocation_hint_description[i].descr ; i++)
+ if (!strcmp(val, allocation_hint_description[i].descr))
+ break;
+
+ if (allocation_hint_description[i].descr) {
+ v = allocation_hint_description[i].value;
+ } else if (sscanf(val, "%llu", &v) != 1) {
+ error("Invalid value '%s'\n", val);
+ ret = -3;
+ goto out;
+ }
+ if (v & ~BTRFS_DEV_ALLOCATION_HINT_MASK) {
+ error("Invalid value '%s'\n", val);
+ ret = -3;
+ goto out;
+ }
+
+ trans = btrfs_start_transaction(device->fs_info->chunk_root, 1);
+ if (IS_ERR(trans)) {
+ ret = PTR_ERR(trans);
+ goto out;
+ }
+
+ /* Manually update the device item in chunk tree */
+ ret = btrfs_update_device(trans, device);
+ if (ret < 0) {
+ errno = -ret;
+ goto out;
+ }
+
+ /*
+ * Commit transaction not only to save the above change but also update
+ * the device item in super block.
+ */
+ ret = btrfs_commit_transaction(trans, device->fs_info->chunk_root);
+
+ }
+out:
+ if (root)
+ close_ctree(root);
+ return ret;
+
+}
+
static int prop_allocation_hint(enum prop_object_type type,
const char *object,
const char *name,
@@ -394,6 +506,9 @@ static int prop_allocation_hint(enum prop_object_type type,
ret = btrfs_find_devid_uuid_by_dev(object, &devid,
sysfs_file + sizeof(BTRFSYSFS) - 1);
+ if (ret == -ENODEV)
+ return prop_allocation_hint_unmounted(object, name, val);
+
if (ret)
goto out;