/* the disk copy procedure reuses the scrub code */
        ret = btrfs_scrub_dev(fs_info, src_device->devid, 0,
-                             src_device->total_bytes,
+                             btrfs_device_get_total_bytes(src_device),
                              &dev_replace->scrub_progress, 0, 1);
 
        ret = btrfs_dev_replace_finishing(root->fs_info, ret);
        memcpy(uuid_tmp, tgt_device->uuid, sizeof(uuid_tmp));
        memcpy(tgt_device->uuid, src_device->uuid, sizeof(tgt_device->uuid));
        memcpy(src_device->uuid, uuid_tmp, sizeof(src_device->uuid));
-       tgt_device->total_bytes = src_device->total_bytes;
-       tgt_device->disk_total_bytes = src_device->disk_total_bytes;
+       btrfs_device_set_total_bytes(tgt_device, src_device->total_bytes);
+       btrfs_device_set_disk_total_bytes(tgt_device,
+                                         src_device->disk_total_bytes);
+       btrfs_device_set_bytes_used(tgt_device, src_device->bytes_used);
        ASSERT(list_empty(&src_device->resized_list));
        tgt_device->commit_total_bytes = src_device->commit_total_bytes;
-       tgt_device->bytes_used = src_device->bytes_used;
        tgt_device->commit_bytes_used = src_device->bytes_used;
        if (fs_info->sb->s_bdev == src_device->bdev)
                fs_info->sb->s_bdev = tgt_device->bdev;
                              struct btrfs_ioctl_dev_replace_args *args)
 {
        struct btrfs_dev_replace *dev_replace = &fs_info->dev_replace;
+       struct btrfs_device *srcdev;
 
        btrfs_dev_replace_lock(dev_replace);
        /* even if !dev_replace_is_valid, the values are good enough for
                break;
        case BTRFS_IOCTL_DEV_REPLACE_STATE_STARTED:
        case BTRFS_IOCTL_DEV_REPLACE_STATE_SUSPENDED:
+               srcdev = dev_replace->srcdev;
                args->status.progress_1000 = div64_u64(dev_replace->cursor_left,
-                       div64_u64(dev_replace->srcdev->total_bytes, 1000));
+                       div64_u64(btrfs_device_get_total_bytes(srcdev), 1000));
                break;
        }
        btrfs_dev_replace_unlock(dev_replace);
 
        ret = btrfs_scrub_dev(fs_info, dev_replace->srcdev->devid,
                              dev_replace->committed_cursor_left,
-                             dev_replace->srcdev->total_bytes,
+                             btrfs_device_get_total_bytes(dev_replace->srcdev),
                              &dev_replace->scrub_progress, 0, 1);
        ret = btrfs_dev_replace_finishing(fs_info, ret);
        WARN_ON(ret);
 
 
        if (device->bytes_used > 0) {
                u64 len = btrfs_dev_extent_length(leaf, extent);
-               device->bytes_used -= len;
+               btrfs_device_set_bytes_used(device, device->bytes_used - len);
                spin_lock(&root->fs_info->free_chunk_lock);
                root->fs_info->free_chunk_space += len;
                spin_unlock(&root->fs_info->free_chunk_lock);
        btrfs_set_device_io_align(leaf, dev_item, device->io_align);
        btrfs_set_device_io_width(leaf, dev_item, device->io_width);
        btrfs_set_device_sector_size(leaf, dev_item, device->sector_size);
-       btrfs_set_device_total_bytes(leaf, dev_item, device->disk_total_bytes);
-       btrfs_set_device_bytes_used(leaf, dev_item, device->bytes_used);
+       btrfs_set_device_total_bytes(leaf, dev_item,
+                                    btrfs_device_get_disk_total_bytes(device));
+       btrfs_set_device_bytes_used(leaf, dev_item,
+                                   btrfs_device_get_bytes_used(device));
        btrfs_set_device_group(leaf, dev_item, 0);
        btrfs_set_device_seek_speed(leaf, dev_item, 0);
        btrfs_set_device_bandwidth(leaf, dev_item, 0);
        }
 
 
-       if (i_size_read(bdev->bd_inode) < srcdev->total_bytes) {
+       if (i_size_read(bdev->bd_inode) <
+           btrfs_device_get_total_bytes(srcdev)) {
                btrfs_err(fs_info, "target device is smaller than source device!");
                ret = -EINVAL;
                goto error;
        device->io_width = root->sectorsize;
        device->io_align = root->sectorsize;
        device->sector_size = root->sectorsize;
-       device->total_bytes = srcdev->total_bytes;
-       device->disk_total_bytes = srcdev->disk_total_bytes;
+       device->total_bytes = btrfs_device_get_total_bytes(srcdev);
+       device->disk_total_bytes = btrfs_device_get_disk_total_bytes(srcdev);
+       device->bytes_used = btrfs_device_get_bytes_used(srcdev);
        ASSERT(list_empty(&srcdev->resized_list));
        device->commit_total_bytes = srcdev->commit_total_bytes;
-       device->bytes_used = srcdev->bytes_used;
        device->commit_bytes_used = device->bytes_used;
        device->dev_root = fs_info->dev_root;
        device->bdev = bdev;
        btrfs_set_device_io_align(leaf, dev_item, device->io_align);
        btrfs_set_device_io_width(leaf, dev_item, device->io_width);
        btrfs_set_device_sector_size(leaf, dev_item, device->sector_size);
-       btrfs_set_device_total_bytes(leaf, dev_item, device->disk_total_bytes);
-       btrfs_set_device_bytes_used(leaf, dev_item, device->bytes_used);
+       btrfs_set_device_total_bytes(leaf, dev_item,
+                                    btrfs_device_get_disk_total_bytes(device));
+       btrfs_set_device_bytes_used(leaf, dev_item,
+                                   btrfs_device_get_bytes_used(device));
        btrfs_mark_buffer_dirty(leaf);
 
 out:
        btrfs_set_super_total_bytes(super_copy, old_total + diff);
        device->fs_devices->total_rw_bytes += diff;
 
-       device->total_bytes = new_size;
-       device->disk_total_bytes = new_size;
+       btrfs_device_set_total_bytes(device, new_size);
+       btrfs_device_set_disk_total_bytes(device, new_size);
        btrfs_clear_space_info_full(device->dev_root->fs_info);
        if (list_empty(&device->resized_list))
                list_add_tail(&device->resized_list,
        /* step one make some room on all the devices */
        devices = &fs_info->fs_devices->devices;
        list_for_each_entry(device, devices, dev_list) {
-               old_size = device->total_bytes;
+               old_size = btrfs_device_get_total_bytes(device);
                size_to_free = div_factor(old_size, 1);
                size_to_free = min(size_to_free, (u64)1 * 1024 * 1024);
                if (!device->writeable ||
-                   device->total_bytes - device->bytes_used > size_to_free ||
+                   btrfs_device_get_total_bytes(device) -
+                   btrfs_device_get_bytes_used(device) > size_to_free ||
                    device->is_tgtdev_for_dev_replace)
                        continue;
 
        struct btrfs_key key;
        struct btrfs_super_block *super_copy = root->fs_info->super_copy;
        u64 old_total = btrfs_super_total_bytes(super_copy);
-       u64 old_size = device->total_bytes;
-       u64 diff = device->total_bytes - new_size;
+       u64 old_size = btrfs_device_get_total_bytes(device);
+       u64 diff = old_size - new_size;
 
        if (device->is_tgtdev_for_dev_replace)
                return -EINVAL;
 
        lock_chunks(root);
 
-       device->total_bytes = new_size;
+       btrfs_device_set_total_bytes(device, new_size);
        if (device->writeable) {
                device->fs_devices->total_rw_bytes -= diff;
                spin_lock(&root->fs_info->free_chunk_lock);
                ret = -ENOSPC;
                lock_chunks(root);
 
-               device->total_bytes = old_size;
+               btrfs_device_set_total_bytes(device, old_size);
                if (device->writeable)
                        device->fs_devices->total_rw_bytes += diff;
                spin_lock(&root->fs_info->free_chunk_lock);
        }
 
        lock_chunks(root);
-       device->disk_total_bytes = new_size;
+       btrfs_device_set_disk_total_bytes(device, new_size);
        if (list_empty(&device->resized_list))
                list_add_tail(&device->resized_list,
                              &root->fs_info->fs_devices->resized_devices);
        if (ret)
                goto error_del_extent;
 
-       for (i = 0; i < map->num_stripes; i++)
-               map->stripes[i].dev->bytes_used += stripe_size;
+       for (i = 0; i < map->num_stripes; i++) {
+               num_bytes = map->stripes[i].dev->bytes_used + stripe_size;
+               btrfs_device_set_bytes_used(map->stripes[i].dev, num_bytes);
+       }
 
        spin_lock(&extent_root->fs_info->free_chunk_lock);
        extent_root->fs_info->free_chunk_space -= (stripe_size *
 
        struct bio *tail;
 };
 
+/*
+ * Use sequence counter to get consistent device stat data on
+ * 32-bit processors.
+ */
+#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
+#include <linux/seqlock.h>
+#define __BTRFS_NEED_DEVICE_DATA_ORDERED
+#define btrfs_device_data_ordered_init(device) \
+       seqcount_init(&device->data_seqcount)
+#else
+#define btrfs_device_data_ordered_init(device) do { } while (0)
+#endif
+
 struct btrfs_device {
        struct list_head dev_list;
        struct list_head dev_alloc_list;
        int can_discard;
        int is_tgtdev_for_dev_replace;
 
+#ifdef __BTRFS_NEED_DEVICE_DATA_ORDERED
+       seqcount_t data_seqcount;
+#endif
+
        /* the internal btrfs device id */
        u64 devid;
 
        atomic_t dev_stat_values[BTRFS_DEV_STAT_VALUES_MAX];
 };
 
+/*
+ * If we read those variants at the context of their own lock, we needn't
+ * use the following helpers, reading them directly is safe.
+ */
+#if BITS_PER_LONG==32 && defined(CONFIG_SMP)
+#define BTRFS_DEVICE_GETSET_FUNCS(name)                                        \
+static inline u64                                                      \
+btrfs_device_get_##name(const struct btrfs_device *dev)                        \
+{                                                                      \
+       u64 size;                                                       \
+       unsigned int seq;                                               \
+                                                                       \
+       do {                                                            \
+               seq = read_seqcount_begin(&dev->data_seqcount);         \
+               size = dev->name;                                       \
+       } while (read_seqcount_retry(&dev->data_seqcount, seq));        \
+       return size;                                                    \
+}                                                                      \
+                                                                       \
+static inline void                                                     \
+btrfs_device_set_##name(struct btrfs_device *dev, u64 size)            \
+{                                                                      \
+       preempt_disable();                                              \
+       write_seqcount_begin(&dev->data_seqcount);                      \
+       dev->name = size;                                               \
+       write_seqcount_end(&dev->data_seqcount);                        \
+       preempt_enable();                                               \
+}
+#elif BITS_PER_LONG==32 && defined(CONFIG_PREEMPT)
+#define BTRFS_DEVICE_GETSET_FUNCS(name)                                        \
+static inline u64                                                      \
+btrfs_device_get_##name(const struct btrfs_device *dev)                        \
+{                                                                      \
+       u64 size;                                                       \
+                                                                       \
+       preempt_disable();                                              \
+       size = dev->name;                                               \
+       preempt_enable();                                               \
+       return size;                                                    \
+}                                                                      \
+                                                                       \
+static inline void                                                     \
+btrfs_device_set_##name(struct btrfs_device *dev, u64 size)            \
+{                                                                      \
+       preempt_disable();                                              \
+       dev->name = size;                                               \
+       preempt_enable();                                               \
+}
+#else
+#define BTRFS_DEVICE_GETSET_FUNCS(name)                                        \
+static inline u64                                                      \
+btrfs_device_get_##name(const struct btrfs_device *dev)                        \
+{                                                                      \
+       return dev->name;                                               \
+}                                                                      \
+                                                                       \
+static inline void                                                     \
+btrfs_device_set_##name(struct btrfs_device *dev, u64 size)            \
+{                                                                      \
+       dev->name = size;                                               \
+}
+#endif
+
+BTRFS_DEVICE_GETSET_FUNCS(total_bytes);
+BTRFS_DEVICE_GETSET_FUNCS(disk_total_bytes);
+BTRFS_DEVICE_GETSET_FUNCS(bytes_used);
+
 struct btrfs_fs_devices {
        u8 fsid[BTRFS_FSID_SIZE]; /* FS specific uuid */