diff options
| -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 9254b3d58dbe..db0b8fc59235 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)); |
