diff mbox series

[5/5] btrfs: Switch seed device to list api

Message ID 20200715104850.19071-6-nborisov@suse.com (mailing list archive)
State New, archived
Headers show
Series Convert seed devices to proper list API | expand

Commit Message

Nikolay Borisov July 15, 2020, 10:48 a.m. UTC
While this patch touches a bunch of files the conversion is
straighforward. Instead of using the implicit linked list anchored at
btrfs_fs_devices::seed the code is switched to using
list_for_each_entry. Previous patches in the series already factored out
code that processed both main and seed devices so in those cases
the factored out functions are called on the main fs_devices and then
on every seed dev inside list_for_each_entry.

Using list api also allows to simplify deletion from the seed dev list
performed in btrfs_rm_device and btrfs_rm_dev_replace_free_srcdev by
substituting a while() loop with a simple list_del_init.

Signed-off-by: Nikolay Borisov <nborisov@suse.com>
---
 fs/btrfs/disk-io.c |  39 ++++++++---------
 fs/btrfs/reada.c   |   9 ++--
 fs/btrfs/volumes.c | 102 ++++++++++++++++++++++-----------------------
 fs/btrfs/volumes.h |   2 +-
 4 files changed, 72 insertions(+), 80 deletions(-)

Comments

kernel test robot July 15, 2020, 1:14 p.m. UTC | #1
Hi Nikolay,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on kdave/for-next]
[also build test ERROR on next-20200715]
[cannot apply to v5.8-rc5]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Nikolay-Borisov/Convert-seed-devices-to-proper-list-API/20200715-185102
base:   https://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux.git for-next
config: x86_64-rhel-7.6-kselftests (attached as .config)
compiler: gcc-9 (Debian 9.3.0-14) 9.3.0
reproduce (this is a W=1 build):
        # save the attached .config to linux build tree
        make W=1 ARCH=x86_64 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All error/warnings (new ones prefixed by >>):

   In file included from fs/btrfs/volumes.c:17:
   fs/btrfs/ctree.h:2271:8: warning: type qualifiers ignored on function return type [-Wignored-qualifiers]
    2271 | size_t __const btrfs_get_num_csums(void);
         |        ^~~~~~~
   In file included from fs/btrfs/volumes.c:28:
   fs/btrfs/sysfs.h:16:1: warning: type qualifiers ignored on function return type [-Wignored-qualifiers]
      16 | const char * const btrfs_feature_set_name(enum btrfs_feature_set set);
         | ^~~~~
   fs/btrfs/volumes.c:1030:6: warning: no previous prototype for '__btrfs_free_extra_devids' [-Wmissing-prototypes]
    1030 | void __btrfs_free_extra_devids(struct btrfs_fs_devices *fs_devices, int step,
         |      ^~~~~~~~~~~~~~~~~~~~~~~~~
   fs/btrfs/volumes.c: In function 'btrfs_rm_dev_replace_free_srcdev':
>> fs/btrfs/volumes.c:2219:28: warning: variable 'tmp_fs_devices' set but not used [-Wunused-but-set-variable]
    2219 |   struct btrfs_fs_devices *tmp_fs_devices;
         |                            ^~~~~~~~~~~~~~
--
   In file included from fs/btrfs/delayed-inode.h:17,
                    from fs/btrfs/super.c:30:
   fs/btrfs/ctree.h:2271:8: warning: type qualifiers ignored on function return type [-Wignored-qualifiers]
    2271 | size_t __const btrfs_get_num_csums(void);
         |        ^~~~~~~
   In file included from fs/btrfs/super.c:46:
   fs/btrfs/sysfs.h:16:1: warning: type qualifiers ignored on function return type [-Wignored-qualifiers]
      16 | const char * const btrfs_feature_set_name(enum btrfs_feature_set set);
         | ^~~~~
   fs/btrfs/super.c: In function 'btrfs_show_devname':
>> fs/btrfs/super.c:2404:28: error: 'struct btrfs_fs_devices' has no member named 'seed'
    2404 |   cur_devices = cur_devices->seed;
         |                            ^~

vim +2404 fs/btrfs/super.c

9e7cc91a6d18a4 Wang Xiaoguang  2016-08-01  2377  
9c5085c147989d Josef Bacik     2012-06-05  2378  static int btrfs_show_devname(struct seq_file *m, struct dentry *root)
9c5085c147989d Josef Bacik     2012-06-05  2379  {
9c5085c147989d Josef Bacik     2012-06-05  2380  	struct btrfs_fs_info *fs_info = btrfs_sb(root->d_sb);
9c5085c147989d Josef Bacik     2012-06-05  2381  	struct btrfs_fs_devices *cur_devices;
9c5085c147989d Josef Bacik     2012-06-05  2382  	struct btrfs_device *dev, *first_dev = NULL;
9c5085c147989d Josef Bacik     2012-06-05  2383  	struct list_head *head;
9c5085c147989d Josef Bacik     2012-06-05  2384  
88c14590cdd6f3 David Sterba    2018-03-16  2385  	/*
88c14590cdd6f3 David Sterba    2018-03-16  2386  	 * Lightweight locking of the devices. We should not need
88c14590cdd6f3 David Sterba    2018-03-16  2387  	 * device_list_mutex here as we only read the device data and the list
88c14590cdd6f3 David Sterba    2018-03-16  2388  	 * is protected by RCU.  Even if a device is deleted during the list
88c14590cdd6f3 David Sterba    2018-03-16  2389  	 * traversals, we'll get valid data, the freeing callback will wait at
52042d8e82ff50 Andrea Gelmini  2018-11-28  2390  	 * least until the rcu_read_unlock.
88c14590cdd6f3 David Sterba    2018-03-16  2391  	 */
88c14590cdd6f3 David Sterba    2018-03-16  2392  	rcu_read_lock();
9c5085c147989d Josef Bacik     2012-06-05  2393  	cur_devices = fs_info->fs_devices;
9c5085c147989d Josef Bacik     2012-06-05  2394  	while (cur_devices) {
9c5085c147989d Josef Bacik     2012-06-05  2395  		head = &cur_devices->devices;
88c14590cdd6f3 David Sterba    2018-03-16  2396  		list_for_each_entry_rcu(dev, head, dev_list) {
e6e674bd4d54fe Anand Jain      2017-12-04  2397  			if (test_bit(BTRFS_DEV_STATE_MISSING, &dev->dev_state))
aa9ddcd4b55571 Josef Bacik     2012-08-02  2398  				continue;
0aeb8a6e67cdde Anand Jain      2014-06-30  2399  			if (!dev->name)
0aeb8a6e67cdde Anand Jain      2014-06-30  2400  				continue;
9c5085c147989d Josef Bacik     2012-06-05  2401  			if (!first_dev || dev->devid < first_dev->devid)
9c5085c147989d Josef Bacik     2012-06-05  2402  				first_dev = dev;
9c5085c147989d Josef Bacik     2012-06-05  2403  		}
9c5085c147989d Josef Bacik     2012-06-05 @2404  		cur_devices = cur_devices->seed;
9c5085c147989d Josef Bacik     2012-06-05  2405  	}
9c5085c147989d Josef Bacik     2012-06-05  2406  
672d599041c862 Misono Tomohiro 2018-08-02  2407  	if (first_dev)
672d599041c862 Misono Tomohiro 2018-08-02  2408  		seq_escape(m, rcu_str_deref(first_dev->name), " \t\n\\");
672d599041c862 Misono Tomohiro 2018-08-02  2409  	else
9c5085c147989d Josef Bacik     2012-06-05  2410  		WARN_ON(1);
88c14590cdd6f3 David Sterba    2018-03-16  2411  	rcu_read_unlock();
9c5085c147989d Josef Bacik     2012-06-05  2412  	return 0;
9c5085c147989d Josef Bacik     2012-06-05  2413  }
9c5085c147989d Josef Bacik     2012-06-05  2414  

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
Nikolay Borisov July 24, 2020, 7:36 a.m. UTC | #2
On 15.07.20 г. 13:48 ч., Nikolay Borisov wrote:
> While this patch touches a bunch of files the conversion is
> straighforward. Instead of using the implicit linked list anchored at
> btrfs_fs_devices::seed the code is switched to using
> list_for_each_entry. Previous patches in the series already factored out
> code that processed both main and seed devices so in those cases
> the factored out functions are called on the main fs_devices and then
> on every seed dev inside list_for_each_entry.
> 
> Using list api also allows to simplify deletion from the seed dev list
> performed in btrfs_rm_device and btrfs_rm_dev_replace_free_srcdev by
> substituting a while() loop with a simple list_del_init.
> 
> Signed-off-by: Nikolay Borisov <nborisov@suse.com>

<snip>

> @@ -6728,8 +6718,8 @@ static struct btrfs_fs_devices *open_seed_devices(struct btrfs_fs_info *fs_info,
>  		goto out;
>  	}
>  
> -	fs_devices->seed = fs_info->fs_devices->seed;
> -	fs_info->fs_devices->seed = fs_devices;
> +	ASSERT(list_empty(&fs_devices->seed_list));

That assert is useless, since fs_devices at this point are a clone from
clone_fs_devices hence its seed_list cannot be non-empty. Consider this
when removing merging it.

> +	list_add_tail(&fs_devices->seed_list, &fs_info->fs_devices->seed_list);
>  out:
>  	return fs_devices;
>  }
> @@ -7141,17 +7131,23 @@ int btrfs_read_chunk_tree(struct btrfs_fs_info *fs_info)
>  
>  void btrfs_init_devices_late(struct btrfs_fs_info *fs_info)
>  {
> -	struct btrfs_fs_devices *fs_devices = fs_info->fs_devices;
> +	struct btrfs_fs_devices *fs_devices = fs_info->fs_devices, *seed_devs;
>  	struct btrfs_device *device;
>  
> -	while (fs_devices) {
> -		mutex_lock(&fs_devices->device_list_mutex);
> -		list_for_each_entry(device, &fs_devices->devices, dev_list)
> +	fs_devices->fs_info = fs_info;
> +
> +	mutex_lock(&fs_devices->device_list_mutex);
> +	list_for_each_entry(device, &fs_devices->devices, dev_list)
> +		device->fs_info = fs_info;
> +	mutex_unlock(&fs_devices->device_list_mutex);
> +
> +	list_for_each_entry(seed_devs, &fs_devices->seed_list, seed_list) {
> +		mutex_lock(&seed_devs->device_list_mutex);
> +		list_for_each_entry(device, &seed_devs->devices, dev_list)
>  			device->fs_info = fs_info;
> -		mutex_unlock(&fs_devices->device_list_mutex);
> +		mutex_unlock(&seed_devs->device_list_mutex);
>  
> -		fs_devices->fs_info = fs_info;
> -		fs_devices = fs_devices->seed;
> +		seed_devs->fs_info = fs_info;
>  	}
>  }
>  
> @@ -7527,8 +7523,10 @@ static int verify_one_dev_extent(struct btrfs_fs_info *fs_info,
>  
>  	/* It's possible this device is a dummy for seed device */
>  	if (dev->disk_total_bytes == 0) {
> -		dev = btrfs_find_device(fs_info->fs_devices->seed, devid, NULL,
> -					NULL, false);
> +		struct btrfs_fs_devices *devs;
> +		devs = list_first_entry(&fs_info->fs_devices->seed_list,
> +					struct btrfs_fs_devices, seed_list);
> +		dev = btrfs_find_device(devs, devid, NULL, NULL, false);
>  		if (!dev) {
>  			btrfs_err(fs_info, "failed to find seed devid %llu",
>  				  devid);
> diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h
> index fc283fdbcece..709f4aacbd3f 100644
> --- a/fs/btrfs/volumes.h
> +++ b/fs/btrfs/volumes.h
> @@ -246,7 +246,7 @@ struct btrfs_fs_devices {
>  	 */
>  	struct list_head alloc_list;
>  
> -	struct btrfs_fs_devices *seed;
> +	struct list_head seed_list;
>  	bool seeding;
>  
>  	int opened;
>
Anand Jain Sept. 2, 2020, 3:58 p.m. UTC | #3
The seed of the current sprout should rather be at the head instead of 
at the bottom.


> @@ -2397,7 +2381,7 @@ static int btrfs_prepare_sprout(struct btrfs_fs_info *fs_info)
>   	fs_devices->open_devices = 0;
>   	fs_devices->missing_devices = 0;
>   	fs_devices->rotating = false;
> -	fs_devices->seed = seed_devices;
> +	list_add_tail(&seed_devices->seed_list, &fs_devices->seed_list);

  It should be list_add_head.

>   
>   	generate_random_uuid(fs_devices->fsid);
>   	memcpy(fs_devices->metadata_uuid, fs_devices->fsid, BTRFS_FSID_SIZE);




> @@ -6728,8 +6718,8 @@ static struct btrfs_fs_devices *open_seed_devices(struct btrfs_fs_info *fs_info,
>   		goto out;
>   	}
>   
> -	fs_devices->seed = fs_info->fs_devices->seed;
> -	fs_info->fs_devices->seed = fs_devices;
> +	ASSERT(list_empty(&fs_devices->seed_list));
> +	list_add_tail(&fs_devices->seed_list, &fs_info->fs_devices->seed_list);

  It should be list_add_head.

>   out:
>   	return fs_devices;
>   }


Thanks, Anand
Nikolay Borisov Sept. 3, 2020, 9:03 a.m. UTC | #4
On 2.09.20 г. 18:58 ч., Anand Jain wrote:
> 
> 
> The seed of the current sprout should rather be at the head instead of
> at the bottom.
> 
> 
>> @@ -2397,7 +2381,7 @@ static int btrfs_prepare_sprout(struct
>> btrfs_fs_info *fs_info)
>>       fs_devices->open_devices = 0;
>>       fs_devices->missing_devices = 0;
>>       fs_devices->rotating = false;
>> -    fs_devices->seed = seed_devices;
>> +    list_add_tail(&seed_devices->seed_list, &fs_devices->seed_list);
> 
>  It should be list_add_head.

Generally yes, but in this case I don't think it makes any functional
differences so even adding at the tail is fine.

> 
>>         generate_random_uuid(fs_devices->fsid);
>>       memcpy(fs_devices->metadata_uuid, fs_devices->fsid,
>> BTRFS_FSID_SIZE);
> 
> 
> 
> 
>> @@ -6728,8 +6718,8 @@ static struct btrfs_fs_devices
>> *open_seed_devices(struct btrfs_fs_info *fs_info,
>>           goto out;
>>       }
>>   -    fs_devices->seed = fs_info->fs_devices->seed;
>> -    fs_info->fs_devices->seed = fs_devices;
>> +    ASSERT(list_empty(&fs_devices->seed_list));
>> +    list_add_tail(&fs_devices->seed_list,
>> &fs_info->fs_devices->seed_list);
> 
>  It should be list_add_head.
> 
>>   out:
>>       return fs_devices;
>>   }
> 
> 
> Thanks, Anand
>
Anand Jain Sept. 3, 2020, 9:33 a.m. UTC | #5
On 3/9/20 5:03 pm, Nikolay Borisov wrote:
> 
> 
> On 2.09.20 г. 18:58 ч., Anand Jain wrote:
>>
>>
>> The seed of the current sprout should rather be at the head instead of
>> at the bottom.
>>
>>
>>> @@ -2397,7 +2381,7 @@ static int btrfs_prepare_sprout(struct
>>> btrfs_fs_info *fs_info)
>>>        fs_devices->open_devices = 0;
>>>        fs_devices->missing_devices = 0;
>>>        fs_devices->rotating = false;
>>> -    fs_devices->seed = seed_devices;
>>> +    list_add_tail(&seed_devices->seed_list, &fs_devices->seed_list);
>>
>>   It should be list_add_head.
> 
> Generally yes, but in this case I don't think it makes any functional
> differences so even adding at the tail is fine.
> 

  Hm No. Adding to the head matches to the order of dependency. As it 
was in the while loop.

Thanks, Anand



>>
>>>          generate_random_uuid(fs_devices->fsid);
>>>        memcpy(fs_devices->metadata_uuid, fs_devices->fsid,
>>> BTRFS_FSID_SIZE);
>>
>>
>>
>>
>>> @@ -6728,8 +6718,8 @@ static struct btrfs_fs_devices
>>> *open_seed_devices(struct btrfs_fs_info *fs_info,
>>>            goto out;
>>>        }
>>>    -    fs_devices->seed = fs_info->fs_devices->seed;
>>> -    fs_info->fs_devices->seed = fs_devices;
>>> +    ASSERT(list_empty(&fs_devices->seed_list));
>>> +    list_add_tail(&fs_devices->seed_list,
>>> &fs_info->fs_devices->seed_list);
>>
>>   It should be list_add_head.
>>
>>>    out:
>>>        return fs_devices;
>>>    }
>>
>>
>> Thanks, Anand
>>
David Sterba Sept. 10, 2020, 4:28 p.m. UTC | #6
On Thu, Sep 03, 2020 at 05:33:26PM +0800, Anand Jain wrote:
> 
> 
> On 3/9/20 5:03 pm, Nikolay Borisov wrote:
> > 
> > 
> > On 2.09.20 г. 18:58 ч., Anand Jain wrote:
> >>
> >>
> >> The seed of the current sprout should rather be at the head instead of
> >> at the bottom.
> >>
> >>
> >>> @@ -2397,7 +2381,7 @@ static int btrfs_prepare_sprout(struct
> >>> btrfs_fs_info *fs_info)
> >>>        fs_devices->open_devices = 0;
> >>>        fs_devices->missing_devices = 0;
> >>>        fs_devices->rotating = false;
> >>> -    fs_devices->seed = seed_devices;
> >>> +    list_add_tail(&seed_devices->seed_list, &fs_devices->seed_list);
> >>
> >>   It should be list_add_head.
> > 
> > Generally yes, but in this case I don't think it makes any functional
> > differences so even adding at the tail is fine.
> > 
> 
>   Hm No. Adding to the head matches to the order of dependency. As it 
> was in the while loop.

Following the same order sounds like a better idea to me. If there's
really no differece and we want to add the entry to the tail, then it
would be another patch with proper reasoning. I'll update it to do
list_head.
diff mbox series

Patch

diff --git a/fs/btrfs/disk-io.c b/fs/btrfs/disk-io.c
index 2fb0b43bc1c5..8767d47f6e29 100644
--- a/fs/btrfs/disk-io.c
+++ b/fs/btrfs/disk-io.c
@@ -545,33 +545,30 @@  static int csum_dirty_buffer(struct btrfs_fs_info *fs_info, struct page *page)
 static int check_tree_block_fsid(struct extent_buffer *eb)
 {
 	struct btrfs_fs_info *fs_info = eb->fs_info;
-	struct btrfs_fs_devices *fs_devices = fs_info->fs_devices;
+	struct btrfs_fs_devices *fs_devices = fs_info->fs_devices, *seed_devs;
 	u8 fsid[BTRFS_FSID_SIZE];
-	int ret = 1;
+	u8 *metadata_uuid;
 
 	read_extent_buffer(eb, fsid, offsetof(struct btrfs_header, fsid),
 			   BTRFS_FSID_SIZE);
-	while (fs_devices) {
-		u8 *metadata_uuid;
+	/*
+	 * Checking the incompat flag is only valid for the current
+	 * fs. For seed devices it's forbidden to have their uuid
+	 * changed so reading ->fsid in this case is fine
+	 */
+	if (btrfs_fs_incompat(fs_info, METADATA_UUID))
+		metadata_uuid = fs_devices->metadata_uuid;
+	else
+		metadata_uuid = fs_devices->fsid;
 
-		/*
-		 * Checking the incompat flag is only valid for the current
-		 * fs. For seed devices it's forbidden to have their uuid
-		 * changed so reading ->fsid in this case is fine
-		 */
-		if (fs_devices == fs_info->fs_devices &&
-		    btrfs_fs_incompat(fs_info, METADATA_UUID))
-			metadata_uuid = fs_devices->metadata_uuid;
-		else
-			metadata_uuid = fs_devices->fsid;
+	if (!memcmp(fsid, metadata_uuid, BTRFS_FSID_SIZE))
+		return 0;
 
-		if (!memcmp(fsid, metadata_uuid, BTRFS_FSID_SIZE)) {
-			ret = 0;
-			break;
-		}
-		fs_devices = fs_devices->seed;
-	}
-	return ret;
+	list_for_each_entry(seed_devs, &fs_devices->seed_list, seed_list)
+		if (!memcmp(fsid, seed_devs->fsid, BTRFS_FSID_SIZE))
+			return 0;
+
+	return 1;
 }
 
 static int btree_readpage_end_io_hook(struct btrfs_io_bio *io_bio,
diff --git a/fs/btrfs/reada.c b/fs/btrfs/reada.c
index aa9d24ed56d7..ac5b07ded0fe 100644
--- a/fs/btrfs/reada.c
+++ b/fs/btrfs/reada.c
@@ -792,16 +792,13 @@  static int __reada_start_for_fsdevs(struct btrfs_fs_devices *fs_devices)
 
 static void __reada_start_machine(struct btrfs_fs_info *fs_info)
 {
-	struct btrfs_fs_devices *fs_devices = fs_info->fs_devices;
+	struct btrfs_fs_devices *fs_devices = fs_info->fs_devices, *seed_devs;
 	int i;
 	u64 enqueued = 0;
 
-again:
 	enqueued += __reada_start_for_fsdevs(fs_devices);
-	if (fs_devices->seed) {
-		fs_devices = fs_devices->seed;
-		goto again;
-	}
+	list_for_each_entry(seed_devs, &fs_devices->seed_list, seed_list)
+		enqueued += __reada_start_for_fsdevs(seed_devs);
 
 	if (enqueued == 0)
 		return;
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
index a0cdd027e99c..7fce7a220a76 100644
--- a/fs/btrfs/volumes.c
+++ b/fs/btrfs/volumes.c
@@ -350,6 +350,7 @@  static struct btrfs_fs_devices *alloc_fs_devices(const u8 *fsid,
 	INIT_LIST_HEAD(&fs_devs->devices);
 	INIT_LIST_HEAD(&fs_devs->alloc_list);
 	INIT_LIST_HEAD(&fs_devs->fs_list);
+	INIT_LIST_HEAD(&fs_devs->seed_list);
 	if (fsid)
 		memcpy(fs_devs->fsid, fsid, BTRFS_FSID_SIZE);
 
@@ -1087,14 +1088,13 @@  void __btrfs_free_extra_devids(struct btrfs_fs_devices *fs_devices, int step,
 void btrfs_free_extra_devids(struct btrfs_fs_devices *fs_devices, int step)
 {
 	struct btrfs_device *latest_dev = NULL;
+	struct btrfs_fs_devices *seed_dev;
 
 	mutex_lock(&uuid_mutex);
-again:
 	__btrfs_free_extra_devids(fs_devices, step, &latest_dev);
-	if (fs_devices->seed) {
-		fs_devices = fs_devices->seed;
-		goto again;
-	}
+
+	list_for_each_entry(seed_dev, &fs_devices->seed_list, seed_list)
+		__btrfs_free_extra_devids(seed_dev, step, &latest_dev);
 
 	fs_devices->latest_bdev = latest_dev->bdev;
 
@@ -1168,20 +1168,18 @@  static void close_fs_devices(struct btrfs_fs_devices *fs_devices)
 
 void btrfs_close_devices(struct btrfs_fs_devices *fs_devices)
 {
-	struct btrfs_fs_devices *seed_devices = NULL;
+	LIST_HEAD(list);
+	struct btrfs_fs_devices *tmp;
 
 	mutex_lock(&uuid_mutex);
 	close_fs_devices(fs_devices);
-	if (!fs_devices->opened) {
-		seed_devices = fs_devices->seed;
-		fs_devices->seed = NULL;
-	}
+	if (!fs_devices->opened)
+		list_splice_init(&fs_devices->seed_list, &list);
 	mutex_unlock(&uuid_mutex);
 
-	while (seed_devices) {
-		fs_devices = seed_devices;
-		seed_devices = fs_devices->seed;
+	list_for_each_entry_safe(fs_devices, tmp, &list, seed_list) {
 		close_fs_devices(fs_devices);
+		list_del(&fs_devices->seed_list);
 		free_fs_devices(fs_devices);
 	}
 }
@@ -2154,14 +2152,7 @@  int btrfs_rm_device(struct btrfs_fs_info *fs_info, const char *device_path,
 	btrfs_free_device(device);
 
 	if (cur_devices->open_devices == 0) {
-		while (fs_devices) {
-			if (fs_devices->seed == cur_devices) {
-				fs_devices->seed = cur_devices->seed;
-				break;
-			}
-			fs_devices = fs_devices->seed;
-		}
-		cur_devices->seed = NULL;
+		list_del_init(&cur_devices->seed_list);
 		close_fs_devices(cur_devices);
 		free_fs_devices(cur_devices);
 	}
@@ -2236,14 +2227,7 @@  void btrfs_rm_dev_replace_free_srcdev(struct btrfs_device *srcdev)
 		ASSERT(fs_devices->seeding);
 
 		tmp_fs_devices = fs_info->fs_devices;
-		while (tmp_fs_devices) {
-			if (tmp_fs_devices->seed == fs_devices) {
-				tmp_fs_devices->seed = fs_devices->seed;
-				break;
-			}
-			tmp_fs_devices = tmp_fs_devices->seed;
-		}
-		fs_devices->seed = NULL;
+		list_del_init(&fs_devices->seed_list);
 		close_fs_devices(fs_devices);
 		free_fs_devices(fs_devices);
 	}
@@ -2397,7 +2381,7 @@  static int btrfs_prepare_sprout(struct btrfs_fs_info *fs_info)
 	fs_devices->open_devices = 0;
 	fs_devices->missing_devices = 0;
 	fs_devices->rotating = false;
-	fs_devices->seed = seed_devices;
+	list_add_tail(&seed_devices->seed_list, &fs_devices->seed_list);
 
 	generate_random_uuid(fs_devices->fsid);
 	memcpy(fs_devices->metadata_uuid, fs_devices->fsid, BTRFS_FSID_SIZE);
@@ -6430,11 +6414,23 @@  struct btrfs_device *btrfs_find_device(struct btrfs_fs_devices *fs_devices,
 				       bool seed)
 {
 	struct btrfs_device *device;
+	struct btrfs_fs_devices *seed_devs;
 
-	while (fs_devices) {
+	if (!fsid ||
+	    !memcmp(fs_devices->metadata_uuid, fsid, BTRFS_FSID_SIZE)) {
+		list_for_each_entry(device, &fs_devices->devices,
+				    dev_list) {
+			if (device->devid == devid &&
+			    (!uuid || memcmp(device->uuid, uuid,
+					     BTRFS_UUID_SIZE) == 0))
+				return device;
+		}
+	}
+
+	list_for_each_entry(seed_devs, &fs_devices->seed_list, seed_list) {
 		if (!fsid ||
-		    !memcmp(fs_devices->metadata_uuid, fsid, BTRFS_FSID_SIZE)) {
-			list_for_each_entry(device, &fs_devices->devices,
+		    !memcmp(seed_devs->metadata_uuid, fsid, BTRFS_FSID_SIZE)) {
+			list_for_each_entry(device, &seed_devs->devices,
 					    dev_list) {
 				if (device->devid == devid &&
 				    (!uuid || memcmp(device->uuid, uuid,
@@ -6442,11 +6438,8 @@  struct btrfs_device *btrfs_find_device(struct btrfs_fs_devices *fs_devices,
 					return device;
 			}
 		}
-		if (seed)
-			fs_devices = fs_devices->seed;
-		else
-			return NULL;
 	}
+
 	return NULL;
 }
 
@@ -6688,13 +6681,10 @@  static struct btrfs_fs_devices *open_seed_devices(struct btrfs_fs_info *fs_info,
 	lockdep_assert_held(&uuid_mutex);
 	ASSERT(fsid);
 
-	fs_devices = fs_info->fs_devices->seed;
-	while (fs_devices) {
+	list_for_each_entry(fs_devices, &fs_info->fs_devices->seed_list, seed_list)
 		if (!memcmp(fs_devices->fsid, fsid, BTRFS_FSID_SIZE))
 			return fs_devices;
 
-		fs_devices = fs_devices->seed;
-	}
 
 	fs_devices = find_fsid(fsid, NULL);
 	if (!fs_devices) {
@@ -6728,8 +6718,8 @@  static struct btrfs_fs_devices *open_seed_devices(struct btrfs_fs_info *fs_info,
 		goto out;
 	}
 
-	fs_devices->seed = fs_info->fs_devices->seed;
-	fs_info->fs_devices->seed = fs_devices;
+	ASSERT(list_empty(&fs_devices->seed_list));
+	list_add_tail(&fs_devices->seed_list, &fs_info->fs_devices->seed_list);
 out:
 	return fs_devices;
 }
@@ -7141,17 +7131,23 @@  int btrfs_read_chunk_tree(struct btrfs_fs_info *fs_info)
 
 void btrfs_init_devices_late(struct btrfs_fs_info *fs_info)
 {
-	struct btrfs_fs_devices *fs_devices = fs_info->fs_devices;
+	struct btrfs_fs_devices *fs_devices = fs_info->fs_devices, *seed_devs;
 	struct btrfs_device *device;
 
-	while (fs_devices) {
-		mutex_lock(&fs_devices->device_list_mutex);
-		list_for_each_entry(device, &fs_devices->devices, dev_list)
+	fs_devices->fs_info = fs_info;
+
+	mutex_lock(&fs_devices->device_list_mutex);
+	list_for_each_entry(device, &fs_devices->devices, dev_list)
+		device->fs_info = fs_info;
+	mutex_unlock(&fs_devices->device_list_mutex);
+
+	list_for_each_entry(seed_devs, &fs_devices->seed_list, seed_list) {
+		mutex_lock(&seed_devs->device_list_mutex);
+		list_for_each_entry(device, &seed_devs->devices, dev_list)
 			device->fs_info = fs_info;
-		mutex_unlock(&fs_devices->device_list_mutex);
+		mutex_unlock(&seed_devs->device_list_mutex);
 
-		fs_devices->fs_info = fs_info;
-		fs_devices = fs_devices->seed;
+		seed_devs->fs_info = fs_info;
 	}
 }
 
@@ -7527,8 +7523,10 @@  static int verify_one_dev_extent(struct btrfs_fs_info *fs_info,
 
 	/* It's possible this device is a dummy for seed device */
 	if (dev->disk_total_bytes == 0) {
-		dev = btrfs_find_device(fs_info->fs_devices->seed, devid, NULL,
-					NULL, false);
+		struct btrfs_fs_devices *devs;
+		devs = list_first_entry(&fs_info->fs_devices->seed_list,
+					struct btrfs_fs_devices, seed_list);
+		dev = btrfs_find_device(devs, devid, NULL, NULL, false);
 		if (!dev) {
 			btrfs_err(fs_info, "failed to find seed devid %llu",
 				  devid);
diff --git a/fs/btrfs/volumes.h b/fs/btrfs/volumes.h
index fc283fdbcece..709f4aacbd3f 100644
--- a/fs/btrfs/volumes.h
+++ b/fs/btrfs/volumes.h
@@ -246,7 +246,7 @@  struct btrfs_fs_devices {
 	 */
 	struct list_head alloc_list;
 
-	struct btrfs_fs_devices *seed;
+	struct list_head seed_list;
 	bool seeding;
 
 	int opened;