xfs_filestream.c 11.0 KB
Newer Older
1 2
/*
 * Copyright (c) 2006-2007 Silicon Graphics, Inc.
3
 * Copyright (c) 2014 Christoph Hellwig.
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
 * 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"
20
#include "xfs_format.h"
21 22 23 24
#include "xfs_log_format.h"
#include "xfs_trans_resv.h"
#include "xfs_sb.h"
#include "xfs_mount.h"
25 26
#include "xfs_inode.h"
#include "xfs_bmap.h"
D
Dave Chinner 已提交
27
#include "xfs_bmap_util.h"
28 29 30
#include "xfs_alloc.h"
#include "xfs_mru_cache.h"
#include "xfs_filestream.h"
C
Christoph Hellwig 已提交
31
#include "xfs_trace.h"
32

33 34 35 36 37 38 39 40 41 42
struct xfs_fstrm_item {
	struct xfs_mru_cache_elem	mru;
	struct xfs_inode		*ip;
	xfs_agnumber_t			ag; /* AG in use for this directory */
};

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

44 45
/*
 * Allocation group filestream associations are tracked with per-ag atomic
46
 * counters.  These counters allow xfs_filestream_pick_ag() to tell whether a
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
 * 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.
 */
81
int
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
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);
}
120

121 122 123 124 125 126 127 128 129
static void
xfs_fstrm_free_func(
	struct xfs_mru_cache_elem *mru)
{
	struct xfs_fstrm_item	*item =
		container_of(mru, struct xfs_fstrm_item, mru);

	xfs_filestream_put_ag(item->ip->i_mount, item->ag);

130
	trace_xfs_filestream_free(item->ip, item->ag);
131

132
	kmem_free(item);
133 134
}

135 136 137 138 139
/*
 * Scan the AGs starting at startag looking for an AG that isn't in use and has
 * at least minlen blocks free.
 */
static int
140 141 142 143 144 145
xfs_filestream_pick_ag(
	struct xfs_inode	*ip,
	xfs_agnumber_t		startag,
	xfs_agnumber_t		*agp,
	int			flags,
	xfs_extlen_t		minlen)
146
{
147 148 149
	struct xfs_mount	*mp = ip->i_mount;
	struct xfs_fstrm_item	*item;
	struct xfs_perag	*pag;
150
	xfs_extlen_t		longest, free = 0, minfree, maxfree = 0;
151 152 153 154
	xfs_agnumber_t		ag, max_ag = NULLAGNUMBER;
	int			err, trylock, nscan;

	ASSERT(S_ISDIR(ip->i_d.di_mode));
155 156 157 158 159 160 161 162 163 164 165

	/* 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++) {
166 167
		trace_xfs_filestream_scan(ip, ag);

168
		pag = xfs_perag_get(mp, ag);
169 170 171

		if (!pag->pagf_init) {
			err = xfs_alloc_pagf_init(mp, NULL, ag, trylock);
172 173
			if (err && !trylock) {
				xfs_perag_put(pag);
174
				return err;
175
			}
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
		}

		/* 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;
			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;
		}

199 200
		longest = xfs_alloc_longest_free_extent(mp, pag,
						XFS_MIN_FREELIST_PAG(pag, mp));
201 202 203 204 205 206 207
		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;
208
			xfs_perag_put(pag);
209 210 211 212 213 214 215
			*agp = ag;
			break;
		}

		/* Drop the reference on this AG, it's not usable. */
		xfs_filestream_put_ag(mp, ag);
next_ag:
216
		xfs_perag_put(pag);
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
		/* 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 */
249
		trace_xfs_filestream_pick(ip, *agp, free, nscan);
250 251 252 253
		*agp = 0;
		return 0;
	}

254
	trace_xfs_filestream_pick(ip, *agp, free, nscan);
255

256
	if (*agp == NULLAGNUMBER)
257 258
		return 0;

D
Dave Chinner 已提交
259
	err = -ENOMEM;
260
	item = kmem_alloc(sizeof(*item), KM_MAYFAIL);
261
	if (!item)
262
		goto out_put_ag;
263

264
	item->ag = *agp;
265 266
	item->ip = ip;

267
	err = xfs_mru_cache_insert(mp->m_filestream, ip->i_ino, &item->mru);
268
	if (err) {
D
Dave Chinner 已提交
269
		if (err == -EEXIST)
270 271
			err = 0;
		goto out_free_item;
272 273 274 275
	}

	return 0;

