diff options
author | Trond Myklebust <trond.myklebust@hammerspace.com> | 2019-08-03 10:11:27 -0400 |
---|---|---|
committer | Trond Myklebust <trond.myklebust@hammerspace.com> | 2019-08-04 22:35:40 -0400 |
commit | c77e22834ae9a11891cb613bd9a551be1b94f2bc (patch) | |
tree | dfa88210f8f4660ce07e597ba5dda83e6aee73af | |
parent | e3c8dc761ead061da2220ee8f8132f729ac3ddfe (diff) |
NFSv4: Fix a potential sleep while atomic in nfs4_do_reclaim()
John Hubbard reports seeing the following stack trace:
nfs4_do_reclaim
rcu_read_lock /* we are now in_atomic() and must not sleep */
nfs4_purge_state_owners
nfs4_free_state_owner
nfs4_destroy_seqid_counter
rpc_destroy_wait_queue
cancel_delayed_work_sync
__cancel_work_timer
__flush_work
start_flush_work
might_sleep:
(kernel/workqueue.c:2975: BUG)
The solution is to separate out the freeing of the state owners
from nfs4_purge_state_owners(), and perform that outside the atomic
context.
Reported-by: John Hubbard <jhubbard@nvidia.com>
Fixes: 0aaaf5c424c7f ("NFS: Cache state owners after files are closed")
Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
-rw-r--r-- | fs/nfs/nfs4_fs.h | 3 | ||||
-rw-r--r-- | fs/nfs/nfs4client.c | 5 | ||||
-rw-r--r-- | fs/nfs/nfs4state.c | 27 |
3 files changed, 28 insertions, 7 deletions
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h index d778dad9a75e..3564da1ba8a1 100644 --- a/fs/nfs/nfs4_fs.h +++ b/fs/nfs/nfs4_fs.h | |||
@@ -465,7 +465,8 @@ static inline void nfs4_schedule_session_recovery(struct nfs4_session *session, | |||
465 | 465 | ||
466 | extern struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *, const struct cred *, gfp_t); | 466 | extern struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *, const struct cred *, gfp_t); |
467 | extern void nfs4_put_state_owner(struct nfs4_state_owner *); | 467 | extern void nfs4_put_state_owner(struct nfs4_state_owner *); |
468 | extern void nfs4_purge_state_owners(struct nfs_server *); | 468 | extern void nfs4_purge_state_owners(struct nfs_server *, struct list_head *); |
469 | extern void nfs4_free_state_owners(struct list_head *head); | ||
469 | extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *); | 470 | extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *); |
470 | extern void nfs4_put_open_state(struct nfs4_state *); | 471 | extern void nfs4_put_open_state(struct nfs4_state *); |
471 | extern void nfs4_close_state(struct nfs4_state *, fmode_t); | 472 | extern void nfs4_close_state(struct nfs4_state *, fmode_t); |
diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c index 616393a01c06..da6204025a2d 100644 --- a/fs/nfs/nfs4client.c +++ b/fs/nfs/nfs4client.c | |||
@@ -758,9 +758,12 @@ out: | |||
758 | 758 | ||
759 | static void nfs4_destroy_server(struct nfs_server *server) | 759 | static void nfs4_destroy_server(struct nfs_server *server) |
760 | { | 760 | { |
761 | LIST_HEAD(freeme); | ||
762 | |||
761 | nfs_server_return_all_delegations(server); | 763 | nfs_server_return_all_delegations(server); |
762 | unset_pnfs_layoutdriver(server); | 764 | unset_pnfs_layoutdriver(server); |
763 | nfs4_purge_state_owners(server); | 765 | nfs4_purge_state_owners(server, &freeme); |
766 | nfs4_free_state_owners(&freeme); | ||
764 | } | 767 | } |
765 | 768 | ||
766 | /* | 769 | /* |
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index d03b9cf42bd0..a4e866b2b43b 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c | |||
@@ -624,24 +624,39 @@ void nfs4_put_state_owner(struct nfs4_state_owner *sp) | |||
624 | /** | 624 | /** |
625 | * nfs4_purge_state_owners - Release all cached state owners | 625 | * nfs4_purge_state_owners - Release all cached state owners |
626 | * @server: nfs_server with cached state owners to release | 626 | * @server: nfs_server with cached state owners to release |
627 | * @head: resulting list of state owners | ||
627 | * | 628 | * |
628 | * Called at umount time. Remaining state owners will be on | 629 | * Called at umount time. Remaining state owners will be on |
629 | * the LRU with ref count of zero. | 630 | * the LRU with ref count of zero. |
631 | * Note that the state owners are not freed, but are added | ||
632 | * to the list @head, which can later be used as an argument | ||
633 | * to nfs4_free_state_owners. | ||
630 | */ | 634 | */ |
631 | void nfs4_purge_state_owners(struct nfs_server *server) | 635 | void nfs4_purge_state_owners(struct nfs_server *server, struct list_head *head) |
632 | { | 636 | { |
633 | struct nfs_client *clp = server->nfs_client; | 637 | struct nfs_client *clp = server->nfs_client; |
634 | struct nfs4_state_owner *sp, *tmp; | 638 | struct nfs4_state_owner *sp, *tmp; |
635 | LIST_HEAD(doomed); | ||
636 | 639 | ||
637 | spin_lock(&clp->cl_lock); | 640 | spin_lock(&clp->cl_lock); |
638 | list_for_each_entry_safe(sp, tmp, &server->state_owners_lru, so_lru) { | 641 | list_for_each_entry_safe(sp, tmp, &server->state_owners_lru, so_lru) { |
639 | list_move(&sp->so_lru, &doomed); | 642 | list_move(&sp->so_lru, head); |
640 | nfs4_remove_state_owner_locked(sp); | 643 | nfs4_remove_state_owner_locked(sp); |
641 | } | 644 | } |
642 | spin_unlock(&clp->cl_lock); | 645 | spin_unlock(&clp->cl_lock); |
646 | } | ||
643 | 647 | ||
644 | list_for_each_entry_safe(sp, tmp, &doomed, so_lru) { | 648 | /** |
649 | * nfs4_purge_state_owners - Release all cached state owners | ||
650 | * @head: resulting list of state owners | ||
651 | * | ||
652 | * Frees a list of state owners that was generated by | ||
653 | * nfs4_purge_state_owners | ||
654 | */ | ||
655 | void nfs4_free_state_owners(struct list_head *head) | ||
656 | { | ||
657 | struct nfs4_state_owner *sp, *tmp; | ||
658 | |||
659 | list_for_each_entry_safe(sp, tmp, head, so_lru) { | ||
645 | list_del(&sp->so_lru); | 660 | list_del(&sp->so_lru); |
646 | nfs4_free_state_owner(sp); | 661 | nfs4_free_state_owner(sp); |
647 | } | 662 | } |
@@ -1865,12 +1880,13 @@ static int nfs4_do_reclaim(struct nfs_client *clp, const struct nfs4_state_recov | |||
1865 | struct nfs4_state_owner *sp; | 1880 | struct nfs4_state_owner *sp; |
1866 | struct nfs_server *server; | 1881 | struct nfs_server *server; |
1867 | struct rb_node *pos; | 1882 | struct rb_node *pos; |
1883 | LIST_HEAD(freeme); | ||
1868 | int status = 0; | 1884 | int status = 0; |
1869 | 1885 | ||
1870 | restart: | 1886 | restart: |
1871 | rcu_read_lock(); | 1887 | rcu_read_lock(); |
1872 | list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { | 1888 | list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { |
1873 | nfs4_purge_state_owners(server); | 1889 | nfs4_purge_state_owners(server, &freeme); |
1874 | spin_lock(&clp->cl_lock); | 1890 | spin_lock(&clp->cl_lock); |
1875 | for (pos = rb_first(&server->state_owners); | 1891 | for (pos = rb_first(&server->state_owners); |
1876 | pos != NULL; | 1892 | pos != NULL; |
@@ -1899,6 +1915,7 @@ restart: | |||
1899 | spin_unlock(&clp->cl_lock); | 1915 | spin_unlock(&clp->cl_lock); |
1900 | } | 1916 | } |
1901 | rcu_read_unlock(); | 1917 | rcu_read_unlock(); |
1918 | nfs4_free_state_owners(&freeme); | ||
1902 | return 0; | 1919 | return 0; |
1903 | } | 1920 | } |
1904 | 1921 | ||