When read items into the cache we have the range of keys that were
missing from the cache. The item walk was stopping when it hit the end
of the missing cache range, not when it hit the end of the keys that
were covered by all the segments.
This would manifest as huge regions of missing items. The read would
walk off the relatively closed end of the highest level segment. It
would keep reading while there were items in the upper levels but all
those keys that would have been found in additional lower level segments
are missing. Eventually it'd hit the end of the higher level sgement
and mark that region as cached.
With it fixed it now stops the read appropriately and will come around
next time to read the range that coveres the next lowest level segment.
Signed-off-by: Zach Brown <zab@versity.com>
We read into the item cache when we hit a region that isn't cached.
Inode index items are created without cached range coverage. It's easy
to trigger read attempts that overlap with dirty inode index items.
insert_item() had a bug where it's notion of overwriting only applied to
logical presence. It always let an insertion overwrite an existing item
if it was a deletion.
But that only makes sense for new item creation. Item cache population
can't do this. In this inode index case it can replace a correct dirty
inode index item with its old pre-deletion item from the read. This
clobbers the deletions and leaks the old inode index item versions.
So teach the item insertion for caching to never, ever, replace an
existing item. This fixes assertion failures from trying to
immediately walk meta seq items after creating a few thousand dirty
entries.
Signed-off-by: Zach Brown <zab@versity.com>
As we write ring blocks we need to update the first_seq to point at
the first live block in the ring.
The existing calculation gets it wrong and stores the seq of the first
block that we wrote in this commit, not the first ring block that
is still live and would need to be read.
Fix the calculation to so that we set first_seq to the first live block
in the ring.
This fixes the bug where a mount can spin printing the super it's using.
This is the server trying to constantly startup as each server start
fails as it can't read the ring.
Signed-off-by: Zach Brown <zab@versity.com>
As we write segments we're not limiting the number of segments they
intersect at the next level. Compactions are limited to a fanout's
worth of overlapping segments. This means that we can get a compaction
where the upper level segment overlapps more than the segments that are
part of the compaction. In this case we can't write the remaining upper
level items at the lower level because now we can have a level with
segments whose keys intersect.
Instead we detect this compaction case. We call it sticky because after
merging with the lower level segments the remaining items in the upper
level need to stick to the upper level. The next time compaction comes
around it'll compact the remaining items with the additional lower
overlaping segments.
Signed-off-by: Zach Brown <zab@versity.com>
Add a LRU and shrinker to reclaim old cached items under memory
pressure. This is pretty awful today because of the separate cached
range structs and rbtree. We do our best to blow away enough of the
cache and range to try and make progress.
Signed-off-by: Zach Brown <zab@versity.com>
Some item tracing functions were really just tracing a key. Refactor it
into a trace class with event users. Later patches can then use the key
trace class.
Signed-off-by: Zach Brown <zab@versity.com>
To actually use it, we first have to copy symbols over from the dlm build
into the scoutfs source directory. Make that happen automatically for us in
the Makefile.
The only users of locking at the moment are mount, unmount and xattr
read/write. Adding more locking calls should be a straight-forward endeavor.
The LVB based server ip communication didn't work out, and LVBS as they are
written don't make sense in a range locking world. So instead, we record the
server ip address in the superblock. This is protected by the listen lock,
which also arbitrates which node will be the manifest server.
We take and drop the dlm lock on each lock/unlock call. Lock caching will
come in a future patch.
Signed-off-by: Mark Fasheh <mfasheh@versity.com>
Using the new interval tree code we add a tree for each lock status list to
efficiently track ranged requests. Internally, most operations on a
resources lock status list (granted, waiting, converting) then are turned
into operations within a given range.
There is no API change other than a new call, dlm_lock_range() and a new
structure, 'struct dlm_key' to define our range endpoints. Keys can have
arbitrary lengths and are compared via memcmp. A ranged blocking ast type is
defined so that users of dlm_lock_range() can know which range they are
blocking.
A rudimentary test, dlmtest.ko is included.
TODO:
- Update userspace entry points, need to add one for new lock call
- Manage backwards compatibility with network protocol
Signed-off-by: Mark Fasheh <mfasheh@versity.com>
Users pass in a comparison function which is used when endpoints need to be
checked against each other. We also put each ITTYPE local definition on it's
own line to facilitate the use of pointers. An upcoming dlm patch will make
use of this to allow for keyed, ranged locking.
Signed-off-by: Mark Fasheh <mfasheh@versity.com>
Also wire it into the build system. We have to figure out how to get scoutfs
pulling in the right headers but that can wait until we have something more
usable.
Signed-off-by: Mark Fasheh <mfasheh@versity.com>
We've seen some cases where compaction writes a new segment that
contains items that aren't sorted. This eventually leads to read being
mislead in its binary search of the items in a segment and failing to
find the items it was looking for.
Signed-off-by: Zach Brown <zab@versity.com>
Writing dirty items into a segment wasn't protected by locking. It's
not racing with item dirtying, bit it's absolutely racing with reads
while modifying the rbtree. And shrinking will be modifying the item
cache at any old time in the future.
Signed-off-by: Zach Brown <zab@versity.com>
It was nice to watch the ring compare nodes so leave behind the trace
and clean up the callers so that uninitialized keys are cleanly null.
Signed-off-by: Zach Brown <zab@versity.com>
Add some percpu string buffers so that we can pass formatted strings as
arguments when printing keys. The percpu struct uses a different buffer
for each argument. We wrap the whole print call in a wrapper that
disables and enables preemption.
Signed-off-by: Zach Brown <zab@versity.com>
The scoutfs_ring_next() and _prev() functions had a really dumb bug
where they check the sign of comparisons by comparing with 1. For
example, next would miss that the walk traversed a lesser item
and wouldn't return the next item.
This was causing compaction to miss underlying segments, creating
segments in levels that had overlapping keys, which then totally
confused reading and kept it from finding the items it was looking for.
Signed-off-by: Zach Brown <zab@versity.com>
A copy and paste bug had us extending the length of keys that were
decremented at their previous length. The whole point of the _cur_len
functions is that they don't have to extend the key buf out to full
precision.
Signed-off-by: Zach Brown <zab@versity.com>
Add a helper that creates a socket and sets nodelay for all sockets and
set reuseaddr in listening sockets.
Signed-off-by: Zach Brown <zab@versity.com>
Restore the item_set_batch trace point by changing the current
insert_batch tracepoint to a class and defining insert and set as class
trace points.
Signed-off-by: Zach Brown <zab@versity.com>
It's pretty dangerous to forcefully remove items without writing
deletion items to lsm segments. This was only used for magical
ephemeral items when we were having them store file data.
Signed-off-by: Zach Brown <zab@versity.com>
Ephemeral items were only used by the page cache which tracked page
contents in items whose values pointed to the pages. Remove their
special case.
Signed-off-by: Zach Brown <zab@versity.com>
We had a simple mechanism for ensuring that transaction didn't create
more items than would fit in a single written segment. We calculated
the most dirty items that a holder could generate and assumed that all
holders dirtied that much.
This had two big problems.
The first was that it wasn't accounting for nested holds.
write_begin/end calls the generic inode dirtying path whild holding a
transaction. This ended up deadlocking as the dirty inode waited to be
able to write while its trans held back in write_begin prevented
writeout.
The second was that the worst case (full size xattr) item dirtying is
enormous and meaningfully restricts concurrent transaction holders.
With no currently dirty items you can have less than 16 full size xattr
writes. This concurrency limit only gets worse as the transaction fills
up with dirty items.
This fixes those problems. It adds precise accounting of the dirty
items that can be created while a transaction is held. These
reservations are tracked in journal_info so that they can be used by
nested holds. The precision allows much greater concurrency as
something like a create will try to reserve a few hundreds bytes instead
of 64k. Normal sized xattr operations won't try to reserve the largest
possible space.
We add some feedback from the item cache to the transaction to issue
warnings if a holder dirties more items than it reserved.
Now that we have precise item/key/value counts (segment space
consumption is a function of all three :/) we can't have a single atomic
track transaction holders. We add a long-overdue trans_info and put a
proper lock and fields there and much more clearly track transaction
serialization amongst the holders and writer.
Signed-off-by: Zach Brown <zab@versity.com>
When we turned existing items into deletion items we'd remove their
values. But we didn't update the count of dirty values to reflect that
removal so the dirty value count would slowly grow without bound.
Signed-off-by: Zach Brown <zab@versity.com>
For each transaction we send a message to to the server asking for a
unique sequence number to associate with the transaction. When we
change metadata or data of an inode we store the current transaction seq
in the inode and we index it with index items like the other inode
fields.
The server remembers the sequences it gives out. When we go to walk the
inode sequence indexes we ask the server for the largest stable seq and
limit results to that seq. This ensures that we never return seqs that
are past dirty items so never have inodes and seqs appear in the past.
Nodes use the sync timer to regularly cycle through seqs and ensure that
inode seq index walks don't get stuck on their otherwise idle seq.
Signed-off-by: Zach Brown <zab@versity.com>
Make sure that data is regularly synced. We switch to a delayed work
struct that is always queued with the sync deadline. If we need an
immediate sync we mod it to now.
Signed-off-by: Zach Brown <zab@versity.com>
This was mostly just a demonstration for how to add messages. We're
about to add a message that we always send on mount so this becomes
completely redundant.
Signed-off-by: Zach Brown <zab@versity.com>
When inserting a newly allocated segment we might find an existing
cached stale segment. We replace it in the cache so that its user can
keep using its stale contents while we work on the new segment.
Replacing doesn't clear the rb_node, though, so we trip over a warning
when we finally free the segment and it looks like it's still present in
the rb tree.
Clear the node after we replace it so that freeing sees a clear node and
doesn't issue a warning.
Signed-off-by: Zach Brown <zab@versity.com>
Add items for indexing inodes by their fields. When we update the inode
item we also delete the old index items and create the new items. We
rename and refactor the old inode since ioctl to now walk the inode
index items.
Signed-off-by: Zach Brown <zab@versity.com>
Now that we have basic file extents we can add a flag to extents to
track offline extents. We have to initialize and test the flags as we
work with extents. Truncation can be told to leave removed extents
around with no block mapping and the offline bit set. Only staging with
the correct data version can write to the offline regions. Demand
staging isn't implemented yet. Reads from offline extents are treated
like sparse regions.
Truncation is a straight forward iteration over the portions of existing
extents which overlap with the truncated blocks.
Writing to offline extents has to first remove the existing offline
extent before then adding the new allocated extents. The 'changes'
mechanism relied on being able to search the current items to find the
changes that should be made before making any changes. This doesn't
work for finding merge candidates for the new allocated insertion
because the old offline extent change won't have been applied yet. We
replace the change mechanism with straight forward item modification and
unwinding.
The generic block fiemap can't communicate offline extents and iterates
over blocks instead of extents. We add our fiemap that iterates
over extents and sets the 'UNKNOWN' flag on offline extents.
Signed-off-by: Zach Brown <zab@versity.com>
The offset comparison in release that was meant to catch wrapping was
inverted and accidentally prevented releasing a single block.
Signed-off-by: Zach Brown <zab@versity.com>
xattrs can have 0 lenth values so fix the item iterator to emit a single
item in the case where the size is 0.
Signed-off-by: Zach Brown <zab@versity.com>
kill_sb tries to sync before calling kill_block_super. It shouldn't do
this on mount errors that wouldn't have initialized the higher level
systems needed for syncing.
Signed-off-by: Zach Brown <zab@versity.com>
The ring node rb walker was returning an exact match for the search key
instead of the last node that was traversed. This stopped callers from
then iterating from the traversed node to find the next or previous
node.
Signed-off-by: Zach Brown <zab@versity.com>
Our first attempt at storing file data put them in items. This was easy
to implement but won't be acceptable in the long term. The cost of the
power of LSM indexing is compaction overhead. That's acceptable for
fine grained metadata but is totally unacceptable for bulk file data.
This switches to storing file data in seperate block allocations which
are referenced by extent items.
The bulk of the change is the mechanics of working with extents. We
have high level callers which add or remove logical extents and then
underlying mechanisms that insert, merge, or split the items that
the extents are stored in.
We have three types of extent items. The primary type maps logical file
regions to physical block extents. The next two store free extents
per-node so that clients don't create lock and LSM contention as they
try and allocate extents.
To fill those per-node free extents we add messages that communcate free
extents in the form of lists of segment allocations from the server.
We don't do any fancy multi-block allocation yet. We only allocate
blocks in get_blocks as writes find unmapped blocks. We do use some
per-task cursors to cache block allocation positions so that these
single block allocations are very likely to merge into larger extents as
tasks stream wites.
This is just the first chunk of the extent work that's coming. A later
patch adds offline flags and fixes up the change nonsense that seemed
like a good idea here.
The final moving part is that we initiate writeback on all newly
allocated extents before we commit the metadata that references the new
blocks. We do this with our own dirty inode tracking because the high
level vfs methods are unusably slow in some upstream kernels (they walk
all inodes, not just dirty inodes.)
Signed-off-by: Zach Brown <zab@versity.com>
The dirent name formatting code accidentally copied the calculation for
the length of the name from the xattrs, which are null terminated. The
durents are not, their length is just the value length minus the dirent
header.
Signed-off-by: Zach Brown <zab@versity.com>
The compaction code is still directly referencing the super block
and calling sync methods as though it was still standalone. This is
mostly OK because only the server runs it. But it isn't quite right
because the sync methods no longer make the rings persistent as they
write the item transaction. The server is in control of that now.
Eventually we'll have compaction messages being sent between the mount
clients and the server. Let's take a step in that direction by having
the compaction work call net methods to get its compaction parameters
and finish the compaction. Eventually these would be marshalled through
request/process/reply code.
But in this first step we know that the compaction code is running on
the server so we can forgo all the messaging and just call in to and out
of compaction. The net calls just holds the ring consistency locks in
the server and call into the manifest to do the work, commiting the
changes when its done.
This is more careful about segno alloction and freeing. Compaction
doesn't call the allocator directly. It gets allocaitons from the
messages and returns them if it doesn't use them. We actually now
free segnos as they're removed from the manifest.
With the server controlling compaction and can tear all the fiddly level
count watching code out of the manifest. Item transactions can't care
about the level counts and the server always tries compaction after the
manifest is updated intead of having the manifest watch the level counts
and call compaction.
Now that the server owns the rings they should not be torn down as the
super is torn down, net does that now. And we need to be more careful
to be sure that writes from dirtying and compaction are stable before
killing the super.
With all this in place moving to shared compaction involves adding the
messages and negotiating concurrent compactions in the manifest.
Signed-off-by: Zach Brown <zab@versity.com>
Inode info struct initialization spread out over three places:
- once for the memory of a slab obect
- when reading an existing inode from items
- when initializing a newly allocated inode
Over time field initializtion got out of sync with these rules. This
makes it more clear which fields get initialized where. In the inode
info struct we group fields by where there initialized. We order the
fields by size and location in the inode struct.
Then we make sure that all the initialization sites have everything
covered. Doing everything in consistent struct order makes it easier
to audit that we haven't missed anything.
What lead to this was realizing that we missed initializing the seqcount
when reading existing inodes. It should have been initialized in the
slab object constructor. The 'staging' boolean has the same problem.
Signed-off-by: Zach Brown <zab@versity.com>
Reviewed-by: Mark Fasheh <mfasheh@versity.com>
For consistency and to keep upstream users (scout-utils, etc) from
needing to include different type headers, we'll change the type to
match the rest of the header.
Signed-off-by: Nic Henke <nic.henke@versity.com>
Add support for more of the known xattr namespaces. This helps
generic/062 in xfstests pass.
Signed-off-by: Zach Brown <zab@versity.com>
Reviewed-by: Mark Fasheh <mfasheh@versity.com>
xfstests generic/062 was failing because it was getting an unexpected
error code when trying to work with xattrs on special files. Adding our
ops gives it the errnos it expects.
Signed-off-by: Zach Brown <zab@versity.com>
Reviewed-by: Mark Fasheh <mfasheh@versity.com>
We're currently excessively freaking out if inode updates fail. Let's
add a little more context to help us track down what goes wrong.
Signed-off-by: Zach Brown <zab@versity.com>
Reviewed-by: Mark Fasheh <mfasheh@versity.com>
We were duplicating the make args a few times so make a little ARGS
variable.
Default to the /lib/modules/$(uname -r) installed kernel source if
SK_KSRC isn't set.
And only try a sparse build that can fail if we can execute the sparse
command.
Signed-off-by: Zach Brown <zab@versity.com>
Reviewed-by: Mark Fasheh <mfasheh@versity.com>