Blame view

fs/nfsd/nfs4layouts.c 18.8 KB
b24413180   Greg Kroah-Hartman   License cleanup: ...
1
  // SPDX-License-Identifier: GPL-2.0
9cf514ccf   Christoph Hellwig   nfsd: implement p...
2
3
4
  /*
   * Copyright (c) 2014 Christoph Hellwig.
   */
f99d4fbda   Christoph Hellwig   nfsd: add SCSI la...
5
  #include <linux/blkdev.h>
c5c707f96   Christoph Hellwig   nfsd: implement p...
6
7
  #include <linux/kmod.h>
  #include <linux/file.h>
9cf514ccf   Christoph Hellwig   nfsd: implement p...
8
9
  #include <linux/jhash.h>
  #include <linux/sched.h>
c5c707f96   Christoph Hellwig   nfsd: implement p...
10
  #include <linux/sunrpc/addr.h>
9cf514ccf   Christoph Hellwig   nfsd: implement p...
11
12
13
  
  #include "pnfs.h"
  #include "netns.h"
31ef83dc0   Christoph Hellwig   nfsd: add trace e...
14
  #include "trace.h"
9cf514ccf   Christoph Hellwig   nfsd: implement p...
15
16
17
18
19
20
21
22
23
24
25
  
  #define NFSDDBG_FACILITY                NFSDDBG_PNFS
  
  struct nfs4_layout {
  	struct list_head		lo_perstate;
  	struct nfs4_layout_stateid	*lo_state;
  	struct nfsd4_layout_seg		lo_seg;
  };
  
  static struct kmem_cache *nfs4_layout_cache;
  static struct kmem_cache *nfs4_layout_stateid_cache;
c4cb89746   Julia Lawall   nfsd: constify nf...
26
  static const struct nfsd4_callback_ops nfsd4_cb_layout_ops;
c5c707f96   Christoph Hellwig   nfsd: implement p...
27
  static const struct lock_manager_operations nfsd4_layouts_lm_ops;
