diff options
Diffstat (limited to 'fs/xfs/xfs_filestream.c')
-rw-r--r-- | fs/xfs/xfs_filestream.c | 84 |
1 files changed, 77 insertions, 7 deletions
diff --git a/fs/xfs/xfs_filestream.c b/fs/xfs/xfs_filestream.c index 390850ee6603..9b715dce5699 100644 --- a/fs/xfs/xfs_filestream.c +++ b/fs/xfs/xfs_filestream.c | |||
@@ -18,13 +18,9 @@ | |||
18 | #include "xfs.h" | 18 | #include "xfs.h" |
19 | #include "xfs_bmap_btree.h" | 19 | #include "xfs_bmap_btree.h" |
20 | #include "xfs_inum.h" | 20 | #include "xfs_inum.h" |
21 | #include "xfs_dir2.h" | ||
22 | #include "xfs_dir2_sf.h" | ||
23 | #include "xfs_attr_sf.h" | ||
24 | #include "xfs_dinode.h" | 21 | #include "xfs_dinode.h" |
25 | #include "xfs_inode.h" | 22 | #include "xfs_inode.h" |
26 | #include "xfs_ag.h" | 23 | #include "xfs_ag.h" |
27 | #include "xfs_dmapi.h" | ||
28 | #include "xfs_log.h" | 24 | #include "xfs_log.h" |
29 | #include "xfs_trans.h" | 25 | #include "xfs_trans.h" |
30 | #include "xfs_sb.h" | 26 | #include "xfs_sb.h" |
@@ -127,6 +123,82 @@ typedef struct fstrm_item | |||
127 | xfs_inode_t *pip; /* Parent directory inode pointer. */ | 123 | xfs_inode_t *pip; /* Parent directory inode pointer. */ |
128 | } fstrm_item_t; | 124 | } fstrm_item_t; |
129 | 125 | ||
126 | /* | ||
127 | * Allocation group filestream associations are tracked with per-ag atomic | ||
128 | * counters. These counters allow _xfs_filestream_pick_ag() to tell whether a | ||
129 | * particular AG already has active filestreams associated with it. The mount | ||
130 | * point's m_peraglock is used to protect these counters from per-ag array | ||
131 | * re-allocation during a growfs operation. When xfs_growfs_data_private() is | ||
132 | * about to reallocate the array, it calls xfs_filestream_flush() with the | ||
133 | * m_peraglock held in write mode. | ||
134 | * | ||
135 | * Since xfs_mru_cache_flush() guarantees that all the free functions for all | ||
136 | * the cache elements have finished executing before it returns, it's safe for | ||
137 | * the free functions to use the atomic counters without m_peraglock protection. | ||
138 | * This allows the implementation of xfs_fstrm_free_func() to be agnostic about | ||
139 | * whether it was called with the m_peraglock held in read mode, write mode or | ||
140 | * not held at all. The race condition this addresses is the following: | ||
141 | * | ||
142 | * - The work queue scheduler fires and pulls a filestream directory cache | ||
143 | * element off the LRU end of the cache for deletion, then gets pre-empted. | ||
144 | * - A growfs operation grabs the m_peraglock in write mode, flushes all the | ||
145 | * remaining items from the cache and reallocates the mount point's per-ag | ||
146 | * array, resetting all the counters to zero. | ||
147 | * - The work queue thread resumes and calls the free function for the element | ||
148 | * it started cleaning up earlier. In the process it decrements the | ||
149 | * filestreams counter for an AG that now has no references. | ||
150 | * | ||
151 | * With a shrinkfs feature, the above scenario could panic the system. | ||
152 | * | ||
153 | * All other uses of the following macros should be protected by either the | ||
154 | * m_peraglock held in read mode, or the cache's internal locking exposed by the | ||
155 | * interval between a call to xfs_mru_cache_lookup() and a call to | ||
156 | * xfs_mru_cache_done(). In addition, the m_peraglock must be held in read mode | ||
157 | * when new elements are added to the cache. | ||
158 | * | ||
159 | * Combined, these locking rules ensure that no associations will ever exist in | ||
160 | * the cache that reference per-ag array elements that have since been | ||
161 | * reallocated. | ||
162 | */ | ||
163 | static int | ||
164 | xfs_filestream_peek_ag( | ||
165 | xfs_mount_t *mp, | ||
166 | xfs_agnumber_t agno) | ||
167 | { | ||
168 | struct xfs_perag *pag; | ||
169 | int ret; | ||
170 | |||
171 | pag = xfs_perag_get(mp, agno); | ||
172 | ret = atomic_read(&pag->pagf_fstrms); | ||
173 | xfs_perag_put(pag); | ||
174 | return ret; | ||
175 | } | ||
176 | |||
177 | static int | ||
178 | xfs_filestream_get_ag( | ||
179 | xfs_mount_t *mp, | ||
180 | xfs_agnumber_t agno) | ||
181 | { | ||
182 | struct xfs_perag *pag; | ||
183 | int ret; | ||
184 | |||
185 | pag = xfs_perag_get(mp, agno); | ||
186 | ret = atomic_inc_return(&pag->pagf_fstrms); | ||
187 | xfs_perag_put(pag); | ||
188 | return ret; | ||
189 | } | ||
190 | |||
191 | static void | ||
192 | xfs_filestream_put_ag( | ||
193 | xfs_mount_t *mp, | ||
194 | xfs_agnumber_t agno) | ||
195 | { | ||
196 | struct xfs_perag *pag; | ||
197 | |||
198 | pag = xfs_perag_get(mp, agno); | ||
199 | atomic_dec(&pag->pagf_fstrms); | ||
200 | xfs_perag_put(pag); | ||
201 | } | ||
130 | 202 | ||
131 | /* | 203 | /* |
132 | * Scan the AGs starting at startag looking for an AG that isn't in use and has | 204 | * Scan the AGs starting at startag looking for an AG that isn't in use and has |
@@ -355,16 +427,14 @@ xfs_fstrm_free_func( | |||
355 | { | 427 | { |
356 | fstrm_item_t *item = (fstrm_item_t *)data; | 428 | fstrm_item_t *item = (fstrm_item_t *)data; |
357 | xfs_inode_t *ip = item->ip; | 429 | xfs_inode_t *ip = item->ip; |
358 | int ref; | ||
359 | 430 | ||
360 | ASSERT(ip->i_ino == ino); | 431 | ASSERT(ip->i_ino == ino); |
361 | 432 | ||
362 | xfs_iflags_clear(ip, XFS_IFILESTREAM); | 433 | xfs_iflags_clear(ip, XFS_IFILESTREAM); |
363 | 434 | ||
364 | /* Drop the reference taken on the AG when the item was added. */ | 435 | /* Drop the reference taken on the AG when the item was added. */ |
365 | ref = xfs_filestream_put_ag(ip->i_mount, item->ag); | 436 | xfs_filestream_put_ag(ip->i_mount, item->ag); |
366 | 437 | ||
367 | ASSERT(ref >= 0); | ||
368 | TRACE_FREE(ip->i_mount, ip, item->pip, item->ag, | 438 | TRACE_FREE(ip->i_mount, ip, item->pip, item->ag, |
369 | xfs_filestream_peek_ag(ip->i_mount, item->ag)); | 439 | xfs_filestream_peek_ag(ip->i_mount, item->ag)); |
370 | 440 | ||