diff options
author | Fred Isaman <iisaman@citi.umich.edu> | 2011-07-30 20:52:45 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2011-07-31 12:18:16 -0400 |
commit | 03341d2cc91c700fc38883e572043a6a8f17dd5c (patch) | |
tree | b5e25d8597188adac536b24158f51ed99e0b10f9 /fs/nfs | |
parent | a60d2ebd93d3c5db5b6913c4844b8e6bd3b5538e (diff) |
pnfsblock: merge extents
Replace a stub, so that extents underlying the layouts are properly
added, merged, or ignored as necessary.
Signed-off-by: Fred Isaman <iisaman@citi.umich.edu>
[pnfsblock: delete the new node before put it]
Signed-off-by: Mingyang Guo <guomingyang@nrchpc.ac.cn>
Signed-off-by: Benny Halevy <bhalevy@panasas.com>
Signed-off-by: Peng Tao <peng_tao@emc.com>
Signed-off-by: Benny Halevy <bhalevy@tonian.com>
Signed-off-by: Jim Rees <rees@umich.edu>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs')
-rw-r--r-- | fs/nfs/blocklayout/blocklayout.h | 13 | ||||
-rw-r--r-- | fs/nfs/blocklayout/extents.c | 106 |
2 files changed, 119 insertions, 0 deletions
diff --git a/fs/nfs/blocklayout/blocklayout.h b/fs/nfs/blocklayout/blocklayout.h index 3e05b08d5347..581d8f47a723 100644 --- a/fs/nfs/blocklayout/blocklayout.h +++ b/fs/nfs/blocklayout/blocklayout.h | |||
@@ -80,6 +80,14 @@ enum extentclass4 { | |||
80 | EXTENT_LISTS = 2, | 80 | EXTENT_LISTS = 2, |
81 | }; | 81 | }; |
82 | 82 | ||
83 | static inline int bl_choose_list(enum exstate4 state) | ||
84 | { | ||
85 | if (state == PNFS_BLOCK_READ_DATA || state == PNFS_BLOCK_NONE_DATA) | ||
86 | return RO_EXTENT; | ||
87 | else | ||
88 | return RW_EXTENT; | ||
89 | } | ||
90 | |||
83 | struct pnfs_block_layout { | 91 | struct pnfs_block_layout { |
84 | struct pnfs_layout_hdr bl_layout; | 92 | struct pnfs_layout_hdr bl_layout; |
85 | struct pnfs_inval_markings bl_inval; /* tracks INVAL->RW transition */ | 93 | struct pnfs_inval_markings bl_inval; /* tracks INVAL->RW transition */ |
@@ -137,5 +145,10 @@ int nfs4_blk_process_layoutget(struct pnfs_layout_hdr *lo, | |||
137 | /* blocklayoutdm.c */ | 145 | /* blocklayoutdm.c */ |
138 | void bl_free_block_dev(struct pnfs_block_dev *bdev); | 146 | void bl_free_block_dev(struct pnfs_block_dev *bdev); |
139 | 147 | ||
148 | /* extents.c */ | ||
140 | void bl_put_extent(struct pnfs_block_extent *be); | 149 | void bl_put_extent(struct pnfs_block_extent *be); |
150 | struct pnfs_block_extent *bl_alloc_extent(void); | ||
151 | int bl_add_merge_extent(struct pnfs_block_layout *bl, | ||
152 | struct pnfs_block_extent *new); | ||
153 | |||
141 | #endif /* FS_NFS_NFS4BLOCKLAYOUT_H */ | 154 | #endif /* FS_NFS_NFS4BLOCKLAYOUT_H */ |
diff --git a/fs/nfs/blocklayout/extents.c b/fs/nfs/blocklayout/extents.c index d0ca7604d33e..ee4891f32492 100644 --- a/fs/nfs/blocklayout/extents.c +++ b/fs/nfs/blocklayout/extents.c | |||
@@ -87,3 +87,109 @@ static void print_elist(struct list_head *list) | |||
87 | } | 87 | } |
88 | dprintk("****************\n"); | 88 | dprintk("****************\n"); |
89 | } | 89 | } |
90 | |||
91 | static inline int | ||
92 | extents_consistent(struct pnfs_block_extent *old, struct pnfs_block_extent *new) | ||
93 | { | ||
94 | /* Note this assumes new->be_f_offset >= old->be_f_offset */ | ||
95 | return (new->be_state == old->be_state) && | ||
96 | ((new->be_state == PNFS_BLOCK_NONE_DATA) || | ||
97 | ((new->be_v_offset - old->be_v_offset == | ||
98 | new->be_f_offset - old->be_f_offset) && | ||
99 | new->be_mdev == old->be_mdev)); | ||
100 | } | ||
101 | |||
102 | /* Adds new to appropriate list in bl, modifying new and removing existing | ||
103 | * extents as appropriate to deal with overlaps. | ||
104 | * | ||
105 | * See bl_find_get_extent for list constraints. | ||
106 | * | ||
107 | * Refcount on new is already set. If end up not using it, or error out, | ||
108 | * need to put the reference. | ||
109 | * | ||
110 | * bl->bl_ext_lock is held by caller. | ||
111 | */ | ||
112 | int | ||
113 | bl_add_merge_extent(struct pnfs_block_layout *bl, | ||
114 | struct pnfs_block_extent *new) | ||
115 | { | ||
116 | struct pnfs_block_extent *be, *tmp; | ||
117 | sector_t end = new->be_f_offset + new->be_length; | ||
118 | struct list_head *list; | ||
119 | |||
120 | dprintk("%s enter with be=%p\n", __func__, new); | ||
121 | print_bl_extent(new); | ||
122 | list = &bl->bl_extents[bl_choose_list(new->be_state)]; | ||
123 | print_elist(list); | ||
124 | |||
125 | /* Scan for proper place to insert, extending new to the left | ||
126 | * as much as possible. | ||
127 | */ | ||
128 | list_for_each_entry_safe(be, tmp, list, be_node) { | ||
129 | if (new->be_f_offset < be->be_f_offset) | ||
130 | break; | ||
131 | if (end <= be->be_f_offset + be->be_length) { | ||
132 | /* new is a subset of existing be*/ | ||
133 | if (extents_consistent(be, new)) { | ||
134 | dprintk("%s: new is subset, ignoring\n", | ||
135 | __func__); | ||
136 | bl_put_extent(new); | ||
137 | return 0; | ||
138 | } else | ||
139 | goto out_err; | ||
140 | } else if (new->be_f_offset <= | ||
141 | be->be_f_offset + be->be_length) { | ||
142 | /* new overlaps or abuts existing be */ | ||
143 | if (extents_consistent(be, new)) { | ||
144 | /* extend new to fully replace be */ | ||
145 | new->be_length += new->be_f_offset - | ||
146 | be->be_f_offset; | ||
147 | new->be_f_offset = be->be_f_offset; | ||
148 | new->be_v_offset = be->be_v_offset; | ||
149 | dprintk("%s: removing %p\n", __func__, be); | ||
150 | list_del(&be->be_node); | ||
151 | bl_put_extent(be); | ||
152 | } else if (new->be_f_offset != | ||
153 | be->be_f_offset + be->be_length) | ||
154 | goto out_err; | ||
155 | } | ||
156 | } | ||
157 | /* Note that if we never hit the above break, be will not point to a | ||
158 | * valid extent. However, in that case &be->be_node==list. | ||
159 | */ | ||
160 | list_add_tail(&new->be_node, &be->be_node); | ||
161 | dprintk("%s: inserting new\n", __func__); | ||
162 | print_elist(list); | ||
163 | /* Scan forward for overlaps. If we find any, extend new and | ||
164 | * remove the overlapped extent. | ||
165 | */ | ||
166 | be = list_prepare_entry(new, list, be_node); | ||
167 | list_for_each_entry_safe_continue(be, tmp, list, be_node) { | ||
168 | if (end < be->be_f_offset) | ||
169 | break; | ||
170 | /* new overlaps or abuts existing be */ | ||
171 | if (extents_consistent(be, new)) { | ||
172 | if (end < be->be_f_offset + be->be_length) { | ||
173 | /* extend new to fully cover be */ | ||
174 | end = be->be_f_offset + be->be_length; | ||
175 | new->be_length = end - new->be_f_offset; | ||
176 | } | ||
177 | dprintk("%s: removing %p\n", __func__, be); | ||
178 | list_del(&be->be_node); | ||
179 | bl_put_extent(be); | ||
180 | } else if (end != be->be_f_offset) { | ||
181 | list_del(&new->be_node); | ||
182 | goto out_err; | ||
183 | } | ||
184 | } | ||
185 | dprintk("%s: after merging\n", __func__); | ||
186 | print_elist(list); | ||
187 | /* FIXME - The per-list consistency checks have all been done, | ||
188 | * should now check cross-list consistency. | ||
189 | */ | ||
190 | return 0; | ||
191 | |||
192 | out_err: | ||
193 | bl_put_extent(new); | ||
194 | return -EIO; | ||
195 | } | ||