xfs_filestream.c 10.6 KB
Newer Older
D
Dave Chinner 已提交
1
// SPDX-License-Identifier: GPL-2.0
2 3
/*
 * Copyright (c) 2006-2007 Silicon Graphics, Inc.
4
 * Copyright (c) 2014 Christoph Hellwig.
5 6 7
 * All Rights Reserved.
 */
#include "xfs.h"
8
#include "xfs_shared.h"
9
#include "xfs_format.h"
10 11 12 13
#include "xfs_log_format.h"
#include "xfs_trans_resv.h"
#include "xfs_sb.h"
#include "xfs_mount.h"
14 15 16 17
#include "xfs_inode.h"
#include "xfs_bmap.h"
#include "xfs_alloc.h"
#include "xfs_mru_cache.h"
C
Christoph Hellwig 已提交
18
#include "xfs_trace.h"
19
#include "xfs_ag_resv.h"
20
#include "xfs_trans.h"
D
Darrick J. Wong 已提交
21
#include "xfs_filestream.h"
22

23 24 25 26 27 28 29 30 31
struct xfs_fstrm_item {
	struct xfs_mru_cache_elem	mru;
	xfs_agnumber_t			ag; /* AG in use for this directory */
};

enum xfs_fstrm_alloc {
	XFS_PICK_USERDATA = 1,
	XFS_PICK_LOWSPACE = 2,
};
32

33 34
/*
 * Allocation group filestream associations are tracked with per-ag atomic
35
 * counters.  These counters allow xfs_filestream_pick_ag() to tell whether a
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
 * 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.
 */
70
int
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
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);
}
109

110 111
static void
xfs_fstrm_free_func(
112
	void			*data,
113 114
	struct xfs_mru_cache_elem *mru)
{
115
	struct xfs_mount	*mp = data;
116 117 118
	struct xfs_fstrm_item	*item =
		container_of(mru, struct xfs_fstrm_item, mru);

119 120
	xfs_filestream_put_ag(mp, item->ag);
	trace_xfs_filestream_free(mp, mru->key, item->ag);
121

122
	kmem_free(item);
123 124
}

125 126 127 128 129
/*
 * Scan the AGs starting at startag looking for an AG that isn't in use and has
 * at least minlen blocks free.
 */
static int
130 131 132 133 134 135
xfs_filestream_pick_ag(
	struct xfs_inode	*ip,
	xfs_agnumber_t		startag,
	xfs_agnumber_t		*agp,
	int			flags,
	xfs_extlen_t		minlen)
136
{
137 138 139
	struct xfs_mount	*mp = ip->i_mount;
	struct xfs_fstrm_item	*item;
	struct xfs_perag	*pag;
140
	xfs_extlen_t		longest, free = 0, minfree, maxfree = 0;
141 142 143
	xfs_agnumber_t		ag, max_ag = NULLAGNUMBER;
	int			err, trylock, nscan;

D
Dave Chinner 已提交
144
	ASSERT(S_ISDIR(VFS_I(ip)->i_mode));
145 146 147 148 149 150 151 152 153 154 155

	/* 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++) {
156
		trace_xfs_filestream_scan(mp, ip->i_ino, ag);
157

158
		pag = xfs_perag_get(mp, ag);
159 160 161

		if (!pag->pagf_init) {
			err = xfs_alloc_pagf_init(mp, NULL, ag, trylock);
162
			if (err) {
163
				xfs_perag_put(pag);
164 165 166 167
				if (err != -EAGAIN)
					return err;
				/* Couldn't lock the AGF, skip this AG. */
				continue;
168
			}
169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
		}

		/* Keep track of the AG with the most free blocks. */
		if (pag->pagf_freeblks > maxfree) {
			maxfree = pag->pagf_freeblks;
			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;
		}

188
		longest = xfs_alloc_longest_free_extent(pag,
189 190
				xfs_alloc_min_freelist(mp, pag),
				xfs_ag_resv_needed(pag, XFS_AG_RESV_NONE));
191 192 193 194 195 196 197
		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;
198
			xfs_perag_put(pag);
199 200 201 202 203 204 205
			*agp = ag;
			break;
		}

		/* Drop the reference on this AG, it's not usable. */
		xfs_filestream_put_ag(mp, ag);
next_ag:
206
		xfs_perag_put(pag);
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
		/* 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);
			free = maxfree;
			*agp = max_ag;
			break;
		}

		/* take AG 0 if none matched */
239
		trace_xfs_filestream_pick(ip, *agp, free, nscan);
240 241 242 243
		*agp = 0;
		return 0;
	}

244
	trace_xfs_filestream_pick(ip, *agp, free, nscan);
245

246
	if (*agp == NULLAGNUMBER)
247 248
		return 0;

D
Dave Chinner 已提交
249
	err = -ENOMEM;
250
	item = kmem_alloc(sizeof(*item), KM_MAYFAIL);
251
	if (!item)
252
		goto out_put_ag;
253

254
	item->ag = *agp;
255

256
	err = xfs_mru_cache_insert(mp->m_filestream, ip->i_ino, &item->mru);
257
	if (err) {
D
Dave Chinner 已提交
258
		if (err == -EEXIST)
259 260
			err = 0;
		goto out_free_item;
261 262 263 264
	}

	return 0;

265
out_free_item:
266
	kmem_free(item);
