@@ -23,39 +23,65 @@ static DEFINE_SPINLOCK(nfs_uuids_lock);
2323 */
2424static LIST_HEAD (nfs_uuids );
2525
26+ /*
27+ * Lock ordering:
28+ * 1: nfs_uuid->lock
29+ * 2: nfs_uuids_lock
30+ * 3: nfs_uuid->list_lock (aka nn->local_clients_lock)
31+ *
32+ * May skip locks in select cases, but never hold multiple
33+ * locks out of order.
34+ */
35+
2636void nfs_uuid_init (nfs_uuid_t * nfs_uuid )
2737{
2838 nfs_uuid -> net = NULL ;
2939 nfs_uuid -> dom = NULL ;
40+ nfs_uuid -> list_lock = NULL ;
3041 INIT_LIST_HEAD (& nfs_uuid -> list );
42+ INIT_LIST_HEAD (& nfs_uuid -> files );
3143 spin_lock_init (& nfs_uuid -> lock );
3244}
3345EXPORT_SYMBOL_GPL (nfs_uuid_init );
3446
3547bool nfs_uuid_begin (nfs_uuid_t * nfs_uuid )
3648{
49+ spin_lock (& nfs_uuid -> lock );
50+ if (nfs_uuid -> net ) {
51+ /* This nfs_uuid is already in use */
52+ spin_unlock (& nfs_uuid -> lock );
53+ return false;
54+ }
55+
3756 spin_lock (& nfs_uuids_lock );
38- /* Is this nfs_uuid already in use? */
3957 if (!list_empty (& nfs_uuid -> list )) {
58+ /* This nfs_uuid is already in use */
4059 spin_unlock (& nfs_uuids_lock );
60+ spin_unlock (& nfs_uuid -> lock );
4161 return false;
4262 }
43- uuid_gen (& nfs_uuid -> uuid );
4463 list_add_tail (& nfs_uuid -> list , & nfs_uuids );
4564 spin_unlock (& nfs_uuids_lock );
4665
66+ uuid_gen (& nfs_uuid -> uuid );
67+ spin_unlock (& nfs_uuid -> lock );
68+
4769 return true;
4870}
4971EXPORT_SYMBOL_GPL (nfs_uuid_begin );
5072
5173void nfs_uuid_end (nfs_uuid_t * nfs_uuid )
5274{
5375 if (nfs_uuid -> net == NULL ) {
54- spin_lock (& nfs_uuids_lock );
55- if (nfs_uuid -> net == NULL )
76+ spin_lock (& nfs_uuid -> lock );
77+ if (nfs_uuid -> net == NULL ) {
78+ /* Not local, remove from nfs_uuids */
79+ spin_lock (& nfs_uuids_lock );
5680 list_del_init (& nfs_uuid -> list );
57- spin_unlock (& nfs_uuids_lock );
58- }
81+ spin_unlock (& nfs_uuids_lock );
82+ }
83+ spin_unlock (& nfs_uuid -> lock );
84+ }
5985}
6086EXPORT_SYMBOL_GPL (nfs_uuid_end );
6187
@@ -73,28 +99,39 @@ static nfs_uuid_t * nfs_uuid_lookup_locked(const uuid_t *uuid)
7399static struct module * nfsd_mod ;
74100
75101void nfs_uuid_is_local (const uuid_t * uuid , struct list_head * list ,
76- struct net * net , struct auth_domain * dom ,
77- struct module * mod )
102+ spinlock_t * list_lock , struct net * net ,
103+ struct auth_domain * dom , struct module * mod )
78104{
79105 nfs_uuid_t * nfs_uuid ;
80106
81107 spin_lock (& nfs_uuids_lock );
82108 nfs_uuid = nfs_uuid_lookup_locked (uuid );
83- if (nfs_uuid ) {
84- kref_get (& dom -> ref );
85- nfs_uuid -> dom = dom ;
86- /*
87- * We don't hold a ref on the net, but instead put
88- * ourselves on a list so the net pointer can be
89- * invalidated.
90- */
91- list_move (& nfs_uuid -> list , list );
92- rcu_assign_pointer (nfs_uuid -> net , net );
93-
94- __module_get (mod );
95- nfsd_mod = mod ;
109+ if (!nfs_uuid ) {
110+ spin_unlock (& nfs_uuids_lock );
111+ return ;
96112 }
113+
114+ /*
115+ * We don't hold a ref on the net, but instead put
116+ * ourselves on @list (nn->local_clients) so the net
117+ * pointer can be invalidated.
118+ */
119+ spin_lock (list_lock ); /* list_lock is nn->local_clients_lock */
120+ list_move (& nfs_uuid -> list , list );
121+ spin_unlock (list_lock );
122+
97123 spin_unlock (& nfs_uuids_lock );
124+ /* Once nfs_uuid is parented to @list, avoid global nfs_uuids_lock */
125+ spin_lock (& nfs_uuid -> lock );
126+
127+ __module_get (mod );
128+ nfsd_mod = mod ;
129+
130+ nfs_uuid -> list_lock = list_lock ;
131+ kref_get (& dom -> ref );
132+ nfs_uuid -> dom = dom ;
133+ rcu_assign_pointer (nfs_uuid -> net , net );
134+ spin_unlock (& nfs_uuid -> lock );
98135}
99136EXPORT_SYMBOL_GPL (nfs_uuid_is_local );
100137
@@ -108,55 +145,96 @@ void nfs_localio_enable_client(struct nfs_client *clp)
108145}
109146EXPORT_SYMBOL_GPL (nfs_localio_enable_client );
110147
111- static void nfs_uuid_put_locked (nfs_uuid_t * nfs_uuid )
148+ /*
149+ * Cleanup the nfs_uuid_t embedded in an nfs_client.
150+ * This is the long-form of nfs_uuid_init().
151+ */
152+ static void nfs_uuid_put (nfs_uuid_t * nfs_uuid )
112153{
113- if (!nfs_uuid -> net )
154+ LIST_HEAD (local_files );
155+ struct nfs_file_localio * nfl , * tmp ;
156+
157+ spin_lock (& nfs_uuid -> lock );
158+ if (unlikely (!nfs_uuid -> net )) {
159+ spin_unlock (& nfs_uuid -> lock );
114160 return ;
115- module_put ( nfsd_mod );
161+ }
116162 RCU_INIT_POINTER (nfs_uuid -> net , NULL );
117163
118164 if (nfs_uuid -> dom ) {
119165 auth_domain_put (nfs_uuid -> dom );
120166 nfs_uuid -> dom = NULL ;
121167 }
122- list_del_init (& nfs_uuid -> list );
168+
169+ list_splice_init (& nfs_uuid -> files , & local_files );
170+ spin_unlock (& nfs_uuid -> lock );
171+
172+ /* Walk list of files and ensure their last references dropped */
173+ list_for_each_entry_safe (nfl , tmp , & local_files , list ) {
174+ nfs_close_local_fh (nfl );
175+ cond_resched ();
176+ }
177+
178+ spin_lock (& nfs_uuid -> lock );
179+ BUG_ON (!list_empty (& nfs_uuid -> files ));
180+
181+ /* Remove client from nn->local_clients */
182+ if (nfs_uuid -> list_lock ) {
183+ spin_lock (nfs_uuid -> list_lock );
184+ BUG_ON (list_empty (& nfs_uuid -> list ));
185+ list_del_init (& nfs_uuid -> list );
186+ spin_unlock (nfs_uuid -> list_lock );
187+ nfs_uuid -> list_lock = NULL ;
188+ }
189+
190+ module_put (nfsd_mod );
191+ spin_unlock (& nfs_uuid -> lock );
123192}
124193
125194void nfs_localio_disable_client (struct nfs_client * clp )
126195{
127- nfs_uuid_t * nfs_uuid = & clp -> cl_uuid ;
196+ nfs_uuid_t * nfs_uuid = NULL ;
128197
129- spin_lock (& nfs_uuid -> lock );
198+ spin_lock (& clp -> cl_uuid . lock ); /* aka &nfs_uuid->lock */
130199 if (test_and_clear_bit (NFS_CS_LOCAL_IO , & clp -> cl_flags )) {
131- spin_lock (& nfs_uuids_lock );
132- nfs_uuid_put_locked (nfs_uuid );
133- spin_unlock (& nfs_uuids_lock );
200+ /* &clp->cl_uuid is always not NULL, using as bool here */
201+ nfs_uuid = & clp -> cl_uuid ;
134202 }
135- spin_unlock (& nfs_uuid -> lock );
203+ spin_unlock (& clp -> cl_uuid .lock );
204+
205+ if (nfs_uuid )
206+ nfs_uuid_put (nfs_uuid );
136207}
137208EXPORT_SYMBOL_GPL (nfs_localio_disable_client );
138209
139- void nfs_localio_invalidate_clients (struct list_head * cl_uuid_list )
210+ void nfs_localio_invalidate_clients (struct list_head * nn_local_clients ,
211+ spinlock_t * nn_local_clients_lock )
140212{
213+ LIST_HEAD (local_clients );
141214 nfs_uuid_t * nfs_uuid , * tmp ;
142-
143- spin_lock (& nfs_uuids_lock );
144- list_for_each_entry_safe (nfs_uuid , tmp , cl_uuid_list , list ) {
145- struct nfs_client * clp =
146- container_of (nfs_uuid , struct nfs_client , cl_uuid );
147-
215+ struct nfs_client * clp ;
216+
217+ spin_lock (nn_local_clients_lock );
218+ list_splice_init (nn_local_clients , & local_clients );
219+ spin_unlock (nn_local_clients_lock );
220+ list_for_each_entry_safe (nfs_uuid , tmp , & local_clients , list ) {
221+ if (WARN_ON (nfs_uuid -> list_lock != nn_local_clients_lock ))
222+ break ;
223+ clp = container_of (nfs_uuid , struct nfs_client , cl_uuid );
148224 nfs_localio_disable_client (clp );
149225 }
150- spin_unlock (& nfs_uuids_lock );
151226}
152227EXPORT_SYMBOL_GPL (nfs_localio_invalidate_clients );
153228
154229static void nfs_uuid_add_file (nfs_uuid_t * nfs_uuid , struct nfs_file_localio * nfl )
155230{
156- spin_lock (& nfs_uuids_lock );
157- if (!nfl -> nfs_uuid )
231+ /* Add nfl to nfs_uuid->files if it isn't already */
232+ spin_lock (& nfs_uuid -> lock );
233+ if (list_empty (& nfl -> list )) {
158234 rcu_assign_pointer (nfl -> nfs_uuid , nfs_uuid );
159- spin_unlock (& nfs_uuids_lock );
235+ list_add_tail (& nfl -> list , & nfs_uuid -> files );
236+ }
237+ spin_unlock (& nfs_uuid -> lock );
160238}
161239
162240/*
@@ -217,14 +295,16 @@ void nfs_close_local_fh(struct nfs_file_localio *nfl)
217295 ro_nf = rcu_access_pointer (nfl -> ro_file );
218296 rw_nf = rcu_access_pointer (nfl -> rw_file );
219297 if (ro_nf || rw_nf ) {
220- spin_lock (& nfs_uuids_lock );
298+ spin_lock (& nfs_uuid -> lock );
221299 if (ro_nf )
222300 ro_nf = rcu_dereference_protected (xchg (& nfl -> ro_file , NULL ), 1 );
223301 if (rw_nf )
224302 rw_nf = rcu_dereference_protected (xchg (& nfl -> rw_file , NULL ), 1 );
225303
226- rcu_assign_pointer (nfl -> nfs_uuid , NULL );
227- spin_unlock (& nfs_uuids_lock );
304+ /* Remove nfl from nfs_uuid->files list */
305+ RCU_INIT_POINTER (nfl -> nfs_uuid , NULL );
306+ list_del_init (& nfl -> list );
307+ spin_unlock (& nfs_uuid -> lock );
228308 rcu_read_unlock ();
229309
230310 if (ro_nf )
0 commit comments