9cf514ccf   Christoph Hellwig   nfsd: implement p...
28
  const struct nfsd4_layout_ops *nfsd4_layout_ops[LAYOUT_TYPE_MAX] =  {
9b9960a0c   Tom Haynes   nfsd: Add a super...
29
30
31
  #ifdef CONFIG_NFSD_FLEXFILELAYOUT
  	[LAYOUT_FLEX_FILES]	= &ff_layout_ops,
  #endif
81c393290   Christoph Hellwig   nfsd: add a new c...
32
  #ifdef CONFIG_NFSD_BLOCKLAYOUT
8650b8a05   Christoph Hellwig   nfsd: pNFS block ...
33
  	[LAYOUT_BLOCK_VOLUME]	= &bl_layout_ops,
81c393290   Christoph Hellwig   nfsd: add a new c...
34
  #endif
f99d4fbda   Christoph Hellwig   nfsd: add SCSI la...
35
36
37
  #ifdef CONFIG_NFSD_SCSILAYOUT
  	[LAYOUT_SCSI]		= &scsi_layout_ops,
  #endif
9cf514ccf   Christoph Hellwig   nfsd: implement p...
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
  };
  
  /* pNFS device ID to export fsid mapping */
  #define DEVID_HASH_BITS	8
  #define DEVID_HASH_SIZE	(1 << DEVID_HASH_BITS)
  #define DEVID_HASH_MASK	(DEVID_HASH_SIZE - 1)
  static u64 nfsd_devid_seq = 1;
  static struct list_head nfsd_devid_hash[DEVID_HASH_SIZE];
  static DEFINE_SPINLOCK(nfsd_devid_lock);
  
  static inline u32 devid_hashfn(u64 idx)
  {
  	return jhash_2words(idx, idx >> 32, 0) & DEVID_HASH_MASK;
  }
  
  static void
  nfsd4_alloc_devid_map(const struct svc_fh *fhp)
  {
  	const struct knfsd_fh *fh = &fhp->fh_handle;
  	size_t fsid_len = key_len(fh->fh_fsid_type);
  	struct nfsd4_deviceid_map *map, *old;
  	int i;
  
  	map = kzalloc(sizeof(*map) + fsid_len, GFP_KERNEL);
  	if (!map)
  		return;
  
  	map->fsid_type = fh->fh_fsid_type;
  	memcpy(&map->fsid, fh->fh_fsid, fsid_len);
  
  	spin_lock(&nfsd_devid_lock);
  	if (fhp->fh_export->ex_devid_map)
  		goto out_unlock;
  
  	for (i = 0; i < DEVID_HASH_SIZE; i++) {
  		list_for_each_entry(old, &nfsd_devid_hash[i], hash) {
  			if (old->fsid_type != fh->fh_fsid_type)
  				continue;
  			if (memcmp(old->fsid, fh->fh_fsid,
  					key_len(old->fsid_type)))
  				continue;
  
  			fhp->fh_export->ex_devid_map = old;
  			goto out_unlock;
  		}
  	}
  
  	map->idx = nfsd_devid_seq++;
  	list_add_tail_rcu(&map->hash, &nfsd_devid_hash[devid_hashfn(map->idx)]);
  	fhp->fh_export->ex_devid_map = map;
  	map = NULL;
  
  out_unlock:
  	spin_unlock(&nfsd_devid_lock);
  	kfree(map);
  }
  
  struct nfsd4_deviceid_map *
  nfsd4_find_devid_map(int idx)
  {
  	struct nfsd4_deviceid_map *map, *ret = NULL;
  
  	rcu_read_lock();
  	list_for_each_entry_rcu(map, &nfsd_devid_hash[devid_hashfn(idx)], hash)
  		if (map->idx == idx)
  			ret = map;
  	rcu_read_unlock();
  
  	return ret;
  }
  
  int
  nfsd4_set_deviceid(struct nfsd4_deviceid *id, const struct svc_fh *fhp,
  		u32 device_generation)
  {
  	if (!fhp->fh_export->ex_devid_map) {
  		nfsd4_alloc_devid_map(fhp);
  		if (!fhp->fh_export->ex_devid_map)
  			return -ENOMEM;
  	}
  
  	id->fsid_idx = fhp->fh_export->ex_devid_map->idx;
  	id->generation = device_generation;
  	id->pad = 0;
  	return 0;
  }
  
  void nfsd4_setup_layout_type(struct svc_export *exp)
  {
9b9960a0c   Tom Haynes   nfsd: Add a super...
127
  #if defined(CONFIG_NFSD_BLOCKLAYOUT) || defined(CONFIG_NFSD_SCSILAYOUT)
8650b8a05   Christoph Hellwig   nfsd: pNFS block ...
128
  	struct super_block *sb = exp->ex_path.mnt->mnt_sb;
9b9960a0c   Tom Haynes   nfsd: Add a super...
129
  #endif
8650b8a05   Christoph Hellwig   nfsd: pNFS block ...
130

f3f03330d   Christoph Hellwig   nfsd: require an ...
131
  	if (!(exp->ex_flags & NFSEXP_PNFS))
9cf514ccf   Christoph Hellwig   nfsd: implement p...
132
  		return;
8650b8a05   Christoph Hellwig   nfsd: pNFS block ...
133

f99d4fbda   Christoph Hellwig   nfsd: add SCSI la...
134
  	/*
9b9960a0c   Tom Haynes   nfsd: Add a super...
135
136
  	 * If flex file is configured, use it by default. Otherwise
  	 * check if the file system supports exporting a block-like layout.
f99d4fbda   Christoph Hellwig   nfsd: add SCSI la...
137
138
139
  	 * If the block device supports reservations prefer the SCSI layout,
  	 * otherwise advertise the block layout.
  	 */
9b9960a0c   Tom Haynes   nfsd: Add a super...
140
  #ifdef CONFIG_NFSD_FLEXFILELAYOUT
8a4c39268   Jeff Layton   nfsd: allow nfsd ...
141
  	exp->ex_layout_types |= 1 << LAYOUT_FLEX_FILES;
9b9960a0c   Tom Haynes   nfsd: Add a super...
142
  #endif
81c393290   Christoph Hellwig   nfsd: add a new c...
143
  #ifdef CONFIG_NFSD_BLOCKLAYOUT
9b9960a0c   Tom Haynes   nfsd: Add a super...
144
  	/* overwrite flex file layout selection if needed */
8650b8a05   Christoph Hellwig   nfsd: pNFS block ...
145
146
147
  	if (sb->s_export_op->get_uuid &&
  	    sb->s_export_op->map_blocks &&
  	    sb->s_export_op->commit_blocks)
8a4c39268   Jeff Layton   nfsd: allow nfsd ...
148
  		exp->ex_layout_types |= 1 << LAYOUT_BLOCK_VOLUME;
81c393290   Christoph Hellwig   nfsd: add a new c...
149
  #endif
f99d4fbda   Christoph Hellwig   nfsd: add SCSI la...
150
151
152
153
154
  #ifdef CONFIG_NFSD_SCSILAYOUT
  	/* overwrite block layout selection if needed */
  	if (sb->s_export_op->map_blocks &&
  	    sb->s_export_op->commit_blocks &&
  	    sb->s_bdev && sb->s_bdev->bd_disk->fops->pr_ops)
8a4c39268   Jeff Layton   nfsd: allow nfsd ...
155
  		exp->ex_layout_types |= 1 << LAYOUT_SCSI;
f99d4fbda   Christoph Hellwig   nfsd: add SCSI la...
156
  #endif
9cf514ccf   Christoph Hellwig   nfsd: implement p...
157
158
159
160
161
162
163
164
  }
  
  static void
  nfsd4_free_layout_stateid(struct nfs4_stid *stid)
  {
  	struct nfs4_layout_stateid *ls = layoutstateid(stid);
  	struct nfs4_client *clp = ls->ls_stid.sc_client;
  	struct nfs4_file *fp = ls->ls_stid.sc_file;
31ef83dc0   Christoph Hellwig   nfsd: add trace e...
165
  	trace_layoutstate_free(&ls->ls_stid.sc_stateid);
9cf514ccf   Christoph Hellwig   nfsd: implement p...
166
167
168
169
170
171
172
  	spin_lock(&clp->cl_lock);
  	list_del_init(&ls->ls_perclnt);
  	spin_unlock(&clp->cl_lock);
  
  	spin_lock(&fp->fi_lock);
  	list_del_init(&ls->ls_perfile);
  	spin_unlock(&fp->fi_lock);
1983a66f5   Jeff Layton   nfsd: don't set a...
173
174
  	if (!nfsd4_layout_ops[ls->ls_layout_type]->disable_recalls)
  		vfs_setlease(ls->ls_file, F_UNLCK, NULL, (void **)&ls);
c5c707f96   Christoph Hellwig   nfsd: implement p...
175
176
177
178
  	fput(ls->ls_file);
  
  	if (ls->ls_recalled)
  		atomic_dec(&ls->ls_stid.sc_file->fi_lo_recalls);
9cf514ccf   Christoph Hellwig   nfsd: implement p...
179
180
  	kmem_cache_free(nfs4_layout_stateid_cache, ls);
  }
