12a82b8beSDavid Chinner /* 22a82b8beSDavid Chinner * Copyright (c) 2006-2007 Silicon Graphics, Inc. 32a82b8beSDavid Chinner * All Rights Reserved. 42a82b8beSDavid Chinner * 52a82b8beSDavid Chinner * This program is free software; you can redistribute it and/or 62a82b8beSDavid Chinner * modify it under the terms of the GNU General Public License as 72a82b8beSDavid Chinner * published by the Free Software Foundation. 82a82b8beSDavid Chinner * 92a82b8beSDavid Chinner * This program is distributed in the hope that it would be useful, 102a82b8beSDavid Chinner * but WITHOUT ANY WARRANTY; without even the implied warranty of 112a82b8beSDavid Chinner * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 122a82b8beSDavid Chinner * GNU General Public License for more details. 132a82b8beSDavid Chinner * 142a82b8beSDavid Chinner * You should have received a copy of the GNU General Public License 152a82b8beSDavid Chinner * along with this program; if not, write the Free Software Foundation, 162a82b8beSDavid Chinner * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 172a82b8beSDavid Chinner */ 182a82b8beSDavid Chinner #include "xfs.h" 192a82b8beSDavid Chinner #include "xfs_bmap_btree.h" 202a82b8beSDavid Chinner #include "xfs_inum.h" 212a82b8beSDavid Chinner #include "xfs_dir2.h" 222a82b8beSDavid Chinner #include "xfs_dir2_sf.h" 232a82b8beSDavid Chinner #include "xfs_attr_sf.h" 242a82b8beSDavid Chinner #include "xfs_dinode.h" 252a82b8beSDavid Chinner #include "xfs_inode.h" 262a82b8beSDavid Chinner #include "xfs_ag.h" 272a82b8beSDavid Chinner #include "xfs_dmapi.h" 282a82b8beSDavid Chinner #include "xfs_log.h" 292a82b8beSDavid Chinner #include "xfs_trans.h" 302a82b8beSDavid Chinner #include "xfs_sb.h" 312a82b8beSDavid Chinner #include "xfs_mount.h" 322a82b8beSDavid Chinner #include "xfs_bmap.h" 332a82b8beSDavid Chinner #include "xfs_alloc.h" 342a82b8beSDavid Chinner #include "xfs_utils.h" 352a82b8beSDavid Chinner #include "xfs_mru_cache.h" 362a82b8beSDavid Chinner #include "xfs_filestream.h" 370b1b213fSChristoph Hellwig #include "xfs_trace.h" 382a82b8beSDavid Chinner 392a82b8beSDavid Chinner #ifdef XFS_FILESTREAMS_TRACE 402a82b8beSDavid Chinner 412a82b8beSDavid Chinner ktrace_t *xfs_filestreams_trace_buf; 422a82b8beSDavid Chinner 432a82b8beSDavid Chinner STATIC void 442a82b8beSDavid Chinner xfs_filestreams_trace( 452a82b8beSDavid Chinner xfs_mount_t *mp, /* mount point */ 462a82b8beSDavid Chinner int type, /* type of trace */ 472a82b8beSDavid Chinner const char *func, /* source function */ 482a82b8beSDavid Chinner int line, /* source line number */ 492a82b8beSDavid Chinner __psunsigned_t arg0, 502a82b8beSDavid Chinner __psunsigned_t arg1, 512a82b8beSDavid Chinner __psunsigned_t arg2, 522a82b8beSDavid Chinner __psunsigned_t arg3, 532a82b8beSDavid Chinner __psunsigned_t arg4, 542a82b8beSDavid Chinner __psunsigned_t arg5) 552a82b8beSDavid Chinner { 562a82b8beSDavid Chinner ktrace_enter(xfs_filestreams_trace_buf, 572a82b8beSDavid Chinner (void *)(__psint_t)(type | (line << 16)), 582a82b8beSDavid Chinner (void *)func, 592a82b8beSDavid Chinner (void *)(__psunsigned_t)current_pid(), 602a82b8beSDavid Chinner (void *)mp, 612a82b8beSDavid Chinner (void *)(__psunsigned_t)arg0, 622a82b8beSDavid Chinner (void *)(__psunsigned_t)arg1, 632a82b8beSDavid Chinner (void *)(__psunsigned_t)arg2, 642a82b8beSDavid Chinner (void *)(__psunsigned_t)arg3, 652a82b8beSDavid Chinner (void *)(__psunsigned_t)arg4, 662a82b8beSDavid Chinner (void *)(__psunsigned_t)arg5, 672a82b8beSDavid Chinner NULL, NULL, NULL, NULL, NULL, NULL); 682a82b8beSDavid Chinner } 692a82b8beSDavid Chinner 702a82b8beSDavid Chinner #define TRACE0(mp,t) TRACE6(mp,t,0,0,0,0,0,0) 712a82b8beSDavid Chinner #define TRACE1(mp,t,a0) TRACE6(mp,t,a0,0,0,0,0,0) 722a82b8beSDavid Chinner #define TRACE2(mp,t,a0,a1) TRACE6(mp,t,a0,a1,0,0,0,0) 732a82b8beSDavid Chinner #define TRACE3(mp,t,a0,a1,a2) TRACE6(mp,t,a0,a1,a2,0,0,0) 742a82b8beSDavid Chinner #define TRACE4(mp,t,a0,a1,a2,a3) TRACE6(mp,t,a0,a1,a2,a3,0,0) 752a82b8beSDavid Chinner #define TRACE5(mp,t,a0,a1,a2,a3,a4) TRACE6(mp,t,a0,a1,a2,a3,a4,0) 762a82b8beSDavid Chinner #define TRACE6(mp,t,a0,a1,a2,a3,a4,a5) \ 7734a622b2SHarvey Harrison xfs_filestreams_trace(mp, t, __func__, __LINE__, \ 782a82b8beSDavid Chinner (__psunsigned_t)a0, (__psunsigned_t)a1, \ 792a82b8beSDavid Chinner (__psunsigned_t)a2, (__psunsigned_t)a3, \ 802a82b8beSDavid Chinner (__psunsigned_t)a4, (__psunsigned_t)a5) 812a82b8beSDavid Chinner 822a82b8beSDavid Chinner #define TRACE_AG_SCAN(mp, ag, ag2) \ 832a82b8beSDavid Chinner TRACE2(mp, XFS_FSTRM_KTRACE_AGSCAN, ag, ag2); 842a82b8beSDavid Chinner #define TRACE_AG_PICK1(mp, max_ag, maxfree) \ 852a82b8beSDavid Chinner TRACE2(mp, XFS_FSTRM_KTRACE_AGPICK1, max_ag, maxfree); 862a82b8beSDavid Chinner #define TRACE_AG_PICK2(mp, ag, ag2, cnt, free, scan, flag) \ 872a82b8beSDavid Chinner TRACE6(mp, XFS_FSTRM_KTRACE_AGPICK2, ag, ag2, \ 882a82b8beSDavid Chinner cnt, free, scan, flag) 892a82b8beSDavid Chinner #define TRACE_UPDATE(mp, ip, ag, cnt, ag2, cnt2) \ 902a82b8beSDavid Chinner TRACE5(mp, XFS_FSTRM_KTRACE_UPDATE, ip, ag, cnt, ag2, cnt2) 912a82b8beSDavid Chinner #define TRACE_FREE(mp, ip, pip, ag, cnt) \ 922a82b8beSDavid Chinner TRACE4(mp, XFS_FSTRM_KTRACE_FREE, ip, pip, ag, cnt) 932a82b8beSDavid Chinner #define TRACE_LOOKUP(mp, ip, pip, ag, cnt) \ 942a82b8beSDavid Chinner TRACE4(mp, XFS_FSTRM_KTRACE_ITEM_LOOKUP, ip, pip, ag, cnt) 952a82b8beSDavid Chinner #define TRACE_ASSOCIATE(mp, ip, pip, ag, cnt) \ 962a82b8beSDavid Chinner TRACE4(mp, XFS_FSTRM_KTRACE_ASSOCIATE, ip, pip, ag, cnt) 972a82b8beSDavid Chinner #define TRACE_MOVEAG(mp, ip, pip, oag, ocnt, nag, ncnt) \ 982a82b8beSDavid Chinner TRACE6(mp, XFS_FSTRM_KTRACE_MOVEAG, ip, pip, oag, ocnt, nag, ncnt) 992a82b8beSDavid Chinner #define TRACE_ORPHAN(mp, ip, ag) \ 1002a82b8beSDavid Chinner TRACE2(mp, XFS_FSTRM_KTRACE_ORPHAN, ip, ag); 1012a82b8beSDavid Chinner 1022a82b8beSDavid Chinner 1032a82b8beSDavid Chinner #else 1042a82b8beSDavid Chinner #define TRACE_AG_SCAN(mp, ag, ag2) 1052a82b8beSDavid Chinner #define TRACE_AG_PICK1(mp, max_ag, maxfree) 1062a82b8beSDavid Chinner #define TRACE_AG_PICK2(mp, ag, ag2, cnt, free, scan, flag) 1072a82b8beSDavid Chinner #define TRACE_UPDATE(mp, ip, ag, cnt, ag2, cnt2) 1082a82b8beSDavid Chinner #define TRACE_FREE(mp, ip, pip, ag, cnt) 1092a82b8beSDavid Chinner #define TRACE_LOOKUP(mp, ip, pip, ag, cnt) 1102a82b8beSDavid Chinner #define TRACE_ASSOCIATE(mp, ip, pip, ag, cnt) 1112a82b8beSDavid Chinner #define TRACE_MOVEAG(mp, ip, pip, oag, ocnt, nag, ncnt) 1122a82b8beSDavid Chinner #define TRACE_ORPHAN(mp, ip, ag) 1132a82b8beSDavid Chinner #endif 1142a82b8beSDavid Chinner 1152a82b8beSDavid Chinner static kmem_zone_t *item_zone; 1162a82b8beSDavid Chinner 1172a82b8beSDavid Chinner /* 1182a82b8beSDavid Chinner * Structure for associating a file or a directory with an allocation group. 1192a82b8beSDavid Chinner * The parent directory pointer is only needed for files, but since there will 1202a82b8beSDavid Chinner * generally be vastly more files than directories in the cache, using the same 1212a82b8beSDavid Chinner * data structure simplifies the code with very little memory overhead. 1222a82b8beSDavid Chinner */ 1232a82b8beSDavid Chinner typedef struct fstrm_item 1242a82b8beSDavid Chinner { 1252a82b8beSDavid Chinner xfs_agnumber_t ag; /* AG currently in use for the file/directory. */ 1262a82b8beSDavid Chinner xfs_inode_t *ip; /* inode self-pointer. */ 1272a82b8beSDavid Chinner xfs_inode_t *pip; /* Parent directory inode pointer. */ 1282a82b8beSDavid Chinner } fstrm_item_t; 1292a82b8beSDavid Chinner 1302a82b8beSDavid Chinner 1312a82b8beSDavid Chinner /* 1322a82b8beSDavid Chinner * Scan the AGs starting at startag looking for an AG that isn't in use and has 1332a82b8beSDavid Chinner * at least minlen blocks free. 1342a82b8beSDavid Chinner */ 1352a82b8beSDavid Chinner static int 1362a82b8beSDavid Chinner _xfs_filestream_pick_ag( 1372a82b8beSDavid Chinner xfs_mount_t *mp, 1382a82b8beSDavid Chinner xfs_agnumber_t startag, 1392a82b8beSDavid Chinner xfs_agnumber_t *agp, 1402a82b8beSDavid Chinner int flags, 1412a82b8beSDavid Chinner xfs_extlen_t minlen) 1422a82b8beSDavid Chinner { 143*4196ac08SDave Chinner int streams, max_streams; 1442a82b8beSDavid Chinner int err, trylock, nscan; 1456cc87645SDave Chinner xfs_extlen_t longest, free, minfree, maxfree = 0; 1462a82b8beSDavid Chinner xfs_agnumber_t ag, max_ag = NULLAGNUMBER; 1472a82b8beSDavid Chinner struct xfs_perag *pag; 1482a82b8beSDavid Chinner 1492a82b8beSDavid Chinner /* 2% of an AG's blocks must be free for it to be chosen. */ 1502a82b8beSDavid Chinner minfree = mp->m_sb.sb_agblocks / 50; 1512a82b8beSDavid Chinner 1522a82b8beSDavid Chinner ag = startag; 1532a82b8beSDavid Chinner *agp = NULLAGNUMBER; 1542a82b8beSDavid Chinner 1552a82b8beSDavid Chinner /* For the first pass, don't sleep trying to init the per-AG. */ 1562a82b8beSDavid Chinner trylock = XFS_ALLOC_FLAG_TRYLOCK; 1572a82b8beSDavid Chinner 1582a82b8beSDavid Chinner for (nscan = 0; 1; nscan++) { 159*4196ac08SDave Chinner pag = xfs_perag_get(mp, ag); 160*4196ac08SDave Chinner TRACE_AG_SCAN(mp, ag, atomic_read(&pag->pagf_fstrms)); 1612a82b8beSDavid Chinner 1622a82b8beSDavid Chinner if (!pag->pagf_init) { 1632a82b8beSDavid Chinner err = xfs_alloc_pagf_init(mp, NULL, ag, trylock); 164*4196ac08SDave Chinner if (err && !trylock) { 165*4196ac08SDave Chinner xfs_perag_put(pag); 1662a82b8beSDavid Chinner return err; 1672a82b8beSDavid Chinner } 168*4196ac08SDave Chinner } 1692a82b8beSDavid Chinner 1702a82b8beSDavid Chinner /* Might fail sometimes during the 1st pass with trylock set. */ 1712a82b8beSDavid Chinner if (!pag->pagf_init) 1722a82b8beSDavid Chinner goto next_ag; 1732a82b8beSDavid Chinner 1742a82b8beSDavid Chinner /* Keep track of the AG with the most free blocks. */ 1752a82b8beSDavid Chinner if (pag->pagf_freeblks > maxfree) { 1762a82b8beSDavid Chinner maxfree = pag->pagf_freeblks; 177*4196ac08SDave Chinner max_streams = atomic_read(&pag->pagf_fstrms); 1782a82b8beSDavid Chinner max_ag = ag; 1792a82b8beSDavid Chinner } 1802a82b8beSDavid Chinner 1812a82b8beSDavid Chinner /* 1822a82b8beSDavid Chinner * The AG reference count does two things: it enforces mutual 1832a82b8beSDavid Chinner * exclusion when examining the suitability of an AG in this 1842a82b8beSDavid Chinner * loop, and it guards against two filestreams being established 1852a82b8beSDavid Chinner * in the same AG as each other. 1862a82b8beSDavid Chinner */ 1872a82b8beSDavid Chinner if (xfs_filestream_get_ag(mp, ag) > 1) { 1882a82b8beSDavid Chinner xfs_filestream_put_ag(mp, ag); 1892a82b8beSDavid Chinner goto next_ag; 1902a82b8beSDavid Chinner } 1912a82b8beSDavid Chinner 1926cc87645SDave Chinner longest = xfs_alloc_longest_free_extent(mp, pag); 1932a82b8beSDavid Chinner if (((minlen && longest >= minlen) || 1942a82b8beSDavid Chinner (!minlen && pag->pagf_freeblks >= minfree)) && 1952a82b8beSDavid Chinner (!pag->pagf_metadata || !(flags & XFS_PICK_USERDATA) || 1962a82b8beSDavid Chinner (flags & XFS_PICK_LOWSPACE))) { 1972a82b8beSDavid Chinner 1982a82b8beSDavid Chinner /* Break out, retaining the reference on the AG. */ 1992a82b8beSDavid Chinner free = pag->pagf_freeblks; 200*4196ac08SDave Chinner streams = atomic_read(&pag->pagf_fstrms); 201*4196ac08SDave Chinner xfs_perag_put(pag); 2022a82b8beSDavid Chinner *agp = ag; 2032a82b8beSDavid Chinner break; 2042a82b8beSDavid Chinner } 2052a82b8beSDavid Chinner 2062a82b8beSDavid Chinner /* Drop the reference on this AG, it's not usable. */ 2072a82b8beSDavid Chinner xfs_filestream_put_ag(mp, ag); 2082a82b8beSDavid Chinner next_ag: 209*4196ac08SDave Chinner xfs_perag_put(pag); 2102a82b8beSDavid Chinner /* Move to the next AG, wrapping to AG 0 if necessary. */ 2112a82b8beSDavid Chinner if (++ag >= mp->m_sb.sb_agcount) 2122a82b8beSDavid Chinner ag = 0; 2132a82b8beSDavid Chinner 2142a82b8beSDavid Chinner /* If a full pass of the AGs hasn't been done yet, continue. */ 2152a82b8beSDavid Chinner if (ag != startag) 2162a82b8beSDavid Chinner continue; 2172a82b8beSDavid Chinner 2182a82b8beSDavid Chinner /* Allow sleeping in xfs_alloc_pagf_init() on the 2nd pass. */ 2192a82b8beSDavid Chinner if (trylock != 0) { 2202a82b8beSDavid Chinner trylock = 0; 2212a82b8beSDavid Chinner continue; 2222a82b8beSDavid Chinner } 2232a82b8beSDavid Chinner 2242a82b8beSDavid Chinner /* Finally, if lowspace wasn't set, set it for the 3rd pass. */ 2252a82b8beSDavid Chinner if (!(flags & XFS_PICK_LOWSPACE)) { 2262a82b8beSDavid Chinner flags |= XFS_PICK_LOWSPACE; 2272a82b8beSDavid Chinner continue; 2282a82b8beSDavid Chinner } 2292a82b8beSDavid Chinner 2302a82b8beSDavid Chinner /* 2312a82b8beSDavid Chinner * Take the AG with the most free space, regardless of whether 2322a82b8beSDavid Chinner * it's already in use by another filestream. 2332a82b8beSDavid Chinner */ 2342a82b8beSDavid Chinner if (max_ag != NULLAGNUMBER) { 2352a82b8beSDavid Chinner xfs_filestream_get_ag(mp, max_ag); 2362a82b8beSDavid Chinner TRACE_AG_PICK1(mp, max_ag, maxfree); 237*4196ac08SDave Chinner streams = max_streams; 2382a82b8beSDavid Chinner free = maxfree; 2392a82b8beSDavid Chinner *agp = max_ag; 2402a82b8beSDavid Chinner break; 2412a82b8beSDavid Chinner } 2422a82b8beSDavid Chinner 2432a82b8beSDavid Chinner /* take AG 0 if none matched */ 2442a82b8beSDavid Chinner TRACE_AG_PICK1(mp, max_ag, maxfree); 2452a82b8beSDavid Chinner *agp = 0; 2462a82b8beSDavid Chinner return 0; 2472a82b8beSDavid Chinner } 2482a82b8beSDavid Chinner 249*4196ac08SDave Chinner TRACE_AG_PICK2(mp, startag, *agp, streams, free, nscan, flags); 2502a82b8beSDavid Chinner 2512a82b8beSDavid Chinner return 0; 2522a82b8beSDavid Chinner } 2532a82b8beSDavid Chinner 2542a82b8beSDavid Chinner /* 2552a82b8beSDavid Chinner * Set the allocation group number for a file or a directory, updating inode 2562a82b8beSDavid Chinner * references and per-AG references as appropriate. Must be called with the 2572a82b8beSDavid Chinner * m_peraglock held in read mode. 2582a82b8beSDavid Chinner */ 2592a82b8beSDavid Chinner static int 2602a82b8beSDavid Chinner _xfs_filestream_update_ag( 2612a82b8beSDavid Chinner xfs_inode_t *ip, 2622a82b8beSDavid Chinner xfs_inode_t *pip, 2632a82b8beSDavid Chinner xfs_agnumber_t ag) 2642a82b8beSDavid Chinner { 2652a82b8beSDavid Chinner int err = 0; 2662a82b8beSDavid Chinner xfs_mount_t *mp; 2672a82b8beSDavid Chinner xfs_mru_cache_t *cache; 2682a82b8beSDavid Chinner fstrm_item_t *item; 2692a82b8beSDavid Chinner xfs_agnumber_t old_ag; 2702a82b8beSDavid Chinner xfs_inode_t *old_pip; 2712a82b8beSDavid Chinner 2722a82b8beSDavid Chinner /* 2732a82b8beSDavid Chinner * Either ip is a regular file and pip is a directory, or ip is a 2742a82b8beSDavid Chinner * directory and pip is NULL. 2752a82b8beSDavid Chinner */ 2762a82b8beSDavid Chinner ASSERT(ip && (((ip->i_d.di_mode & S_IFREG) && pip && 2772a82b8beSDavid Chinner (pip->i_d.di_mode & S_IFDIR)) || 2782a82b8beSDavid Chinner ((ip->i_d.di_mode & S_IFDIR) && !pip))); 2792a82b8beSDavid Chinner 2802a82b8beSDavid Chinner mp = ip->i_mount; 2812a82b8beSDavid Chinner cache = mp->m_filestream; 2822a82b8beSDavid Chinner 2832a82b8beSDavid Chinner item = xfs_mru_cache_lookup(cache, ip->i_ino); 2842a82b8beSDavid Chinner if (item) { 2852a82b8beSDavid Chinner ASSERT(item->ip == ip); 2862a82b8beSDavid Chinner old_ag = item->ag; 2872a82b8beSDavid Chinner item->ag = ag; 2882a82b8beSDavid Chinner old_pip = item->pip; 2892a82b8beSDavid Chinner item->pip = pip; 2902a82b8beSDavid Chinner xfs_mru_cache_done(cache); 2912a82b8beSDavid Chinner 2922a82b8beSDavid Chinner /* 2932a82b8beSDavid Chinner * If the AG has changed, drop the old ref and take a new one, 2942a82b8beSDavid Chinner * effectively transferring the reference from old to new AG. 2952a82b8beSDavid Chinner */ 2962a82b8beSDavid Chinner if (ag != old_ag) { 2972a82b8beSDavid Chinner xfs_filestream_put_ag(mp, old_ag); 2982a82b8beSDavid Chinner xfs_filestream_get_ag(mp, ag); 2992a82b8beSDavid Chinner } 3002a82b8beSDavid Chinner 3012a82b8beSDavid Chinner /* 3022a82b8beSDavid Chinner * If ip is a file and its pip has changed, drop the old ref and 3032a82b8beSDavid Chinner * take a new one. 3042a82b8beSDavid Chinner */ 3052a82b8beSDavid Chinner if (pip && pip != old_pip) { 3062a82b8beSDavid Chinner IRELE(old_pip); 3072a82b8beSDavid Chinner IHOLD(pip); 3082a82b8beSDavid Chinner } 3092a82b8beSDavid Chinner 3102a82b8beSDavid Chinner TRACE_UPDATE(mp, ip, old_ag, xfs_filestream_peek_ag(mp, old_ag), 3112a82b8beSDavid Chinner ag, xfs_filestream_peek_ag(mp, ag)); 3122a82b8beSDavid Chinner return 0; 3132a82b8beSDavid Chinner } 3142a82b8beSDavid Chinner 3152a82b8beSDavid Chinner item = kmem_zone_zalloc(item_zone, KM_MAYFAIL); 3162a82b8beSDavid Chinner if (!item) 3172a82b8beSDavid Chinner return ENOMEM; 3182a82b8beSDavid Chinner 3192a82b8beSDavid Chinner item->ag = ag; 3202a82b8beSDavid Chinner item->ip = ip; 3212a82b8beSDavid Chinner item->pip = pip; 3222a82b8beSDavid Chinner 3232a82b8beSDavid Chinner err = xfs_mru_cache_insert(cache, ip->i_ino, item); 3242a82b8beSDavid Chinner if (err) { 3252a82b8beSDavid Chinner kmem_zone_free(item_zone, item); 3262a82b8beSDavid Chinner return err; 3272a82b8beSDavid Chinner } 3282a82b8beSDavid Chinner 3292a82b8beSDavid Chinner /* Take a reference on the AG. */ 3302a82b8beSDavid Chinner xfs_filestream_get_ag(mp, ag); 3312a82b8beSDavid Chinner 3322a82b8beSDavid Chinner /* 3332a82b8beSDavid Chinner * Take a reference on the inode itself regardless of whether it's a 3342a82b8beSDavid Chinner * regular file or a directory. 3352a82b8beSDavid Chinner */ 3362a82b8beSDavid Chinner IHOLD(ip); 3372a82b8beSDavid Chinner 3382a82b8beSDavid Chinner /* 3392a82b8beSDavid Chinner * In the case of a regular file, take a reference on the parent inode 3402a82b8beSDavid Chinner * as well to ensure it remains in-core. 3412a82b8beSDavid Chinner */ 3422a82b8beSDavid Chinner if (pip) 3432a82b8beSDavid Chinner IHOLD(pip); 3442a82b8beSDavid Chinner 3452a82b8beSDavid Chinner TRACE_UPDATE(mp, ip, ag, xfs_filestream_peek_ag(mp, ag), 3462a82b8beSDavid Chinner ag, xfs_filestream_peek_ag(mp, ag)); 3472a82b8beSDavid Chinner 3482a82b8beSDavid Chinner return 0; 3492a82b8beSDavid Chinner } 3502a82b8beSDavid Chinner 3512a82b8beSDavid Chinner /* xfs_fstrm_free_func(): callback for freeing cached stream items. */ 352a8272ce0SDavid Chinner STATIC void 3532a82b8beSDavid Chinner xfs_fstrm_free_func( 354bcc7b445SEric Sandeen unsigned long ino, 355bcc7b445SEric Sandeen void *data) 3562a82b8beSDavid Chinner { 357bcc7b445SEric Sandeen fstrm_item_t *item = (fstrm_item_t *)data; 3582a82b8beSDavid Chinner xfs_inode_t *ip = item->ip; 3592a82b8beSDavid Chinner int ref; 3602a82b8beSDavid Chinner 3612a82b8beSDavid Chinner ASSERT(ip->i_ino == ino); 3622a82b8beSDavid Chinner 3632a82b8beSDavid Chinner xfs_iflags_clear(ip, XFS_IFILESTREAM); 3642a82b8beSDavid Chinner 3652a82b8beSDavid Chinner /* Drop the reference taken on the AG when the item was added. */ 3662a82b8beSDavid Chinner ref = xfs_filestream_put_ag(ip->i_mount, item->ag); 3672a82b8beSDavid Chinner 3682a82b8beSDavid Chinner ASSERT(ref >= 0); 3692a82b8beSDavid Chinner TRACE_FREE(ip->i_mount, ip, item->pip, item->ag, 3702a82b8beSDavid Chinner xfs_filestream_peek_ag(ip->i_mount, item->ag)); 3712a82b8beSDavid Chinner 3722a82b8beSDavid Chinner /* 3732a82b8beSDavid Chinner * _xfs_filestream_update_ag() always takes a reference on the inode 3742a82b8beSDavid Chinner * itself, whether it's a file or a directory. Release it here. 3752a82b8beSDavid Chinner * This can result in the inode being freed and so we must 3762a82b8beSDavid Chinner * not hold any inode locks when freeing filesstreams objects 3772a82b8beSDavid Chinner * otherwise we can deadlock here. 3782a82b8beSDavid Chinner */ 3792a82b8beSDavid Chinner IRELE(ip); 3802a82b8beSDavid Chinner 3812a82b8beSDavid Chinner /* 3822a82b8beSDavid Chinner * In the case of a regular file, _xfs_filestream_update_ag() also 3832a82b8beSDavid Chinner * takes a ref on the parent inode to keep it in-core. Release that 3842a82b8beSDavid Chinner * too. 3852a82b8beSDavid Chinner */ 3862a82b8beSDavid Chinner if (item->pip) 3872a82b8beSDavid Chinner IRELE(item->pip); 3882a82b8beSDavid Chinner 3892a82b8beSDavid Chinner /* Finally, free the memory allocated for the item. */ 3902a82b8beSDavid Chinner kmem_zone_free(item_zone, item); 3912a82b8beSDavid Chinner } 3922a82b8beSDavid Chinner 3932a82b8beSDavid Chinner /* 3942a82b8beSDavid Chinner * xfs_filestream_init() is called at xfs initialisation time to set up the 3952a82b8beSDavid Chinner * memory zone that will be used for filestream data structure allocation. 3962a82b8beSDavid Chinner */ 3972a82b8beSDavid Chinner int 3982a82b8beSDavid Chinner xfs_filestream_init(void) 3992a82b8beSDavid Chinner { 4002a82b8beSDavid Chinner item_zone = kmem_zone_init(sizeof(fstrm_item_t), "fstrm_item"); 4019f8868ffSChristoph Hellwig if (!item_zone) 4029f8868ffSChristoph Hellwig return -ENOMEM; 4030b1b213fSChristoph Hellwig 4049f8868ffSChristoph Hellwig return 0; 4052a82b8beSDavid Chinner } 4062a82b8beSDavid Chinner 4072a82b8beSDavid Chinner /* 4082a82b8beSDavid Chinner * xfs_filestream_uninit() is called at xfs termination time to destroy the 4092a82b8beSDavid Chinner * memory zone that was used for filestream data structure allocation. 4102a82b8beSDavid Chinner */ 4112a82b8beSDavid Chinner void 4122a82b8beSDavid Chinner xfs_filestream_uninit(void) 4132a82b8beSDavid Chinner { 4142a82b8beSDavid Chinner kmem_zone_destroy(item_zone); 4152a82b8beSDavid Chinner } 4162a82b8beSDavid Chinner 4172a82b8beSDavid Chinner /* 4182a82b8beSDavid Chinner * xfs_filestream_mount() is called when a file system is mounted with the 4192a82b8beSDavid Chinner * filestream option. It is responsible for allocating the data structures 4202a82b8beSDavid Chinner * needed to track the new file system's file streams. 4212a82b8beSDavid Chinner */ 4222a82b8beSDavid Chinner int 4232a82b8beSDavid Chinner xfs_filestream_mount( 4242a82b8beSDavid Chinner xfs_mount_t *mp) 4252a82b8beSDavid Chinner { 4262a82b8beSDavid Chinner int err; 4272a82b8beSDavid Chinner unsigned int lifetime, grp_count; 4282a82b8beSDavid Chinner 4292a82b8beSDavid Chinner /* 4302a82b8beSDavid Chinner * The filestream timer tunable is currently fixed within the range of 4312a82b8beSDavid Chinner * one second to four minutes, with five seconds being the default. The 4322a82b8beSDavid Chinner * group count is somewhat arbitrary, but it'd be nice to adhere to the 4332a82b8beSDavid Chinner * timer tunable to within about 10 percent. This requires at least 10 4342a82b8beSDavid Chinner * groups. 4352a82b8beSDavid Chinner */ 4362a82b8beSDavid Chinner lifetime = xfs_fstrm_centisecs * 10; 4372a82b8beSDavid Chinner grp_count = 10; 4382a82b8beSDavid Chinner 4392a82b8beSDavid Chinner err = xfs_mru_cache_create(&mp->m_filestream, lifetime, grp_count, 440bcc7b445SEric Sandeen xfs_fstrm_free_func); 4412a82b8beSDavid Chinner 4422a82b8beSDavid Chinner return err; 4432a82b8beSDavid Chinner } 4442a82b8beSDavid Chinner 4452a82b8beSDavid Chinner /* 4462a82b8beSDavid Chinner * xfs_filestream_unmount() is called when a file system that was mounted with 4472a82b8beSDavid Chinner * the filestream option is unmounted. It drains the data structures created 4482a82b8beSDavid Chinner * to track the file system's file streams and frees all the memory that was 4492a82b8beSDavid Chinner * allocated. 4502a82b8beSDavid Chinner */ 4512a82b8beSDavid Chinner void 4522a82b8beSDavid Chinner xfs_filestream_unmount( 4532a82b8beSDavid Chinner xfs_mount_t *mp) 4542a82b8beSDavid Chinner { 4552a82b8beSDavid Chinner xfs_mru_cache_destroy(mp->m_filestream); 4562a82b8beSDavid Chinner } 4572a82b8beSDavid Chinner 4582a82b8beSDavid Chinner /* 4592a82b8beSDavid Chinner * If the mount point's m_perag array is going to be reallocated, all 4602a82b8beSDavid Chinner * outstanding cache entries must be flushed to avoid accessing reference count 4612a82b8beSDavid Chinner * addresses that have been freed. The call to xfs_filestream_flush() must be 4622a82b8beSDavid Chinner * made inside the block that holds the m_peraglock in write mode to do the 4632a82b8beSDavid Chinner * reallocation. 4642a82b8beSDavid Chinner */ 4652a82b8beSDavid Chinner void 4662a82b8beSDavid Chinner xfs_filestream_flush( 4672a82b8beSDavid Chinner xfs_mount_t *mp) 4682a82b8beSDavid Chinner { 46965de5567SDavid Chinner xfs_mru_cache_flush(mp->m_filestream); 4702a82b8beSDavid Chinner } 4712a82b8beSDavid Chinner 4722a82b8beSDavid Chinner /* 4732a82b8beSDavid Chinner * Return the AG of the filestream the file or directory belongs to, or 4742a82b8beSDavid Chinner * NULLAGNUMBER otherwise. 4752a82b8beSDavid Chinner */ 4762a82b8beSDavid Chinner xfs_agnumber_t 4772a82b8beSDavid Chinner xfs_filestream_lookup_ag( 4782a82b8beSDavid Chinner xfs_inode_t *ip) 4792a82b8beSDavid Chinner { 4802a82b8beSDavid Chinner xfs_mru_cache_t *cache; 4812a82b8beSDavid Chinner fstrm_item_t *item; 4822a82b8beSDavid Chinner xfs_agnumber_t ag; 4832a82b8beSDavid Chinner int ref; 4842a82b8beSDavid Chinner 4852a82b8beSDavid Chinner if (!(ip->i_d.di_mode & (S_IFREG | S_IFDIR))) { 4862a82b8beSDavid Chinner ASSERT(0); 4872a82b8beSDavid Chinner return NULLAGNUMBER; 4882a82b8beSDavid Chinner } 4892a82b8beSDavid Chinner 4902a82b8beSDavid Chinner cache = ip->i_mount->m_filestream; 4912a82b8beSDavid Chinner item = xfs_mru_cache_lookup(cache, ip->i_ino); 4922a82b8beSDavid Chinner if (!item) { 4932a82b8beSDavid Chinner TRACE_LOOKUP(ip->i_mount, ip, NULL, NULLAGNUMBER, 0); 4942a82b8beSDavid Chinner return NULLAGNUMBER; 4952a82b8beSDavid Chinner } 4962a82b8beSDavid Chinner 4972a82b8beSDavid Chinner ASSERT(ip == item->ip); 4982a82b8beSDavid Chinner ag = item->ag; 4992a82b8beSDavid Chinner ref = xfs_filestream_peek_ag(ip->i_mount, ag); 5002a82b8beSDavid Chinner xfs_mru_cache_done(cache); 5012a82b8beSDavid Chinner 5022a82b8beSDavid Chinner TRACE_LOOKUP(ip->i_mount, ip, item->pip, ag, ref); 5032a82b8beSDavid Chinner return ag; 5042a82b8beSDavid Chinner } 5052a82b8beSDavid Chinner 5062a82b8beSDavid Chinner /* 5072a82b8beSDavid Chinner * xfs_filestream_associate() should only be called to associate a regular file 5082a82b8beSDavid Chinner * with its parent directory. Calling it with a child directory isn't 5092a82b8beSDavid Chinner * appropriate because filestreams don't apply to entire directory hierarchies. 5102a82b8beSDavid Chinner * Creating a file in a child directory of an existing filestream directory 5112a82b8beSDavid Chinner * starts a new filestream with its own allocation group association. 5122a82b8beSDavid Chinner * 5132a82b8beSDavid Chinner * Returns < 0 on error, 0 if successful association occurred, > 0 if 5142a82b8beSDavid Chinner * we failed to get an association because of locking issues. 5152a82b8beSDavid Chinner */ 5162a82b8beSDavid Chinner int 5172a82b8beSDavid Chinner xfs_filestream_associate( 5182a82b8beSDavid Chinner xfs_inode_t *pip, 5192a82b8beSDavid Chinner xfs_inode_t *ip) 5202a82b8beSDavid Chinner { 5212a82b8beSDavid Chinner xfs_mount_t *mp; 5222a82b8beSDavid Chinner xfs_mru_cache_t *cache; 5232a82b8beSDavid Chinner fstrm_item_t *item; 5242a82b8beSDavid Chinner xfs_agnumber_t ag, rotorstep, startag; 5252a82b8beSDavid Chinner int err = 0; 5262a82b8beSDavid Chinner 5272a82b8beSDavid Chinner ASSERT(pip->i_d.di_mode & S_IFDIR); 5282a82b8beSDavid Chinner ASSERT(ip->i_d.di_mode & S_IFREG); 5292a82b8beSDavid Chinner if (!(pip->i_d.di_mode & S_IFDIR) || !(ip->i_d.di_mode & S_IFREG)) 5302a82b8beSDavid Chinner return -EINVAL; 5312a82b8beSDavid Chinner 5322a82b8beSDavid Chinner mp = pip->i_mount; 5332a82b8beSDavid Chinner cache = mp->m_filestream; 5342a82b8beSDavid Chinner down_read(&mp->m_peraglock); 5352a82b8beSDavid Chinner 5362a82b8beSDavid Chinner /* 5372a82b8beSDavid Chinner * We have a problem, Houston. 5382a82b8beSDavid Chinner * 5392a82b8beSDavid Chinner * Taking the iolock here violates inode locking order - we already 5402a82b8beSDavid Chinner * hold the ilock. Hence if we block getting this lock we may never 5412a82b8beSDavid Chinner * wake. Unfortunately, that means if we can't get the lock, we're 5422a82b8beSDavid Chinner * screwed in terms of getting a stream association - we can't spin 5432a82b8beSDavid Chinner * waiting for the lock because someone else is waiting on the lock we 5442a82b8beSDavid Chinner * hold and we cannot drop that as we are in a transaction here. 5452a82b8beSDavid Chinner * 546075fe102SChristoph Hellwig * Lucky for us, this inversion is not a problem because it's a 547075fe102SChristoph Hellwig * directory inode that we are trying to lock here. 5482a82b8beSDavid Chinner * 5492a82b8beSDavid Chinner * So, if we can't get the iolock without sleeping then just give up 5502a82b8beSDavid Chinner */ 5512a82b8beSDavid Chinner if (!xfs_ilock_nowait(pip, XFS_IOLOCK_EXCL)) { 5522a82b8beSDavid Chinner up_read(&mp->m_peraglock); 5532a82b8beSDavid Chinner return 1; 5542a82b8beSDavid Chinner } 5552a82b8beSDavid Chinner 5562a82b8beSDavid Chinner /* If the parent directory is already in the cache, use its AG. */ 5572a82b8beSDavid Chinner item = xfs_mru_cache_lookup(cache, pip->i_ino); 5582a82b8beSDavid Chinner if (item) { 5592a82b8beSDavid Chinner ASSERT(item->ip == pip); 5602a82b8beSDavid Chinner ag = item->ag; 5612a82b8beSDavid Chinner xfs_mru_cache_done(cache); 5622a82b8beSDavid Chinner 5632a82b8beSDavid Chinner TRACE_LOOKUP(mp, pip, pip, ag, xfs_filestream_peek_ag(mp, ag)); 5642a82b8beSDavid Chinner err = _xfs_filestream_update_ag(ip, pip, ag); 5652a82b8beSDavid Chinner 5662a82b8beSDavid Chinner goto exit; 5672a82b8beSDavid Chinner } 5682a82b8beSDavid Chinner 5692a82b8beSDavid Chinner /* 5702a82b8beSDavid Chinner * Set the starting AG using the rotor for inode32, otherwise 5712a82b8beSDavid Chinner * use the directory inode's AG. 5722a82b8beSDavid Chinner */ 5732a82b8beSDavid Chinner if (mp->m_flags & XFS_MOUNT_32BITINODES) { 5742a82b8beSDavid Chinner rotorstep = xfs_rotorstep; 5752a82b8beSDavid Chinner startag = (mp->m_agfrotor / rotorstep) % mp->m_sb.sb_agcount; 5762a82b8beSDavid Chinner mp->m_agfrotor = (mp->m_agfrotor + 1) % 5772a82b8beSDavid Chinner (mp->m_sb.sb_agcount * rotorstep); 5782a82b8beSDavid Chinner } else 5792a82b8beSDavid Chinner startag = XFS_INO_TO_AGNO(mp, pip->i_ino); 5802a82b8beSDavid Chinner 5812a82b8beSDavid Chinner /* Pick a new AG for the parent inode starting at startag. */ 5822a82b8beSDavid Chinner err = _xfs_filestream_pick_ag(mp, startag, &ag, 0, 0); 5832a82b8beSDavid Chinner if (err || ag == NULLAGNUMBER) 5842a82b8beSDavid Chinner goto exit_did_pick; 5852a82b8beSDavid Chinner 5862a82b8beSDavid Chinner /* Associate the parent inode with the AG. */ 5872a82b8beSDavid Chinner err = _xfs_filestream_update_ag(pip, NULL, ag); 5882a82b8beSDavid Chinner if (err) 5892a82b8beSDavid Chinner goto exit_did_pick; 5902a82b8beSDavid Chinner 5912a82b8beSDavid Chinner /* Associate the file inode with the AG. */ 5922a82b8beSDavid Chinner err = _xfs_filestream_update_ag(ip, pip, ag); 5932a82b8beSDavid Chinner if (err) 5942a82b8beSDavid Chinner goto exit_did_pick; 5952a82b8beSDavid Chinner 5962a82b8beSDavid Chinner TRACE_ASSOCIATE(mp, ip, pip, ag, xfs_filestream_peek_ag(mp, ag)); 5972a82b8beSDavid Chinner 5982a82b8beSDavid Chinner exit_did_pick: 5992a82b8beSDavid Chinner /* 6002a82b8beSDavid Chinner * If _xfs_filestream_pick_ag() returned a valid AG, remove the 6012a82b8beSDavid Chinner * reference it took on it, since the file and directory will have taken 6022a82b8beSDavid Chinner * their own now if they were successfully cached. 6032a82b8beSDavid Chinner */ 6042a82b8beSDavid Chinner if (ag != NULLAGNUMBER) 6052a82b8beSDavid Chinner xfs_filestream_put_ag(mp, ag); 6062a82b8beSDavid Chinner 6072a82b8beSDavid Chinner exit: 6082a82b8beSDavid Chinner xfs_iunlock(pip, XFS_IOLOCK_EXCL); 6092a82b8beSDavid Chinner up_read(&mp->m_peraglock); 6102a82b8beSDavid Chinner return -err; 6112a82b8beSDavid Chinner } 6122a82b8beSDavid Chinner 6132a82b8beSDavid Chinner /* 6142a82b8beSDavid Chinner * Pick a new allocation group for the current file and its file stream. This 6152a82b8beSDavid Chinner * function is called by xfs_bmap_filestreams() with the mount point's per-ag 6162a82b8beSDavid Chinner * lock held. 6172a82b8beSDavid Chinner */ 6182a82b8beSDavid Chinner int 6192a82b8beSDavid Chinner xfs_filestream_new_ag( 6202a82b8beSDavid Chinner xfs_bmalloca_t *ap, 6212a82b8beSDavid Chinner xfs_agnumber_t *agp) 6222a82b8beSDavid Chinner { 6232a82b8beSDavid Chinner int flags, err; 6242a82b8beSDavid Chinner xfs_inode_t *ip, *pip = NULL; 6252a82b8beSDavid Chinner xfs_mount_t *mp; 6262a82b8beSDavid Chinner xfs_mru_cache_t *cache; 6272a82b8beSDavid Chinner xfs_extlen_t minlen; 6282a82b8beSDavid Chinner fstrm_item_t *dir, *file; 6292a82b8beSDavid Chinner xfs_agnumber_t ag = NULLAGNUMBER; 6302a82b8beSDavid Chinner 6312a82b8beSDavid Chinner ip = ap->ip; 6322a82b8beSDavid Chinner mp = ip->i_mount; 6332a82b8beSDavid Chinner cache = mp->m_filestream; 6342a82b8beSDavid Chinner minlen = ap->alen; 6352a82b8beSDavid Chinner *agp = NULLAGNUMBER; 6362a82b8beSDavid Chinner 6372a82b8beSDavid Chinner /* 6382a82b8beSDavid Chinner * Look for the file in the cache, removing it if it's found. Doing 6392a82b8beSDavid Chinner * this allows it to be held across the dir lookup that follows. 6402a82b8beSDavid Chinner */ 6412a82b8beSDavid Chinner file = xfs_mru_cache_remove(cache, ip->i_ino); 6422a82b8beSDavid Chinner if (file) { 6432a82b8beSDavid Chinner ASSERT(ip == file->ip); 6442a82b8beSDavid Chinner 6452a82b8beSDavid Chinner /* Save the file's parent inode and old AG number for later. */ 6462a82b8beSDavid Chinner pip = file->pip; 6472a82b8beSDavid Chinner ag = file->ag; 6482a82b8beSDavid Chinner 6492a82b8beSDavid Chinner /* Look for the file's directory in the cache. */ 6502a82b8beSDavid Chinner dir = xfs_mru_cache_lookup(cache, pip->i_ino); 6512a82b8beSDavid Chinner if (dir) { 6522a82b8beSDavid Chinner ASSERT(pip == dir->ip); 6532a82b8beSDavid Chinner 6542a82b8beSDavid Chinner /* 6552a82b8beSDavid Chinner * If the directory has already moved on to a new AG, 6562a82b8beSDavid Chinner * use that AG as the new AG for the file. Don't 6572a82b8beSDavid Chinner * forget to twiddle the AG refcounts to match the 6582a82b8beSDavid Chinner * movement. 6592a82b8beSDavid Chinner */ 6602a82b8beSDavid Chinner if (dir->ag != file->ag) { 6612a82b8beSDavid Chinner xfs_filestream_put_ag(mp, file->ag); 6622a82b8beSDavid Chinner xfs_filestream_get_ag(mp, dir->ag); 6632a82b8beSDavid Chinner *agp = file->ag = dir->ag; 6642a82b8beSDavid Chinner } 6652a82b8beSDavid Chinner 6662a82b8beSDavid Chinner xfs_mru_cache_done(cache); 6672a82b8beSDavid Chinner } 6682a82b8beSDavid Chinner 6692a82b8beSDavid Chinner /* 6702a82b8beSDavid Chinner * Put the file back in the cache. If this fails, the free 6712a82b8beSDavid Chinner * function needs to be called to tidy up in the same way as if 6722a82b8beSDavid Chinner * the item had simply expired from the cache. 6732a82b8beSDavid Chinner */ 6742a82b8beSDavid Chinner err = xfs_mru_cache_insert(cache, ip->i_ino, file); 6752a82b8beSDavid Chinner if (err) { 6762a82b8beSDavid Chinner xfs_fstrm_free_func(ip->i_ino, file); 6772a82b8beSDavid Chinner return err; 6782a82b8beSDavid Chinner } 6792a82b8beSDavid Chinner 6802a82b8beSDavid Chinner /* 6812a82b8beSDavid Chinner * If the file's AG was moved to the directory's new AG, there's 6822a82b8beSDavid Chinner * nothing more to be done. 6832a82b8beSDavid Chinner */ 6842a82b8beSDavid Chinner if (*agp != NULLAGNUMBER) { 6852a82b8beSDavid Chinner TRACE_MOVEAG(mp, ip, pip, 6862a82b8beSDavid Chinner ag, xfs_filestream_peek_ag(mp, ag), 6872a82b8beSDavid Chinner *agp, xfs_filestream_peek_ag(mp, *agp)); 6882a82b8beSDavid Chinner return 0; 6892a82b8beSDavid Chinner } 6902a82b8beSDavid Chinner } 6912a82b8beSDavid Chinner 6922a82b8beSDavid Chinner /* 6932a82b8beSDavid Chinner * If the file's parent directory is known, take its iolock in exclusive 6942a82b8beSDavid Chinner * mode to prevent two sibling files from racing each other to migrate 6952a82b8beSDavid Chinner * themselves and their parent to different AGs. 6962a82b8beSDavid Chinner */ 6972a82b8beSDavid Chinner if (pip) 6982a82b8beSDavid Chinner xfs_ilock(pip, XFS_IOLOCK_EXCL); 6992a82b8beSDavid Chinner 7002a82b8beSDavid Chinner /* 7012a82b8beSDavid Chinner * A new AG needs to be found for the file. If the file's parent 7022a82b8beSDavid Chinner * directory is also known, it will be moved to the new AG as well to 7032a82b8beSDavid Chinner * ensure that files created inside it in future use the new AG. 7042a82b8beSDavid Chinner */ 7052a82b8beSDavid Chinner ag = (ag == NULLAGNUMBER) ? 0 : (ag + 1) % mp->m_sb.sb_agcount; 7062a82b8beSDavid Chinner flags = (ap->userdata ? XFS_PICK_USERDATA : 0) | 7072a82b8beSDavid Chinner (ap->low ? XFS_PICK_LOWSPACE : 0); 7082a82b8beSDavid Chinner 7092a82b8beSDavid Chinner err = _xfs_filestream_pick_ag(mp, ag, agp, flags, minlen); 7102a82b8beSDavid Chinner if (err || *agp == NULLAGNUMBER) 7112a82b8beSDavid Chinner goto exit; 7122a82b8beSDavid Chinner 7132a82b8beSDavid Chinner /* 7142a82b8beSDavid Chinner * If the file wasn't found in the file cache, then its parent directory 7152a82b8beSDavid Chinner * inode isn't known. For this to have happened, the file must either 7162a82b8beSDavid Chinner * be pre-existing, or it was created long enough ago that its cache 7172a82b8beSDavid Chinner * entry has expired. This isn't the sort of usage that the filestreams 7182a82b8beSDavid Chinner * allocator is trying to optimise, so there's no point trying to track 7192a82b8beSDavid Chinner * its new AG somehow in the filestream data structures. 7202a82b8beSDavid Chinner */ 7212a82b8beSDavid Chinner if (!pip) { 7222a82b8beSDavid Chinner TRACE_ORPHAN(mp, ip, *agp); 7232a82b8beSDavid Chinner goto exit; 7242a82b8beSDavid Chinner } 7252a82b8beSDavid Chinner 7262a82b8beSDavid Chinner /* Associate the parent inode with the AG. */ 7272a82b8beSDavid Chinner err = _xfs_filestream_update_ag(pip, NULL, *agp); 7282a82b8beSDavid Chinner if (err) 7292a82b8beSDavid Chinner goto exit; 7302a82b8beSDavid Chinner 7312a82b8beSDavid Chinner /* Associate the file inode with the AG. */ 7322a82b8beSDavid Chinner err = _xfs_filestream_update_ag(ip, pip, *agp); 7332a82b8beSDavid Chinner if (err) 7342a82b8beSDavid Chinner goto exit; 7352a82b8beSDavid Chinner 7362a82b8beSDavid Chinner TRACE_MOVEAG(mp, ip, pip, NULLAGNUMBER, 0, 7372a82b8beSDavid Chinner *agp, xfs_filestream_peek_ag(mp, *agp)); 7382a82b8beSDavid Chinner 7392a82b8beSDavid Chinner exit: 7402a82b8beSDavid Chinner /* 7412a82b8beSDavid Chinner * If _xfs_filestream_pick_ag() returned a valid AG, remove the 7422a82b8beSDavid Chinner * reference it took on it, since the file and directory will have taken 7432a82b8beSDavid Chinner * their own now if they were successfully cached. 7442a82b8beSDavid Chinner */ 7452a82b8beSDavid Chinner if (*agp != NULLAGNUMBER) 7462a82b8beSDavid Chinner xfs_filestream_put_ag(mp, *agp); 7472a82b8beSDavid Chinner else 7482a82b8beSDavid Chinner *agp = 0; 7492a82b8beSDavid Chinner 7502a82b8beSDavid Chinner if (pip) 7512a82b8beSDavid Chinner xfs_iunlock(pip, XFS_IOLOCK_EXCL); 7522a82b8beSDavid Chinner 7532a82b8beSDavid Chinner return err; 7542a82b8beSDavid Chinner } 7552a82b8beSDavid Chinner 7562a82b8beSDavid Chinner /* 7572a82b8beSDavid Chinner * Remove an association between an inode and a filestream object. 7582a82b8beSDavid Chinner * Typically this is done on last close of an unlinked file. 7592a82b8beSDavid Chinner */ 7602a82b8beSDavid Chinner void 7612a82b8beSDavid Chinner xfs_filestream_deassociate( 7622a82b8beSDavid Chinner xfs_inode_t *ip) 7632a82b8beSDavid Chinner { 7642a82b8beSDavid Chinner xfs_mru_cache_t *cache = ip->i_mount->m_filestream; 7652a82b8beSDavid Chinner 7662a82b8beSDavid Chinner xfs_mru_cache_delete(cache, ip->i_ino); 7672a82b8beSDavid Chinner } 768