Blame view

fs/xfs/xfs_filestream.c 23 KB
2a82b8be8   David Chinner   [XFS] Concurrent ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
  /*
   * Copyright (c) 2006-2007 Silicon Graphics, Inc.
   * All Rights Reserved.
   *
   * This program is free software; you can redistribute it and/or
   * modify it under the terms of the GNU General Public License as
   * published by the Free Software Foundation.
   *
   * This program is distributed in the hope that it would be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write the Free Software Foundation,
   * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
   */
  #include "xfs.h"
  #include "xfs_bmap_btree.h"
  #include "xfs_inum.h"
2a82b8be8   David Chinner   [XFS] Concurrent ...
21
22
23
  #include "xfs_dinode.h"
  #include "xfs_inode.h"
  #include "xfs_ag.h"
2a82b8be8   David Chinner   [XFS] Concurrent ...
24
25
26
27
28
29
30
31
32
  #include "xfs_log.h"
  #include "xfs_trans.h"
  #include "xfs_sb.h"
  #include "xfs_mount.h"
  #include "xfs_bmap.h"
  #include "xfs_alloc.h"
  #include "xfs_utils.h"
  #include "xfs_mru_cache.h"
  #include "xfs_filestream.h"
0b1b213fc   Christoph Hellwig   xfs: event tracin...
33
  #include "xfs_trace.h"
2a82b8be8   David Chinner   [XFS] Concurrent ...
34
35
36
37
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
  
  #ifdef XFS_FILESTREAMS_TRACE
  
  ktrace_t *xfs_filestreams_trace_buf;
  
  STATIC void
  xfs_filestreams_trace(
  	xfs_mount_t	*mp,	/* mount point */
  	int		type,	/* type of trace */
  	const char	*func,	/* source function */
  	int		line,	/* source line number */
  	__psunsigned_t	arg0,
  	__psunsigned_t	arg1,
  	__psunsigned_t	arg2,
  	__psunsigned_t	arg3,
  	__psunsigned_t	arg4,
  	__psunsigned_t	arg5)
  {
  	ktrace_enter(xfs_filestreams_trace_buf,
  		(void *)(__psint_t)(type | (line << 16)),
  		(void *)func,
  		(void *)(__psunsigned_t)current_pid(),
  		(void *)mp,
  		(void *)(__psunsigned_t)arg0,
  		(void *)(__psunsigned_t)arg1,
  		(void *)(__psunsigned_t)arg2,
  		(void *)(__psunsigned_t)arg3,
  		(void *)(__psunsigned_t)arg4,
  		(void *)(__psunsigned_t)arg5,
  		NULL, NULL, NULL, NULL, NULL, NULL);
  }
  
  #define TRACE0(mp,t)			TRACE6(mp,t,0,0,0,0,0,0)
  #define TRACE1(mp,t,a0)			TRACE6(mp,t,a0,0,0,0,0,0)
  #define TRACE2(mp,t,a0,a1)		TRACE6(mp,t,a0,a1,0,0,0,0)
  #define TRACE3(mp,t,a0,a1,a2)		TRACE6(mp,t,a0,a1,a2,0,0,0)
  #define TRACE4(mp,t,a0,a1,a2,a3)	TRACE6(mp,t,a0,a1,a2,a3,0,0)
  #define TRACE5(mp,t,a0,a1,a2,a3,a4)	TRACE6(mp,t,a0,a1,a2,a3,a4,0)
  #define TRACE6(mp,t,a0,a1,a2,a3,a4,a5) \
