@@ -320,7 +320,10 @@ static const char *prop_compression_extract(struct inode *inode)
static int prop_readmirror_validate(struct inode *inode, const char *value,
size_t len)
{
+ char *value_dup;
+ char *devid_str;
struct btrfs_root *root = BTRFS_I(inode)->root;
+ struct btrfs_fs_devices *fs_devices = root->fs_info->fs_devices;
if (root->root_key.objectid != BTRFS_FS_TREE_OBJECTID)
return -EINVAL;
@@ -328,16 +331,80 @@ static int prop_readmirror_validate(struct inode *inode, const char *value,
if (!len)
return 0;
- return -EINVAL;
+ if (len <= 5 || strncmp("devid", value, 5))
+ return -EINVAL;
+
+ value_dup = kstrndup(value + 5, len - 5, GFP_KERNEL);
+ if (!value_dup)
+ return -ENOMEM;
+
+ while ((devid_str = strsep(&value_dup, ",")) != NULL) {
+ u64 devid;
+ struct btrfs_device *device;
+
+ if (kstrtoull(devid_str, 10, &devid)) {
+ kfree(value_dup);
+ return -EINVAL;
+ }
+
+ device = btrfs_find_device(fs_devices, devid, NULL, NULL, false);
+ if (!device) {
+ kfree(value_dup);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
}
static int prop_readmirror_apply(struct inode *inode, const char *value,
size_t len)
{
+ char *value_dup;
+ char *devid_str;
+ struct btrfs_device *device;
struct btrfs_fs_devices *fs_devices = btrfs_sb(inode->i_sb)->fs_devices;
+ if (value) {
+ value_dup = kstrndup(value + 5, len - 5, GFP_KERNEL);
+ if (!value_dup)
+ return -ENOMEM;
+ }
+
+ /* Both set and reset has to clear the exisiting values */
+ list_for_each_entry(device, &fs_devices->devices, dev_list) {
+ if (test_bit(BTRFS_DEV_STATE_READ_OPTIMISED,
+ &device->dev_state)) {
+ clear_bit(BTRFS_DEV_STATE_READ_OPTIMISED,
+ &device->dev_state);
+ }
+ }
fs_devices->readmirror_policy = BTRFS_READMIRROR_DEFAULT;
+ /* Its only reset so just return */
+ if (!value)
+ return 0;
+
+ while ((devid_str = strsep(&value_dup, ",")) != NULL) {
+ u64 devid;
+
+ /* Has been verified in validate() this will not fail */
+ if (kstrtoull(devid_str, 10, &devid)) {
+ kfree(value_dup);
+ return -EINVAL;
+ }
+
+ device = btrfs_find_device(fs_devices, devid, NULL, NULL, false);
+ if (!device) {
+ kfree(value_dup);
+ return -EINVAL;
+ }
+
+ set_bit(BTRFS_DEV_STATE_READ_OPTIMISED, &device->dev_state);
+ fs_devices->readmirror_policy = BTRFS_READMIRROR_DEVID;
+ }
+
+ kfree(value_dup);
return 0;
}
@@ -5495,6 +5495,7 @@ static int find_live_mirror(struct btrfs_fs_info *fs_info,
int preferred_mirror;
int tolerance;
struct btrfs_device *srcdev;
+ bool found = false;
ASSERT((map->type &
(BTRFS_BLOCK_GROUP_RAID1 | BTRFS_BLOCK_GROUP_RAID10)));
@@ -5505,6 +5506,21 @@ static int find_live_mirror(struct btrfs_fs_info *fs_info,
num_stripes = map->num_stripes;
switch(fs_info->fs_devices->readmirror_policy) {
+ case BTRFS_READMIRROR_DEVID:
+ /* skip raid10 for now */
+ if (map->type & BTRFS_BLOCK_GROUP_RAID1) {
+ for (i = first; i < first + num_stripes; i++) {
+ if (test_bit(BTRFS_DEV_STATE_READ_OPTIMISED,
+ &map->stripes[i].dev->dev_state)) {
+ preferred_mirror = i;
+ found = true;
+ break;
+ }
+ }
+ if (found)
+ break;
+ }
+ /* fall through */
case BTRFS_READMIRROR_DEFAULT:
/* fall through */
default:
@@ -41,6 +41,7 @@ struct btrfs_pending_bios {
#define BTRFS_DEV_STATE_MISSING (2)
#define BTRFS_DEV_STATE_REPLACE_TGT (3)
#define BTRFS_DEV_STATE_FLUSH_SENT (4)
+#define BTRFS_DEV_STATE_READ_OPTIMISED (5)
struct btrfs_device {
struct list_head dev_list;
@@ -206,6 +207,7 @@ BTRFS_DEVICE_GETSET_FUNCS(bytes_used);
enum btrfs_readmirror_policy {
BTRFS_READMIRROR_DEFAULT,
+ BTRFS_READMIRROR_DEVID,
};
struct btrfs_fs_devices {