On Wed, 2017-03-29 at 19:06 +0000, George Kondiles wrote:
I am attempting to use the SPDK blob store to implement a basic NVMe-
based flat file store. I understand that this is a new addition to
the SPDK that is under active development and that
documentation/examples of usage are sparse. But this is a great new
addition to the SPDK that I've been tracking and so I'm eager to
begin using it.
I'm glad you're using it! Note that this is not even part of an
official release yet. Further, the API we're going to release as part
of SPDK 17.03 is not even the API that I envision the blobstore will
have when all is said and done. I just want to correctly set
expectations - I'm going to change the API quite a bit and not
everything in the API currently makes sense. I also reserve the right
to change the on-disk format still, for at least a few more months.
Feedback of any kind is very much welcome.
With that being said, I've been scouring through its usage in the
bdev component, as well as the test cases in an attempt to glean how
I might integrate it into my code base (specifically, I am already
successfully using the SPDK to interact with NVMe devices) but have a
few high-level questions that I hope are easy to answer.
1) In the most basic usage, it seems IO channels should be 1-to-1
with threads. It looks like I must start a thread,
call spdk_allocate_thread(), then spdk_get_io_channel() to get the
spdk_io_channel instance created and associated with that thread.
You'll always need to call spdk_allocate_thread when each new thread
starts up that is using the blobstore (unless you are using our event
framework from lib/event, which does that for you). If you want the
blobstore to talk to the bdev layer, then you'll want to call
spdk_get_io_channel and pass it the bdev as the io_device parameter.
There is a full example of how to do this in lib/blob/bdev/blob_bdev.c.
I highly recommend for this first version that you just follow that
If you want the blobstore to talk directly to the NVMe driver, however,
I haven't written an example to show you how just yet. I think the
easiest way to implement spdk_bs_dev::create_channel directly on the
NVMe driver is to make it call spdk_nvme_ctrlr_alloc_io_qpair and then
return (and cast) the queue pair to an spdk_io_channel object. That's
cheating a bit but I think it will work out. I'll try to write up an
example that demonstrates the best way to do this in the next week or
two. There are some other challenges here, such as who polls each queue
pair for completions, that using the bdev layer just solves for you.
Since spdk_bs_dev.create_channel is synchronous, it looks like I must
block the create_channel() call while the above is happening in the
new IO thread. Is this a reasonable approach, or am I misinterpreting
how IO channels are intended to work?
The spdk_bs_dev::create_channel function will only be called on the
thread that will be using that channel. That thread should have already
been set up with the spdk_allocate_thread when it started, but you can
just call spdk_get_io_channel from within the create_channel callback.
See lib/blob/bdev/blob_bdev.c for an example that you can probably just
2) I've already got a set of IO threads for executing asynchronous
NVMe operations (e.g. spdk_nvme_ns_cmd_read(...)) against one or more
devices. These IO threads each own a set of NVMe queue pairs, and
have queuing mechanisms allowing for the submission of work to be
performed against a specific device. Given this, I am interpreting an
IO channel to essentially be an additional "outer" queue of pending
blob-IO operations that are processed by an additional, dedicated
thread. A call to spdk_bs_dev.read() or .write() would find the
correct IO channel thread, enqueue an "outer" blob op, and the
channel IO thread would then enqueue one or more lower-level NVMe IO
operations on the "inner" queue. Does this interpretation match the
intended usage? Am I missing something?
I think you're on the right track here. Our spdk_io_channel structure
is just a software construct for tracking per-thread contexts up and
down the I/O stack. The bottom of that stack is an NVMe queue pair
typically. This idea is a powerful one, but one that we haven't done a
great job explaining just yet. It is a dramatic departure from concepts
present in POSIX too, so it will be unfamiliar to most people.
3) spdk_bs_dev.unmap() appears to correspond to dealloc/TRIM. Is this
Yes. SATA calls it TRIM. NVMe calls it dealloc. SCSI calls it UNMAP.
Maybe I should call it dealloc because that's actually the most
descriptive term and we're very NVMe-centric. Of the three terms, I'm
sure that's the one least used though.
4) I've read through the docs at http://www.spdk.io/doc/blob.html and
understand at a high level how things are being stored on disk, but
there are references to the caching of metadata. My current workload
will likely generate on the order of 100K to 1M blobs of sizes
ranging from 512KB to 32MB, each with a couple of small attributes.
Is there any way to estimate the total size (in memory) of the cache?
Also, are any metadata modifications O(n) in the number of blobs?
Blob metadata is cached, but only while a blob is open. If you close
the blob all of the memory is released. I don't have exact counts (and
it is very much subject to change), but you can expect maybe ~128B per
open blob. There are a few operations (i.e. opening a blob) that are
currently O(N) where N is the number of OPEN blobs. This is just
because I haven't had a chance to implement a better algorithm yet.
There aren't any operations that are O(N) where N is the total number
of blobs. In general, blobs are entirely independent of one another
because they each have their own blocks for metadata and data and the
location of that metadata can be determined entirely from the blobid
with no shared data structure. That's the real key to this design -
with the exception of a bit mask that requires central coordination for
a brief, synchronous period only when doing a few rare metadata
operations (create, sync, delete), every other operation on the
blobstore can happen entirely in parallel with no locks.
Thanks in advance for any help or insight anyone can provide. Any
assistance is greatly appreciated.
- George Kondiles
SPDK mailing list