| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
A clone from a template opens the template with rbd_open(). librados leaves rados_osd_op_timeout and rados_mon_op_timeout at 0, which means an operation waits forever, so a completion that never arrives parks the agent thread that issued it for the life of the process. The agent runs a host's storage commands in sequence, so every command queued behind that thread stops with it. Nothing released the cluster handles either. Rados only calls rados_shutdown() from finalize(), so a handle the KVM plugin opened stayed alive until the garbage collector happened to reach it, and then shut down on the finalizer thread rather than on the thread that did the work. Several paths also leaked the IO context and the open image when a call failed part way through, because the clean up ran only on the success path, and the snapshot backup never closed its image at all. Add CephUtil, which opens a connected handle with the timeouts applied and offers non-throwing helpers to close an image, destroy an IO context and shut a handle down. Route every handle in the plugin through it and clean up from finally blocks, so that each path releases what it opened. The timeouts bound a single operation rather than a whole request, so a long copy or flatten is made up of many operations that are each well inside the limit. They are exposed as rados.osd.op.timeout and rados.mon.op.timeout in agent.properties; setting either to 0 restores the previous behaviour of waiting forever. Signed-off-by: Brad House <bhouse@nexthop.ai>
A read write open registers a watcher on the image header and lets the image take its exclusive lock. A clone parent, and an image that is only read or stat'ed, need neither, and the extra work is done inside rbd_open() while the caller waits. Open the template read only when cloning from it, on both the same cluster and the cross cluster path, and open the snapshot read only when backing it up. The base snapshot still has to be created and protected on a writable handle, so the first clone of a template reopens it read write for that step alone; every later clone of the same template finds the snapshot and keeps the read only handle. Signed-off-by: Brad House <bhouse@nexthop.ai>
Codecov Report❌ Patch coverage is 8.50340% with 269 lines in your changes missing coverage. Please review. @@ Coverage Diff @@
## 4.22 #14199 +/- ##
==========================================
Coverage 18.32% 18.32%
- Complexity 16644 16656 +12
==========================================
Files 5939 5940 +1
Lines 537768 537869 +101
Branches 65929 65939 +10
==========================================
+ Hits 98551 98586 +35
- Misses 428008 428070 +62
- Partials 11209 11213 +4
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness.
|
Sorry, something went wrong.
Correctness fixes to the two preceding commits: - deleteSnapshot reported success when the image could not be opened. Moving the connect and open inside the inner try put rbd_open() failures into a catch that only logs, so the method fell through to its success return. The management server would drop the snapshot record while the snapshot stayed on the cluster, and a protected one then blocks removal of its parent volume. The open is back outside that catch. - The template reopen closed the read only handle and left the field pointing at it, so a failure of the reopen sent the already freed pointer to rbd_close() a second time in the finally. That is a use after free in librbd, which takes the agent down rather than raising an exception. The field is cleared first. - CephUtil.connect() abandoned the cluster handle if anything between rados_create() and the return threw, which is the leak the class exists to prevent, and the new connect timeout makes that path ordinary rather than rare. It now releases the handle before rethrowing. - The cephx secret is null for a pool with no cephx user, and librados aborts the process rather than returning an error if it is handed one. Guard as getRbdPhysicalDisk already does. Failures that were silent are now reported, which matters because a timeout makes them reachable: - rbd_read returns a negative errno rather than throwing, and the snapshot backup loop treated that as end of image: a short file on secondary storage, recorded as a good backup. It now fails, as does a write error, which was swallowed too. - A failed RBD snapshot create or remove logged and then returned a success answer, recording a snapshot in CloudStack with nothing behind it on the cluster. Also: client_mount_timeout now honours 0 like the other two, ioCtxDestroyQuietly guards the handle it dereferences, the constants left unused by the previous commits are removed, the three properties are documented in agent.properties, and the RBD cleanup tests assert the handle is shut down. Signed-off-by: Brad House <bhouse@nexthop.ai>
Default rados.osd.op.timeout and rados.mon.op.timeout to 0, which leaves the options unset and keeps the librados behaviour of waiting forever. Nothing about how an existing deployment talks to Ceph changes unless an operator sets them. rados.client.mount.timeout keeps its default of 30, which is the value the code passed before it became a property. agent.properties carries the reasoning and a suggested starting point, since an operator has no way to pick a value from the property name alone. Signed-off-by: Brad House <bhouse@nexthop.ai>
…ccess Closing an RBD image is where librbd flushes, so a close that fails on an image just written is the last chance a lost write has to surface. Routing those two closes through the non-throwing helper turned that into a warning and returned the disk anyway, which would register a volume whose tail writes never landed. Close the written destination explicitly and let it fail the copy; the finally still covers the paths that did not get that far. deleteSnapshot had the same shape: a failed snapUnprotect or snapRemove was logged and the method still answered "removed successfully", dropping the record while the snapshot kept pinning space on the cluster, and one left protected also blocks removal of its parent volume. It now fails, except for ENOENT, where the snapshot is already gone and the delete has nothing left to do. The same ENOENT tolerance keeps the RBD branch of ManageSnapshotCommand idempotent for deletes. Also corrects the applyTimeouts javadoc, which said 0 means waiting forever for all three options; that is true of the two operation timeouts but the connect timeout falls back to the librados default of 300s. The snapshot test now pins the unwind order, since destroying an IO context after its cluster handle has been shut down would be a use after free. Signed-off-by: Brad House <bhouse@nexthop.ai>
|
@weizhouapache a [SL] Jenkins job has been kicked to build packages. It will be bundled with KVM, XenServer and VMware SystemVM templates. I'll keep you posted as I make progress. |
Sorry, something went wrong.
|
Packaging result [SF]: ✔️ el8 ✔️ el9 ✔️ el10 ✔️ debian ✔️ suse15. SL-JID 19300 |
Sorry, something went wrong.
|
@weizhouapache a [SL] Trillian-Jenkins test job (ol8 mgmt + kvm-ol8) has been kicked to run smoke tests |
Sorry, something went wrong.
|
[SF] Trillian test result (tid-17029)
|
Sorry, something went wrong.
|
@vladimirpetrov @sureshanaparti could you take a look at this one? We'd like this fix to make it into the upcoming 4.22.2 release. |
Sorry, something went wrong.
There was a problem hiding this comment.
code lgtm
run 156 smoke tests with this PR on an environment with 2 ceph pools, no issues found
Sorry, something went wrong.
|
with both timeouts at 0 by default, does anything here stop the original hang out of the box? looks like the agent would still wait forever on that rbd_open |
Sorry, something went wrong.
it looks @bhouse-nexthop replied to a wrong PR |
Sorry, something went wrong.
|
@Damans227 you're right, out of the box this doesn't bound that wait. With both timeouts at 0, CephUtil leaves the librados options unset, and the command still runs on the agent request thread, so an rbd_open that never completes still parks it and the host's queue behind it. Leaving them at 0 was deliberate. A single value that fits every cluster is hard to pick, and a timeout that's too tight turns a slow but healthy operation during backfill or recovery into a failed deploy or snapshot. So I made it opt in and put a suggested starting point in agent.properties (60s osd / 30s mon). What does apply by default: the clone parent is now opened read only, so rbd_open no longer registers a header watch or sets up the exclusive lock. That is the kind of internal completion the stuck thread was waiting on. Cluster handles are released deterministically, and the silent failures (short reads, failed snapshot create/remove) are now reported. None of that guarantees the hang can't happen again, though. Only the timeouts bound it, and as the description says, even they may not reach every internal librbd wait. If you and the other reviewers would prefer non-zero defaults, I'm happy to switch to 60/30. I've also fixed the description, which overstated what happens without the timeouts set. |
Sorry, something went wrong.
|
@weizhouapache @Damans227 this is waiting on one decision: whether the two Ceph timeouts should stay at 0 (opt in, as now) or default to 60s osd / 30s mon (see my reply above on 10-14). I'm happy to switch the defaults today if you'd prefer that. Once that's settled, could it go into 4.22.2? I understand the release is due late this week or early next. |
Sorry, something went wrong.
@bhouse-nexthop lets default to 60s osd / 30s mon. at 0 the hang still happens for anyone who doesnt know to set them, and a failed deploy can be retried while a stuck host cant. anyone with a slow cluster can still raise them or set 0. |
Sorry, something went wrong.
Default rados.osd.op.timeout to 60 and rados.mon.op.timeout to 30. With the options left unset, an operation that never completes still parks the agent thread, and with it the host's storage command queue, for anyone who does not know to set them. A failed operation can be retried; a wedged host cannot. Setting either property to 0 still leaves the option unset and keeps the librados behaviour of waiting forever, and a cluster whose single operations legitimately run longer can raise them. Signed-off-by: Brad House <bhouse@nexthop.ai>
|
@Damans227 agreed, done in 85de0b5. rados.osd.op.timeout now defaults to 60 and rados.mon.op.timeout to 30. Setting either to 0 still leaves the librados option unset (wait forever), and a slow cluster can raise them. agent.properties, the AgentProperties javadoc and the PR description are updated to match. |
Sorry, something went wrong.
@bhouse-nexthop that covers it |
Sorry, something went wrong.
There was a problem hiding this comment.
thanks @bhouse-nexthop, the new defaults look right. CLGTM
Sorry, something went wrong.
@weizhouapache , do we considder this as tested with this? |
Sorry, something went wrong.
I have not tested the latest changes |
Sorry, something went wrong.
ok, can you re-run against your ceph env? |
Sorry, something went wrong.
|
@weizhouapache a [SL] Jenkins job has been kicked to build packages. It will be bundled with KVM, XenServer and VMware SystemVM templates. I'll keep you posted as I make progress. |
Sorry, something went wrong.
|
Packaging result [SF]: ✔️ el8 ✔️ el9 ✔️ el10 ✖️ debian ✔️ suse15. SL-JID 19772 |
Sorry, something went wrong.
|
Force-pushed to fix the author and committer identity on the last commit ("kvm: bound Ceph operations by default"). It had been committed with a placeholder git identity. There is no code change: every commit's tree is identical to before, and the head is now 6ae707f. @weizhouapache, your pending re-test applies to this head the same way. |
Sorry, something went wrong.
KVMStoragePoolManager.getPhysicalDisk() retries any failure 100 times, 3 s apart, to wait for a volume created on another host to show up. With RBD lookups going through librbd and Ceph operations now bounded, a Ceph outage makes each try fail with -110 after rados.osd.op.timeout, and the loop turns that into about 100 x 63 s, roughly 105 minutes, during which the command holds an agent handler thread. A lab test with the OSD ports blocked saw this on deploy (cloneVolumeFromBaseTemplate) and on createSnapshot. Only librbd's -ENOENT means the image is not there yet. Fail at once when the cause chain carries any other RbdException, or any RadosException (a connect failure, or -ENOENT for a pool that is gone). Errors that are not from Ceph are retried as before. Signed-off-by: Brad House <bhouse@nexthop.ai>
Signed-off-by: Brad House <bhouse@nexthop.ai>
getRbdPhysicalDisk came in with apache#14188 after this change branched, so it still built its own Rados handle with only client_mount_timeout set. The lookup that runs on every deploy and VM start would then block forever during an OSD outage. Open through CephUtil.connect(), so it gets the same operation timeouts as every other Ceph handle in the plugin, and release the IO context and the cluster handle from the finally with the non-throwing helpers, so a failure to release cannot mask the exception on its way out. The cephx guard that this method carried is now inside CephUtil.connect() and covers every caller. Signed-off-by: Brad House <bhouse@nexthop.ai>
Test report: Ceph outages on a KVM host, with the agent's Ceph traffic blockedEnvironment
How the fault was injected
History
1. OSD ports blocked, deploy a VM to the host
2. "Without": the timeouts set to 0, which is the stock behaviour
3. Snapshot of a running VM's ROOT with the OSD ports blocked
4. Snapshot backup interrupted mid-copy
5. Watchers on the template image (from the first run, same code for this part)
6. Cluster handles released (from the first run, same code for this part)
Result: pass.
Not covered
|
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
Description
A VM deploy wedged a KVM agent thread for 7.8 hours and took the cluster's VM creation with it. The thread was cloning a template on RBD and never returned.
Stack from the agent, with the native frames underneath:
Ceph was healthy the whole time: HEALTH_OK, every OSD up, no watchers on the template, no blocklisted clients, and the same template opened instantly with rbd on that same host.
The user visible effect was that new VM deploys kept failing with InsufficientServerCapacityException, on a cluster where every host had terabytes of free memory. That error is what a deploy reports when the planner ends up with no usable destination, whatever the reason, so it sent us looking at capacity for a while before the real cause turned up in an agent thread dump.
Three things in the KVM plugin turned one stuck call into an outage:
The agent runs a host's storage commands in sequence, so the stuck thread also blocked every command queued behind it for that host.
Fix
1. Bound the operations. A new CephUtil.connect() applies per-operation timeouts to every handle:
The operation timeouts are on by default. Left at 0, the hang still happens for anyone who doesn't know to set them, and a failed operation can be retried while a wedged host can't. Setting either to 0 leaves the librados option unset and keeps the old wait-forever behaviour, and a cluster whose single operations legitimately run longer, for example during heavy backfill or recovery, can raise them. agent/conf/agent.properties documents both.
They limit a single operation and not a whole request, so a long copy or flatten is made up of many operations that are each well inside the limit.
2. Release what we open. CephUtil adds non-throwing closeQuietly, ioCtxDestroyQuietly and shutDownQuietly, and every path cleans up from a finally.
3. Open read only where we only read. A read write open registers a watcher on the image header and lets the image take its exclusive lock. A clone parent needs neither:
Creating and protecting the base snapshot are writes, so the first clone of a template reopens it read write for that one step. Every later clone of that template keeps the read only handle. The same change applies to the cross cluster copy and to reading a snapshot during backup. Paths that really do write, such as snapCreate, snapUnprotect and snapRemove, are unchanged.
4. Report failures that were silent. Wrong regardless, and reachable the moment a timeout is set:
What this does not fix
The thread was waiting on an internal librbd completion that never arrived, so a per operation timeout may not reach that particular wait. Where the timeouts do reach it, this change lets the agent recover and keeps one stuck call from blocking a host's whole command queue. It does not fix librbd.
Types of changes
How Has This Been Tested?