On Thu, Jul 09, 2020 at 08:55:26AM +0800, Gao Xiang wrote: > On Thu, Jul 09, 2020 at 09:33:11AM +1000, Dave Chinner wrote: > > On Tue, Jul 07, 2020 at 09:57:41PM +0800, Gao Xiang wrote: > > > Currently, we use AGI buffer lock to protect in-memory linked list for > > > unlinked inodes but since it's not necessary to modify AGI unless the > > > head of the unlinked list is modified. So let's removing the AGI buffer > > > modification dependency if possible, including 1) adding another per-AG > > > dedicated lock to protect the whole list and 2) inserting unlinked > > > inodes from tail. > > > > > > For 2), the tail of bucket 0 is now recorded in perag for xfs_iunlink() > > > to use. xfs_iunlink_remove() still support old multiple short bucket > > > lists for recovery code. > > > > I would split this into two separate patches. One to move to a perag > > based locking strategy, another to change from head to tail > > addition as they are largely independent algorithmic changes. > > Yes, that is much better from the perspective of spilting patches and > I thought that before. It seems that is like 2 steps but the proposed > target solution is as a whole (in other words, 2 steps are code-related) > and I'm not sure how large these code is sharable or can be inherited > but rather than introduce some code in patch 2 and then remove immediately > and turn into a new code in patch 3). I'm not sure how large logic could > be sharable between these 2 dependent steps so I didn't do that. > > I will spilt patches in the next RFC version to make a try. Thanks! [snip] > > i.e. this: > > > > xfs_iunlink() > > { > > > > get locks > > do list insert > > drop locks > > } > > > > Is better for understanding, maintenance and future modification > > than: > > > > xfs_iunlink() > > { > > > > get perag > > lock perag > > look at tail of list > > if (empty) { > > unlock perag > > read/lock AGI > > lock perag > > look at tail of list > > if (empty) > > do head insert > > goto out > > } > > do tail insert > > out: > > update inode/pag tails > > unlock > > drop perag > > } > > > > It's trivial for a reader to understand what the first version of > > xfs_iunlink() is going to do without needing to understand the > > intraccies of the locking strategies. However, it takes time and > > effort to undestand exactly waht the second one is doing because > > it's not clear where lock ends and list modifications start, nor > > what the locking rules are for the different modifications that are > > being made. Essentially, it goes back to the complex > > locking-intertwined-with-modification-algorithm problem the current > > TOT code has. > > > > I'd much prefer to see something like this: > > > > /* > > * Inode allocation in the O_TMPFILE path defines the AGI/unlinked > > * list lock order as being AGI->perag unlinked list lock. We are > > * inverting it here as the fast path tail addition does not need to > > * modify the AGI at all. Hence we only need the AGI lock if the > > * tail is empty, but if we fail to get it without blocking then we > > * need to fall back to the slower, correct lock order. > > */ > > xfs_iunlink_insert_lock() > > { > > get perag; > > lock_perag(); > > if (!tail empty) > > return; > > if (trylock AGI) > > return; > > (adding some notes here, this patch doesn't use try lock here > finally but unlock perag and take AGI and relock and recheck tail_empty.... > since the tail non-empty is rare...) *nod* My point was largely that this sort of thing is really obvious and easy to optimise once the locking is cleanly separated. Adding a trylock rather than drop/relock is another patch for the series :P Cheers, Dave. -- Dave Chinner david@xxxxxxxxxxxxx