diff options
author | Andy Adamson <andros@netapp.com> | 2013-05-08 16:21:18 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2013-05-08 17:19:32 -0400 |
commit | c23266d532b4de796a346f57a66587c5db17d27e (patch) | |
tree | ff380244bcc16957bb0849cac47d5383a2b710af /fs/nfs | |
parent | d497ab975141666e674e7bd8729e00095ec23c9d (diff) |
NFS4.1 Fix data server connection race
Unlike meta data server mounts which support multiple mount points to
the same server via struct nfs_server, data servers support a single connection.
Concurrent calls to setup the data server connection can race where the first
call allocates the nfs_client struct, and before the cache struct nfs_client
pointer can be set, a second call also tries to setup the connection, finds the
already allocated nfs_client, bumps the reference count, re-initializes the
session,etc. This results in a hanging data server session after umount.
Signed-off-by: Andy Adamson <andros@netapp.com>
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs')
-rw-r--r-- | fs/nfs/nfs4filelayout.h | 2 | ||||
-rw-r--r-- | fs/nfs/nfs4filelayoutdev.c | 26 |
2 files changed, 26 insertions, 2 deletions
diff --git a/fs/nfs/nfs4filelayout.h b/fs/nfs/nfs4filelayout.h index b8da95548d3d..235ff952d3c8 100644 --- a/fs/nfs/nfs4filelayout.h +++ b/fs/nfs/nfs4filelayout.h | |||
@@ -70,6 +70,8 @@ struct nfs4_pnfs_ds { | |||
70 | struct list_head ds_addrs; | 70 | struct list_head ds_addrs; |
71 | struct nfs_client *ds_clp; | 71 | struct nfs_client *ds_clp; |
72 | atomic_t ds_count; | 72 | atomic_t ds_count; |
73 | unsigned long ds_state; | ||
74 | #define NFS4DS_CONNECTING 0 /* ds is establishing connection */ | ||
73 | }; | 75 | }; |
74 | 76 | ||
75 | struct nfs4_file_layout_dsaddr { | 77 | struct nfs4_file_layout_dsaddr { |
diff --git a/fs/nfs/nfs4filelayoutdev.c b/fs/nfs/nfs4filelayoutdev.c index 1fe284f01f8b..661a0f611215 100644 --- a/fs/nfs/nfs4filelayoutdev.c +++ b/fs/nfs/nfs4filelayoutdev.c | |||
@@ -775,6 +775,22 @@ nfs4_fl_select_ds_fh(struct pnfs_layout_segment *lseg, u32 j) | |||
775 | return flseg->fh_array[i]; | 775 | return flseg->fh_array[i]; |
776 | } | 776 | } |
777 | 777 | ||
778 | static void nfs4_wait_ds_connect(struct nfs4_pnfs_ds *ds) | ||
779 | { | ||
780 | might_sleep(); | ||
781 | wait_on_bit(&ds->ds_state, NFS4DS_CONNECTING, | ||
782 | nfs_wait_bit_killable, TASK_KILLABLE); | ||
783 | } | ||
784 | |||
785 | static void nfs4_clear_ds_conn_bit(struct nfs4_pnfs_ds *ds) | ||
786 | { | ||
787 | smp_mb__before_clear_bit(); | ||
788 | clear_bit(NFS4DS_CONNECTING, &ds->ds_state); | ||
789 | smp_mb__after_clear_bit(); | ||
790 | wake_up_bit(&ds->ds_state, NFS4DS_CONNECTING); | ||
791 | } | ||
792 | |||
793 | |||
778 | struct nfs4_pnfs_ds * | 794 | struct nfs4_pnfs_ds * |
779 | nfs4_fl_prepare_ds(struct pnfs_layout_segment *lseg, u32 ds_idx) | 795 | nfs4_fl_prepare_ds(struct pnfs_layout_segment *lseg, u32 ds_idx) |
780 | { | 796 | { |
@@ -791,16 +807,22 @@ nfs4_fl_prepare_ds(struct pnfs_layout_segment *lseg, u32 ds_idx) | |||
791 | filelayout_mark_devid_invalid(devid); | 807 | filelayout_mark_devid_invalid(devid); |
792 | return NULL; | 808 | return NULL; |
793 | } | 809 | } |
810 | if (ds->ds_clp) | ||
811 | return ds; | ||
794 | 812 | ||
795 | if (!ds->ds_clp) { | 813 | if (test_and_set_bit(NFS4DS_CONNECTING, &ds->ds_state) == 0) { |
796 | struct nfs_server *s = NFS_SERVER(lseg->pls_layout->plh_inode); | 814 | struct nfs_server *s = NFS_SERVER(lseg->pls_layout->plh_inode); |
797 | int err; | 815 | int err; |
798 | 816 | ||
799 | err = nfs4_ds_connect(s, ds); | 817 | err = nfs4_ds_connect(s, ds); |
800 | if (err) { | 818 | if (err) { |
801 | nfs4_mark_deviceid_unavailable(devid); | 819 | nfs4_mark_deviceid_unavailable(devid); |
802 | return NULL; | 820 | ds = NULL; |
803 | } | 821 | } |
822 | nfs4_clear_ds_conn_bit(ds); | ||
823 | } else { | ||
824 | /* Either ds is connected, or ds is NULL */ | ||
825 | nfs4_wait_ds_connect(ds); | ||
804 | } | 826 | } |
805 | return ds; | 827 | return ds; |
806 | } | 828 | } |