diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/btrfs/ioctl.c | 100 |
1 files changed, 76 insertions, 24 deletions
diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c index 9254b3d58db..db0b8fc5923 100644 --- a/fs/btrfs/ioctl.c +++ b/fs/btrfs/ioctl.c | |||
@@ -1879,6 +1879,22 @@ static long btrfs_ioctl_default_subvol(struct file *file, void __user *argp) | |||
1879 | return 0; | 1879 | return 0; |
1880 | } | 1880 | } |
1881 | 1881 | ||
1882 | static void get_block_group_info(struct list_head *groups_list, | ||
1883 | struct btrfs_ioctl_space_info *space) | ||
1884 | { | ||
1885 | struct btrfs_block_group_cache *block_group; | ||
1886 | |||
1887 | space->total_bytes = 0; | ||
1888 | space->used_bytes = 0; | ||
1889 | space->flags = 0; | ||
1890 | list_for_each_entry(block_group, groups_list, list) { | ||
1891 | space->flags = block_group->flags; | ||
1892 | space->total_bytes += block_group->key.offset; | ||
1893 | space->used_bytes += | ||
1894 | btrfs_block_group_used(&block_group->item); | ||
1895 | } | ||
1896 | } | ||
1897 | |||
1882 | long btrfs_ioctl_space_info(struct btrfs_root *root, void __user *arg) | 1898 | long btrfs_ioctl_space_info(struct btrfs_root *root, void __user *arg) |
1883 | { | 1899 | { |
1884 | struct btrfs_ioctl_space_args space_args; | 1900 | struct btrfs_ioctl_space_args space_args; |
@@ -1887,27 +1903,56 @@ long btrfs_ioctl_space_info(struct btrfs_root *root, void __user *arg) | |||
1887 | struct btrfs_ioctl_space_info *dest_orig; | 1903 | struct btrfs_ioctl_space_info *dest_orig; |
1888 | struct btrfs_ioctl_space_info *user_dest; | 1904 | struct btrfs_ioctl_space_info *user_dest; |
1889 | struct btrfs_space_info *info; | 1905 | struct btrfs_space_info *info; |
1906 | u64 types[] = {BTRFS_BLOCK_GROUP_DATA, | ||
1907 | BTRFS_BLOCK_GROUP_SYSTEM, | ||
1908 | BTRFS_BLOCK_GROUP_METADATA, | ||
1909 | BTRFS_BLOCK_GROUP_DATA | BTRFS_BLOCK_GROUP_METADATA}; | ||
1910 | int num_types = 4; | ||
1890 | int alloc_size; | 1911 | int alloc_size; |
1891 | int ret = 0; | 1912 | int ret = 0; |
1892 | int slot_count = 0; | 1913 | int slot_count = 0; |
1914 | int i, c; | ||
1893 | 1915 | ||
1894 | if (copy_from_user(&space_args, | 1916 | if (copy_from_user(&space_args, |
1895 | (struct btrfs_ioctl_space_args __user *)arg, | 1917 | (struct btrfs_ioctl_space_args __user *)arg, |
1896 | sizeof(space_args))) | 1918 | sizeof(space_args))) |
1897 | return -EFAULT; | 1919 | return -EFAULT; |
1898 | 1920 | ||
1899 | /* first we count slots */ | 1921 | for (i = 0; i < num_types; i++) { |
1900 | rcu_read_lock(); | 1922 | struct btrfs_space_info *tmp; |
1901 | list_for_each_entry_rcu(info, &root->fs_info->space_info, list) | 1923 | |
1902 | slot_count++; | 1924 | info = NULL; |
1903 | rcu_read_unlock(); | 1925 | rcu_read_lock(); |
1926 | list_for_each_entry_rcu(tmp, &root->fs_info->space_info, | ||
1927 | list) { | ||
1928 | if (tmp->flags == types[i]) { | ||
1929 | info = tmp; | ||
1930 | break; | ||
1931 | } | ||
1932 | } | ||
1933 | rcu_read_unlock(); | ||
1934 | |||
1935 | if (!info) | ||
1936 | continue; | ||
1937 | |||
1938 | down_read(&info->groups_sem); | ||
1939 | for (c = 0; c < BTRFS_NR_RAID_TYPES; c++) { | ||
1940 | if (!list_empty(&info->block_groups[c])) | ||
1941 | slot_count++; | ||
1942 | } | ||
1943 | up_read(&info->groups_sem); | ||
1944 | } | ||
1904 | 1945 | ||
1905 | /* space_slots == 0 means they are asking for a count */ | 1946 | /* space_slots == 0 means they are asking for a count */ |
1906 | if (space_args.space_slots == 0) { | 1947 | if (space_args.space_slots == 0) { |
1907 | space_args.total_spaces = slot_count; | 1948 | space_args.total_spaces = slot_count; |
1908 | goto out; | 1949 | goto out; |
1909 | } | 1950 | } |
1951 | |||
1952 | slot_count = min_t(int, space_args.space_slots, slot_count); | ||
1953 | |||
1910 | alloc_size = sizeof(*dest) * slot_count; | 1954 | alloc_size = sizeof(*dest) * slot_count; |
1955 | |||
1911 | /* we generally have at most 6 or so space infos, one for each raid | 1956 | /* we generally have at most 6 or so space infos, one for each raid |
1912 | * level. So, a whole page should be more than enough for everyone | 1957 | * level. So, a whole page should be more than enough for everyone |
1913 | */ | 1958 | */ |
@@ -1921,27 +1966,34 @@ long btrfs_ioctl_space_info(struct btrfs_root *root, void __user *arg) | |||
1921 | dest_orig = dest; | 1966 | dest_orig = dest; |
1922 | 1967 | ||
1923 | /* now we have a buffer to copy into */ | 1968 | /* now we have a buffer to copy into */ |
1924 | rcu_read_lock(); | 1969 | for (i = 0; i < num_types; i++) { |
1925 | list_for_each_entry_rcu(info, &root->fs_info->space_info, list) { | 1970 | struct btrfs_space_info *tmp; |
1926 | /* make sure we don't copy more than we allocated | 1971 | |
1927 | * in our buffer | 1972 | info = NULL; |
1928 | */ | 1973 | rcu_read_lock(); |
1929 | if (slot_count == 0) | 1974 | list_for_each_entry_rcu(tmp, &root->fs_info->space_info, |
1930 | break; | 1975 | list) { |
1931 | slot_count--; | 1976 | if (tmp->flags == types[i]) { |
1932 | 1977 | info = tmp; | |
1933 | /* make sure userland has enough room in their buffer */ | 1978 | break; |
1934 | if (space_args.total_spaces >= space_args.space_slots) | 1979 | } |
1935 | break; | 1980 | } |
1981 | rcu_read_unlock(); | ||
1936 | 1982 | ||
1937 | space.flags = info->flags; | 1983 | if (!info) |
1938 | space.total_bytes = info->total_bytes; | 1984 | continue; |
1939 | space.used_bytes = info->bytes_used; | 1985 | down_read(&info->groups_sem); |
1940 | memcpy(dest, &space, sizeof(space)); | 1986 | for (c = 0; c < BTRFS_NR_RAID_TYPES; c++) { |
1941 | dest++; | 1987 | if (!list_empty(&info->block_groups[c])) { |
1942 | space_args.total_spaces++; | 1988 | get_block_group_info(&info->block_groups[c], |
1989 | &space); | ||
1990 | memcpy(dest, &space, sizeof(space)); | ||
1991 | dest++; | ||
1992 | space_args.total_spaces++; | ||
1993 | } | ||
1994 | } | ||
1995 | up_read(&info->groups_sem); | ||
1943 | } | 1996 | } |
1944 | rcu_read_unlock(); | ||
1945 | 1997 | ||
1946 | user_dest = (struct btrfs_ioctl_space_info *) | 1998 | user_dest = (struct btrfs_ioctl_space_info *) |
1947 | (arg + sizeof(struct btrfs_ioctl_space_args)); | 1999 | (arg + sizeof(struct btrfs_ioctl_space_args)); |