int xfs_filestream_associate( xfs_inode_t *pip, xfs_inode_t *ip) { xfs_mount_t *mp; xfs_mru_cache_t *cache; fstrm_item_t *item; xfs_agnumber_t ag, rotorstep, startag; int err = 0; ASSERT(pip->i_d.di_mode & S_IFDIR); ASSERT(ip->i_d.di_mode & S_IFREG); if (!(pip->i_d.di_mode & S_IFDIR) || !(ip->i_d.di_mode & S_IFREG)) return -EINVAL; mp = pip->i_mount; cache = mp->m_filestream; /* * We have a problem, Houston. * * Taking the iolock here violates inode locking order - we already * hold the ilock. Hence if we block getting this lock we may never * wake. Unfortunately, that means if we can't get the lock, we're * screwed in terms of getting a stream association - we can't spin * waiting for the lock because someone else is waiting on the lock we * hold and we cannot drop that as we are in a transaction here. * * Lucky for us, this inversion is not a problem because it's a * directory inode that we are trying to lock here. * * So, if we can't get the iolock without sleeping then just give up */ if (!xfs_ilock_nowait(pip, XFS_IOLOCK_EXCL)) return 1; /* If the parent directory is already in the cache, use its AG. */ item = xfs_mru_cache_lookup(cache, pip->i_ino); if (item) { ASSERT(item->ip == pip); ag = item->ag; xfs_mru_cache_done(cache); TRACE_LOOKUP(mp, pip, pip, ag, xfs_filestream_peek_ag(mp, ag)); err = _xfs_filestream_update_ag(ip, pip, ag); goto exit; } /* * Set the starting AG using the rotor for inode32, otherwise * use the directory inode's AG. */ if (mp->m_flags & XFS_MOUNT_32BITINODES) { rotorstep = xfs_rotorstep; startag = (mp->m_agfrotor / rotorstep) % mp->m_sb.sb_agcount; mp->m_agfrotor = (mp->m_agfrotor + 1) % (mp->m_sb.sb_agcount * rotorstep); } else startag = XFS_INO_TO_AGNO(mp, pip->i_ino); /* Pick a new AG for the parent inode starting at startag. */ err = _xfs_filestream_pick_ag(mp, startag, &ag, 0, 0); if (err || ag == NULLAGNUMBER) goto exit_did_pick; /* Associate the parent inode with the AG. */ err = _xfs_filestream_update_ag(pip, NULL, ag); if (err) goto exit_did_pick; /* Associate the file inode with the AG. */ err = _xfs_filestream_update_ag(ip, pip, ag); if (err) goto exit_did_pick; TRACE_ASSOCIATE(mp, ip, pip, ag, xfs_filestream_peek_ag(mp, ag)); exit_did_pick: /* * If _xfs_filestream_pick_ag() returned a valid AG, remove the * reference it took on it, since the file and directory will have taken * their own now if they were successfully cached. */ if (ag != NULLAGNUMBER) xfs_filestream_put_ag(mp, ag); exit: xfs_iunlock(pip, XFS_IOLOCK_EXCL); return -err; }
int xfs_filestream_associate( xfs_inode_t *pip, xfs_inode_t *ip) { xfs_mount_t *mp; xfs_mru_cache_t *cache; fstrm_item_t *item; xfs_agnumber_t ag, rotorstep, startag; int err = 0; ASSERT(S_ISDIR(pip->i_d.di_mode)); ASSERT(S_ISREG(ip->i_d.di_mode)); if (!S_ISDIR(pip->i_d.di_mode) || !S_ISREG(ip->i_d.di_mode)) return -EINVAL; mp = pip->i_mount; cache = mp->m_filestream; if (!xfs_ilock_nowait(pip, XFS_IOLOCK_EXCL)) return 1; item = xfs_mru_cache_lookup(cache, pip->i_ino); if (item) { ASSERT(item->ip == pip); ag = item->ag; xfs_mru_cache_done(cache); TRACE_LOOKUP(mp, pip, pip, ag, xfs_filestream_peek_ag(mp, ag)); err = _xfs_filestream_update_ag(ip, pip, ag); goto exit; } if (mp->m_flags & XFS_MOUNT_32BITINODES) { rotorstep = xfs_rotorstep; startag = (mp->m_agfrotor / rotorstep) % mp->m_sb.sb_agcount; mp->m_agfrotor = (mp->m_agfrotor + 1) % (mp->m_sb.sb_agcount * rotorstep); } else startag = XFS_INO_TO_AGNO(mp, pip->i_ino); err = _xfs_filestream_pick_ag(mp, startag, &ag, 0, 0); if (err || ag == NULLAGNUMBER) goto exit_did_pick; err = _xfs_filestream_update_ag(pip, NULL, ag); if (err) goto exit_did_pick; err = _xfs_filestream_update_ag(ip, pip, ag); if (err) goto exit_did_pick; TRACE_ASSOCIATE(mp, ip, pip, ag, xfs_filestream_peek_ag(mp, ag)); exit_did_pick: if (ag != NULLAGNUMBER) xfs_filestream_put_ag(mp, ag); exit: xfs_iunlock(pip, XFS_IOLOCK_EXCL); return -err; }