34a622b2e   Harvey Harrison   [XFS] replace rem...
73
  	xfs_filestreams_trace(mp, t, __func__, __LINE__, \
2a82b8be8   David Chinner   [XFS] Concurrent ...
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
  				(__psunsigned_t)a0, (__psunsigned_t)a1, \
  				(__psunsigned_t)a2, (__psunsigned_t)a3, \
  				(__psunsigned_t)a4, (__psunsigned_t)a5)
  
  #define TRACE_AG_SCAN(mp, ag, ag2) \
  		TRACE2(mp, XFS_FSTRM_KTRACE_AGSCAN, ag, ag2);
  #define TRACE_AG_PICK1(mp, max_ag, maxfree) \
  		TRACE2(mp, XFS_FSTRM_KTRACE_AGPICK1, max_ag, maxfree);
  #define TRACE_AG_PICK2(mp, ag, ag2, cnt, free, scan, flag) \
  		TRACE6(mp, XFS_FSTRM_KTRACE_AGPICK2, ag, ag2, \
  			 cnt, free, scan, flag)
  #define TRACE_UPDATE(mp, ip, ag, cnt, ag2, cnt2) \
  		TRACE5(mp, XFS_FSTRM_KTRACE_UPDATE, ip, ag, cnt, ag2, cnt2)
  #define TRACE_FREE(mp, ip, pip, ag, cnt) \
  		TRACE4(mp, XFS_FSTRM_KTRACE_FREE, ip, pip, ag, cnt)
  #define TRACE_LOOKUP(mp, ip, pip, ag, cnt) \
  		TRACE4(mp, XFS_FSTRM_KTRACE_ITEM_LOOKUP, ip, pip, ag, cnt)
  #define TRACE_ASSOCIATE(mp, ip, pip, ag, cnt) \
  		TRACE4(mp, XFS_FSTRM_KTRACE_ASSOCIATE, ip, pip, ag, cnt)
  #define TRACE_MOVEAG(mp, ip, pip, oag, ocnt, nag, ncnt) \
  		TRACE6(mp, XFS_FSTRM_KTRACE_MOVEAG, ip, pip, oag, ocnt, nag, ncnt)
  #define TRACE_ORPHAN(mp, ip, ag) \
  		TRACE2(mp, XFS_FSTRM_KTRACE_ORPHAN, ip, ag);
  
  
  #else
  #define TRACE_AG_SCAN(mp, ag, ag2)
  #define TRACE_AG_PICK1(mp, max_ag, maxfree)
  #define TRACE_AG_PICK2(mp, ag, ag2, cnt, free, scan, flag)
  #define TRACE_UPDATE(mp, ip, ag, cnt, ag2, cnt2)
  #define TRACE_FREE(mp, ip, pip, ag, cnt)
  #define TRACE_LOOKUP(mp, ip, pip, ag, cnt)
  #define TRACE_ASSOCIATE(mp, ip, pip, ag, cnt)
  #define TRACE_MOVEAG(mp, ip, pip, oag, ocnt, nag, ncnt)
  #define TRACE_ORPHAN(mp, ip, ag)
  #endif
  
  static kmem_zone_t *item_zone;
  
  /*
   * Structure for associating a file or a directory with an allocation group.
   * The parent directory pointer is only needed for files, but since there will
   * generally be vastly more files than directories in the cache, using the same
   * data structure simplifies the code with very little memory overhead.
   */
  typedef struct fstrm_item
  {
  	xfs_agnumber_t	ag;	/* AG currently in use for the file/directory. */
  	xfs_inode_t	*ip;	/* inode self-pointer. */
  	xfs_inode_t	*pip;	/* Parent directory inode pointer. */
  } fstrm_item_t;
0664ce8d0   Christoph Hellwig   xfs: clean up fil...
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
  /*
   * Allocation group filestream associations are tracked with per-ag atomic
   * counters.  These counters allow _xfs_filestream_pick_ag() to tell whether a
   * particular AG already has active filestreams associated with it. The mount
   * point's m_peraglock is used to protect these counters from per-ag array
   * re-allocation during a growfs operation.  When xfs_growfs_data_private() is
   * about to reallocate the array, it calls xfs_filestream_flush() with the
   * m_peraglock held in write mode.
   *
   * Since xfs_mru_cache_flush() guarantees that all the free functions for all
   * the cache elements have finished executing before it returns, it's safe for
   * the free functions to use the atomic counters without m_peraglock protection.
   * This allows the implementation of xfs_fstrm_free_func() to be agnostic about
   * whether it was called with the m_peraglock held in read mode, write mode or
   * not held at all.  The race condition this addresses is the following:
   *
   *  - The work queue scheduler fires and pulls a filestream directory cache
   *    element off the LRU end of the cache for deletion, then gets pre-empted.
   *  - A growfs operation grabs the m_peraglock in write mode, flushes all the
   *    remaining items from the cache and reallocates the mount point's per-ag
   *    array, resetting all the counters to zero.
   *  - The work queue thread resumes and calls the free function for the element
   *    it started cleaning up earlier.  In the process it decrements the
   *    filestreams counter for an AG that now has no references.
   *
   * With a shrinkfs feature, the above scenario could panic the system.
   *
   * All other uses of the following macros should be protected by either the
   * m_peraglock held in read mode, or the cache's internal locking exposed by the
   * interval between a call to xfs_mru_cache_lookup() and a call to
   * xfs_mru_cache_done().  In addition, the m_peraglock must be held in read mode
   * when new elements are added to the cache.
   *
   * Combined, these locking rules ensure that no associations will ever exist in
   * the cache that reference per-ag array elements that have since been
   * reallocated.
   */
  static int
  xfs_filestream_peek_ag(
  	xfs_mount_t	*mp,
  	xfs_agnumber_t	agno)
  {
  	struct xfs_perag *pag;
  	int		ret;
  
  	pag = xfs_perag_get(mp, agno);
  	ret = atomic_read(&pag->pagf_fstrms);
  	xfs_perag_put(pag);
  	return ret;
  }
  
  static int
  xfs_filestream_get_ag(
  	xfs_mount_t	*mp,
  	xfs_agnumber_t	agno)
  {
  	struct xfs_perag *pag;
  	int		ret;
  
  	pag = xfs_perag_get(mp, agno);
  	ret = atomic_inc_return(&pag->pagf_fstrms);
  	xfs_perag_put(pag);
  	return ret;
  }
  
  static void
  xfs_filestream_put_ag(
  	xfs_mount_t	*mp,
  	xfs_agnumber_t	agno)
  {
  	struct xfs_perag *pag;
  
  	pag = xfs_perag_get(mp, agno);
  	atomic_dec(&pag->pagf_fstrms);
  	xfs_perag_put(pag);
  }
