| David Chinner | 2a82b8b | 2007-07-11 11:09:12 +1000 | [diff] [blame] | 1 | /* | 
|  | 2 | * Copyright (c) 2006-2007 Silicon Graphics, Inc. | 
|  | 3 | * All Rights Reserved. | 
|  | 4 | * | 
|  | 5 | * This program is free software; you can redistribute it and/or | 
|  | 6 | * modify it under the terms of the GNU General Public License as | 
|  | 7 | * published by the Free Software Foundation. | 
|  | 8 | * | 
|  | 9 | * This program is distributed in the hope that it would be useful, | 
|  | 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|  | 12 | * GNU General Public License for more details. | 
|  | 13 | * | 
|  | 14 | * You should have received a copy of the GNU General Public License | 
|  | 15 | * along with this program; if not, write the Free Software Foundation, | 
|  | 16 | * Inc.,  51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA | 
|  | 17 | */ | 
|  | 18 | #include "xfs.h" | 
|  | 19 | #include "xfs_bmap_btree.h" | 
|  | 20 | #include "xfs_inum.h" | 
|  | 21 | #include "xfs_dir2.h" | 
|  | 22 | #include "xfs_dir2_sf.h" | 
|  | 23 | #include "xfs_attr_sf.h" | 
|  | 24 | #include "xfs_dinode.h" | 
|  | 25 | #include "xfs_inode.h" | 
|  | 26 | #include "xfs_ag.h" | 
|  | 27 | #include "xfs_dmapi.h" | 
|  | 28 | #include "xfs_log.h" | 
|  | 29 | #include "xfs_trans.h" | 
|  | 30 | #include "xfs_sb.h" | 
|  | 31 | #include "xfs_mount.h" | 
|  | 32 | #include "xfs_bmap.h" | 
|  | 33 | #include "xfs_alloc.h" | 
|  | 34 | #include "xfs_utils.h" | 
|  | 35 | #include "xfs_mru_cache.h" | 
|  | 36 | #include "xfs_filestream.h" | 
|  | 37 |  | 
|  | 38 | #ifdef XFS_FILESTREAMS_TRACE | 
|  | 39 |  | 
|  | 40 | ktrace_t *xfs_filestreams_trace_buf; | 
|  | 41 |  | 
|  | 42 | STATIC void | 
|  | 43 | xfs_filestreams_trace( | 
|  | 44 | xfs_mount_t	*mp,	/* mount point */ | 
|  | 45 | int		type,	/* type of trace */ | 
|  | 46 | const char	*func,	/* source function */ | 
|  | 47 | int		line,	/* source line number */ | 
|  | 48 | __psunsigned_t	arg0, | 
|  | 49 | __psunsigned_t	arg1, | 
|  | 50 | __psunsigned_t	arg2, | 
|  | 51 | __psunsigned_t	arg3, | 
|  | 52 | __psunsigned_t	arg4, | 
|  | 53 | __psunsigned_t	arg5) | 
|  | 54 | { | 
|  | 55 | ktrace_enter(xfs_filestreams_trace_buf, | 
|  | 56 | (void *)(__psint_t)(type | (line << 16)), | 
|  | 57 | (void *)func, | 
|  | 58 | (void *)(__psunsigned_t)current_pid(), | 
|  | 59 | (void *)mp, | 
|  | 60 | (void *)(__psunsigned_t)arg0, | 
|  | 61 | (void *)(__psunsigned_t)arg1, | 
|  | 62 | (void *)(__psunsigned_t)arg2, | 
|  | 63 | (void *)(__psunsigned_t)arg3, | 
|  | 64 | (void *)(__psunsigned_t)arg4, | 
|  | 65 | (void *)(__psunsigned_t)arg5, | 
|  | 66 | NULL, NULL, NULL, NULL, NULL, NULL); | 
|  | 67 | } | 
|  | 68 |  | 
|  | 69 | #define TRACE0(mp,t)			TRACE6(mp,t,0,0,0,0,0,0) | 
|  | 70 | #define TRACE1(mp,t,a0)			TRACE6(mp,t,a0,0,0,0,0,0) | 
|  | 71 | #define TRACE2(mp,t,a0,a1)		TRACE6(mp,t,a0,a1,0,0,0,0) | 
|  | 72 | #define TRACE3(mp,t,a0,a1,a2)		TRACE6(mp,t,a0,a1,a2,0,0,0) | 
|  | 73 | #define TRACE4(mp,t,a0,a1,a2,a3)	TRACE6(mp,t,a0,a1,a2,a3,0,0) | 
|  | 74 | #define TRACE5(mp,t,a0,a1,a2,a3,a4)	TRACE6(mp,t,a0,a1,a2,a3,a4,0) | 
|  | 75 | #define TRACE6(mp,t,a0,a1,a2,a3,a4,a5) \ | 
|  | 76 | xfs_filestreams_trace(mp, t, __FUNCTION__, __LINE__, \ | 
|  | 77 | (__psunsigned_t)a0, (__psunsigned_t)a1, \ | 
|  | 78 | (__psunsigned_t)a2, (__psunsigned_t)a3, \ | 
|  | 79 | (__psunsigned_t)a4, (__psunsigned_t)a5) | 
|  | 80 |  | 
|  | 81 | #define TRACE_AG_SCAN(mp, ag, ag2) \ | 
|  | 82 | TRACE2(mp, XFS_FSTRM_KTRACE_AGSCAN, ag, ag2); | 
|  | 83 | #define TRACE_AG_PICK1(mp, max_ag, maxfree) \ | 
|  | 84 | TRACE2(mp, XFS_FSTRM_KTRACE_AGPICK1, max_ag, maxfree); | 
|  | 85 | #define TRACE_AG_PICK2(mp, ag, ag2, cnt, free, scan, flag) \ | 
|  | 86 | TRACE6(mp, XFS_FSTRM_KTRACE_AGPICK2, ag, ag2, \ | 
|  | 87 | cnt, free, scan, flag) | 
|  | 88 | #define TRACE_UPDATE(mp, ip, ag, cnt, ag2, cnt2) \ | 
|  | 89 | TRACE5(mp, XFS_FSTRM_KTRACE_UPDATE, ip, ag, cnt, ag2, cnt2) | 
|  | 90 | #define TRACE_FREE(mp, ip, pip, ag, cnt) \ | 
|  | 91 | TRACE4(mp, XFS_FSTRM_KTRACE_FREE, ip, pip, ag, cnt) | 
|  | 92 | #define TRACE_LOOKUP(mp, ip, pip, ag, cnt) \ | 
|  | 93 | TRACE4(mp, XFS_FSTRM_KTRACE_ITEM_LOOKUP, ip, pip, ag, cnt) | 
|  | 94 | #define TRACE_ASSOCIATE(mp, ip, pip, ag, cnt) \ | 
|  | 95 | TRACE4(mp, XFS_FSTRM_KTRACE_ASSOCIATE, ip, pip, ag, cnt) | 
|  | 96 | #define TRACE_MOVEAG(mp, ip, pip, oag, ocnt, nag, ncnt) \ | 
|  | 97 | TRACE6(mp, XFS_FSTRM_KTRACE_MOVEAG, ip, pip, oag, ocnt, nag, ncnt) | 
|  | 98 | #define TRACE_ORPHAN(mp, ip, ag) \ | 
|  | 99 | TRACE2(mp, XFS_FSTRM_KTRACE_ORPHAN, ip, ag); | 
|  | 100 |  | 
|  | 101 |  | 
|  | 102 | #else | 
|  | 103 | #define TRACE_AG_SCAN(mp, ag, ag2) | 
|  | 104 | #define TRACE_AG_PICK1(mp, max_ag, maxfree) | 
|  | 105 | #define TRACE_AG_PICK2(mp, ag, ag2, cnt, free, scan, flag) | 
|  | 106 | #define TRACE_UPDATE(mp, ip, ag, cnt, ag2, cnt2) | 
|  | 107 | #define TRACE_FREE(mp, ip, pip, ag, cnt) | 
|  | 108 | #define TRACE_LOOKUP(mp, ip, pip, ag, cnt) | 
|  | 109 | #define TRACE_ASSOCIATE(mp, ip, pip, ag, cnt) | 
|  | 110 | #define TRACE_MOVEAG(mp, ip, pip, oag, ocnt, nag, ncnt) | 
|  | 111 | #define TRACE_ORPHAN(mp, ip, ag) | 
|  | 112 | #endif | 
|  | 113 |  | 
|  | 114 | static kmem_zone_t *item_zone; | 
|  | 115 |  | 
|  | 116 | /* | 
|  | 117 | * Structure for associating a file or a directory with an allocation group. | 
|  | 118 | * The parent directory pointer is only needed for files, but since there will | 
|  | 119 | * generally be vastly more files than directories in the cache, using the same | 
|  | 120 | * data structure simplifies the code with very little memory overhead. | 
|  | 121 | */ | 
|  | 122 | typedef struct fstrm_item | 
|  | 123 | { | 
|  | 124 | xfs_agnumber_t	ag;	/* AG currently in use for the file/directory. */ | 
|  | 125 | xfs_inode_t	*ip;	/* inode self-pointer. */ | 
|  | 126 | xfs_inode_t	*pip;	/* Parent directory inode pointer. */ | 
|  | 127 | } fstrm_item_t; | 
|  | 128 |  | 
|  | 129 |  | 
|  | 130 | /* | 
|  | 131 | * Scan the AGs starting at startag looking for an AG that isn't in use and has | 
|  | 132 | * at least minlen blocks free. | 
|  | 133 | */ | 
|  | 134 | static int | 
|  | 135 | _xfs_filestream_pick_ag( | 
|  | 136 | xfs_mount_t	*mp, | 
|  | 137 | xfs_agnumber_t	startag, | 
|  | 138 | xfs_agnumber_t	*agp, | 
|  | 139 | int		flags, | 
|  | 140 | xfs_extlen_t	minlen) | 
|  | 141 | { | 
|  | 142 | int		err, trylock, nscan; | 
|  | 143 | xfs_extlen_t	delta, longest, need, free, minfree, maxfree = 0; | 
|  | 144 | xfs_agnumber_t	ag, max_ag = NULLAGNUMBER; | 
|  | 145 | struct xfs_perag *pag; | 
|  | 146 |  | 
|  | 147 | /* 2% of an AG's blocks must be free for it to be chosen. */ | 
|  | 148 | minfree = mp->m_sb.sb_agblocks / 50; | 
|  | 149 |  | 
|  | 150 | ag = startag; | 
|  | 151 | *agp = NULLAGNUMBER; | 
|  | 152 |  | 
|  | 153 | /* For the first pass, don't sleep trying to init the per-AG. */ | 
|  | 154 | trylock = XFS_ALLOC_FLAG_TRYLOCK; | 
|  | 155 |  | 
|  | 156 | for (nscan = 0; 1; nscan++) { | 
|  | 157 |  | 
|  | 158 | TRACE_AG_SCAN(mp, ag, xfs_filestream_peek_ag(mp, ag)); | 
|  | 159 |  | 
|  | 160 | pag = mp->m_perag + ag; | 
|  | 161 |  | 
|  | 162 | if (!pag->pagf_init) { | 
|  | 163 | err = xfs_alloc_pagf_init(mp, NULL, ag, trylock); | 
|  | 164 | if (err && !trylock) | 
|  | 165 | return err; | 
|  | 166 | } | 
|  | 167 |  | 
|  | 168 | /* Might fail sometimes during the 1st pass with trylock set. */ | 
|  | 169 | if (!pag->pagf_init) | 
|  | 170 | goto next_ag; | 
|  | 171 |  | 
|  | 172 | /* Keep track of the AG with the most free blocks. */ | 
|  | 173 | if (pag->pagf_freeblks > maxfree) { | 
|  | 174 | maxfree = pag->pagf_freeblks; | 
|  | 175 | max_ag = ag; | 
|  | 176 | } | 
|  | 177 |  | 
|  | 178 | /* | 
|  | 179 | * The AG reference count does two things: it enforces mutual | 
|  | 180 | * exclusion when examining the suitability of an AG in this | 
|  | 181 | * loop, and it guards against two filestreams being established | 
|  | 182 | * in the same AG as each other. | 
|  | 183 | */ | 
|  | 184 | if (xfs_filestream_get_ag(mp, ag) > 1) { | 
|  | 185 | xfs_filestream_put_ag(mp, ag); | 
|  | 186 | goto next_ag; | 
|  | 187 | } | 
|  | 188 |  | 
|  | 189 | need = XFS_MIN_FREELIST_PAG(pag, mp); | 
|  | 190 | delta = need > pag->pagf_flcount ? need - pag->pagf_flcount : 0; | 
|  | 191 | longest = (pag->pagf_longest > delta) ? | 
|  | 192 | (pag->pagf_longest - delta) : | 
|  | 193 | (pag->pagf_flcount > 0 || pag->pagf_longest > 0); | 
|  | 194 |  | 
|  | 195 | if (((minlen && longest >= minlen) || | 
|  | 196 | (!minlen && pag->pagf_freeblks >= minfree)) && | 
|  | 197 | (!pag->pagf_metadata || !(flags & XFS_PICK_USERDATA) || | 
|  | 198 | (flags & XFS_PICK_LOWSPACE))) { | 
|  | 199 |  | 
|  | 200 | /* Break out, retaining the reference on the AG. */ | 
|  | 201 | free = pag->pagf_freeblks; | 
|  | 202 | *agp = ag; | 
|  | 203 | break; | 
|  | 204 | } | 
|  | 205 |  | 
|  | 206 | /* Drop the reference on this AG, it's not usable. */ | 
|  | 207 | xfs_filestream_put_ag(mp, ag); | 
|  | 208 | next_ag: | 
|  | 209 | /* Move to the next AG, wrapping to AG 0 if necessary. */ | 
|  | 210 | if (++ag >= mp->m_sb.sb_agcount) | 
|  | 211 | ag = 0; | 
|  | 212 |  | 
|  | 213 | /* If a full pass of the AGs hasn't been done yet, continue. */ | 
|  | 214 | if (ag != startag) | 
|  | 215 | continue; | 
|  | 216 |  | 
|  | 217 | /* Allow sleeping in xfs_alloc_pagf_init() on the 2nd pass. */ | 
|  | 218 | if (trylock != 0) { | 
|  | 219 | trylock = 0; | 
|  | 220 | continue; | 
|  | 221 | } | 
|  | 222 |  | 
|  | 223 | /* Finally, if lowspace wasn't set, set it for the 3rd pass. */ | 
|  | 224 | if (!(flags & XFS_PICK_LOWSPACE)) { | 
|  | 225 | flags |= XFS_PICK_LOWSPACE; | 
|  | 226 | continue; | 
|  | 227 | } | 
|  | 228 |  | 
|  | 229 | /* | 
|  | 230 | * Take the AG with the most free space, regardless of whether | 
|  | 231 | * it's already in use by another filestream. | 
|  | 232 | */ | 
|  | 233 | if (max_ag != NULLAGNUMBER) { | 
|  | 234 | xfs_filestream_get_ag(mp, max_ag); | 
|  | 235 | TRACE_AG_PICK1(mp, max_ag, maxfree); | 
|  | 236 | free = maxfree; | 
|  | 237 | *agp = max_ag; | 
|  | 238 | break; | 
|  | 239 | } | 
|  | 240 |  | 
|  | 241 | /* take AG 0 if none matched */ | 
|  | 242 | TRACE_AG_PICK1(mp, max_ag, maxfree); | 
|  | 243 | *agp = 0; | 
|  | 244 | return 0; | 
|  | 245 | } | 
|  | 246 |  | 
|  | 247 | TRACE_AG_PICK2(mp, startag, *agp, xfs_filestream_peek_ag(mp, *agp), | 
|  | 248 | free, nscan, flags); | 
|  | 249 |  | 
|  | 250 | return 0; | 
|  | 251 | } | 
|  | 252 |  | 
|  | 253 | /* | 
|  | 254 | * Set the allocation group number for a file or a directory, updating inode | 
|  | 255 | * references and per-AG references as appropriate.  Must be called with the | 
|  | 256 | * m_peraglock held in read mode. | 
|  | 257 | */ | 
|  | 258 | static int | 
|  | 259 | _xfs_filestream_update_ag( | 
|  | 260 | xfs_inode_t	*ip, | 
|  | 261 | xfs_inode_t	*pip, | 
|  | 262 | xfs_agnumber_t	ag) | 
|  | 263 | { | 
|  | 264 | int		err = 0; | 
|  | 265 | xfs_mount_t	*mp; | 
|  | 266 | xfs_mru_cache_t	*cache; | 
|  | 267 | fstrm_item_t	*item; | 
|  | 268 | xfs_agnumber_t	old_ag; | 
|  | 269 | xfs_inode_t	*old_pip; | 
|  | 270 |  | 
|  | 271 | /* | 
|  | 272 | * Either ip is a regular file and pip is a directory, or ip is a | 
|  | 273 | * directory and pip is NULL. | 
|  | 274 | */ | 
|  | 275 | ASSERT(ip && (((ip->i_d.di_mode & S_IFREG) && pip && | 
|  | 276 | (pip->i_d.di_mode & S_IFDIR)) || | 
|  | 277 | ((ip->i_d.di_mode & S_IFDIR) && !pip))); | 
|  | 278 |  | 
|  | 279 | mp = ip->i_mount; | 
|  | 280 | cache = mp->m_filestream; | 
|  | 281 |  | 
|  | 282 | item = xfs_mru_cache_lookup(cache, ip->i_ino); | 
|  | 283 | if (item) { | 
|  | 284 | ASSERT(item->ip == ip); | 
|  | 285 | old_ag = item->ag; | 
|  | 286 | item->ag = ag; | 
|  | 287 | old_pip = item->pip; | 
|  | 288 | item->pip = pip; | 
|  | 289 | xfs_mru_cache_done(cache); | 
|  | 290 |  | 
|  | 291 | /* | 
|  | 292 | * If the AG has changed, drop the old ref and take a new one, | 
|  | 293 | * effectively transferring the reference from old to new AG. | 
|  | 294 | */ | 
|  | 295 | if (ag != old_ag) { | 
|  | 296 | xfs_filestream_put_ag(mp, old_ag); | 
|  | 297 | xfs_filestream_get_ag(mp, ag); | 
|  | 298 | } | 
|  | 299 |  | 
|  | 300 | /* | 
|  | 301 | * If ip is a file and its pip has changed, drop the old ref and | 
|  | 302 | * take a new one. | 
|  | 303 | */ | 
|  | 304 | if (pip && pip != old_pip) { | 
|  | 305 | IRELE(old_pip); | 
|  | 306 | IHOLD(pip); | 
|  | 307 | } | 
|  | 308 |  | 
|  | 309 | TRACE_UPDATE(mp, ip, old_ag, xfs_filestream_peek_ag(mp, old_ag), | 
|  | 310 | ag, xfs_filestream_peek_ag(mp, ag)); | 
|  | 311 | return 0; | 
|  | 312 | } | 
|  | 313 |  | 
|  | 314 | item = kmem_zone_zalloc(item_zone, KM_MAYFAIL); | 
|  | 315 | if (!item) | 
|  | 316 | return ENOMEM; | 
|  | 317 |  | 
|  | 318 | item->ag = ag; | 
|  | 319 | item->ip = ip; | 
|  | 320 | item->pip = pip; | 
|  | 321 |  | 
|  | 322 | err = xfs_mru_cache_insert(cache, ip->i_ino, item); | 
|  | 323 | if (err) { | 
|  | 324 | kmem_zone_free(item_zone, item); | 
|  | 325 | return err; | 
|  | 326 | } | 
|  | 327 |  | 
|  | 328 | /* Take a reference on the AG. */ | 
|  | 329 | xfs_filestream_get_ag(mp, ag); | 
|  | 330 |  | 
|  | 331 | /* | 
|  | 332 | * Take a reference on the inode itself regardless of whether it's a | 
|  | 333 | * regular file or a directory. | 
|  | 334 | */ | 
|  | 335 | IHOLD(ip); | 
|  | 336 |  | 
|  | 337 | /* | 
|  | 338 | * In the case of a regular file, take a reference on the parent inode | 
|  | 339 | * as well to ensure it remains in-core. | 
|  | 340 | */ | 
|  | 341 | if (pip) | 
|  | 342 | IHOLD(pip); | 
|  | 343 |  | 
|  | 344 | TRACE_UPDATE(mp, ip, ag, xfs_filestream_peek_ag(mp, ag), | 
|  | 345 | ag, xfs_filestream_peek_ag(mp, ag)); | 
|  | 346 |  | 
|  | 347 | return 0; | 
|  | 348 | } | 
|  | 349 |  | 
|  | 350 | /* xfs_fstrm_free_func(): callback for freeing cached stream items. */ | 
|  | 351 | void | 
|  | 352 | xfs_fstrm_free_func( | 
| Eric Sandeen | bcc7b44 | 2007-08-30 17:21:38 +1000 | [diff] [blame] | 353 | unsigned long	ino, | 
|  | 354 | void		*data) | 
| David Chinner | 2a82b8b | 2007-07-11 11:09:12 +1000 | [diff] [blame] | 355 | { | 
| Eric Sandeen | bcc7b44 | 2007-08-30 17:21:38 +1000 | [diff] [blame] | 356 | fstrm_item_t	*item  = (fstrm_item_t *)data; | 
| David Chinner | 2a82b8b | 2007-07-11 11:09:12 +1000 | [diff] [blame] | 357 | xfs_inode_t	*ip = item->ip; | 
|  | 358 | int ref; | 
|  | 359 |  | 
|  | 360 | ASSERT(ip->i_ino == ino); | 
|  | 361 |  | 
|  | 362 | xfs_iflags_clear(ip, XFS_IFILESTREAM); | 
|  | 363 |  | 
|  | 364 | /* Drop the reference taken on the AG when the item was added. */ | 
|  | 365 | ref = xfs_filestream_put_ag(ip->i_mount, item->ag); | 
|  | 366 |  | 
|  | 367 | ASSERT(ref >= 0); | 
|  | 368 | TRACE_FREE(ip->i_mount, ip, item->pip, item->ag, | 
|  | 369 | xfs_filestream_peek_ag(ip->i_mount, item->ag)); | 
|  | 370 |  | 
|  | 371 | /* | 
|  | 372 | * _xfs_filestream_update_ag() always takes a reference on the inode | 
|  | 373 | * itself, whether it's a file or a directory.  Release it here. | 
|  | 374 | * This can result in the inode being freed and so we must | 
|  | 375 | * not hold any inode locks when freeing filesstreams objects | 
|  | 376 | * otherwise we can deadlock here. | 
|  | 377 | */ | 
|  | 378 | IRELE(ip); | 
|  | 379 |  | 
|  | 380 | /* | 
|  | 381 | * In the case of a regular file, _xfs_filestream_update_ag() also | 
|  | 382 | * takes a ref on the parent inode to keep it in-core.  Release that | 
|  | 383 | * too. | 
|  | 384 | */ | 
|  | 385 | if (item->pip) | 
|  | 386 | IRELE(item->pip); | 
|  | 387 |  | 
|  | 388 | /* Finally, free the memory allocated for the item. */ | 
|  | 389 | kmem_zone_free(item_zone, item); | 
|  | 390 | } | 
|  | 391 |  | 
|  | 392 | /* | 
|  | 393 | * xfs_filestream_init() is called at xfs initialisation time to set up the | 
|  | 394 | * memory zone that will be used for filestream data structure allocation. | 
|  | 395 | */ | 
|  | 396 | int | 
|  | 397 | xfs_filestream_init(void) | 
|  | 398 | { | 
|  | 399 | item_zone = kmem_zone_init(sizeof(fstrm_item_t), "fstrm_item"); | 
|  | 400 | #ifdef XFS_FILESTREAMS_TRACE | 
|  | 401 | xfs_filestreams_trace_buf = ktrace_alloc(XFS_FSTRM_KTRACE_SIZE, KM_SLEEP); | 
|  | 402 | #endif | 
|  | 403 | return item_zone ? 0 : -ENOMEM; | 
|  | 404 | } | 
|  | 405 |  | 
|  | 406 | /* | 
|  | 407 | * xfs_filestream_uninit() is called at xfs termination time to destroy the | 
|  | 408 | * memory zone that was used for filestream data structure allocation. | 
|  | 409 | */ | 
|  | 410 | void | 
|  | 411 | xfs_filestream_uninit(void) | 
|  | 412 | { | 
|  | 413 | #ifdef XFS_FILESTREAMS_TRACE | 
|  | 414 | ktrace_free(xfs_filestreams_trace_buf); | 
|  | 415 | #endif | 
|  | 416 | kmem_zone_destroy(item_zone); | 
|  | 417 | } | 
|  | 418 |  | 
|  | 419 | /* | 
|  | 420 | * xfs_filestream_mount() is called when a file system is mounted with the | 
|  | 421 | * filestream option.  It is responsible for allocating the data structures | 
|  | 422 | * needed to track the new file system's file streams. | 
|  | 423 | */ | 
|  | 424 | int | 
|  | 425 | xfs_filestream_mount( | 
|  | 426 | xfs_mount_t	*mp) | 
|  | 427 | { | 
|  | 428 | int		err; | 
|  | 429 | unsigned int	lifetime, grp_count; | 
|  | 430 |  | 
|  | 431 | /* | 
|  | 432 | * The filestream timer tunable is currently fixed within the range of | 
|  | 433 | * one second to four minutes, with five seconds being the default.  The | 
|  | 434 | * group count is somewhat arbitrary, but it'd be nice to adhere to the | 
|  | 435 | * timer tunable to within about 10 percent.  This requires at least 10 | 
|  | 436 | * groups. | 
|  | 437 | */ | 
|  | 438 | lifetime  = xfs_fstrm_centisecs * 10; | 
|  | 439 | grp_count = 10; | 
|  | 440 |  | 
|  | 441 | err = xfs_mru_cache_create(&mp->m_filestream, lifetime, grp_count, | 
| Eric Sandeen | bcc7b44 | 2007-08-30 17:21:38 +1000 | [diff] [blame] | 442 | xfs_fstrm_free_func); | 
| David Chinner | 2a82b8b | 2007-07-11 11:09:12 +1000 | [diff] [blame] | 443 |  | 
|  | 444 | return err; | 
|  | 445 | } | 
|  | 446 |  | 
|  | 447 | /* | 
|  | 448 | * xfs_filestream_unmount() is called when a file system that was mounted with | 
|  | 449 | * the filestream option is unmounted.  It drains the data structures created | 
|  | 450 | * to track the file system's file streams and frees all the memory that was | 
|  | 451 | * allocated. | 
|  | 452 | */ | 
|  | 453 | void | 
|  | 454 | xfs_filestream_unmount( | 
|  | 455 | xfs_mount_t	*mp) | 
|  | 456 | { | 
|  | 457 | xfs_mru_cache_destroy(mp->m_filestream); | 
|  | 458 | } | 
|  | 459 |  | 
|  | 460 | /* | 
|  | 461 | * If the mount point's m_perag array is going to be reallocated, all | 
|  | 462 | * outstanding cache entries must be flushed to avoid accessing reference count | 
|  | 463 | * addresses that have been freed.  The call to xfs_filestream_flush() must be | 
|  | 464 | * made inside the block that holds the m_peraglock in write mode to do the | 
|  | 465 | * reallocation. | 
|  | 466 | */ | 
|  | 467 | void | 
|  | 468 | xfs_filestream_flush( | 
|  | 469 | xfs_mount_t	*mp) | 
|  | 470 | { | 
| David Chinner | 65de556 | 2007-08-16 15:21:11 +1000 | [diff] [blame] | 471 | xfs_mru_cache_flush(mp->m_filestream); | 
| David Chinner | 2a82b8b | 2007-07-11 11:09:12 +1000 | [diff] [blame] | 472 | } | 
|  | 473 |  | 
|  | 474 | /* | 
|  | 475 | * Return the AG of the filestream the file or directory belongs to, or | 
|  | 476 | * NULLAGNUMBER otherwise. | 
|  | 477 | */ | 
|  | 478 | xfs_agnumber_t | 
|  | 479 | xfs_filestream_lookup_ag( | 
|  | 480 | xfs_inode_t	*ip) | 
|  | 481 | { | 
|  | 482 | xfs_mru_cache_t	*cache; | 
|  | 483 | fstrm_item_t	*item; | 
|  | 484 | xfs_agnumber_t	ag; | 
|  | 485 | int		ref; | 
|  | 486 |  | 
|  | 487 | if (!(ip->i_d.di_mode & (S_IFREG | S_IFDIR))) { | 
|  | 488 | ASSERT(0); | 
|  | 489 | return NULLAGNUMBER; | 
|  | 490 | } | 
|  | 491 |  | 
|  | 492 | cache = ip->i_mount->m_filestream; | 
|  | 493 | item = xfs_mru_cache_lookup(cache, ip->i_ino); | 
|  | 494 | if (!item) { | 
|  | 495 | TRACE_LOOKUP(ip->i_mount, ip, NULL, NULLAGNUMBER, 0); | 
|  | 496 | return NULLAGNUMBER; | 
|  | 497 | } | 
|  | 498 |  | 
|  | 499 | ASSERT(ip == item->ip); | 
|  | 500 | ag = item->ag; | 
|  | 501 | ref = xfs_filestream_peek_ag(ip->i_mount, ag); | 
|  | 502 | xfs_mru_cache_done(cache); | 
|  | 503 |  | 
|  | 504 | TRACE_LOOKUP(ip->i_mount, ip, item->pip, ag, ref); | 
|  | 505 | return ag; | 
|  | 506 | } | 
|  | 507 |  | 
|  | 508 | /* | 
|  | 509 | * xfs_filestream_associate() should only be called to associate a regular file | 
|  | 510 | * with its parent directory.  Calling it with a child directory isn't | 
|  | 511 | * appropriate because filestreams don't apply to entire directory hierarchies. | 
|  | 512 | * Creating a file in a child directory of an existing filestream directory | 
|  | 513 | * starts a new filestream with its own allocation group association. | 
|  | 514 | * | 
|  | 515 | * Returns < 0 on error, 0 if successful association occurred, > 0 if | 
|  | 516 | * we failed to get an association because of locking issues. | 
|  | 517 | */ | 
|  | 518 | int | 
|  | 519 | xfs_filestream_associate( | 
|  | 520 | xfs_inode_t	*pip, | 
|  | 521 | xfs_inode_t	*ip) | 
|  | 522 | { | 
|  | 523 | xfs_mount_t	*mp; | 
|  | 524 | xfs_mru_cache_t	*cache; | 
|  | 525 | fstrm_item_t	*item; | 
|  | 526 | xfs_agnumber_t	ag, rotorstep, startag; | 
|  | 527 | int		err = 0; | 
|  | 528 |  | 
|  | 529 | ASSERT(pip->i_d.di_mode & S_IFDIR); | 
|  | 530 | ASSERT(ip->i_d.di_mode & S_IFREG); | 
|  | 531 | if (!(pip->i_d.di_mode & S_IFDIR) || !(ip->i_d.di_mode & S_IFREG)) | 
|  | 532 | return -EINVAL; | 
|  | 533 |  | 
|  | 534 | mp = pip->i_mount; | 
|  | 535 | cache = mp->m_filestream; | 
|  | 536 | down_read(&mp->m_peraglock); | 
|  | 537 |  | 
|  | 538 | /* | 
|  | 539 | * We have a problem, Houston. | 
|  | 540 | * | 
|  | 541 | * Taking the iolock here violates inode locking order - we already | 
|  | 542 | * hold the ilock. Hence if we block getting this lock we may never | 
|  | 543 | * wake. Unfortunately, that means if we can't get the lock, we're | 
|  | 544 | * screwed in terms of getting a stream association - we can't spin | 
|  | 545 | * waiting for the lock because someone else is waiting on the lock we | 
|  | 546 | * hold and we cannot drop that as we are in a transaction here. | 
|  | 547 | * | 
|  | 548 | * Lucky for us, this inversion is rarely a problem because it's a | 
|  | 549 | * directory inode that we are trying to lock here and that means the | 
|  | 550 | * only place that matters is xfs_sync_inodes() and SYNC_DELWRI is | 
|  | 551 | * used. i.e. freeze, remount-ro, quotasync or unmount. | 
|  | 552 | * | 
|  | 553 | * So, if we can't get the iolock without sleeping then just give up | 
|  | 554 | */ | 
|  | 555 | if (!xfs_ilock_nowait(pip, XFS_IOLOCK_EXCL)) { | 
|  | 556 | up_read(&mp->m_peraglock); | 
|  | 557 | return 1; | 
|  | 558 | } | 
|  | 559 |  | 
|  | 560 | /* If the parent directory is already in the cache, use its AG. */ | 
|  | 561 | item = xfs_mru_cache_lookup(cache, pip->i_ino); | 
|  | 562 | if (item) { | 
|  | 563 | ASSERT(item->ip == pip); | 
|  | 564 | ag = item->ag; | 
|  | 565 | xfs_mru_cache_done(cache); | 
|  | 566 |  | 
|  | 567 | TRACE_LOOKUP(mp, pip, pip, ag, xfs_filestream_peek_ag(mp, ag)); | 
|  | 568 | err = _xfs_filestream_update_ag(ip, pip, ag); | 
|  | 569 |  | 
|  | 570 | goto exit; | 
|  | 571 | } | 
|  | 572 |  | 
|  | 573 | /* | 
|  | 574 | * Set the starting AG using the rotor for inode32, otherwise | 
|  | 575 | * use the directory inode's AG. | 
|  | 576 | */ | 
|  | 577 | if (mp->m_flags & XFS_MOUNT_32BITINODES) { | 
|  | 578 | rotorstep = xfs_rotorstep; | 
|  | 579 | startag = (mp->m_agfrotor / rotorstep) % mp->m_sb.sb_agcount; | 
|  | 580 | mp->m_agfrotor = (mp->m_agfrotor + 1) % | 
|  | 581 | (mp->m_sb.sb_agcount * rotorstep); | 
|  | 582 | } else | 
|  | 583 | startag = XFS_INO_TO_AGNO(mp, pip->i_ino); | 
|  | 584 |  | 
|  | 585 | /* Pick a new AG for the parent inode starting at startag. */ | 
|  | 586 | err = _xfs_filestream_pick_ag(mp, startag, &ag, 0, 0); | 
|  | 587 | if (err || ag == NULLAGNUMBER) | 
|  | 588 | goto exit_did_pick; | 
|  | 589 |  | 
|  | 590 | /* Associate the parent inode with the AG. */ | 
|  | 591 | err = _xfs_filestream_update_ag(pip, NULL, ag); | 
|  | 592 | if (err) | 
|  | 593 | goto exit_did_pick; | 
|  | 594 |  | 
|  | 595 | /* Associate the file inode with the AG. */ | 
|  | 596 | err = _xfs_filestream_update_ag(ip, pip, ag); | 
|  | 597 | if (err) | 
|  | 598 | goto exit_did_pick; | 
|  | 599 |  | 
|  | 600 | TRACE_ASSOCIATE(mp, ip, pip, ag, xfs_filestream_peek_ag(mp, ag)); | 
|  | 601 |  | 
|  | 602 | exit_did_pick: | 
|  | 603 | /* | 
|  | 604 | * If _xfs_filestream_pick_ag() returned a valid AG, remove the | 
|  | 605 | * reference it took on it, since the file and directory will have taken | 
|  | 606 | * their own now if they were successfully cached. | 
|  | 607 | */ | 
|  | 608 | if (ag != NULLAGNUMBER) | 
|  | 609 | xfs_filestream_put_ag(mp, ag); | 
|  | 610 |  | 
|  | 611 | exit: | 
|  | 612 | xfs_iunlock(pip, XFS_IOLOCK_EXCL); | 
|  | 613 | up_read(&mp->m_peraglock); | 
|  | 614 | return -err; | 
|  | 615 | } | 
|  | 616 |  | 
|  | 617 | /* | 
|  | 618 | * Pick a new allocation group for the current file and its file stream.  This | 
|  | 619 | * function is called by xfs_bmap_filestreams() with the mount point's per-ag | 
|  | 620 | * lock held. | 
|  | 621 | */ | 
|  | 622 | int | 
|  | 623 | xfs_filestream_new_ag( | 
|  | 624 | xfs_bmalloca_t	*ap, | 
|  | 625 | xfs_agnumber_t	*agp) | 
|  | 626 | { | 
|  | 627 | int		flags, err; | 
|  | 628 | xfs_inode_t	*ip, *pip = NULL; | 
|  | 629 | xfs_mount_t	*mp; | 
|  | 630 | xfs_mru_cache_t	*cache; | 
|  | 631 | xfs_extlen_t	minlen; | 
|  | 632 | fstrm_item_t	*dir, *file; | 
|  | 633 | xfs_agnumber_t	ag = NULLAGNUMBER; | 
|  | 634 |  | 
|  | 635 | ip = ap->ip; | 
|  | 636 | mp = ip->i_mount; | 
|  | 637 | cache = mp->m_filestream; | 
|  | 638 | minlen = ap->alen; | 
|  | 639 | *agp = NULLAGNUMBER; | 
|  | 640 |  | 
|  | 641 | /* | 
|  | 642 | * Look for the file in the cache, removing it if it's found.  Doing | 
|  | 643 | * this allows it to be held across the dir lookup that follows. | 
|  | 644 | */ | 
|  | 645 | file = xfs_mru_cache_remove(cache, ip->i_ino); | 
|  | 646 | if (file) { | 
|  | 647 | ASSERT(ip == file->ip); | 
|  | 648 |  | 
|  | 649 | /* Save the file's parent inode and old AG number for later. */ | 
|  | 650 | pip = file->pip; | 
|  | 651 | ag = file->ag; | 
|  | 652 |  | 
|  | 653 | /* Look for the file's directory in the cache. */ | 
|  | 654 | dir = xfs_mru_cache_lookup(cache, pip->i_ino); | 
|  | 655 | if (dir) { | 
|  | 656 | ASSERT(pip == dir->ip); | 
|  | 657 |  | 
|  | 658 | /* | 
|  | 659 | * If the directory has already moved on to a new AG, | 
|  | 660 | * use that AG as the new AG for the file. Don't | 
|  | 661 | * forget to twiddle the AG refcounts to match the | 
|  | 662 | * movement. | 
|  | 663 | */ | 
|  | 664 | if (dir->ag != file->ag) { | 
|  | 665 | xfs_filestream_put_ag(mp, file->ag); | 
|  | 666 | xfs_filestream_get_ag(mp, dir->ag); | 
|  | 667 | *agp = file->ag = dir->ag; | 
|  | 668 | } | 
|  | 669 |  | 
|  | 670 | xfs_mru_cache_done(cache); | 
|  | 671 | } | 
|  | 672 |  | 
|  | 673 | /* | 
|  | 674 | * Put the file back in the cache.  If this fails, the free | 
|  | 675 | * function needs to be called to tidy up in the same way as if | 
|  | 676 | * the item had simply expired from the cache. | 
|  | 677 | */ | 
|  | 678 | err = xfs_mru_cache_insert(cache, ip->i_ino, file); | 
|  | 679 | if (err) { | 
|  | 680 | xfs_fstrm_free_func(ip->i_ino, file); | 
|  | 681 | return err; | 
|  | 682 | } | 
|  | 683 |  | 
|  | 684 | /* | 
|  | 685 | * If the file's AG was moved to the directory's new AG, there's | 
|  | 686 | * nothing more to be done. | 
|  | 687 | */ | 
|  | 688 | if (*agp != NULLAGNUMBER) { | 
|  | 689 | TRACE_MOVEAG(mp, ip, pip, | 
|  | 690 | ag, xfs_filestream_peek_ag(mp, ag), | 
|  | 691 | *agp, xfs_filestream_peek_ag(mp, *agp)); | 
|  | 692 | return 0; | 
|  | 693 | } | 
|  | 694 | } | 
|  | 695 |  | 
|  | 696 | /* | 
|  | 697 | * If the file's parent directory is known, take its iolock in exclusive | 
|  | 698 | * mode to prevent two sibling files from racing each other to migrate | 
|  | 699 | * themselves and their parent to different AGs. | 
|  | 700 | */ | 
|  | 701 | if (pip) | 
|  | 702 | xfs_ilock(pip, XFS_IOLOCK_EXCL); | 
|  | 703 |  | 
|  | 704 | /* | 
|  | 705 | * A new AG needs to be found for the file.  If the file's parent | 
|  | 706 | * directory is also known, it will be moved to the new AG as well to | 
|  | 707 | * ensure that files created inside it in future use the new AG. | 
|  | 708 | */ | 
|  | 709 | ag = (ag == NULLAGNUMBER) ? 0 : (ag + 1) % mp->m_sb.sb_agcount; | 
|  | 710 | flags = (ap->userdata ? XFS_PICK_USERDATA : 0) | | 
|  | 711 | (ap->low ? XFS_PICK_LOWSPACE : 0); | 
|  | 712 |  | 
|  | 713 | err = _xfs_filestream_pick_ag(mp, ag, agp, flags, minlen); | 
|  | 714 | if (err || *agp == NULLAGNUMBER) | 
|  | 715 | goto exit; | 
|  | 716 |  | 
|  | 717 | /* | 
|  | 718 | * If the file wasn't found in the file cache, then its parent directory | 
|  | 719 | * inode isn't known.  For this to have happened, the file must either | 
|  | 720 | * be pre-existing, or it was created long enough ago that its cache | 
|  | 721 | * entry has expired.  This isn't the sort of usage that the filestreams | 
|  | 722 | * allocator is trying to optimise, so there's no point trying to track | 
|  | 723 | * its new AG somehow in the filestream data structures. | 
|  | 724 | */ | 
|  | 725 | if (!pip) { | 
|  | 726 | TRACE_ORPHAN(mp, ip, *agp); | 
|  | 727 | goto exit; | 
|  | 728 | } | 
|  | 729 |  | 
|  | 730 | /* Associate the parent inode with the AG. */ | 
|  | 731 | err = _xfs_filestream_update_ag(pip, NULL, *agp); | 
|  | 732 | if (err) | 
|  | 733 | goto exit; | 
|  | 734 |  | 
|  | 735 | /* Associate the file inode with the AG. */ | 
|  | 736 | err = _xfs_filestream_update_ag(ip, pip, *agp); | 
|  | 737 | if (err) | 
|  | 738 | goto exit; | 
|  | 739 |  | 
|  | 740 | TRACE_MOVEAG(mp, ip, pip, NULLAGNUMBER, 0, | 
|  | 741 | *agp, xfs_filestream_peek_ag(mp, *agp)); | 
|  | 742 |  | 
|  | 743 | exit: | 
|  | 744 | /* | 
|  | 745 | * If _xfs_filestream_pick_ag() returned a valid AG, remove the | 
|  | 746 | * reference it took on it, since the file and directory will have taken | 
|  | 747 | * their own now if they were successfully cached. | 
|  | 748 | */ | 
|  | 749 | if (*agp != NULLAGNUMBER) | 
|  | 750 | xfs_filestream_put_ag(mp, *agp); | 
|  | 751 | else | 
|  | 752 | *agp = 0; | 
|  | 753 |  | 
|  | 754 | if (pip) | 
|  | 755 | xfs_iunlock(pip, XFS_IOLOCK_EXCL); | 
|  | 756 |  | 
|  | 757 | return err; | 
|  | 758 | } | 
|  | 759 |  | 
|  | 760 | /* | 
|  | 761 | * Remove an association between an inode and a filestream object. | 
|  | 762 | * Typically this is done on last close of an unlinked file. | 
|  | 763 | */ | 
|  | 764 | void | 
|  | 765 | xfs_filestream_deassociate( | 
|  | 766 | xfs_inode_t	*ip) | 
|  | 767 | { | 
|  | 768 | xfs_mru_cache_t	*cache = ip->i_mount->m_filestream; | 
|  | 769 |  | 
|  | 770 | xfs_mru_cache_delete(cache, ip->i_ino); | 
|  | 771 | } |