@@ -23,39 +23,65 @@ static DEFINE_SPINLOCK(nfs_uuids_lock);
23
23
*/
24
24
static LIST_HEAD (nfs_uuids );
25
25
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
+
26
36
void nfs_uuid_init (nfs_uuid_t * nfs_uuid )
27
37
{
28
38
nfs_uuid -> net = NULL ;
29
39
nfs_uuid -> dom = NULL ;
40
+ nfs_uuid -> list_lock = NULL ;
30
41
INIT_LIST_HEAD (& nfs_uuid -> list );
42
+ INIT_LIST_HEAD (& nfs_uuid -> files );
31
43
spin_lock_init (& nfs_uuid -> lock );
32
44
}
33
45
EXPORT_SYMBOL_GPL (nfs_uuid_init );
34
46
35
47
bool nfs_uuid_begin (nfs_uuid_t * nfs_uuid )
36
48
{
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
+
37
56
spin_lock (& nfs_uuids_lock );
38
- /* Is this nfs_uuid already in use? */
39
57
if (!list_empty (& nfs_uuid -> list )) {
58
+ /* This nfs_uuid is already in use */
40
59
spin_unlock (& nfs_uuids_lock );
60
+ spin_unlock (& nfs_uuid -> lock );
41
61
return false;
42
62
}
43
- uuid_gen (& nfs_uuid -> uuid );
44
63
list_add_tail (& nfs_uuid -> list , & nfs_uuids );
45
64
spin_unlock (& nfs_uuids_lock );
46
65
66
+ uuid_gen (& nfs_uuid -> uuid );
67
+ spin_unlock (& nfs_uuid -> lock );
68
+
47
69
return true;
48
70
}
49
71
EXPORT_SYMBOL_GPL (nfs_uuid_begin );
50
72
51
73
void nfs_uuid_end (nfs_uuid_t * nfs_uuid )
52
74
{
53
75
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 );
56
80
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
+ }
59
85
}
60
86
EXPORT_SYMBOL_GPL (nfs_uuid_end );
61
87
@@ -73,28 +99,39 @@ static nfs_uuid_t * nfs_uuid_lookup_locked(const uuid_t *uuid)
73
99
static struct module * nfsd_mod ;
74
100
75
101
void 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 )
78
104
{
79
105
nfs_uuid_t * nfs_uuid ;
80
106
81
107
spin_lock (& nfs_uuids_lock );
82
108
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 ;
96
112
}
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
+
97
123
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 );
98
135
}
99
136
EXPORT_SYMBOL_GPL (nfs_uuid_is_local );
100
137
@@ -108,55 +145,96 @@ void nfs_localio_enable_client(struct nfs_client *clp)
108
145
}
109
146
EXPORT_SYMBOL_GPL (nfs_localio_enable_client );
110
147
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 )
112
153
{
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 );
114
160
return ;
115
- module_put ( nfsd_mod );
161
+ }
116
162
RCU_INIT_POINTER (nfs_uuid -> net , NULL );
117
163
118
164
if (nfs_uuid -> dom ) {
119
165
auth_domain_put (nfs_uuid -> dom );
120
166
nfs_uuid -> dom = NULL ;
121
167
}
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 );
123
192
}
124
193
125
194
void nfs_localio_disable_client (struct nfs_client * clp )
126
195
{
127
- nfs_uuid_t * nfs_uuid = & clp -> cl_uuid ;
196
+ nfs_uuid_t * nfs_uuid = NULL ;
128
197
129
- spin_lock (& nfs_uuid -> lock );
198
+ spin_lock (& clp -> cl_uuid . lock ); /* aka &nfs_uuid->lock */
130
199
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 ;
134
202
}
135
- spin_unlock (& nfs_uuid -> lock );
203
+ spin_unlock (& clp -> cl_uuid .lock );
204
+
205
+ if (nfs_uuid )
206
+ nfs_uuid_put (nfs_uuid );
136
207
}
137
208
EXPORT_SYMBOL_GPL (nfs_localio_disable_client );
138
209
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 )
140
212
{
213
+ LIST_HEAD (local_clients );
141
214
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 );
148
224
nfs_localio_disable_client (clp );
149
225
}
150
- spin_unlock (& nfs_uuids_lock );
151
226
}
152
227
EXPORT_SYMBOL_GPL (nfs_localio_invalidate_clients );
153
228
154
229
static void nfs_uuid_add_file (nfs_uuid_t * nfs_uuid , struct nfs_file_localio * nfl )
155
230
{
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 )) {
158
234
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 );
160
238
}
161
239
162
240
/*
@@ -217,14 +295,16 @@ void nfs_close_local_fh(struct nfs_file_localio *nfl)
217
295
ro_nf = rcu_access_pointer (nfl -> ro_file );
218
296
rw_nf = rcu_access_pointer (nfl -> rw_file );
219
297
if (ro_nf || rw_nf ) {
220
- spin_lock (& nfs_uuids_lock );
298
+ spin_lock (& nfs_uuid -> lock );
221
299
if (ro_nf )
222
300
ro_nf = rcu_dereference_protected (xchg (& nfl -> ro_file , NULL ), 1 );
223
301
if (rw_nf )
224
302
rw_nf = rcu_dereference_protected (xchg (& nfl -> rw_file , NULL ), 1 );
225
303
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 );
228
308
rcu_read_unlock ();
229
309
230
310
if (ro_nf )
0 commit comments