2a82b8be8   David Chinner   [XFS] Concurrent ...
201
202
203
204
205
206
207
208
209
210
211
212
213
  
  /*
   * Scan the AGs starting at startag looking for an AG that isn't in use and has
   * at least minlen blocks free.
   */
  static int
  _xfs_filestream_pick_ag(
  	xfs_mount_t	*mp,
  	xfs_agnumber_t	startag,
  	xfs_agnumber_t	*agp,
  	int		flags,
  	xfs_extlen_t	minlen)
  {
4196ac08c   Dave Chinner   xfs: Convert file...
214
  	int		streams, max_streams;
2a82b8be8   David Chinner   [XFS] Concurrent ...
215
  	int		err, trylock, nscan;
6cc87645e   Dave Chinner   xfs: factor out c...
216
  	xfs_extlen_t	longest, free, minfree, maxfree = 0;
2a82b8be8   David Chinner   [XFS] Concurrent ...
217
218
219
220
221
222
223
224
225
226
227
228
229
  	xfs_agnumber_t	ag, max_ag = NULLAGNUMBER;
  	struct xfs_perag *pag;
  
  	/* 2% of an AG's blocks must be free for it to be chosen. */
  	minfree = mp->m_sb.sb_agblocks / 50;
  
  	ag = startag;
  	*agp = NULLAGNUMBER;
  
  	/* For the first pass, don't sleep trying to init the per-AG. */
  	trylock = XFS_ALLOC_FLAG_TRYLOCK;
  
  	for (nscan = 0; 1; nscan++) {
4196ac08c   Dave Chinner   xfs: Convert file...
230
231
  		pag = xfs_perag_get(mp, ag);
  		TRACE_AG_SCAN(mp, ag, atomic_read(&pag->pagf_fstrms));
2a82b8be8   David Chinner   [XFS] Concurrent ...
232
233
234
  
  		if (!pag->pagf_init) {
  			err = xfs_alloc_pagf_init(mp, NULL, ag, trylock);
4196ac08c   Dave Chinner   xfs: Convert file...
235
236
  			if (err && !trylock) {
  				xfs_perag_put(pag);
2a82b8be8   David Chinner   [XFS] Concurrent ...
237
  				return err;
4196ac08c   Dave Chinner   xfs: Convert file...
238
  			}
2a82b8be8   David Chinner   [XFS] Concurrent ...
239
240
241
242
243
244
245
246
247
  		}
  
  		/* Might fail sometimes during the 1st pass with trylock set. */
  		if (!pag->pagf_init)
  			goto next_ag;
  
  		/* Keep track of the AG with the most free blocks. */
  		if (pag->pagf_freeblks > maxfree) {
  			maxfree = pag->pagf_freeblks;
4196ac08c   Dave Chinner   xfs: Convert file...
248
  			max_streams = atomic_read(&pag->pagf_fstrms);
2a82b8be8   David Chinner   [XFS] Concurrent ...
249
250
251
252
253
254
255
256
257
258
259
260
261
  			max_ag = ag;
  		}
  
  		/*
  		 * The AG reference count does two things: it enforces mutual
  		 * exclusion when examining the suitability of an AG in this
  		 * loop, and it guards against two filestreams being established
  		 * in the same AG as each other.
  		 */
  		if (xfs_filestream_get_ag(mp, ag) > 1) {
  			xfs_filestream_put_ag(mp, ag);
  			goto next_ag;
  		}
6cc87645e   Dave Chinner   xfs: factor out c...
262
  		longest = xfs_alloc_longest_free_extent(mp, pag);
2a82b8be8   David Chinner   [XFS] Concurrent ...
263
264
265
266
267
268
269
  		if (((minlen && longest >= minlen) ||
  		     (!minlen && pag->pagf_freeblks >= minfree)) &&
  		    (!pag->pagf_metadata || !(flags & XFS_PICK_USERDATA) ||
  		     (flags & XFS_PICK_LOWSPACE))) {
  
  			/* Break out, retaining the reference on the AG. */
  			free = pag->pagf_freeblks;
4196ac08c   Dave Chinner   xfs: Convert file...
270
271
  			streams = atomic_read(&pag->pagf_fstrms);
  			xfs_perag_put(pag);
2a82b8be8   David Chinner   [XFS] Concurrent ...
272
273
274
275
276
277
278
  			*agp = ag;
  			break;
  		}
  
  		/* Drop the reference on this AG, it's not usable. */
  		xfs_filestream_put_ag(mp, ag);
  next_ag:
4196ac08c   Dave Chinner   xfs: Convert file...
279
  		xfs_perag_put(pag);
2a82b8be8   David Chinner   [XFS] Concurrent ...
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
  		/* Move to the next AG, wrapping to AG 0 if necessary. */
  		if (++ag >= mp->m_sb.sb_agcount)
  			ag = 0;
  
  		/* If a full pass of the AGs hasn't been done yet, continue. */
  		if (ag != startag)
  			continue;
  
  		/* Allow sleeping in xfs_alloc_pagf_init() on the 2nd pass. */
  		if (trylock != 0) {
  			trylock = 0;
  			continue;
  		}
  
  		/* Finally, if lowspace wasn't set, set it for the 3rd pass. */
  		if (!(flags & XFS_PICK_LOWSPACE)) {
  			flags |= XFS_PICK_LOWSPACE;
  			continue;
  		}
  
  		/*
  		 * Take the AG with the most free space, regardless of whether
  		 * it's already in use by another filestream.
  		 */
  		if (max_ag != NULLAGNUMBER) {
  			xfs_filestream_get_ag(mp, max_ag);
  			TRACE_AG_PICK1(mp, max_ag, maxfree);
4196ac08c   Dave Chinner   xfs: Convert file...
307
  			streams = max_streams;
2a82b8be8   David Chinner   [XFS] Concurrent ...
308
309
310
311
312
313
314
315
316
317
  			free = maxfree;
  			*agp = max_ag;
  			break;
  		}
  
  		/* take AG 0 if none matched */
  		TRACE_AG_PICK1(mp, max_ag, maxfree);
  		*agp = 0;
  		return 0;
  	}
4196ac08c   Dave Chinner   xfs: Convert file...
318
  	TRACE_AG_PICK2(mp, startag, *agp, streams, free, nscan, flags);
2a82b8be8   David Chinner   [XFS] Concurrent ...
319
320
321
322
323
324
  
  	return 0;
  }
  
  /*
   * Set the allocation group number for a file or a directory, updating inode
1c1c6ebcf   Dave Chinner   xfs: Replace per-...
325
   * references and per-AG references as appropriate.
2a82b8be8   David Chinner   [XFS] Concurrent ...
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
   */
  static int
  _xfs_filestream_update_ag(
  	xfs_inode_t	*ip,
  	xfs_inode_t	*pip,
  	xfs_agnumber_t	ag)
  {
  	int		err = 0;
  	xfs_mount_t	*mp;
  	xfs_mru_cache_t	*cache;
  	fstrm_item_t	*item;
  	xfs_agnumber_t	old_ag;
  	xfs_inode_t	*old_pip;
  
  	/*
  	 * Either ip is a regular file and pip is a directory, or ip is a
  	 * directory and pip is NULL.
  	 */
abbede1b3   Al Viro   xfs: get rid of o...
344
  	ASSERT(ip && ((S_ISREG(ip->i_d.di_mode) && pip &&
03209378b   Al Viro   xfs: fix misspell...
345
346
  	               S_ISDIR(pip->i_d.di_mode)) ||
  	              (S_ISDIR(ip->i_d.di_mode) && !pip)));
2a82b8be8   David Chinner   [XFS] Concurrent ...
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
  
  	mp = ip->i_mount;
  	cache = mp->m_filestream;
  
  	item = xfs_mru_cache_lookup(cache, ip->i_ino);
  	if (item) {
  		ASSERT(item->ip == ip);
  		old_ag = item->ag;
  		item->ag = ag;
  		old_pip = item->pip;
  		item->pip = pip;
  		xfs_mru_cache_done(cache);
  
  		/*
  		 * If the AG has changed, drop the old ref and take a new one,
  		 * effectively transferring the reference from old to new AG.
  		 */
  		if (ag != old_ag) {
  			xfs_filestream_put_ag(mp, old_ag);
  			xfs_filestream_get_ag(mp, ag);
  		}
  
  		/*
  		 * If ip is a file and its pip has changed, drop the old ref and
  		 * take a new one.
  		 */
  		if (pip && pip != old_pip) {
  			IRELE(old_pip);
  			IHOLD(pip);
  		}
  
  		TRACE_UPDATE(mp, ip, old_ag, xfs_filestream_peek_ag(mp, old_ag),
  				ag, xfs_filestream_peek_ag(mp, ag));
  		return 0;
  	}
  
  	item = kmem_zone_zalloc(item_zone, KM_MAYFAIL);
  	if (!item)
  		return ENOMEM;
  
  	item->ag = ag;
  	item->ip = ip;
  	item->pip = pip;
  
  	err = xfs_mru_cache_insert(cache, ip->i_ino, item);
  	if (err) {
  		kmem_zone_free(item_zone, item);
  		return err;
  	}
  
  	/* Take a reference on the AG. */
  	xfs_filestream_get_ag(mp, ag);
  
  	/*
  	 * Take a reference on the inode itself regardless of whether it's a
  	 * regular file or a directory.
  	 */
  	IHOLD(ip);
  
  	/*
  	 * In the case of a regular file, take a reference on the parent inode
  	 * as well to ensure it remains in-core.
  	 */
  	if (pip)
  		IHOLD(pip);
  
  	TRACE_UPDATE(mp, ip, ag, xfs_filestream_peek_ag(mp, ag),
  			ag, xfs_filestream_peek_ag(mp, ag));
  
  	return 0;
  }
  
  /* xfs_fstrm_free_func(): callback for freeing cached stream items. */