267 268 269
out_put_ag:
	xfs_filestream_put_ag(mp, *agp);
	return err;
270 271
}

272 273 274
static struct xfs_inode *
xfs_filestream_get_parent(
	struct xfs_inode	*ip)
275
{
276 277
	struct inode		*inode = VFS_I(ip), *dir = NULL;
	struct dentry		*dentry, *parent;
278

279 280 281
	dentry = d_find_alias(inode);
	if (!dentry)
		goto out;
282

283 284 285
	parent = dget_parent(dentry);
	if (!parent)
		goto out_dput;
286

287
	dir = igrab(d_inode(parent));
288
	dput(parent);
289

290 291 292 293
out_dput:
	dput(dentry);
out:
	return dir ? XFS_I(dir) : NULL;
294 295 296
}

/*
297 298 299 300
 * Find the right allocation group for a file, either by finding an
 * existing file stream or creating a new one.
 *
 * Returns NULLAGNUMBER in case of an error.
301 302 303
 */
xfs_agnumber_t
xfs_filestream_lookup_ag(
304
	struct xfs_inode	*ip)
305
{
306 307
	struct xfs_mount	*mp = ip->i_mount;
	struct xfs_inode	*pip = NULL;
308
	xfs_agnumber_t		startag, ag = NULLAGNUMBER;
309
	struct xfs_mru_cache_elem *mru;
310

D
Dave Chinner 已提交
311
	ASSERT(S_ISREG(VFS_I(ip)->i_mode));
312

313 314
	pip = xfs_filestream_get_parent(ip);
	if (!pip)
315
		return NULLAGNUMBER;
316 317

	mru = xfs_mru_cache_lookup(mp->m_filestream, pip->i_ino);
318
	if (mru) {
319
		ag = container_of(mru, struct xfs_fstrm_item, mru)->ag;
320
		xfs_mru_cache_done(mp->m_filestream);
321

322
		trace_xfs_filestream_lookup(mp, ip->i_ino, ag);
323
		goto out;
324 325 326 327 328 329 330
	}

	/*
	 * Set the starting AG using the rotor for inode32, otherwise
	 * use the directory inode's AG.
	 */
	if (mp->m_flags & XFS_MOUNT_32BITINODES) {
331
		xfs_agnumber_t	 rotorstep = xfs_rotorstep;
332 333 334 335 336 337
		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);

338 339 340
	if (xfs_filestream_pick_ag(pip, startag, &ag, 0, 0))
		ag = NULLAGNUMBER;
out:
341
	xfs_irele(pip);
342
	return ag;
343 344 345
}

/*
346 347 348 349
 * Pick a new allocation group for the current file and its file stream.
 *
 * This is called when the allocator can't find a suitable extent in the
 * current AG, and we have to move the stream into a new AG with more space.
350 351 352
 */
int
xfs_filestream_new_ag(
D
Dave Chinner 已提交
353 354
	struct xfs_bmalloca	*ap,
	xfs_agnumber_t		*agp)
355
{
356 357 358 359
	struct xfs_inode	*ip = ap->ip, *pip;
	struct xfs_mount	*mp = ip->i_mount;
	xfs_extlen_t		minlen = ap->length;
	xfs_agnumber_t		startag = 0;
360 361
	int			flags = 0;
	int			err = 0;
362
	struct xfs_mru_cache_elem *mru;
363

364
	*agp = NULLAGNUMBER;
365

366 367 368
	pip = xfs_filestream_get_parent(ip);
	if (!pip)
		goto exit;
369

370 371 372 373 374
	mru = xfs_mru_cache_remove(mp->m_filestream, pip->i_ino);
	if (mru) {
		struct xfs_fstrm_item *item =
			container_of(mru, struct xfs_fstrm_item, mru);
		startag = (item->ag + 1) % mp->m_sb.sb_agcount;
375 376
	}

377
	if (ap->datatype & XFS_ALLOC_USERDATA)
378
		flags |= XFS_PICK_USERDATA;
379
	if (ap->tp->t_flags & XFS_TRANS_LOWMODE)
380
		flags |= XFS_PICK_LOWSPACE;
381

382
	err = xfs_filestream_pick_ag(pip, startag, agp, flags, minlen);
383 384

	/*
385
	 * Only free the item here so we skip over the old AG earlier.
386
	 */
387
	if (mru)
388
		xfs_fstrm_free_func(mp, mru);
389

390
	xfs_irele(pip);
391 392 393 394 395
exit:
	if (*agp == NULLAGNUMBER)
		*agp = 0;
	return err;
}
396

397 398 399 400 401 402
void
xfs_filestream_deassociate(
	struct xfs_inode	*ip)
{
	xfs_mru_cache_delete(ip->i_mount->m_filestream, ip->i_ino);
}
403

404 405 406 407
int
xfs_filestream_mount(
	xfs_mount_t	*mp)
{
408
	/*
409 410 411 412 413
	 * 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.
414
	 */
415 416
	return xfs_mru_cache_create(&mp->m_filestream, mp,
			xfs_fstrm_centisecs * 10, 10, xfs_fstrm_free_func);
417
}
418

419 420 421 422 423 424
void
xfs_filestream_unmount(
	xfs_mount_t	*mp)
{
	xfs_mru_cache_destroy(mp->m_filestream);
}