diff options
author | Chris Mason <chris.mason@oracle.com> | 2008-03-24 15:02:07 -0400 |
---|---|---|
committer | Chris Mason <chris.mason@oracle.com> | 2008-09-25 11:04:01 -0400 |
commit | 8a4b83cc8bd75fca29ac68615896d9e92820e7c2 (patch) | |
tree | b7f99cf53c322665b78cca10742cc734ad070729 /fs/btrfs/volumes.c | |
parent | 239b14b32dc39232ebf9cce29ff77c4c564355fd (diff) |
Btrfs: Add support for device scanning and detection ioctls
Signed-off-by: Chris Mason <chris.mason@oracle.com>
Diffstat (limited to 'fs/btrfs/volumes.c')
-rw-r--r-- | fs/btrfs/volumes.c | 236 |
1 files changed, 222 insertions, 14 deletions
diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c index 16fb6bbe6e28..263f01cc3db4 100644 --- a/fs/btrfs/volumes.c +++ b/fs/btrfs/volumes.c | |||
@@ -17,6 +17,7 @@ | |||
17 | */ | 17 | */ |
18 | #include <linux/sched.h> | 18 | #include <linux/sched.h> |
19 | #include <linux/bio.h> | 19 | #include <linux/bio.h> |
20 | #include <linux/buffer_head.h> | ||
20 | #include "ctree.h" | 21 | #include "ctree.h" |
21 | #include "extent_map.h" | 22 | #include "extent_map.h" |
22 | #include "disk-io.h" | 23 | #include "disk-io.h" |
@@ -28,6 +29,215 @@ struct map_lookup { | |||
28 | struct btrfs_device *dev; | 29 | struct btrfs_device *dev; |
29 | u64 physical; | 30 | u64 physical; |
30 | }; | 31 | }; |
32 | static DEFINE_MUTEX(uuid_mutex); | ||
33 | static LIST_HEAD(fs_uuids); | ||
34 | |||
35 | int btrfs_cleanup_fs_uuids(void) | ||
36 | { | ||
37 | struct btrfs_fs_devices *fs_devices; | ||
38 | struct list_head *uuid_cur; | ||
39 | struct list_head *devices_cur; | ||
40 | struct btrfs_device *dev; | ||
41 | |||
42 | list_for_each(uuid_cur, &fs_uuids) { | ||
43 | fs_devices = list_entry(uuid_cur, struct btrfs_fs_devices, | ||
44 | list); | ||
45 | while(!list_empty(&fs_devices->devices)) { | ||
46 | devices_cur = fs_devices->devices.next; | ||
47 | dev = list_entry(devices_cur, struct btrfs_device, | ||
48 | dev_list); | ||
49 | printk("uuid cleanup finds %s\n", dev->name); | ||
50 | if (dev->bdev) { | ||
51 | printk("closing\n"); | ||
52 | close_bdev_excl(dev->bdev); | ||
53 | } | ||
54 | list_del(&dev->dev_list); | ||
55 | kfree(dev); | ||
56 | } | ||
57 | } | ||
58 | return 0; | ||
59 | } | ||
60 | |||
61 | static struct btrfs_device *__find_device(struct list_head *head, u64 devid) | ||
62 | { | ||
63 | struct btrfs_device *dev; | ||
64 | struct list_head *cur; | ||
65 | |||
66 | list_for_each(cur, head) { | ||
67 | dev = list_entry(cur, struct btrfs_device, dev_list); | ||
68 | if (dev->devid == devid) | ||
69 | return dev; | ||
70 | } | ||
71 | return NULL; | ||
72 | } | ||
73 | |||
74 | static struct btrfs_fs_devices *find_fsid(u8 *fsid) | ||
75 | { | ||
76 | struct list_head *cur; | ||
77 | struct btrfs_fs_devices *fs_devices; | ||
78 | |||
79 | list_for_each(cur, &fs_uuids) { | ||
80 | fs_devices = list_entry(cur, struct btrfs_fs_devices, list); | ||
81 | if (memcmp(fsid, fs_devices->fsid, BTRFS_FSID_SIZE) == 0) | ||
82 | return fs_devices; | ||
83 | } | ||
84 | return NULL; | ||
85 | } | ||
86 | |||
87 | static int device_list_add(const char *path, | ||
88 | struct btrfs_super_block *disk_super, | ||
89 | u64 devid, struct btrfs_fs_devices **fs_devices_ret) | ||
90 | { | ||
91 | struct btrfs_device *device; | ||
92 | struct btrfs_fs_devices *fs_devices; | ||
93 | u64 found_transid = btrfs_super_generation(disk_super); | ||
94 | |||
95 | fs_devices = find_fsid(disk_super->fsid); | ||
96 | if (!fs_devices) { | ||
97 | fs_devices = kmalloc(sizeof(*fs_devices), GFP_NOFS); | ||
98 | if (!fs_devices) | ||
99 | return -ENOMEM; | ||
100 | INIT_LIST_HEAD(&fs_devices->devices); | ||
101 | list_add(&fs_devices->list, &fs_uuids); | ||
102 | memcpy(fs_devices->fsid, disk_super->fsid, BTRFS_FSID_SIZE); | ||
103 | fs_devices->latest_devid = devid; | ||
104 | fs_devices->latest_trans = found_transid; | ||
105 | fs_devices->lowest_devid = (u64)-1; | ||
106 | fs_devices->num_devices = 0; | ||
107 | device = NULL; | ||
108 | } else { | ||
109 | device = __find_device(&fs_devices->devices, devid); | ||
110 | } | ||
111 | if (!device) { | ||
112 | device = kzalloc(sizeof(*device), GFP_NOFS); | ||
113 | if (!device) { | ||
114 | /* we can safely leave the fs_devices entry around */ | ||
115 | return -ENOMEM; | ||
116 | } | ||
117 | device->devid = devid; | ||
118 | device->name = kstrdup(path, GFP_NOFS); | ||
119 | if (!device->name) { | ||
120 | kfree(device); | ||
121 | return -ENOMEM; | ||
122 | } | ||
123 | list_add(&device->dev_list, &fs_devices->devices); | ||
124 | fs_devices->num_devices++; | ||
125 | } | ||
126 | |||
127 | if (found_transid > fs_devices->latest_trans) { | ||
128 | fs_devices->latest_devid = devid; | ||
129 | fs_devices->latest_trans = found_transid; | ||
130 | } | ||
131 | if (fs_devices->lowest_devid > devid) { | ||
132 | fs_devices->lowest_devid = devid; | ||
133 | printk("lowest devid now %Lu\n", devid); | ||
134 | } | ||
135 | *fs_devices_ret = fs_devices; | ||
136 | return 0; | ||
137 | } | ||
138 | |||
139 | int btrfs_close_devices(struct btrfs_fs_devices *fs_devices) | ||
140 | { | ||
141 | struct list_head *head = &fs_devices->devices; | ||
142 | struct list_head *cur; | ||
143 | struct btrfs_device *device; | ||
144 | |||
145 | mutex_lock(&uuid_mutex); | ||
146 | list_for_each(cur, head) { | ||
147 | device = list_entry(cur, struct btrfs_device, dev_list); | ||
148 | if (device->bdev) { | ||
149 | close_bdev_excl(device->bdev); | ||
150 | printk("close devices closes %s\n", device->name); | ||
151 | } | ||
152 | device->bdev = NULL; | ||
153 | } | ||
154 | mutex_unlock(&uuid_mutex); | ||
155 | return 0; | ||
156 | } | ||
157 | |||
158 | int btrfs_open_devices(struct btrfs_fs_devices *fs_devices, | ||
159 | int flags, void *holder) | ||
160 | { | ||
161 | struct block_device *bdev; | ||
162 | struct list_head *head = &fs_devices->devices; | ||
163 | struct list_head *cur; | ||
164 | struct btrfs_device *device; | ||
165 | int ret; | ||
166 | |||
167 | mutex_lock(&uuid_mutex); | ||
168 | list_for_each(cur, head) { | ||
169 | device = list_entry(cur, struct btrfs_device, dev_list); | ||
170 | bdev = open_bdev_excl(device->name, flags, holder); | ||
171 | printk("opening %s devid %Lu\n", device->name, device->devid); | ||
172 | if (IS_ERR(bdev)) { | ||
173 | printk("open %s failed\n", device->name); | ||
174 | ret = PTR_ERR(bdev); | ||
175 | goto fail; | ||
176 | } | ||
177 | if (device->devid == fs_devices->latest_devid) | ||
178 | fs_devices->latest_bdev = bdev; | ||
179 | if (device->devid == fs_devices->lowest_devid) { | ||
180 | fs_devices->lowest_bdev = bdev; | ||
181 | printk("lowest bdev %s\n", device->name); | ||
182 | } | ||
183 | device->bdev = bdev; | ||
184 | } | ||
185 | mutex_unlock(&uuid_mutex); | ||
186 | return 0; | ||
187 | fail: | ||
188 | mutex_unlock(&uuid_mutex); | ||
189 | btrfs_close_devices(fs_devices); | ||
190 | return ret; | ||
191 | } | ||
192 | |||
193 | int btrfs_scan_one_device(const char *path, int flags, void *holder, | ||
194 | struct btrfs_fs_devices **fs_devices_ret) | ||
195 | { | ||
196 | struct btrfs_super_block *disk_super; | ||
197 | struct block_device *bdev; | ||
198 | struct buffer_head *bh; | ||
199 | int ret; | ||
200 | u64 devid; | ||
201 | |||
202 | mutex_lock(&uuid_mutex); | ||
203 | |||
204 | printk("scan one opens %s\n", path); | ||
205 | bdev = open_bdev_excl(path, flags, holder); | ||
206 | |||
207 | if (IS_ERR(bdev)) { | ||
208 | printk("open failed\n"); | ||
209 | ret = PTR_ERR(bdev); | ||
210 | goto error; | ||
211 | } | ||
212 | |||
213 | ret = set_blocksize(bdev, 4096); | ||
214 | if (ret) | ||
215 | goto error_close; | ||
216 | bh = __bread(bdev, BTRFS_SUPER_INFO_OFFSET / 4096, 4096); | ||
217 | if (!bh) { | ||
218 | ret = -EIO; | ||
219 | goto error_close; | ||
220 | } | ||
221 | disk_super = (struct btrfs_super_block *)bh->b_data; | ||
222 | if (strncmp((char *)(&disk_super->magic), BTRFS_MAGIC, | ||
223 | sizeof(disk_super->magic))) { | ||
224 | printk("no btrfs found on %s\n", path); | ||
225 | ret = -ENOENT; | ||
226 | goto error_brelse; | ||
227 | } | ||
228 | devid = le64_to_cpu(disk_super->dev_item.devid); | ||
229 | printk("found device %Lu on %s\n", devid, path); | ||
230 | ret = device_list_add(path, disk_super, devid, fs_devices_ret); | ||
231 | |||
232 | error_brelse: | ||
233 | brelse(bh); | ||
234 | error_close: | ||
235 | close_bdev_excl(bdev); | ||
236 | printk("scan one closes bdev %s\n", path); | ||
237 | error: | ||
238 | mutex_unlock(&uuid_mutex); | ||
239 | return ret; | ||
240 | } | ||
31 | 241 | ||
32 | /* | 242 | /* |
33 | * this uses a pretty simple search, the expectation is that it is | 243 | * this uses a pretty simple search, the expectation is that it is |
@@ -56,6 +266,10 @@ static int find_free_dev_extent(struct btrfs_trans_handle *trans, | |||
56 | 266 | ||
57 | /* FIXME use last free of some kind */ | 267 | /* FIXME use last free of some kind */ |
58 | 268 | ||
269 | /* we don't want to overwrite the superblock on the drive, | ||
270 | * so we make sure to start at an offset of at least 1MB | ||
271 | */ | ||
272 | search_start = max((u64)1024 * 1024, search_start); | ||
59 | key.objectid = device->devid; | 273 | key.objectid = device->devid; |
60 | key.offset = search_start; | 274 | key.offset = search_start; |
61 | key.type = BTRFS_DEV_EXTENT_KEY; | 275 | key.type = BTRFS_DEV_EXTENT_KEY; |
@@ -285,6 +499,7 @@ int btrfs_add_device(struct btrfs_trans_handle *trans, | |||
285 | leaf = path->nodes[0]; | 499 | leaf = path->nodes[0]; |
286 | dev_item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_dev_item); | 500 | dev_item = btrfs_item_ptr(leaf, path->slots[0], struct btrfs_dev_item); |
287 | 501 | ||
502 | device->devid = free_devid; | ||
288 | btrfs_set_device_id(leaf, dev_item, device->devid); | 503 | btrfs_set_device_id(leaf, dev_item, device->devid); |
289 | btrfs_set_device_type(leaf, dev_item, device->type); | 504 | btrfs_set_device_type(leaf, dev_item, device->type); |
290 | btrfs_set_device_io_align(leaf, dev_item, device->io_align); | 505 | btrfs_set_device_io_align(leaf, dev_item, device->io_align); |
@@ -382,7 +597,7 @@ int btrfs_alloc_chunk(struct btrfs_trans_handle *trans, | |||
382 | struct btrfs_device *device = NULL; | 597 | struct btrfs_device *device = NULL; |
383 | struct btrfs_chunk *chunk; | 598 | struct btrfs_chunk *chunk; |
384 | struct list_head private_devs; | 599 | struct list_head private_devs; |
385 | struct list_head *dev_list = &extent_root->fs_info->devices; | 600 | struct list_head *dev_list = &extent_root->fs_info->fs_devices->devices; |
386 | struct list_head *cur; | 601 | struct list_head *cur; |
387 | struct extent_map_tree *em_tree; | 602 | struct extent_map_tree *em_tree; |
388 | struct map_lookup *map; | 603 | struct map_lookup *map; |
@@ -449,7 +664,7 @@ again: | |||
449 | key.objectid, | 664 | key.objectid, |
450 | calc_size, &dev_offset); | 665 | calc_size, &dev_offset); |
451 | BUG_ON(ret); | 666 | BUG_ON(ret); |
452 | 667 | printk("alloc chunk size %Lu from dev %Lu\n", calc_size, device->devid); | |
453 | device->bytes_used += calc_size; | 668 | device->bytes_used += calc_size; |
454 | ret = btrfs_update_device(trans, device); | 669 | ret = btrfs_update_device(trans, device); |
455 | BUG_ON(ret); | 670 | BUG_ON(ret); |
@@ -592,17 +807,9 @@ int btrfs_map_bio(struct btrfs_root *root, int rw, struct bio *bio) | |||
592 | 807 | ||
593 | struct btrfs_device *btrfs_find_device(struct btrfs_root *root, u64 devid) | 808 | struct btrfs_device *btrfs_find_device(struct btrfs_root *root, u64 devid) |
594 | { | 809 | { |
595 | struct btrfs_device *dev; | 810 | struct list_head *head = &root->fs_info->fs_devices->devices; |
596 | struct list_head *cur = root->fs_info->devices.next; | ||
597 | struct list_head *head = &root->fs_info->devices; | ||
598 | 811 | ||
599 | while(cur != head) { | 812 | return __find_device(head, devid); |
600 | dev = list_entry(cur, struct btrfs_device, dev_list); | ||
601 | if (dev->devid == devid) | ||
602 | return dev; | ||
603 | cur = cur->next; | ||
604 | } | ||
605 | return NULL; | ||
606 | } | 813 | } |
607 | 814 | ||
608 | static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key, | 815 | static int read_one_chunk(struct btrfs_root *root, struct btrfs_key *key, |
@@ -699,15 +906,16 @@ static int read_one_dev(struct btrfs_root *root, | |||
699 | devid = btrfs_device_id(leaf, dev_item); | 906 | devid = btrfs_device_id(leaf, dev_item); |
700 | device = btrfs_find_device(root, devid); | 907 | device = btrfs_find_device(root, devid); |
701 | if (!device) { | 908 | if (!device) { |
909 | printk("warning devid %Lu not found already\n", devid); | ||
702 | device = kmalloc(sizeof(*device), GFP_NOFS); | 910 | device = kmalloc(sizeof(*device), GFP_NOFS); |
703 | if (!device) | 911 | if (!device) |
704 | return -ENOMEM; | 912 | return -ENOMEM; |
705 | list_add(&device->dev_list, &root->fs_info->devices); | 913 | list_add(&device->dev_list, |
914 | &root->fs_info->fs_devices->devices); | ||
706 | } | 915 | } |
707 | 916 | ||
708 | fill_device_from_item(leaf, dev_item, device); | 917 | fill_device_from_item(leaf, dev_item, device); |
709 | device->dev_root = root->fs_info->dev_root; | 918 | device->dev_root = root->fs_info->dev_root; |
710 | device->bdev = root->fs_info->sb->s_bdev; | ||
711 | ret = 0; | 919 | ret = 0; |
712 | #if 0 | 920 | #if 0 |
713 | ret = btrfs_open_device(device); | 921 | ret = btrfs_open_device(device); |