a8272ce0c   David Chinner   [XFS] Fix up spar...
420
  STATIC void
2a82b8be8   David Chinner   [XFS] Concurrent ...
421
  xfs_fstrm_free_func(
bcc7b445e   Eric Sandeen   [XFS] fix filestr...
422
423
  	unsigned long	ino,
  	void		*data)
2a82b8be8   David Chinner   [XFS] Concurrent ...
424
  {
bcc7b445e   Eric Sandeen   [XFS] fix filestr...
425
  	fstrm_item_t	*item  = (fstrm_item_t *)data;
2a82b8be8   David Chinner   [XFS] Concurrent ...
426
  	xfs_inode_t	*ip = item->ip;
2a82b8be8   David Chinner   [XFS] Concurrent ...
427
428
429
430
431
432
  
  	ASSERT(ip->i_ino == ino);
  
  	xfs_iflags_clear(ip, XFS_IFILESTREAM);
  
  	/* Drop the reference taken on the AG when the item was added. */
0664ce8d0   Christoph Hellwig   xfs: clean up fil...
433
  	xfs_filestream_put_ag(ip->i_mount, item->ag);
2a82b8be8   David Chinner   [XFS] Concurrent ...
434

2a82b8be8   David Chinner   [XFS] Concurrent ...
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
462
463
464
465
466
  	TRACE_FREE(ip->i_mount, ip, item->pip, item->ag,
  		xfs_filestream_peek_ag(ip->i_mount, item->ag));
  
  	/*
  	 * _xfs_filestream_update_ag() always takes a reference on the inode
  	 * itself, whether it's a file or a directory.  Release it here.
  	 * This can result in the inode being freed and so we must
  	 * not hold any inode locks when freeing filesstreams objects
  	 * otherwise we can deadlock here.
  	 */
  	IRELE(ip);
  
  	/*
  	 * In the case of a regular file, _xfs_filestream_update_ag() also
  	 * takes a ref on the parent inode to keep it in-core.  Release that
  	 * too.
  	 */
  	if (item->pip)
  		IRELE(item->pip);
  
  	/* Finally, free the memory allocated for the item. */
  	kmem_zone_free(item_zone, item);
  }
  
  /*
   * xfs_filestream_init() is called at xfs initialisation time to set up the
   * memory zone that will be used for filestream data structure allocation.
   */
  int
  xfs_filestream_init(void)
  {
  	item_zone = kmem_zone_init(sizeof(fstrm_item_t), "fstrm_item");
9f8868ffb   Christoph Hellwig   [XFS] streamline ...
467
468
  	if (!item_zone)
  		return -ENOMEM;
0b1b213fc   Christoph Hellwig   xfs: event tracin...
469

9f8868ffb   Christoph Hellwig   [XFS] streamline ...
470
  	return 0;
2a82b8be8   David Chinner   [XFS] Concurrent ...
471
472
473
474
475
476
477
478
479
  }
  
  /*
   * xfs_filestream_uninit() is called at xfs termination time to destroy the
   * memory zone that was used for filestream data structure allocation.
   */
  void
  xfs_filestream_uninit(void)
  {
2a82b8be8   David Chinner   [XFS] Concurrent ...
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
  	kmem_zone_destroy(item_zone);
  }
  
  /*
   * xfs_filestream_mount() is called when a file system is mounted with the
   * filestream option.  It is responsible for allocating the data structures
   * needed to track the new file system's file streams.
   */
  int
  xfs_filestream_mount(
  	xfs_mount_t	*mp)
  {
  	int		err;
  	unsigned int	lifetime, grp_count;
  
  	/*
  	 * The filestream timer tunable is currently fixed within the range of
  	 * one second to four minutes, with five seconds being the default.  The
  	 * group count is somewhat arbitrary, but it'd be nice to adhere to the
  	 * timer tunable to within about 10 percent.  This requires at least 10
  	 * groups.
  	 */
  	lifetime  = xfs_fstrm_centisecs * 10;
  	grp_count = 10;
  
  	err = xfs_mru_cache_create(&mp->m_filestream, lifetime, grp_count,
bcc7b445e   Eric Sandeen   [XFS] fix filestr...
506
  	                     xfs_fstrm_free_func);
2a82b8be8   David Chinner   [XFS] Concurrent ...
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
  
  	return err;
  }
  
  /*
   * xfs_filestream_unmount() is called when a file system that was mounted with
   * the filestream option is unmounted.  It drains the data structures created
   * to track the file system's file streams and frees all the memory that was
   * allocated.
   */
  void
  xfs_filestream_unmount(
  	xfs_mount_t	*mp)
  {
  	xfs_mru_cache_destroy(mp->m_filestream);
  }
  
  /*
2a82b8be8   David Chinner   [XFS] Concurrent ...
525
526
527
528
529
530
531
532
533
534
535
   * Return the AG of the filestream the file or directory belongs to, or
   * NULLAGNUMBER otherwise.
   */
  xfs_agnumber_t
  xfs_filestream_lookup_ag(
  	xfs_inode_t	*ip)
  {
  	xfs_mru_cache_t	*cache;
  	fstrm_item_t	*item;
  	xfs_agnumber_t	ag;
  	int		ref;
03209378b   Al Viro   xfs: fix misspell...
536
  	if (!S_ISREG(ip->i_d.di_mode) && !S_ISDIR(ip->i_d.di_mode)) {
2a82b8be8   David Chinner   [XFS] Concurrent ...
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
  		ASSERT(0);
  		return NULLAGNUMBER;
  	}
  
  	cache = ip->i_mount->m_filestream;
  	item = xfs_mru_cache_lookup(cache, ip->i_ino);
  	if (!item) {
  		TRACE_LOOKUP(ip->i_mount, ip, NULL, NULLAGNUMBER, 0);
  		return NULLAGNUMBER;
  	}
  
  	ASSERT(ip == item->ip);
  	ag = item->ag;
  	ref = xfs_filestream_peek_ag(ip->i_mount, ag);
  	xfs_mru_cache_done(cache);
  
  	TRACE_LOOKUP(ip->i_mount, ip, item->pip, ag, ref);
  	return ag;
  }
  
  /*
   * xfs_filestream_associate() should only be called to associate a regular file
   * with its parent directory.  Calling it with a child directory isn't
   * appropriate because filestreams don't apply to entire directory hierarchies.
   * Creating a file in a child directory of an existing filestream directory
   * starts a new filestream with its own allocation group association.
   *
   * Returns < 0 on error, 0 if successful association occurred, > 0 if
   * we failed to get an association because of locking issues.
   */
  int
  xfs_filestream_associate(
  	xfs_inode_t	*pip,
  	xfs_inode_t	*ip)
  {
  	xfs_mount_t	*mp;
  	xfs_mru_cache_t	*cache;
  	fstrm_item_t	*item;
  	xfs_agnumber_t	ag, rotorstep, startag;
  	int		err = 0;
03209378b   Al Viro   xfs: fix misspell...
577
578
579
  	ASSERT(S_ISDIR(pip->i_d.di_mode));
  	ASSERT(S_ISREG(ip->i_d.di_mode));
  	if (!S_ISDIR(pip->i_d.di_mode) || !S_ISREG(ip->i_d.di_mode))
2a82b8be8   David Chinner   [XFS] Concurrent ...
580
581
582
583
  		return -EINVAL;
  
  	mp = pip->i_mount;
  	cache = mp->m_filestream;
2a82b8be8   David Chinner   [XFS] Concurrent ...
584
585
586
587
588
589
590
591
592
593
594
  
  	/*
  	 * We have a problem, Houston.
  	 *
  	 * Taking the iolock here violates inode locking order - we already
  	 * hold the ilock. Hence if we block getting this lock we may never
  	 * wake. Unfortunately, that means if we can't get the lock, we're
  	 * screwed in terms of getting a stream association - we can't spin
  	 * waiting for the lock because someone else is waiting on the lock we
  	 * hold and we cannot drop that as we are in a transaction here.
  	 *
075fe1028   Christoph Hellwig   xfs: split xfs_sy...
595
596
  	 * Lucky for us, this inversion is not a problem because it's a
  	 * directory inode that we are trying to lock here.
2a82b8be8   David Chinner   [XFS] Concurrent ...
597
598
599
  	 *
  	 * So, if we can't get the iolock without sleeping then just give up
  	 */
1c1c6ebcf   Dave Chinner   xfs: Replace per-...
600
  	if (!xfs_ilock_nowait(pip, XFS_IOLOCK_EXCL))
2a82b8be8   David Chinner   [XFS] Concurrent ...
601
  		return 1;
2a82b8be8   David Chinner   [XFS] Concurrent ...
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
  
  	/* If the parent directory is already in the cache, use its AG. */
  	item = xfs_mru_cache_lookup(cache, pip->i_ino);
  	if (item) {
  		ASSERT(item->ip == pip);
  		ag = item->ag;
  		xfs_mru_cache_done(cache);
  
  		TRACE_LOOKUP(mp, pip, pip, ag, xfs_filestream_peek_ag(mp, ag));
  		err = _xfs_filestream_update_ag(ip, pip, ag);
  
  		goto exit;
  	}
  
  	/*
  	 * Set the starting AG using the rotor for inode32, otherwise
  	 * use the directory inode's AG.
  	 */
  	if (mp->m_flags & XFS_MOUNT_32BITINODES) {
  		rotorstep = xfs_rotorstep;
  		startag = (mp->m_agfrotor / rotorstep) % mp->m_sb.sb_agcount;
  		mp->m_agfrotor = (mp->m_agfrotor + 1) %
  		                 (mp->m_sb.sb_agcount * rotorstep);
  	} else
  		startag = XFS_INO_TO_AGNO(mp, pip->i_ino);
  
  	/* Pick a new AG for the parent inode starting at startag. */
  	err = _xfs_filestream_pick_ag(mp, startag, &ag, 0, 0);
  	if (err || ag == NULLAGNUMBER)
  		goto exit_did_pick;
  
  	/* Associate the parent inode with the AG. */
  	err = _xfs_filestream_update_ag(pip, NULL, ag);
  	if (err)
  		goto exit_did_pick;
  
  	/* Associate the file inode with the AG. */
  	err = _xfs_filestream_update_ag(ip, pip, ag);
  	if (err)
  		goto exit_did_pick;
  
  	TRACE_ASSOCIATE(mp, ip, pip, ag, xfs_filestream_peek_ag(mp, ag));
  
  exit_did_pick:
  	/*
  	 * If _xfs_filestream_pick_ag() returned a valid AG, remove the
  	 * reference it took on it, since the file and directory will have taken
  	 * their own now if they were successfully cached.
  	 */
  	if (ag != NULLAGNUMBER)
  		xfs_filestream_put_ag(mp, ag);
  
  exit:
  	xfs_iunlock(pip, XFS_IOLOCK_EXCL);
2a82b8be8   David Chinner   [XFS] Concurrent ...
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
  	return -err;
  }
  
  /*
   * Pick a new allocation group for the current file and its file stream.  This
   * function is called by xfs_bmap_filestreams() with the mount point's per-ag
   * lock held.
   */
  int
  xfs_filestream_new_ag(
  	xfs_bmalloca_t	*ap,
  	xfs_agnumber_t	*agp)
  {
  	int		flags, err;
  	xfs_inode_t	*ip, *pip = NULL;
  	xfs_mount_t	*mp;
  	xfs_mru_cache_t	*cache;
  	xfs_extlen_t	minlen;
  	fstrm_item_t	*dir, *file;
  	xfs_agnumber_t	ag = NULLAGNUMBER;
  
  	ip = ap->ip;
  	mp = ip->i_mount;
  	cache = mp->m_filestream;
3a75667e9   Dave Chinner   xfs: rename alloc...
680
  	minlen = ap->length;
2a82b8be8   David Chinner   [XFS] Concurrent ...
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
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
729
730
731
732
733
734
735
736
737
738
739
740
741
  	*agp = NULLAGNUMBER;
  
  	/*
  	 * Look for the file in the cache, removing it if it's found.  Doing
  	 * this allows it to be held across the dir lookup that follows.
  	 */
  	file = xfs_mru_cache_remove(cache, ip->i_ino);
  	if (file) {
  		ASSERT(ip == file->ip);
  
  		/* Save the file's parent inode and old AG number for later. */
  		pip = file->pip;
  		ag = file->ag;
  
  		/* Look for the file's directory in the cache. */
  		dir = xfs_mru_cache_lookup(cache, pip->i_ino);
  		if (dir) {
  			ASSERT(pip == dir->ip);
  
  			/*
  			 * If the directory has already moved on to a new AG,
  			 * use that AG as the new AG for the file. Don't
  			 * forget to twiddle the AG refcounts to match the
  			 * movement.
  			 */
  			if (dir->ag != file->ag) {
  				xfs_filestream_put_ag(mp, file->ag);
  				xfs_filestream_get_ag(mp, dir->ag);
  				*agp = file->ag = dir->ag;
  			}
  
  			xfs_mru_cache_done(cache);
  		}
  
  		/*
  		 * Put the file back in the cache.  If this fails, the free
  		 * function needs to be called to tidy up in the same way as if
  		 * the item had simply expired from the cache.
  		 */
  		err = xfs_mru_cache_insert(cache, ip->i_ino, file);
  		if (err) {
  			xfs_fstrm_free_func(ip->i_ino, file);
  			return err;
  		}
  
  		/*
  		 * If the file's AG was moved to the directory's new AG, there's
  		 * nothing more to be done.
  		 */
  		if (*agp != NULLAGNUMBER) {
  			TRACE_MOVEAG(mp, ip, pip,
  					ag, xfs_filestream_peek_ag(mp, ag),
  					*agp, xfs_filestream_peek_ag(mp, *agp));
  			return 0;
  		}
  	}
  
  	/*
  	 * If the file's parent directory is known, take its iolock in exclusive
  	 * mode to prevent two sibling files from racing each other to migrate
  	 * themselves and their parent to different AGs.
785ce4180   Christoph Hellwig   xfs: tell lockdep...
742
743
744
745
746
747
  	 *
  	 * Note that we lock the parent directory iolock inside the child
  	 * iolock here.  That's fine as we never hold both parent and child
  	 * iolock in any other place.  This is different from the ilock,
  	 * which requires locking of the child after the parent for namespace
  	 * operations.
2a82b8be8   David Chinner   [XFS] Concurrent ...
748
749
  	 */
  	if (pip)
785ce4180   Christoph Hellwig   xfs: tell lockdep...
750
  		xfs_ilock(pip, XFS_IOLOCK_EXCL | XFS_IOLOCK_PARENT);
2a82b8be8   David Chinner   [XFS] Concurrent ...
751
752
753
754
755
756
757
758
  
  	/*
  	 * A new AG needs to be found for the file.  If the file's parent
  	 * directory is also known, it will be moved to the new AG as well to
  	 * ensure that files created inside it in future use the new AG.
  	 */
  	ag = (ag == NULLAGNUMBER) ? 0 : (ag + 1) % mp->m_sb.sb_agcount;
  	flags = (ap->userdata ? XFS_PICK_USERDATA : 0) |
0937e0fd8   Dave Chinner   xfs: move firstbl...
759
  	        (ap->flist->xbf_low ? XFS_PICK_LOWSPACE : 0);
2a82b8be8   David Chinner   [XFS] Concurrent ...
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
  
  	err = _xfs_filestream_pick_ag(mp, ag, agp, flags, minlen);
  	if (err || *agp == NULLAGNUMBER)
  		goto exit;
  
  	/*
  	 * If the file wasn't found in the file cache, then its parent directory
  	 * inode isn't known.  For this to have happened, the file must either
  	 * be pre-existing, or it was created long enough ago that its cache
  	 * entry has expired.  This isn't the sort of usage that the filestreams
  	 * allocator is trying to optimise, so there's no point trying to track
  	 * its new AG somehow in the filestream data structures.
  	 */
  	if (!pip) {
  		TRACE_ORPHAN(mp, ip, *agp);
  		goto exit;
  	}
  
  	/* Associate the parent inode with the AG. */
  	err = _xfs_filestream_update_ag(pip, NULL, *agp);
  	if (err)
  		goto exit;
  
  	/* Associate the file inode with the AG. */
  	err = _xfs_filestream_update_ag(ip, pip, *agp);
  	if (err)
  		goto exit;
  
  	TRACE_MOVEAG(mp, ip, pip, NULLAGNUMBER, 0,
  			*agp, xfs_filestream_peek_ag(mp, *agp));
  
  exit:
  	/*
  	 * If _xfs_filestream_pick_ag() returned a valid AG, remove the
  	 * reference it took on it, since the file and directory will have taken
  	 * their own now if they were successfully cached.
  	 */
  	if (*agp != NULLAGNUMBER)
  		xfs_filestream_put_ag(mp, *agp);
  	else
  		*agp = 0;
  
  	if (pip)
  		xfs_iunlock(pip, XFS_IOLOCK_EXCL);
  
  	return err;
  }
  
  /*
   * Remove an association between an inode and a filestream object.
   * Typically this is done on last close of an unlinked file.
   */
  void
  xfs_filestream_deassociate(
  	xfs_inode_t	*ip)
  {
  	xfs_mru_cache_t	*cache = ip->i_mount->m_filestream;
  
  	xfs_mru_cache_delete(cache, ip->i_ino);
  }