In terms of overhead for the interface, is your concern due to the potential for more heap allocations for storing tasks to complete the future? If that's the case, Adam's std::function replacement [1], which allocates a configurable amount of storage on the stack (as opposed to going to the heap for functions larger than 16 bytes as in the GCC implementation [2]), might be a good compromise.
I had some time to deep-dive through the seastar library this week and was really impressed by its design. I know we (semi-)recently needed to introduce a workaround change to librbd which now results in a context switch for each AIO op -- all to ensure that we never block the QEMU IO thread. Long term, I would love if we could take some of these reactive programming concepts to avoid all potential blocking on the librbd / librados IO path (with a side benefit of perhaps eliminating the potential for thousands of network IO threads by instead sharding the work). [1] http://comments.gmane.org/gmane.comp.file-systems.ceph.devel/27100 [2] http://www.drdobbs.com/cpp/efficient-use-of-lambda-expressions-and/232500059?pgno=2 -- Jason Dillaman ----- Original Message ----- > From: "Samuel Just" <sj...@redhat.com> > To: "ceph-devel" <ceph-devel@vger.kernel.org> > Cc: "Milosz Tanski" <mil...@adfin.com>, "Matt Benjamin" > <mbenja...@redhat.com>, "Haomai Wang" <haomaiw...@gmail.com>, > "Yehuda Sadeh-Weinraub" <ysade...@redhat.com>, "Sage Weil" > <s...@newdream.net>, "Adam Emerson" > <aemer...@redhat.com>, "Stephen L Blinick" <stephen.l.blin...@intel.com> > Sent: Wednesday, October 28, 2015 4:15:11 PM > Subject: Re: Async reads, sync writes, op thread model discussion > > Sorry for the long delay since my last update, been working mostly on > infernalis stabilization :) > > https://github.com/athanatos/ceph/tree/wip-do-op-2015-10-28 > > has my current branch. > > I've changed direction a bit, a future based interface seems like it > will provide the most tolerable interface for sequencing operations on > possibly async objectstore results. I've got an early version of a > common/Future.h fleshed out (very different goals from the C++ futures > library, I'm afraid -- based heavily on the seastar library) and a > commit which converts get_object_context to use the interface. > > One of the main questions for me is whether we can get the overhead of > the Future interface and operation sequencing low enough to be > tolerable for fast synchronous backends. I'm still working on trying > to get at least a simple read path converted to use async objectstore > operations so we can test the overhead and the performance improvement > for an async backend. > -Sam > > On Fri, Aug 28, 2015 at 2:25 PM, Samuel Just <sj...@redhat.com> wrote: > > Oh, yeah, we'll definitely test for correctness for async reads on > > filestore, I'm just worried about validating the performance > > assumptions. The 3700s might be just fine for that validation though. > > -Sam > > > > On Fri, Aug 28, 2015 at 1:01 PM, Blinick, Stephen L > > <stephen.l.blin...@intel.com> wrote: > >> This sounds ok, with the synchronous interface still possible to the > >> ObjectStore based on return code. > >> > >> I'd think that the async read interface can be evaluated with any > >> hardware, at least for correctness, by observing the queue depth to the > >> device during a test run. Also, I think asynchronous reads may benefit > >> various types of NAND SSD's as they do better with more parallelism and I > >> typically see very low queuedepth to them today with Filestore (one of > >> the reasons I think doubling up OSD's on a single flash device helps > >> benchmarks). > >> > >> Thanks, > >> > >> Stephen > >> > >> > >> > >> -----Original Message----- > >> From: ceph-devel-ow...@vger.kernel.org > >> [mailto:ceph-devel-ow...@vger.kernel.org] On Behalf Of Samuel Just > >> Sent: Thursday, August 27, 2015 4:22 PM > >> To: Milosz Tanski > >> Cc: Matt Benjamin; Haomai Wang; Yehuda Sadeh-Weinraub; Sage Weil; > >> ceph-devel > >> Subject: Re: Async reads, sync writes, op thread model discussion > >> > >> It's been a couple of weeks, so I thought I'd send out a short progress > >> update. I've started by trying to nail down enough of the threading > >> design/async interface to start refactoring do_op. For the moment, I've > >> backtracked on the token approach mostly because it seemed more > >> complicated than necessary. I'm thinking we'll keep a callback like > >> mechanism, but move responsibility for queuing and execution back to the > >> interface user by allowing the user to pass a completion queue and an > >> uninterpreted completion pointer. These two commits have the gist of the > >> direction I'm going in (the actual code is more a place holder today). > >> An OSDReactor instance will replace each of the "shards" in the current > >> sharded work queue. Any aio initiated by a pg operation from a reactor > >> will pass that reactor's queue, ensuring that the completion winds up > >> back in the same thread. > >> Writes would work pretty much the same way, but with two callbacks. > >> > >> My plan is to flesh this out to the point where the OSD works again, and > >> then refactor the osd write path to use this mechanism for basic rbd > >> writes. That should be enough to let us evaluate whether this is a good > >> path forward for async writes. Async reads may be a bit tricky to > >> evaluate. It seems like we'd need hardware that needs that kind of queue > >> depth and an objectstore implementation which can exploit it. > >> I'll wire up filestore to do async reads optionally for testing purposes, > >> but it's not clear to me that there will be cases where filestore would > >> want to do an async read rather than a sync read. > >> > >> https://github.com/athanatos/ceph/commit/642b7190d70a5970534b911f929e6e3885bf99c4 > >> https://github.com/athanatos/ceph/commit/42bee815081a91abd003bf7170ef1270f23222f6 > >> -Sam > >> > >> On Fri, Aug 14, 2015 at 3:36 PM, Milosz Tanski <mil...@adfin.com> wrote: > >>> On Fri, Aug 14, 2015 at 5:19 PM, Matt Benjamin <mbenja...@redhat.com> > >>> wrote: > >>>> Hi, > >>>> > >>>> I tend to agree with your comments regarding swapcontext/fibers. I am > >>>> not much more enamored of jumping to new models (new! frameworks!) as a > >>>> single jump, either. > >>> > >>> Not suggesting the libraries/frameworks. Just brining up promises as > >>> an alternative technique to coroutines. Dealing with spaghetti > >>> evented/callback code gets old after doing it for 10+ years. Then > >>> throw in blocking IO. > >>> > >>> And FYI, the data flow promises go back in comp sci back to the 80s. > >>> > >>> Cheers, > >>> - Milosz > >>> > >>>> > >>>> I like the way I interpreted Sam's design to be going, and in > >>>> particular, that it seems to allow for consistent handling of read, > >>>> write transactions. I also would like to see how Yehuda's system works > >>>> before arguing generalities. > >>>> > >>>> My intuition is, since the goal is more deterministic performance in > >>>> a short horizion, you > >>>> > >>>> a. need to prioritize transparency over novel abstractions b. need to > >>>> build solid microbenchmarks that encapsulate small, then larger > >>>> pieces of the work pipeline > >>>> > >>>> My .05. > >>>> > >>>> Matt > >>>> > >>>> -- > >>>> Matt Benjamin > >>>> Red Hat, Inc. > >>>> 315 West Huron Street, Suite 140A > >>>> Ann Arbor, Michigan 48103 > >>>> > >>>> http://www.redhat.com/en/technologies/storage > >>>> > >>>> tel. 734-761-4689 > >>>> fax. 734-769-8938 > >>>> cel. 734-216-5309 > >>>> > >>>> ----- Original Message ----- > >>>>> From: "Milosz Tanski" <mil...@adfin.com> > >>>>> To: "Haomai Wang" <haomaiw...@gmail.com> > >>>>> Cc: "Yehuda Sadeh-Weinraub" <ysade...@redhat.com>, "Samuel Just" > >>>>> <sj...@redhat.com>, "Sage Weil" <s...@newdream.net>, > >>>>> ceph-devel@vger.kernel.org > >>>>> Sent: Friday, August 14, 2015 4:56:26 PM > >>>>> Subject: Re: Async reads, sync writes, op thread model discussion > >>>>> > >>>>> On Tue, Aug 11, 2015 at 10:50 PM, Haomai Wang <haomaiw...@gmail.com> > >>>>> wrote: > >>>>> > On Wed, Aug 12, 2015 at 6:34 AM, Yehuda Sadeh-Weinraub > >>>>> > <ysade...@redhat.com> wrote: > >>>>> >> Already mentioned it on irc, adding to ceph-devel for the sake of > >>>>> >> completeness. I did some infrastructure work for rgw and it seems > >>>>> >> (at least to me) that it could at least be partially useful here. > >>>>> >> Basically it's an async execution framework that utilizes > >>>>> >> coroutines. > >>>>> >> It's comprised of aio notification manager that can also be tied > >>>>> >> into coroutines execution. The coroutines themselves are > >>>>> >> stackless, they are implemented as state machines, but using some > >>>>> >> boost trickery to hide the details so they can be written very > >>>>> >> similar to blocking methods. Coroutines can also execute other > >>>>> >> coroutines and can be stacked, or can generate concurrent > >>>>> >> execution. It's still somewhat in flux, but I think it's mostly > >>>>> >> done and already useful at this point, so if there's anything you > >>>>> >> could use it might be a good idea to avoid effort duplication. > >>>>> >> > >>>>> > > >>>>> > coroutines like qemu is cool. The only thing I afraid is the > >>>>> > complicate of debug and it's really a big task :-( > >>>>> > > >>>>> > I agree with sage that this design is really a new implementation > >>>>> > for objectstore so that it's harmful to existing objectstore impl. > >>>>> > I also suffer the pain from sync read xattr, we may add a async > >>>>> > read interface to solove this? > >>>>> > > >>>>> > For context switch thing, now we have at least 3 cs for one op at > >>>>> > osd side. messenger -> op queue -> objectstore queue. I guess op > >>>>> > queue -> objectstore is easier to kick off just as sam said. We > >>>>> > can make write journal inline with queue_transaction, so the > >>>>> > caller could directly handle the transaction right now. > >>>>> > >>>>> I would caution agains coroutines (fibers) esp. in a multi-threaded > >>>>> environment. Posix has officially obsoleted the swapcontext family > >>>>> of functions in 1003.1-2004 and removed it in 1003.1-2008. That's > >>>>> because they were notoriously non portable, and buggy. And yes you > >>>>> can use something like boost::context / boost::coroutine instead but > >>>>> they also have platform limitations. These implementations tend to > >>>>> abuse / turn of various platform scrutiny features (like the one for > >>>>> setjmp/longjmp). And on top of that many platforms don't consider > >>>>> alternative context so you end up with obscure bugs. I've debugged > >>>>> my fair share of bugs in Mordor coroutines with C++ exceptions, and > >>>>> errno variables (since errno is really a function on linux and it's > >>>>> output a pointer to threads errno is marked pure) if your coroutine > >>>>> migrates threads. And you need to migrate them because of blocking > >>>>> and uneven processor/thread distribution. > >>>>> > >>>>> None of these are obstacles that can't be solved, but added together > >>>>> they become a pretty long term liability. So I think long and hard > >>>>> about it. Qemu doesn't have some of those issues because it's uses a > >>>>> single thread and a much simpler C ABI that it deals with. > >>>>> > >>>>> An alternative to coroutines that goes a long way towards solving > >>>>> the callback spaghetti problem is futures/promises. I'm not talking > >>>>> of the very future model that exists in C++11 library but more along > >>>>> the lines that exist in other languages (like what's being done in > >>>>> Javascript today). There's a good implementation of it Folly (the > >>>>> facebook c++11 library). They have a very nice piece of > >>>>> documentation here to understand how they work and how they differ. > >>>>> > >>>>> That future model is very handy when dealing with the callback > >>>>> control flow problem. You can chain a bunch of processing steps that > >>>>> requires some async action, return a future and continue so on and so > >>>>> forth. > >>>>> Also, it makes handling complex error cases easy by giving you a way > >>>>> to skip lots of processing steps strait to onError at the end of the > >>>>> chain. > >>>>> > >>>>> Take a look at folly. Take a look at the expanded boost futures > >>>>> (they call this future continuations: > >>>>> http://www.boost.org/doc/libs/1_54_0/doc/html/thread/synchronization > >>>>> .html#thread.synchronization.futures.then > >>>>> ). Also, building a cut down future framework just for Ceph (or > >>>>> reduced set folly one) might be another option. > >>>>> > >>>>> Just an alternative. > >>>>> > >>>>> > > >>>>> > Anyway, I think we need to do some changes for this field. > >>>>> > > >>>>> >> Yehuda > >>>>> >> > >>>>> >> On Tue, Aug 11, 2015 at 3:19 PM, Samuel Just <sj...@redhat.com> > >>>>> >> wrote: > >>>>> >>> Yeah, I'm perfectly happy to have wrappers. I'm also not at all > >>>>> >>> tied to the actual interface I presented so much as the notion > >>>>> >>> that the next thing to do is restructure the OpWQ users as async > >>>>> >>> state machines. > >>>>> >>> -Sam > >>>>> >>> > >>>>> >>> On Tue, Aug 11, 2015 at 1:05 PM, Sage Weil <s...@newdream.net> > >>>>> >>> wrote: > >>>>> >>>> On Tue, 11 Aug 2015, Samuel Just wrote: > >>>>> >>>>> Currently, there are some deficiencies in how the OSD maps ops > >>>>> >>>>> onto > >>>>> >>>>> threads: > >>>>> >>>>> > >>>>> >>>>> 1. Reads are always syncronous limiting the queue depth seen > >>>>> >>>>> from the device > >>>>> >>>>> and therefore the possible parallelism. > >>>>> >>>>> 2. Writes are always asyncronous forcing even very fast writes > >>>>> >>>>> to be completed > >>>>> >>>>> in a seperate thread. > >>>>> >>>>> 3. do_op cannot surrender the thread/pg lock during an > >>>>> >>>>> operation forcing reads > >>>>> >>>>> required to continue the operation to be syncronous. > >>>>> >>>>> > >>>>> >>>>> For spinning disks, this is mostly ok since they don't benefit > >>>>> >>>>> as much from large read queues, and writes (filestore with > >>>>> >>>>> journal) are too slow for the thread switches to make a big > >>>>> >>>>> difference. For very fast flash, however, we want the > >>>>> >>>>> flexibility to allow the backend to perform writes > >>>>> >>>>> syncronously or asyncronously when it makes sense, and to > >>>>> >>>>> maintain a larger number of outstanding reads than we have > >>>>> >>>>> threads. To that end, I suggest changing the ObjectStore > >>>>> >>>>> interface to be somewhat polling based: > >>>>> >>>>> > >>>>> >>>>> /// Create new token > >>>>> >>>>> void *create_operation_token() = 0; bool > >>>>> >>>>> is_operation_complete(void *token) = 0; bool > >>>>> >>>>> is_operation_committed(void *token) = 0; bool > >>>>> >>>>> is_operation_applied(void *token) = 0; void > >>>>> >>>>> wait_for_committed(void *token) = 0; void > >>>>> >>>>> wait_for_applied(void *token) = 0; void wait_for_complete(void > >>>>> >>>>> *token) = 0; /// Get result of operation int get_result(void > >>>>> >>>>> *token) = 0; /// Must only be called once > >>>>> >>>>> is_opearation_complete(token) void reset_operation_token(void > >>>>> >>>>> *token) = 0; /// Must only be called once > >>>>> >>>>> is_opearation_complete(token) void detroy_operation_token(void > >>>>> >>>>> *token) = 0; > >>>>> >>>>> > >>>>> >>>>> /** > >>>>> >>>>> * Queue a transaction > >>>>> >>>>> * > >>>>> >>>>> * token must be either fresh or reset since the last operation. > >>>>> >>>>> * If the operation is completed syncronously, token can be > >>>>> >>>>> resused > >>>>> >>>>> * without calling reset_operation_token. > >>>>> >>>>> * > >>>>> >>>>> * @result 0 if completed syncronously, -EAGAIN if async */ > >>>>> >>>>> int queue_transaction( > >>>>> >>>>> Transaction *t, > >>>>> >>>>> OpSequencer *osr, > >>>>> >>>>> void *token > >>>>> >>>>> ) = 0; > >>>>> >>>>> > >>>>> >>>>> /** > >>>>> >>>>> * Queue a transaction > >>>>> >>>>> * > >>>>> >>>>> * token must be either fresh or reset since the last operation. > >>>>> >>>>> * If the operation is completed syncronously, token can be > >>>>> >>>>> resused > >>>>> >>>>> * without calling reset_operation_token. > >>>>> >>>>> * > >>>>> >>>>> * @result -EAGAIN if async, 0 or -error otherwise. > >>>>> >>>>> */ > >>>>> >>>>> int read(..., void *token) = 0; ... > >>>>> >>>>> > >>>>> >>>>> The "token" concept here is opaque to allow the implementation > >>>>> >>>>> some flexibility. Ideally, it would be nice to be able to > >>>>> >>>>> include libaio operation contexts directly. > >>>>> >>>>> > >>>>> >>>>> The main goal here is for the backend to have the freedom to > >>>>> >>>>> complete writes and reads asyncronously or syncronously as the > >>>>> >>>>> sitation warrants. > >>>>> >>>>> It also leaves the interface user in control of where the > >>>>> >>>>> operation completion is handled. Each op thread can therefore > >>>>> >>>>> handle its own > >>>>> >>>>> completions: > >>>>> >>>>> > >>>>> >>>>> struct InProgressOp { > >>>>> >>>>> PGRef pg; > >>>>> >>>>> ObjectStore::Token *token; > >>>>> >>>>> OpContext *ctx; > >>>>> >>>>> }; > >>>>> >>>>> vector<InProgressOp> in_progress(MAX_IN_PROGRESS); > >>>>> >>>> > >>>>> >>>> Probably a deque<> since we'll be pushign new requests and > >>>>> >>>> slurping off completed ones? Or, we can make token not > >>>>> >>>> completely opaque, so that it includes a boost::intrusive::list > >>>>> >>>> node and can be strung on a user-managed queue. > >>>>> >>>> > >>>>> >>>>> for (auto op : in_progress) { > >>>>> >>>>> op.token = objectstore->create_operation_token(); > >>>>> >>>>> } > >>>>> >>>>> > >>>>> >>>>> uint64_t next_to_start = 0; > >>>>> >>>>> uint64_t next_to_complete = 0; > >>>>> >>>>> > >>>>> >>>>> while (1) { > >>>>> >>>>> if (next_to_complete - next_to_start == MAX_IN_PROGRESS) { > >>>>> >>>>> InProgressOp &op = in_progress[next_to_complete % > >>>>> >>>>> MAX_IN_PROGRESS]; > >>>>> >>>>> objectstore->wait_for_complete(op.token); > >>>>> >>>>> } > >>>>> >>>>> for (; next_to_complete < next_to_start; ++next_to_complete) { > >>>>> >>>>> InProgressOp &op = in_progress[next_to_complete % > >>>>> >>>>> MAX_IN_PROGRESS]; > >>>>> >>>>> if (objectstore->is_operation_complete(op.token)) { > >>>>> >>>>> PGRef pg = op.pg; > >>>>> >>>>> OpContext *ctx = op.ctx; > >>>>> >>>>> op.pg = PGRef(); > >>>>> >>>>> op.ctx = nullptr; > >>>>> >>>>> objectstore->reset_operation_token(op.token); > >>>>> >>>>> if (pg->continue_op( > >>>>> >>>>> ctx, &in_progress_ops[next_to_start % > >>>>> >>>>> MAX_IN_PROGRESS]) > >>>>> >>>>> == -EAGAIN) { > >>>>> >>>>> ++next_to_start; > >>>>> >>>>> continue; > >>>>> >>>>> } > >>>>> >>>>> } else { > >>>>> >>>>> break; > >>>>> >>>>> } > >>>>> >>>>> } > >>>>> >>>>> pair<OpRequestRef, PGRef> dq = // get new request from queue; > >>>>> >>>>> if (dq.second->do_op( > >>>>> >>>>> dq.first, &in_progress_ops[next_to_start % > >>>>> >>>>> MAX_IN_PROGRESS]) > >>>>> >>>>> == -EAGAIN) { > >>>>> >>>>> ++next_to_start; > >>>>> >>>>> } > >>>>> >>>>> } > >>>>> >>>>> > >>>>> >>>>> A design like this would allow the op thread to move onto > >>>>> >>>>> another task if the objectstore implementation wants to > >>>>> >>>>> perform an async operation. For this to work, there is some > >>>>> >>>>> work to be done: > >>>>> >>>>> > >>>>> >>>>> 1. All current reads in the read and write paths (probably > >>>>> >>>>> including the attr > >>>>> >>>>> reads in get_object_context and friends) need to be able to > >>>>> >>>>> handle > >>>>> >>>>> getting > >>>>> >>>>> -EAGAIN from the objectstore. > >>>>> >>>> > >>>>> >>>> Can we leave the old read methods in place as blocking > >>>>> >>>> versions, and have them block on the token before returning? > >>>>> >>>> That'll make the transition less painful. > >>>>> >>>> > >>>>> >>>>> 2. Writes and reads need to be able to handle having the pg > >>>>> >>>>> lock dropped > >>>>> >>>>> during the operation. This should be ok since the actual > >>>>> >>>>> object > >>>>> >>>>> information > >>>>> >>>>> is protected by the RWState locks. > >>>>> >>>> > >>>>> >>>> All of the async write pieces already handle this (recheck PG > >>>>> >>>> state after taking the lock). If they don't get -EAGAIN they'd > >>>>> >>>> just call the next stage, probably with a flag indicating that > >>>>> >>>> validation can be skipped (since the lock hasn't been dropped)? > >>>>> >>>> > >>>>> >>>>> 3. OpContext needs to have enough information to pick up where > >>>>> >>>>> the operation > >>>>> >>>>> left off. This suggests that we should obtain all required > >>>>> >>>>> ObjectContexts > >>>>> >>>>> at the beginning of the operation. Cache/Tiering complicates > >>>>> >>>>> this. > >>>>> >>>> > >>>>> >>>> Yeah... > >>>>> >>>> > >>>>> >>>>> 4. The object class interface will need to be replaced with a > >>>>> >>>>> new interface > >>>>> >>>>> based on possibly async reads. We can maintain compatibility > >>>>> >>>>> with > >>>>> >>>>> the > >>>>> >>>>> current ones by launching a new thread to handle any message > >>>>> >>>>> which > >>>>> >>>>> happens > >>>>> >>>>> to contain an old-style object class operation. > >>>>> >>>> > >>>>> >>>> Again, for now, wrappers would avoid this? > >>>>> >>>> > >>>>> >>>> s > >>>>> >>>>> > >>>>> >>>>> Most of this needs to happen to support object class > >>>>> >>>>> operations on ec pools anyway. > >>>>> >>>>> -Sam > >>>>> >>>>> -- > >>>>> >>>>> To unsubscribe from this list: send the line "unsubscribe > >>>>> >>>>> ceph-devel" > >>>>> >>>>> in > >>>>> >>>>> the body of a message to majord...@vger.kernel.org More > >>>>> >>>>> majordomo info at http://vger.kernel.org/majordomo-info.html > >>>>> >>>>> > >>>>> >>>>> > >>>>> >>> -- > >>>>> >>> To unsubscribe from this list: send the line "unsubscribe > >>>>> >>> ceph-devel" in the body of a message to > >>>>> >>> majord...@vger.kernel.org More majordomo info at > >>>>> >>> http://vger.kernel.org/majordomo-info.html > >>>>> >> -- > >>>>> >> To unsubscribe from this list: send the line "unsubscribe > >>>>> >> ceph-devel" in the body of a message to majord...@vger.kernel.org > >>>>> >> More majordomo info at > >>>>> >> http://vger.kernel.org/majordomo-info.html > >>>>> > > >>>>> > > >>>>> > > >>>>> > -- > >>>>> > Best Regards, > >>>>> > > >>>>> > Wheat > >>>>> > -- > >>>>> > To unsubscribe from this list: send the line "unsubscribe > >>>>> > ceph-devel" in the body of a message to majord...@vger.kernel.org > >>>>> > More majordomo info at http://vger.kernel.org/majordomo-info.html > >>>>> > >>>>> > >>>>> > >>>>> -- > >>>>> Milosz Tanski > >>>>> CTO > >>>>> 16 East 34th Street, 15th floor > >>>>> New York, NY 10016 > >>>>> > >>>>> p: 646-253-9055 > >>>>> e: mil...@adfin.com > >>>>> -- > >>>>> To unsubscribe from this list: send the line "unsubscribe > >>>>> ceph-devel" in the body of a message to majord...@vger.kernel.org > >>>>> More majordomo info at http://vger.kernel.org/majordomo-info.html > >>>>> > >>> > >>> > >>> > >>> -- > >>> Milosz Tanski > >>> CTO > >>> 16 East 34th Street, 15th floor > >>> New York, NY 10016 > >>> > >>> p: 646-253-9055 > >>> e: mil...@adfin.com > >> -- > >> To unsubscribe from this list: send the line "unsubscribe ceph-devel" in > >> the body of a message to majord...@vger.kernel.org More majordomo info at > >> http://vger.kernel.org/majordomo-info.html > -- > To unsubscribe from this list: send the line "unsubscribe ceph-devel" in > the body of a message to majord...@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html > -- To unsubscribe from this list: send the line "unsubscribe ceph-devel" in the body of a message to majord...@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html