276
out_free_item:
277
	kmem_free(item);
278 279 280
out_put_ag:
	xfs_filestream_put_ag(mp, *agp);
	return err;
281 282
}

283 284 285
static struct xfs_inode *
xfs_filestream_get_parent(
	struct xfs_inode	*ip)
286
{
287 288
	struct inode		*inode = VFS_I(ip), *dir = NULL;
	struct dentry		*dentry, *parent;
289

290 291 292
	dentry = d_find_alias(inode);
	if (!dentry)
		goto out;
293

294 295 296
	parent = dget_parent(dentry);
	if (!parent)
		goto out_dput;
297

298
	dir = igrab(d_inode(parent));
299
	dput(parent);
300

301 302 303 304
out_dput:
	dput(dentry);
out:
	return dir ? XFS_I(dir) : NULL;
305 306 307
}

/*
308 309 310 311
 * 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.
312 313 314
 */
xfs_agnumber_t
xfs_filestream_lookup_ag(
315
	struct xfs_inode	*ip)
316
{
317 318
	struct xfs_mount	*mp = ip->i_mount;
	struct xfs_inode	*pip = NULL;
319
	xfs_agnumber_t		startag, ag = NULLAGNUMBER;
320
	struct xfs_mru_cache_elem *mru;
321

322
	ASSERT(S_ISREG(ip->i_d.di_mode));
323

324 325
	pip = xfs_filestream_get_parent(ip);
	if (!pip)
326
		return NULLAGNUMBER;
327 328

	mru = xfs_mru_cache_lookup(mp->m_filestream, pip->i_ino);
329
	if (mru) {
330
		ag = container_of(mru, struct xfs_fstrm_item, mru)->ag;
331
		xfs_mru_cache_done(mp->m_filestream);
332

333
		trace_xfs_filestream_lookup(ip, ag);
334
		goto out;
335 336 337 338 339 340 341
	}

	/*
	 * Set the starting AG using the rotor for inode32, otherwise
	 * use the directory inode's AG.
	 */
	if (mp->m_flags & XFS_MOUNT_32BITINODES) {
342
		xfs_agnumber_t	 rotorstep = xfs_rotorstep;
343 344 345 346 347 348
		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);

349 350 351 352 353
	if (xfs_filestream_pick_ag(pip, startag, &ag, 0, 0))
		ag = NULLAGNUMBER;
out:
	IRELE(pip);
	return ag;
354 355 356
}

/*
357 358 359 360
 * 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.
361 362 363
 */
int
xfs_filestream_new_ag(
D
Dave Chinner 已提交
364 365
	struct xfs_bmalloca	*ap,
	xfs_agnumber_t		*agp)
366
{
367 368 369 370 371 372
	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;
	int			flags, err = 0;
	struct xfs_mru_cache_elem *mru;
373

374
	*agp = NULLAGNUMBER;
375

376 377 378
	pip = xfs_filestream_get_parent(ip);
	if (!pip)
		goto exit;
379

380 381 382 383 384
	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;
385 386 387
	}

	flags = (ap->userdata ? XFS_PICK_USERDATA : 0) |
388
	        (ap->flist->xbf_low ? XFS_PICK_LOWSPACE : 0);
389

390
	err = xfs_filestream_pick_ag(pip, startag, agp, flags, minlen);
391 392

	/*
393
	 * Only free the item here so we skip over the old AG earlier.
394
	 */
395 396
	if (mru)
		xfs_fstrm_free_func(mru);
397

398 399 400 401 402 403
	IRELE(pip);
exit:
	if (*agp == NULLAGNUMBER)
		*agp = 0;
	return err;
}
404

405 406 407 408 409 410
void
xfs_filestream_deassociate(
	struct xfs_inode	*ip)
{
	xfs_mru_cache_delete(ip->i_mount->m_filestream, ip->i_ino);
}
411

412 413 414 415
int
xfs_filestream_mount(
	xfs_mount_t	*mp)
{
416
	/*
417 418 419 420 421
	 * 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.
422
	 */
423 424 425
	return xfs_mru_cache_create(&mp->m_filestream, xfs_fstrm_centisecs * 10,
				    10, xfs_fstrm_free_func);
}
426

427 428 429 430 431 432
void
xfs_filestream_unmount(
	xfs_mount_t	*mp)
{
	xfs_mru_cache_destroy(mp->m_filestream);
}