c5c707f96   Christoph Hellwig   nfsd: implement p...
181
182
183
184
185
  static int
  nfsd4_layout_setlease(struct nfs4_layout_stateid *ls)
  {
  	struct file_lock *fl;
  	int status;
1983a66f5   Jeff Layton   nfsd: don't set a...
186
187
  	if (nfsd4_layout_ops[ls->ls_layout_type]->disable_recalls)
  		return 0;
c5c707f96   Christoph Hellwig   nfsd: implement p...
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
  	fl = locks_alloc_lock();
  	if (!fl)
  		return -ENOMEM;
  	locks_init_lock(fl);
  	fl->fl_lmops = &nfsd4_layouts_lm_ops;
  	fl->fl_flags = FL_LAYOUT;
  	fl->fl_type = F_RDLCK;
  	fl->fl_end = OFFSET_MAX;
  	fl->fl_owner = ls;
  	fl->fl_pid = current->tgid;
  	fl->fl_file = ls->ls_file;
  
  	status = vfs_setlease(fl->fl_file, fl->fl_type, &fl, NULL);
  	if (status) {
  		locks_free_lock(fl);
  		return status;
  	}
  	BUG_ON(fl != NULL);
  	return 0;
  }
9cf514ccf   Christoph Hellwig   nfsd: implement p...
208
209
210
211
212
213
214
215
  static struct nfs4_layout_stateid *
  nfsd4_alloc_layout_stateid(struct nfsd4_compound_state *cstate,
  		struct nfs4_stid *parent, u32 layout_type)
  {
  	struct nfs4_client *clp = cstate->clp;
  	struct nfs4_file *fp = parent->sc_file;
  	struct nfs4_layout_stateid *ls;
  	struct nfs4_stid *stp;
d19fb70dd   Kinglong Mee   NFSD: Fix a null ...
216
217
  	stp = nfs4_alloc_stid(cstate->clp, nfs4_layout_stateid_cache,
  					nfsd4_free_layout_stateid);
9cf514ccf   Christoph Hellwig   nfsd: implement p...
218
219
  	if (!stp)
  		return NULL;
d19fb70dd   Kinglong Mee   NFSD: Fix a null ...
220

9cf514ccf   Christoph Hellwig   nfsd: implement p...
221
222
223
224
225
226
227
228
  	get_nfs4_file(fp);
  	stp->sc_file = fp;
  
  	ls = layoutstateid(stp);
  	INIT_LIST_HEAD(&ls->ls_perclnt);
  	INIT_LIST_HEAD(&ls->ls_perfile);
  	spin_lock_init(&ls->ls_lock);
  	INIT_LIST_HEAD(&ls->ls_layouts);
cc8a55320   Jeff Layton   nfsd: serialize l...
229
  	mutex_init(&ls->ls_mutex);
9cf514ccf   Christoph Hellwig   nfsd: implement p...
230
  	ls->ls_layout_type = layout_type;
c5c707f96   Christoph Hellwig   nfsd: implement p...
231
232
233
234
235
236
237
238
239
240
  	nfsd4_init_cb(&ls->ls_recall, clp, &nfsd4_cb_layout_ops,
  			NFSPROC4_CLNT_CB_LAYOUT);
  
  	if (parent->sc_type == NFS4_DELEG_STID)
  		ls->ls_file = get_file(fp->fi_deleg_file);
  	else
  		ls->ls_file = find_any_file(fp);
  	BUG_ON(!ls->ls_file);
  
  	if (nfsd4_layout_setlease(ls)) {
1ca4b88e7   Kinglong Mee   nfsd: Fix a file ...
241
  		fput(ls->ls_file);
c5c707f96   Christoph Hellwig   nfsd: implement p...
242
243
244
245
  		put_nfs4_file(fp);
  		kmem_cache_free(nfs4_layout_stateid_cache, ls);
  		return NULL;
  	}
9cf514ccf   Christoph Hellwig   nfsd: implement p...
246
247
248
249
250
251
252
253
254
  
  	spin_lock(&clp->cl_lock);
  	stp->sc_type = NFS4_LAYOUT_STID;
  	list_add(&ls->ls_perclnt, &clp->cl_lo_states);
  	spin_unlock(&clp->cl_lock);
  
  	spin_lock(&fp->fi_lock);
  	list_add(&ls->ls_perfile, &fp->fi_lo_states);
  	spin_unlock(&fp->fi_lock);
31ef83dc0   Christoph Hellwig   nfsd: add trace e...
255
  	trace_layoutstate_alloc(&ls->ls_stid.sc_stateid);
9cf514ccf   Christoph Hellwig   nfsd: implement p...
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
  	return ls;
  }
  
  __be32
  nfsd4_preprocess_layout_stateid(struct svc_rqst *rqstp,
  		struct nfsd4_compound_state *cstate, stateid_t *stateid,
  		bool create, u32 layout_type, struct nfs4_layout_stateid **lsp)
  {
  	struct nfs4_layout_stateid *ls;
  	struct nfs4_stid *stid;
  	unsigned char typemask = NFS4_LAYOUT_STID;
  	__be32 status;
  
  	if (create)
  		typemask |= (NFS4_OPEN_STID | NFS4_LOCK_STID | NFS4_DELEG_STID);
  
  	status = nfsd4_lookup_stateid(cstate, stateid, typemask, &stid,
  			net_generic(SVC_NET(rqstp), nfsd_net_id));
  	if (status)
  		goto out;
  
  	if (!fh_match(&cstate->current_fh.fh_handle,
  		      &stid->sc_file->fi_fhandle)) {
  		status = nfserr_bad_stateid;
  		goto out_put_stid;
  	}
  
  	if (stid->sc_type != NFS4_LAYOUT_STID) {
  		ls = nfsd4_alloc_layout_stateid(cstate, stid, layout_type);
  		nfs4_put_stid(stid);
  
  		status = nfserr_jukebox;
  		if (!ls)
  			goto out;
cc8a55320   Jeff Layton   nfsd: serialize l...
290
  		mutex_lock(&ls->ls_mutex);
9cf514ccf   Christoph Hellwig   nfsd: implement p...
291
292
293
294
  	} else {
  		ls = container_of(stid, struct nfs4_layout_stateid, ls_stid);
  
  		status = nfserr_bad_stateid;
cc8a55320   Jeff Layton   nfsd: serialize l...
295
  		mutex_lock(&ls->ls_mutex);
14b7f4a1e   Jeff Layton   nfsd: handle seqi...
296
  		if (nfsd4_stateid_generation_after(stateid, &stid->sc_stateid))
cc8a55320   Jeff Layton   nfsd: serialize l...
297
  			goto out_unlock_stid;
9cf514ccf   Christoph Hellwig   nfsd: implement p...
298
  		if (layout_type != ls->ls_layout_type)
cc8a55320   Jeff Layton   nfsd: serialize l...
299
  			goto out_unlock_stid;
9cf514ccf   Christoph Hellwig   nfsd: implement p...
300
301
302
303
  	}
  
  	*lsp = ls;
  	return 0;
cc8a55320   Jeff Layton   nfsd: serialize l...
304
305
  out_unlock_stid:
  	mutex_unlock(&ls->ls_mutex);
9cf514ccf   Christoph Hellwig   nfsd: implement p...
306
307
308
309
310
  out_put_stid:
  	nfs4_put_stid(stid);
  out:
  	return status;
  }
