summaryrefslogtreecommitdiffstats
path: root/fs/notify
diff options
context:
space:
mode:
authorJan Kara <jack@suse.cz>2018-10-17 07:07:05 -0400
committerJan Kara <jack@suse.cz>2018-10-25 09:49:19 -0400
commit721fb6fbfd2132164c2e8777cc837f9b2c1794dc (patch)
tree72be987a81f1ba85ecb1fd60b2f412ed0a08c44c /fs/notify
parent99c228a994ec8b1580c43631866fd2c5440f5bfd (diff)
fsnotify: Fix busy inodes during unmount
Detaching of mark connector from fsnotify_put_mark() can race with unmounting of the filesystem like: CPU1 CPU2 fsnotify_put_mark() spin_lock(&conn->lock); ... inode = fsnotify_detach_connector_from_object(conn) spin_unlock(&conn->lock); generic_shutdown_super() fsnotify_unmount_inodes() sees connector detached for inode -> nothing to do evict_inode() barfs on pending inode reference iput(inode); Resulting in "Busy inodes after unmount" message and possible kernel oops. Make fsnotify_unmount_inodes() properly wait for outstanding inode references from detached connectors. Note that the accounting of outstanding inode references in the superblock can cause some cacheline contention on the counter. OTOH it happens only during deletion of the last notification mark from an inode (or during unlinking of watched inode) and that is not too bad. I have measured time to create & delete inotify watch 100000 times from 64 processes in parallel (each process having its own inotify group and its own file on a shared superblock) on a 64 CPU machine. Average and standard deviation of 15 runs look like: Avg Stddev Vanilla 9.817400 0.276165 Fixed 9.710467 0.228294 So there's no statistically significant difference. Fixes: 6b3f05d24d35 ("fsnotify: Detach mark from object list when last reference is dropped") CC: stable@vger.kernel.org Signed-off-by: Jan Kara <jack@suse.cz>
Diffstat (limited to 'fs/notify')
-rw-r--r--fs/notify/fsnotify.c3
-rw-r--r--fs/notify/mark.c39
2 files changed, 34 insertions, 8 deletions
diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c
index 875975504409..2172ba516c61 100644
--- a/fs/notify/fsnotify.c
+++ b/fs/notify/fsnotify.c
@@ -96,6 +96,9 @@ static void fsnotify_unmount_inodes(struct super_block *sb)
96 96
97 if (iput_inode) 97 if (iput_inode)
98 iput(iput_inode); 98 iput(iput_inode);
99 /* Wait for outstanding inode references from connectors */
100 wait_var_event(&sb->s_fsnotify_inode_refs,
101 !atomic_long_read(&sb->s_fsnotify_inode_refs));
99} 102}
100 103
101void fsnotify_sb_delete(struct super_block *sb) 104void fsnotify_sb_delete(struct super_block *sb)
diff --git a/fs/notify/mark.c b/fs/notify/mark.c
index b5172ccb2e60..d2dd16cb5989 100644
--- a/fs/notify/mark.c
+++ b/fs/notify/mark.c
@@ -181,17 +181,20 @@ static void fsnotify_connector_destroy_workfn(struct work_struct *work)
181 } 181 }
182} 182}
183 183
184static struct inode *fsnotify_detach_connector_from_object( 184static void *fsnotify_detach_connector_from_object(
185 struct fsnotify_mark_connector *conn) 185 struct fsnotify_mark_connector *conn,
186 unsigned int *type)
186{ 187{
187 struct inode *inode = NULL; 188 struct inode *inode = NULL;
188 189
190 *type = conn->type;
189 if (conn->type == FSNOTIFY_OBJ_TYPE_DETACHED) 191 if (conn->type == FSNOTIFY_OBJ_TYPE_DETACHED)
190 return NULL; 192 return NULL;
191 193
192 if (conn->type == FSNOTIFY_OBJ_TYPE_INODE) { 194 if (conn->type == FSNOTIFY_OBJ_TYPE_INODE) {
193 inode = fsnotify_conn_inode(conn); 195 inode = fsnotify_conn_inode(conn);
194 inode->i_fsnotify_mask = 0; 196 inode->i_fsnotify_mask = 0;
197 atomic_long_inc(&inode->i_sb->s_fsnotify_inode_refs);
195 } else if (conn->type == FSNOTIFY_OBJ_TYPE_VFSMOUNT) { 198 } else if (conn->type == FSNOTIFY_OBJ_TYPE_VFSMOUNT) {
196 fsnotify_conn_mount(conn)->mnt_fsnotify_mask = 0; 199 fsnotify_conn_mount(conn)->mnt_fsnotify_mask = 0;
197 } else if (conn->type == FSNOTIFY_OBJ_TYPE_SB) { 200 } else if (conn->type == FSNOTIFY_OBJ_TYPE_SB) {
@@ -215,10 +218,29 @@ static void fsnotify_final_mark_destroy(struct fsnotify_mark *mark)
215 fsnotify_put_group(group); 218 fsnotify_put_group(group);
216} 219}
217 220
221/* Drop object reference originally held by a connector */
222static void fsnotify_drop_object(unsigned int type, void *objp)
223{
224 struct inode *inode;
225 struct super_block *sb;
226
227 if (!objp)
228 return;
229 /* Currently only inode references are passed to be dropped */
230 if (WARN_ON_ONCE(type != FSNOTIFY_OBJ_TYPE_INODE))
231 return;
232 inode = objp;
233 sb = inode->i_sb;
234 iput(inode);
235 if (atomic_long_dec_and_test(&sb->s_fsnotify_inode_refs))
236 wake_up_var(&sb->s_fsnotify_inode_refs);
237}
238
218void fsnotify_put_mark(struct fsnotify_mark *mark) 239void fsnotify_put_mark(struct fsnotify_mark *mark)
219{ 240{
220 struct fsnotify_mark_connector *conn; 241 struct fsnotify_mark_connector *conn;
221 struct inode *inode = NULL; 242 void *objp = NULL;
243 unsigned int type = FSNOTIFY_OBJ_TYPE_DETACHED;
222 bool free_conn = false; 244 bool free_conn = false;
223 245
224 /* Catch marks that were actually never attached to object */ 246 /* Catch marks that were actually never attached to object */
@@ -238,7 +260,7 @@ void fsnotify_put_mark(struct fsnotify_mark *mark)
238 conn = mark->connector; 260 conn = mark->connector;
239 hlist_del_init_rcu(&mark->obj_list); 261 hlist_del_init_rcu(&mark->obj_list);
240 if (hlist_empty(&conn->list)) { 262 if (hlist_empty(&conn->list)) {
241 inode = fsnotify_detach_connector_from_object(conn); 263 objp = fsnotify_detach_connector_from_object(conn, &type);
242 free_conn = true; 264 free_conn = true;
243 } else { 265 } else {
244 __fsnotify_recalc_mask(conn); 266 __fsnotify_recalc_mask(conn);
@@ -246,7 +268,7 @@ void fsnotify_put_mark(struct fsnotify_mark *mark)
246 mark->connector = NULL; 268 mark->connector = NULL;
247 spin_unlock(&conn->lock); 269 spin_unlock(&conn->lock);
248 270
249 iput(inode); 271 fsnotify_drop_object(type, objp);
250 272
251 if (free_conn) { 273 if (free_conn) {
252 spin_lock(&destroy_lock); 274 spin_lock(&destroy_lock);
@@ -713,7 +735,8 @@ void fsnotify_destroy_marks(fsnotify_connp_t *connp)
713{ 735{
714 struct fsnotify_mark_connector *conn; 736 struct fsnotify_mark_connector *conn;
715 struct fsnotify_mark *mark, *old_mark = NULL; 737 struct fsnotify_mark *mark, *old_mark = NULL;
716 struct inode *inode; 738 void *objp;
739 unsigned int type;
717 740
718 conn = fsnotify_grab_connector(connp); 741 conn = fsnotify_grab_connector(connp);
719 if (!conn) 742 if (!conn)
@@ -739,11 +762,11 @@ void fsnotify_destroy_marks(fsnotify_connp_t *connp)
739 * mark references get dropped. It would lead to strange results such 762 * mark references get dropped. It would lead to strange results such
740 * as delaying inode deletion or blocking unmount. 763 * as delaying inode deletion or blocking unmount.
741 */ 764 */
742 inode = fsnotify_detach_connector_from_object(conn); 765 objp = fsnotify_detach_connector_from_object(conn, &type);
743 spin_unlock(&conn->lock); 766 spin_unlock(&conn->lock);
744 if (old_mark) 767 if (old_mark)
745 fsnotify_put_mark(old_mark); 768 fsnotify_put_mark(old_mark);
746 iput(inode); 769 fsnotify_drop_object(type, objp);
747} 770}
748 771
749/* 772/*