zstream: Add subcommand applying zvol stream to block device or raw disk image - #18565
zstream: Add subcommand applying zvol stream to block device or raw disk image#18565ryan-moeller wants to merge 3 commits into
Conversation
39e6611 to
503a0b4
Compare
There is work underway to implement a few optimizations as mentioned in the description, but I'm opening this for review now as those optimizations may be incorporated later or separately. |
503a0b4 to
7396b73
Compare
|
606c32e to
8e87a75
Compare
|
behlendorf
left a comment
There was a problem hiding this comment.
@ryan-moeller this will definitely be handy! After going through #18509 I felt the best way forward was to get that major refactoring merged first. It's now in master so when you get a chance please go ahead and rebase this, update it to use the new APIs and then I can take another look.
|
Excellent, give me a few days on it and I'll report back. |
|
OK, back atcha. I submitted this as a PR against KlaraSystems/zstream-raw. There are more details in the PR, but it went pretty smoothly. It did motivate a couple of changes to the infrastructure, so thanks for the opportunity to look this over. |
I have not fully incorporated the changes @GarthSnyder proposed yet, this push was mainly to clean up his PR against my branch to simplify review there. |
|
f4c7f8e to
74a8b1f
Compare
|
74a8b1f to
287ec62
Compare
This is all the additional features, so barring any minor corrections this should be ready to go. |
287ec62 to
7ad94a2
Compare
That would be my preference as well. 🙂 Even 4GB is a stretch, at least for this operation. Here's what's going on. But the queue itself has a much larger capacity than Were they all WRITEs, that'd be over 20GB of memory. But since most records are not WRITEs, you're seeing a baseline of 4GB. When you start adding buffers to the IOV queue, you're cherry-picking the WRITE records from the stream, so that memory adds up quickly. But 16 or 32 IOVs seems like a reasonable default. I'm not sure why you're seeing the explosion from 4GB to 12GB with a value of 32. That doesn't seem like it should be possible. A default > 1 probably wouldn't have any effect on performance with these large WRITE sizes, but for smaller or fragmented WRITEs it might be of some benefit, and it makes That You can shrink the input queue by using the |
For zvols, sends are by far mostly WRITEs to one object, and occasional FREEs: Unfortunately, I'm out of time for more work on this project outside of small last minute corrections. I can't justify additional budget for tuning the memory use of the chain mechanism. I hope the buffer limit is reasonable enough for this feature to be acceptable after raising the default to 32, and further improvements can be made separately. |
c51a4ba to
45ff098
Compare
|
|
Memory consumption is probably better addressed at a higher level anyway, e.g. a limit on the total sum of bytes in payloads that are allowed to be in flight at any given moment. Multiple subcommands rewrite payloads, so there probably needs to be a |
45ff098 to
1a81d74
Compare
|
Use fspacectl(2) on FreeBSD to do hole punching for randfree_file and build it on both platforms. With this, compress_004_pos can be moved to the common runfile. Sponsored-by: Klara, Inc. Sponsored-by: Railway Corporation Signed-off-by: Ryan Moeller <ryan.moeller@klarasystems.com>
And remove dead code following its use. Sponsored-by: Klara, Inc. Sponsored-by: Railway Corporation Signed-off-by: Ryan Moeller <ryan.moeller@klarasystems.com>
Introduce a "zstream raw" command. This command allows zvol send streams to be applied to a raw disk image or block device, with support for incremental sends and compression. Encrypted (raw) streams are not supported, but compression, large blocks, and embedded write records are handled. Contiguous writes can optionally be buffered and issued together with pwritev(2). Block devices supporting TRIM/UNMAP use the platform-appropriate BLKDISCARD or SIOCGDELETE ioctl to handle FREE records. Raw file volume images use the platform-appropriate hole punching operation to handle FREE records if supported by the underlying filesystem. Sponsored-by: Klara, Inc. Sponsored-by: Railway Corporation Signed-off-by: Ryan Moeller <ryan.moeller@klarasystems.com>
behlendorf
left a comment
There was a problem hiding this comment.
Yeah, we can tackle the memory consumption issues as a separate change. Thanks for working on this.
And remove dead code following its use. Sponsored-by: Klara, Inc. Sponsored-by: Railway Corporation Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Reviewed-by: Garth Snyder <garth@garthsnyder.com> Signed-off-by: Ryan Moeller <ryan.moeller@klarasystems.com> Closes #18565
Introduce a "zstream raw" command. This command allows zvol send streams to be applied to a raw disk image or block device, with support for incremental sends and compression. Encrypted (raw) streams are not supported, but compression, large blocks, and embedded write records are handled. Contiguous writes can optionally be buffered and issued together with pwritev(2). Block devices supporting TRIM/UNMAP use the platform-appropriate BLKDISCARD or SIOCGDELETE ioctl to handle FREE records. Raw file volume images use the platform-appropriate hole punching operation to handle FREE records if supported by the underlying filesystem. Sponsored-by: Klara, Inc. Sponsored-by: Railway Corporation Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Reviewed-by: Garth Snyder <garth@garthsnyder.com> Signed-off-by: Ryan Moeller <ryan.moeller@klarasystems.com> Closes #18565
|
Merged. Thanks for working on this. |
`zstream` reads stream record payloads into memory while processing a stream. Most subcommands use several parallel queues, which currently have queue lengths on the order of 1000 slots. Depending on the pipeline, there may be several thousand records in memory at any given moment. This is fine for typical filesystem send streams because most records aren't payload-bearing and default record sizes are modest. However, it's possible for streams to be far denser, as illustrated by this zvol send stream breakdown posted by @ryan-moeller in openzfs#18565: ``` SUMMARY: Total DRR_BEGIN records = 1 (0 bytes) Total DRR_END records = 1 (0 bytes) Total DRR_OBJECT records = 2 (0 bytes) Total DRR_FREEOBJECTS records = 1 (0 bytes) Total DRR_WRITE records = 4304609 (70131052544 bytes) Total DRR_WRITE_BYREF records = 0 (0 bytes) Total DRR_WRITE_EMBEDDED records = 645 (55664 bytes) Total DRR_FREE records = 2202 (0 bytes) Total DRR_SPILL records = 0 (0 bytes) Total DRR_OBJECT_RANGE records = 0 (0 bytes) Total DRR_REDACT records = 0 (0 bytes) Total records = 4307461 Total payload size = 70131108208 (0x105423c970) Total header overhead = 1343927832 (0x501ab618) Total stream length = 71475036040 (0x10a43e7f88) ``` This PR standardizes functions for manipulating payloads within `zstream` and adds memory accounting and read throttling. Modules anywhere within a processing chain may want to modify or dispose of payloads, so memory accounting is thread-safe. Throttling is applied at the point of ingress, `chain_read()`. That function blocks when the limit is reached. However, parallel queues and later chain segments continue to function normally, so data naturally drains out of the pipeline over time. The memory budget is currently set at 512MB plus 10% of system memory beyond 4GB. There's no reason these parameters can't be made configurable in the future, but for now they're hard-coded. The budget is just an accounting limit and there is no up-front allocation. Typical processing of filesystem streams will not approach this limit. Memory accounting covers only payload data attached to DRR packets within a processing chain. When data is removed from a stream for separate handling (now achieved by calling `export_payload()`), it's no longer tracked by the standard memory accounting.
`zstream` reads stream record payloads into memory while processing a stream. Most subcommands use several parallel queues, which currently have queue lengths on the order of 1000 slots. Depending on the pipeline, there may be several thousand records in memory at any given moment. This is fine for typical filesystem send streams because most records aren't payload-bearing and default record sizes are modest. However, it's possible for streams to be far denser, as illustrated by this zvol send stream breakdown posted by @ryan-moeller in openzfs#18565: ``` SUMMARY: Total DRR_BEGIN records = 1 (0 bytes) Total DRR_END records = 1 (0 bytes) Total DRR_OBJECT records = 2 (0 bytes) Total DRR_FREEOBJECTS records = 1 (0 bytes) Total DRR_WRITE records = 4304609 (70131052544 bytes) Total DRR_WRITE_BYREF records = 0 (0 bytes) Total DRR_WRITE_EMBEDDED records = 645 (55664 bytes) Total DRR_FREE records = 2202 (0 bytes) Total DRR_SPILL records = 0 (0 bytes) Total DRR_OBJECT_RANGE records = 0 (0 bytes) Total DRR_REDACT records = 0 (0 bytes) Total records = 4307461 Total payload size = 70131108208 (0x105423c970) Total header overhead = 1343927832 (0x501ab618) Total stream length = 71475036040 (0x10a43e7f88) ``` This PR standardizes functions for manipulating payloads within `zstream` and adds memory accounting and read throttling. Modules anywhere within a processing chain may want to modify or dispose of payloads, so memory accounting is thread-safe. Throttling is applied at the point of ingress, `chain_read()`. That function blocks when the limit is reached. However, parallel queues and later chain segments continue to function normally, so data naturally drains out of the pipeline over time. The memory budget is currently set at 512MB plus 10% of system memory beyond 4GB. There's no reason these parameters can't be made configurable in the future, but for now they're hard-coded. The budget is just an accounting limit and there is no up-front allocation. Typical processing of filesystem streams will not approach this limit. Memory accounting covers only payload data attached to DRR packets within a processing chain. When data is removed from a stream for separate handling (now achieved by calling `export_payload()`), it's no longer tracked by the standard memory accounting. Signed-off-by: Garth Snyder <garth@garthsnyder.com>
`zstream` reads stream record payloads into memory while processing a stream. Most subcommands use several parallel queues, which currently have queue lengths on the order of 1000 slots. Depending on the pipeline, there may be several thousand records in memory at any given moment. This is fine for typical filesystem send streams because most records aren't payload-bearing and default record sizes are modest. However, it's possible for streams to be far denser, as illustrated by this zvol send stream breakdown posted by @ryan-moeller in openzfs#18565: ``` SUMMARY: Total DRR_BEGIN records = 1 (0 bytes) Total DRR_END records = 1 (0 bytes) Total DRR_OBJECT records = 2 (0 bytes) Total DRR_FREEOBJECTS records = 1 (0 bytes) Total DRR_WRITE records = 4304609 (70131052544 bytes) Total DRR_WRITE_BYREF records = 0 (0 bytes) Total DRR_WRITE_EMBEDDED records = 645 (55664 bytes) Total DRR_FREE records = 2202 (0 bytes) Total DRR_SPILL records = 0 (0 bytes) Total DRR_OBJECT_RANGE records = 0 (0 bytes) Total DRR_REDACT records = 0 (0 bytes) Total records = 4307461 Total payload size = 70131108208 (0x105423c970) Total header overhead = 1343927832 (0x501ab618) Total stream length = 71475036040 (0x10a43e7f88) ``` This PR standardizes functions for manipulating payloads within `zstream` and adds memory accounting and read throttling. Modules anywhere within a processing chain may want to modify or dispose of payloads, so memory accounting is thread-safe. Throttling is applied at the point of ingress, `chain_read()`. That function blocks when the limit is reached. However, parallel queues and later chain segments continue to function normally, so data naturally drains out of the pipeline over time. The memory budget is currently set at 512MB plus 10% of system memory beyond 4GB. There's no reason these parameters can't be made configurable in the future, but for now they're hard-coded. The budget is just an accounting limit and there is no up-front allocation. Typical processing of filesystem streams will not approach this limit. Memory accounting covers only payload data attached to DRR packets within a processing chain. When data is removed from a stream for separate handling (now achieved by calling `export_payload()`), it's no longer tracked by the standard memory accounting. Signed-off-by: Garth Snyder <garth@garthsnyder.com>
`zstream` reads stream record payloads into memory while processing a stream. Most subcommands use several parallel queues, which currently have queue lengths on the order of 1000 slots. Depending on the pipeline, there may be several thousand records in memory at any given moment. This is fine for typical filesystem send streams because most records aren't payload-bearing and default record sizes are modest. However, it's possible for streams to be far denser, as illustrated by this zvol send stream breakdown posted by @ryan-moeller in openzfs#18565: ``` SUMMARY: Total DRR_BEGIN records = 1 (0 bytes) Total DRR_END records = 1 (0 bytes) Total DRR_OBJECT records = 2 (0 bytes) Total DRR_FREEOBJECTS records = 1 (0 bytes) Total DRR_WRITE records = 4304609 (70131052544 bytes) Total DRR_WRITE_BYREF records = 0 (0 bytes) Total DRR_WRITE_EMBEDDED records = 645 (55664 bytes) Total DRR_FREE records = 2202 (0 bytes) Total DRR_SPILL records = 0 (0 bytes) Total DRR_OBJECT_RANGE records = 0 (0 bytes) Total DRR_REDACT records = 0 (0 bytes) Total records = 4307461 Total payload size = 70131108208 (0x105423c970) Total header overhead = 1343927832 (0x501ab618) Total stream length = 71475036040 (0x10a43e7f88) ``` This PR standardizes functions for manipulating payloads within `zstream` and adds memory accounting and read throttling. Modules anywhere within a processing chain may want to modify or dispose of payloads, so memory accounting is thread-safe. Throttling is applied at the point of ingress, `chain_read()`. That function blocks when the limit is reached. However, parallel queues and later chain segments continue to function normally, so data naturally drains out of the pipeline over time. The memory budget is currently set at 512MB plus 10% of system memory beyond 4GB. There's no reason these parameters can't be made configurable in the future, but for now they're hard-coded. The budget is just an accounting limit and there is no up-front allocation. Typical processing of filesystem streams will not approach this limit. Memory accounting covers only payload data attached to DRR packets within a processing chain. When data is removed from a stream for separate handling (now achieved by calling `export_payload()`), it's no longer tracked by the standard memory accounting. Signed-off-by: Garth Snyder <garth@garthsnyder.com>
`zstream` reads stream record payloads into memory while processing a stream. Most subcommands use several parallel queues, which currently have queue lengths on the order of 1000 slots. Depending on the pipeline, there may be several thousand records in memory at any given moment. This is fine for typical filesystem send streams because most records aren't payload-bearing and default record sizes are modest. However, it's possible for streams to be far denser, as illustrated by this zvol send stream breakdown posted by @ryan-moeller in openzfs#18565: ``` SUMMARY: Total DRR_BEGIN records = 1 (0 bytes) Total DRR_END records = 1 (0 bytes) Total DRR_OBJECT records = 2 (0 bytes) Total DRR_FREEOBJECTS records = 1 (0 bytes) Total DRR_WRITE records = 4304609 (70131052544 bytes) Total DRR_WRITE_BYREF records = 0 (0 bytes) Total DRR_WRITE_EMBEDDED records = 645 (55664 bytes) Total DRR_FREE records = 2202 (0 bytes) Total DRR_SPILL records = 0 (0 bytes) Total DRR_OBJECT_RANGE records = 0 (0 bytes) Total DRR_REDACT records = 0 (0 bytes) Total records = 4307461 Total payload size = 70131108208 (0x105423c970) Total header overhead = 1343927832 (0x501ab618) Total stream length = 71475036040 (0x10a43e7f88) ``` This PR standardizes functions for manipulating payloads within `zstream` and adds memory accounting and read throttling. Modules anywhere within a processing chain may want to modify or dispose of payloads, so memory accounting is thread-safe. Throttling is applied at the point of ingress, `chain_read()`. That function blocks when the limit is reached. However, parallel queues and later chain segments continue to function normally, so data naturally drains out of the pipeline over time. The memory budget is currently set at 512MB plus 10% of system memory beyond 4GB. There's no reason these parameters can't be made configurable in the future, but for now they're hard-coded. The budget is just an accounting limit and there is no up-front allocation. Typical processing of filesystem streams will not approach this limit. Memory accounting covers only payload data attached to DRR packets within a processing chain. When data is removed from a stream for separate handling (now achieved by calling `export_payload()`), it's no longer tracked by the standard memory accounting. Signed-off-by: Garth Snyder <garth@garthsnyder.com>
`zstream` reads stream record payloads into memory while processing a stream. Most subcommands use several parallel queues, which currently have queue lengths on the order of 1000 slots. Depending on the pipeline, there may be several thousand records in memory at any given moment. This is fine for typical filesystem send streams because most records aren't payload-bearing and default record sizes are modest. However, it's possible for streams to be far denser, as illustrated by this zvol send stream breakdown posted by @ryan-moeller in openzfs#18565: ``` SUMMARY: Total DRR_BEGIN records = 1 (0 bytes) Total DRR_END records = 1 (0 bytes) Total DRR_OBJECT records = 2 (0 bytes) Total DRR_FREEOBJECTS records = 1 (0 bytes) Total DRR_WRITE records = 4304609 (70131052544 bytes) Total DRR_WRITE_BYREF records = 0 (0 bytes) Total DRR_WRITE_EMBEDDED records = 645 (55664 bytes) Total DRR_FREE records = 2202 (0 bytes) Total DRR_SPILL records = 0 (0 bytes) Total DRR_OBJECT_RANGE records = 0 (0 bytes) Total DRR_REDACT records = 0 (0 bytes) Total records = 4307461 Total payload size = 70131108208 (0x105423c970) Total header overhead = 1343927832 (0x501ab618) Total stream length = 71475036040 (0x10a43e7f88) ``` This PR standardizes functions for manipulating payloads within `zstream` and adds memory accounting and read throttling. Modules anywhere within a processing chain may want to modify or dispose of payloads, so memory accounting is thread-safe. Throttling is applied at the point of ingress, `chain_read()`. That function blocks when the limit is reached. However, parallel queues and later chain segments continue to function normally, so data naturally drains out of the pipeline over time. The memory budget is currently set at 512MB plus 10% of system memory beyond 4GB. There's no reason these parameters can't be made configurable in the future, but for now they're hard-coded. The budget is just an accounting limit and there is no up-front allocation. Typical processing of filesystem streams will not approach this limit. Memory accounting covers only payload data attached to DRR packets within a processing chain. When data is removed from a stream for separate handling (now achieved by calling `export_payload()`), it's no longer tracked by the standard memory accounting.
`zstream` reads stream record payloads into memory while processing a stream. Most subcommands use several parallel queues, which currently have queue lengths on the order of 1000 slots. Depending on the pipeline, there may be several thousand records in memory at any given moment. This is fine for typical filesystem send streams because most records aren't payload-bearing and default record sizes are modest. However, it's possible for streams to be far denser, as illustrated by this zvol send stream breakdown posted by @ryan-moeller in openzfs#18565: ``` SUMMARY: Total DRR_BEGIN records = 1 (0 bytes) Total DRR_END records = 1 (0 bytes) Total DRR_OBJECT records = 2 (0 bytes) Total DRR_FREEOBJECTS records = 1 (0 bytes) Total DRR_WRITE records = 4304609 (70131052544 bytes) Total DRR_WRITE_BYREF records = 0 (0 bytes) Total DRR_WRITE_EMBEDDED records = 645 (55664 bytes) Total DRR_FREE records = 2202 (0 bytes) Total DRR_SPILL records = 0 (0 bytes) Total DRR_OBJECT_RANGE records = 0 (0 bytes) Total DRR_REDACT records = 0 (0 bytes) Total records = 4307461 Total payload size = 70131108208 (0x105423c970) Total header overhead = 1343927832 (0x501ab618) Total stream length = 71475036040 (0x10a43e7f88) ``` This PR standardizes functions for manipulating payloads within `zstream` and adds memory accounting and read throttling. Modules anywhere within a processing chain may want to modify or dispose of payloads, so memory accounting is thread-safe. Throttling is applied at the point of ingress, `chain_read()`. That function blocks when the limit is reached. However, parallel queues and later chain segments continue to function normally, so data naturally drains out of the pipeline over time. The memory budget is currently set at 512MB plus 10% of system memory beyond 4GB. There's no reason these parameters can't be made configurable in the future, but for now they're hard-coded. The budget is just an accounting limit and there is no up-front allocation. Typical processing of filesystem streams will not approach this limit. Memory accounting covers only payload data attached to DRR packets within a processing chain. When data is removed from a stream for separate handling (now achieved by calling `export_payload()`), it's no longer tracked by the standard memory accounting. Signed-off-by: Garth Snyder <garth@garthsnyder.com>
`zstream` reads stream record payloads into memory while processing a stream. Most subcommands use several parallel queues, which currently have queue lengths on the order of 1000 slots. Depending on the pipeline, there may be several thousand records in memory at any given moment. This is fine for typical filesystem send streams because most records aren't payload-bearing and default record sizes are modest. However, it's possible for streams to be far denser, as illustrated by this zvol send stream breakdown posted by @ryan-moeller in openzfs#18565: ``` SUMMARY: Total DRR_BEGIN records = 1 (0 bytes) Total DRR_END records = 1 (0 bytes) Total DRR_OBJECT records = 2 (0 bytes) Total DRR_FREEOBJECTS records = 1 (0 bytes) Total DRR_WRITE records = 4304609 (70131052544 bytes) Total DRR_WRITE_BYREF records = 0 (0 bytes) Total DRR_WRITE_EMBEDDED records = 645 (55664 bytes) Total DRR_FREE records = 2202 (0 bytes) Total DRR_SPILL records = 0 (0 bytes) Total DRR_OBJECT_RANGE records = 0 (0 bytes) Total DRR_REDACT records = 0 (0 bytes) Total records = 4307461 Total payload size = 70131108208 (0x105423c970) Total header overhead = 1343927832 (0x501ab618) Total stream length = 71475036040 (0x10a43e7f88) ``` This PR standardizes functions for manipulating payloads within `zstream` and adds memory accounting and read throttling. Modules anywhere within a processing chain may want to modify or dispose of payloads, so memory accounting is thread-safe. Throttling is applied at the point of ingress, `chain_read()`. That function blocks when the limit is reached. However, parallel queues and later chain segments continue to function normally, so data naturally drains out of the pipeline over time. The memory budget is currently set at 512MB plus 10% of system memory beyond 4GB. There's no reason these parameters can't be made configurable in the future, but for now they're hard-coded. The budget is just an accounting limit and there is no up-front allocation. Typical processing of filesystem streams will not approach this limit. Memory accounting covers only payload data attached to DRR packets within a processing chain. When data is removed from a stream for separate handling (now achieved by calling `export_payload()`), it's no longer tracked by the standard memory accounting. Signed-off-by: Garth Snyder <garth@garthsnyder.com>
`zstream` reads stream record payloads into memory while processing a stream. Most subcommands use several parallel queues, which currently have queue lengths on the order of 1000 slots. Depending on the pipeline, there may be several thousand records in memory at any given moment. This is fine for typical filesystem send streams because most records aren't payload-bearing and default record sizes are modest. However, it's possible for streams to be far denser, as illustrated by this zvol send stream breakdown posted by @ryan-moeller in openzfs#18565: ``` SUMMARY: Total DRR_BEGIN records = 1 (0 bytes) Total DRR_END records = 1 (0 bytes) Total DRR_OBJECT records = 2 (0 bytes) Total DRR_FREEOBJECTS records = 1 (0 bytes) Total DRR_WRITE records = 4304609 (70131052544 bytes) Total DRR_WRITE_BYREF records = 0 (0 bytes) Total DRR_WRITE_EMBEDDED records = 645 (55664 bytes) Total DRR_FREE records = 2202 (0 bytes) Total DRR_SPILL records = 0 (0 bytes) Total DRR_OBJECT_RANGE records = 0 (0 bytes) Total DRR_REDACT records = 0 (0 bytes) Total records = 4307461 Total payload size = 70131108208 (0x105423c970) Total header overhead = 1343927832 (0x501ab618) Total stream length = 71475036040 (0x10a43e7f88) ``` This PR standardizes functions for manipulating payloads within `zstream` and adds memory accounting and read throttling. Modules anywhere within a processing chain may want to modify or dispose of payloads, so memory accounting is thread-safe. Throttling is applied at the point of ingress, `chain_read()`. That function blocks when the limit is reached. However, parallel queues and later chain segments continue to function normally, so data naturally drains out of the pipeline over time. The memory budget is currently set at 512MB plus 10% of system memory beyond 4GB. There's no reason these parameters can't be made configurable in the future, but for now they're hard-coded. The budget is just an accounting limit and there is no up-front allocation. Typical processing of filesystem streams will not approach this limit. Memory accounting covers only payload data attached to DRR packets within a processing chain. When data is removed from a stream for separate handling (now achieved by calling `export_payload()`), it's no longer tracked by the standard memory accounting. Signed-off-by: Garth Snyder <garth@garthsnyder.com>
`zstream` reads stream record payloads into memory while processing a stream. Most subcommands use several parallel queues, which currently have queue lengths on the order of 1000 slots. Depending on the pipeline, there may be several thousand records in memory at any given moment. This is fine for typical filesystem send streams because most records aren't payload-bearing and default record sizes are modest. However, it's possible for streams to be far denser, as illustrated by this zvol send stream breakdown posted by @ryan-moeller in #18565: ``` SUMMARY: Total DRR_BEGIN records = 1 (0 bytes) Total DRR_END records = 1 (0 bytes) Total DRR_OBJECT records = 2 (0 bytes) Total DRR_FREEOBJECTS records = 1 (0 bytes) Total DRR_WRITE records = 4304609 (70131052544 bytes) Total DRR_WRITE_BYREF records = 0 (0 bytes) Total DRR_WRITE_EMBEDDED records = 645 (55664 bytes) Total DRR_FREE records = 2202 (0 bytes) Total DRR_SPILL records = 0 (0 bytes) Total DRR_OBJECT_RANGE records = 0 (0 bytes) Total DRR_REDACT records = 0 (0 bytes) Total records = 4307461 Total payload size = 70131108208 (0x105423c970) Total header overhead = 1343927832 (0x501ab618) Total stream length = 71475036040 (0x10a43e7f88) ``` This PR standardizes functions for manipulating payloads within `zstream` and adds memory accounting and read throttling. Modules anywhere within a processing chain may want to modify or dispose of payloads, so memory accounting is thread-safe. Throttling is applied at the point of ingress, `chain_read()`. That function blocks when the limit is reached. However, parallel queues and later chain segments continue to function normally, so data naturally drains out of the pipeline over time. The memory budget is currently set at 512MB plus 10% of system memory beyond 4GB. There's no reason these parameters can't be made configurable in the future, but for now they're hard-coded. The budget is just an accounting limit and there is no up-front allocation. Typical processing of filesystem streams will not approach this limit. Memory accounting covers only payload data attached to DRR packets within a processing chain. When data is removed from a stream for separate handling (now achieved by calling `export_payload()`), it's no longer tracked by the standard memory accounting. Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Signed-off-by: Garth Snyder <garth@garthsnyder.com> Closes #18982
Motivation and Context
There are situations where it is desirable to migrate a workload from a zvol to another format, such as a real device or a disk image file. For an offline migration, dd is adequate. When the zvol must remain in use during the operation, a snapshot can provide a consistent, stable state to copy, but alone this will still lose any changes that occur after the snapshot.
ZFS incremental replication offers a clean solution by capturing the bulk of the transfer in an initial snapshot, with a subsequent incremental snapshot to capture only the changes after the initial snapshot (thus completing much more quickly). The missing piece for this operation is the ability to apply zvol send streams to the new medium.
Description
Introduce a
zstream rawsubcommand that applies the operations in a zvol send stream to a raw device or regular file. The main stream operations of interest areWRITErecords which cause data to be written to the target volume andFREErecords which cause data to be discarded from the target volume.For now, writes are applied using
pwrite(2)(after decompression if applicable), and for generality frees are applied by writing zeros from a single zeroed page usingpwritev(2). Zvols are initialized with a large free to the end (-1) in the send stream, which is translated to a series offtruncate(2)operations for regular files or simply skipped for devices.Future work is underway for optimizing writes by combining contiguous writes into a single
pwritev(2)call and frees by issuingBLKDISCARD(on Linux). OtherFREEoptimizations such as hole punching for files or TRIM/UNMAP mechanisms for FreeBSD are not planned, but these would be welcome contributions.Support for the following stream features is implemented:
It is possible to apply streams repeatedly or even in the wrong order, as no ZFS/stream metadata is preserved by the target volume. To offer some safety mechanism, the
zstream rawoperation prints out the ending snapshot guid at the completion of the stream operations. This can then optionally be provided to future invocations aszstream raw -g GUIDto sanity check thefromguidbefore applying the subsequent stream, aborting on mismatch. Handling of this GUID is left as the responsibility of the user.How Has This Been Tested?
A new ZTS test is added which does some basic verification of a few operations using dd and xxhash. Separate manual testing with XFS and ext4 was done but is not automated for the test suite. Exhaustive testing of every combination of features would add excessive time to test runs.
Types of changes
Checklist:
Signed-off-by.