c5c707f96   Christoph Hellwig   nfsd: implement p...
311
312
313
314
315
316
317
318
319
320
321
  static void
  nfsd4_recall_file_layout(struct nfs4_layout_stateid *ls)
  {
  	spin_lock(&ls->ls_lock);
  	if (ls->ls_recalled)
  		goto out_unlock;
  
  	ls->ls_recalled = true;
  	atomic_inc(&ls->ls_stid.sc_file->fi_lo_recalls);
  	if (list_empty(&ls->ls_layouts))
  		goto out_unlock;
31ef83dc0   Christoph Hellwig   nfsd: add trace e...
322
  	trace_layout_recall(&ls->ls_stid.sc_stateid);
c5c707f96   Christoph Hellwig   nfsd: implement p...
323
  	atomic_inc(&ls->ls_stid.sc_count);
c5c707f96   Christoph Hellwig   nfsd: implement p...
324
325
326
327
328
  	nfsd4_run_cb(&ls->ls_recall);
  
  out_unlock:
  	spin_unlock(&ls->ls_lock);
  }
9cf514ccf   Christoph Hellwig   nfsd: implement p...
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
  static inline u64
  layout_end(struct nfsd4_layout_seg *seg)
  {
  	u64 end = seg->offset + seg->length;
  	return end >= seg->offset ? end : NFS4_MAX_UINT64;
  }
  
  static void
  layout_update_len(struct nfsd4_layout_seg *lo, u64 end)
  {
  	if (end == NFS4_MAX_UINT64)
  		lo->length = NFS4_MAX_UINT64;
  	else
  		lo->length = end - lo->offset;
  }
  
  static bool
  layouts_overlapping(struct nfs4_layout *lo, struct nfsd4_layout_seg *s)
  {
  	if (s->iomode != IOMODE_ANY && s->iomode != lo->lo_seg.iomode)
  		return false;
  	if (layout_end(&lo->lo_seg) <= s->offset)
  		return false;
  	if (layout_end(s) <= lo->lo_seg.offset)
  		return false;
  	return true;
  }
  
  static bool
  layouts_try_merge(struct nfsd4_layout_seg *lo, struct nfsd4_layout_seg *new)
  {
  	if (lo->iomode != new->iomode)
  		return false;
  	if (layout_end(new) < lo->offset)
  		return false;
  	if (layout_end(lo) < new->offset)
  		return false;
  
  	lo->offset = min(lo->offset, new->offset);
  	layout_update_len(lo, max(layout_end(lo), layout_end(new)));
  	return true;
  }
