Issue #3312 has been updated by dillon.

Definitely not a bug.  hammer2_chain_modify() must be called prior to any 
intent to modify content, instead of assuming that the chain is in a particular 
state due to some other procedure call (such as a hammer2_chain_create()).  It 
is very important that there be no confusion there, because modifying the 
content of a chain's data with the chain in the wrong state can cause chaos.  
Also, modifying the buffer cache without properly configuring the state of the 
underlying buffer can cause chaos.

What should happen here is that the newly allocated chain will usually have the 
HAMMER2_CHAIN_INITIAL flag set.  e.g. line 3421 of hammer2_chain.c.  In most 
cases.  This will cause hammer2_chain_modify() to issue hammer2_io_new() 
instead of hammer2_io_bread() in hammer2_chain_modify() -> 
hammer2_chain_load_data() around line 1205.  This will construct a dirty buffer 
with all zero's content instead of trying to read the block from disk.

There are numerous special cases here.   The buffer cache and the hammer2_dio 
cache use 64KB blocks.  If a hammer2_chain represents a smaller block, the 
bread must still be done because other portions of that block may be referenced 
by other parts of the filesystem.   If a 64KB chain is being allocated, though, 
it should be able to avoid the bread.

There is a further optimization in the allocator when allocating small blocks 
out of an initially fully-free 64KB block to also avoid the bread operation.  
This catches the remaining cases (typically chains with smaller block sizes 
than the natural 64KB block size).  That is around line 756 of 
hammer2_freemap.c.  Here, if allocating any block size out of an initially 
fully-free 64KB area, hammer2_io_newnz() is called to create a dirty pre-zero'd 
backing for that in the DIO and buffer cache (which avoids having to do a 
bread()).

The underlying buffer cache buffer will remain dirty for some time, usually 
tens of seconds before being flushed, and thus be able to catch most bulk 
operations without forcing a bread().

-Matt

----------------------------------------
Submit #3312: hammer2: redundant chain modify after chain creation
http://bugs.dragonflybsd.org/issues/3312#change-14256

* Author: tkusumi
* Status: New
* Priority: Normal
* Target version: 6.4
* Start date: 2022-02-05
----------------------------------------
It looks to me hammer2_chain_modify() calls right after hammer2_chain_create() 
in creat/mkdir/etc syscalls are all redundant.

Take a look at hammer2_xop_inode_create_det() for example. A caller process 
always passes NULL chain to hammer2_chain_create(), so it always allocates a 
new chain and then calls hammer2_chain_modify() which only initializes a new 
buf for devvp.

After successful hammer2_chain_create(), a caller explicitly calls 
hammer2_chain_modify() for the second time. The chain is already modified + has 
non-zero data_off, so it breads data for buf from above (which I believe 
contains garbage at this point).

After that a caller copies ondisk inode to its chain data which is a pointer to 
somewhere in devvp's buf data. The flusher eventually recursively flushes chain 
data to devvp along with flushing vp's buf data (user data) to devvp.

What I don't understand is why the second hammer2_chain_modify() is needed. The 
first one called from hammer2_chain_create() seems good enough in these cases.

In fact I've had no issue with this diff to test above.
https://leaf.dragonflybsd.org/~tkusumi/diff/0001-hammer2-omit-redundant-chain-modify-after-creation.patch



-- 
You have received this notification because you have either subscribed to it, 
or are involved in it.
To change your notification preferences, please click here: 
http://bugs.dragonflybsd.org/my/account

Reply via email to