c5c707f96   Christoph Hellwig   nfsd: implement p...
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
  static __be32
  nfsd4_recall_conflict(struct nfs4_layout_stateid *ls)
  {
  	struct nfs4_file *fp = ls->ls_stid.sc_file;
  	struct nfs4_layout_stateid *l, *n;
  	__be32 nfserr = nfs_ok;
  
  	assert_spin_locked(&fp->fi_lock);
  
  	list_for_each_entry_safe(l, n, &fp->fi_lo_states, ls_perfile) {
  		if (l != ls) {
  			nfsd4_recall_file_layout(l);
  			nfserr = nfserr_recallconflict;
  		}
  	}
  
  	return nfserr;
  }
9cf514ccf   Christoph Hellwig   nfsd: implement p...
389
390
391
392
  __be32
  nfsd4_insert_layout(struct nfsd4_layoutget *lgp, struct nfs4_layout_stateid *ls)
  {
  	struct nfsd4_layout_seg *seg = &lgp->lg_seg;
c5c707f96   Christoph Hellwig   nfsd: implement p...
393
  	struct nfs4_file *fp = ls->ls_stid.sc_file;
9cf514ccf   Christoph Hellwig   nfsd: implement p...
394
  	struct nfs4_layout *lp, *new = NULL;
c5c707f96   Christoph Hellwig   nfsd: implement p...
395
  	__be32 nfserr;
9cf514ccf   Christoph Hellwig   nfsd: implement p...
396

c5c707f96   Christoph Hellwig   nfsd: implement p...
397
398
399
400
  	spin_lock(&fp->fi_lock);
  	nfserr = nfsd4_recall_conflict(ls);
  	if (nfserr)
  		goto out;
9cf514ccf   Christoph Hellwig   nfsd: implement p...
401
402
403
404
405
406
  	spin_lock(&ls->ls_lock);
  	list_for_each_entry(lp, &ls->ls_layouts, lo_perstate) {
  		if (layouts_try_merge(&lp->lo_seg, seg))
  			goto done;
  	}
  	spin_unlock(&ls->ls_lock);
c5c707f96   Christoph Hellwig   nfsd: implement p...
407
  	spin_unlock(&fp->fi_lock);
9cf514ccf   Christoph Hellwig   nfsd: implement p...
408
409
410
411
412
413
  
  	new = kmem_cache_alloc(nfs4_layout_cache, GFP_KERNEL);
  	if (!new)
  		return nfserr_jukebox;
  	memcpy(&new->lo_seg, seg, sizeof(lp->lo_seg));
  	new->lo_state = ls;
c5c707f96   Christoph Hellwig   nfsd: implement p...
414
415
416
417
  	spin_lock(&fp->fi_lock);
  	nfserr = nfsd4_recall_conflict(ls);
  	if (nfserr)
  		goto out;
9cf514ccf   Christoph Hellwig   nfsd: implement p...
418
419
420
421
422
423
424
425
426
427
  	spin_lock(&ls->ls_lock);
  	list_for_each_entry(lp, &ls->ls_layouts, lo_perstate) {
  		if (layouts_try_merge(&lp->lo_seg, seg))
  			goto done;
  	}
  
  	atomic_inc(&ls->ls_stid.sc_count);
  	list_add_tail(&new->lo_perstate, &ls->ls_layouts);
  	new = NULL;
  done:
9767feb2c   Jeff Layton   nfsd: ensure that...
428
  	nfs4_inc_and_copy_stateid(&lgp->lg_sid, &ls->ls_stid);
9cf514ccf   Christoph Hellwig   nfsd: implement p...
429
  	spin_unlock(&ls->ls_lock);
c5c707f96   Christoph Hellwig   nfsd: implement p...
430
431
  out:
  	spin_unlock(&fp->fi_lock);
9cf514ccf   Christoph Hellwig   nfsd: implement p...
432
433
  	if (new)
  		kmem_cache_free(nfs4_layout_cache, new);
c5c707f96   Christoph Hellwig   nfsd: implement p...
434
  	return nfserr;
9cf514ccf   Christoph Hellwig   nfsd: implement p...
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
  }
  
  static void
  nfsd4_free_layouts(struct list_head *reaplist)
  {
  	while (!list_empty(reaplist)) {
  		struct nfs4_layout *lp = list_first_entry(reaplist,
  				struct nfs4_layout, lo_perstate);
  
  		list_del(&lp->lo_perstate);
  		nfs4_put_stid(&lp->lo_state->ls_stid);
  		kmem_cache_free(nfs4_layout_cache, lp);
  	}
  }
  
  static void
  nfsd4_return_file_layout(struct nfs4_layout *lp, struct nfsd4_layout_seg *seg,
  		struct list_head *reaplist)
  {
  	struct nfsd4_layout_seg *lo = &lp->lo_seg;
  	u64 end = layout_end(lo);
  
  	if (seg->offset <= lo->offset) {
  		if (layout_end(seg) >= end) {
  			list_move_tail(&lp->lo_perstate, reaplist);
  			return;
  		}
7890203da   Kinglong Mee   NFSD: Fix bad upd...
462
  		lo->offset = layout_end(seg);
9cf514ccf   Christoph Hellwig   nfsd: implement p...
463
464
465
466
467
468
469
  	} else {
  		/* retain the whole layout segment on a split. */
  		if (layout_end(seg) < end) {
  			dprintk("%s: split not supported
  ", __func__);
  			return;
  		}
7890203da   Kinglong Mee   NFSD: Fix bad upd...
470
  		end = seg->offset;
9cf514ccf   Christoph Hellwig   nfsd: implement p...
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
  	}
  
  	layout_update_len(lo, end);
  }
  
  __be32
  nfsd4_return_file_layouts(struct svc_rqst *rqstp,
  		struct nfsd4_compound_state *cstate,
  		struct nfsd4_layoutreturn *lrp)
  {
  	struct nfs4_layout_stateid *ls;
  	struct nfs4_layout *lp, *n;
  	LIST_HEAD(reaplist);
  	__be32 nfserr;
  	int found = 0;
  
  	nfserr = nfsd4_preprocess_layout_stateid(rqstp, cstate, &lrp->lr_sid,
  						false, lrp->lr_layout_type,
  						&ls);
31ef83dc0   Christoph Hellwig   nfsd: add trace e...
490
491
  	if (nfserr) {
  		trace_layout_return_lookup_fail(&lrp->lr_sid);
9cf514ccf   Christoph Hellwig   nfsd: implement p...
492
  		return nfserr;
31ef83dc0   Christoph Hellwig   nfsd: add trace e...
493
  	}
9cf514ccf   Christoph Hellwig   nfsd: implement p...
494
495
496
497
498
499
500
501
502
  
  	spin_lock(&ls->ls_lock);
  	list_for_each_entry_safe(lp, n, &ls->ls_layouts, lo_perstate) {
  		if (layouts_overlapping(lp, &lrp->lr_seg)) {
  			nfsd4_return_file_layout(lp, &lrp->lr_seg, &reaplist);
  			found++;
  		}
  	}
  	if (!list_empty(&ls->ls_layouts)) {
9767feb2c   Jeff Layton   nfsd: ensure that...
503
504
  		if (found)
  			nfs4_inc_and_copy_stateid(&lrp->lr_sid, &ls->ls_stid);
9cf514ccf   Christoph Hellwig   nfsd: implement p...
505
506
  		lrp->lrs_present = 1;
  	} else {
31ef83dc0   Christoph Hellwig   nfsd: add trace e...
507
  		trace_layoutstate_unhash(&ls->ls_stid.sc_stateid);
9cf514ccf   Christoph Hellwig   nfsd: implement p...
508
509
510
511
  		nfs4_unhash_stid(&ls->ls_stid);
  		lrp->lrs_present = 0;
  	}
  	spin_unlock(&ls->ls_lock);
cc8a55320   Jeff Layton   nfsd: serialize l...
512
  	mutex_unlock(&ls->ls_mutex);
9cf514ccf   Christoph Hellwig   nfsd: implement p...
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
  	nfs4_put_stid(&ls->ls_stid);
  	nfsd4_free_layouts(&reaplist);
  	return nfs_ok;
  }
  
  __be32
  nfsd4_return_client_layouts(struct svc_rqst *rqstp,
  		struct nfsd4_compound_state *cstate,
  		struct nfsd4_layoutreturn *lrp)
  {
  	struct nfs4_layout_stateid *ls, *n;
  	struct nfs4_client *clp = cstate->clp;
  	struct nfs4_layout *lp, *t;
  	LIST_HEAD(reaplist);
  
  	lrp->lrs_present = 0;
  
  	spin_lock(&clp->cl_lock);
  	list_for_each_entry_safe(ls, n, &clp->cl_lo_states, ls_perclnt) {
6f8f28ec5   Kinglong Mee   NFSD: Check layou...
532
533
  		if (ls->ls_layout_type != lrp->lr_layout_type)
  			continue;
9cf514ccf   Christoph Hellwig   nfsd: implement p...
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
  		if (lrp->lr_return_type == RETURN_FSID &&
  		    !fh_fsid_match(&ls->ls_stid.sc_file->fi_fhandle,
  				   &cstate->current_fh.fh_handle))
  			continue;
  
  		spin_lock(&ls->ls_lock);
  		list_for_each_entry_safe(lp, t, &ls->ls_layouts, lo_perstate) {
  			if (lrp->lr_seg.iomode == IOMODE_ANY ||
  			    lrp->lr_seg.iomode == lp->lo_seg.iomode)
  				list_move_tail(&lp->lo_perstate, &reaplist);
  		}
  		spin_unlock(&ls->ls_lock);
  	}
  	spin_unlock(&clp->cl_lock);
  
  	nfsd4_free_layouts(&reaplist);
  	return 0;
  }
  
  static void
  nfsd4_return_all_layouts(struct nfs4_layout_stateid *ls,
  		struct list_head *reaplist)
  {
  	spin_lock(&ls->ls_lock);
  	list_splice_init(&ls->ls_layouts, reaplist);
  	spin_unlock(&ls->ls_lock);
  }
  
  void
  nfsd4_return_all_client_layouts(struct nfs4_client *clp)
  {
  	struct nfs4_layout_stateid *ls, *n;
  	LIST_HEAD(reaplist);
  
  	spin_lock(&clp->cl_lock);
  	list_for_each_entry_safe(ls, n, &clp->cl_lo_states, ls_perclnt)
  		nfsd4_return_all_layouts(ls, &reaplist);
  	spin_unlock(&clp->cl_lock);
  
  	nfsd4_free_layouts(&reaplist);
  }
  
  void
  nfsd4_return_all_file_layouts(struct nfs4_client *clp, struct nfs4_file *fp)
  {
  	struct nfs4_layout_stateid *ls, *n;
  	LIST_HEAD(reaplist);
  
  	spin_lock(&fp->fi_lock);
  	list_for_each_entry_safe(ls, n, &fp->fi_lo_states, ls_perfile) {
  		if (ls->ls_stid.sc_client == clp)
  			nfsd4_return_all_layouts(ls, &reaplist);
  	}
  	spin_unlock(&fp->fi_lock);
  
  	nfsd4_free_layouts(&reaplist);
  }
c5c707f96   Christoph Hellwig   nfsd: implement p...
591
592
593
594
595
  static void
  nfsd4_cb_layout_fail(struct nfs4_layout_stateid *ls)
  {
  	struct nfs4_client *clp = ls->ls_stid.sc_client;
  	char addr_str[INET6_ADDRSTRLEN];
377e7a27c   Greg Kroah-Hartman   Make static userm...
596
  	static char const nfsd_recall_failed[] = "/sbin/nfsd-recall-failed";
c5c707f96   Christoph Hellwig   nfsd: implement p...
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
  	static char *envp[] = {
  		"HOME=/",
  		"TERM=linux",
  		"PATH=/sbin:/usr/sbin:/bin:/usr/bin",
  		NULL
  	};
  	char *argv[8];
  	int error;
  
  	rpc_ntop((struct sockaddr *)&clp->cl_addr, addr_str, sizeof(addr_str));
  
  	printk(KERN_WARNING
  		"nfsd: client %s failed to respond to layout recall. "
  		"  Fencing..
  ", addr_str);
377e7a27c   Greg Kroah-Hartman   Make static userm...
612
  	argv[0] = (char *)nfsd_recall_failed;
c5c707f96   Christoph Hellwig   nfsd: implement p...
613
614
615
  	argv[1] = addr_str;
  	argv[2] = ls->ls_file->f_path.mnt->mnt_sb->s_id;
  	argv[3] = NULL;
377e7a27c   Greg Kroah-Hartman   Make static userm...
616
617
  	error = call_usermodehelper(nfsd_recall_failed, argv, envp,
  				    UMH_WAIT_PROC);
c5c707f96   Christoph Hellwig   nfsd: implement p...
618
619
620
621
622
623
  	if (error) {
  		printk(KERN_ERR "nfsd: fence failed for client %s: %d!
  ",
  			addr_str, error);
  	}
  }
cc8a55320   Jeff Layton   nfsd: serialize l...
624
625
626
627
628
629
630
  static void
  nfsd4_cb_layout_prepare(struct nfsd4_callback *cb)
  {
  	struct nfs4_layout_stateid *ls =
  		container_of(cb, struct nfs4_layout_stateid, ls_recall);
  
  	mutex_lock(&ls->ls_mutex);
9767feb2c   Jeff Layton   nfsd: ensure that...
631
  	nfs4_inc_and_copy_stateid(&ls->ls_recall_sid, &ls->ls_stid);
be20aa00c   Jeff Layton   nfsd: don't hold ...
632
  	mutex_unlock(&ls->ls_mutex);
cc8a55320   Jeff Layton   nfsd: serialize l...
633
  }
c5c707f96   Christoph Hellwig   nfsd: implement p...
634
635
636
637
638
  static int
  nfsd4_cb_layout_done(struct nfsd4_callback *cb, struct rpc_task *task)
  {
  	struct nfs4_layout_stateid *ls =
  		container_of(cb, struct nfs4_layout_stateid, ls_recall);
6b9b21073   Jeff Layton   nfsd: give up on ...
639
640
  	struct nfsd_net *nn;
  	ktime_t now, cutoff;
f99d4fbda   Christoph Hellwig   nfsd: add SCSI la...
641
  	const struct nfsd4_layout_ops *ops;
c5c707f96   Christoph Hellwig   nfsd: implement p...
642
  	LIST_HEAD(reaplist);
6b9b21073   Jeff Layton   nfsd: give up on ...
643

c5c707f96   Christoph Hellwig   nfsd: implement p...
644
645
  	switch (task->tk_status) {
  	case 0:
6b9b21073   Jeff Layton   nfsd: give up on ...
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
  	case -NFS4ERR_DELAY:
  		/*
  		 * Anything left? If not, then call it done. Note that we don't
  		 * take the spinlock since this is an optimization and nothing
  		 * should get added until the cb counter goes to zero.
  		 */
  		if (list_empty(&ls->ls_layouts))
  			return 1;
  
  		/* Poll the client until it's done with the layout */
  		now = ktime_get();
  		nn = net_generic(ls->ls_stid.sc_client->net, nfsd_net_id);
  
  		/* Client gets 2 lease periods to return it */
  		cutoff = ktime_add_ns(task->tk_start,
  					 nn->nfsd4_lease * NSEC_PER_SEC * 2);
  
  		if (ktime_before(now, cutoff)) {
  			rpc_delay(task, HZ/100); /* 10 mili-seconds */
  			return 0;
  		}
  		/* Fallthrough */
c5c707f96   Christoph Hellwig   nfsd: implement p...
668
669
670
671
  	default:
  		/*
  		 * Unknown error or non-responding client, we'll need to fence.
  		 */
f99d4fbda   Christoph Hellwig   nfsd: add SCSI la...
672
673
674
675
676
677
678
  		trace_layout_recall_fail(&ls->ls_stid.sc_stateid);
  
  		ops = nfsd4_layout_ops[ls->ls_layout_type];
  		if (ops->fence_client)
  			ops->fence_client(ls);
  		else
  			nfsd4_cb_layout_fail(ls);
c5c707f96   Christoph Hellwig   nfsd: implement p...
679
  		return -1;
851238a22   Jeff Layton   nfsd: fix error h...
680
681
682
683
  	case -NFS4ERR_NOMATCHING_LAYOUT:
  		trace_layout_recall_done(&ls->ls_stid.sc_stateid);
  		task->tk_status = 0;
  		return 1;
c5c707f96   Christoph Hellwig   nfsd: implement p...
684
685
686
687
688
689
690
691
692
  	}
  }
  
  static void
  nfsd4_cb_layout_release(struct nfsd4_callback *cb)
  {
  	struct nfs4_layout_stateid *ls =
  		container_of(cb, struct nfs4_layout_stateid, ls_recall);
  	LIST_HEAD(reaplist);
31ef83dc0   Christoph Hellwig   nfsd: add trace e...
693
  	trace_layout_recall_release(&ls->ls_stid.sc_stateid);
c5c707f96   Christoph Hellwig   nfsd: implement p...
694
695
696
697
  	nfsd4_return_all_layouts(ls, &reaplist);
  	nfsd4_free_layouts(&reaplist);
  	nfs4_put_stid(&ls->ls_stid);
  }
c4cb89746   Julia Lawall   nfsd: constify nf...
698
  static const struct nfsd4_callback_ops nfsd4_cb_layout_ops = {
cc8a55320   Jeff Layton   nfsd: serialize l...
699
  	.prepare	= nfsd4_cb_layout_prepare,
c5c707f96   Christoph Hellwig   nfsd: implement p...
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
  	.done		= nfsd4_cb_layout_done,
  	.release	= nfsd4_cb_layout_release,
  };
  
  static bool
  nfsd4_layout_lm_break(struct file_lock *fl)
  {
  	/*
  	 * We don't want the locks code to timeout the lease for us;
  	 * we'll remove it ourself if a layout isn't returned
  	 * in time:
  	 */
  	fl->fl_break_time = 0;
  	nfsd4_recall_file_layout(fl->fl_owner);
  	return false;
  }
  
  static int
  nfsd4_layout_lm_change(struct file_lock *onlist, int arg,
  		struct list_head *dispose)
  {
  	BUG_ON(!(arg & F_UNLCK));
  	return lease_modify(onlist, arg, dispose);
  }
  
  static const struct lock_manager_operations nfsd4_layouts_lm_ops = {
  	.lm_break	= nfsd4_layout_lm_break,
  	.lm_change	= nfsd4_layout_lm_change,
  };
9cf514ccf   Christoph Hellwig   nfsd: implement p...
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
  int
  nfsd4_init_pnfs(void)
  {
  	int i;
  
  	for (i = 0; i < DEVID_HASH_SIZE; i++)
  		INIT_LIST_HEAD(&nfsd_devid_hash[i]);
  
  	nfs4_layout_cache = kmem_cache_create("nfs4_layout",
  			sizeof(struct nfs4_layout), 0, 0, NULL);
  	if (!nfs4_layout_cache)
  		return -ENOMEM;
  
  	nfs4_layout_stateid_cache = kmem_cache_create("nfs4_layout_stateid",
  			sizeof(struct nfs4_layout_stateid), 0, 0, NULL);
  	if (!nfs4_layout_stateid_cache) {
  		kmem_cache_destroy(nfs4_layout_cache);
  		return -ENOMEM;
  	}
  	return 0;
  }
  
  void
  nfsd4_exit_pnfs(void)
  {
  	int i;
  
  	kmem_cache_destroy(nfs4_layout_cache);
  	kmem_cache_destroy(nfs4_layout_stateid_cache);
  
  	for (i = 0; i < DEVID_HASH_SIZE; i++) {
  		struct nfsd4_deviceid_map *map, *n;
  
  		list_for_each_entry_safe(map, n, &nfsd_devid_hash[i], hash)
  			kfree(map);
  	}
  }