mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-24 03:08:51 +03:00
Compare commits
270 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a8c2b7ebc6 | |||
| 2af898ee24 | |||
| c32c2f17d0 | |||
| 2254b2bbbe | |||
| 5c4ec382a7 | |||
| e22bfd8149 | |||
| f45ad7bff6 | |||
| 0a3a4d067a | |||
| ba8024a284 | |||
| edc2675aed | |||
| 98bb45e27a | |||
| 44f463824b | |||
| b0d579bc55 | |||
| 7e5def8ae0 | |||
| 89019a846b | |||
| 41f7723e9c | |||
| 2b8c3cb0c8 | |||
| f325d76e96 | |||
| e3fb781c5f | |||
| 14a5e48fb9 | |||
| 01937958ce | |||
| edb504f9db | |||
| 2428fbbfcf | |||
| 16d298188f | |||
| f42f8702ce | |||
| 9e58d5ef38 | |||
| 320f9de8ab | |||
| 262275ab26 | |||
| 55f39a01e6 | |||
| b884768e46 | |||
| f8f4e13776 | |||
| 5f07d51751 | |||
| b2f003c4f4 | |||
| 9014da2b01 | |||
| 45579c9515 | |||
| b32f1279d4 | |||
| 1b0cd07131 | |||
| 8c6867dae4 | |||
| 99310c0aa0 | |||
| d126980e5f | |||
| 88ef5b238b | |||
| 30d8b85702 | |||
| 45f0437912 | |||
| dc3eea871a | |||
| d2c8103a68 | |||
| 3ea1f7f193 | |||
| 4356dd23a9 | |||
| 75318ec497 | |||
| c1629734ab | |||
| 778290d5bc | |||
| 98bc8e0b23 | |||
| caafa436eb | |||
| fe8de1c8a6 | |||
| 1bd93ea1e0 | |||
| 6857950e46 | |||
| 716ce2b89e | |||
| 9daae583d8 | |||
| b5ee3df776 | |||
| 17cd9a8e0c | |||
| 2a16d4cfaf | |||
| 3350a33908 | |||
| 3eef58c9b6 | |||
| 4805781c74 | |||
| b06f40ea9b | |||
| 6b5cc49d81 | |||
| ef7a79488a | |||
| a2f759146d | |||
| f79c0de208 | |||
| 1667816089 | |||
| d1ed1be3cd | |||
| e749242a99 | |||
| 9267ef84fd | |||
| 0ee129199f | |||
| 245be00597 | |||
| c38d702330 | |||
| 3f729907c8 | |||
| cca220d7c6 | |||
| 4ed30958ce | |||
| 2f118072cb | |||
| 7440f10ec1 | |||
| 8bb800d6b4 | |||
| bbf61c118f | |||
| d296b09456 | |||
| 5ac017fc04 | |||
| f5ecab3aef | |||
| fd01167ffd | |||
| 3b118f0a34 | |||
| ebe443c8ff | |||
| 63f3396233 | |||
| 2deb4526ee | |||
| a1662ffcaa | |||
| ea921bf6a6 | |||
| 6e627cc468 | |||
| 3eb3a13628 | |||
| c234706270 | |||
| 6059ba27c4 | |||
| 927f40d089 | |||
| 6356d50e67 | |||
| bd69ae3b53 | |||
| 9a2e90c9fc | |||
| 240ccfc13a | |||
| c30e716c81 | |||
| 267fd7b0f1 | |||
| dc0176eeec | |||
| 0a0af41bd9 | |||
| 3808006edf | |||
| c17922b8a9 | |||
| 8d7f17798d | |||
| 6dc40e2ada | |||
| 792f88131c | |||
| 33bb1e8256 | |||
| bcaba38e42 | |||
| 5e3085e360 | |||
| 99920d823e | |||
| 338523dd6e | |||
| 2644784f49 | |||
| b1f61f05b4 | |||
| e5ba614d05 | |||
| 30ac8de48a | |||
| c705d8386b | |||
| d5b10b3ef3 | |||
| 5a84c60fb9 | |||
| 26941ce90b | |||
| 07ce5d7390 | |||
| 1d805a534b | |||
| a9ff89e05c | |||
| d85011ed69 | |||
| b3da003ebf | |||
| 478754a8f5 | |||
| 31ff122aa2 | |||
| 18c662b845 | |||
| c797f0898e | |||
| 5e566c5772 | |||
| 23227313a2 | |||
| 3713b73335 | |||
| 310e63dfd1 | |||
| a196b3bc3d | |||
| a58e1284d8 | |||
| f1dde3fb20 | |||
| 5f38142e7b | |||
| 29b79dcfe9 | |||
| ecc972c7f0 | |||
| 6c891ade8b | |||
| 03658d5081 | |||
| 5d62588032 | |||
| 6897ea475f | |||
| 3790bfa80f | |||
| 6b278f3223 | |||
| f1236ebf35 | |||
| 184087f822 | |||
| 834815e9f7 | |||
| 0f1ff38476 | |||
| e3b28e16ce | |||
| 2f62fdd644 | |||
| 137b3e6cff | |||
| d1630dda58 | |||
| 701ebd014a | |||
| 5b8ec2cf39 | |||
| 9d1a39cec6 | |||
| 2a7b736dce | |||
| ecc8af1812 | |||
| 129e3e8dc3 | |||
| 9fb09f79e5 | |||
| a2ee6568c6 | |||
| 9c1a8eaa51 | |||
| a8fa31b50b | |||
| 8d82a19def | |||
| c2aacf2087 | |||
| 9a6c57845a | |||
| d27a40d28f | |||
| aebc5df418 | |||
| 7a8bef3983 | |||
| d486dee89e | |||
| 7de8fb33a2 | |||
| 904c03672b | |||
| 03955e3488 | |||
| 88e4e0d5dd | |||
| 03f638a8ef | |||
| 5dc25de668 | |||
| 53e5890cff | |||
| a94447ddf3 | |||
| 7192ec7942 | |||
| 06acbbc429 | |||
| 6116bbd744 | |||
| a803eacf26 | |||
| 504bfc8b49 | |||
| 53a8cbd70e | |||
| 505b97ae20 | |||
| 30a64ebaed | |||
| da16fc5739 | |||
| 3c7fa6ca33 | |||
| 36e0ddb744 | |||
| cf21b5b5b2 | |||
| 1030f807ba | |||
| d45702bcfa | |||
| ddd20dbe0b | |||
| 4a98780933 | |||
| 6db8f1a0d1 | |||
| e06711412b | |||
| 68ba1d2fa9 | |||
| 4e11137989 | |||
| be9be1cc3e | |||
| eab4536081 | |||
| 954516cec1 | |||
| 841cb5ee2a | |||
| d4cf31275b | |||
| fedc1d96a8 | |||
| 59511072b4 | |||
| a5c8119eba | |||
| d7881a6dca | |||
| 951e62169e | |||
| 9add19b37d | |||
| b2d633202d | |||
| 414f4a9c54 | |||
| 1c4f5e7d92 | |||
| 246e515cf8 | |||
| 2d41e75e52 | |||
| d834d6811b | |||
| 029a1b0c20 | |||
| c45254b0ec | |||
| 318fdeb51f | |||
| d3d20bf442 | |||
| 99598264fc | |||
| abe30b7b40 | |||
| f90ee0ca3d | |||
| 4ed955e280 | |||
| 1721f13e76 | |||
| 6e893ef62a | |||
| e0eaaf8144 | |||
| cb8a074dcb | |||
| c3ac4ccabb | |||
| 8d688ce66a | |||
| b544fe4123 | |||
| 926c6ec453 | |||
| 91b2f6ab1c | |||
| 851a7cd833 | |||
| 83d4d1a784 | |||
| 80cc2f6111 | |||
| b97948276d | |||
| 4cfc086e4d | |||
| 25d232f407 | |||
| edd7c24623 | |||
| bef6a8bc3a | |||
| 266b181e75 | |||
| c474f5e9a7 | |||
| 4e6a9e4598 | |||
| 661907e6bc | |||
| d3e7d981d4 | |||
| a2a0440918 | |||
| 45d1abc74d | |||
| 89950722c6 | |||
| 4810a108e8 | |||
| ae5b4a05ff | |||
| 3468fdbd34 | |||
| fb3f1fdbd6 | |||
| 426563be70 | |||
| aec4318870 | |||
| 2d9b57d39f | |||
| 4bdb8fcfa8 | |||
| 58c1c40a5e | |||
| 751575fe6f | |||
| 751941e248 | |||
| ef605a5517 | |||
| 8eb6dcec7d | |||
| 07cbcd5089 | |||
| 12acabe2a4 | |||
| 20c88dc3ef | |||
| 0c8fedeb35 | |||
| affb7141d7 | |||
| e0031d86b7 |
@@ -1,10 +0,0 @@
|
||||
root = true
|
||||
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.{c,h}]
|
||||
tab_width = 8
|
||||
indent_style = tab
|
||||
+66
-122
@@ -1,12 +1,10 @@
|
||||
# Contributing to OpenZFS
|
||||
<p align="center">
|
||||
<img alt="OpenZFS Logo"
|
||||
src="https://openzfs.github.io/openzfs-docs/_static/img/logo/480px-Open-ZFS-Secondary-Logo-Colour-halfsize.png"/>
|
||||
</p>
|
||||
# Contributing to ZFS on Linux
|
||||
<p align="center"><img src="http://zfsonlinux.org/images/zfs-linux.png"/></p>
|
||||
|
||||
*First of all, thank you for taking the time to contribute!*
|
||||
|
||||
By using the following guidelines, you can help us make OpenZFS even better.
|
||||
By using the following guidelines, you can help us make ZFS on Linux even
|
||||
better.
|
||||
|
||||
## Table Of Contents
|
||||
[What should I know before I get
|
||||
@@ -29,22 +27,19 @@ started?](#what-should-i-know-before-i-get-started)
|
||||
* [Commit Message Formats](#commit-message-formats)
|
||||
* [New Changes](#new-changes)
|
||||
* [OpenZFS Patch Ports](#openzfs-patch-ports)
|
||||
* [Coverity Defect Fixes](#coverity-defect-fixes)
|
||||
* [Signed Off By](#signed-off-by)
|
||||
|
||||
Helpful resources
|
||||
|
||||
* [OpenZFS Documentation](https://openzfs.github.io/openzfs-docs/)
|
||||
* [OpenZFS Developer Resources](http://open-zfs.org/wiki/Developer_resources)
|
||||
* [Git and GitHub for beginners](https://openzfs.github.io/openzfs-docs/Developer%20Resources/Git%20and%20GitHub%20for%20beginners.html)
|
||||
* [ZFS on Linux wiki](https://github.com/zfsonlinux/zfs/wiki)
|
||||
* [OpenZFS Documentation](http://open-zfs.org/wiki/Developer_resources)
|
||||
|
||||
## What should I know before I get started?
|
||||
|
||||
### Get ZFS
|
||||
You can build zfs packages by following [these
|
||||
instructions](https://openzfs.github.io/openzfs-docs/Developer%20Resources/Building%20ZFS.html),
|
||||
instructions](https://github.com/zfsonlinux/zfs/wiki/Building-ZFS),
|
||||
or install stable packages from [your distribution's
|
||||
repository](https://openzfs.github.io/openzfs-docs/Getting%20Started/index.html).
|
||||
repository](https://github.com/zfsonlinux/zfs/wiki/Getting-Started).
|
||||
|
||||
### Debug ZFS
|
||||
A variety of methods and tools are available to aid ZFS developers.
|
||||
@@ -53,30 +48,28 @@ configure option should be set. This will enable additional correctness
|
||||
checks and all the ASSERTs to help quickly catch potential issues.
|
||||
|
||||
In addition, there are numerous utilities and debugging files which
|
||||
provide visibility into the inner workings of ZFS. The most useful
|
||||
of these tools are discussed in detail on the [Troubleshooting
|
||||
page](https://openzfs.github.io/openzfs-docs/Basic%20Concepts/Troubleshooting.html).
|
||||
provide visibility in to the inner workings of ZFS. The most useful
|
||||
of these tools are discussed in detail on the [debugging ZFS wiki
|
||||
page](https://github.com/zfsonlinux/zfs/wiki/Debugging).
|
||||
|
||||
### Where can I ask for help?
|
||||
The [zfs-discuss mailing
|
||||
list](https://openzfs.github.io/openzfs-docs/Project%20and%20Community/Mailing%20Lists.html)
|
||||
or IRC are the best places to ask for help. Please do not file
|
||||
support requests on the GitHub issue tracker.
|
||||
The [mailing list](https://github.com/zfsonlinux/zfs/wiki/Mailing-Lists)
|
||||
is the best place to ask for help.
|
||||
|
||||
## How Can I Contribute?
|
||||
|
||||
### Reporting Bugs
|
||||
*Please* contact us via the [zfs-discuss mailing
|
||||
list](https://openzfs.github.io/openzfs-docs/Project%20and%20Community/Mailing%20Lists.html)
|
||||
or IRC if you aren't certain that you are experiencing a bug.
|
||||
*Please* contact us via the [mailing
|
||||
list](https://github.com/zfsonlinux/zfs/wiki/Mailing-Lists) if you aren't
|
||||
certain that you are experiencing a bug.
|
||||
|
||||
If you run into an issue, please search our [issue
|
||||
tracker](https://github.com/openzfs/zfs/issues) *first* to ensure the
|
||||
tracker](https://github.com/zfsonlinux/zfs/issues) *first* to ensure the
|
||||
issue hasn't been reported before. Open a new issue only if you haven't
|
||||
found anything similar to your issue.
|
||||
|
||||
You can open a new issue and search existing issues using the public [issue
|
||||
tracker](https://github.com/openzfs/zfs/issues).
|
||||
tracker](https://github.com/zfsonlinux/zfs/issues).
|
||||
|
||||
#### When opening a new issue, please include the following information at the top of the issue:
|
||||
* What distribution (with version) you are using.
|
||||
@@ -108,13 +101,13 @@ information like:
|
||||
* Stack traces which may be logged to `dmesg`.
|
||||
|
||||
### Suggesting Enhancements
|
||||
OpenZFS is a widely deployed production filesystem which is under active
|
||||
development. The team's primary focus is on fixing known issues, improving
|
||||
performance, and adding compelling new features.
|
||||
ZFS on Linux is a widely deployed production filesystem which is under
|
||||
active development. The team's primary focus is on fixing known issues,
|
||||
improving performance, and adding compelling new features.
|
||||
|
||||
You can view the list of proposed features
|
||||
by filtering the issue tracker by the ["Type: Feature"
|
||||
label](https://github.com/openzfs/zfs/issues?q=is%3Aopen+is%3Aissue+label%3A%22Type%3A+Feature%22).
|
||||
by filtering the issue tracker by the ["Feature"
|
||||
label](https://github.com/zfsonlinux/zfs/issues?q=is%3Aopen+is%3Aissue+label%3AFeature).
|
||||
If you have an idea for a feature first check this list. If your idea already
|
||||
appears then add a +1 to the top most comment, this helps us gauge interest
|
||||
in that feature.
|
||||
@@ -123,11 +116,8 @@ Otherwise, open a new issue and describe your proposed feature. Why is this
|
||||
feature needed? What problem does it solve?
|
||||
|
||||
### Pull Requests
|
||||
|
||||
#### General
|
||||
|
||||
* All pull requests, except backports and releases, must be based on the current master branch
|
||||
and should apply without conflicts.
|
||||
* All pull requests must be based on the current master branch and apply
|
||||
without conflicts.
|
||||
* Please attempt to limit pull requests to a single commit which resolves
|
||||
one specific issue.
|
||||
* Make sure your commit messages are in the correct format. See the
|
||||
@@ -139,28 +129,16 @@ logically independent patches which build on each other. This makes large
|
||||
changes easier to review and approve which speeds up the merging process.
|
||||
* Try to keep pull requests simple. Simple code with comments is much easier
|
||||
to review and approve.
|
||||
* All proposed changes must be approved by an OpenZFS organization member.
|
||||
* If you have an idea you'd like to discuss or which requires additional testing, consider opening it as a draft pull request.
|
||||
Once everything is in good shape and the details have been worked out you can remove its draft status.
|
||||
Any required reviews can then be finalized and the pull request merged.
|
||||
|
||||
#### Tests and Benchmarks
|
||||
* Every pull request will by tested by the buildbot on multiple platforms by running the [zfs-tests.sh and zloop.sh](
|
||||
https://openzfs.github.io/openzfs-docs/Developer%20Resources/Building%20ZFS.html#running-zloop-sh-and-zfs-tests-sh) test suites.
|
||||
* To verify your changes conform to the [style guidelines](
|
||||
https://github.com/openzfs/zfs/blob/master/.github/CONTRIBUTING.md#style-guides
|
||||
), please run `make checkstyle` and resolve any warnings.
|
||||
* Static code analysis of each pull request is performed by the buildbot; run `make lint` to check your changes.
|
||||
* Test cases should be provided when appropriate.
|
||||
This includes making sure new features have adequate code coverage.
|
||||
* If your pull request improves performance, please include some benchmarks.
|
||||
* The pull request must pass all required [ZFS
|
||||
Buildbot](http://build.zfsonlinux.org/) builders before
|
||||
being accepted. If you are experiencing intermittent TEST
|
||||
builder failures, you may be experiencing a [test suite
|
||||
issue](https://github.com/openzfs/zfs/issues?q=is%3Aissue+is%3Aopen+label%3A%22Type%3A+Test+Suite%22).
|
||||
There are also various [buildbot options](https://openzfs.github.io/openzfs-docs/Developer%20Resources/Buildbot%20Options.html)
|
||||
issue](https://github.com/zfsonlinux/zfs/issues?q=is%3Aissue+is%3Aopen+label%3A%22Test+Suite%22).
|
||||
There are also various [buildbot options](https://github.com/zfsonlinux/zfs/wiki/Buildbot-Options)
|
||||
to control how changes are tested.
|
||||
* All proposed changes must be approved by a ZFS on Linux organization member.
|
||||
|
||||
### Testing
|
||||
All help is appreciated! If you're in a position to run the latest code
|
||||
@@ -170,41 +148,16 @@ range of realistic workloads, configurations and architectures we're better
|
||||
able quickly identify and resolve potential issues.
|
||||
|
||||
Users can also run the [ZFS Test
|
||||
Suite](https://github.com/openzfs/zfs/tree/master/tests) on their systems
|
||||
Suite](https://github.com/zfsonlinux/zfs/tree/master/tests) on their systems
|
||||
to verify ZFS is behaving as intended.
|
||||
|
||||
## Style Guides
|
||||
|
||||
### Repository Structure
|
||||
|
||||
OpenZFS uses a standardised branching structure.
|
||||
- The "development and main branch", is the branch all development should be based on.
|
||||
- "Release branches" contain the latest released code for said version.
|
||||
- "Staging branches" contain selected commits prior to being released.
|
||||
|
||||
**Branch Names:**
|
||||
- Development and Main branch: `master`
|
||||
- Release branches: `zfs-$VERSION-release`
|
||||
- Staging branches: `zfs-$VERSION-staging`
|
||||
|
||||
`$VERSION` should be replaced with the `major.minor` version number.
|
||||
_(This is the version number without the `.patch` version at the end)_
|
||||
|
||||
### Coding Conventions
|
||||
We currently use [C Style and Coding Standards for
|
||||
SunOS](http://www.cis.upenn.edu/%7Elee/06cse480/data/cstyle.ms.pdf) as our
|
||||
coding convention.
|
||||
|
||||
This repository has an `.editorconfig` file. If your editor [supports
|
||||
editorconfig](https://editorconfig.org/#download), it will
|
||||
automatically respect most of this project's whitespace preferences.
|
||||
|
||||
Additionally, Git can help warn on whitespace problems as well:
|
||||
|
||||
```
|
||||
git config --local core.whitespace trailing-space,space-before-tab,indent-with-non-tab,-tab-in-indent
|
||||
```
|
||||
|
||||
### Commit Message Formats
|
||||
#### New Changes
|
||||
Commit messages for new changes must meet the following guidelines:
|
||||
@@ -214,10 +167,18 @@ first line in the commit message.
|
||||
please summarize important information such as why the proposed
|
||||
approach was chosen or a brief description of the bug you are resolving.
|
||||
Each line of the body must be 72 characters or less.
|
||||
* The last line must be a `Signed-off-by:` tag. See the
|
||||
[Signed Off By](#signed-off-by) section for more information.
|
||||
* The last line must be a `Signed-off-by:` tag with the developer's
|
||||
name followed by their email. This is the developer's certification
|
||||
that they have the right to submit the patch for inclusion into
|
||||
the code base and indicates agreement to the [Developer's Certificate
|
||||
of Origin](https://www.kernel.org/doc/html/latest/process/submitting-patches.html#sign-your-work-the-developer-s-certificate-of-origin).
|
||||
Code without a proper signoff cannot be merged.
|
||||
|
||||
An example commit message for new changes is provided below.
|
||||
Git can append the `Signed-off-by` line to your commit messages. Simply
|
||||
provide the `-s` or `--signoff` option when performing a `git commit`.
|
||||
For more information about writing commit messages, visit [How to Write
|
||||
a Git Commit Message](https://chris.beams.io/posts/git-commit/).
|
||||
An example commit message is provided below.
|
||||
|
||||
```
|
||||
This line is a brief summary of your change
|
||||
@@ -230,52 +191,35 @@ attempting to solve.
|
||||
Signed-off-by: Contributor <contributor@email.com>
|
||||
```
|
||||
|
||||
#### Coverity Defect Fixes
|
||||
If you are submitting a fix to a
|
||||
[Coverity defect](https://scan.coverity.com/projects/zfsonlinux-zfs),
|
||||
the commit message should meet the following guidelines:
|
||||
* Provides a subject line in the format of
|
||||
`Fix coverity defects: CID dddd, dddd...` where `dddd` represents
|
||||
each CID fixed by the commit.
|
||||
* Provides a body which lists each Coverity defect and how it was corrected.
|
||||
* The last line must be a `Signed-off-by:` tag. See the
|
||||
[Signed Off By](#signed-off-by) section for more information.
|
||||
#### OpenZFS Patch Ports
|
||||
If you are porting an OpenZFS patch, the commit message must meet
|
||||
the following guidelines:
|
||||
* The first line must be the summary line from the OpenZFS commit.
|
||||
It must begin with `OpenZFS dddd - ` where `dddd` is the OpenZFS issue number.
|
||||
* Provides a `Authored by:` line to attribute the patch to the original author.
|
||||
* Provides the `Reviewed by:` and `Approved by:` lines from the original
|
||||
OpenZFS commit.
|
||||
* Provides a `Ported-by:` line with the developer's name followed by
|
||||
their email.
|
||||
* Provides a `OpenZFS-issue:` line which is a link to the original illumos
|
||||
issue.
|
||||
* Provides a `OpenZFS-commit:` line which links back to the original OpenZFS
|
||||
commit.
|
||||
* If necessary, provide some porting notes to describe any deviations from
|
||||
the original OpenZFS commit.
|
||||
|
||||
An example Coverity defect fix commit message is provided below.
|
||||
An example OpenZFS patch port commit message is provided below.
|
||||
```
|
||||
Fix coverity defects: CID 12345, 67890
|
||||
OpenZFS 1234 - Summary from the original OpenZFS commit
|
||||
|
||||
CID 12345: Logically dead code (DEADCODE)
|
||||
Authored by: Original Author <original@email.com>
|
||||
Reviewed by: Reviewer One <reviewer1@email.com>
|
||||
Reviewed by: Reviewer Two <reviewer2@email.com>
|
||||
Approved by: Approver One <approver1@email.com>
|
||||
Ported-by: ZFS Contributor <contributor@email.com>
|
||||
|
||||
Removed the if(var != 0) block because the condition could never be
|
||||
satisfied.
|
||||
Provide some porting notes here if necessary.
|
||||
|
||||
CID 67890: Resource Leak (RESOURCE_LEAK)
|
||||
|
||||
Ensure free is called after allocating memory in function().
|
||||
|
||||
Signed-off-by: Contributor <contributor@email.com>
|
||||
OpenZFS-issue: https://www.illumos.org/issues/1234
|
||||
OpenZFS-commit: https://github.com/openzfs/openzfs/commit/abcd1234
|
||||
```
|
||||
|
||||
#### Signed Off By
|
||||
A line tagged as `Signed-off-by:` must contain the developer's
|
||||
name followed by their email. This is the developer's certification
|
||||
that they have the right to submit the patch for inclusion into
|
||||
the code base and indicates agreement to the [Developer's Certificate
|
||||
of Origin](https://www.kernel.org/doc/html/latest/process/submitting-patches.html#sign-your-work-the-developer-s-certificate-of-origin).
|
||||
Code without a proper signoff cannot be merged.
|
||||
|
||||
Git can append the `Signed-off-by` line to your commit messages. Simply
|
||||
provide the `-s` or `--signoff` option when performing a `git commit`.
|
||||
For more information about writing commit messages, visit [How to Write
|
||||
a Git Commit Message](https://chris.beams.io/posts/git-commit/).
|
||||
|
||||
#### Co-authored By
|
||||
If someone else had part in your pull request, please add the following to the commit:
|
||||
`Co-authored-by: Name <gitregistered@email.address>`
|
||||
This is useful if their authorship was lost during squashing, rebasing, etc.,
|
||||
but may be used in any situation where there are co-authors.
|
||||
|
||||
The email address used here should be the same as on the GitHub profile of said user.
|
||||
If said user does not have their email address public, please use the following instead:
|
||||
`Co-authored-by: Name <[username]@users.noreply.github.com>`
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
<!--
|
||||
Thank you for reporting an issue.
|
||||
|
||||
*IMPORTANT* - Please search our issue tracker *before* making a new issue.
|
||||
If you cannot find a similar issue, then create a new issue.
|
||||
https://github.com/zfsonlinux/zfs/issues
|
||||
|
||||
*IMPORTANT* - This issue tracker is for *bugs* and *issues* only.
|
||||
Please search the wiki and the mailing list archives before asking
|
||||
questions on the mailing list.
|
||||
https://github.com/zfsonlinux/zfs/wiki/Mailing-Lists
|
||||
|
||||
Please fill in as much of the template as possible.
|
||||
-->
|
||||
|
||||
### System information
|
||||
<!-- add version after "|" character -->
|
||||
Type | Version/Name
|
||||
--- | ---
|
||||
Distribution Name |
|
||||
Distribution Version |
|
||||
Linux Kernel |
|
||||
Architecture |
|
||||
ZFS Version |
|
||||
SPL Version |
|
||||
<!--
|
||||
Commands to find ZFS/SPL versions:
|
||||
modinfo zfs | grep -iw version
|
||||
modinfo spl | grep -iw version
|
||||
-->
|
||||
|
||||
### Describe the problem you're observing
|
||||
|
||||
### Describe how to reproduce the problem
|
||||
|
||||
### Include any warning/errors/backtraces from the system logs
|
||||
<!--
|
||||
*IMPORTANT* - Please mark logs and text output from terminal commands
|
||||
or else Github will not display them correctly.
|
||||
An example is provided below.
|
||||
|
||||
Example:
|
||||
```
|
||||
this is an example how log text should be marked (wrap it with ```)
|
||||
```
|
||||
-->
|
||||
@@ -1,55 +0,0 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve OpenZFS
|
||||
title: ''
|
||||
labels: 'Type: Defect'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!-- Please fill out the following template, which will help other contributors address your issue. -->
|
||||
|
||||
<!--
|
||||
Thank you for reporting an issue.
|
||||
|
||||
*IMPORTANT* - Please check our issue tracker before opening a new issue.
|
||||
Additional valuable information can be found in the OpenZFS documentation
|
||||
and mailing list archives.
|
||||
|
||||
Please fill in as much of the template as possible.
|
||||
-->
|
||||
|
||||
### System information
|
||||
<!-- add version after "|" character -->
|
||||
Type | Version/Name
|
||||
--- | ---
|
||||
Distribution Name |
|
||||
Distribution Version |
|
||||
Kernel Version |
|
||||
Architecture |
|
||||
OpenZFS Version |
|
||||
<!--
|
||||
Command to find OpenZFS version:
|
||||
zfs version
|
||||
|
||||
Commands to find kernel version:
|
||||
uname -r # Linux
|
||||
freebsd-version -r # FreeBSD
|
||||
-->
|
||||
|
||||
### Describe the problem you're observing
|
||||
|
||||
### Describe how to reproduce the problem
|
||||
|
||||
### Include any warning/errors/backtraces from the system logs
|
||||
<!--
|
||||
*IMPORTANT* - Please mark logs and text output from terminal commands
|
||||
or else Github will not display them correctly.
|
||||
An example is provided below.
|
||||
|
||||
Example:
|
||||
```
|
||||
this is an example how log text should be marked (wrap it with ```)
|
||||
```
|
||||
-->
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: OpenZFS Questions
|
||||
url: https://github.com/openzfs/zfs/discussions/new
|
||||
about: Ask the community for help
|
||||
- name: OpenZFS Community Support Mailing list (Linux)
|
||||
url: https://zfsonlinux.topicbox.com/groups/zfs-discuss
|
||||
about: Get community support for OpenZFS on Linux
|
||||
- name: FreeBSD Community Support Mailing list
|
||||
url: https://lists.freebsd.org/mailman/listinfo/freebsd-fs
|
||||
about: Get community support for OpenZFS on FreeBSD
|
||||
- name: OpenZFS on IRC
|
||||
url: https://web.libera.chat/#openzfs
|
||||
about: Use IRC to get community support for OpenZFS
|
||||
@@ -1,33 +0,0 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest a feature for OpenZFS
|
||||
title: ''
|
||||
labels: 'Type: Feature'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
Thank you for suggesting a feature.
|
||||
|
||||
Please check our issue tracker before opening a new feature request.
|
||||
Filling out the following template will help other contributors better understand your proposed feature.
|
||||
-->
|
||||
|
||||
### Describe the feature would like to see added to OpenZFS
|
||||
|
||||
<!--
|
||||
Provide a clear and concise description of the feature.
|
||||
-->
|
||||
|
||||
### How will this feature improve OpenZFS?
|
||||
|
||||
<!--
|
||||
What problem does this feature solve?
|
||||
-->
|
||||
|
||||
### Additional context
|
||||
|
||||
<!--
|
||||
Any additional information you can add about the proposal?
|
||||
-->
|
||||
@@ -1,25 +1,22 @@
|
||||
<!--- Please fill out the following template, which will help other contributors review your Pull Request. -->
|
||||
|
||||
<!--- Provide a general summary of your changes in the Title above -->
|
||||
|
||||
<!---
|
||||
Documentation on ZFS Buildbot options can be found at
|
||||
https://openzfs.github.io/openzfs-docs/Developer%20Resources/Buildbot%20Options.html
|
||||
https://github.com/zfsonlinux/zfs/wiki/Buildbot-Options
|
||||
-->
|
||||
|
||||
### Description
|
||||
<!--- Describe your changes in detail -->
|
||||
|
||||
### Motivation and Context
|
||||
<!--- Why is this change required? What problem does it solve? -->
|
||||
<!--- If it fixes an open issue, please link to the issue here. -->
|
||||
|
||||
### Description
|
||||
<!--- Describe your changes in detail -->
|
||||
|
||||
### How Has This Been Tested?
|
||||
<!--- Please describe in detail how you tested your changes. -->
|
||||
<!--- Include details of your testing environment, and the tests you ran to -->
|
||||
<!--- see how your change affects other areas of the code, etc. -->
|
||||
<!--- If your change is a performance enhancement, please provide benchmarks here. -->
|
||||
<!--- Please think about using the draft PR feature if appropriate -->
|
||||
|
||||
### Types of changes
|
||||
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
|
||||
@@ -28,15 +25,15 @@ https://openzfs.github.io/openzfs-docs/Developer%20Resources/Buildbot%20Options.
|
||||
- [ ] Performance enhancement (non-breaking change which improves efficiency)
|
||||
- [ ] Code cleanup (non-breaking change which makes code smaller or more readable)
|
||||
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
|
||||
- [ ] Library ABI change (libzfs, libzfs\_core, libnvpair, libuutil and libzfsbootenv)
|
||||
- [ ] Documentation (a change to man pages or other documentation)
|
||||
|
||||
### Checklist:
|
||||
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
|
||||
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
|
||||
- [ ] My code follows the OpenZFS [code style requirements](https://github.com/openzfs/zfs/blob/master/.github/CONTRIBUTING.md#coding-conventions).
|
||||
- [ ] My code follows the ZFS on Linux code style requirements.
|
||||
- [ ] I have updated the documentation accordingly.
|
||||
- [ ] I have read the [**contributing** document](https://github.com/openzfs/zfs/blob/master/.github/CONTRIBUTING.md).
|
||||
- [ ] I have added [tests](https://github.com/openzfs/zfs/tree/master/tests) to cover my changes.
|
||||
- [ ] I have run the ZFS Test Suite with this change applied.
|
||||
- [ ] All commit messages are properly formatted and contain [`Signed-off-by`](https://github.com/openzfs/zfs/blob/master/.github/CONTRIBUTING.md#signed-off-by).
|
||||
- [ ] I have read the **CONTRIBUTING** document.
|
||||
- [ ] I have added tests to cover my changes.
|
||||
- [ ] All new and existing tests passed.
|
||||
- [ ] All commit messages are properly formatted and contain `Signed-off-by`.
|
||||
- [ ] Change has been approved by a ZFS on Linux member.
|
||||
|
||||
+17
-12
@@ -1,25 +1,30 @@
|
||||
codecov:
|
||||
notify:
|
||||
require_ci_to_pass: false # always post
|
||||
after_n_builds: 2 # user and kernel
|
||||
require_ci_to_pass: no
|
||||
|
||||
coverage:
|
||||
precision: 0 # 0 decimals of precision
|
||||
round: nearest # Round to nearest precision point
|
||||
range: "50...90" # red -> yellow -> green
|
||||
precision: 2
|
||||
round: down
|
||||
range: "50...100"
|
||||
|
||||
status:
|
||||
project:
|
||||
default:
|
||||
threshold: 1% # allow 1% coverage variance
|
||||
threshold: 1%
|
||||
|
||||
patch:
|
||||
default:
|
||||
threshold: 1% # allow 1% coverage variance
|
||||
threshold: 1%
|
||||
|
||||
parsers:
|
||||
gcov:
|
||||
branch_detection:
|
||||
conditional: yes
|
||||
loop: yes
|
||||
method: no
|
||||
macro: no
|
||||
|
||||
comment:
|
||||
layout: "reach, diff, flags, footer"
|
||||
behavior: once # update if exists; post new; skip if deleted
|
||||
require_changes: yes # only post when coverage changes
|
||||
|
||||
# ignore: Please place any ignores in config/ax_code_coverage.m4 instead
|
||||
layout: "header, sunburst, diff"
|
||||
behavior: default
|
||||
require_changes: no
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
# Configuration for probot-no-response - https://github.com/probot/no-response
|
||||
|
||||
# Number of days of inactivity before an Issue is closed for lack of response
|
||||
daysUntilClose: 31
|
||||
# Label requiring a response
|
||||
responseRequiredLabel: "Status: Feedback requested"
|
||||
# Comment to post when closing an Issue for lack of response. Set to `false` to disable
|
||||
closeComment: >
|
||||
This issue has been automatically closed because there has been no response
|
||||
to our request for more information from the original author. With only the
|
||||
information that is currently in the issue, we don't have enough information
|
||||
to take action. Please reach out if you have or find the answers we need so
|
||||
that we can investigate further.
|
||||
@@ -1,26 +0,0 @@
|
||||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 365
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 90
|
||||
# Limit to only `issues` or `pulls`
|
||||
only: issues
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- "Type: Feature"
|
||||
- "Bot: Not Stale"
|
||||
- "Status: Work in Progress"
|
||||
# Set to true to ignore issues in a project (defaults to false)
|
||||
exemptProjects: true
|
||||
# Set to true to ignore issues in a milestone (defaults to false)
|
||||
exemptMilestones: true
|
||||
# Set to true to ignore issues with an assignee (defaults to false)
|
||||
exemptAssignees: true
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: "Status: Stale"
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This issue has been automatically marked as "stale" because it has not had
|
||||
any activity for a while. It will be closed in 90 days if no further activity occurs.
|
||||
Thank you for your contributions.
|
||||
# Limit the number of actions per hour, from 1-30. Default is 30
|
||||
limitPerRun: 6
|
||||
@@ -0,0 +1,3 @@
|
||||
preprocessorErrorDirective:./module/zfs/vdev_raidz_math_avx512f.c:243
|
||||
preprocessorErrorDirective:./module/zfs/vdev_raidz_math_sse2.c:266
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
acl
|
||||
alien
|
||||
attr
|
||||
autoconf
|
||||
bc
|
||||
build-essential
|
||||
curl
|
||||
dbench
|
||||
debhelper-compat
|
||||
dh-python
|
||||
dkms
|
||||
fakeroot
|
||||
fio
|
||||
gdb
|
||||
gdebi
|
||||
git
|
||||
ksh
|
||||
lcov
|
||||
libacl1-dev
|
||||
libaio-dev
|
||||
libattr1-dev
|
||||
libblkid-dev
|
||||
libcurl4-openssl-dev
|
||||
libdevmapper-dev
|
||||
libelf-dev
|
||||
libffi-dev
|
||||
libmount-dev
|
||||
libpam0g-dev
|
||||
libselinux1-dev
|
||||
libssl-dev
|
||||
libtool
|
||||
libudev-dev
|
||||
linux-headers-generic
|
||||
lsscsi
|
||||
mdadm
|
||||
nfs-kernel-server
|
||||
pamtester
|
||||
parted
|
||||
po-debconf
|
||||
python3
|
||||
python3-all-dev
|
||||
python3-cffi
|
||||
python3-dev
|
||||
python3-packaging
|
||||
python3-pip
|
||||
python3-setuptools
|
||||
python3-sphinx
|
||||
rng-tools-debian
|
||||
rsync
|
||||
samba
|
||||
sysstat
|
||||
uuid-dev
|
||||
watchdog
|
||||
wget
|
||||
xfslibs-dev
|
||||
xz-utils
|
||||
zlib1g-dev
|
||||
@@ -1,2 +0,0 @@
|
||||
pax-utils
|
||||
shellcheck
|
||||
@@ -1,57 +0,0 @@
|
||||
name: checkstyle
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
checkstyle:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
# https://github.com/orgs/community/discussions/47863
|
||||
sudo apt-mark hold grub-efi-amd64-signed
|
||||
sudo apt-get update --fix-missing
|
||||
sudo apt-get upgrade
|
||||
sudo xargs --arg-file=${{ github.workspace }}/.github/workflows/build-dependencies.txt apt-get install -qq
|
||||
sudo xargs --arg-file=${{ github.workspace }}/.github/workflows/checkstyle-dependencies.txt apt-get install -qq
|
||||
sudo python3 -m pip install --quiet flake8
|
||||
sudo apt-get clean
|
||||
|
||||
# confirm that the tools are installed
|
||||
# the build system doesn't fail when they are not
|
||||
flake8 --version
|
||||
scanelf --version
|
||||
shellcheck --version
|
||||
- name: Prepare
|
||||
run: |
|
||||
sh ./autogen.sh
|
||||
./configure
|
||||
make -j$(nproc)
|
||||
- name: Checkstyle
|
||||
run: |
|
||||
make checkstyle
|
||||
- name: Lint
|
||||
run: |
|
||||
make lint
|
||||
- name: CheckABI
|
||||
id: CheckABI
|
||||
run: |
|
||||
sudo docker run -v $(pwd):/source ghcr.io/openzfs/libabigail make checkabi
|
||||
- name: StoreABI
|
||||
if: failure() && steps.CheckABI.outcome == 'failure'
|
||||
run: |
|
||||
sudo docker run -v $(pwd):/source ghcr.io/openzfs/libabigail make storeabi
|
||||
- name: Prepare artifacts
|
||||
if: failure() && steps.CheckABI.outcome == 'failure'
|
||||
run: |
|
||||
find -name *.abi | tar -cf abi_files.tar -T -
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: failure() && steps.CheckABI.outcome == 'failure'
|
||||
with:
|
||||
name: New ABI files (use only if you're sure about interface changes)
|
||||
path: abi_files.tar
|
||||
@@ -1,23 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -eu
|
||||
|
||||
# remove 4GiB of images
|
||||
sudo systemd-run docker system prune --force --all --volumes
|
||||
|
||||
# remove unused software
|
||||
sudo systemd-run --wait rm -rf \
|
||||
"$AGENT_TOOLSDIRECTORY" \
|
||||
/opt/* \
|
||||
/usr/local/* \
|
||||
/usr/share/az* \
|
||||
/usr/share/dotnet \
|
||||
/usr/share/gradle* \
|
||||
/usr/share/miniconda \
|
||||
/usr/share/swift \
|
||||
/var/lib/gems \
|
||||
/var/lib/mysql \
|
||||
/var/lib/snapd
|
||||
|
||||
# trim the cleaned space
|
||||
sudo fstrim /
|
||||
@@ -1,81 +0,0 @@
|
||||
name: zfs-tests-functional
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
tests-functional-ubuntu:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [20.04, 22.04]
|
||||
runs-on: ubuntu-${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
# https://github.com/orgs/community/discussions/47863
|
||||
sudo apt-mark hold grub-efi-amd64-signed
|
||||
sudo apt-get update --fix-missing
|
||||
sudo apt-get upgrade
|
||||
sudo xargs --arg-file=${{ github.workspace }}/.github/workflows/build-dependencies.txt apt-get install -qq
|
||||
sudo apt-get clean
|
||||
- name: Autogen.sh
|
||||
run: |
|
||||
sh autogen.sh
|
||||
- name: Configure
|
||||
run: |
|
||||
./configure --enable-debug --enable-debuginfo
|
||||
- name: Make
|
||||
run: |
|
||||
make --no-print-directory -s pkg-utils pkg-kmod
|
||||
- name: Install
|
||||
run: |
|
||||
sudo dpkg -i *.deb
|
||||
# Update order of directories to search for modules, otherwise
|
||||
# Ubuntu will load kernel-shipped ones.
|
||||
sudo sed -i.bak 's/updates/extra updates/' /etc/depmod.d/ubuntu.conf
|
||||
sudo depmod
|
||||
sudo modprobe zfs
|
||||
# Workaround for cloud-init bug
|
||||
# see https://github.com/openzfs/zfs/issues/12644
|
||||
FILE=/lib/udev/rules.d/10-cloud-init-hook-hotplug.rules
|
||||
if [ -r "${FILE}" ]; then
|
||||
HASH=$(md5sum "${FILE}" | awk '{ print $1 }')
|
||||
if [ "${HASH}" = "121ff0ef1936cd2ef65aec0458a35772" ]; then
|
||||
# Just shove a zd* exclusion right above the hotplug hook...
|
||||
sudo sed -i -e s/'LABEL="cloudinit_hook"'/'KERNEL=="zd*", GOTO="cloudinit_end"\n&'/ "${FILE}"
|
||||
sudo udevadm control --reload-rules
|
||||
fi
|
||||
fi
|
||||
- name: Clear the kernel ring buffer
|
||||
run: |
|
||||
sudo dmesg -c >/var/tmp/dmesg-prerun
|
||||
- name: Reclaim and report disk space
|
||||
run: |
|
||||
${{ github.workspace }}/.github/workflows/scripts/reclaim_disk_space.sh
|
||||
df -h /
|
||||
- name: Tests
|
||||
run: |
|
||||
/usr/share/zfs/zfs-tests.sh -vR -s 3G
|
||||
timeout-minutes: 330
|
||||
- name: Prepare artifacts
|
||||
if: failure()
|
||||
run: |
|
||||
RESULTS_PATH=$(readlink -f /var/tmp/test_results/current)
|
||||
sudo dmesg > $RESULTS_PATH/dmesg
|
||||
sudo cp /var/log/syslog /var/tmp/dmesg-prerun $RESULTS_PATH/
|
||||
sudo chmod +r $RESULTS_PATH/*
|
||||
# Replace ':' in dir names, actions/upload-artifact doesn't support it
|
||||
for f in $(find /var/tmp/test_results -name '*:*'); do mv "$f" "${f//:/__}"; done
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: failure()
|
||||
with:
|
||||
name: Test logs Ubuntu-${{ matrix.os }}
|
||||
path: |
|
||||
/var/tmp/test_results/*
|
||||
!/var/tmp/test_results/current
|
||||
if-no-files-found: ignore
|
||||
@@ -1,77 +0,0 @@
|
||||
name: zfs-tests-sanity
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
# https://github.com/orgs/community/discussions/47863
|
||||
sudo apt-mark hold grub-efi-amd64-signed
|
||||
sudo apt-get update --fix-missing
|
||||
sudo apt-get upgrade
|
||||
sudo xargs --arg-file=${{ github.workspace }}/.github/workflows/build-dependencies.txt apt-get install -qq
|
||||
sudo apt-get clean
|
||||
- name: Autogen.sh
|
||||
run: |
|
||||
sh autogen.sh
|
||||
- name: Configure
|
||||
run: |
|
||||
./configure --enable-debug --enable-debuginfo
|
||||
- name: Make
|
||||
run: |
|
||||
make --no-print-directory -s pkg-utils pkg-kmod
|
||||
- name: Install
|
||||
run: |
|
||||
sudo dpkg -i *.deb
|
||||
# Update order of directories to search for modules, otherwise
|
||||
# Ubuntu will load kernel-shipped ones.
|
||||
sudo sed -i.bak 's/updates/extra updates/' /etc/depmod.d/ubuntu.conf
|
||||
sudo depmod
|
||||
sudo modprobe zfs
|
||||
# Workaround for cloud-init bug
|
||||
# see https://github.com/openzfs/zfs/issues/12644
|
||||
FILE=/lib/udev/rules.d/10-cloud-init-hook-hotplug.rules
|
||||
if [ -r "${FILE}" ]; then
|
||||
HASH=$(md5sum "${FILE}" | awk '{ print $1 }')
|
||||
if [ "${HASH}" = "121ff0ef1936cd2ef65aec0458a35772" ]; then
|
||||
# Just shove a zd* exclusion right above the hotplug hook...
|
||||
sudo sed -i -e s/'LABEL="cloudinit_hook"'/'KERNEL=="zd*", GOTO="cloudinit_end"\n&'/ "${FILE}"
|
||||
sudo udevadm control --reload-rules
|
||||
fi
|
||||
fi
|
||||
- name: Clear the kernel ring buffer
|
||||
run: |
|
||||
sudo dmesg -c >/var/tmp/dmesg-prerun
|
||||
- name: Reclaim and report disk space
|
||||
run: |
|
||||
${{ github.workspace }}/.github/workflows/scripts/reclaim_disk_space.sh
|
||||
df -h /
|
||||
- name: Tests
|
||||
run: |
|
||||
/usr/share/zfs/zfs-tests.sh -vR -s 3G -r sanity
|
||||
timeout-minutes: 330
|
||||
- name: Prepare artifacts
|
||||
if: failure()
|
||||
run: |
|
||||
RESULTS_PATH=$(readlink -f /var/tmp/test_results/current)
|
||||
sudo dmesg > $RESULTS_PATH/dmesg
|
||||
sudo cp /var/log/syslog /var/tmp/dmesg-prerun $RESULTS_PATH/
|
||||
sudo chmod +r $RESULTS_PATH/*
|
||||
# Replace ':' in dir names, actions/upload-artifact doesn't support it
|
||||
for f in $(find /var/tmp/test_results -name '*:*'); do mv "$f" "${f//:/__}"; done
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: failure()
|
||||
with:
|
||||
name: Test logs Ubuntu-${{ matrix.os }}
|
||||
path: |
|
||||
/var/tmp/test_results/*
|
||||
!/var/tmp/test_results/current
|
||||
if-no-files-found: ignore
|
||||
@@ -1,65 +0,0 @@
|
||||
name: zloop
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
runs-on: ubuntu-22.04
|
||||
env:
|
||||
TEST_DIR: /var/tmp/zloop
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
# https://github.com/orgs/community/discussions/47863
|
||||
sudo apt-mark hold grub-efi-amd64-signed
|
||||
sudo apt-get update --fix-missing
|
||||
sudo apt-get upgrade
|
||||
sudo xargs --arg-file=${{ github.workspace }}/.github/workflows/build-dependencies.txt apt-get install -qq
|
||||
sudo apt-get clean
|
||||
- name: Autogen.sh
|
||||
run: |
|
||||
sh autogen.sh
|
||||
- name: Configure
|
||||
run: |
|
||||
./configure --enable-debug --enable-debuginfo
|
||||
- name: Make
|
||||
run: |
|
||||
make --no-print-directory -s pkg-utils pkg-kmod
|
||||
- name: Install
|
||||
run: |
|
||||
sudo dpkg -i *.deb
|
||||
# Update order of directories to search for modules, otherwise
|
||||
# Ubuntu will load kernel-shipped ones.
|
||||
sudo sed -i.bak 's/updates/extra updates/' /etc/depmod.d/ubuntu.conf
|
||||
sudo depmod
|
||||
sudo modprobe zfs
|
||||
- name: Tests
|
||||
run: |
|
||||
sudo mkdir -p $TEST_DIR
|
||||
# run for 10 minutes or at most 2 iterations for a maximum runner
|
||||
# time of 20 minutes.
|
||||
sudo /usr/share/zfs/zloop.sh -t 600 -I 2 -l -m1 -- -T 120 -P 60
|
||||
- name: Prepare artifacts
|
||||
if: failure()
|
||||
run: |
|
||||
sudo chmod +r -R $TEST_DIR/
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: failure()
|
||||
with:
|
||||
name: Logs
|
||||
path: |
|
||||
/var/tmp/zloop/*/
|
||||
!/var/tmp/zloop/*/vdev/
|
||||
if-no-files-found: ignore
|
||||
- uses: actions/upload-artifact@v3
|
||||
if: failure()
|
||||
with:
|
||||
name: Pool files
|
||||
path: |
|
||||
/var/tmp/zloop/*/vdev/
|
||||
if-no-files-found: ignore
|
||||
+2
-11
@@ -14,7 +14,6 @@
|
||||
# Normal rules
|
||||
#
|
||||
*.[oa]
|
||||
*.o.ur-safe
|
||||
*.lo
|
||||
*.la
|
||||
*.mod.c
|
||||
@@ -22,8 +21,6 @@
|
||||
*.swp
|
||||
*.gcno
|
||||
*.gcda
|
||||
*.pyc
|
||||
*.pyo
|
||||
.deps
|
||||
.libs
|
||||
.dirstamp
|
||||
@@ -36,7 +33,6 @@ Makefile.in
|
||||
# Top level generated files specific to this top level dir
|
||||
#
|
||||
/bin
|
||||
/build
|
||||
/configure
|
||||
/config.log
|
||||
/config.status
|
||||
@@ -45,6 +41,8 @@ Makefile.in
|
||||
/zfs_config.h.in
|
||||
/zfs.release
|
||||
/stamp-h1
|
||||
/.script-config
|
||||
/zfs-script-config.sh
|
||||
/aclocal.m4
|
||||
/autom4te.cache
|
||||
|
||||
@@ -61,10 +59,3 @@ cscope.*
|
||||
*.tar.gz
|
||||
*.patch
|
||||
*.orig
|
||||
*.log
|
||||
*.tmp
|
||||
venv
|
||||
|
||||
*.so
|
||||
*.so.debug
|
||||
*.so.full
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
[submodule "scripts/zfs-images"]
|
||||
path = scripts/zfs-images
|
||||
url = https://github.com/openzfs/zfs-images
|
||||
url = https://github.com/zfsonlinux/zfs-images
|
||||
|
||||
@@ -1,308 +1,95 @@
|
||||
MAINTAINERS:
|
||||
Brian Behlendorf is the principal developer of the ZFS on Linux port.
|
||||
He works full time as a computer scientist at Lawrence Livermore
|
||||
National Laboratory on the ZFS and Lustre filesystems. However,
|
||||
this port would not have been possible without the help of many
|
||||
others who have contributed their time, effort, and insight.
|
||||
|
||||
Brian Behlendorf <behlendorf1@llnl.gov>
|
||||
Tony Hutter <hutter2@llnl.gov>
|
||||
Brian Behlendorf <behlendorf1@llnl.gov>
|
||||
|
||||
PAST MAINTAINERS:
|
||||
First and foremost the hard working ZFS developers at Sun/Oracle.
|
||||
They are responsible for the bulk of the code in this project and
|
||||
without their efforts there never would have been a ZFS filesystem.
|
||||
|
||||
Ned Bass <bass6@llnl.gov>
|
||||
The ZFS Development Team at Sun/Oracle
|
||||
|
||||
CONTRIBUTORS:
|
||||
Next all the developers at KQ Infotech who implemented a prototype
|
||||
ZFS Posix Layer (ZPL). Their implementation provided an excellent
|
||||
reference for adding the ZPL functionality.
|
||||
|
||||
Aaron Fineman <abyxcos@gmail.com>
|
||||
Adam Leventhal <ahl@delphix.com>
|
||||
Adam Stevko <adam.stevko@gmail.com>
|
||||
Ahmed G <ahmedg@delphix.com>
|
||||
Akash Ayare <aayare@delphix.com>
|
||||
Alan Somers <asomers@gmail.com>
|
||||
Alar Aun <spamtoaun@gmail.com>
|
||||
Albert Lee <trisk@nexenta.com>
|
||||
Alec Salazar <alec.j.salazar@gmail.com>
|
||||
Alejandro R. Sedeño <asedeno@mit.edu>
|
||||
Alek Pinchuk <alek@nexenta.com>
|
||||
Alex Braunegg <alex.braunegg@gmail.com>
|
||||
Alex McWhirter <alexmcwhirter@triadic.us>
|
||||
Alex Reece <alex@delphix.com>
|
||||
Alex Wilson <alex.wilson@joyent.com>
|
||||
Alex Zhuravlev <alexey.zhuravlev@intel.com>
|
||||
Alexander Eremin <a.eremin@nexenta.com>
|
||||
Alexander Motin <mav@freebsd.org>
|
||||
Alexander Pyhalov <apyhalov@gmail.com>
|
||||
Alexander Stetsenko <ams@nexenta.com>
|
||||
Alexey Shvetsov <alexxy@gentoo.org>
|
||||
Alexey Smirnoff <fling@member.fsf.org>
|
||||
Allan Jude <allanjude@freebsd.org>
|
||||
AndCycle <andcycle@andcycle.idv.tw>
|
||||
Andreas Buschmann <andreas.buschmann@tech.net.de>
|
||||
Andreas Dilger <adilger@intel.com>
|
||||
Andrew Barnes <barnes333@gmail.com>
|
||||
Andrew Hamilton <ahamilto@tjhsst.edu>
|
||||
Andrew Reid <ColdCanuck@nailedtotheperch.com>
|
||||
Andrew Stormont <andrew.stormont@nexenta.com>
|
||||
Andrew Tselischev <andrewtselischev@gmail.com>
|
||||
Andrey Vesnovaty <andrey.vesnovaty@gmail.com>
|
||||
Andriy Gapon <avg@freebsd.org>
|
||||
Andy Bakun <github@thwartedefforts.org>
|
||||
Aniruddha Shankar <k@191a.net>
|
||||
Antonio Russo <antonio.e.russo@gmail.com>
|
||||
Arkadiusz Bubała <arkadiusz.bubala@open-e.com>
|
||||
Arne Jansen <arne@die-jansens.de>
|
||||
Aron Xu <happyaron.xu@gmail.com>
|
||||
Bart Coddens <bart.coddens@gmail.com>
|
||||
Basil Crow <basil.crow@delphix.com>
|
||||
Huang Liu <liu.huang@zte.com.cn>
|
||||
Ben Allen <bsallen@alcf.anl.gov>
|
||||
Ben Rubson <ben.rubson@gmail.com>
|
||||
Benjamin Albrecht <git@albrecht.io>
|
||||
Bill McGonigle <bill-github.com-public1@bfccomputing.com>
|
||||
Bill Pijewski <wdp@joyent.com>
|
||||
Boris Protopopov <boris.protopopov@nexenta.com>
|
||||
Brad Lewis <brad.lewis@delphix.com>
|
||||
Brian Behlendorf <behlendorf1@llnl.gov>
|
||||
Brian J. Murrell <brian@sun.com>
|
||||
Caleb James DeLisle <calebdelisle@lavabit.com>
|
||||
Cao Xuewen <cao.xuewen@zte.com.cn>
|
||||
Carlo Landmeter <clandmeter@gmail.com>
|
||||
Carlos Alberto Lopez Perez <clopez@igalia.com>
|
||||
Chaoyu Zhang <zhang.chaoyu@zte.com.cn>
|
||||
Chen Can <chen.can2@zte.com.cn>
|
||||
Chen Haiquan <oc@yunify.com>
|
||||
Chip Parker <aparker@enthought.com>
|
||||
Chris Burroughs <chris.burroughs@gmail.com>
|
||||
Chris Dunlap <cdunlap@llnl.gov>
|
||||
Chris Dunlop <chris@onthe.net.au>
|
||||
Chris Siden <chris.siden@delphix.com>
|
||||
Chris Wedgwood <cw@f00f.org>
|
||||
Chris Williamson <chris.williamson@delphix.com>
|
||||
Chris Zubrzycki <github@mid-earth.net>
|
||||
Christ Schlacta <aarcane@aarcane.info>
|
||||
Christer Ekholm <che@chrekh.se>
|
||||
Christian Kohlschütter <christian@kohlschutter.com>
|
||||
Christian Neukirchen <chneukirchen@gmail.com>
|
||||
Christian Schwarz <me@cschwarz.com>
|
||||
Christopher Voltz <cjunk@voltz.ws>
|
||||
Chunwei Chen <david.chen@nutanix.com>
|
||||
Clemens Fruhwirth <clemens@endorphin.org>
|
||||
Coleman Kane <ckane@colemankane.org>
|
||||
Colin Ian King <colin.king@canonical.com>
|
||||
Craig Loomis <cloomis@astro.princeton.edu>
|
||||
Craig Sanders <github@taz.net.au>
|
||||
Cyril Plisko <cyril.plisko@infinidat.com>
|
||||
DHE <git@dehacked.net>
|
||||
Damian Wojsław <damian@wojslaw.pl>
|
||||
Dan Kimmel <dan.kimmel@delphix.com>
|
||||
Dan McDonald <danmcd@nexenta.com>
|
||||
Dan Swartzendruber <dswartz@druber.com>
|
||||
Dan Vatca <dan.vatca@gmail.com>
|
||||
Daniel Hoffman <dj.hoffman@delphix.com>
|
||||
Daniel Verite <daniel@verite.pro>
|
||||
Daniil Lunev <d.lunev.mail@gmail.com>
|
||||
Darik Horn <dajhorn@vanadac.com>
|
||||
Dave Eddy <dave@daveeddy.com>
|
||||
David Lamparter <equinox@diac24.net>
|
||||
David Qian <david.qian@intel.com>
|
||||
David Quigley <david.quigley@intel.com>
|
||||
Debabrata Banerjee <dbanerje@akamai.com>
|
||||
Denys Rtveliashvili <denys@rtveliashvili.name>
|
||||
Derek Dai <daiderek@gmail.com>
|
||||
Dimitri John Ledkov <xnox@ubuntu.com>
|
||||
Dmitry Khasanov <pik4ez@gmail.com>
|
||||
Dominik Hassler <hadfl@omniosce.org>
|
||||
Dominik Honnef <dominikh@fork-bomb.org>
|
||||
Don Brady <don.brady@delphix.com>
|
||||
Dr. András Korn <korn-github.com@elan.rulez.org>
|
||||
Eli Rosenthal <eli.rosenthal@delphix.com>
|
||||
Eric Desrochers <eric.desrochers@canonical.com>
|
||||
Eric Dillmann <eric@jave.fr>
|
||||
Eric Schrock <Eric.Schrock@delphix.com>
|
||||
Etienne Dechamps <etienne@edechamps.fr>
|
||||
Evan Susarret <evansus@gmail.com>
|
||||
Fabian Grünbichler <f.gruenbichler@proxmox.com>
|
||||
Fajar A. Nugraha <github@fajar.net>
|
||||
Fan Yong <fan.yong@intel.com>
|
||||
Feng Sun <loyou85@gmail.com>
|
||||
Frederik Wessels <wessels147@gmail.com>
|
||||
Frédéric Vanniere <f.vanniere@planet-work.com>
|
||||
Garrett D'Amore <garrett@nexenta.com>
|
||||
Garrison Jensen <garrison.jensen@gmail.com>
|
||||
Gary Mills <gary_mills@fastmail.fm>
|
||||
Gaurav Kumar <gauravk.18@gmail.com>
|
||||
GeLiXin <ge.lixin@zte.com.cn>
|
||||
George Amanakis <g_amanakis@yahoo.com>
|
||||
George Melikov <mail@gmelikov.ru>
|
||||
George Wilson <gwilson@delphix.com>
|
||||
Georgy Yakovlev <ya@sysdump.net>
|
||||
Giuseppe Di Natale <guss80@gmail.com>
|
||||
Gordan Bobic <gordan@redsleeve.org>
|
||||
Gordon Ross <gwr@nexenta.com>
|
||||
Gregor Kopka <gregor@kopka.net>
|
||||
Grischa Zengel <github.zfsonlinux@zengel.info>
|
||||
Gunnar Beutner <gunnar@beutner.name>
|
||||
Gvozden Neskovic <neskovic@gmail.com>
|
||||
Hajo Möller <dasjoe@gmail.com>
|
||||
Hans Rosenfeld <hans.rosenfeld@nexenta.com>
|
||||
Håkan Johansson <f96hajo@chalmers.se>
|
||||
Igor Kozhukhov <ikozhukhov@gmail.com>
|
||||
Igor Lvovsky <ilvovsky@gmail.com>
|
||||
Isaac Huang <he.huang@intel.com>
|
||||
JK Dingwall <james@dingwall.me.uk>
|
||||
Jacek Fefliński <feflik@gmail.com>
|
||||
James Cowgill <james.cowgill@mips.com>
|
||||
James Lee <jlee@thestaticvoid.com>
|
||||
James Pan <jiaming.pan@yahoo.com>
|
||||
Jan Engelhardt <jengelh@inai.de>
|
||||
Jan Kryl <jan.kryl@nexenta.com>
|
||||
Jan Sanislo <oystr@cs.washington.edu>
|
||||
Jason King <jason.brian.king@gmail.com>
|
||||
Jason Zaman <jasonzaman@gmail.com>
|
||||
Javen Wu <wu.javen@gmail.com>
|
||||
Jeremy Gill <jgill@parallax-innovations.com>
|
||||
Jeremy Jones <jeremy@delphix.com>
|
||||
Jerry Jelinek <jerry.jelinek@joyent.com>
|
||||
Jinshan Xiong <jinshan.xiong@intel.com>
|
||||
Joe Stein <joe.stein@delphix.com>
|
||||
John Albietz <inthecloud247@gmail.com>
|
||||
John Eismeier <john.eismeier@gmail.com>
|
||||
John L. Hammond <john.hammond@intel.com>
|
||||
John Layman <jlayman@sagecloud.com>
|
||||
John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>
|
||||
John Wren Kennedy <john.kennedy@delphix.com>
|
||||
Johnny Stenback <github@jstenback.com>
|
||||
Jorgen Lundman <lundman@lundman.net>
|
||||
Josef 'Jeff' Sipek <josef.sipek@nexenta.com>
|
||||
Joshua M. Clulow <josh@sysmgr.org>
|
||||
Justin Bedő <cu@cua0.org>
|
||||
Justin Lecher <jlec@gentoo.org>
|
||||
Justin T. Gibbs <gibbs@FreeBSD.org>
|
||||
Jörg Thalheim <joerg@higgsboson.tk>
|
||||
KORN Andras <korn@elan.rulez.org>
|
||||
Kamil Domański <kamil@domanski.co>
|
||||
Karsten Kretschmer <kkretschmer@gmail.com>
|
||||
Kash Pande <kash@tripleback.net>
|
||||
Keith M Wesolowski <wesolows@foobazco.org>
|
||||
Kevin Tanguy <kevin.tanguy@ovh.net>
|
||||
KireinaHoro <i@jsteward.moe>
|
||||
Kjeld Schouten-Lebbing <kjeld@schouten-lebbing.nl>
|
||||
Kohsuke Kawaguchi <kk@kohsuke.org>
|
||||
Kyle Blatter <kyleblatter@llnl.gov>
|
||||
Kyle Fuller <inbox@kylefuller.co.uk>
|
||||
Loli <ezomori.nozomu@gmail.com>
|
||||
Lars Johannsen <laj@it.dk>
|
||||
Li Dongyang <dongyang.li@anu.edu.au>
|
||||
Li Wei <W.Li@Sun.COM>
|
||||
Lukas Wunner <lukas@wunner.de>
|
||||
Madhav Suresh <madhav.suresh@delphix.com>
|
||||
Manoj Joseph <manoj.joseph@delphix.com>
|
||||
Manuel Amador (Rudd-O) <rudd-o@rudd-o.com>
|
||||
Marcel Huber <marcelhuberfoo@gmail.com>
|
||||
Marcel Telka <marcel.telka@nexenta.com>
|
||||
Marcel Wysocki <maci.stgn@gmail.com>
|
||||
Mark Shellenbaum <Mark.Shellenbaum@Oracle.COM>
|
||||
Mark Wright <markwright@internode.on.net>
|
||||
Martin Matuska <mm@FreeBSD.org>
|
||||
Massimo Maggi <me@massimo-maggi.eu>
|
||||
Matt Johnston <matt@fugro-fsi.com.au>
|
||||
Matt Kemp <matt@mattikus.com>
|
||||
Matthew Ahrens <matt@delphix.com>
|
||||
Matthew Thode <mthode@mthode.org>
|
||||
Matus Kral <matuskral@me.com>
|
||||
Max Grossman <max.grossman@delphix.com>
|
||||
Maximilian Mehnert <maximilian.mehnert@gmx.de>
|
||||
Michael Gebetsroither <michael@mgeb.org>
|
||||
Michael Kjorling <michael@kjorling.se>
|
||||
Michael Martin <mgmartin.mgm@gmail.com>
|
||||
Michael Niewöhner <foss@mniewoehner.de>
|
||||
Mike Gerdts <mike.gerdts@joyent.com>
|
||||
Mike Harsch <mike@harschsystems.com>
|
||||
Mike Leddy <mike.leddy@gmail.com>
|
||||
Mike Swanson <mikeonthecomputer@gmail.com>
|
||||
Milan Jurik <milan.jurik@xylab.cz>
|
||||
Morgan Jones <mjones@rice.edu>
|
||||
Moritz Maxeiner <moritz@ucworks.org>
|
||||
Nathaniel Clark <Nathaniel.Clark@misrule.us>
|
||||
Nathaniel Wesley Filardo <nwf@cs.jhu.edu>
|
||||
Nav Ravindranath <nav@delphix.com>
|
||||
Neal Gompa (ニール・ゴンパ) <ngompa13@gmail.com>
|
||||
Ned Bass <bass6@llnl.gov>
|
||||
Neependra Khare <neependra@kqinfotech.com>
|
||||
Neil Stockbridge <neil@dist.ro>
|
||||
Nick Garvey <garvey.nick@gmail.com>
|
||||
Nikolay Borisov <n.borisov.lkml@gmail.com>
|
||||
Olaf Faaland <faaland1@llnl.gov>
|
||||
Oleg Drokin <green@linuxhacker.ru>
|
||||
Oleg Stepura <oleg@stepura.com>
|
||||
Patrik Greco <sikevux@sikevux.se>
|
||||
Paul B. Henson <henson@acm.org>
|
||||
Paul Dagnelie <pcd@delphix.com>
|
||||
Paul Zuchowski <pzuchowski@datto.com>
|
||||
Pavel Boldin <boldin.pavel@gmail.com>
|
||||
Pavel Zakharov <pavel.zakharov@delphix.com>
|
||||
Pawel Jakub Dawidek <pjd@FreeBSD.org>
|
||||
Pedro Giffuni <pfg@freebsd.org>
|
||||
Peng <peng.hse@xtaotech.com>
|
||||
Peter Ashford <ashford@accs.com>
|
||||
Prakash Surya <prakash.surya@delphix.com>
|
||||
Prasad Joshi <prasadjoshi124@gmail.com>
|
||||
Ralf Ertzinger <ralf@skytale.net>
|
||||
Randall Mason <ClashTheBunny@gmail.com>
|
||||
Remy Blank <remy.blank@pobox.com>
|
||||
Ricardo M. Correia <ricardo.correia@oracle.com>
|
||||
Rich Ercolani <rincebrain@gmail.com>
|
||||
Richard Elling <Richard.Elling@RichardElling.com>
|
||||
Richard Laager <rlaager@wiktel.com>
|
||||
Richard Lowe <richlowe@richlowe.net>
|
||||
Richard Sharpe <rsharpe@samba.org>
|
||||
Richard Yao <ryao@gentoo.org>
|
||||
Rohan Puri <rohan.puri15@gmail.com>
|
||||
Romain Dolbeau <romain.dolbeau@atos.net>
|
||||
Roman Strashkin <roman.strashkin@nexenta.com>
|
||||
Ruben Kerkhof <ruben@rubenkerkhof.com>
|
||||
Saso Kiselkov <saso.kiselkov@nexenta.com>
|
||||
Scot W. Stevenson <scot.stevenson@gmail.com>
|
||||
Sean Eric Fagan <sef@ixsystems.com>
|
||||
Sebastian Gottschall <s.gottschall@dd-wrt.com>
|
||||
Sen Haerens <sen@senhaerens.be>
|
||||
Serapheim Dimitropoulos <serapheim@delphix.com>
|
||||
Seth Forshee <seth.forshee@canonical.com>
|
||||
Shampavman <sham.pavman@nexenta.com>
|
||||
Shen Yan <shenyanxxxy@qq.com>
|
||||
Simon Guest <simon.guest@tesujimath.org>
|
||||
Simon Klinkert <simon.klinkert@gmail.com>
|
||||
Sowrabha Gopal <sowrabha.gopal@delphix.com>
|
||||
Stanislav Seletskiy <s.seletskiy@gmail.com>
|
||||
Steffen Müthing <steffen.muething@iwr.uni-heidelberg.de>
|
||||
Stephen Blinick <stephen.blinick@delphix.com>
|
||||
Steve Dougherty <sdougherty@barracuda.com>
|
||||
Steven Burgess <sburgess@dattobackup.com>
|
||||
Steven Hartland <smh@freebsd.org>
|
||||
Steven Johnson <sjohnson@sakuraindustries.com>
|
||||
Stian Ellingsen <stian@plaimi.net>
|
||||
Suman Chakravartula <schakrava@gmail.com>
|
||||
Sydney Vanda <sydney.m.vanda@intel.com>
|
||||
Sören Tempel <soeren+git@soeren-tempel.net>
|
||||
Thijs Cramer <thijs.cramer@gmail.com>
|
||||
Tim Chase <tim@chase2k.com>
|
||||
Tim Connors <tconnors@rather.puzzling.org>
|
||||
Tim Crawford <tcrawford@datto.com>
|
||||
Tim Haley <Tim.Haley@Sun.COM>
|
||||
Tobin Harding <me@tobin.cc>
|
||||
Tom Caputi <tcaputi@datto.com>
|
||||
Tom Matthews <tom@axiom-partners.com>
|
||||
Tom Prince <tom.prince@ualberta.net>
|
||||
Tomohiro Kusumi <kusumi.tomohiro@gmail.com>
|
||||
Tony Hutter <hutter2@llnl.gov>
|
||||
Toomas Soome <tsoome@me.com>
|
||||
Trey Dockendorf <treydock@gmail.com>
|
||||
Turbo Fredriksson <turbo@bayour.com>
|
||||
Tyler J. Stachecki <stachecki.tyler@gmail.com>
|
||||
Vitaut Bajaryn <vitaut.bayaryn@gmail.com>
|
||||
Weigang Li <weigang.li@intel.com>
|
||||
Will Andrews <will@freebsd.org>
|
||||
Will Rouesnel <w.rouesnel@gmail.com>
|
||||
Wolfgang Bumiller <w.bumiller@proxmox.com>
|
||||
Xin Li <delphij@FreeBSD.org>
|
||||
Ying Zhu <casualfisher@gmail.com>
|
||||
YunQiang Su <syq@debian.org>
|
||||
Yuri Pankov <yuri.pankov@gmail.com>
|
||||
Yuxuan Shui <yshuiv7@gmail.com>
|
||||
Zachary Bedell <zac@thebedells.org>
|
||||
Anand Mitra <mitra@kqinfotech.com>
|
||||
Anurag Agarwal <anurag@kqinfotech.com>
|
||||
Neependra Khare <neependra@kqinfotech.com>
|
||||
Prasad Joshi <prasad@kqinfotech.com>
|
||||
Rohan Puri <rohan@kqinfotech.com>
|
||||
Sandip Divekar <sandipd@kqinfotech.com>
|
||||
Shoaib <shoaib@kqinfotech.com>
|
||||
Shrirang <shrirang@kqinfotech.com>
|
||||
|
||||
Additionally the following individuals have all made contributions
|
||||
to the project and deserve to be acknowledged.
|
||||
|
||||
Albert Lee <trisk@nexenta.com>
|
||||
Alejandro R. Sedeño <asedeno@mit.edu>
|
||||
Alex Zhuravlev <bzzz@whamcloud.com>
|
||||
Alexander Eremin <a.eremin@nexenta.com>
|
||||
Alexander Stetsenko <ams@nexenta.com>
|
||||
Alexey Shvetsov <alexxy@gentoo.org>
|
||||
Andreas Dilger <adilger@whamcloud.com>
|
||||
Andrew Reid <ColdCanuck@nailedtotheperch.com>
|
||||
Andrew Stormont <andrew.stormont@nexenta.com>
|
||||
Andrew Tselischev <andrewtselischev@gmail.com>
|
||||
Andriy Gapon <avg@FreeBSD.org>
|
||||
Aniruddha Shankar <k@191a.net>
|
||||
Bill Pijewski <wdp@joyent.com>
|
||||
Chris Dunlap <cdunlap@llnl.gov>
|
||||
Chris Dunlop <chris@onthe.net.au>
|
||||
Chris Siden <chris.siden@delphix.com>
|
||||
Chris Wedgwood <cw@f00f.org>
|
||||
Christian Kohlschütter <christian@kohlschutter.com>
|
||||
Christopher Siden <chris.siden@delphix.com>
|
||||
Craig Sanders <github@taz.net.au>
|
||||
Cyril Plisko <cyril.plisko@mountall.com>
|
||||
Dan McDonald <danmcd@nexenta.com>
|
||||
Daniel Verite <daniel@verite.pro>
|
||||
Darik Horn <dajhorn@vanadac.com>
|
||||
Eric Schrock <Eric.Schrock@delphix.com>
|
||||
Etienne Dechamps <etienne.dechamps@ovh.net>
|
||||
Fajar A. Nugraha <github@fajar.net>
|
||||
Frederik Wessels <wessels147@gmail.com>
|
||||
Garrett D'Amore <garrett@nexenta.com>
|
||||
George Wilson <george.wilson@delphix.com>
|
||||
Gordon Ross <gwr@nexenta.com>
|
||||
Gregor Kopka <mailfrom-github.com@kopka.net>
|
||||
Gunnar Beutner <gunnar@beutner.name>
|
||||
James H <james@kagisoft.co.uk>
|
||||
Javen Wu <wu.javen@gmail.com>
|
||||
Jeremy Gill <jgill@parallax-innovations.com>
|
||||
Jorgen Lundman <lundman@lundman.net>
|
||||
KORN Andras <korn@elan.rulez.org>
|
||||
Kyle Fuller <inbox@kylefuller.co.uk>
|
||||
Manuel Amador (Rudd-O) <rudd-o@rudd-o.com>
|
||||
Martin Matuska <mm@FreeBSD.org>
|
||||
Massimo Maggi <massimo@mmmm.it>
|
||||
Matthew Ahrens <mahrens@delphix.com>
|
||||
Michael Martin <mgmartin.mgm@gmail.com>
|
||||
Mike Harsch <mike@harschsystems.com>
|
||||
Ned Bass <bass6@llnl.gov>
|
||||
Oleg Stepura <oleg@stepura.com>
|
||||
P.SCH <p88@yahoo.com>
|
||||
Pawel Jakub Dawidek <pawel@dawidek.net>
|
||||
Prakash Surya <surya1@llnl.gov>
|
||||
Prasad Joshi <pjoshi@stec-inc.com>
|
||||
Ricardo M. Correia <Ricardo.M.Correia@Sun.COM>
|
||||
Richard Laager <rlaager@wiktel.com>
|
||||
Richard Lowe <richlowe@richlowe.net>
|
||||
Richard Yao <ryao@cs.stonybrook.edu>
|
||||
Rohan Puri <rohan.puri15@gmail.com>
|
||||
Shampavman <sham.pavman@nexenta.com>
|
||||
Simon Klinkert <klinkert@webgods.de>
|
||||
Suman Chakravartula <suman@gogrid.com>
|
||||
Tim Haley <Tim.Haley@Sun.COM>
|
||||
Turbo Fredriksson <turbo@bayour.com>
|
||||
Xin Li <delphij@FreeBSD.org>
|
||||
Yuxuan Shui <yshuiv7@gmail.com>
|
||||
Zachary Bedell <zac@thebedells.org>
|
||||
nordaux <nordaux@gmail.com>
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
The [OpenZFS Code of Conduct](https://openzfs.org/wiki/Code_of_Conduct)
|
||||
applies to spaces associated with the OpenZFS project, including GitHub.
|
||||
@@ -1,31 +1,33 @@
|
||||
Refer to the git commit log for authoritative copyright attribution.
|
||||
The majority of the code in the ZFS on Linux port comes from OpenSolaris
|
||||
which has been released under the terms of the CDDL open source license.
|
||||
This includes the core ZFS code, libavl, libnvpair, libefi, libunicode,
|
||||
and libutil. The original OpenSolaris source can be downloaded from:
|
||||
|
||||
The original ZFS source code was obtained from Open Solaris which was
|
||||
released under the terms of the CDDL open source license. Additional
|
||||
changes have been included from OpenZFS and the Illumos project which
|
||||
are similarly licensed. These projects can be found on Github at:
|
||||
http://dlc.sun.com/osol/on/downloads/b121/on-src.tar.bz2
|
||||
|
||||
* https://github.com/illumos/illumos-gate
|
||||
* https://github.com/openzfs/openzfs
|
||||
Files which do not originate from OpenSolaris are noted in the file header
|
||||
and attributed properly. These exceptions include, but are not limited
|
||||
to, the vdev_disk.c and zvol.c implementation which are licensed under
|
||||
the CDDL.
|
||||
|
||||
The zpios test code is originally derived from the Lustre pios test code
|
||||
which is licensed under the GPLv2. As such the heavily modified zpios
|
||||
kernel test code also remains licensed under the GPLv2.
|
||||
|
||||
The latest stable and development versions of this port can be downloaded
|
||||
from the official ZFS on Linux site located at:
|
||||
|
||||
http://zfsonlinux.org/
|
||||
|
||||
This ZFS on Linux port was produced at the Lawrence Livermore National
|
||||
Laboratory (LLNL) under Contract No. DE-AC52-07NA27344 (Contract 44)
|
||||
between the U.S. Department of Energy (DOE) and Lawrence Livermore
|
||||
National Security, LLC (LLNS) for the operation of LLNL. It has been
|
||||
approved for release under LLNL-CODE-403049.
|
||||
|
||||
Unless otherwise noted, all files in this distribution are released
|
||||
under the Common Development and Distribution License (CDDL).
|
||||
Exceptions are noted within the associated source files. See the file
|
||||
OPENSOLARIS.LICENSE for more information.
|
||||
|
||||
Exceptions are noted within the associated source files headers and
|
||||
by including a THIRDPARTYLICENSE file with the license terms. A few
|
||||
notable exceptions and their respective licenses include:
|
||||
|
||||
* Skein Checksum Implementation: module/icp/algs/skein/THIRDPARTYLICENSE
|
||||
* AES Implementation: module/icp/asm-x86_64/aes/THIRDPARTYLICENSE.gladman
|
||||
* AES Implementation: module/icp/asm-x86_64/aes/THIRDPARTYLICENSE.openssl
|
||||
* PBKDF2 Implementation: lib/libzfs/THIRDPARTYLICENSE.openssl
|
||||
* SPL Implementation: module/os/linux/spl/THIRDPARTYLICENSE.gplv2
|
||||
* GCM Implementation: module/icp/asm-x86_64/modes/THIRDPARTYLICENSE.cryptogams
|
||||
* GCM Implementation: module/icp/asm-x86_64/modes/THIRDPARTYLICENSE.openssl
|
||||
* GHASH Implementation: module/icp/asm-x86_64/modes/THIRDPARTYLICENSE.cryptogams
|
||||
* GHASH Implementation: module/icp/asm-x86_64/modes/THIRDPARTYLICENSE.openssl
|
||||
|
||||
This product includes software developed by the OpenSSL Project for use
|
||||
in the OpenSSL Toolkit (http://www.openssl.org/)
|
||||
|
||||
See the LICENSE and NOTICE for more information.
|
||||
Refer to the git commit log for authoritative copyright attribution.
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
This work was produced at the Lawrence Livermore National Laboratory
|
||||
(LLNL) under Contract No. DE-AC52-07NA27344 (Contract 44) between
|
||||
the U.S. Department of Energy (DOE) and Lawrence Livermore National
|
||||
Security, LLC (LLNS) for the operation of LLNL.
|
||||
|
||||
This work was prepared as an account of work sponsored by an agency of
|
||||
the United States Government. Neither the United States Government nor
|
||||
Lawrence Livermore National Security, LLC nor any of their employees,
|
||||
makes any warranty, express or implied, or assumes any liability or
|
||||
responsibility for the accuracy, completeness, or usefulness of any
|
||||
information, apparatus, product, or process disclosed, or represents
|
||||
that its use would not infringe privately-owned rights.
|
||||
|
||||
Reference herein to any specific commercial products, process, or
|
||||
services by trade name, trademark, manufacturer or otherwise does
|
||||
not necessarily constitute or imply its endorsement, recommendation,
|
||||
or favoring by the United States Government or Lawrence Livermore
|
||||
National Security, LLC. The views and opinions of authors expressed
|
||||
herein do not necessarily state or reflect those of the United States
|
||||
Government or Lawrence Livermore National Security, LLC, and shall
|
||||
not be used for advertising or product endorsement purposes.
|
||||
|
||||
The precise terms and conditions for copying, distribution, and
|
||||
modification are specified in the file OPENSOLARIS.LICENSE.
|
||||
@@ -1,10 +1,8 @@
|
||||
Meta: 1
|
||||
Name: zfs
|
||||
Branch: 1.0
|
||||
Version: 2.1.11
|
||||
Release: 1
|
||||
Release-Tags: relext
|
||||
License: CDDL
|
||||
Author: OpenZFS
|
||||
Linux-Maximum: 6.2
|
||||
Linux-Minimum: 3.10
|
||||
Meta: 1
|
||||
Name: zfs
|
||||
Branch: 1.0
|
||||
Version: 0.7.13
|
||||
Release: 1
|
||||
Release-Tags: relext
|
||||
License: CDDL
|
||||
Author: OpenZFS on Linux
|
||||
|
||||
+32
-179
@@ -1,69 +1,32 @@
|
||||
include $(top_srcdir)/config/Shellcheck.am
|
||||
|
||||
ACLOCAL_AMFLAGS = -I config
|
||||
|
||||
SUBDIRS = include
|
||||
if BUILD_LINUX
|
||||
SUBDIRS += rpm
|
||||
endif
|
||||
include config/rpm.am
|
||||
include config/deb.am
|
||||
include config/tgz.am
|
||||
|
||||
SUBDIRS = include rpm
|
||||
if CONFIG_USER
|
||||
SUBDIRS += man scripts lib tests cmd etc contrib
|
||||
if BUILD_LINUX
|
||||
SUBDIRS += udev
|
||||
endif
|
||||
SUBDIRS += udev etc man scripts lib tests cmd contrib
|
||||
endif
|
||||
if CONFIG_KERNEL
|
||||
SUBDIRS += module
|
||||
|
||||
extradir = $(prefix)/src/zfs-$(VERSION)
|
||||
extradir = @prefix@/src/zfs-$(VERSION)
|
||||
extra_HEADERS = zfs.release.in zfs_config.h.in
|
||||
|
||||
if BUILD_LINUX
|
||||
kerneldir = $(prefix)/src/zfs-$(VERSION)/$(LINUX_VERSION)
|
||||
kerneldir = @prefix@/src/zfs-$(VERSION)/$(LINUX_VERSION)
|
||||
nodist_kernel_HEADERS = zfs.release zfs_config.h module/$(LINUX_SYMBOLS)
|
||||
endif
|
||||
endif
|
||||
|
||||
AUTOMAKE_OPTIONS = foreign
|
||||
EXTRA_DIST = autogen.sh copy-builtin
|
||||
EXTRA_DIST += config/config.awk config/rpm.am config/deb.am config/tgz.am
|
||||
EXTRA_DIST += AUTHORS CODE_OF_CONDUCT.md COPYRIGHT LICENSE META NEWS NOTICE
|
||||
EXTRA_DIST += README.md RELEASES.md
|
||||
EXTRA_DIST += module/lua/README.zfs module/os/linux/spl/README.md
|
||||
|
||||
# Include all the extra licensing information for modules
|
||||
EXTRA_DIST += module/icp/algs/skein/THIRDPARTYLICENSE
|
||||
EXTRA_DIST += module/icp/algs/skein/THIRDPARTYLICENSE.descrip
|
||||
EXTRA_DIST += module/icp/asm-x86_64/aes/THIRDPARTYLICENSE.gladman
|
||||
EXTRA_DIST += module/icp/asm-x86_64/aes/THIRDPARTYLICENSE.gladman.descrip
|
||||
EXTRA_DIST += module/icp/asm-x86_64/aes/THIRDPARTYLICENSE.openssl
|
||||
EXTRA_DIST += module/icp/asm-x86_64/aes/THIRDPARTYLICENSE.openssl.descrip
|
||||
EXTRA_DIST += module/icp/asm-x86_64/modes/THIRDPARTYLICENSE.cryptogams
|
||||
EXTRA_DIST += module/icp/asm-x86_64/modes/THIRDPARTYLICENSE.cryptogams.descrip
|
||||
EXTRA_DIST += module/icp/asm-x86_64/modes/THIRDPARTYLICENSE.openssl
|
||||
EXTRA_DIST += module/icp/asm-x86_64/modes/THIRDPARTYLICENSE.openssl.descrip
|
||||
EXTRA_DIST += module/os/linux/spl/THIRDPARTYLICENSE.gplv2
|
||||
EXTRA_DIST += module/os/linux/spl/THIRDPARTYLICENSE.gplv2.descrip
|
||||
EXTRA_DIST += module/zfs/THIRDPARTYLICENSE.cityhash
|
||||
EXTRA_DIST += module/zfs/THIRDPARTYLICENSE.cityhash.descrip
|
||||
EXTRA_DIST += META DISCLAIMER COPYRIGHT README.markdown OPENSOLARIS.LICENSE
|
||||
|
||||
@CODE_COVERAGE_RULES@
|
||||
|
||||
GITREV = include/zfs_gitrev.h
|
||||
|
||||
PHONY = gitrev
|
||||
gitrev:
|
||||
$(AM_V_GEN)$(top_srcdir)/scripts/make_gitrev.sh $(GITREV)
|
||||
|
||||
all: gitrev
|
||||
|
||||
# Double-colon rules are allowed; there are multiple independent definitions.
|
||||
maintainer-clean-local::
|
||||
-$(RM) $(GITREV)
|
||||
|
||||
distclean-local::
|
||||
-$(RM) -R autom4te*.cache build
|
||||
-$(RM) -R autom4te*.cache
|
||||
-find . \( -name SCCS -o -name BitKeeper -o -name .svn -o -name CVS \
|
||||
-o -name .pc -o -name .hg -o -name .git \) -prune -o \
|
||||
\( -name '*.orig' -o -name '*.rej' -o -name '*~' \
|
||||
@@ -74,173 +37,63 @@ distclean-local::
|
||||
-o -name '*.gcno' \) \
|
||||
-type f -print | xargs $(RM)
|
||||
|
||||
all-local:
|
||||
-[ -x ${top_builddir}/scripts/zfs-tests.sh ] && \
|
||||
${top_builddir}/scripts/zfs-tests.sh -c
|
||||
|
||||
dist-hook:
|
||||
$(AM_V_GEN)$(top_srcdir)/scripts/make_gitrev.sh -D $(distdir) $(GITREV)
|
||||
$(SED) ${ac_inplace} -e 's/Release:[[:print:]]*/Release: $(RELEASE)/' \
|
||||
sed -i 's/Release:[[:print:]]*/Release: $(RELEASE)/' \
|
||||
$(distdir)/META
|
||||
|
||||
if BUILD_LINUX
|
||||
# For compatibility, create a matching spl-x.y.z directly which contains
|
||||
# symlinks to the updated header and object file locations. These
|
||||
# compatibility links will be removed in the next major release.
|
||||
if CONFIG_KERNEL
|
||||
install-data-hook:
|
||||
rm -rf $(DESTDIR)$(prefix)/src/spl-$(VERSION) && \
|
||||
mkdir $(DESTDIR)$(prefix)/src/spl-$(VERSION) && \
|
||||
cd $(DESTDIR)$(prefix)/src/spl-$(VERSION) && \
|
||||
ln -s ../zfs-$(VERSION)/include/spl include && \
|
||||
ln -s ../zfs-$(VERSION)/$(LINUX_VERSION) $(LINUX_VERSION) && \
|
||||
ln -s ../zfs-$(VERSION)/zfs_config.h.in spl_config.h.in && \
|
||||
ln -s ../zfs-$(VERSION)/zfs.release.in spl.release.in && \
|
||||
cd $(DESTDIR)$(prefix)/src/zfs-$(VERSION)/$(LINUX_VERSION) && \
|
||||
ln -fs zfs_config.h spl_config.h && \
|
||||
ln -fs zfs.release spl.release
|
||||
endif
|
||||
endif
|
||||
checkstyle: cstyle shellcheck flake8 commitcheck
|
||||
|
||||
PHONY += codecheck
|
||||
codecheck: cstyle shellcheck checkbashisms flake8 mancheck testscheck vcscheck zstdcheck
|
||||
|
||||
PHONY += checkstyle
|
||||
checkstyle: codecheck commitcheck
|
||||
|
||||
PHONY += commitcheck
|
||||
commitcheck:
|
||||
@if git rev-parse --git-dir > /dev/null 2>&1; then \
|
||||
${top_srcdir}/scripts/commitcheck.sh; \
|
||||
scripts/commitcheck.sh; \
|
||||
fi
|
||||
|
||||
if HAVE_PARALLEL
|
||||
cstyle_line = -print0 | parallel -X0 ${top_srcdir}/scripts/cstyle.pl -cpP {}
|
||||
else
|
||||
cstyle_line = -exec ${top_srcdir}/scripts/cstyle.pl -cpP {} +
|
||||
endif
|
||||
PHONY += cstyle
|
||||
cstyle:
|
||||
@find ${top_srcdir} -name build -prune \
|
||||
-o -type f -name '*.[hc]' \
|
||||
! -name 'zfs_config.*' ! -name '*.mod.c' \
|
||||
! -name 'opt_global.h' ! -name '*_if*.h' \
|
||||
! -name 'zstd_compat_wrapper.h' \
|
||||
! -path './module/zstd/lib/*' \
|
||||
$(cstyle_line)
|
||||
@find ${top_srcdir} -name '*.[hc]' ! -name 'zfs_config.*' \
|
||||
! -name '*.mod.c' -type f -exec scripts/cstyle.pl -cpP {} \+
|
||||
|
||||
filter_executable = -exec test -x '{}' \; -print
|
||||
|
||||
SHELLCHECKDIRS = cmd contrib etc scripts tests
|
||||
SHELLCHECKSCRIPTS = autogen.sh
|
||||
|
||||
PHONY += checkabi storeabi
|
||||
|
||||
checklibabiversion:
|
||||
libabiversion=`abidw -v | $(SED) 's/[^0-9]//g'`; \
|
||||
if test $$libabiversion -lt "200"; then \
|
||||
/bin/echo -e "\n" \
|
||||
"*** Please use libabigail 2.0.0 version or newer;\n" \
|
||||
"*** otherwise results are not consistent!\n" \
|
||||
"(or see https://github.com/openzfs/libabigail-docker )\n"; \
|
||||
exit 1; \
|
||||
fi;
|
||||
|
||||
checkabi: checklibabiversion lib
|
||||
$(MAKE) -C lib checkabi
|
||||
|
||||
storeabi: checklibabiversion lib
|
||||
$(MAKE) -C lib storeabi
|
||||
|
||||
PHONY += mancheck
|
||||
mancheck:
|
||||
${top_srcdir}/scripts/mancheck.sh ${top_srcdir}/man ${top_srcdir}/tests/test-runner/man
|
||||
|
||||
if BUILD_LINUX
|
||||
stat_fmt = -c '%A %n'
|
||||
else
|
||||
stat_fmt = -f '%Sp %N'
|
||||
endif
|
||||
|
||||
PHONY += testscheck
|
||||
testscheck:
|
||||
@find ${top_srcdir}/tests/zfs-tests -type f \
|
||||
\( -name '*.ksh' -not ${filter_executable} \) -o \
|
||||
\( -name '*.kshlib' ${filter_executable} \) -o \
|
||||
\( -name '*.shlib' ${filter_executable} \) -o \
|
||||
\( -name '*.cfg' ${filter_executable} \) | \
|
||||
xargs -r stat ${stat_fmt} | \
|
||||
awk '{c++; print} END {if(c>0) exit 1}'
|
||||
|
||||
PHONY += vcscheck
|
||||
vcscheck:
|
||||
@if git rev-parse --git-dir > /dev/null 2>&1; then \
|
||||
git ls-files . --exclude-standard --others | \
|
||||
awk '{c++; print} END {if(c>0) exit 1}' ; \
|
||||
shellcheck:
|
||||
@if type shellcheck > /dev/null 2>&1; then \
|
||||
shellcheck --exclude=SC1090 --format=gcc scripts/paxcheck.sh \
|
||||
scripts/zloop.sh \
|
||||
scripts/zfs-tests.sh \
|
||||
scripts/zfs.sh \
|
||||
scripts/commitcheck.sh \
|
||||
$$(find cmd/zed/zed.d/*.sh -type f) \
|
||||
$$(find cmd/zpool/zpool.d/* -executable); \
|
||||
fi
|
||||
|
||||
PHONY += zstdcheck
|
||||
zstdcheck:
|
||||
@$(MAKE) -C module/zstd checksymbols
|
||||
|
||||
PHONY += lint
|
||||
lint: cppcheck paxcheck
|
||||
|
||||
CPPCHECKDIRS = cmd lib module
|
||||
PHONY += cppcheck
|
||||
cppcheck: $(CPPCHECKDIRS)
|
||||
@if test -n "$(CPPCHECK)"; then \
|
||||
set -e ; for dir in $(CPPCHECKDIRS) ; do \
|
||||
$(MAKE) -C $$dir cppcheck ; \
|
||||
done \
|
||||
else \
|
||||
echo "skipping cppcheck because cppcheck is not installed"; \
|
||||
cppcheck:
|
||||
@if type cppcheck > /dev/null 2>&1; then \
|
||||
cppcheck --quiet --force --error-exitcode=2 --inline-suppr \
|
||||
--suppressions-list=.github/suppressions.txt \
|
||||
-UHAVE_SSE2 -UHAVE_AVX512F -UHAVE_UIO_ZEROCOPY \
|
||||
-UHAVE_DNLC ${top_srcdir}; \
|
||||
fi
|
||||
|
||||
PHONY += paxcheck
|
||||
paxcheck:
|
||||
@if type scanelf > /dev/null 2>&1; then \
|
||||
${top_srcdir}/scripts/paxcheck.sh ${top_builddir}; \
|
||||
else \
|
||||
echo "skipping paxcheck because scanelf is not installed"; \
|
||||
scripts/paxcheck.sh ${top_srcdir}; \
|
||||
fi
|
||||
|
||||
PHONY += flake8
|
||||
flake8:
|
||||
@if type flake8 > /dev/null 2>&1; then \
|
||||
flake8 ${top_srcdir}; \
|
||||
else \
|
||||
echo "skipping flake8 because flake8 is not installed"; \
|
||||
fi
|
||||
|
||||
PHONY += ctags
|
||||
ctags:
|
||||
$(RM) tags
|
||||
find $(top_srcdir) -name '.?*' -prune \
|
||||
-o -type f -name '*.[hcS]' -print | xargs ctags -a
|
||||
find $(top_srcdir) -name .git -prune -o -name '*.[hc]' | xargs ctags
|
||||
|
||||
PHONY += etags
|
||||
etags:
|
||||
$(RM) TAGS
|
||||
find $(top_srcdir) -name '.?*' -prune \
|
||||
-o -type f -name '*.[hcS]' -print | xargs etags -a
|
||||
find $(top_srcdir) -name .pc -prune -o -name '*.[hc]' | xargs etags -a
|
||||
|
||||
PHONY += cscopelist
|
||||
cscopelist:
|
||||
find $(top_srcdir) -name '.?*' -prune \
|
||||
-o -type f -name '*.[hc]' -print >cscope.files
|
||||
|
||||
PHONY += tags
|
||||
tags: ctags etags
|
||||
|
||||
PHONY += pkg pkg-dkms pkg-kmod pkg-utils
|
||||
pkg: @DEFAULT_PACKAGE@
|
||||
pkg-dkms: @DEFAULT_PACKAGE@-dkms
|
||||
pkg-kmod: @DEFAULT_PACKAGE@-kmod
|
||||
pkg-utils: @DEFAULT_PACKAGE@-utils
|
||||
|
||||
include config/rpm.am
|
||||
include config/deb.am
|
||||
include config/tgz.am
|
||||
|
||||
.PHONY: $(PHONY)
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
Descriptions of all releases can be found on github:
|
||||
|
||||
https://github.com/openzfs/zfs/releases
|
||||
@@ -1,16 +0,0 @@
|
||||
This work was produced under the auspices of the U.S. Department of Energy by
|
||||
Lawrence Livermore National Laboratory under Contract DE-AC52-07NA27344.
|
||||
|
||||
This work was prepared as an account of work sponsored by an agency of the
|
||||
United States Government. Neither the United States Government nor Lawrence
|
||||
Livermore National Security, LLC, nor any of their employees makes any warranty,
|
||||
expressed or implied, or assumes any legal liability or responsibility for the
|
||||
accuracy, completeness, or usefulness of any information, apparatus, product, or
|
||||
process disclosed, or represents that its use would not infringe privately owned
|
||||
rights. Reference herein to any specific commercial product, process, or service
|
||||
by trade name, trademark, manufacturer, or otherwise does not necessarily
|
||||
constitute or imply its endorsement, recommendation, or favoring by the United
|
||||
States Government or Lawrence Livermore National Security, LLC. The views and
|
||||
opinions of authors expressed herein do not necessarily state or reflect those
|
||||
of the United States Government or Lawrence Livermore National Security, LLC,
|
||||
and shall not be used for advertising or product endorsement purposes.
|
||||
@@ -0,0 +1,19 @@
|
||||

|
||||
|
||||
ZFS on Linux is an advanced file system and volume manager which was originally
|
||||
developed for Solaris and is now maintained by the OpenZFS community.
|
||||
|
||||
[](https://codecov.io/gh/zfsonlinux/zfs)
|
||||
|
||||
# Official Resources
|
||||
* [Site](http://zfsonlinux.org)
|
||||
* [Wiki](https://github.com/zfsonlinux/zfs/wiki)
|
||||
* [Mailing lists](https://github.com/zfsonlinux/zfs/wiki/Mailing-Lists)
|
||||
* [OpenZFS site](http://open-zfs.org/)
|
||||
|
||||
# Installation
|
||||
Full documentation for installing ZoL on your favorite Linux distribution can
|
||||
be found at [our site](http://zfsonlinux.org/).
|
||||
|
||||
# Contribute & Develop
|
||||
We have a separate document with [contribution guidelines](./.github/CONTRIBUTING.md).
|
||||
@@ -1,35 +0,0 @@
|
||||

|
||||
|
||||
OpenZFS is an advanced file system and volume manager which was originally
|
||||
developed for Solaris and is now maintained by the OpenZFS community.
|
||||
This repository contains the code for running OpenZFS on Linux and FreeBSD.
|
||||
|
||||
[](https://codecov.io/gh/openzfs/zfs)
|
||||
[](https://scan.coverity.com/projects/openzfs-zfs)
|
||||
|
||||
# Official Resources
|
||||
|
||||
* [Documentation](https://openzfs.github.io/openzfs-docs/) - for using and developing this repo
|
||||
* [ZoL Site](https://zfsonlinux.org) - Linux release info & links
|
||||
* [Mailing lists](https://openzfs.github.io/openzfs-docs/Project%20and%20Community/Mailing%20Lists.html)
|
||||
* [OpenZFS site](https://openzfs.org/) - for conference videos and info on other platforms (illumos, OSX, Windows, etc)
|
||||
|
||||
# Installation
|
||||
|
||||
Full documentation for installing OpenZFS on your favorite operating system can
|
||||
be found at the [Getting Started Page](https://openzfs.github.io/openzfs-docs/Getting%20Started/index.html).
|
||||
|
||||
# Contribute & Develop
|
||||
|
||||
We have a separate document with [contribution guidelines](./.github/CONTRIBUTING.md).
|
||||
|
||||
We have a [Code of Conduct](./CODE_OF_CONDUCT.md).
|
||||
|
||||
# Release
|
||||
|
||||
OpenZFS is released under a CDDL license.
|
||||
For more details see the NOTICE, LICENSE and COPYRIGHT files; `UCRL-CODE-235197`
|
||||
|
||||
# Supported Kernels
|
||||
* The `META` file contains the officially recognized supported Linux kernel versions.
|
||||
* Supported FreeBSD versions are any supported branches and releases starting from 12.2-RELEASE.
|
||||
-37
@@ -1,37 +0,0 @@
|
||||
OpenZFS uses the MAJOR.MINOR.PATCH versioning scheme described here:
|
||||
|
||||
* MAJOR - Incremented at the discretion of the OpenZFS developers to indicate
|
||||
a particularly noteworthy feature or change. An increase in MAJOR number
|
||||
does not indicate any incompatible on-disk format change. The ability
|
||||
to import a ZFS pool is controlled by the feature flags enabled on the
|
||||
pool and the feature flags supported by the installed OpenZFS version.
|
||||
Increasing the MAJOR version is expected to be an infrequent occurrence.
|
||||
|
||||
* MINOR - Incremented to indicate new functionality such as a new feature
|
||||
flag, pool/dataset property, zfs/zpool sub-command, new user/kernel
|
||||
interface, etc. MINOR releases may introduce incompatible changes to the
|
||||
user space library APIs (libzfs.so). Existing user/kernel interfaces are
|
||||
considered to be stable to maximize compatibility between OpenZFS releases.
|
||||
Additions to the user/kernel interface are backwards compatible.
|
||||
|
||||
* PATCH - Incremented when applying documentation updates, important bug
|
||||
fixes, minor performance improvements, and kernel compatibility patches.
|
||||
The user space library APIs and user/kernel interface are considered to
|
||||
be stable. PATCH releases for a MAJOR.MINOR are published as needed.
|
||||
|
||||
Two release branches are maintained for OpenZFS, they are:
|
||||
|
||||
* OpenZFS LTS - A designated MAJOR.MINOR release with periodic PATCH
|
||||
releases that incorporate important changes backported from newer OpenZFS
|
||||
releases. This branch is intended for use in environments using an
|
||||
LTS, enterprise, or similarly managed kernel (RHEL, Ubuntu LTS, Debian).
|
||||
Minor changes to support these distribution kernels will be applied as
|
||||
needed. New kernel versions released after the OpenZFS LTS release are
|
||||
not supported. LTS releases will receive patches for at least 2 years.
|
||||
The current LTS release is OpenZFS 2.1.
|
||||
|
||||
* OpenZFS current - Tracks the newest MAJOR.MINOR release. This branch
|
||||
includes support for the latest OpenZFS features and recently releases
|
||||
kernels. When a new MINOR release is tagged the previous MINOR release
|
||||
will no longer be maintained (unless it is an LTS release). New MINOR
|
||||
releases are planned to occur roughly annually.
|
||||
@@ -1,15 +1,17 @@
|
||||
#!/bin/sh
|
||||
|
||||
### prepare
|
||||
#TEST_PREPARE_WATCHDOG="yes"
|
||||
#TEST_PREPARE_SHARES="yes"
|
||||
#TEST_PREPARE_WATCHDOG="no"
|
||||
|
||||
### SPLAT
|
||||
#TEST_SPLAT_SKIP="yes"
|
||||
#TEST_SPLAT_OPTIONS="-acvx"
|
||||
|
||||
### ztest
|
||||
#TEST_ZTEST_SKIP="yes"
|
||||
#TEST_ZTEST_TIMEOUT=1800
|
||||
#TEST_ZTEST_DIR="/var/tmp/"
|
||||
#TEST_ZTEST_OPTIONS="-V"
|
||||
#TEST_ZTEST_CORE_DIR="/mnt/zloop"
|
||||
|
||||
### zimport
|
||||
#TEST_ZIMPORT_SKIP="yes"
|
||||
@@ -29,13 +31,9 @@
|
||||
|
||||
### zfs-tests.sh
|
||||
#TEST_ZFSTESTS_SKIP="yes"
|
||||
#TEST_ZFSTESTS_DIR="/mnt/"
|
||||
#TEST_ZFSTESTS_DISKS="vdb vdc vdd"
|
||||
#TEST_ZFSTESTS_DISKSIZE="8G"
|
||||
#TEST_ZFSTESTS_ITERS="1"
|
||||
#TEST_ZFSTESTS_OPTIONS="-vx"
|
||||
#TEST_ZFSTESTS_RUNFILE="linux.run"
|
||||
#TEST_ZFSTESTS_TAGS="functional"
|
||||
|
||||
### zfsstress
|
||||
#TEST_ZFSSTRESS_SKIP="yes"
|
||||
@@ -44,7 +42,53 @@
|
||||
#TEST_ZFSSTRESS_RUNTIME=300
|
||||
#TEST_ZFSSTRESS_POOL="tank"
|
||||
#TEST_ZFSSTRESS_FS="fish"
|
||||
#TEST_ZFSSTRESS_FSOPT="-o overlay=on"
|
||||
#TEST_ZFSSTRESS_VDEV="/var/tmp/vdev"
|
||||
#TEST_ZFSSTRESS_DIR="/$TEST_ZFSSTRESS_POOL/$TEST_ZFSSTRESS_FS"
|
||||
#TEST_ZFSSTRESS_OPTIONS=""
|
||||
|
||||
### per-builder customization
|
||||
#
|
||||
# BB_NAME=builder-name <distribution-version-architecture-type>
|
||||
# - distribution=Amazon,Debian,Fedora,RHEL,SUSE,Ubuntu
|
||||
# - version=x.y
|
||||
# - architecture=x86_64,i686,arm,aarch64
|
||||
# - type=build,test
|
||||
#
|
||||
case "$BB_NAME" in
|
||||
Amazon*)
|
||||
# ZFS enabled xfstests fails to build
|
||||
TEST_XFSTESTS_SKIP="yes"
|
||||
;;
|
||||
CentOS-7*)
|
||||
# ZFS enabled xfstests fails to build
|
||||
TEST_XFSTESTS_SKIP="yes"
|
||||
;;
|
||||
CentOS-6*)
|
||||
;;
|
||||
Debian*)
|
||||
;;
|
||||
Fedora*)
|
||||
;;
|
||||
RHEL*)
|
||||
;;
|
||||
SUSE*)
|
||||
;;
|
||||
Ubuntu-16.04*)
|
||||
# ZFS enabled xfstests fails to build
|
||||
TEST_XFSTESTS_SKIP="yes"
|
||||
;;
|
||||
Ubuntu*)
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
|
||||
###
|
||||
#
|
||||
# Disable the following test suites on 32-bit systems.
|
||||
#
|
||||
if [ $(getconf LONG_BIT) = "32" ]; then
|
||||
TEST_ZTEST_SKIP="yes"
|
||||
TEST_XFSTESTS_SKIP="yes"
|
||||
TEST_ZFSSTRESS_SKIP="yes"
|
||||
fi
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#!/bin/sh
|
||||
|
||||
autoreconf -fiv || exit 1
|
||||
autoreconf -fiv
|
||||
rm -Rf autom4te.cache
|
||||
|
||||
+3
-27
@@ -1,27 +1,3 @@
|
||||
include $(top_srcdir)/config/Shellcheck.am
|
||||
|
||||
SUBDIRS = zfs zpool zdb zhack zinject zstream ztest
|
||||
SUBDIRS += fsck_zfs vdev_id raidz_test zfs_ids_to_path
|
||||
SUBDIRS += zpool_influxdb
|
||||
|
||||
CPPCHECKDIRS = zfs zpool zdb zhack zinject zstream ztest
|
||||
CPPCHECKDIRS += raidz_test zfs_ids_to_path zpool_influxdb
|
||||
|
||||
# TODO: #12084: SHELLCHECKDIRS = fsck_zfs vdev_id zpool
|
||||
SHELLCHECKDIRS = fsck_zfs zpool
|
||||
|
||||
if USING_PYTHON
|
||||
SUBDIRS += arcstat arc_summary dbufstat
|
||||
endif
|
||||
|
||||
if BUILD_LINUX
|
||||
SUBDIRS += mount_zfs zed zgenhostid zvol_id zvol_wait
|
||||
CPPCHECKDIRS += mount_zfs zed zgenhostid zvol_id
|
||||
SHELLCHECKDIRS += zed
|
||||
endif
|
||||
|
||||
PHONY = cppcheck
|
||||
cppcheck: $(CPPCHECKDIRS)
|
||||
set -e ; for dir in $(CPPCHECKDIRS) ; do \
|
||||
$(MAKE) -C $$dir cppcheck ; \
|
||||
done
|
||||
SUBDIRS = zfs zpool zdb zhack zinject zstreamdump ztest zpios
|
||||
SUBDIRS += mount_zfs fsck_zfs zvol_id vdev_id arcstat dbufstat zed
|
||||
SUBDIRS += arc_summary raidz_test zgenhostid
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
arc_summary
|
||||
@@ -1,8 +1 @@
|
||||
bin_SCRIPTS = arc_summary
|
||||
|
||||
CLEANFILES = arc_summary
|
||||
EXTRA_DIST = arc_summary3
|
||||
SCRIPT = arc_summary3
|
||||
|
||||
arc_summary: $(SCRIPT)
|
||||
cp $< $@
|
||||
dist_bin_SCRIPTS = arc_summary.py
|
||||
|
||||
Executable
+1039
File diff suppressed because it is too large
Load Diff
@@ -1,978 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
#
|
||||
# Copyright (c) 2008 Ben Rockwood <benr@cuddletech.com>,
|
||||
# Copyright (c) 2010 Martin Matuska <mm@FreeBSD.org>,
|
||||
# Copyright (c) 2010-2011 Jason J. Hellenthal <jhell@DataIX.net>,
|
||||
# Copyright (c) 2017 Scot W. Stevenson <scot.stevenson@gmail.com>
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
# SUCH DAMAGE.
|
||||
"""Print statistics on the ZFS ARC Cache and other information
|
||||
|
||||
Provides basic information on the ARC, its efficiency, the L2ARC (if present),
|
||||
the Data Management Unit (DMU), Virtual Devices (VDEVs), and tunables. See
|
||||
the in-source documentation and code at
|
||||
https://github.com/openzfs/zfs/blob/master/module/zfs/arc.c for details.
|
||||
The original introduction to arc_summary can be found at
|
||||
http://cuddletech.com/?p=454
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
import errno
|
||||
|
||||
# We can't use env -S portably, and we need python3 -u to handle pipes in
|
||||
# the shell abruptly closing the way we want to, so...
|
||||
import io
|
||||
if isinstance(sys.__stderr__.buffer, io.BufferedWriter):
|
||||
os.execv(sys.executable, [sys.executable, "-u"] + sys.argv)
|
||||
|
||||
DESCRIPTION = 'Print ARC and other statistics for OpenZFS'
|
||||
INDENT = ' '*8
|
||||
LINE_LENGTH = 72
|
||||
DATE_FORMAT = '%a %b %d %H:%M:%S %Y'
|
||||
TITLE = 'ZFS Subsystem Report'
|
||||
|
||||
SECTIONS = 'arc archits dmu l2arc spl tunables vdev zil'.split()
|
||||
SECTION_HELP = 'print info from one section ('+' '.join(SECTIONS)+')'
|
||||
|
||||
# Tunables and SPL are handled separately because they come from
|
||||
# different sources
|
||||
SECTION_PATHS = {'arc': 'arcstats',
|
||||
'dmu': 'dmu_tx',
|
||||
'l2arc': 'arcstats', # L2ARC stuff lives in arcstats
|
||||
'vdev': 'vdev_cache_stats',
|
||||
'zfetch': 'zfetchstats',
|
||||
'zil': 'zil'}
|
||||
|
||||
parser = argparse.ArgumentParser(description=DESCRIPTION)
|
||||
parser.add_argument('-a', '--alternate', action='store_true', default=False,
|
||||
help='use alternate formatting for tunables and SPL',
|
||||
dest='alt')
|
||||
parser.add_argument('-d', '--description', action='store_true', default=False,
|
||||
help='print descriptions with tunables and SPL',
|
||||
dest='desc')
|
||||
parser.add_argument('-g', '--graph', action='store_true', default=False,
|
||||
help='print graph on ARC use and exit', dest='graph')
|
||||
parser.add_argument('-p', '--page', type=int, dest='page',
|
||||
help='print page by number (DEPRECATED, use "-s")')
|
||||
parser.add_argument('-r', '--raw', action='store_true', default=False,
|
||||
help='dump all available data with minimal formatting',
|
||||
dest='raw')
|
||||
parser.add_argument('-s', '--section', dest='section', help=SECTION_HELP)
|
||||
ARGS = parser.parse_args()
|
||||
|
||||
|
||||
if sys.platform.startswith('freebsd'):
|
||||
# Requires py36-sysctl on FreeBSD
|
||||
import sysctl
|
||||
|
||||
VDEV_CACHE_SIZE = 'vdev.cache_size'
|
||||
|
||||
def is_value(ctl):
|
||||
return ctl.type != sysctl.CTLTYPE_NODE
|
||||
|
||||
def namefmt(ctl, base='vfs.zfs.'):
|
||||
# base is removed from the name
|
||||
cut = len(base)
|
||||
return ctl.name[cut:]
|
||||
|
||||
def load_kstats(section):
|
||||
base = 'kstat.zfs.misc.{section}.'.format(section=section)
|
||||
fmt = lambda kstat: '{name} : {value}'.format(name=namefmt(kstat, base),
|
||||
value=kstat.value)
|
||||
kstats = sysctl.filter(base)
|
||||
return [fmt(kstat) for kstat in kstats if is_value(kstat)]
|
||||
|
||||
def get_params(base):
|
||||
ctls = sysctl.filter(base)
|
||||
return {namefmt(ctl): str(ctl.value) for ctl in ctls if is_value(ctl)}
|
||||
|
||||
def get_tunable_params():
|
||||
return get_params('vfs.zfs')
|
||||
|
||||
def get_vdev_params():
|
||||
return get_params('vfs.zfs.vdev')
|
||||
|
||||
def get_version_impl(request):
|
||||
# FreeBSD reports versions for zpl and spa instead of zfs and spl.
|
||||
name = {'zfs': 'zpl',
|
||||
'spl': 'spa'}[request]
|
||||
mib = 'vfs.zfs.version.{}'.format(name)
|
||||
version = sysctl.filter(mib)[0].value
|
||||
return '{} version {}'.format(name, version)
|
||||
|
||||
def get_descriptions(_request):
|
||||
ctls = sysctl.filter('vfs.zfs')
|
||||
return {namefmt(ctl): ctl.description for ctl in ctls if is_value(ctl)}
|
||||
|
||||
|
||||
elif sys.platform.startswith('linux'):
|
||||
KSTAT_PATH = '/proc/spl/kstat/zfs'
|
||||
SPL_PATH = '/sys/module/spl/parameters'
|
||||
TUNABLES_PATH = '/sys/module/zfs/parameters'
|
||||
|
||||
VDEV_CACHE_SIZE = 'zfs_vdev_cache_size'
|
||||
|
||||
def load_kstats(section):
|
||||
path = os.path.join(KSTAT_PATH, section)
|
||||
with open(path) as f:
|
||||
return list(f)[2:] # Get rid of header
|
||||
|
||||
def get_params(basepath):
|
||||
"""Collect information on the Solaris Porting Layer (SPL) or the
|
||||
tunables, depending on the PATH given. Does not check if PATH is
|
||||
legal.
|
||||
"""
|
||||
result = {}
|
||||
for name in os.listdir(basepath):
|
||||
path = os.path.join(basepath, name)
|
||||
with open(path) as f:
|
||||
value = f.read()
|
||||
result[name] = value.strip()
|
||||
return result
|
||||
|
||||
def get_spl_params():
|
||||
return get_params(SPL_PATH)
|
||||
|
||||
def get_tunable_params():
|
||||
return get_params(TUNABLES_PATH)
|
||||
|
||||
def get_vdev_params():
|
||||
return get_params(TUNABLES_PATH)
|
||||
|
||||
def get_version_impl(request):
|
||||
# The original arc_summary called /sbin/modinfo/{spl,zfs} to get
|
||||
# the version information. We switch to /sys/module/{spl,zfs}/version
|
||||
# to make sure we get what is really loaded in the kernel
|
||||
try:
|
||||
with open("/sys/module/{}/version".format(request)) as f:
|
||||
return f.read().strip()
|
||||
except:
|
||||
return "(unknown)"
|
||||
|
||||
def get_descriptions(request):
|
||||
"""Get the descriptions of the Solaris Porting Layer (SPL) or the
|
||||
tunables, return with minimal formatting.
|
||||
"""
|
||||
|
||||
if request not in ('spl', 'zfs'):
|
||||
print('ERROR: description of "{0}" requested)'.format(request))
|
||||
sys.exit(1)
|
||||
|
||||
descs = {}
|
||||
target_prefix = 'parm:'
|
||||
|
||||
# We would prefer to do this with /sys/modules -- see the discussion at
|
||||
# get_version() -- but there isn't a way to get the descriptions from
|
||||
# there, so we fall back on modinfo
|
||||
command = ["/sbin/modinfo", request, "-0"]
|
||||
|
||||
info = ''
|
||||
|
||||
try:
|
||||
|
||||
info = subprocess.run(command, stdout=subprocess.PIPE,
|
||||
check=True, universal_newlines=True)
|
||||
raw_output = info.stdout.split('\0')
|
||||
|
||||
except subprocess.CalledProcessError:
|
||||
print("Error: Descriptions not available",
|
||||
"(can't access kernel module)")
|
||||
sys.exit(1)
|
||||
|
||||
for line in raw_output:
|
||||
|
||||
if not line.startswith(target_prefix):
|
||||
continue
|
||||
|
||||
line = line[len(target_prefix):].strip()
|
||||
name, raw_desc = line.split(':', 1)
|
||||
desc = raw_desc.rsplit('(', 1)[0]
|
||||
|
||||
if desc == '':
|
||||
desc = '(No description found)'
|
||||
|
||||
descs[name.strip()] = desc.strip()
|
||||
|
||||
return descs
|
||||
|
||||
def handle_unraisableException(exc_type, exc_value=None, exc_traceback=None,
|
||||
err_msg=None, object=None):
|
||||
handle_Exception(exc_type, object, exc_traceback)
|
||||
|
||||
def handle_Exception(ex_cls, ex, tb):
|
||||
if ex_cls is KeyboardInterrupt:
|
||||
sys.exit()
|
||||
|
||||
if ex_cls is BrokenPipeError:
|
||||
# It turns out that while sys.exit() triggers an exception
|
||||
# not handled message on Python 3.8+, os._exit() does not.
|
||||
os._exit(0)
|
||||
|
||||
if ex_cls is OSError:
|
||||
if ex.errno == errno.ENOTCONN:
|
||||
sys.exit()
|
||||
|
||||
raise ex
|
||||
|
||||
if hasattr(sys,'unraisablehook'): # Python 3.8+
|
||||
sys.unraisablehook = handle_unraisableException
|
||||
sys.excepthook = handle_Exception
|
||||
|
||||
|
||||
def cleanup_line(single_line):
|
||||
"""Format a raw line of data from /proc and isolate the name value
|
||||
part, returning a tuple with each. Currently, this gets rid of the
|
||||
middle '4'. For example "arc_no_grow 4 0" returns the tuple
|
||||
("arc_no_grow", "0").
|
||||
"""
|
||||
name, _, value = single_line.split()
|
||||
|
||||
return name, value
|
||||
|
||||
|
||||
def draw_graph(kstats_dict):
|
||||
"""Draw a primitive graph representing the basic information on the
|
||||
ARC -- its size and the proportion used by MFU and MRU -- and quit.
|
||||
We use max size of the ARC to calculate how full it is. This is a
|
||||
very rough representation.
|
||||
"""
|
||||
|
||||
arc_stats = isolate_section('arcstats', kstats_dict)
|
||||
|
||||
GRAPH_INDENT = ' '*4
|
||||
GRAPH_WIDTH = 60
|
||||
arc_size = f_bytes(arc_stats['size'])
|
||||
arc_perc = f_perc(arc_stats['size'], arc_stats['c_max'])
|
||||
mfu_size = f_bytes(arc_stats['mfu_size'])
|
||||
mru_size = f_bytes(arc_stats['mru_size'])
|
||||
meta_limit = f_bytes(arc_stats['arc_meta_limit'])
|
||||
meta_size = f_bytes(arc_stats['arc_meta_used'])
|
||||
dnode_limit = f_bytes(arc_stats['arc_dnode_limit'])
|
||||
dnode_size = f_bytes(arc_stats['dnode_size'])
|
||||
|
||||
info_form = ('ARC: {0} ({1}) MFU: {2} MRU: {3} META: {4} ({5}) '
|
||||
'DNODE {6} ({7})')
|
||||
info_line = info_form.format(arc_size, arc_perc, mfu_size, mru_size,
|
||||
meta_size, meta_limit, dnode_size,
|
||||
dnode_limit)
|
||||
info_spc = ' '*int((GRAPH_WIDTH-len(info_line))/2)
|
||||
info_line = GRAPH_INDENT+info_spc+info_line
|
||||
|
||||
graph_line = GRAPH_INDENT+'+'+('-'*(GRAPH_WIDTH-2))+'+'
|
||||
|
||||
mfu_perc = float(int(arc_stats['mfu_size'])/int(arc_stats['c_max']))
|
||||
mru_perc = float(int(arc_stats['mru_size'])/int(arc_stats['c_max']))
|
||||
arc_perc = float(int(arc_stats['size'])/int(arc_stats['c_max']))
|
||||
total_ticks = float(arc_perc)*GRAPH_WIDTH
|
||||
mfu_ticks = mfu_perc*GRAPH_WIDTH
|
||||
mru_ticks = mru_perc*GRAPH_WIDTH
|
||||
other_ticks = total_ticks-(mfu_ticks+mru_ticks)
|
||||
|
||||
core_form = 'F'*int(mfu_ticks)+'R'*int(mru_ticks)+'O'*int(other_ticks)
|
||||
core_spc = ' '*(GRAPH_WIDTH-(2+len(core_form)))
|
||||
core_line = GRAPH_INDENT+'|'+core_form+core_spc+'|'
|
||||
|
||||
for line in ('', info_line, graph_line, core_line, graph_line, ''):
|
||||
print(line)
|
||||
|
||||
|
||||
def f_bytes(byte_string):
|
||||
"""Return human-readable representation of a byte value in
|
||||
powers of 2 (eg "KiB" for "kibibytes", etc) to two decimal
|
||||
points. Values smaller than one KiB are returned without
|
||||
decimal points. Note "bytes" is a reserved keyword.
|
||||
"""
|
||||
|
||||
prefixes = ([2**80, "YiB"], # yobibytes (yotta)
|
||||
[2**70, "ZiB"], # zebibytes (zetta)
|
||||
[2**60, "EiB"], # exbibytes (exa)
|
||||
[2**50, "PiB"], # pebibytes (peta)
|
||||
[2**40, "TiB"], # tebibytes (tera)
|
||||
[2**30, "GiB"], # gibibytes (giga)
|
||||
[2**20, "MiB"], # mebibytes (mega)
|
||||
[2**10, "KiB"]) # kibibytes (kilo)
|
||||
|
||||
bites = int(byte_string)
|
||||
|
||||
if bites >= 2**10:
|
||||
for limit, unit in prefixes:
|
||||
|
||||
if bites >= limit:
|
||||
value = bites / limit
|
||||
break
|
||||
|
||||
result = '{0:.1f} {1}'.format(value, unit)
|
||||
else:
|
||||
result = '{0} Bytes'.format(bites)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def f_hits(hits_string):
|
||||
"""Create a human-readable representation of the number of hits.
|
||||
The single-letter symbols used are SI to avoid the confusion caused
|
||||
by the different "short scale" and "long scale" representations in
|
||||
English, which use the same words for different values. See
|
||||
https://en.wikipedia.org/wiki/Names_of_large_numbers and:
|
||||
https://physics.nist.gov/cuu/Units/prefixes.html
|
||||
"""
|
||||
|
||||
numbers = ([10**24, 'Y'], # yotta (septillion)
|
||||
[10**21, 'Z'], # zetta (sextillion)
|
||||
[10**18, 'E'], # exa (quintrillion)
|
||||
[10**15, 'P'], # peta (quadrillion)
|
||||
[10**12, 'T'], # tera (trillion)
|
||||
[10**9, 'G'], # giga (billion)
|
||||
[10**6, 'M'], # mega (million)
|
||||
[10**3, 'k']) # kilo (thousand)
|
||||
|
||||
hits = int(hits_string)
|
||||
|
||||
if hits >= 1000:
|
||||
for limit, symbol in numbers:
|
||||
|
||||
if hits >= limit:
|
||||
value = hits/limit
|
||||
break
|
||||
|
||||
result = "%0.1f%s" % (value, symbol)
|
||||
else:
|
||||
result = "%d" % hits
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def f_perc(value1, value2):
|
||||
"""Calculate percentage and return in human-readable form. If
|
||||
rounding produces the result '0.0' though the first number is
|
||||
not zero, include a 'less-than' symbol to avoid confusion.
|
||||
Division by zero is handled by returning 'n/a'; no error
|
||||
is called.
|
||||
"""
|
||||
|
||||
v1 = float(value1)
|
||||
v2 = float(value2)
|
||||
|
||||
try:
|
||||
perc = 100 * v1/v2
|
||||
except ZeroDivisionError:
|
||||
result = 'n/a'
|
||||
else:
|
||||
result = '{0:0.1f} %'.format(perc)
|
||||
|
||||
if result == '0.0 %' and v1 > 0:
|
||||
result = '< 0.1 %'
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def format_raw_line(name, value):
|
||||
"""For the --raw option for the tunable and SPL outputs, decide on the
|
||||
correct formatting based on the --alternate flag.
|
||||
"""
|
||||
|
||||
if ARGS.alt:
|
||||
result = '{0}{1}={2}'.format(INDENT, name, value)
|
||||
else:
|
||||
# Right-align the value within the line length if it fits,
|
||||
# otherwise just separate it from the name by a single space.
|
||||
fit = LINE_LENGTH - len(INDENT) - len(name)
|
||||
overflow = len(value) + 1
|
||||
w = max(fit, overflow)
|
||||
result = '{0}{1}{2:>{w}}'.format(INDENT, name, value, w=w)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def get_kstats():
|
||||
"""Collect information on the ZFS subsystem. The step does not perform any
|
||||
further processing, giving us the option to only work on what is actually
|
||||
needed. The name "kstat" is a holdover from the Solaris utility of the same
|
||||
name.
|
||||
"""
|
||||
|
||||
result = {}
|
||||
|
||||
for section in SECTION_PATHS.values():
|
||||
if section not in result:
|
||||
result[section] = load_kstats(section)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def get_version(request):
|
||||
"""Get the version number of ZFS or SPL on this machine for header.
|
||||
Returns an error string, but does not raise an error, if we can't
|
||||
get the ZFS/SPL version.
|
||||
"""
|
||||
|
||||
if request not in ('spl', 'zfs'):
|
||||
error_msg = '(ERROR: "{0}" requested)'.format(request)
|
||||
return error_msg
|
||||
|
||||
return get_version_impl(request)
|
||||
|
||||
|
||||
def print_header():
|
||||
"""Print the initial heading with date and time as well as info on the
|
||||
kernel and ZFS versions. This is not called for the graph.
|
||||
"""
|
||||
|
||||
# datetime is now recommended over time but we keep the exact formatting
|
||||
# from the older version of arc_summary in case there are scripts
|
||||
# that expect it in this way
|
||||
daydate = time.strftime(DATE_FORMAT)
|
||||
spc_date = LINE_LENGTH-len(daydate)
|
||||
sys_version = os.uname()
|
||||
|
||||
sys_msg = sys_version.sysname+' '+sys_version.release
|
||||
zfs = get_version('zfs')
|
||||
spc_zfs = LINE_LENGTH-len(zfs)
|
||||
|
||||
machine_msg = 'Machine: '+sys_version.nodename+' ('+sys_version.machine+')'
|
||||
spl = get_version('spl')
|
||||
spc_spl = LINE_LENGTH-len(spl)
|
||||
|
||||
print('\n'+('-'*LINE_LENGTH))
|
||||
print('{0:<{spc}}{1}'.format(TITLE, daydate, spc=spc_date))
|
||||
print('{0:<{spc}}{1}'.format(sys_msg, zfs, spc=spc_zfs))
|
||||
print('{0:<{spc}}{1}\n'.format(machine_msg, spl, spc=spc_spl))
|
||||
|
||||
|
||||
def print_raw(kstats_dict):
|
||||
"""Print all available data from the system in a minimally sorted format.
|
||||
This can be used as a source to be piped through 'grep'.
|
||||
"""
|
||||
|
||||
sections = sorted(kstats_dict.keys())
|
||||
|
||||
for section in sections:
|
||||
|
||||
print('\n{0}:'.format(section.upper()))
|
||||
lines = sorted(kstats_dict[section])
|
||||
|
||||
for line in lines:
|
||||
name, value = cleanup_line(line)
|
||||
print(format_raw_line(name, value))
|
||||
|
||||
# Tunables and SPL must be handled separately because they come from a
|
||||
# different source and have descriptions the user might request
|
||||
print()
|
||||
section_spl()
|
||||
section_tunables()
|
||||
|
||||
|
||||
def isolate_section(section_name, kstats_dict):
|
||||
"""From the complete information on all sections, retrieve only those
|
||||
for one section.
|
||||
"""
|
||||
|
||||
try:
|
||||
section_data = kstats_dict[section_name]
|
||||
except KeyError:
|
||||
print('ERROR: Data on {0} not available'.format(section_data))
|
||||
sys.exit(1)
|
||||
|
||||
section_dict = dict(cleanup_line(l) for l in section_data)
|
||||
|
||||
return section_dict
|
||||
|
||||
|
||||
# Formatted output helper functions
|
||||
|
||||
|
||||
def prt_1(text, value):
|
||||
"""Print text and one value, no indent"""
|
||||
spc = ' '*(LINE_LENGTH-(len(text)+len(value)))
|
||||
print('{0}{spc}{1}'.format(text, value, spc=spc))
|
||||
|
||||
|
||||
def prt_i1(text, value):
|
||||
"""Print text and one value, with indent"""
|
||||
spc = ' '*(LINE_LENGTH-(len(INDENT)+len(text)+len(value)))
|
||||
print(INDENT+'{0}{spc}{1}'.format(text, value, spc=spc))
|
||||
|
||||
|
||||
def prt_2(text, value1, value2):
|
||||
"""Print text and two values, no indent"""
|
||||
values = '{0:>9} {1:>9}'.format(value1, value2)
|
||||
spc = ' '*(LINE_LENGTH-(len(text)+len(values)+2))
|
||||
print('{0}{spc} {1}'.format(text, values, spc=spc))
|
||||
|
||||
|
||||
def prt_i2(text, value1, value2):
|
||||
"""Print text and two values, with indent"""
|
||||
values = '{0:>9} {1:>9}'.format(value1, value2)
|
||||
spc = ' '*(LINE_LENGTH-(len(INDENT)+len(text)+len(values)+2))
|
||||
print(INDENT+'{0}{spc} {1}'.format(text, values, spc=spc))
|
||||
|
||||
|
||||
# The section output concentrates on important parameters instead of
|
||||
# being exhaustive (that is what the --raw parameter is for)
|
||||
|
||||
|
||||
def section_arc(kstats_dict):
|
||||
"""Give basic information on the ARC, MRU and MFU. This is the first
|
||||
and most used section.
|
||||
"""
|
||||
|
||||
arc_stats = isolate_section('arcstats', kstats_dict)
|
||||
|
||||
throttle = arc_stats['memory_throttle_count']
|
||||
|
||||
if throttle == '0':
|
||||
health = 'HEALTHY'
|
||||
else:
|
||||
health = 'THROTTLED'
|
||||
|
||||
prt_1('ARC status:', health)
|
||||
prt_i1('Memory throttle count:', throttle)
|
||||
print()
|
||||
|
||||
arc_size = arc_stats['size']
|
||||
arc_target_size = arc_stats['c']
|
||||
arc_max = arc_stats['c_max']
|
||||
arc_min = arc_stats['c_min']
|
||||
mfu_size = arc_stats['mfu_size']
|
||||
mru_size = arc_stats['mru_size']
|
||||
meta_limit = arc_stats['arc_meta_limit']
|
||||
meta_size = arc_stats['arc_meta_used']
|
||||
dnode_limit = arc_stats['arc_dnode_limit']
|
||||
dnode_size = arc_stats['dnode_size']
|
||||
target_size_ratio = '{0}:1'.format(int(arc_max) // int(arc_min))
|
||||
|
||||
prt_2('ARC size (current):',
|
||||
f_perc(arc_size, arc_max), f_bytes(arc_size))
|
||||
prt_i2('Target size (adaptive):',
|
||||
f_perc(arc_target_size, arc_max), f_bytes(arc_target_size))
|
||||
prt_i2('Min size (hard limit):',
|
||||
f_perc(arc_min, arc_max), f_bytes(arc_min))
|
||||
prt_i2('Max size (high water):',
|
||||
target_size_ratio, f_bytes(arc_max))
|
||||
caches_size = int(mfu_size)+int(mru_size)
|
||||
prt_i2('Most Frequently Used (MFU) cache size:',
|
||||
f_perc(mfu_size, caches_size), f_bytes(mfu_size))
|
||||
prt_i2('Most Recently Used (MRU) cache size:',
|
||||
f_perc(mru_size, caches_size), f_bytes(mru_size))
|
||||
prt_i2('Metadata cache size (hard limit):',
|
||||
f_perc(meta_limit, arc_max), f_bytes(meta_limit))
|
||||
prt_i2('Metadata cache size (current):',
|
||||
f_perc(meta_size, meta_limit), f_bytes(meta_size))
|
||||
prt_i2('Dnode cache size (hard limit):',
|
||||
f_perc(dnode_limit, meta_limit), f_bytes(dnode_limit))
|
||||
prt_i2('Dnode cache size (current):',
|
||||
f_perc(dnode_size, dnode_limit), f_bytes(dnode_size))
|
||||
print()
|
||||
|
||||
print('ARC hash breakdown:')
|
||||
prt_i1('Elements max:', f_hits(arc_stats['hash_elements_max']))
|
||||
prt_i2('Elements current:',
|
||||
f_perc(arc_stats['hash_elements'], arc_stats['hash_elements_max']),
|
||||
f_hits(arc_stats['hash_elements']))
|
||||
prt_i1('Collisions:', f_hits(arc_stats['hash_collisions']))
|
||||
|
||||
prt_i1('Chain max:', f_hits(arc_stats['hash_chain_max']))
|
||||
prt_i1('Chains:', f_hits(arc_stats['hash_chains']))
|
||||
print()
|
||||
|
||||
print('ARC misc:')
|
||||
prt_i1('Deleted:', f_hits(arc_stats['deleted']))
|
||||
prt_i1('Mutex misses:', f_hits(arc_stats['mutex_miss']))
|
||||
prt_i1('Eviction skips:', f_hits(arc_stats['evict_skip']))
|
||||
prt_i1('Eviction skips due to L2 writes:',
|
||||
f_hits(arc_stats['evict_l2_skip']))
|
||||
prt_i1('L2 cached evictions:', f_bytes(arc_stats['evict_l2_cached']))
|
||||
prt_i1('L2 eligible evictions:', f_bytes(arc_stats['evict_l2_eligible']))
|
||||
prt_i2('L2 eligible MFU evictions:',
|
||||
f_perc(arc_stats['evict_l2_eligible_mfu'],
|
||||
arc_stats['evict_l2_eligible']),
|
||||
f_bytes(arc_stats['evict_l2_eligible_mfu']))
|
||||
prt_i2('L2 eligible MRU evictions:',
|
||||
f_perc(arc_stats['evict_l2_eligible_mru'],
|
||||
arc_stats['evict_l2_eligible']),
|
||||
f_bytes(arc_stats['evict_l2_eligible_mru']))
|
||||
prt_i1('L2 ineligible evictions:',
|
||||
f_bytes(arc_stats['evict_l2_ineligible']))
|
||||
print()
|
||||
|
||||
|
||||
def section_archits(kstats_dict):
|
||||
"""Print information on how the caches are accessed ("arc hits").
|
||||
"""
|
||||
|
||||
arc_stats = isolate_section('arcstats', kstats_dict)
|
||||
all_accesses = int(arc_stats['hits'])+int(arc_stats['misses'])
|
||||
actual_hits = int(arc_stats['mfu_hits'])+int(arc_stats['mru_hits'])
|
||||
|
||||
prt_1('ARC total accesses (hits + misses):', f_hits(all_accesses))
|
||||
ta_todo = (('Cache hit ratio:', arc_stats['hits']),
|
||||
('Cache miss ratio:', arc_stats['misses']),
|
||||
('Actual hit ratio (MFU + MRU hits):', actual_hits))
|
||||
|
||||
for title, value in ta_todo:
|
||||
prt_i2(title, f_perc(value, all_accesses), f_hits(value))
|
||||
|
||||
dd_total = int(arc_stats['demand_data_hits']) +\
|
||||
int(arc_stats['demand_data_misses'])
|
||||
prt_i2('Data demand efficiency:',
|
||||
f_perc(arc_stats['demand_data_hits'], dd_total),
|
||||
f_hits(dd_total))
|
||||
|
||||
dp_total = int(arc_stats['prefetch_data_hits']) +\
|
||||
int(arc_stats['prefetch_data_misses'])
|
||||
prt_i2('Data prefetch efficiency:',
|
||||
f_perc(arc_stats['prefetch_data_hits'], dp_total),
|
||||
f_hits(dp_total))
|
||||
|
||||
known_hits = int(arc_stats['mfu_hits']) +\
|
||||
int(arc_stats['mru_hits']) +\
|
||||
int(arc_stats['mfu_ghost_hits']) +\
|
||||
int(arc_stats['mru_ghost_hits'])
|
||||
|
||||
anon_hits = int(arc_stats['hits'])-known_hits
|
||||
|
||||
print()
|
||||
print('Cache hits by cache type:')
|
||||
cl_todo = (('Most frequently used (MFU):', arc_stats['mfu_hits']),
|
||||
('Most recently used (MRU):', arc_stats['mru_hits']),
|
||||
('Most frequently used (MFU) ghost:',
|
||||
arc_stats['mfu_ghost_hits']),
|
||||
('Most recently used (MRU) ghost:',
|
||||
arc_stats['mru_ghost_hits']))
|
||||
|
||||
for title, value in cl_todo:
|
||||
prt_i2(title, f_perc(value, arc_stats['hits']), f_hits(value))
|
||||
|
||||
# For some reason, anon_hits can turn negative, which is weird. Until we
|
||||
# have figured out why this happens, we just hide the problem, following
|
||||
# the behavior of the original arc_summary.
|
||||
if anon_hits >= 0:
|
||||
prt_i2('Anonymously used:',
|
||||
f_perc(anon_hits, arc_stats['hits']), f_hits(anon_hits))
|
||||
|
||||
print()
|
||||
print('Cache hits by data type:')
|
||||
dt_todo = (('Demand data:', arc_stats['demand_data_hits']),
|
||||
('Prefetch data:', arc_stats['prefetch_data_hits']),
|
||||
('Demand metadata:', arc_stats['demand_metadata_hits']),
|
||||
('Prefetch metadata:',
|
||||
arc_stats['prefetch_metadata_hits']))
|
||||
|
||||
for title, value in dt_todo:
|
||||
prt_i2(title, f_perc(value, arc_stats['hits']), f_hits(value))
|
||||
|
||||
print()
|
||||
print('Cache misses by data type:')
|
||||
dm_todo = (('Demand data:', arc_stats['demand_data_misses']),
|
||||
('Prefetch data:',
|
||||
arc_stats['prefetch_data_misses']),
|
||||
('Demand metadata:', arc_stats['demand_metadata_misses']),
|
||||
('Prefetch metadata:',
|
||||
arc_stats['prefetch_metadata_misses']))
|
||||
|
||||
for title, value in dm_todo:
|
||||
prt_i2(title, f_perc(value, arc_stats['misses']), f_hits(value))
|
||||
|
||||
print()
|
||||
|
||||
|
||||
def section_dmu(kstats_dict):
|
||||
"""Collect information on the DMU"""
|
||||
|
||||
zfetch_stats = isolate_section('zfetchstats', kstats_dict)
|
||||
|
||||
zfetch_access_total = int(zfetch_stats['hits'])+int(zfetch_stats['misses'])
|
||||
|
||||
prt_1('DMU prefetch efficiency:', f_hits(zfetch_access_total))
|
||||
prt_i2('Hit ratio:', f_perc(zfetch_stats['hits'], zfetch_access_total),
|
||||
f_hits(zfetch_stats['hits']))
|
||||
prt_i2('Miss ratio:', f_perc(zfetch_stats['misses'], zfetch_access_total),
|
||||
f_hits(zfetch_stats['misses']))
|
||||
print()
|
||||
|
||||
|
||||
def section_l2arc(kstats_dict):
|
||||
"""Collect information on L2ARC device if present. If not, tell user
|
||||
that we're skipping the section.
|
||||
"""
|
||||
|
||||
# The L2ARC statistics live in the same section as the normal ARC stuff
|
||||
arc_stats = isolate_section('arcstats', kstats_dict)
|
||||
|
||||
if arc_stats['l2_size'] == '0':
|
||||
print('L2ARC not detected, skipping section\n')
|
||||
return
|
||||
|
||||
l2_errors = int(arc_stats['l2_writes_error']) +\
|
||||
int(arc_stats['l2_cksum_bad']) +\
|
||||
int(arc_stats['l2_io_error'])
|
||||
|
||||
l2_access_total = int(arc_stats['l2_hits'])+int(arc_stats['l2_misses'])
|
||||
health = 'HEALTHY'
|
||||
|
||||
if l2_errors > 0:
|
||||
health = 'DEGRADED'
|
||||
|
||||
prt_1('L2ARC status:', health)
|
||||
|
||||
l2_todo = (('Low memory aborts:', 'l2_abort_lowmem'),
|
||||
('Free on write:', 'l2_free_on_write'),
|
||||
('R/W clashes:', 'l2_rw_clash'),
|
||||
('Bad checksums:', 'l2_cksum_bad'),
|
||||
('I/O errors:', 'l2_io_error'))
|
||||
|
||||
for title, value in l2_todo:
|
||||
prt_i1(title, f_hits(arc_stats[value]))
|
||||
|
||||
print()
|
||||
prt_1('L2ARC size (adaptive):', f_bytes(arc_stats['l2_size']))
|
||||
prt_i2('Compressed:', f_perc(arc_stats['l2_asize'], arc_stats['l2_size']),
|
||||
f_bytes(arc_stats['l2_asize']))
|
||||
prt_i2('Header size:',
|
||||
f_perc(arc_stats['l2_hdr_size'], arc_stats['l2_size']),
|
||||
f_bytes(arc_stats['l2_hdr_size']))
|
||||
prt_i2('MFU allocated size:',
|
||||
f_perc(arc_stats['l2_mfu_asize'], arc_stats['l2_asize']),
|
||||
f_bytes(arc_stats['l2_mfu_asize']))
|
||||
prt_i2('MRU allocated size:',
|
||||
f_perc(arc_stats['l2_mru_asize'], arc_stats['l2_asize']),
|
||||
f_bytes(arc_stats['l2_mru_asize']))
|
||||
prt_i2('Prefetch allocated size:',
|
||||
f_perc(arc_stats['l2_prefetch_asize'], arc_stats['l2_asize']),
|
||||
f_bytes(arc_stats['l2_prefetch_asize']))
|
||||
prt_i2('Data (buffer content) allocated size:',
|
||||
f_perc(arc_stats['l2_bufc_data_asize'], arc_stats['l2_asize']),
|
||||
f_bytes(arc_stats['l2_bufc_data_asize']))
|
||||
prt_i2('Metadata (buffer content) allocated size:',
|
||||
f_perc(arc_stats['l2_bufc_metadata_asize'], arc_stats['l2_asize']),
|
||||
f_bytes(arc_stats['l2_bufc_metadata_asize']))
|
||||
|
||||
print()
|
||||
prt_1('L2ARC breakdown:', f_hits(l2_access_total))
|
||||
prt_i2('Hit ratio:',
|
||||
f_perc(arc_stats['l2_hits'], l2_access_total),
|
||||
f_hits(arc_stats['l2_hits']))
|
||||
prt_i2('Miss ratio:',
|
||||
f_perc(arc_stats['l2_misses'], l2_access_total),
|
||||
f_hits(arc_stats['l2_misses']))
|
||||
prt_i1('Feeds:', f_hits(arc_stats['l2_feeds']))
|
||||
|
||||
print()
|
||||
print('L2ARC writes:')
|
||||
|
||||
if arc_stats['l2_writes_done'] != arc_stats['l2_writes_sent']:
|
||||
prt_i2('Writes sent:', 'FAULTED', f_hits(arc_stats['l2_writes_sent']))
|
||||
prt_i2('Done ratio:',
|
||||
f_perc(arc_stats['l2_writes_done'],
|
||||
arc_stats['l2_writes_sent']),
|
||||
f_hits(arc_stats['l2_writes_done']))
|
||||
prt_i2('Error ratio:',
|
||||
f_perc(arc_stats['l2_writes_error'],
|
||||
arc_stats['l2_writes_sent']),
|
||||
f_hits(arc_stats['l2_writes_error']))
|
||||
else:
|
||||
prt_i2('Writes sent:', '100 %', f_hits(arc_stats['l2_writes_sent']))
|
||||
|
||||
print()
|
||||
print('L2ARC evicts:')
|
||||
prt_i1('Lock retries:', f_hits(arc_stats['l2_evict_lock_retry']))
|
||||
prt_i1('Upon reading:', f_hits(arc_stats['l2_evict_reading']))
|
||||
print()
|
||||
|
||||
|
||||
def section_spl(*_):
|
||||
"""Print the SPL parameters, if requested with alternative format
|
||||
and/or descriptions. This does not use kstats.
|
||||
"""
|
||||
|
||||
if sys.platform.startswith('freebsd'):
|
||||
# No SPL support in FreeBSD
|
||||
return
|
||||
|
||||
spls = get_spl_params()
|
||||
keylist = sorted(spls.keys())
|
||||
print('Solaris Porting Layer (SPL):')
|
||||
|
||||
if ARGS.desc:
|
||||
descriptions = get_descriptions('spl')
|
||||
|
||||
for key in keylist:
|
||||
value = spls[key]
|
||||
|
||||
if ARGS.desc:
|
||||
try:
|
||||
print(INDENT+'#', descriptions[key])
|
||||
except KeyError:
|
||||
print(INDENT+'# (No description found)') # paranoid
|
||||
|
||||
print(format_raw_line(key, value))
|
||||
|
||||
print()
|
||||
|
||||
|
||||
def section_tunables(*_):
|
||||
"""Print the tunables, if requested with alternative format and/or
|
||||
descriptions. This does not use kstasts.
|
||||
"""
|
||||
|
||||
tunables = get_tunable_params()
|
||||
keylist = sorted(tunables.keys())
|
||||
print('Tunables:')
|
||||
|
||||
if ARGS.desc:
|
||||
descriptions = get_descriptions('zfs')
|
||||
|
||||
for key in keylist:
|
||||
value = tunables[key]
|
||||
|
||||
if ARGS.desc:
|
||||
try:
|
||||
print(INDENT+'#', descriptions[key])
|
||||
except KeyError:
|
||||
print(INDENT+'# (No description found)') # paranoid
|
||||
|
||||
print(format_raw_line(key, value))
|
||||
|
||||
print()
|
||||
|
||||
|
||||
def section_vdev(kstats_dict):
|
||||
"""Collect information on VDEV caches"""
|
||||
|
||||
# Currently [Nov 2017] the VDEV cache is disabled, because it is actually
|
||||
# harmful. When this is the case, we just skip the whole entry. See
|
||||
# https://github.com/openzfs/zfs/blob/master/module/zfs/vdev_cache.c
|
||||
# for details
|
||||
tunables = get_vdev_params()
|
||||
|
||||
if tunables[VDEV_CACHE_SIZE] == '0':
|
||||
print('VDEV cache disabled, skipping section\n')
|
||||
return
|
||||
|
||||
vdev_stats = isolate_section('vdev_cache_stats', kstats_dict)
|
||||
|
||||
vdev_cache_total = int(vdev_stats['hits']) +\
|
||||
int(vdev_stats['misses']) +\
|
||||
int(vdev_stats['delegations'])
|
||||
|
||||
prt_1('VDEV cache summary:', f_hits(vdev_cache_total))
|
||||
prt_i2('Hit ratio:', f_perc(vdev_stats['hits'], vdev_cache_total),
|
||||
f_hits(vdev_stats['hits']))
|
||||
prt_i2('Miss ratio:', f_perc(vdev_stats['misses'], vdev_cache_total),
|
||||
f_hits(vdev_stats['misses']))
|
||||
prt_i2('Delegations:', f_perc(vdev_stats['delegations'], vdev_cache_total),
|
||||
f_hits(vdev_stats['delegations']))
|
||||
print()
|
||||
|
||||
|
||||
def section_zil(kstats_dict):
|
||||
"""Collect information on the ZFS Intent Log. Some of the information
|
||||
taken from https://github.com/openzfs/zfs/blob/master/include/sys/zil.h
|
||||
"""
|
||||
|
||||
zil_stats = isolate_section('zil', kstats_dict)
|
||||
|
||||
prt_1('ZIL committed transactions:',
|
||||
f_hits(zil_stats['zil_itx_count']))
|
||||
prt_i1('Commit requests:', f_hits(zil_stats['zil_commit_count']))
|
||||
prt_i1('Flushes to stable storage:',
|
||||
f_hits(zil_stats['zil_commit_writer_count']))
|
||||
prt_i2('Transactions to SLOG storage pool:',
|
||||
f_bytes(zil_stats['zil_itx_metaslab_slog_bytes']),
|
||||
f_hits(zil_stats['zil_itx_metaslab_slog_count']))
|
||||
prt_i2('Transactions to non-SLOG storage pool:',
|
||||
f_bytes(zil_stats['zil_itx_metaslab_normal_bytes']),
|
||||
f_hits(zil_stats['zil_itx_metaslab_normal_count']))
|
||||
print()
|
||||
|
||||
|
||||
section_calls = {'arc': section_arc,
|
||||
'archits': section_archits,
|
||||
'dmu': section_dmu,
|
||||
'l2arc': section_l2arc,
|
||||
'spl': section_spl,
|
||||
'tunables': section_tunables,
|
||||
'vdev': section_vdev,
|
||||
'zil': section_zil}
|
||||
|
||||
|
||||
def main():
|
||||
"""Run program. The options to draw a graph and to print all data raw are
|
||||
treated separately because they come with their own call.
|
||||
"""
|
||||
|
||||
kstats = get_kstats()
|
||||
|
||||
if ARGS.graph:
|
||||
draw_graph(kstats)
|
||||
sys.exit(0)
|
||||
|
||||
print_header()
|
||||
|
||||
if ARGS.raw:
|
||||
print_raw(kstats)
|
||||
|
||||
elif ARGS.section:
|
||||
|
||||
try:
|
||||
section_calls[ARGS.section](kstats)
|
||||
except KeyError:
|
||||
print('Error: Section "{0}" unknown'.format(ARGS.section))
|
||||
sys.exit(1)
|
||||
|
||||
elif ARGS.page:
|
||||
print('WARNING: Pages are deprecated, please use "--section"\n')
|
||||
|
||||
pages_to_calls = {1: 'arc',
|
||||
2: 'archits',
|
||||
3: 'l2arc',
|
||||
4: 'dmu',
|
||||
5: 'vdev',
|
||||
6: 'tunables'}
|
||||
|
||||
try:
|
||||
call = pages_to_calls[ARGS.page]
|
||||
except KeyError:
|
||||
print('Error: Page "{0}" not supported'.format(ARGS.page))
|
||||
sys.exit(1)
|
||||
else:
|
||||
section_calls[call](kstats)
|
||||
|
||||
else:
|
||||
# If no parameters were given, we print all sections. We might want to
|
||||
# change the sequence by hand
|
||||
calls = sorted(section_calls.keys())
|
||||
|
||||
for section in calls:
|
||||
section_calls[section](kstats)
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1 +0,0 @@
|
||||
arcstat
|
||||
@@ -1,5 +1 @@
|
||||
include $(top_srcdir)/config/Substfiles.am
|
||||
|
||||
bin_SCRIPTS = arcstat
|
||||
|
||||
SUBSTFILES += $(bin_SCRIPTS)
|
||||
dist_bin_SCRIPTS = arcstat.py
|
||||
|
||||
@@ -1,25 +1,20 @@
|
||||
#!/usr/bin/env @PYTHON_SHEBANG@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Print out ZFS ARC Statistics exported via kstat(1)
|
||||
# For a definition of fields, or usage, use arcstat -v
|
||||
# For a definition of fields, or usage, use arctstat.pl -v
|
||||
#
|
||||
# This script was originally a fork of the original arcstat.pl (0.1)
|
||||
# by Neelakanth Nadgir, originally published on his Sun blog on
|
||||
# This script is a fork of the original arcstat.pl (0.1) by
|
||||
# Neelakanth Nadgir, originally published on his Sun blog on
|
||||
# 09/18/2007
|
||||
# http://blogs.sun.com/realneel/entry/zfs_arc_statistics
|
||||
#
|
||||
# A new version aimed to improve upon the original by adding features
|
||||
# and fixing bugs as needed. This version was maintained by Mike
|
||||
# Harsch and was hosted in a public open source repository:
|
||||
# This version aims to improve upon the original by adding features
|
||||
# and fixing bugs as needed. This version is maintained by
|
||||
# Mike Harsch and is hosted in a public open source repository:
|
||||
# http://github.com/mharsch/arcstat
|
||||
#
|
||||
# but has since moved to the illumos-gate repository.
|
||||
#
|
||||
# This Python port was written by John Hixson for FreeNAS, introduced
|
||||
# in commit e2c29f:
|
||||
# https://github.com/freenas/freenas
|
||||
#
|
||||
# and has been improved by many people since.
|
||||
# Comments, Questions, or Suggestions are always welcome.
|
||||
# Contact the maintainer at ( mike at harschsystems dot com )
|
||||
#
|
||||
# CDDL HEADER START
|
||||
#
|
||||
@@ -47,8 +42,7 @@
|
||||
# @hdr is the array of fields that needs to be printed, so we
|
||||
# just iterate over this array and print the values using our pretty printer.
|
||||
#
|
||||
# This script must remain compatible with Python 3.6+.
|
||||
#
|
||||
|
||||
|
||||
import sys
|
||||
import time
|
||||
@@ -56,16 +50,16 @@ import getopt
|
||||
import re
|
||||
import copy
|
||||
|
||||
from decimal import Decimal
|
||||
from signal import signal, SIGINT, SIGWINCH, SIG_DFL
|
||||
|
||||
|
||||
cols = {
|
||||
# HDR: [Size, Scale, Description]
|
||||
"time": [8, -1, "Time"],
|
||||
"hits": [4, 1000, "ARC reads per second"],
|
||||
"miss": [4, 1000, "ARC misses per second"],
|
||||
"read": [4, 1000, "Total ARC accesses per second"],
|
||||
"hit%": [4, 100, "ARC hit percentage"],
|
||||
"hit%": [4, 100, "ARC Hit percentage"],
|
||||
"miss%": [5, 100, "ARC miss percentage"],
|
||||
"dhit": [4, 1000, "Demand hits per second"],
|
||||
"dmis": [4, 1000, "Demand misses per second"],
|
||||
@@ -77,23 +71,16 @@ cols = {
|
||||
"pm%": [3, 100, "Prefetch miss percentage"],
|
||||
"mhit": [4, 1000, "Metadata hits per second"],
|
||||
"mmis": [4, 1000, "Metadata misses per second"],
|
||||
"mread": [5, 1000, "Metadata accesses per second"],
|
||||
"mread": [4, 1000, "Metadata accesses per second"],
|
||||
"mh%": [3, 100, "Metadata hit percentage"],
|
||||
"mm%": [3, 100, "Metadata miss percentage"],
|
||||
"arcsz": [5, 1024, "ARC size"],
|
||||
"size": [4, 1024, "ARC size"],
|
||||
"c": [4, 1024, "ARC target size"],
|
||||
"mfu": [4, 1000, "MFU list hits per second"],
|
||||
"mru": [4, 1000, "MRU list hits per second"],
|
||||
"mfug": [4, 1000, "MFU ghost list hits per second"],
|
||||
"mrug": [4, 1000, "MRU ghost list hits per second"],
|
||||
"arcsz": [5, 1024, "ARC Size"],
|
||||
"c": [4, 1024, "ARC Target Size"],
|
||||
"mfu": [4, 1000, "MFU List hits per second"],
|
||||
"mru": [4, 1000, "MRU List hits per second"],
|
||||
"mfug": [4, 1000, "MFU Ghost List hits per second"],
|
||||
"mrug": [4, 1000, "MRU Ghost List hits per second"],
|
||||
"eskip": [5, 1000, "evict_skip per second"],
|
||||
"el2skip": [7, 1000, "evict skip, due to l2 writes, per second"],
|
||||
"el2cach": [7, 1024, "Size of L2 cached evictions per second"],
|
||||
"el2el": [5, 1024, "Size of L2 eligible evictions per second"],
|
||||
"el2mfu": [6, 1024, "Size of L2 eligible MFU evictions per second"],
|
||||
"el2mru": [6, 1024, "Size of L2 eligible MRU evictions per second"],
|
||||
"el2inel": [7, 1024, "Size of L2 ineligible evictions per second"],
|
||||
"mtxmis": [6, 1000, "mutex_miss per second"],
|
||||
"dread": [5, 1000, "Demand accesses per second"],
|
||||
"pread": [5, 1000, "Prefetch accesses per second"],
|
||||
@@ -102,29 +89,14 @@ cols = {
|
||||
"l2read": [6, 1000, "Total L2ARC accesses per second"],
|
||||
"l2hit%": [6, 100, "L2ARC access hit percentage"],
|
||||
"l2miss%": [7, 100, "L2ARC access miss percentage"],
|
||||
"l2pref": [6, 1024, "L2ARC prefetch allocated size"],
|
||||
"l2mfu": [5, 1024, "L2ARC MFU allocated size"],
|
||||
"l2mru": [5, 1024, "L2ARC MRU allocated size"],
|
||||
"l2data": [6, 1024, "L2ARC data allocated size"],
|
||||
"l2meta": [6, 1024, "L2ARC metadata allocated size"],
|
||||
"l2pref%": [7, 100, "L2ARC prefetch percentage"],
|
||||
"l2mfu%": [6, 100, "L2ARC MFU percentage"],
|
||||
"l2mru%": [6, 100, "L2ARC MRU percentage"],
|
||||
"l2data%": [7, 100, "L2ARC data percentage"],
|
||||
"l2meta%": [7, 100, "L2ARC metadata percentage"],
|
||||
"l2asize": [7, 1024, "Actual (compressed) size of the L2ARC"],
|
||||
"l2size": [6, 1024, "Size of the L2ARC"],
|
||||
"l2bytes": [7, 1024, "Bytes read per second from the L2ARC"],
|
||||
"grow": [4, 1000, "ARC grow disabled"],
|
||||
"need": [4, 1024, "ARC reclaim need"],
|
||||
"free": [4, 1024, "ARC free memory"],
|
||||
"avail": [5, 1024, "ARC available memory"],
|
||||
"waste": [5, 1024, "Wasted memory due to round up to pagesize"],
|
||||
"l2bytes": [7, 1024, "bytes read per second from the L2ARC"],
|
||||
}
|
||||
|
||||
v = {}
|
||||
hdr = ["time", "read", "miss", "miss%", "dmis", "dm%", "pmis", "pm%", "mmis",
|
||||
"mm%", "size", "c", "avail"]
|
||||
"mm%", "arcsz", "c"]
|
||||
xhdr = ["time", "mfu", "mru", "mfug", "mrug", "eskip", "mtxmis", "dread",
|
||||
"pread", "read"]
|
||||
sint = 1 # Default interval is 1 second
|
||||
@@ -134,56 +106,12 @@ opfile = None
|
||||
sep = " " # Default separator is 2 spaces
|
||||
version = "0.4"
|
||||
l2exist = False
|
||||
cmd = ("Usage: arcstat [-havxp] [-f fields] [-o file] [-s string] [interval "
|
||||
cmd = ("Usage: arcstat.py [-hvx] [-f fields] [-o file] [-s string] [interval "
|
||||
"[count]]\n")
|
||||
cur = {}
|
||||
d = {}
|
||||
out = None
|
||||
kstat = None
|
||||
pretty_print = True
|
||||
|
||||
|
||||
if sys.platform.startswith('freebsd'):
|
||||
# Requires py-sysctl on FreeBSD
|
||||
import sysctl
|
||||
|
||||
def kstat_update():
|
||||
global kstat
|
||||
|
||||
k = [ctl for ctl in sysctl.filter('kstat.zfs.misc.arcstats')
|
||||
if ctl.type != sysctl.CTLTYPE_NODE]
|
||||
|
||||
if not k:
|
||||
sys.exit(1)
|
||||
|
||||
kstat = {}
|
||||
|
||||
for s in k:
|
||||
if not s:
|
||||
continue
|
||||
|
||||
name, value = s.name, s.value
|
||||
# Trims 'kstat.zfs.misc.arcstats' from the name
|
||||
kstat[name[24:]] = int(value)
|
||||
|
||||
elif sys.platform.startswith('linux'):
|
||||
def kstat_update():
|
||||
global kstat
|
||||
|
||||
k = [line.strip() for line in open('/proc/spl/kstat/zfs/arcstats')]
|
||||
|
||||
if not k:
|
||||
sys.exit(1)
|
||||
|
||||
del k[0:2]
|
||||
kstat = {}
|
||||
|
||||
for s in k:
|
||||
if not s:
|
||||
continue
|
||||
|
||||
name, unused, value = s.split()
|
||||
kstat[name] = int(value)
|
||||
|
||||
|
||||
def detailed_usage():
|
||||
@@ -199,7 +127,6 @@ def detailed_usage():
|
||||
def usage():
|
||||
sys.stderr.write("%s\n" % cmd)
|
||||
sys.stderr.write("\t -h : Print this help message\n")
|
||||
sys.stderr.write("\t -a : Print all possible stats\n")
|
||||
sys.stderr.write("\t -v : List all possible field headers and definitions"
|
||||
"\n")
|
||||
sys.stderr.write("\t -x : Print extended stats\n")
|
||||
@@ -207,17 +134,35 @@ def usage():
|
||||
sys.stderr.write("\t -o : Redirect output to the specified file\n")
|
||||
sys.stderr.write("\t -s : Override default field separator with custom "
|
||||
"character or string\n")
|
||||
sys.stderr.write("\t -p : Disable auto-scaling of numerical fields\n")
|
||||
sys.stderr.write("\nExamples:\n")
|
||||
sys.stderr.write("\tarcstat -o /tmp/a.log 2 10\n")
|
||||
sys.stderr.write("\tarcstat -s \",\" -o /tmp/a.log 2 10\n")
|
||||
sys.stderr.write("\tarcstat -v\n")
|
||||
sys.stderr.write("\tarcstat -f time,hit%,dh%,ph%,mh% 1\n")
|
||||
sys.stderr.write("\tarcstat.py -o /tmp/a.log 2 10\n")
|
||||
sys.stderr.write("\tarcstat.py -s \",\" -o /tmp/a.log 2 10\n")
|
||||
sys.stderr.write("\tarcstat.py -v\n")
|
||||
sys.stderr.write("\tarcstat.py -f time,hit%,dh%,ph%,mh% 1\n")
|
||||
sys.stderr.write("\n")
|
||||
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def kstat_update():
|
||||
global kstat
|
||||
|
||||
k = [line.strip() for line in open('/proc/spl/kstat/zfs/arcstats')]
|
||||
|
||||
if not k:
|
||||
sys.exit(1)
|
||||
|
||||
del k[0:2]
|
||||
kstat = {}
|
||||
|
||||
for s in k:
|
||||
if not s:
|
||||
continue
|
||||
|
||||
name, unused, value = s.split()
|
||||
kstat[name] = Decimal(value)
|
||||
|
||||
|
||||
def snap_stats():
|
||||
global cur
|
||||
global kstat
|
||||
@@ -248,7 +193,7 @@ def prettynum(sz, scale, num=0):
|
||||
elif 0 < num < 1:
|
||||
num = 0
|
||||
|
||||
while abs(num) > scale and index < 5:
|
||||
while num > scale and index < 5:
|
||||
save = num
|
||||
num = num / scale
|
||||
index += 1
|
||||
@@ -256,7 +201,7 @@ def prettynum(sz, scale, num=0):
|
||||
if index == 0:
|
||||
return "%*d" % (sz, num)
|
||||
|
||||
if abs(save / scale) < 10:
|
||||
if (save / scale) < 10:
|
||||
return "%*.1f%s" % (sz - 1, num, suffix[index])
|
||||
else:
|
||||
return "%*d%s" % (sz - 1, num, suffix[index])
|
||||
@@ -266,14 +211,12 @@ def print_values():
|
||||
global hdr
|
||||
global sep
|
||||
global v
|
||||
global pretty_print
|
||||
|
||||
if pretty_print:
|
||||
fmt = lambda col: prettynum(cols[col][0], cols[col][1], v[col])
|
||||
else:
|
||||
fmt = lambda col: str(v[col])
|
||||
|
||||
sys.stdout.write(sep.join(fmt(col) for col in hdr))
|
||||
for col in hdr:
|
||||
sys.stdout.write("%s%s" % (
|
||||
prettynum(cols[col][0], cols[col][1], v[col]),
|
||||
sep
|
||||
))
|
||||
sys.stdout.write("\n")
|
||||
sys.stdout.flush()
|
||||
|
||||
@@ -281,14 +224,9 @@ def print_values():
|
||||
def print_header():
|
||||
global hdr
|
||||
global sep
|
||||
global pretty_print
|
||||
|
||||
if pretty_print:
|
||||
fmt = lambda col: "%*s" % (cols[col][0], col)
|
||||
else:
|
||||
fmt = lambda col: col
|
||||
|
||||
sys.stdout.write(sep.join(fmt(col) for col in hdr))
|
||||
for col in hdr:
|
||||
sys.stdout.write("%*s%s" % (cols[col][0], col, sep))
|
||||
sys.stdout.write("\n")
|
||||
|
||||
|
||||
@@ -325,10 +263,8 @@ def init():
|
||||
global sep
|
||||
global out
|
||||
global l2exist
|
||||
global pretty_print
|
||||
|
||||
desired_cols = None
|
||||
aflag = False
|
||||
xflag = False
|
||||
hflag = False
|
||||
vflag = False
|
||||
@@ -337,16 +273,14 @@ def init():
|
||||
try:
|
||||
opts, args = getopt.getopt(
|
||||
sys.argv[1:],
|
||||
"axo:hvs:f:p",
|
||||
"xo:hvs:f:",
|
||||
[
|
||||
"all",
|
||||
"extended",
|
||||
"outfile",
|
||||
"help",
|
||||
"verbose",
|
||||
"separator",
|
||||
"columns",
|
||||
"parsable"
|
||||
"columns"
|
||||
]
|
||||
)
|
||||
except getopt.error as msg:
|
||||
@@ -355,8 +289,6 @@ def init():
|
||||
opts = None
|
||||
|
||||
for opt, arg in opts:
|
||||
if opt in ('-a', '--all'):
|
||||
aflag = True
|
||||
if opt in ('-x', '--extended'):
|
||||
xflag = True
|
||||
if opt in ('-o', '--outfile'):
|
||||
@@ -372,13 +304,19 @@ def init():
|
||||
if opt in ('-f', '--columns'):
|
||||
desired_cols = arg
|
||||
i += 1
|
||||
if opt in ('-p', '--parsable'):
|
||||
pretty_print = False
|
||||
i += 1
|
||||
|
||||
argv = sys.argv[i:]
|
||||
sint = int(argv[0]) if argv else sint
|
||||
count = int(argv[1]) if len(argv) > 1 else (0 if len(argv) > 0 else 1)
|
||||
sint = Decimal(argv[0]) if argv else sint
|
||||
count = int(argv[1]) if len(argv) > 1 else count
|
||||
|
||||
if len(argv) > 1:
|
||||
sint = Decimal(argv[0])
|
||||
count = int(argv[1])
|
||||
|
||||
elif len(argv) > 0:
|
||||
sint = Decimal(argv[0])
|
||||
count = 0
|
||||
|
||||
if hflag or (xflag and desired_cols):
|
||||
usage()
|
||||
@@ -418,12 +356,6 @@ def init():
|
||||
incompat)
|
||||
usage()
|
||||
|
||||
if aflag:
|
||||
if l2exist:
|
||||
hdr = cols.keys()
|
||||
else:
|
||||
hdr = [col for col in cols.keys() if not col.startswith("l2")]
|
||||
|
||||
if opfile:
|
||||
try:
|
||||
out = open(opfile, "w")
|
||||
@@ -472,19 +404,12 @@ def calculate():
|
||||
v["mm%"] = 100 - v["mh%"] if v["mread"] > 0 else 0
|
||||
|
||||
v["arcsz"] = cur["size"]
|
||||
v["size"] = cur["size"]
|
||||
v["c"] = cur["c"]
|
||||
v["mfu"] = d["mfu_hits"] / sint
|
||||
v["mru"] = d["mru_hits"] / sint
|
||||
v["mrug"] = d["mru_ghost_hits"] / sint
|
||||
v["mfug"] = d["mfu_ghost_hits"] / sint
|
||||
v["eskip"] = d["evict_skip"] / sint
|
||||
v["el2skip"] = d["evict_l2_skip"] / sint
|
||||
v["el2cach"] = d["evict_l2_cached"] / sint
|
||||
v["el2el"] = d["evict_l2_eligible"] / sint
|
||||
v["el2mfu"] = d["evict_l2_eligible_mfu"] / sint
|
||||
v["el2mru"] = d["evict_l2_eligible_mru"] / sint
|
||||
v["el2inel"] = d["evict_l2_ineligible"] / sint
|
||||
v["mtxmis"] = d["mutex_miss"] / sint
|
||||
|
||||
if l2exist:
|
||||
@@ -498,23 +423,6 @@ def calculate():
|
||||
v["l2size"] = cur["l2_size"]
|
||||
v["l2bytes"] = d["l2_read_bytes"] / sint
|
||||
|
||||
v["l2pref"] = cur["l2_prefetch_asize"]
|
||||
v["l2mfu"] = cur["l2_mfu_asize"]
|
||||
v["l2mru"] = cur["l2_mru_asize"]
|
||||
v["l2data"] = cur["l2_bufc_data_asize"]
|
||||
v["l2meta"] = cur["l2_bufc_metadata_asize"]
|
||||
v["l2pref%"] = 100 * v["l2pref"] / v["l2asize"]
|
||||
v["l2mfu%"] = 100 * v["l2mfu"] / v["l2asize"]
|
||||
v["l2mru%"] = 100 * v["l2mru"] / v["l2asize"]
|
||||
v["l2data%"] = 100 * v["l2data"] / v["l2asize"]
|
||||
v["l2meta%"] = 100 * v["l2meta"] / v["l2asize"]
|
||||
|
||||
v["grow"] = 0 if cur["arc_no_grow"] else 1
|
||||
v["need"] = cur["arc_need_free"]
|
||||
v["free"] = cur["memory_free_bytes"]
|
||||
v["avail"] = cur["memory_available_bytes"]
|
||||
v["waste"] = cur["abd_chunk_waste_size"]
|
||||
|
||||
|
||||
def main():
|
||||
global sint
|
||||
@@ -1 +0,0 @@
|
||||
dbufstat
|
||||
@@ -1,5 +1 @@
|
||||
include $(top_srcdir)/config/Substfiles.am
|
||||
|
||||
bin_SCRIPTS = dbufstat
|
||||
|
||||
SUBSTFILES += $(bin_SCRIPTS)
|
||||
dist_bin_SCRIPTS = dbufstat.py
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env @PYTHON_SHEBANG@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Print out statistics for all cached dmu buffers. This information
|
||||
# is available through the dbufs kstat and may be post-processed as
|
||||
@@ -27,17 +27,14 @@
|
||||
# Copyright (C) 2013 Lawrence Livermore National Security, LLC.
|
||||
# Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
|
||||
#
|
||||
# This script must remain compatible with and Python 3.6+.
|
||||
#
|
||||
|
||||
import sys
|
||||
import getopt
|
||||
import errno
|
||||
import re
|
||||
|
||||
bhdr = ["pool", "objset", "object", "level", "blkid", "offset", "dbsize"]
|
||||
bxhdr = ["pool", "objset", "object", "level", "blkid", "offset", "dbsize",
|
||||
"meta", "state", "dbholds", "dbc", "list", "atype", "flags",
|
||||
"meta", "state", "dbholds", "list", "atype", "flags",
|
||||
"count", "asize", "access", "mru", "gmru", "mfu", "gmfu", "l2",
|
||||
"l2_dattr", "l2_asize", "l2_comp", "aholds", "dtype", "btype",
|
||||
"data_bs", "meta_bs", "bsize", "lvls", "dholds", "blocks", "dsize"]
|
||||
@@ -48,7 +45,7 @@ dxhdr = ["pool", "objset", "object", "dtype", "btype", "data_bs", "meta_bs",
|
||||
"bsize", "lvls", "dholds", "blocks", "dsize", "cached", "direct",
|
||||
"indirect", "bonus", "spill"]
|
||||
dincompat = ["level", "blkid", "offset", "dbsize", "meta", "state", "dbholds",
|
||||
"dbc", "list", "atype", "flags", "count", "asize", "access",
|
||||
"list", "atype", "flags", "count", "asize", "access",
|
||||
"mru", "gmru", "mfu", "gmfu", "l2", "l2_dattr", "l2_asize",
|
||||
"l2_comp", "aholds"]
|
||||
|
||||
@@ -56,7 +53,7 @@ thdr = ["pool", "objset", "dtype", "cached"]
|
||||
txhdr = ["pool", "objset", "dtype", "cached", "direct", "indirect",
|
||||
"bonus", "spill"]
|
||||
tincompat = ["object", "level", "blkid", "offset", "dbsize", "meta", "state",
|
||||
"dbc", "dbholds", "list", "atype", "flags", "count", "asize",
|
||||
"dbholds", "list", "atype", "flags", "count", "asize",
|
||||
"access", "mru", "gmru", "mfu", "gmfu", "l2", "l2_dattr",
|
||||
"l2_asize", "l2_comp", "aholds", "btype", "data_bs", "meta_bs",
|
||||
"bsize", "lvls", "dholds", "blocks", "dsize"]
|
||||
@@ -73,10 +70,9 @@ cols = {
|
||||
"meta": [4, -1, "is this buffer metadata?"],
|
||||
"state": [5, -1, "state of buffer (read, cached, etc)"],
|
||||
"dbholds": [7, 1000, "number of holds on buffer"],
|
||||
"dbc": [3, -1, "in dbuf cache"],
|
||||
"list": [4, -1, "which ARC list contains this buffer"],
|
||||
"atype": [7, -1, "ARC header type (data or metadata)"],
|
||||
"flags": [9, -1, "ARC read flags"],
|
||||
"flags": [8, -1, "ARC read flags"],
|
||||
"count": [5, -1, "ARC data count"],
|
||||
"asize": [7, 1024, "size of this ARC buffer"],
|
||||
"access": [10, -1, "time this ARC buffer was last accessed"],
|
||||
@@ -108,30 +104,15 @@ cols = {
|
||||
hdr = None
|
||||
xhdr = None
|
||||
sep = " " # Default separator is 2 spaces
|
||||
cmd = ("Usage: dbufstat [-bdhnrtvx] [-i file] [-f fields] [-o file] "
|
||||
"[-s string] [-F filter]\n")
|
||||
cmd = ("Usage: dbufstat.py [-bdhrtvx] [-i file] [-f fields] [-o file] "
|
||||
"[-s string]\n")
|
||||
raw = 0
|
||||
|
||||
|
||||
if sys.platform.startswith("freebsd"):
|
||||
import io
|
||||
# Requires py-sysctl on FreeBSD
|
||||
import sysctl
|
||||
|
||||
def default_ifile():
|
||||
dbufs = sysctl.filter("kstat.zfs.misc.dbufs")[0].value
|
||||
sys.stdin = io.StringIO(dbufs)
|
||||
return "-"
|
||||
|
||||
elif sys.platform.startswith("linux"):
|
||||
def default_ifile():
|
||||
return "/proc/spl/kstat/zfs/dbufs"
|
||||
|
||||
|
||||
def print_incompat_helper(incompat):
|
||||
cnt = 0
|
||||
for key in sorted(incompat):
|
||||
if cnt == 0:
|
||||
if cnt is 0:
|
||||
sys.stderr.write("\t")
|
||||
elif cnt > 8:
|
||||
sys.stderr.write(",\n\t")
|
||||
@@ -170,7 +151,6 @@ def usage():
|
||||
sys.stderr.write("\t -b : Print table of information for each dbuf\n")
|
||||
sys.stderr.write("\t -d : Print table of information for each dnode\n")
|
||||
sys.stderr.write("\t -h : Print this help message\n")
|
||||
sys.stderr.write("\t -n : Exclude header from output\n")
|
||||
sys.stderr.write("\t -r : Print raw values\n")
|
||||
sys.stderr.write("\t -t : Print table of information for each dnode type"
|
||||
"\n")
|
||||
@@ -182,13 +162,11 @@ def usage():
|
||||
sys.stderr.write("\t -o : Redirect output to the specified file\n")
|
||||
sys.stderr.write("\t -s : Override default field separator with custom "
|
||||
"character or string\n")
|
||||
sys.stderr.write("\t -F : Filter output by value or regex\n")
|
||||
sys.stderr.write("\nExamples:\n")
|
||||
sys.stderr.write("\tdbufstat -d -o /tmp/d.log\n")
|
||||
sys.stderr.write("\tdbufstat -t -s \",\" -o /tmp/t.log\n")
|
||||
sys.stderr.write("\tdbufstat -v\n")
|
||||
sys.stderr.write("\tdbufstat -d -f pool,object,objset,dsize,cached\n")
|
||||
sys.stderr.write("\tdbufstat -bx -F dbc=1,objset=54,pool=testpool\n")
|
||||
sys.stderr.write("\tdbufstat.py -d -o /tmp/d.log\n")
|
||||
sys.stderr.write("\tdbufstat.py -t -s \",\" -o /tmp/t.log\n")
|
||||
sys.stderr.write("\tdbufstat.py -v\n")
|
||||
sys.stderr.write("\tdbufstat.py -d -f pool,object,objset,dsize,cached\n")
|
||||
sys.stderr.write("\n")
|
||||
|
||||
sys.exit(1)
|
||||
@@ -250,8 +228,7 @@ def print_header():
|
||||
|
||||
|
||||
def get_typestring(t):
|
||||
ot_strings = [
|
||||
"DMU_OT_NONE",
|
||||
type_strings = ["DMU_OT_NONE",
|
||||
# general:
|
||||
"DMU_OT_OBJECT_DIRECTORY",
|
||||
"DMU_OT_OBJECT_ARRAY",
|
||||
@@ -314,39 +291,15 @@ def get_typestring(t):
|
||||
"DMU_OT_DEADLIST_HDR",
|
||||
"DMU_OT_DSL_CLONES",
|
||||
"DMU_OT_BPOBJ_SUBOBJ"]
|
||||
otn_strings = {
|
||||
0x80: "DMU_OTN_UINT8_DATA",
|
||||
0xc0: "DMU_OTN_UINT8_METADATA",
|
||||
0x81: "DMU_OTN_UINT16_DATA",
|
||||
0xc1: "DMU_OTN_UINT16_METADATA",
|
||||
0x82: "DMU_OTN_UINT32_DATA",
|
||||
0xc2: "DMU_OTN_UINT32_METADATA",
|
||||
0x83: "DMU_OTN_UINT64_DATA",
|
||||
0xc3: "DMU_OTN_UINT64_METADATA",
|
||||
0x84: "DMU_OTN_ZAP_DATA",
|
||||
0xc4: "DMU_OTN_ZAP_METADATA",
|
||||
0xa0: "DMU_OTN_UINT8_ENC_DATA",
|
||||
0xe0: "DMU_OTN_UINT8_ENC_METADATA",
|
||||
0xa1: "DMU_OTN_UINT16_ENC_DATA",
|
||||
0xe1: "DMU_OTN_UINT16_ENC_METADATA",
|
||||
0xa2: "DMU_OTN_UINT32_ENC_DATA",
|
||||
0xe2: "DMU_OTN_UINT32_ENC_METADATA",
|
||||
0xa3: "DMU_OTN_UINT64_ENC_DATA",
|
||||
0xe3: "DMU_OTN_UINT64_ENC_METADATA",
|
||||
0xa4: "DMU_OTN_ZAP_ENC_DATA",
|
||||
0xe4: "DMU_OTN_ZAP_ENC_METADATA"}
|
||||
|
||||
# If "-rr" option is used, don't convert to string representation
|
||||
if raw > 1:
|
||||
return "%i" % t
|
||||
|
||||
try:
|
||||
if t < len(ot_strings):
|
||||
return ot_strings[t]
|
||||
else:
|
||||
return otn_strings[t]
|
||||
except (IndexError, KeyError):
|
||||
return "(UNKNOWN)"
|
||||
return type_strings[t]
|
||||
except IndexError:
|
||||
return "%i" % t
|
||||
|
||||
|
||||
def get_compstring(c):
|
||||
@@ -358,7 +311,7 @@ def get_compstring(c):
|
||||
"ZIO_COMPRESS_GZIP_6", "ZIO_COMPRESS_GZIP_7",
|
||||
"ZIO_COMPRESS_GZIP_8", "ZIO_COMPRESS_GZIP_9",
|
||||
"ZIO_COMPRESS_ZLE", "ZIO_COMPRESS_LZ4",
|
||||
"ZIO_COMPRESS_ZSTD", "ZIO_COMPRESS_FUNCTION"]
|
||||
"ZIO_COMPRESS_FUNCTION"]
|
||||
|
||||
# If "-rr" option is used, don't convert to string representation
|
||||
if raw > 1:
|
||||
@@ -431,32 +384,12 @@ def update_dict(d, k, line, labels):
|
||||
return d
|
||||
|
||||
|
||||
def skip_line(vals, filters):
|
||||
'''
|
||||
Determines if a line should be skipped during printing
|
||||
based on a set of filters
|
||||
'''
|
||||
if len(filters) == 0:
|
||||
return False
|
||||
|
||||
for key in vals:
|
||||
if key in filters:
|
||||
val = prettynum(cols[key][0], cols[key][1], vals[key]).strip()
|
||||
# we want a full match here
|
||||
if re.match("(?:" + filters[key] + r")\Z", val) is None:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def print_dict(d, filters, noheader):
|
||||
if not noheader:
|
||||
print_header()
|
||||
def print_dict(d):
|
||||
print_header()
|
||||
for pool in list(d.keys()):
|
||||
for objset in list(d[pool].keys()):
|
||||
for v in list(d[pool][objset].values()):
|
||||
if not skip_line(v, filters):
|
||||
print_values(v)
|
||||
print_values(v)
|
||||
|
||||
|
||||
def dnodes_build_dict(filehandle):
|
||||
@@ -497,7 +430,7 @@ def types_build_dict(filehandle):
|
||||
return types
|
||||
|
||||
|
||||
def buffers_print_all(filehandle, filters, noheader):
|
||||
def buffers_print_all(filehandle):
|
||||
labels = dict()
|
||||
|
||||
# First 3 lines are header information, skip the first two
|
||||
@@ -508,14 +441,11 @@ def buffers_print_all(filehandle, filters, noheader):
|
||||
for i, v in enumerate(next(filehandle).split()):
|
||||
labels[v] = i
|
||||
|
||||
if not noheader:
|
||||
print_header()
|
||||
print_header()
|
||||
|
||||
# The rest of the file is buffer information
|
||||
for line in filehandle:
|
||||
vals = parse_line(line.split(), labels)
|
||||
if not skip_line(vals, filters):
|
||||
print_values(vals)
|
||||
print_values(parse_line(line.split(), labels))
|
||||
|
||||
|
||||
def main():
|
||||
@@ -532,13 +462,11 @@ def main():
|
||||
tflag = False
|
||||
vflag = False
|
||||
xflag = False
|
||||
nflag = False
|
||||
filters = dict()
|
||||
|
||||
try:
|
||||
opts, args = getopt.getopt(
|
||||
sys.argv[1:],
|
||||
"bdf:hi:o:rs:tvxF:n",
|
||||
"bdf:hi:o:rs:tvx",
|
||||
[
|
||||
"buffers",
|
||||
"dnodes",
|
||||
@@ -549,8 +477,7 @@ def main():
|
||||
"separator",
|
||||
"types",
|
||||
"verbose",
|
||||
"extended",
|
||||
"filter"
|
||||
"extended"
|
||||
]
|
||||
)
|
||||
except getopt.error:
|
||||
@@ -580,35 +507,6 @@ def main():
|
||||
vflag = True
|
||||
if opt in ('-x', '--extended'):
|
||||
xflag = True
|
||||
if opt in ('-n', '--noheader'):
|
||||
nflag = True
|
||||
if opt in ('-F', '--filter'):
|
||||
fils = [x.strip() for x in arg.split(",")]
|
||||
|
||||
for fil in fils:
|
||||
f = [x.strip() for x in fil.split("=")]
|
||||
|
||||
if len(f) != 2:
|
||||
sys.stderr.write("Invalid filter '%s'.\n" % fil)
|
||||
sys.exit(1)
|
||||
|
||||
if f[0] not in cols:
|
||||
sys.stderr.write("Invalid field '%s' in filter.\n" % f[0])
|
||||
sys.exit(1)
|
||||
|
||||
if f[0] in filters:
|
||||
sys.stderr.write("Field '%s' specified multiple times in "
|
||||
"filter.\n" % f[0])
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
re.compile("(?:" + f[1] + r")\Z")
|
||||
except re.error:
|
||||
sys.stderr.write("Invalid regex for field '%s' in "
|
||||
"filter.\n" % f[0])
|
||||
sys.exit(1)
|
||||
|
||||
filters[f[0]] = f[1]
|
||||
|
||||
if hflag or (xflag and desired_cols):
|
||||
usage()
|
||||
@@ -660,9 +558,9 @@ def main():
|
||||
sys.exit(1)
|
||||
|
||||
if not ifile:
|
||||
ifile = default_ifile()
|
||||
ifile = '/proc/spl/kstat/zfs/dbufs'
|
||||
|
||||
if ifile != "-":
|
||||
if ifile is not "-":
|
||||
try:
|
||||
tmp = open(ifile, "r")
|
||||
sys.stdin = tmp
|
||||
@@ -671,13 +569,13 @@ def main():
|
||||
sys.exit(1)
|
||||
|
||||
if bflag:
|
||||
buffers_print_all(sys.stdin, filters, nflag)
|
||||
buffers_print_all(sys.stdin)
|
||||
|
||||
if dflag:
|
||||
print_dict(dnodes_build_dict(sys.stdin), filters, nflag)
|
||||
print_dict(dnodes_build_dict(sys.stdin))
|
||||
|
||||
if tflag:
|
||||
print_dict(types_build_dict(sys.stdin), filters, nflag)
|
||||
print_dict(types_build_dict(sys.stdin))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
@@ -1 +0,0 @@
|
||||
/fsck.zfs
|
||||
@@ -1,6 +1 @@
|
||||
include $(top_srcdir)/config/Substfiles.am
|
||||
include $(top_srcdir)/config/Shellcheck.am
|
||||
|
||||
dist_sbin_SCRIPTS = fsck.zfs
|
||||
|
||||
SUBSTFILES += $(dist_sbin_SCRIPTS)
|
||||
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# fsck.zfs: A fsck helper to accomidate distributions that expect
|
||||
# to be able to execute a fsck on all filesystem types. Currently
|
||||
# this script does nothing but it could be extended to act as a
|
||||
# compatibility wrapper for 'zpool scrub'.
|
||||
#
|
||||
|
||||
exit 0
|
||||
@@ -1,44 +0,0 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# fsck.zfs: A fsck helper to accommodate distributions that expect
|
||||
# to be able to execute a fsck on all filesystem types.
|
||||
#
|
||||
# This script simply bubbles up some already-known-about errors,
|
||||
# see fsck.zfs(8)
|
||||
#
|
||||
|
||||
if [ "$#" = "0" ]; then
|
||||
echo "Usage: $0 [options] dataset…" >&2
|
||||
exit 16
|
||||
fi
|
||||
|
||||
ret=0
|
||||
for dataset in "$@"; do
|
||||
case "$dataset" in
|
||||
-*)
|
||||
continue
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
|
||||
pool="${dataset%%/*}"
|
||||
|
||||
case "$(@sbindir@/zpool list -Ho health "$pool")" in
|
||||
DEGRADED)
|
||||
ret=$(( ret | 4 ))
|
||||
;;
|
||||
FAULTED)
|
||||
awk '!/^([[:space:]]*#.*)?$/ && $1 == "'"$dataset"'" && $3 == "zfs" {exit 1}' /etc/fstab || \
|
||||
ret=$(( ret | 8 ))
|
||||
;;
|
||||
"")
|
||||
# Pool not found, error printed by zpool(8)
|
||||
ret=$(( ret | 8 ))
|
||||
;;
|
||||
*)
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
exit "$ret"
|
||||
@@ -1,5 +1,9 @@
|
||||
include $(top_srcdir)/config/Rules.am
|
||||
|
||||
DEFAULT_INCLUDES += \
|
||||
-I$(top_srcdir)/include \
|
||||
-I$(top_srcdir)/lib/libspl/include
|
||||
|
||||
#
|
||||
# Ignore the prefix for the mount helper. It must be installed in /sbin/
|
||||
# because this path is hardcoded in the mount(8) for security reasons.
|
||||
@@ -13,10 +17,8 @@ mount_zfs_SOURCES = \
|
||||
mount_zfs.c
|
||||
|
||||
mount_zfs_LDADD = \
|
||||
$(abs_top_builddir)/lib/libzfs/libzfs.la \
|
||||
$(abs_top_builddir)/lib/libzfs_core/libzfs_core.la \
|
||||
$(abs_top_builddir)/lib/libnvpair/libnvpair.la
|
||||
|
||||
mount_zfs_LDADD += $(LTLIBINTL)
|
||||
|
||||
include $(top_srcdir)/config/CppCheck.am
|
||||
$(top_builddir)/lib/libnvpair/libnvpair.la \
|
||||
$(top_builddir)/lib/libuutil/libuutil.la \
|
||||
$(top_builddir)/lib/libzpool/libzpool.la \
|
||||
$(top_builddir)/lib/libzfs/libzfs.la \
|
||||
$(top_builddir)/lib/libzfs_core/libzfs_core.la
|
||||
|
||||
+310
-81
@@ -31,57 +31,256 @@
|
||||
#include <sys/mntent.h>
|
||||
#include <sys/stat.h>
|
||||
#include <libzfs.h>
|
||||
#include <libzutil.h>
|
||||
#include <locale.h>
|
||||
#include <getopt.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define ZS_COMMENT 0x00000000 /* comment */
|
||||
#define ZS_ZFSUTIL 0x00000001 /* caller is zfs(8) */
|
||||
|
||||
libzfs_handle_t *g_zfs;
|
||||
|
||||
typedef struct option_map {
|
||||
const char *name;
|
||||
unsigned long mntmask;
|
||||
unsigned long zfsmask;
|
||||
} option_map_t;
|
||||
|
||||
static const option_map_t option_map[] = {
|
||||
/* Canonicalized filesystem independent options from mount(8) */
|
||||
{ MNTOPT_NOAUTO, MS_COMMENT, ZS_COMMENT },
|
||||
{ MNTOPT_DEFAULTS, MS_COMMENT, ZS_COMMENT },
|
||||
{ MNTOPT_NODEVICES, MS_NODEV, ZS_COMMENT },
|
||||
{ MNTOPT_DIRSYNC, MS_DIRSYNC, ZS_COMMENT },
|
||||
{ MNTOPT_NOEXEC, MS_NOEXEC, ZS_COMMENT },
|
||||
{ MNTOPT_GROUP, MS_GROUP, ZS_COMMENT },
|
||||
{ MNTOPT_NETDEV, MS_COMMENT, ZS_COMMENT },
|
||||
{ MNTOPT_NOFAIL, MS_COMMENT, ZS_COMMENT },
|
||||
{ MNTOPT_NOSUID, MS_NOSUID, ZS_COMMENT },
|
||||
{ MNTOPT_OWNER, MS_OWNER, ZS_COMMENT },
|
||||
{ MNTOPT_REMOUNT, MS_REMOUNT, ZS_COMMENT },
|
||||
{ MNTOPT_RO, MS_RDONLY, ZS_COMMENT },
|
||||
{ MNTOPT_RW, MS_COMMENT, ZS_COMMENT },
|
||||
{ MNTOPT_SYNC, MS_SYNCHRONOUS, ZS_COMMENT },
|
||||
{ MNTOPT_USER, MS_USERS, ZS_COMMENT },
|
||||
{ MNTOPT_USERS, MS_USERS, ZS_COMMENT },
|
||||
/* acl flags passed with util-linux-2.24 mount command */
|
||||
{ MNTOPT_ACL, MS_POSIXACL, ZS_COMMENT },
|
||||
{ MNTOPT_NOACL, MS_COMMENT, ZS_COMMENT },
|
||||
{ MNTOPT_POSIXACL, MS_POSIXACL, ZS_COMMENT },
|
||||
#ifdef MS_NOATIME
|
||||
{ MNTOPT_NOATIME, MS_NOATIME, ZS_COMMENT },
|
||||
#endif
|
||||
#ifdef MS_NODIRATIME
|
||||
{ MNTOPT_NODIRATIME, MS_NODIRATIME, ZS_COMMENT },
|
||||
#endif
|
||||
#ifdef MS_RELATIME
|
||||
{ MNTOPT_RELATIME, MS_RELATIME, ZS_COMMENT },
|
||||
#endif
|
||||
#ifdef MS_STRICTATIME
|
||||
{ MNTOPT_STRICTATIME, MS_STRICTATIME, ZS_COMMENT },
|
||||
#endif
|
||||
#ifdef MS_LAZYTIME
|
||||
{ MNTOPT_LAZYTIME, MS_LAZYTIME, ZS_COMMENT },
|
||||
#endif
|
||||
{ MNTOPT_CONTEXT, MS_COMMENT, ZS_COMMENT },
|
||||
{ MNTOPT_FSCONTEXT, MS_COMMENT, ZS_COMMENT },
|
||||
{ MNTOPT_DEFCONTEXT, MS_COMMENT, ZS_COMMENT },
|
||||
{ MNTOPT_ROOTCONTEXT, MS_COMMENT, ZS_COMMENT },
|
||||
#ifdef MS_I_VERSION
|
||||
{ MNTOPT_IVERSION, MS_I_VERSION, ZS_COMMENT },
|
||||
#endif
|
||||
#ifdef MS_MANDLOCK
|
||||
{ MNTOPT_NBMAND, MS_MANDLOCK, ZS_COMMENT },
|
||||
#endif
|
||||
/* Valid options not found in mount(8) */
|
||||
{ MNTOPT_BIND, MS_BIND, ZS_COMMENT },
|
||||
#ifdef MS_REC
|
||||
{ MNTOPT_RBIND, MS_BIND|MS_REC, ZS_COMMENT },
|
||||
#endif
|
||||
{ MNTOPT_COMMENT, MS_COMMENT, ZS_COMMENT },
|
||||
#ifdef MS_NOSUB
|
||||
{ MNTOPT_NOSUB, MS_NOSUB, ZS_COMMENT },
|
||||
#endif
|
||||
#ifdef MS_SILENT
|
||||
{ MNTOPT_QUIET, MS_SILENT, ZS_COMMENT },
|
||||
#endif
|
||||
/* Custom zfs options */
|
||||
{ MNTOPT_XATTR, MS_COMMENT, ZS_COMMENT },
|
||||
{ MNTOPT_NOXATTR, MS_COMMENT, ZS_COMMENT },
|
||||
{ MNTOPT_ZFSUTIL, MS_COMMENT, ZS_ZFSUTIL },
|
||||
{ NULL, 0, 0 } };
|
||||
|
||||
/*
|
||||
* Opportunistically convert a target string into a pool name. If the
|
||||
* string does not represent a block device with a valid zfs label
|
||||
* then it is passed through without modification.
|
||||
* Break the mount option in to a name/value pair. The name is
|
||||
* validated against the option map and mount flags set accordingly.
|
||||
*/
|
||||
static void
|
||||
parse_dataset(const char *target, char **dataset)
|
||||
static int
|
||||
parse_option(char *mntopt, unsigned long *mntflags,
|
||||
unsigned long *zfsflags, int sloppy)
|
||||
{
|
||||
const option_map_t *opt;
|
||||
char *ptr, *name, *value = NULL;
|
||||
int error = 0;
|
||||
|
||||
name = strdup(mntopt);
|
||||
if (name == NULL)
|
||||
return (ENOMEM);
|
||||
|
||||
for (ptr = name; ptr && *ptr; ptr++) {
|
||||
if (*ptr == '=') {
|
||||
*ptr = '\0';
|
||||
value = ptr+1;
|
||||
VERIFY3P(value, !=, NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (opt = option_map; opt->name != NULL; opt++) {
|
||||
if (strncmp(name, opt->name, strlen(name)) == 0) {
|
||||
*mntflags |= opt->mntmask;
|
||||
*zfsflags |= opt->zfsmask;
|
||||
error = 0;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (!sloppy)
|
||||
error = ENOENT;
|
||||
out:
|
||||
/* If required further process on the value may be done here */
|
||||
free(name);
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Translate the mount option string in to MS_* mount flags for the
|
||||
* kernel vfs. When sloppy is non-zero unknown options will be ignored
|
||||
* otherwise they are considered fatal are copied in to badopt.
|
||||
*/
|
||||
static int
|
||||
parse_options(char *mntopts, unsigned long *mntflags, unsigned long *zfsflags,
|
||||
int sloppy, char *badopt, char *mtabopt)
|
||||
{
|
||||
int error = 0, quote = 0, flag = 0, count = 0;
|
||||
char *ptr, *opt, *opts;
|
||||
|
||||
opts = strdup(mntopts);
|
||||
if (opts == NULL)
|
||||
return (ENOMEM);
|
||||
|
||||
*mntflags = 0;
|
||||
opt = NULL;
|
||||
|
||||
/*
|
||||
* Prior to util-linux 2.36.2, if a file or directory in the
|
||||
* current working directory was named 'dataset' then mount(8)
|
||||
* would prepend the current working directory to the dataset.
|
||||
* Check for it and strip the prepended path when it is added.
|
||||
* Scan through all mount options which must be comma delimited.
|
||||
* We must be careful to notice regions which are double quoted
|
||||
* and skip commas in these regions. Each option is then checked
|
||||
* to determine if it is a known option.
|
||||
*/
|
||||
for (ptr = opts; ptr && !flag; ptr++) {
|
||||
if (opt == NULL)
|
||||
opt = ptr;
|
||||
|
||||
if (*ptr == '"')
|
||||
quote = !quote;
|
||||
|
||||
if (quote)
|
||||
continue;
|
||||
|
||||
if (*ptr == '\0')
|
||||
flag = 1;
|
||||
|
||||
if ((*ptr == ',') || (*ptr == '\0')) {
|
||||
*ptr = '\0';
|
||||
|
||||
error = parse_option(opt, mntflags, zfsflags, sloppy);
|
||||
if (error) {
|
||||
strcpy(badopt, opt);
|
||||
goto out;
|
||||
|
||||
}
|
||||
|
||||
if (!(*mntflags & MS_REMOUNT) &&
|
||||
!(*zfsflags & ZS_ZFSUTIL)) {
|
||||
if (count > 0)
|
||||
strlcat(mtabopt, ",", MNT_LINE_MAX);
|
||||
|
||||
strlcat(mtabopt, opt, MNT_LINE_MAX);
|
||||
count++;
|
||||
}
|
||||
|
||||
opt = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
free(opts);
|
||||
return (error);
|
||||
}
|
||||
|
||||
/*
|
||||
* Return the pool/dataset to mount given the name passed to mount. This
|
||||
* is expected to be of the form pool/dataset, however may also refer to
|
||||
* a block device if that device contains a valid zfs label.
|
||||
*/
|
||||
static char *
|
||||
parse_dataset(char *dataset)
|
||||
{
|
||||
char cwd[PATH_MAX];
|
||||
if (getcwd(cwd, PATH_MAX) == NULL) {
|
||||
perror("getcwd");
|
||||
return;
|
||||
struct stat64 statbuf;
|
||||
int error;
|
||||
int len;
|
||||
|
||||
/*
|
||||
* We expect a pool/dataset to be provided, however if we're
|
||||
* given a device which is a member of a zpool we attempt to
|
||||
* extract the pool name stored in the label. Given the pool
|
||||
* name we can mount the root dataset.
|
||||
*/
|
||||
error = stat64(dataset, &statbuf);
|
||||
if (error == 0) {
|
||||
nvlist_t *config;
|
||||
char *name;
|
||||
int fd;
|
||||
|
||||
fd = open(dataset, O_RDONLY);
|
||||
if (fd < 0)
|
||||
goto out;
|
||||
|
||||
error = zpool_read_label(fd, &config, NULL);
|
||||
(void) close(fd);
|
||||
if (error)
|
||||
goto out;
|
||||
|
||||
error = nvlist_lookup_string(config,
|
||||
ZPOOL_CONFIG_POOL_NAME, &name);
|
||||
if (error) {
|
||||
nvlist_free(config);
|
||||
} else {
|
||||
dataset = strdup(name);
|
||||
nvlist_free(config);
|
||||
return (dataset);
|
||||
}
|
||||
}
|
||||
int len = strlen(cwd);
|
||||
if (strncmp(cwd, target, len) == 0)
|
||||
target += len;
|
||||
out:
|
||||
/*
|
||||
* If a file or directory in your current working directory is
|
||||
* named 'dataset' then mount(8) will prepend your current working
|
||||
* directory to the dataset. There is no way to prevent this
|
||||
* behavior so we simply check for it and strip the prepended
|
||||
* patch when it is added.
|
||||
*/
|
||||
if (getcwd(cwd, PATH_MAX) == NULL)
|
||||
return (dataset);
|
||||
|
||||
/* Assume pool/dataset is more likely */
|
||||
strlcpy(*dataset, target, PATH_MAX);
|
||||
len = strlen(cwd);
|
||||
|
||||
int fd = open(target, O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return;
|
||||
/* Do not add one when cwd already ends in a trailing '/' */
|
||||
if (strncmp(cwd, dataset, len) == 0)
|
||||
return (dataset + len + (cwd[len-1] != '/'));
|
||||
|
||||
nvlist_t *cfg = NULL;
|
||||
if (zpool_read_label(fd, &cfg, NULL) == 0) {
|
||||
char *nm = NULL;
|
||||
if (!nvlist_lookup_string(cfg, ZPOOL_CONFIG_POOL_NAME, &nm))
|
||||
strlcpy(*dataset, nm, PATH_MAX);
|
||||
nvlist_free(cfg);
|
||||
}
|
||||
|
||||
if (close(fd))
|
||||
perror("close");
|
||||
return (dataset);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -125,8 +324,8 @@ mtab_update(char *dataset, char *mntpoint, char *type, char *mntopts)
|
||||
if (!fp) {
|
||||
(void) fprintf(stderr, gettext(
|
||||
"filesystem '%s' was mounted, but /etc/mtab "
|
||||
"could not be opened due to error: %s\n"),
|
||||
dataset, strerror(errno));
|
||||
"could not be opened due to error %d\n"),
|
||||
dataset, errno);
|
||||
return (MOUNT_FILEIO);
|
||||
}
|
||||
|
||||
@@ -134,8 +333,8 @@ mtab_update(char *dataset, char *mntpoint, char *type, char *mntopts)
|
||||
if (error) {
|
||||
(void) fprintf(stderr, gettext(
|
||||
"filesystem '%s' was mounted, but /etc/mtab "
|
||||
"could not be updated due to error: %s\n"),
|
||||
dataset, strerror(errno));
|
||||
"could not be updated due to error %d\n"),
|
||||
dataset, errno);
|
||||
return (MOUNT_FILEIO);
|
||||
}
|
||||
|
||||
@@ -144,6 +343,34 @@ mtab_update(char *dataset, char *mntpoint, char *type, char *mntopts)
|
||||
return (MOUNT_SUCCESS);
|
||||
}
|
||||
|
||||
static void
|
||||
append_mntopt(const char *name, const char *val, char *mntopts,
|
||||
char *mtabopt, boolean_t quote)
|
||||
{
|
||||
char tmp[MNT_LINE_MAX];
|
||||
|
||||
snprintf(tmp, MNT_LINE_MAX, quote ? ",%s=\"%s\"" : ",%s=%s", name, val);
|
||||
|
||||
if (mntopts)
|
||||
strlcat(mntopts, tmp, MNT_LINE_MAX);
|
||||
|
||||
if (mtabopt)
|
||||
strlcat(mtabopt, tmp, MNT_LINE_MAX);
|
||||
}
|
||||
|
||||
static void
|
||||
zfs_selinux_setcontext(zfs_handle_t *zhp, zfs_prop_t zpt, const char *name,
|
||||
char *mntopts, char *mtabopt)
|
||||
{
|
||||
char context[ZFS_MAXPROPLEN];
|
||||
|
||||
if (zfs_prop_get(zhp, zpt, context, sizeof (context),
|
||||
NULL, NULL, 0, B_FALSE) == 0) {
|
||||
if (strcmp(context, "none") != 0)
|
||||
append_mntopt(name, context, mntopts, mtabopt, B_TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
@@ -154,13 +381,12 @@ main(int argc, char **argv)
|
||||
char badopt[MNT_LINE_MAX] = { '\0' };
|
||||
char mtabopt[MNT_LINE_MAX] = { '\0' };
|
||||
char mntpoint[PATH_MAX];
|
||||
char dataset[PATH_MAX], *pdataset = dataset;
|
||||
char *dataset;
|
||||
unsigned long mntflags = 0, zfsflags = 0, remount = 0;
|
||||
int sloppy = 0, fake = 0, verbose = 0, nomtab = 0, zfsutil = 0;
|
||||
int error, c;
|
||||
|
||||
(void) setlocale(LC_ALL, "");
|
||||
(void) setlocale(LC_NUMERIC, "C");
|
||||
(void) textdomain(TEXT_DOMAIN);
|
||||
|
||||
opterr = 0;
|
||||
@@ -185,11 +411,10 @@ main(int argc, char **argv)
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
if (optopt)
|
||||
(void) fprintf(stderr,
|
||||
gettext("Invalid option '%c'\n"), optopt);
|
||||
(void) fprintf(stderr, gettext("Invalid option '%c'\n"),
|
||||
optopt);
|
||||
(void) fprintf(stderr, gettext("Usage: mount.zfs "
|
||||
"[-sfnvh] [-o options] <dataset> <mountpoint>\n"));
|
||||
"[-sfnv] [-o options] <dataset> <mountpoint>\n"));
|
||||
return (MOUNT_USAGE);
|
||||
}
|
||||
}
|
||||
@@ -211,18 +436,18 @@ main(int argc, char **argv)
|
||||
return (MOUNT_USAGE);
|
||||
}
|
||||
|
||||
parse_dataset(argv[0], &pdataset);
|
||||
dataset = parse_dataset(argv[0]);
|
||||
|
||||
/* canonicalize the mount point */
|
||||
if (realpath(argv[1], mntpoint) == NULL) {
|
||||
(void) fprintf(stderr, gettext("filesystem '%s' cannot be "
|
||||
"mounted at '%s' due to canonicalization error: %s\n"),
|
||||
dataset, argv[1], strerror(errno));
|
||||
"mounted at '%s' due to canonicalization error %d.\n"),
|
||||
dataset, argv[1], errno);
|
||||
return (MOUNT_SYSERR);
|
||||
}
|
||||
|
||||
/* validate mount options and set mntflags */
|
||||
error = zfs_parse_mount_options(mntopts, &mntflags, &zfsflags, sloppy,
|
||||
error = parse_options(mntopts, &mntflags, &zfsflags, sloppy,
|
||||
badopt, mtabopt);
|
||||
if (error) {
|
||||
switch (error) {
|
||||
@@ -246,6 +471,13 @@ main(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
(void) fprintf(stdout, gettext("mount.zfs:\n"
|
||||
" dataset: \"%s\"\n mountpoint: \"%s\"\n"
|
||||
" mountflags: 0x%lx\n zfsflags: 0x%lx\n"
|
||||
" mountopts: \"%s\"\n mtabopts: \"%s\"\n"),
|
||||
dataset, mntpoint, mntflags, zfsflags, mntopts, mtabopt);
|
||||
|
||||
if (mntflags & MS_REMOUNT) {
|
||||
nomtab = 1;
|
||||
remount = 1;
|
||||
@@ -255,7 +487,7 @@ main(int argc, char **argv)
|
||||
zfsutil = 1;
|
||||
|
||||
if ((g_zfs = libzfs_init()) == NULL) {
|
||||
(void) fprintf(stderr, "%s\n", libzfs_error_init(errno));
|
||||
(void) fprintf(stderr, "%s", libzfs_error_init(errno));
|
||||
return (MOUNT_SYSERR);
|
||||
}
|
||||
|
||||
@@ -268,11 +500,33 @@ main(int argc, char **argv)
|
||||
return (MOUNT_USAGE);
|
||||
}
|
||||
|
||||
if (!zfsutil || sloppy ||
|
||||
libzfs_envvar_is_set("ZFS_MOUNT_HELPER")) {
|
||||
zfs_adjust_mount_options(zhp, mntpoint, mntopts, mtabopt);
|
||||
/*
|
||||
* Checks to see if the ZFS_PROP_SELINUX_CONTEXT exists
|
||||
* if it does, create a tmp variable in case it's needed
|
||||
* checks to see if the selinux context is set to the default
|
||||
* if it is, allow the setting of the other context properties
|
||||
* this is needed because the 'context' property overrides others
|
||||
* if it is not the default, set the 'context' property
|
||||
*/
|
||||
if (zfs_prop_get(zhp, ZFS_PROP_SELINUX_CONTEXT, prop, sizeof (prop),
|
||||
NULL, NULL, 0, B_FALSE) == 0) {
|
||||
if (strcmp(prop, "none") == 0) {
|
||||
zfs_selinux_setcontext(zhp, ZFS_PROP_SELINUX_FSCONTEXT,
|
||||
MNTOPT_FSCONTEXT, mntopts, mtabopt);
|
||||
zfs_selinux_setcontext(zhp, ZFS_PROP_SELINUX_DEFCONTEXT,
|
||||
MNTOPT_DEFCONTEXT, mntopts, mtabopt);
|
||||
zfs_selinux_setcontext(zhp,
|
||||
ZFS_PROP_SELINUX_ROOTCONTEXT, MNTOPT_ROOTCONTEXT,
|
||||
mntopts, mtabopt);
|
||||
} else {
|
||||
append_mntopt(MNTOPT_CONTEXT, prop,
|
||||
mntopts, mtabopt, B_TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
/* A hint used to determine an auto-mounted snapshot mount point */
|
||||
append_mntopt(MNTOPT_MNTPOINT, mntpoint, mntopts, NULL, B_FALSE);
|
||||
|
||||
/* treat all snapshots as legacy mount points */
|
||||
if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT)
|
||||
(void) strlcpy(prop, ZFS_MOUNTPOINT_LEGACY, ZFS_MAXPROPLEN);
|
||||
@@ -289,11 +543,12 @@ main(int argc, char **argv)
|
||||
if (zfs_version == 0) {
|
||||
fprintf(stderr, gettext("unable to fetch "
|
||||
"ZFS version for filesystem '%s'\n"), dataset);
|
||||
zfs_close(zhp);
|
||||
libzfs_fini(g_zfs);
|
||||
return (MOUNT_SYSERR);
|
||||
}
|
||||
|
||||
zfs_close(zhp);
|
||||
libzfs_fini(g_zfs);
|
||||
|
||||
/*
|
||||
* Legacy mount points may only be mounted using 'mount', never using
|
||||
* 'zfs mount'. However, since 'zfs mount' actually invokes 'mount'
|
||||
@@ -311,8 +566,6 @@ main(int argc, char **argv)
|
||||
"Use 'zfs set mountpoint=%s' or 'mount -t zfs %s %s'.\n"
|
||||
"See zfs(8) for more information.\n"),
|
||||
dataset, mntpoint, dataset, mntpoint);
|
||||
zfs_close(zhp);
|
||||
libzfs_fini(g_zfs);
|
||||
return (MOUNT_USAGE);
|
||||
}
|
||||
|
||||
@@ -323,38 +576,14 @@ main(int argc, char **argv)
|
||||
"Use 'zfs set mountpoint=%s' or 'zfs mount %s'.\n"
|
||||
"See zfs(8) for more information.\n"),
|
||||
dataset, "legacy", dataset);
|
||||
zfs_close(zhp);
|
||||
libzfs_fini(g_zfs);
|
||||
return (MOUNT_USAGE);
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
(void) fprintf(stdout, gettext("mount.zfs:\n"
|
||||
" dataset: \"%s\"\n mountpoint: \"%s\"\n"
|
||||
" mountflags: 0x%lx\n zfsflags: 0x%lx\n"
|
||||
" mountopts: \"%s\"\n mtabopts: \"%s\"\n"),
|
||||
dataset, mntpoint, mntflags, zfsflags, mntopts, mtabopt);
|
||||
|
||||
if (!fake) {
|
||||
if (zfsutil && !sloppy &&
|
||||
!libzfs_envvar_is_set("ZFS_MOUNT_HELPER")) {
|
||||
error = zfs_mount_at(zhp, mntopts, mntflags, mntpoint);
|
||||
if (error) {
|
||||
(void) fprintf(stderr, "zfs_mount_at() failed: "
|
||||
"%s", libzfs_error_description(g_zfs));
|
||||
zfs_close(zhp);
|
||||
libzfs_fini(g_zfs);
|
||||
return (MOUNT_SYSERR);
|
||||
}
|
||||
} else {
|
||||
error = mount(dataset, mntpoint, MNTTYPE_ZFS,
|
||||
mntflags, mntopts);
|
||||
}
|
||||
error = mount(dataset, mntpoint, MNTTYPE_ZFS,
|
||||
mntflags, mntopts);
|
||||
}
|
||||
|
||||
zfs_close(zhp);
|
||||
libzfs_fini(g_zfs);
|
||||
|
||||
if (error) {
|
||||
switch (errno) {
|
||||
case ENOENT:
|
||||
@@ -389,7 +618,7 @@ main(int argc, char **argv)
|
||||
"mount the filesystem again.\n"), dataset);
|
||||
return (MOUNT_SYSERR);
|
||||
}
|
||||
fallthrough;
|
||||
/* fallthru */
|
||||
#endif
|
||||
default:
|
||||
(void) fprintf(stderr, gettext("filesystem "
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
include $(top_srcdir)/config/Rules.am
|
||||
|
||||
# Includes kernel code, generate warnings for large stack frames
|
||||
AM_CFLAGS += $(FRAME_LARGER_THAN)
|
||||
AM_CFLAGS += $(DEBUG_STACKFLAGS) $(FRAME_LARGER_THAN)
|
||||
AM_CPPFLAGS += -DDEBUG
|
||||
|
||||
# Unconditionally enable ASSERTs
|
||||
AM_CPPFLAGS += -DDEBUG -UNDEBUG -DZFS_DEBUG
|
||||
DEFAULT_INCLUDES += \
|
||||
-I$(top_srcdir)/include \
|
||||
-I$(top_srcdir)/lib/libspl/include
|
||||
|
||||
bin_PROGRAMS = raidz_test
|
||||
|
||||
@@ -14,9 +15,8 @@ raidz_test_SOURCES = \
|
||||
raidz_bench.c
|
||||
|
||||
raidz_test_LDADD = \
|
||||
$(abs_top_builddir)/lib/libzpool/libzpool.la \
|
||||
$(abs_top_builddir)/lib/libzfs_core/libzfs_core.la
|
||||
$(top_builddir)/lib/libnvpair/libnvpair.la \
|
||||
$(top_builddir)/lib/libuutil/libuutil.la \
|
||||
$(top_builddir)/lib/libzpool/libzpool.la
|
||||
|
||||
raidz_test_LDADD += -lm
|
||||
|
||||
include $(top_srcdir)/config/CppCheck.am
|
||||
raidz_test_LDADD += -lm -ldl
|
||||
|
||||
@@ -31,6 +31,8 @@
|
||||
#include <sys/vdev_raidz_impl.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <sys/time.h>
|
||||
|
||||
#include "raidz_test.h"
|
||||
|
||||
#define GEN_BENCH_MEMORY (((uint64_t)1ULL)<<32)
|
||||
@@ -81,17 +83,8 @@ run_gen_bench_impl(const char *impl)
|
||||
/* create suitable raidz_map */
|
||||
ncols = rto_opts.rto_dcols + fn + 1;
|
||||
zio_bench.io_size = 1ULL << ds;
|
||||
|
||||
if (rto_opts.rto_expand) {
|
||||
rm_bench = vdev_raidz_map_alloc_expanded(
|
||||
zio_bench.io_abd,
|
||||
zio_bench.io_size, zio_bench.io_offset,
|
||||
rto_opts.rto_ashift, ncols+1, ncols,
|
||||
fn+1, rto_opts.rto_expand_offset);
|
||||
} else {
|
||||
rm_bench = vdev_raidz_map_alloc(&zio_bench,
|
||||
BENCH_ASHIFT, ncols, fn+1);
|
||||
}
|
||||
rm_bench = vdev_raidz_map_alloc(&zio_bench,
|
||||
BENCH_ASHIFT, ncols, fn+1);
|
||||
|
||||
/* estimate iteration count */
|
||||
iter_cnt = GEN_BENCH_MEMORY;
|
||||
@@ -120,7 +113,7 @@ run_gen_bench_impl(const char *impl)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
void
|
||||
run_gen_bench(void)
|
||||
{
|
||||
char **impl_name;
|
||||
@@ -170,16 +163,8 @@ run_rec_bench_impl(const char *impl)
|
||||
(1ULL << BENCH_ASHIFT))
|
||||
continue;
|
||||
|
||||
if (rto_opts.rto_expand) {
|
||||
rm_bench = vdev_raidz_map_alloc_expanded(
|
||||
zio_bench.io_abd,
|
||||
zio_bench.io_size, zio_bench.io_offset,
|
||||
BENCH_ASHIFT, ncols+1, ncols,
|
||||
PARITY_PQR, rto_opts.rto_expand_offset);
|
||||
} else {
|
||||
rm_bench = vdev_raidz_map_alloc(&zio_bench,
|
||||
BENCH_ASHIFT, ncols, PARITY_PQR);
|
||||
}
|
||||
rm_bench = vdev_raidz_map_alloc(&zio_bench,
|
||||
BENCH_ASHIFT, ncols, PARITY_PQR);
|
||||
|
||||
/* estimate iteration count */
|
||||
iter_cnt = (REC_BENCH_MEMORY);
|
||||
@@ -212,7 +197,7 @@ run_rec_bench_impl(const char *impl)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
void
|
||||
run_rec_bench(void)
|
||||
{
|
||||
char **impl_name;
|
||||
|
||||
+50
-287
@@ -77,20 +77,16 @@ static void print_opts(raidz_test_opts_t *opts, boolean_t force)
|
||||
(void) fprintf(stdout, DBLSEP "Running with options:\n"
|
||||
" (-a) zio ashift : %zu\n"
|
||||
" (-o) zio offset : 1 << %zu\n"
|
||||
" (-e) expanded map : %s\n"
|
||||
" (-r) reflow offset : %llx\n"
|
||||
" (-d) number of raidz data columns : %zu\n"
|
||||
" (-s) size of DATA : 1 << %zu\n"
|
||||
" (-S) sweep parameters : %s \n"
|
||||
" (-v) verbose : %s \n\n",
|
||||
opts->rto_ashift, /* -a */
|
||||
ilog2(opts->rto_offset), /* -o */
|
||||
opts->rto_expand ? "yes" : "no", /* -e */
|
||||
(u_longlong_t)opts->rto_expand_offset, /* -r */
|
||||
opts->rto_dcols, /* -d */
|
||||
ilog2(opts->rto_dsize), /* -s */
|
||||
opts->rto_sweep ? "yes" : "no", /* -S */
|
||||
verbose); /* -v */
|
||||
opts->rto_ashift, /* -a */
|
||||
ilog2(opts->rto_offset), /* -o */
|
||||
opts->rto_dcols, /* -d */
|
||||
ilog2(opts->rto_dsize), /* -s */
|
||||
opts->rto_sweep ? "yes" : "no", /* -S */
|
||||
verbose); /* -v */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,8 +104,6 @@ static void usage(boolean_t requested)
|
||||
"\t[-S parameter sweep (default: %s)]\n"
|
||||
"\t[-t timeout for parameter sweep test]\n"
|
||||
"\t[-B benchmark all raidz implementations]\n"
|
||||
"\t[-e use expanded raidz map (default: %s)]\n"
|
||||
"\t[-r expanded raidz map reflow offset (default: %llx)]\n"
|
||||
"\t[-v increase verbosity (default: %zu)]\n"
|
||||
"\t[-h (print help)]\n"
|
||||
"\t[-T test the test, see if failure would be detected]\n"
|
||||
@@ -120,8 +114,6 @@ static void usage(boolean_t requested)
|
||||
o->rto_dcols, /* -d */
|
||||
ilog2(o->rto_dsize), /* -s */
|
||||
rto_opts.rto_sweep ? "yes" : "no", /* -S */
|
||||
rto_opts.rto_expand ? "yes" : "no", /* -e */
|
||||
(u_longlong_t)o->rto_expand_offset, /* -r */
|
||||
o->rto_v); /* -d */
|
||||
|
||||
exit(requested ? 0 : 1);
|
||||
@@ -136,7 +128,7 @@ static void process_options(int argc, char **argv)
|
||||
|
||||
bcopy(&rto_opts_defaults, o, sizeof (*o));
|
||||
|
||||
while ((opt = getopt(argc, argv, "TDBSvha:er:o:d:s:t:")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "TDBSvha:o:d:s:t:")) != -1) {
|
||||
value = 0;
|
||||
|
||||
switch (opt) {
|
||||
@@ -144,12 +136,6 @@ static void process_options(int argc, char **argv)
|
||||
value = strtoull(optarg, NULL, 0);
|
||||
o->rto_ashift = MIN(13, MAX(9, value));
|
||||
break;
|
||||
case 'e':
|
||||
o->rto_expand = 1;
|
||||
break;
|
||||
case 'r':
|
||||
o->rto_expand_offset = strtoull(optarg, NULL, 0);
|
||||
break;
|
||||
case 'o':
|
||||
value = strtoull(optarg, NULL, 0);
|
||||
o->rto_offset = ((1ULL << MIN(12, value)) >> 9) << 9;
|
||||
@@ -193,34 +179,25 @@ static void process_options(int argc, char **argv)
|
||||
}
|
||||
}
|
||||
|
||||
#define DATA_COL(rr, i) ((rr)->rr_col[rr->rr_firstdatacol + (i)].rc_abd)
|
||||
#define DATA_COL_SIZE(rr, i) ((rr)->rr_col[rr->rr_firstdatacol + (i)].rc_size)
|
||||
#define DATA_COL(rm, i) ((rm)->rm_col[raidz_parity(rm) + (i)].rc_abd)
|
||||
#define DATA_COL_SIZE(rm, i) ((rm)->rm_col[raidz_parity(rm) + (i)].rc_size)
|
||||
|
||||
#define CODE_COL(rr, i) ((rr)->rr_col[(i)].rc_abd)
|
||||
#define CODE_COL_SIZE(rr, i) ((rr)->rr_col[(i)].rc_size)
|
||||
#define CODE_COL(rm, i) ((rm)->rm_col[(i)].rc_abd)
|
||||
#define CODE_COL_SIZE(rm, i) ((rm)->rm_col[(i)].rc_size)
|
||||
|
||||
static int
|
||||
cmp_code(raidz_test_opts_t *opts, const raidz_map_t *rm, const int parity)
|
||||
{
|
||||
int r, i, ret = 0;
|
||||
int i, ret = 0;
|
||||
|
||||
VERIFY(parity >= 1 && parity <= 3);
|
||||
|
||||
for (r = 0; r < rm->rm_nrows; r++) {
|
||||
raidz_row_t * const rr = rm->rm_row[r];
|
||||
raidz_row_t * const rrg = opts->rm_golden->rm_row[r];
|
||||
for (i = 0; i < parity; i++) {
|
||||
if (CODE_COL_SIZE(rrg, i) == 0) {
|
||||
VERIFY0(CODE_COL_SIZE(rr, i));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (abd_cmp(CODE_COL(rr, i),
|
||||
CODE_COL(rrg, i)) != 0) {
|
||||
ret++;
|
||||
LOG_OPT(D_DEBUG, opts,
|
||||
"\nParity block [%d] different!\n", i);
|
||||
}
|
||||
for (i = 0; i < parity; i++) {
|
||||
if (abd_cmp(CODE_COL(rm, i), CODE_COL(opts->rm_golden, i))
|
||||
!= 0) {
|
||||
ret++;
|
||||
LOG_OPT(D_DEBUG, opts,
|
||||
"\nParity block [%d] different!\n", i);
|
||||
}
|
||||
}
|
||||
return (ret);
|
||||
@@ -229,26 +206,16 @@ cmp_code(raidz_test_opts_t *opts, const raidz_map_t *rm, const int parity)
|
||||
static int
|
||||
cmp_data(raidz_test_opts_t *opts, raidz_map_t *rm)
|
||||
{
|
||||
int r, i, dcols, ret = 0;
|
||||
int i, ret = 0;
|
||||
int dcols = opts->rm_golden->rm_cols - raidz_parity(opts->rm_golden);
|
||||
|
||||
for (r = 0; r < rm->rm_nrows; r++) {
|
||||
raidz_row_t *rr = rm->rm_row[r];
|
||||
raidz_row_t *rrg = opts->rm_golden->rm_row[r];
|
||||
dcols = opts->rm_golden->rm_row[0]->rr_cols -
|
||||
raidz_parity(opts->rm_golden);
|
||||
for (i = 0; i < dcols; i++) {
|
||||
if (DATA_COL_SIZE(rrg, i) == 0) {
|
||||
VERIFY0(DATA_COL_SIZE(rr, i));
|
||||
continue;
|
||||
}
|
||||
for (i = 0; i < dcols; i++) {
|
||||
if (abd_cmp(DATA_COL(opts->rm_golden, i), DATA_COL(rm, i))
|
||||
!= 0) {
|
||||
ret++;
|
||||
|
||||
if (abd_cmp(DATA_COL(rrg, i),
|
||||
DATA_COL(rr, i)) != 0) {
|
||||
ret++;
|
||||
|
||||
LOG_OPT(D_DEBUG, opts,
|
||||
"\nData block [%d] different!\n", i);
|
||||
}
|
||||
LOG_OPT(D_DEBUG, opts,
|
||||
"\nData block [%d] different!\n", i);
|
||||
}
|
||||
}
|
||||
return (ret);
|
||||
@@ -269,13 +236,12 @@ init_rand(void *data, size_t size, void *private)
|
||||
static void
|
||||
corrupt_colums(raidz_map_t *rm, const int *tgts, const int cnt)
|
||||
{
|
||||
for (int r = 0; r < rm->rm_nrows; r++) {
|
||||
raidz_row_t *rr = rm->rm_row[r];
|
||||
for (int i = 0; i < cnt; i++) {
|
||||
raidz_col_t *col = &rr->rr_col[tgts[i]];
|
||||
abd_iterate_func(col->rc_abd, 0, col->rc_size,
|
||||
init_rand, NULL);
|
||||
}
|
||||
int i;
|
||||
raidz_col_t *col;
|
||||
|
||||
for (i = 0; i < cnt; i++) {
|
||||
col = &rm->rm_col[tgts[i]];
|
||||
abd_iterate_func(col->rc_abd, 0, col->rc_size, init_rand, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -322,22 +288,10 @@ init_raidz_golden_map(raidz_test_opts_t *opts, const int parity)
|
||||
|
||||
VERIFY0(vdev_raidz_impl_set("original"));
|
||||
|
||||
if (opts->rto_expand) {
|
||||
opts->rm_golden =
|
||||
vdev_raidz_map_alloc_expanded(opts->zio_golden->io_abd,
|
||||
opts->zio_golden->io_size, opts->zio_golden->io_offset,
|
||||
opts->rto_ashift, total_ncols+1, total_ncols,
|
||||
parity, opts->rto_expand_offset);
|
||||
rm_test = vdev_raidz_map_alloc_expanded(zio_test->io_abd,
|
||||
zio_test->io_size, zio_test->io_offset,
|
||||
opts->rto_ashift, total_ncols+1, total_ncols,
|
||||
parity, opts->rto_expand_offset);
|
||||
} else {
|
||||
opts->rm_golden = vdev_raidz_map_alloc(opts->zio_golden,
|
||||
opts->rto_ashift, total_ncols, parity);
|
||||
rm_test = vdev_raidz_map_alloc(zio_test,
|
||||
opts->rto_ashift, total_ncols, parity);
|
||||
}
|
||||
opts->rm_golden = vdev_raidz_map_alloc(opts->zio_golden,
|
||||
opts->rto_ashift, total_ncols, parity);
|
||||
rm_test = vdev_raidz_map_alloc(zio_test,
|
||||
opts->rto_ashift, total_ncols, parity);
|
||||
|
||||
VERIFY(opts->zio_golden);
|
||||
VERIFY(opts->rm_golden);
|
||||
@@ -358,187 +312,6 @@ init_raidz_golden_map(raidz_test_opts_t *opts, const int parity)
|
||||
return (err);
|
||||
}
|
||||
|
||||
/*
|
||||
* If reflow is not in progress, reflow_offset should be UINT64_MAX.
|
||||
* For each row, if the row is entirely before reflow_offset, it will
|
||||
* come from the new location. Otherwise this row will come from the
|
||||
* old location. Therefore, rows that straddle the reflow_offset will
|
||||
* come from the old location.
|
||||
*
|
||||
* NOTE: Until raidz expansion is implemented this function is only
|
||||
* needed by raidz_test.c to the multi-row raid_map_t functionality.
|
||||
*/
|
||||
raidz_map_t *
|
||||
vdev_raidz_map_alloc_expanded(abd_t *abd, uint64_t size, uint64_t offset,
|
||||
uint64_t ashift, uint64_t physical_cols, uint64_t logical_cols,
|
||||
uint64_t nparity, uint64_t reflow_offset)
|
||||
{
|
||||
/* The zio's size in units of the vdev's minimum sector size. */
|
||||
uint64_t s = size >> ashift;
|
||||
uint64_t q, r, bc, devidx, asize = 0, tot;
|
||||
|
||||
/*
|
||||
* "Quotient": The number of data sectors for this stripe on all but
|
||||
* the "big column" child vdevs that also contain "remainder" data.
|
||||
* AKA "full rows"
|
||||
*/
|
||||
q = s / (logical_cols - nparity);
|
||||
|
||||
/*
|
||||
* "Remainder": The number of partial stripe data sectors in this I/O.
|
||||
* This will add a sector to some, but not all, child vdevs.
|
||||
*/
|
||||
r = s - q * (logical_cols - nparity);
|
||||
|
||||
/* The number of "big columns" - those which contain remainder data. */
|
||||
bc = (r == 0 ? 0 : r + nparity);
|
||||
|
||||
/*
|
||||
* The total number of data and parity sectors associated with
|
||||
* this I/O.
|
||||
*/
|
||||
tot = s + nparity * (q + (r == 0 ? 0 : 1));
|
||||
|
||||
/* How many rows contain data (not skip) */
|
||||
uint64_t rows = howmany(tot, logical_cols);
|
||||
int cols = MIN(tot, logical_cols);
|
||||
|
||||
raidz_map_t *rm = kmem_zalloc(offsetof(raidz_map_t, rm_row[rows]),
|
||||
KM_SLEEP);
|
||||
rm->rm_nrows = rows;
|
||||
|
||||
for (uint64_t row = 0; row < rows; row++) {
|
||||
raidz_row_t *rr = kmem_alloc(offsetof(raidz_row_t,
|
||||
rr_col[cols]), KM_SLEEP);
|
||||
rm->rm_row[row] = rr;
|
||||
|
||||
/* The starting RAIDZ (parent) vdev sector of the row. */
|
||||
uint64_t b = (offset >> ashift) + row * logical_cols;
|
||||
|
||||
/*
|
||||
* If we are in the middle of a reflow, and any part of this
|
||||
* row has not been copied, then use the old location of
|
||||
* this row.
|
||||
*/
|
||||
int row_phys_cols = physical_cols;
|
||||
if (b + (logical_cols - nparity) > reflow_offset >> ashift)
|
||||
row_phys_cols--;
|
||||
|
||||
/* starting child of this row */
|
||||
uint64_t child_id = b % row_phys_cols;
|
||||
/* The starting byte offset on each child vdev. */
|
||||
uint64_t child_offset = (b / row_phys_cols) << ashift;
|
||||
|
||||
/*
|
||||
* We set cols to the entire width of the block, even
|
||||
* if this row is shorter. This is needed because parity
|
||||
* generation (for Q and R) needs to know the entire width,
|
||||
* because it treats the short row as though it was
|
||||
* full-width (and the "phantom" sectors were zero-filled).
|
||||
*
|
||||
* Another approach to this would be to set cols shorter
|
||||
* (to just the number of columns that we might do i/o to)
|
||||
* and have another mechanism to tell the parity generation
|
||||
* about the "entire width". Reconstruction (at least
|
||||
* vdev_raidz_reconstruct_general()) would also need to
|
||||
* know about the "entire width".
|
||||
*/
|
||||
rr->rr_cols = cols;
|
||||
rr->rr_bigcols = bc;
|
||||
rr->rr_missingdata = 0;
|
||||
rr->rr_missingparity = 0;
|
||||
rr->rr_firstdatacol = nparity;
|
||||
rr->rr_abd_empty = NULL;
|
||||
rr->rr_nempty = 0;
|
||||
|
||||
for (int c = 0; c < rr->rr_cols; c++, child_id++) {
|
||||
if (child_id >= row_phys_cols) {
|
||||
child_id -= row_phys_cols;
|
||||
child_offset += 1ULL << ashift;
|
||||
}
|
||||
rr->rr_col[c].rc_devidx = child_id;
|
||||
rr->rr_col[c].rc_offset = child_offset;
|
||||
rr->rr_col[c].rc_orig_data = NULL;
|
||||
rr->rr_col[c].rc_error = 0;
|
||||
rr->rr_col[c].rc_tried = 0;
|
||||
rr->rr_col[c].rc_skipped = 0;
|
||||
rr->rr_col[c].rc_need_orig_restore = B_FALSE;
|
||||
|
||||
uint64_t dc = c - rr->rr_firstdatacol;
|
||||
if (c < rr->rr_firstdatacol) {
|
||||
rr->rr_col[c].rc_size = 1ULL << ashift;
|
||||
rr->rr_col[c].rc_abd =
|
||||
abd_alloc_linear(rr->rr_col[c].rc_size,
|
||||
B_TRUE);
|
||||
} else if (row == rows - 1 && bc != 0 && c >= bc) {
|
||||
/*
|
||||
* Past the end, this for parity generation.
|
||||
*/
|
||||
rr->rr_col[c].rc_size = 0;
|
||||
rr->rr_col[c].rc_abd = NULL;
|
||||
} else {
|
||||
/*
|
||||
* "data column" (col excluding parity)
|
||||
* Add an ASCII art diagram here
|
||||
*/
|
||||
uint64_t off;
|
||||
|
||||
if (c < bc || r == 0) {
|
||||
off = dc * rows + row;
|
||||
} else {
|
||||
off = r * rows +
|
||||
(dc - r) * (rows - 1) + row;
|
||||
}
|
||||
rr->rr_col[c].rc_size = 1ULL << ashift;
|
||||
rr->rr_col[c].rc_abd = abd_get_offset_struct(
|
||||
&rr->rr_col[c].rc_abdstruct,
|
||||
abd, off << ashift, 1 << ashift);
|
||||
}
|
||||
|
||||
asize += rr->rr_col[c].rc_size;
|
||||
}
|
||||
/*
|
||||
* If all data stored spans all columns, there's a danger that
|
||||
* parity will always be on the same device and, since parity
|
||||
* isn't read during normal operation, that that device's I/O
|
||||
* bandwidth won't be used effectively. We therefore switch
|
||||
* the parity every 1MB.
|
||||
*
|
||||
* ...at least that was, ostensibly, the theory. As a practical
|
||||
* matter unless we juggle the parity between all devices
|
||||
* evenly, we won't see any benefit. Further, occasional writes
|
||||
* that aren't a multiple of the LCM of the number of children
|
||||
* and the minimum stripe width are sufficient to avoid pessimal
|
||||
* behavior. Unfortunately, this decision created an implicit
|
||||
* on-disk format requirement that we need to support for all
|
||||
* eternity, but only for single-parity RAID-Z.
|
||||
*
|
||||
* If we intend to skip a sector in the zeroth column for
|
||||
* padding we must make sure to note this swap. We will never
|
||||
* intend to skip the first column since at least one data and
|
||||
* one parity column must appear in each row.
|
||||
*/
|
||||
if (rr->rr_firstdatacol == 1 && rr->rr_cols > 1 &&
|
||||
(offset & (1ULL << 20))) {
|
||||
ASSERT(rr->rr_cols >= 2);
|
||||
ASSERT(rr->rr_col[0].rc_size == rr->rr_col[1].rc_size);
|
||||
devidx = rr->rr_col[0].rc_devidx;
|
||||
uint64_t o = rr->rr_col[0].rc_offset;
|
||||
rr->rr_col[0].rc_devidx = rr->rr_col[1].rc_devidx;
|
||||
rr->rr_col[0].rc_offset = rr->rr_col[1].rc_offset;
|
||||
rr->rr_col[1].rc_devidx = devidx;
|
||||
rr->rr_col[1].rc_offset = o;
|
||||
}
|
||||
|
||||
}
|
||||
ASSERT3U(asize, ==, tot << ashift);
|
||||
|
||||
/* init RAIDZ parity ops */
|
||||
rm->rm_ops = vdev_raidz_math_get_ops();
|
||||
|
||||
return (rm);
|
||||
}
|
||||
|
||||
static raidz_map_t *
|
||||
init_raidz_map(raidz_test_opts_t *opts, zio_t **zio, const int parity)
|
||||
{
|
||||
@@ -557,15 +330,8 @@ init_raidz_map(raidz_test_opts_t *opts, zio_t **zio, const int parity)
|
||||
(*zio)->io_abd = raidz_alloc(alloc_dsize);
|
||||
init_zio_abd(*zio);
|
||||
|
||||
if (opts->rto_expand) {
|
||||
rm = vdev_raidz_map_alloc_expanded((*zio)->io_abd,
|
||||
(*zio)->io_size, (*zio)->io_offset,
|
||||
opts->rto_ashift, total_ncols+1, total_ncols,
|
||||
parity, opts->rto_expand_offset);
|
||||
} else {
|
||||
rm = vdev_raidz_map_alloc(*zio, opts->rto_ashift,
|
||||
total_ncols, parity);
|
||||
}
|
||||
rm = vdev_raidz_map_alloc(*zio, opts->rto_ashift,
|
||||
total_ncols, parity);
|
||||
VERIFY(rm);
|
||||
|
||||
/* Make sure code columns are destroyed */
|
||||
@@ -654,7 +420,7 @@ run_rec_check_impl(raidz_test_opts_t *opts, raidz_map_t *rm, const int fn)
|
||||
if (fn < RAIDZ_REC_PQ) {
|
||||
/* can reconstruct 1 failed data disk */
|
||||
for (x0 = 0; x0 < opts->rto_dcols; x0++) {
|
||||
if (x0 >= rm->rm_row[0]->rr_cols - raidz_parity(rm))
|
||||
if (x0 >= rm->rm_cols - raidz_parity(rm))
|
||||
continue;
|
||||
|
||||
/* Check if should stop */
|
||||
@@ -679,11 +445,10 @@ run_rec_check_impl(raidz_test_opts_t *opts, raidz_map_t *rm, const int fn)
|
||||
} else if (fn < RAIDZ_REC_PQR) {
|
||||
/* can reconstruct 2 failed data disk */
|
||||
for (x0 = 0; x0 < opts->rto_dcols; x0++) {
|
||||
if (x0 >= rm->rm_row[0]->rr_cols - raidz_parity(rm))
|
||||
if (x0 >= rm->rm_cols - raidz_parity(rm))
|
||||
continue;
|
||||
for (x1 = x0 + 1; x1 < opts->rto_dcols; x1++) {
|
||||
if (x1 >= rm->rm_row[0]->rr_cols -
|
||||
raidz_parity(rm))
|
||||
if (x1 >= rm->rm_cols - raidz_parity(rm))
|
||||
continue;
|
||||
|
||||
/* Check if should stop */
|
||||
@@ -710,15 +475,14 @@ run_rec_check_impl(raidz_test_opts_t *opts, raidz_map_t *rm, const int fn)
|
||||
} else {
|
||||
/* can reconstruct 3 failed data disk */
|
||||
for (x0 = 0; x0 < opts->rto_dcols; x0++) {
|
||||
if (x0 >= rm->rm_row[0]->rr_cols - raidz_parity(rm))
|
||||
if (x0 >= rm->rm_cols - raidz_parity(rm))
|
||||
continue;
|
||||
for (x1 = x0 + 1; x1 < opts->rto_dcols; x1++) {
|
||||
if (x1 >= rm->rm_row[0]->rr_cols -
|
||||
raidz_parity(rm))
|
||||
if (x1 >= rm->rm_cols - raidz_parity(rm))
|
||||
continue;
|
||||
for (x2 = x1 + 1; x2 < opts->rto_dcols; x2++) {
|
||||
if (x2 >= rm->rm_row[0]->rr_cols -
|
||||
raidz_parity(rm))
|
||||
if (x2 >=
|
||||
rm->rm_cols - raidz_parity(rm))
|
||||
continue;
|
||||
|
||||
/* Check if should stop */
|
||||
@@ -936,12 +700,12 @@ run_sweep(void)
|
||||
opts->rto_dcols = dcols_v[d];
|
||||
opts->rto_offset = (1 << ashift_v[a]) * rand();
|
||||
opts->rto_dsize = size_v[s];
|
||||
opts->rto_expand = rto_opts.rto_expand;
|
||||
opts->rto_expand_offset = rto_opts.rto_expand_offset;
|
||||
opts->rto_v = 0; /* be quiet */
|
||||
|
||||
VERIFY3P(thread_create(NULL, 0, sweep_thread, (void *) opts,
|
||||
0, NULL, TS_RUN, defclsyspri), !=, NULL);
|
||||
VERIFY3P(zk_thread_create(NULL, 0,
|
||||
(thread_func_t)sweep_thread,
|
||||
(void *) opts, 0, NULL, TS_RUN, 0,
|
||||
PTHREAD_CREATE_JOINABLE), !=, NULL);
|
||||
}
|
||||
|
||||
exit:
|
||||
@@ -970,7 +734,6 @@ exit:
|
||||
return (sweep_state == SWEEP_ERROR ? SWEEP_ERROR : 0);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
@@ -996,7 +759,7 @@ main(int argc, char **argv)
|
||||
|
||||
process_options(argc, argv);
|
||||
|
||||
kernel_init(SPA_MODE_READ);
|
||||
kernel_init(FREAD);
|
||||
|
||||
/* setup random data because rand() is not reentrant */
|
||||
rand_data = (int *)umem_alloc(SPA_MAXBLOCKSIZE, UMEM_NOFAIL);
|
||||
|
||||
@@ -38,21 +38,18 @@ static const char *raidz_impl_names[] = {
|
||||
"avx512bw",
|
||||
"aarch64_neon",
|
||||
"aarch64_neonx2",
|
||||
"powerpc_altivec",
|
||||
NULL
|
||||
};
|
||||
|
||||
typedef struct raidz_test_opts {
|
||||
size_t rto_ashift;
|
||||
uint64_t rto_offset;
|
||||
size_t rto_offset;
|
||||
size_t rto_dcols;
|
||||
size_t rto_dsize;
|
||||
size_t rto_v;
|
||||
size_t rto_sweep;
|
||||
size_t rto_sweep_timeout;
|
||||
size_t rto_benchmark;
|
||||
size_t rto_expand;
|
||||
uint64_t rto_expand_offset;
|
||||
size_t rto_sanity;
|
||||
size_t rto_gdb;
|
||||
|
||||
@@ -71,8 +68,6 @@ static const raidz_test_opts_t rto_opts_defaults = {
|
||||
.rto_v = 0,
|
||||
.rto_sweep = 0,
|
||||
.rto_benchmark = 0,
|
||||
.rto_expand = 0,
|
||||
.rto_expand_offset = -1ULL,
|
||||
.rto_sanity = 0,
|
||||
.rto_gdb = 0,
|
||||
.rto_should_stop = B_FALSE
|
||||
@@ -117,7 +112,4 @@ void init_zio_abd(zio_t *zio);
|
||||
|
||||
void run_raidz_benchmark(void);
|
||||
|
||||
struct raidz_map *vdev_raidz_map_alloc_expanded(abd_t *, uint64_t, uint64_t,
|
||||
uint64_t, uint64_t, uint64_t, uint64_t, uint64_t);
|
||||
|
||||
#endif /* RAIDZ_TEST_H */
|
||||
|
||||
@@ -1,3 +1 @@
|
||||
include $(top_srcdir)/config/Shellcheck.am
|
||||
|
||||
dist_udev_SCRIPTS = vdev_id
|
||||
|
||||
+129
-315
@@ -79,34 +79,6 @@
|
||||
# channel 86:00.0 1 A
|
||||
# channel 86:00.0 0 B
|
||||
|
||||
# #
|
||||
# # Example vdev_id.conf - multipath / multijbod-daisychaining
|
||||
# #
|
||||
#
|
||||
# multipath yes
|
||||
# multijbod yes
|
||||
#
|
||||
# # PCI_ID HBA PORT CHANNEL NAME
|
||||
# channel 85:00.0 1 A
|
||||
# channel 85:00.0 0 B
|
||||
# channel 86:00.0 1 A
|
||||
# channel 86:00.0 0 B
|
||||
|
||||
# #
|
||||
# # Example vdev_id.conf - multipath / mixed
|
||||
# #
|
||||
#
|
||||
# multipath yes
|
||||
# slot mix
|
||||
#
|
||||
# # PCI_ID HBA PORT CHANNEL NAME
|
||||
# channel 85:00.0 3 A
|
||||
# channel 85:00.0 2 B
|
||||
# channel 86:00.0 3 A
|
||||
# channel 86:00.0 2 B
|
||||
# channel af:00.0 0 C
|
||||
# channel af:00.0 1 C
|
||||
|
||||
# #
|
||||
# # Example vdev_id.conf - alias
|
||||
# #
|
||||
@@ -120,10 +92,9 @@ PATH=/bin:/sbin:/usr/bin:/usr/sbin
|
||||
CONFIG=/etc/zfs/vdev_id.conf
|
||||
PHYS_PER_PORT=
|
||||
DEV=
|
||||
MULTIPATH=
|
||||
TOPOLOGY=
|
||||
BAY=
|
||||
ENCL_ID=""
|
||||
UNIQ_ENCL_ID=""
|
||||
|
||||
usage() {
|
||||
cat << EOF
|
||||
@@ -131,153 +102,71 @@ Usage: vdev_id [-h]
|
||||
vdev_id <-d device> [-c config_file] [-p phys_per_port]
|
||||
[-g sas_direct|sas_switch|scsi] [-m]
|
||||
|
||||
-c specify name of an alternative config file [default=$CONFIG]
|
||||
-c specify name of alernate config file [default=$CONFIG]
|
||||
-d specify basename of device (i.e. sda)
|
||||
-e Create enclose device symlinks only (/dev/by-enclosure)
|
||||
-g Storage network topology [default="$TOPOLOGY"]
|
||||
-m Run in multipath mode
|
||||
-j Run in multijbod mode
|
||||
-p number of phy's per switch port [default=$PHYS_PER_PORT]
|
||||
-h show this summary
|
||||
EOF
|
||||
exit 1
|
||||
# exit with error to avoid processing usage message by a udev rule
|
||||
exit 0
|
||||
}
|
||||
|
||||
map_slot() {
|
||||
LINUX_SLOT=$1
|
||||
CHANNEL=$2
|
||||
local LINUX_SLOT=$1
|
||||
local CHANNEL=$2
|
||||
local MAPPED_SLOT=
|
||||
|
||||
MAPPED_SLOT=$(awk -v linux_slot="$LINUX_SLOT" -v channel="$CHANNEL" \
|
||||
'$1 == "slot" && $2 == linux_slot && \
|
||||
($4 ~ "^"channel"$" || $4 ~ /^$/) { print $3; exit}' $CONFIG)
|
||||
MAPPED_SLOT=`awk "\\$1 == \"slot\" && \\$2 == ${LINUX_SLOT} && \
|
||||
\\$4 ~ /^${CHANNEL}$|^$/ { print \\$3; exit }" $CONFIG`
|
||||
if [ -z "$MAPPED_SLOT" ] ; then
|
||||
MAPPED_SLOT=$LINUX_SLOT
|
||||
fi
|
||||
printf "%d" "${MAPPED_SLOT}"
|
||||
printf "%d" ${MAPPED_SLOT}
|
||||
}
|
||||
|
||||
map_channel() {
|
||||
MAPPED_CHAN=
|
||||
PCI_ID=$1
|
||||
PORT=$2
|
||||
local MAPPED_CHAN=
|
||||
local PCI_ID=$1
|
||||
local PORT=$2
|
||||
|
||||
case $TOPOLOGY in
|
||||
"sas_switch")
|
||||
MAPPED_CHAN=$(awk -v port="$PORT" \
|
||||
'$1 == "channel" && $2 == port \
|
||||
{ print $3; exit }' $CONFIG)
|
||||
MAPPED_CHAN=`awk "\\$1 == \"channel\" && \\$2 == ${PORT} \
|
||||
{ print \\$3; exit }" $CONFIG`
|
||||
;;
|
||||
"sas_direct"|"scsi")
|
||||
MAPPED_CHAN=$(awk -v pciID="$PCI_ID" -v port="$PORT" \
|
||||
'$1 == "channel" && $2 == pciID && $3 == port \
|
||||
{print $4}' $CONFIG)
|
||||
MAPPED_CHAN=`awk "\\$1 == \"channel\" && \
|
||||
\\$2 == \"${PCI_ID}\" && \\$3 == ${PORT} \
|
||||
{ print \\$4; exit }" $CONFIG`
|
||||
;;
|
||||
esac
|
||||
printf "%s" "${MAPPED_CHAN}"
|
||||
}
|
||||
|
||||
get_encl_id() {
|
||||
set -- $(echo $1)
|
||||
count=$#
|
||||
|
||||
i=1
|
||||
while [ $i -le $count ] ; do
|
||||
d=$(eval echo '$'{$i})
|
||||
id=$(cat "/sys/class/enclosure/${d}/id")
|
||||
ENCL_ID="${ENCL_ID} $id"
|
||||
i=$((i + 1))
|
||||
done
|
||||
}
|
||||
|
||||
get_uniq_encl_id() {
|
||||
for uuid in ${ENCL_ID}; do
|
||||
found=0
|
||||
|
||||
for count in ${UNIQ_ENCL_ID}; do
|
||||
if [ $count = $uuid ]; then
|
||||
found=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ $found -eq 0 ]; then
|
||||
UNIQ_ENCL_ID="${UNIQ_ENCL_ID} $uuid"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# map_jbod explainer: The bsg driver knows the difference between a SAS
|
||||
# expander and fanout expander. Use hostX instance along with top-level
|
||||
# (whole enclosure) expander instances in /sys/class/enclosure and
|
||||
# matching a field in an array of expanders, using the index of the
|
||||
# matched array field as the enclosure instance, thereby making jbod IDs
|
||||
# dynamic. Avoids reliance on high overhead userspace commands like
|
||||
# multipath and lsscsi and instead uses existing sysfs data. $HOSTCHAN
|
||||
# variable derived from devpath gymnastics in sas_handler() function.
|
||||
map_jbod() {
|
||||
DEVEXP=$(ls -l "/sys/block/$DEV/device/" | grep enclos | awk -F/ '{print $(NF-1) }')
|
||||
DEV=$1
|
||||
|
||||
# Use "set --" to create index values (Arrays)
|
||||
set -- $(ls -l /sys/class/enclosure | grep -v "^total" | awk '{print $9}')
|
||||
# Get count of total elements
|
||||
JBOD_COUNT=$#
|
||||
JBOD_ITEM=$*
|
||||
|
||||
# Build JBODs (enclosure) id from sys/class/enclosure/<dev>/id
|
||||
get_encl_id "$JBOD_ITEM"
|
||||
# Different expander instances for each paths.
|
||||
# Filter out and keep only unique id.
|
||||
get_uniq_encl_id
|
||||
|
||||
# Identify final 'mapped jbod'
|
||||
j=0
|
||||
for count in ${UNIQ_ENCL_ID}; do
|
||||
i=1
|
||||
j=$((j + 1))
|
||||
while [ $i -le $JBOD_COUNT ] ; do
|
||||
d=$(eval echo '$'{$i})
|
||||
id=$(cat "/sys/class/enclosure/${d}/id")
|
||||
if [ "$d" = "$DEVEXP" ] && [ $id = $count ] ; then
|
||||
MAPPED_JBOD=$j
|
||||
break
|
||||
fi
|
||||
i=$((i + 1))
|
||||
done
|
||||
done
|
||||
|
||||
printf "%d" "${MAPPED_JBOD}"
|
||||
printf "%s" ${MAPPED_CHAN}
|
||||
}
|
||||
|
||||
sas_handler() {
|
||||
if [ -z "$PHYS_PER_PORT" ] ; then
|
||||
PHYS_PER_PORT=$(awk '$1 == "phys_per_port" \
|
||||
{print $2; exit}' $CONFIG)
|
||||
PHYS_PER_PORT=`awk "\\$1 == \"phys_per_port\" \
|
||||
{print \\$2; exit}" $CONFIG`
|
||||
fi
|
||||
PHYS_PER_PORT=${PHYS_PER_PORT:-4}
|
||||
|
||||
if ! echo "$PHYS_PER_PORT" | grep -q -E '^[0-9]+$' ; then
|
||||
if ! echo $PHYS_PER_PORT | grep -q -E '^[0-9]+$' ; then
|
||||
echo "Error: phys_per_port value $PHYS_PER_PORT is non-numeric"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$MULTIPATH_MODE" ] ; then
|
||||
MULTIPATH_MODE=$(awk '$1 == "multipath" \
|
||||
{print $2; exit}' $CONFIG)
|
||||
fi
|
||||
|
||||
if [ -z "$MULTIJBOD_MODE" ] ; then
|
||||
MULTIJBOD_MODE=$(awk '$1 == "multijbod" \
|
||||
{print $2; exit}' $CONFIG)
|
||||
MULTIPATH_MODE=`awk "\\$1 == \"multipath\" \
|
||||
{print \\$2; exit}" $CONFIG`
|
||||
fi
|
||||
|
||||
# Use first running component device if we're handling a dm-mpath device
|
||||
if [ "$MULTIPATH_MODE" = "yes" ] ; then
|
||||
# If udev didn't tell us the UUID via DM_NAME, check /dev/mapper
|
||||
if [ -z "$DM_NAME" ] ; then
|
||||
DM_NAME=$(ls -l --full-time /dev/mapper |
|
||||
grep "$DEV"$ | awk '{print $9}')
|
||||
DM_NAME=`ls -l --full-time /dev/mapper |
|
||||
awk "/\/$DEV$/{print \\$9}"`
|
||||
fi
|
||||
|
||||
# For raw disks udev exports DEVTYPE=partition when
|
||||
@@ -287,50 +176,28 @@ sas_handler() {
|
||||
# we have to append the -part suffix directly in the
|
||||
# helper.
|
||||
if [ "$DEVTYPE" != "partition" ] ; then
|
||||
# Match p[number], remove the 'p' and prepend "-part"
|
||||
PART=$(echo "$DM_NAME" |
|
||||
awk 'match($0,/p[0-9]+$/) {print "-part"substr($0,RSTART+1,RLENGTH-1)}')
|
||||
PART=`echo $DM_NAME | awk -Fp '/p/{print "-part"$2}'`
|
||||
fi
|
||||
|
||||
# Strip off partition information.
|
||||
DM_NAME=$(echo "$DM_NAME" | sed 's/p[0-9][0-9]*$//')
|
||||
DM_NAME=`echo $DM_NAME | sed 's/p[0-9][0-9]*$//'`
|
||||
if [ -z "$DM_NAME" ] ; then
|
||||
return
|
||||
fi
|
||||
|
||||
# Utilize DM device name to gather subordinate block devices
|
||||
# using sysfs to avoid userspace utilities
|
||||
|
||||
# If our DEVNAME is something like /dev/dm-177, then we may be
|
||||
# able to get our DMDEV from it.
|
||||
DMDEV=$(echo $DEVNAME | sed 's;/dev/;;g')
|
||||
if [ ! -e /sys/block/$DMDEV/slaves/* ] ; then
|
||||
# It's not there, try looking in /dev/mapper
|
||||
DMDEV=$(ls -l --full-time /dev/mapper | grep $DM_NAME |
|
||||
awk '{gsub("../", " "); print $NF}')
|
||||
fi
|
||||
|
||||
# Use sysfs pointers in /sys/block/dm-X/slaves because using
|
||||
# userspace tools creates lots of overhead and should be avoided
|
||||
# whenever possible. Use awk to isolate lowest instance of
|
||||
# sd device member in dm device group regardless of string
|
||||
# length.
|
||||
DEV=$(ls "/sys/block/$DMDEV/slaves" | awk '
|
||||
{ len=sprintf ("%20s",length($0)); gsub(/ /,0,str); a[NR]=len "_" $0; }
|
||||
END {
|
||||
asort(a)
|
||||
print substr(a[1],22)
|
||||
}')
|
||||
|
||||
# Get the raw scsi device name from multipath -ll. Strip off
|
||||
# leading pipe symbols to make field numbering consistent.
|
||||
DEV=`multipath -ll $DM_NAME |
|
||||
awk '/running/{gsub("^[|]"," "); print $3 ; exit}'`
|
||||
if [ -z "$DEV" ] ; then
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
if echo "$DEV" | grep -q ^/devices/ ; then
|
||||
if echo $DEV | grep -q ^/devices/ ; then
|
||||
sys_path=$DEV
|
||||
else
|
||||
sys_path=$(udevadm info -q path -p "/sys/block/$DEV" 2>/dev/null)
|
||||
sys_path=`udevadm info -q path -p /sys/block/$DEV 2>/dev/null`
|
||||
fi
|
||||
|
||||
# Use positional parameters as an ad-hoc array
|
||||
@@ -340,104 +207,84 @@ sas_handler() {
|
||||
|
||||
# Get path up to /sys/.../hostX
|
||||
i=1
|
||||
|
||||
while [ $i -le "$num_dirs" ] ; do
|
||||
d=$(eval echo '$'{$i})
|
||||
while [ $i -le $num_dirs ] ; do
|
||||
d=$(eval echo \${$i})
|
||||
scsi_host_dir="$scsi_host_dir/$d"
|
||||
echo "$d" | grep -q -E '^host[0-9]+$' && break
|
||||
i=$((i + 1))
|
||||
echo $d | grep -q -E '^host[0-9]+$' && break
|
||||
i=$(($i + 1))
|
||||
done
|
||||
|
||||
# Lets grab the SAS host channel number and save it for JBOD sorting later
|
||||
HOSTCHAN=$(echo "$d" | awk -F/ '{ gsub("host","",$NF); print $NF}')
|
||||
|
||||
if [ $i = "$num_dirs" ] ; then
|
||||
if [ $i = $num_dirs ] ; then
|
||||
return
|
||||
fi
|
||||
|
||||
PCI_ID=$(eval echo '$'{$((i -1))} | awk -F: '{print $2":"$3}')
|
||||
PCI_ID=$(eval echo \${$(($i -1))} | awk -F: '{print $2":"$3}')
|
||||
|
||||
# In sas_switch mode, the directory four levels beneath
|
||||
# /sys/.../hostX contains symlinks to phy devices that reveal
|
||||
# the switch port number. In sas_direct mode, the phy links one
|
||||
# directory down reveal the HBA port.
|
||||
port_dir=$scsi_host_dir
|
||||
|
||||
case $TOPOLOGY in
|
||||
"sas_switch") j=$((i + 4)) ;;
|
||||
"sas_direct") j=$((i + 1)) ;;
|
||||
"sas_switch") j=$(($i + 4)) ;;
|
||||
"sas_direct") j=$(($i + 1)) ;;
|
||||
esac
|
||||
|
||||
i=$((i + 1))
|
||||
|
||||
i=$(($i + 1))
|
||||
while [ $i -le $j ] ; do
|
||||
port_dir="$port_dir/$(eval echo '$'{$i})"
|
||||
i=$((i + 1))
|
||||
port_dir="$port_dir/$(eval echo \${$i})"
|
||||
i=$(($i + 1))
|
||||
done
|
||||
|
||||
PHY=$(ls -vd "$port_dir"/phy* 2>/dev/null | head -1 | awk -F: '{print $NF}')
|
||||
PHY=`ls -d $port_dir/phy* 2>/dev/null | head -1 | awk -F: '{print $NF}'`
|
||||
if [ -z "$PHY" ] ; then
|
||||
PHY=0
|
||||
fi
|
||||
PORT=$((PHY / PHYS_PER_PORT))
|
||||
PORT=$(( $PHY / $PHYS_PER_PORT ))
|
||||
|
||||
# Look in /sys/.../sas_device/end_device-X for the bay_identifier
|
||||
# attribute.
|
||||
end_device_dir=$port_dir
|
||||
|
||||
while [ $i -lt "$num_dirs" ] ; do
|
||||
d=$(eval echo '$'{$i})
|
||||
while [ $i -lt $num_dirs ] ; do
|
||||
d=$(eval echo \${$i})
|
||||
end_device_dir="$end_device_dir/$d"
|
||||
if echo "$d" | grep -q '^end_device' ; then
|
||||
if echo $d | grep -q '^end_device' ; then
|
||||
end_device_dir="$end_device_dir/sas_device/$d"
|
||||
break
|
||||
fi
|
||||
i=$((i + 1))
|
||||
i=$(($i + 1))
|
||||
done
|
||||
|
||||
# Add 'mix' slot type for environments where dm-multipath devices
|
||||
# include end-devices connected via SAS expanders or direct connection
|
||||
# to SAS HBA. A mixed connectivity environment such as pool devices
|
||||
# contained in a SAS JBOD and spare drives or log devices directly
|
||||
# connected in a server backplane without expanders in the I/O path.
|
||||
SLOT=
|
||||
|
||||
case $BAY in
|
||||
"bay")
|
||||
SLOT=$(cat "$end_device_dir/bay_identifier" 2>/dev/null)
|
||||
;;
|
||||
"mix")
|
||||
if [ $(cat "$end_device_dir/bay_identifier" 2>/dev/null) ] ; then
|
||||
SLOT=$(cat "$end_device_dir/bay_identifier" 2>/dev/null)
|
||||
else
|
||||
SLOT=$(cat "$end_device_dir/phy_identifier" 2>/dev/null)
|
||||
fi
|
||||
SLOT=`cat $end_device_dir/bay_identifier 2>/dev/null`
|
||||
;;
|
||||
"phy")
|
||||
SLOT=$(cat "$end_device_dir/phy_identifier" 2>/dev/null)
|
||||
SLOT=`cat $end_device_dir/phy_identifier 2>/dev/null`
|
||||
;;
|
||||
"port")
|
||||
d=$(eval echo '$'{$i})
|
||||
SLOT=$(echo "$d" | sed -e 's/^.*://')
|
||||
d=$(eval echo \${$i})
|
||||
SLOT=`echo $d | sed -e 's/^.*://'`
|
||||
;;
|
||||
"id")
|
||||
i=$((i + 1))
|
||||
d=$(eval echo '$'{$i})
|
||||
SLOT=$(echo "$d" | sed -e 's/^.*://')
|
||||
i=$(($i + 1))
|
||||
d=$(eval echo \${$i})
|
||||
SLOT=`echo $d | sed -e 's/^.*://'`
|
||||
;;
|
||||
"lun")
|
||||
i=$((i + 2))
|
||||
d=$(eval echo '$'{$i})
|
||||
SLOT=$(echo "$d" | sed -e 's/^.*://')
|
||||
i=$(($i + 2))
|
||||
d=$(eval echo \${$i})
|
||||
SLOT=`echo $d | sed -e 's/^.*://'`
|
||||
;;
|
||||
"ses")
|
||||
# look for this SAS path in all SCSI Enclosure Services
|
||||
# (SES) enclosures
|
||||
sas_address=$(cat "$end_device_dir/sas_address" 2>/dev/null)
|
||||
enclosures=$(lsscsi -g | \
|
||||
sed -n -e '/enclosu/s/^.* \([^ ][^ ]*\) *$/\1/p')
|
||||
sas_address=`cat $end_device_dir/sas_address 2>/dev/null`
|
||||
enclosures=`lsscsi -g | \
|
||||
sed -n -e '/enclosu/s/^.* \([^ ][^ ]*\) *$/\1/p'`
|
||||
for enclosure in $enclosures; do
|
||||
set -- $(sg_ses -p aes "$enclosure" | \
|
||||
set -- $(sg_ses -p aes $enclosure | \
|
||||
awk "/device slot number:/{slot=\$12} \
|
||||
/SAS address: $sas_address/\
|
||||
{print slot}")
|
||||
@@ -452,55 +299,42 @@ sas_handler() {
|
||||
return
|
||||
fi
|
||||
|
||||
if [ "$MULTIJBOD_MODE" = "yes" ] ; then
|
||||
CHAN=$(map_channel "$PCI_ID" "$PORT")
|
||||
SLOT=$(map_slot "$SLOT" "$CHAN")
|
||||
JBOD=$(map_jbod "$DEV")
|
||||
|
||||
if [ -z "$CHAN" ] ; then
|
||||
return
|
||||
fi
|
||||
echo "${CHAN}"-"${JBOD}"-"${SLOT}${PART}"
|
||||
else
|
||||
CHAN=$(map_channel "$PCI_ID" "$PORT")
|
||||
SLOT=$(map_slot "$SLOT" "$CHAN")
|
||||
|
||||
if [ -z "$CHAN" ] ; then
|
||||
return
|
||||
fi
|
||||
echo "${CHAN}${SLOT}${PART}"
|
||||
CHAN=`map_channel $PCI_ID $PORT`
|
||||
SLOT=`map_slot $SLOT $CHAN`
|
||||
if [ -z "$CHAN" ] ; then
|
||||
return
|
||||
fi
|
||||
echo ${CHAN}${SLOT}${PART}
|
||||
}
|
||||
|
||||
scsi_handler() {
|
||||
if [ -z "$FIRST_BAY_NUMBER" ] ; then
|
||||
FIRST_BAY_NUMBER=$(awk '$1 == "first_bay_number" \
|
||||
{print $2; exit}' $CONFIG)
|
||||
FIRST_BAY_NUMBER=`awk "\\$1 == \"first_bay_number\" \
|
||||
{print \\$2; exit}" $CONFIG`
|
||||
fi
|
||||
FIRST_BAY_NUMBER=${FIRST_BAY_NUMBER:-0}
|
||||
|
||||
if [ -z "$PHYS_PER_PORT" ] ; then
|
||||
PHYS_PER_PORT=$(awk '$1 == "phys_per_port" \
|
||||
{print $2; exit}' $CONFIG)
|
||||
PHYS_PER_PORT=`awk "\\$1 == \"phys_per_port\" \
|
||||
{print \\$2; exit}" $CONFIG`
|
||||
fi
|
||||
PHYS_PER_PORT=${PHYS_PER_PORT:-4}
|
||||
|
||||
if ! echo "$PHYS_PER_PORT" | grep -q -E '^[0-9]+$' ; then
|
||||
if ! echo $PHYS_PER_PORT | grep -q -E '^[0-9]+$' ; then
|
||||
echo "Error: phys_per_port value $PHYS_PER_PORT is non-numeric"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$MULTIPATH_MODE" ] ; then
|
||||
MULTIPATH_MODE=$(awk '$1 == "multipath" \
|
||||
{print $2; exit}' $CONFIG)
|
||||
MULTIPATH_MODE=`awk "\\$1 == \"multipath\" \
|
||||
{print \\$2; exit}" $CONFIG`
|
||||
fi
|
||||
|
||||
# Use first running component device if we're handling a dm-mpath device
|
||||
if [ "$MULTIPATH_MODE" = "yes" ] ; then
|
||||
# If udev didn't tell us the UUID via DM_NAME, check /dev/mapper
|
||||
if [ -z "$DM_NAME" ] ; then
|
||||
DM_NAME=$(ls -l --full-time /dev/mapper |
|
||||
grep "$DEV"$ | awk '{print $9}')
|
||||
DM_NAME=`ls -l --full-time /dev/mapper |
|
||||
awk "/\/$DEV$/{print \\$9}"`
|
||||
fi
|
||||
|
||||
# For raw disks udev exports DEVTYPE=partition when
|
||||
@@ -510,30 +344,28 @@ scsi_handler() {
|
||||
# we have to append the -part suffix directly in the
|
||||
# helper.
|
||||
if [ "$DEVTYPE" != "partition" ] ; then
|
||||
# Match p[number], remove the 'p' and prepend "-part"
|
||||
PART=$(echo "$DM_NAME" |
|
||||
awk 'match($0,/p[0-9]+$/) {print "-part"substr($0,RSTART+1,RLENGTH-1)}')
|
||||
PART=`echo $DM_NAME | awk -Fp '/p/{print "-part"$2}'`
|
||||
fi
|
||||
|
||||
# Strip off partition information.
|
||||
DM_NAME=$(echo "$DM_NAME" | sed 's/p[0-9][0-9]*$//')
|
||||
DM_NAME=`echo $DM_NAME | sed 's/p[0-9][0-9]*$//'`
|
||||
if [ -z "$DM_NAME" ] ; then
|
||||
return
|
||||
fi
|
||||
|
||||
# Get the raw scsi device name from multipath -ll. Strip off
|
||||
# leading pipe symbols to make field numbering consistent.
|
||||
DEV=$(multipath -ll "$DM_NAME" |
|
||||
awk '/running/{gsub("^[|]"," "); print $3 ; exit}')
|
||||
DEV=`multipath -ll $DM_NAME |
|
||||
awk '/running/{gsub("^[|]"," "); print $3 ; exit}'`
|
||||
if [ -z "$DEV" ] ; then
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
if echo "$DEV" | grep -q ^/devices/ ; then
|
||||
if echo $DEV | grep -q ^/devices/ ; then
|
||||
sys_path=$DEV
|
||||
else
|
||||
sys_path=$(udevadm info -q path -p "/sys/block/$DEV" 2>/dev/null)
|
||||
sys_path=`udevadm info -q path -p /sys/block/$DEV 2>/dev/null`
|
||||
fi
|
||||
|
||||
# expect sys_path like this, for example:
|
||||
@@ -546,47 +378,44 @@ scsi_handler() {
|
||||
|
||||
# Get path up to /sys/.../hostX
|
||||
i=1
|
||||
|
||||
while [ $i -le "$num_dirs" ] ; do
|
||||
d=$(eval echo '$'{$i})
|
||||
while [ $i -le $num_dirs ] ; do
|
||||
d=$(eval echo \${$i})
|
||||
scsi_host_dir="$scsi_host_dir/$d"
|
||||
|
||||
echo "$d" | grep -q -E '^host[0-9]+$' && break
|
||||
i=$((i + 1))
|
||||
echo $d | grep -q -E '^host[0-9]+$' && break
|
||||
i=$(($i + 1))
|
||||
done
|
||||
|
||||
if [ $i = "$num_dirs" ] ; then
|
||||
if [ $i = $num_dirs ] ; then
|
||||
return
|
||||
fi
|
||||
|
||||
PCI_ID=$(eval echo '$'{$((i -1))} | awk -F: '{print $2":"$3}')
|
||||
PCI_ID=$(eval echo \${$(($i -1))} | awk -F: '{print $2":"$3}')
|
||||
|
||||
# In scsi mode, the directory two levels beneath
|
||||
# /sys/.../hostX reveals the port and slot.
|
||||
port_dir=$scsi_host_dir
|
||||
j=$((i + 2))
|
||||
j=$(($i + 2))
|
||||
|
||||
i=$((i + 1))
|
||||
i=$(($i + 1))
|
||||
while [ $i -le $j ] ; do
|
||||
port_dir="$port_dir/$(eval echo '$'{$i})"
|
||||
i=$((i + 1))
|
||||
port_dir="$port_dir/$(eval echo \${$i})"
|
||||
i=$(($i + 1))
|
||||
done
|
||||
|
||||
set -- $(echo "$port_dir" | sed -e 's/^.*:\([^:]*\):\([^:]*\)$/\1 \2/')
|
||||
set -- $(echo $port_dir | sed -e 's/^.*:\([^:]*\):\([^:]*\)$/\1 \2/')
|
||||
PORT=$1
|
||||
SLOT=$(($2 + FIRST_BAY_NUMBER))
|
||||
SLOT=$(($2 + $FIRST_BAY_NUMBER))
|
||||
|
||||
if [ -z "$SLOT" ] ; then
|
||||
return
|
||||
fi
|
||||
|
||||
CHAN=$(map_channel "$PCI_ID" "$PORT")
|
||||
SLOT=$(map_slot "$SLOT" "$CHAN")
|
||||
|
||||
CHAN=`map_channel $PCI_ID $PORT`
|
||||
SLOT=`map_slot $SLOT $CHAN`
|
||||
if [ -z "$CHAN" ] ; then
|
||||
return
|
||||
fi
|
||||
echo "${CHAN}${SLOT}${PART}"
|
||||
echo ${CHAN}${SLOT}${PART}
|
||||
}
|
||||
|
||||
# Figure out the name for the enclosure symlink
|
||||
@@ -596,10 +425,8 @@ enclosure_handler () {
|
||||
# DEVPATH=/sys/devices/pci0000:00/0000:00:03.0/0000:05:00.0/host0/subsystem/devices/0:0:0:0/scsi_generic/sg0
|
||||
|
||||
# Get the enclosure ID ("0:0:0:0")
|
||||
ENC="${DEVPATH%/*}"
|
||||
ENC="${ENC%/*}"
|
||||
ENC="${ENC##*/}"
|
||||
if [ ! -d "/sys/class/enclosure/$ENC" ] ; then
|
||||
ENC=$(basename $(readlink -m "/sys/$DEVPATH/../.."))
|
||||
if [ ! -d /sys/class/enclosure/$ENC ] ; then
|
||||
# Not an enclosure, bail out
|
||||
return
|
||||
fi
|
||||
@@ -607,26 +434,25 @@ enclosure_handler () {
|
||||
# Get the long sysfs device path to our enclosure. Looks like:
|
||||
# /devices/pci0000:00/0000:00:03.0/0000:05:00.0/host0/port-0:0/ ... /enclosure/0:0:0:0
|
||||
|
||||
ENC_DEVICE=$(readlink "/sys/class/enclosure/$ENC")
|
||||
ENC_DEVICE=$(readlink /sys/class/enclosure/$ENC)
|
||||
|
||||
# Grab the full path to the hosts port dir:
|
||||
# /devices/pci0000:00/0000:00:03.0/0000:05:00.0/host0/port-0:0
|
||||
PORT_DIR=$(echo "$ENC_DEVICE" | grep -Eo '.+host[0-9]+/port-[0-9]+:[0-9]+')
|
||||
PORT_DIR=$(echo $ENC_DEVICE | grep -Eo '.+host[0-9]+/port-[0-9]+:[0-9]+')
|
||||
|
||||
# Get the port number
|
||||
PORT_ID=$(echo "$PORT_DIR" | grep -Eo "[0-9]+$")
|
||||
PORT_ID=$(echo $PORT_DIR | grep -Eo "[0-9]+$")
|
||||
|
||||
# The PCI directory is two directories up from the port directory
|
||||
# /sys/devices/pci0000:00/0000:00:03.0/0000:05:00.0
|
||||
PCI_ID_LONG="$(readlink -m "/sys/$PORT_DIR/../..")"
|
||||
PCI_ID_LONG="${PCI_ID_LONG##*/}"
|
||||
PCI_ID_LONG=$(basename $(readlink -m "/sys/$PORT_DIR/../.."))
|
||||
|
||||
# Strip down the PCI address from 0000:05:00.0 to 05:00.0
|
||||
PCI_ID="${PCI_ID_LONG#[0-9]*:}"
|
||||
PCI_ID=$(echo "$PCI_ID_LONG" | sed -r 's/^[0-9]+://g')
|
||||
|
||||
# Name our device according to vdev_id.conf (like "L0" or "U1").
|
||||
NAME=$(awk "/channel/{if (\$1 == \"channel\" && \$2 == \"$PCI_ID\" && \
|
||||
\$3 == \"$PORT_ID\") {print \$4\$3}}" $CONFIG)
|
||||
\$3 == \"$PORT_ID\") {print \$4int(count[\$4])}; count[\$4]++}" $CONFIG)
|
||||
|
||||
echo "${NAME}"
|
||||
}
|
||||
@@ -661,12 +487,10 @@ alias_handler () {
|
||||
# digits as partitions, causing alias creation to fail. This
|
||||
# ambiguity seems unavoidable, so devices using this facility
|
||||
# must not use such names.
|
||||
DM_PART=
|
||||
if echo "$DM_NAME" | grep -q -E 'p[0-9][0-9]*$' ; then
|
||||
local DM_PART=
|
||||
if echo $DM_NAME | grep -q -E 'p[0-9][0-9]*$' ; then
|
||||
if [ "$DEVTYPE" != "partition" ] ; then
|
||||
# Match p[number], remove the 'p' and prepend "-part"
|
||||
DM_PART=$(echo "$DM_NAME" |
|
||||
awk 'match($0,/p[0-9]+$/) {print "-part"substr($0,RSTART+1,RLENGTH-1)}')
|
||||
DM_PART=`echo $DM_NAME | awk -Fp '/p/{print "-part"$2}'`
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -674,25 +498,21 @@ alias_handler () {
|
||||
for link in $DEVLINKS ; do
|
||||
# Remove partition information to match key of top-level device.
|
||||
if [ -n "$DM_PART" ] ; then
|
||||
link=$(echo "$link" | sed 's/p[0-9][0-9]*$//')
|
||||
link=`echo $link | sed 's/p[0-9][0-9]*$//'`
|
||||
fi
|
||||
# Check both the fully qualified and the base name of link.
|
||||
for l in $link ${link##*/} ; do
|
||||
if [ ! -z "$l" ]; then
|
||||
alias=$(awk -v var="$l" '($1 == "alias") && \
|
||||
($3 == var) \
|
||||
{ print $2; exit }' $CONFIG)
|
||||
if [ -n "$alias" ] ; then
|
||||
echo "${alias}${DM_PART}"
|
||||
return
|
||||
fi
|
||||
for l in $link `basename $link` ; do
|
||||
alias=`awk "\\$1 == \"alias\" && \\$3 == \"${l}\" \
|
||||
{ print \\$2; exit }" $CONFIG`
|
||||
if [ -n "$alias" ] ; then
|
||||
echo ${alias}${DM_PART}
|
||||
return
|
||||
fi
|
||||
done
|
||||
done
|
||||
}
|
||||
|
||||
# main
|
||||
while getopts 'c:d:eg:jmp:h' OPTION; do
|
||||
while getopts 'c:d:eg:mp:h' OPTION; do
|
||||
case ${OPTION} in
|
||||
c)
|
||||
CONFIG=${OPTARG}
|
||||
@@ -705,9 +525,7 @@ while getopts 'c:d:eg:jmp:h' OPTION; do
|
||||
# create the enclosure device symlinks only. We also need
|
||||
# "enclosure_symlinks yes" set in vdev_id.config to actually create the
|
||||
# symlink.
|
||||
ENCLOSURE_MODE=$(awk '{if ($1 == "enclosure_symlinks") \
|
||||
print $2}' "$CONFIG")
|
||||
|
||||
ENCLOSURE_MODE=$(awk '{if ($1 == "enclosure_symlinks") print $2}' $CONFIG)
|
||||
if [ "$ENCLOSURE_MODE" != "yes" ] ; then
|
||||
exit 0
|
||||
fi
|
||||
@@ -718,9 +536,6 @@ while getopts 'c:d:eg:jmp:h' OPTION; do
|
||||
p)
|
||||
PHYS_PER_PORT=${OPTARG}
|
||||
;;
|
||||
j)
|
||||
MULTIJBOD_MODE=yes
|
||||
;;
|
||||
m)
|
||||
MULTIPATH_MODE=yes
|
||||
;;
|
||||
@@ -730,35 +545,34 @@ while getopts 'c:d:eg:jmp:h' OPTION; do
|
||||
esac
|
||||
done
|
||||
|
||||
if [ ! -r "$CONFIG" ] ; then
|
||||
echo "Error: Config file \"$CONFIG\" not found"
|
||||
exit 1
|
||||
if [ ! -r $CONFIG ] ; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ -z "$DEV" ] && [ -z "$ENCLOSURE_MODE" ] ; then
|
||||
if [ -z "$DEV" -a -z "$ENCLOSURE_MODE" ] ; then
|
||||
echo "Error: missing required option -d"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$TOPOLOGY" ] ; then
|
||||
TOPOLOGY=$(awk '($1 == "topology") {print $2; exit}' "$CONFIG")
|
||||
TOPOLOGY=`awk "\\$1 == \"topology\" {print \\$2; exit}" $CONFIG`
|
||||
fi
|
||||
|
||||
if [ -z "$BAY" ] ; then
|
||||
BAY=$(awk '($1 == "slot") {print $2; exit}' "$CONFIG")
|
||||
BAY=`awk "\\$1 == \"slot\" {print \\$2; exit}" $CONFIG`
|
||||
fi
|
||||
|
||||
TOPOLOGY=${TOPOLOGY:-sas_direct}
|
||||
|
||||
# Should we create /dev/by-enclosure symlinks?
|
||||
if [ "$ENCLOSURE_MODE" = "yes" ] && [ "$TOPOLOGY" = "sas_direct" ] ; then
|
||||
if [ "$ENCLOSURE_MODE" = "yes" -a "$TOPOLOGY" = "sas_direct" ] ; then
|
||||
ID_ENCLOSURE=$(enclosure_handler)
|
||||
if [ -z "$ID_ENCLOSURE" ] ; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Just create the symlinks to the enclosure devices and then exit.
|
||||
ENCLOSURE_PREFIX=$(awk '/enclosure_symlinks_prefix/{print $2}' "$CONFIG")
|
||||
ENCLOSURE_PREFIX=$(awk '/enclosure_symlinks_prefix/{print $2}' $CONFIG)
|
||||
if [ -z "$ENCLOSURE_PREFIX" ] ; then
|
||||
ENCLOSURE_PREFIX="enc"
|
||||
fi
|
||||
@@ -768,16 +582,16 @@ if [ "$ENCLOSURE_MODE" = "yes" ] && [ "$TOPOLOGY" = "sas_direct" ] ; then
|
||||
fi
|
||||
|
||||
# First check if an alias was defined for this device.
|
||||
ID_VDEV=$(alias_handler)
|
||||
ID_VDEV=`alias_handler`
|
||||
|
||||
if [ -z "$ID_VDEV" ] ; then
|
||||
BAY=${BAY:-bay}
|
||||
case $TOPOLOGY in
|
||||
sas_direct|sas_switch)
|
||||
ID_VDEV=$(sas_handler)
|
||||
ID_VDEV=`sas_handler`
|
||||
;;
|
||||
scsi)
|
||||
ID_VDEV=$(scsi_handler)
|
||||
ID_VDEV=`scsi_handler`
|
||||
;;
|
||||
*)
|
||||
echo "Error: unknown topology $TOPOLOGY"
|
||||
|
||||
+11
-9
@@ -1,18 +1,20 @@
|
||||
include $(top_srcdir)/config/Rules.am
|
||||
|
||||
# Unconditionally enable debugging for zdb
|
||||
AM_CPPFLAGS += -DDEBUG -UNDEBUG -DZFS_DEBUG
|
||||
AM_CPPFLAGS += -DDEBUG
|
||||
|
||||
DEFAULT_INCLUDES += \
|
||||
-I$(top_srcdir)/include \
|
||||
-I$(top_srcdir)/lib/libspl/include
|
||||
|
||||
sbin_PROGRAMS = zdb
|
||||
|
||||
zdb_SOURCES = \
|
||||
zdb.c \
|
||||
zdb_il.c \
|
||||
zdb.h
|
||||
zdb_il.c
|
||||
|
||||
zdb_LDADD = \
|
||||
$(abs_top_builddir)/lib/libzpool/libzpool.la \
|
||||
$(abs_top_builddir)/lib/libzfs_core/libzfs_core.la \
|
||||
$(abs_top_builddir)/lib/libnvpair/libnvpair.la
|
||||
|
||||
include $(top_srcdir)/config/CppCheck.am
|
||||
$(top_builddir)/lib/libnvpair/libnvpair.la \
|
||||
$(top_builddir)/lib/libuutil/libuutil.la \
|
||||
$(top_builddir)/lib/libzpool/libzpool.la \
|
||||
$(top_builddir)/lib/libzfs/libzfs.la \
|
||||
$(top_builddir)/lib/libzfs_core/libzfs_core.la
|
||||
|
||||
+571
-4878
File diff suppressed because it is too large
Load Diff
+70
-97
@@ -25,7 +25,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2013, 2017 by Delphix. All rights reserved.
|
||||
* Copyright (c) 2013, 2016 by Delphix. All rights reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -42,14 +42,11 @@
|
||||
#include <sys/resource.h>
|
||||
#include <sys/zil.h>
|
||||
#include <sys/zil_impl.h>
|
||||
#include <sys/spa_impl.h>
|
||||
#include <sys/abd.h>
|
||||
|
||||
#include "zdb.h"
|
||||
|
||||
extern uint8_t dump_opt[256];
|
||||
|
||||
static char tab_prefix[4] = "\t\t\t";
|
||||
static char prefix[4] = "\t\t\t";
|
||||
|
||||
static void
|
||||
print_log_bp(const blkptr_t *bp, const char *prefix)
|
||||
@@ -62,9 +59,8 @@ print_log_bp(const blkptr_t *bp, const char *prefix)
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
zil_prt_rec_create(zilog_t *zilog, int txtype, const void *arg)
|
||||
zil_prt_rec_create(zilog_t *zilog, int txtype, lr_create_t *lr)
|
||||
{
|
||||
const lr_create_t *lr = arg;
|
||||
time_t crtime = lr->lr_crtime[0];
|
||||
char *name, *link;
|
||||
lr_attr_t *lrattr;
|
||||
@@ -79,55 +75,49 @@ zil_prt_rec_create(zilog_t *zilog, int txtype, const void *arg)
|
||||
|
||||
if (txtype == TX_SYMLINK) {
|
||||
link = name + strlen(name) + 1;
|
||||
(void) printf("%s%s -> %s\n", tab_prefix, name, link);
|
||||
(void) printf("%s%s -> %s\n", prefix, name, link);
|
||||
} else if (txtype != TX_MKXATTR) {
|
||||
(void) printf("%s%s\n", tab_prefix, name);
|
||||
(void) printf("%s%s\n", prefix, name);
|
||||
}
|
||||
|
||||
(void) printf("%s%s", tab_prefix, ctime(&crtime));
|
||||
(void) printf("%sdoid %llu, foid %llu, slots %llu, mode %llo\n",
|
||||
tab_prefix, (u_longlong_t)lr->lr_doid,
|
||||
(void) printf("%s%s", prefix, ctime(&crtime));
|
||||
(void) printf("%sdoid %llu, foid %llu, slots %llu, mode %llo\n", prefix,
|
||||
(u_longlong_t)lr->lr_doid,
|
||||
(u_longlong_t)LR_FOID_GET_OBJ(lr->lr_foid),
|
||||
(u_longlong_t)LR_FOID_GET_SLOTS(lr->lr_foid),
|
||||
(longlong_t)lr->lr_mode);
|
||||
(void) printf("%suid %llu, gid %llu, gen %llu, rdev 0x%llx\n",
|
||||
tab_prefix,
|
||||
(void) printf("%suid %llu, gid %llu, gen %llu, rdev 0x%llx\n", prefix,
|
||||
(u_longlong_t)lr->lr_uid, (u_longlong_t)lr->lr_gid,
|
||||
(u_longlong_t)lr->lr_gen, (u_longlong_t)lr->lr_rdev);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
zil_prt_rec_remove(zilog_t *zilog, int txtype, const void *arg)
|
||||
zil_prt_rec_remove(zilog_t *zilog, int txtype, lr_remove_t *lr)
|
||||
{
|
||||
const lr_remove_t *lr = arg;
|
||||
|
||||
(void) printf("%sdoid %llu, name %s\n", tab_prefix,
|
||||
(void) printf("%sdoid %llu, name %s\n", prefix,
|
||||
(u_longlong_t)lr->lr_doid, (char *)(lr + 1));
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
zil_prt_rec_link(zilog_t *zilog, int txtype, const void *arg)
|
||||
zil_prt_rec_link(zilog_t *zilog, int txtype, lr_link_t *lr)
|
||||
{
|
||||
const lr_link_t *lr = arg;
|
||||
|
||||
(void) printf("%sdoid %llu, link_obj %llu, name %s\n", tab_prefix,
|
||||
(void) printf("%sdoid %llu, link_obj %llu, name %s\n", prefix,
|
||||
(u_longlong_t)lr->lr_doid, (u_longlong_t)lr->lr_link_obj,
|
||||
(char *)(lr + 1));
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
zil_prt_rec_rename(zilog_t *zilog, int txtype, const void *arg)
|
||||
zil_prt_rec_rename(zilog_t *zilog, int txtype, lr_rename_t *lr)
|
||||
{
|
||||
const lr_rename_t *lr = arg;
|
||||
char *snm = (char *)(lr + 1);
|
||||
char *tnm = snm + strlen(snm) + 1;
|
||||
|
||||
(void) printf("%ssdoid %llu, tdoid %llu\n", tab_prefix,
|
||||
(void) printf("%ssdoid %llu, tdoid %llu\n", prefix,
|
||||
(u_longlong_t)lr->lr_sdoid, (u_longlong_t)lr->lr_tdoid);
|
||||
(void) printf("%ssrc %s tgt %s\n", tab_prefix, snm, tnm);
|
||||
(void) printf("%ssrc %s tgt %s\n", prefix, snm, tnm);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
@@ -135,8 +125,9 @@ static int
|
||||
zil_prt_rec_write_cb(void *data, size_t len, void *unused)
|
||||
{
|
||||
char *cdata = data;
|
||||
int i;
|
||||
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
for (i = 0; i < len; i++) {
|
||||
if (isprint(*cdata))
|
||||
(void) printf("%c ", *cdata);
|
||||
else
|
||||
@@ -148,16 +139,15 @@ zil_prt_rec_write_cb(void *data, size_t len, void *unused)
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
zil_prt_rec_write(zilog_t *zilog, int txtype, const void *arg)
|
||||
zil_prt_rec_write(zilog_t *zilog, int txtype, lr_write_t *lr)
|
||||
{
|
||||
const lr_write_t *lr = arg;
|
||||
abd_t *data;
|
||||
const blkptr_t *bp = &lr->lr_blkptr;
|
||||
blkptr_t *bp = &lr->lr_blkptr;
|
||||
zbookmark_phys_t zb;
|
||||
int verbose = MAX(dump_opt['d'], dump_opt['i']);
|
||||
int error;
|
||||
|
||||
(void) printf("%sfoid %llu, offset %llx, length %llx\n", tab_prefix,
|
||||
(void) printf("%sfoid %llu, offset %llx, length %llx\n", prefix,
|
||||
(u_longlong_t)lr->lr_foid, (u_longlong_t)lr->lr_offset,
|
||||
(u_longlong_t)lr->lr_length);
|
||||
|
||||
@@ -165,21 +155,20 @@ zil_prt_rec_write(zilog_t *zilog, int txtype, const void *arg)
|
||||
return;
|
||||
|
||||
if (lr->lr_common.lrc_reclen == sizeof (lr_write_t)) {
|
||||
(void) printf("%shas blkptr, %s\n", tab_prefix,
|
||||
(void) printf("%shas blkptr, %s\n", prefix,
|
||||
!BP_IS_HOLE(bp) &&
|
||||
bp->blk_birth >= spa_min_claim_txg(zilog->zl_spa) ?
|
||||
bp->blk_birth >= spa_first_txg(zilog->zl_spa) ?
|
||||
"will claim" : "won't claim");
|
||||
print_log_bp(bp, tab_prefix);
|
||||
print_log_bp(bp, prefix);
|
||||
|
||||
if (BP_IS_HOLE(bp)) {
|
||||
(void) printf("\t\t\tLSIZE 0x%llx\n",
|
||||
(u_longlong_t)BP_GET_LSIZE(bp));
|
||||
(void) printf("%s<hole>\n", tab_prefix);
|
||||
(void) printf("%s<hole>\n", prefix);
|
||||
return;
|
||||
}
|
||||
if (bp->blk_birth < zilog->zl_header->zh_claim_txg) {
|
||||
(void) printf("%s<block already committed>\n",
|
||||
tab_prefix);
|
||||
(void) printf("%s<block already committed>\n", prefix);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -199,7 +188,7 @@ zil_prt_rec_write(zilog_t *zilog, int txtype, const void *arg)
|
||||
abd_copy_from_buf(data, lr + 1, lr->lr_length);
|
||||
}
|
||||
|
||||
(void) printf("%s", tab_prefix);
|
||||
(void) printf("%s", prefix);
|
||||
(void) abd_iterate_func(data,
|
||||
0, MIN(lr->lr_length, (verbose < 6 ? 20 : SPA_MAXBLOCKSIZE)),
|
||||
zil_prt_rec_write_cb, NULL);
|
||||
@@ -211,55 +200,52 @@ out:
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
zil_prt_rec_truncate(zilog_t *zilog, int txtype, const void *arg)
|
||||
zil_prt_rec_truncate(zilog_t *zilog, int txtype, lr_truncate_t *lr)
|
||||
{
|
||||
const lr_truncate_t *lr = arg;
|
||||
|
||||
(void) printf("%sfoid %llu, offset 0x%llx, length 0x%llx\n", tab_prefix,
|
||||
(void) printf("%sfoid %llu, offset 0x%llx, length 0x%llx\n", prefix,
|
||||
(u_longlong_t)lr->lr_foid, (longlong_t)lr->lr_offset,
|
||||
(u_longlong_t)lr->lr_length);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
zil_prt_rec_setattr(zilog_t *zilog, int txtype, const void *arg)
|
||||
zil_prt_rec_setattr(zilog_t *zilog, int txtype, lr_setattr_t *lr)
|
||||
{
|
||||
const lr_setattr_t *lr = arg;
|
||||
time_t atime = (time_t)lr->lr_atime[0];
|
||||
time_t mtime = (time_t)lr->lr_mtime[0];
|
||||
|
||||
(void) printf("%sfoid %llu, mask 0x%llx\n", tab_prefix,
|
||||
(void) printf("%sfoid %llu, mask 0x%llx\n", prefix,
|
||||
(u_longlong_t)lr->lr_foid, (u_longlong_t)lr->lr_mask);
|
||||
|
||||
if (lr->lr_mask & AT_MODE) {
|
||||
(void) printf("%sAT_MODE %llo\n", tab_prefix,
|
||||
(void) printf("%sAT_MODE %llo\n", prefix,
|
||||
(longlong_t)lr->lr_mode);
|
||||
}
|
||||
|
||||
if (lr->lr_mask & AT_UID) {
|
||||
(void) printf("%sAT_UID %llu\n", tab_prefix,
|
||||
(void) printf("%sAT_UID %llu\n", prefix,
|
||||
(u_longlong_t)lr->lr_uid);
|
||||
}
|
||||
|
||||
if (lr->lr_mask & AT_GID) {
|
||||
(void) printf("%sAT_GID %llu\n", tab_prefix,
|
||||
(void) printf("%sAT_GID %llu\n", prefix,
|
||||
(u_longlong_t)lr->lr_gid);
|
||||
}
|
||||
|
||||
if (lr->lr_mask & AT_SIZE) {
|
||||
(void) printf("%sAT_SIZE %llu\n", tab_prefix,
|
||||
(void) printf("%sAT_SIZE %llu\n", prefix,
|
||||
(u_longlong_t)lr->lr_size);
|
||||
}
|
||||
|
||||
if (lr->lr_mask & AT_ATIME) {
|
||||
(void) printf("%sAT_ATIME %llu.%09llu %s", tab_prefix,
|
||||
(void) printf("%sAT_ATIME %llu.%09llu %s", prefix,
|
||||
(u_longlong_t)lr->lr_atime[0],
|
||||
(u_longlong_t)lr->lr_atime[1],
|
||||
ctime(&atime));
|
||||
}
|
||||
|
||||
if (lr->lr_mask & AT_MTIME) {
|
||||
(void) printf("%sAT_MTIME %llu.%09llu %s", tab_prefix,
|
||||
(void) printf("%sAT_MTIME %llu.%09llu %s", prefix,
|
||||
(u_longlong_t)lr->lr_mtime[0],
|
||||
(u_longlong_t)lr->lr_mtime[1],
|
||||
ctime(&mtime));
|
||||
@@ -268,48 +254,46 @@ zil_prt_rec_setattr(zilog_t *zilog, int txtype, const void *arg)
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
zil_prt_rec_acl(zilog_t *zilog, int txtype, const void *arg)
|
||||
zil_prt_rec_acl(zilog_t *zilog, int txtype, lr_acl_t *lr)
|
||||
{
|
||||
const lr_acl_t *lr = arg;
|
||||
|
||||
(void) printf("%sfoid %llu, aclcnt %llu\n", tab_prefix,
|
||||
(void) printf("%sfoid %llu, aclcnt %llu\n", prefix,
|
||||
(u_longlong_t)lr->lr_foid, (u_longlong_t)lr->lr_aclcnt);
|
||||
}
|
||||
|
||||
typedef void (*zil_prt_rec_func_t)(zilog_t *, int, const void *);
|
||||
typedef void (*zil_prt_rec_func_t)(zilog_t *, int, void *);
|
||||
typedef struct zil_rec_info {
|
||||
zil_prt_rec_func_t zri_print;
|
||||
const char *zri_name;
|
||||
char *zri_name;
|
||||
uint64_t zri_count;
|
||||
} zil_rec_info_t;
|
||||
|
||||
static zil_rec_info_t zil_rec_info[TX_MAX_TYPE] = {
|
||||
{.zri_print = NULL, .zri_name = "Total "},
|
||||
{.zri_print = zil_prt_rec_create, .zri_name = "TX_CREATE "},
|
||||
{.zri_print = zil_prt_rec_create, .zri_name = "TX_MKDIR "},
|
||||
{.zri_print = zil_prt_rec_create, .zri_name = "TX_MKXATTR "},
|
||||
{.zri_print = zil_prt_rec_create, .zri_name = "TX_SYMLINK "},
|
||||
{.zri_print = zil_prt_rec_remove, .zri_name = "TX_REMOVE "},
|
||||
{.zri_print = zil_prt_rec_remove, .zri_name = "TX_RMDIR "},
|
||||
{.zri_print = zil_prt_rec_link, .zri_name = "TX_LINK "},
|
||||
{.zri_print = zil_prt_rec_rename, .zri_name = "TX_RENAME "},
|
||||
{.zri_print = zil_prt_rec_write, .zri_name = "TX_WRITE "},
|
||||
{.zri_print = zil_prt_rec_truncate, .zri_name = "TX_TRUNCATE "},
|
||||
{.zri_print = zil_prt_rec_setattr, .zri_name = "TX_SETATTR "},
|
||||
{.zri_print = zil_prt_rec_acl, .zri_name = "TX_ACL_V0 "},
|
||||
{.zri_print = zil_prt_rec_acl, .zri_name = "TX_ACL_ACL "},
|
||||
{.zri_print = zil_prt_rec_create, .zri_name = "TX_CREATE_ACL "},
|
||||
{.zri_print = zil_prt_rec_create, .zri_name = "TX_CREATE_ATTR "},
|
||||
{.zri_print = zil_prt_rec_create, .zri_name = "TX_CREATE_ACL_ATTR "},
|
||||
{.zri_print = zil_prt_rec_create, .zri_name = "TX_MKDIR_ACL "},
|
||||
{.zri_print = zil_prt_rec_create, .zri_name = "TX_MKDIR_ATTR "},
|
||||
{.zri_print = zil_prt_rec_create, .zri_name = "TX_MKDIR_ACL_ATTR "},
|
||||
{.zri_print = zil_prt_rec_write, .zri_name = "TX_WRITE2 "},
|
||||
{ NULL, "Total " },
|
||||
{ (zil_prt_rec_func_t)zil_prt_rec_create, "TX_CREATE " },
|
||||
{ (zil_prt_rec_func_t)zil_prt_rec_create, "TX_MKDIR " },
|
||||
{ (zil_prt_rec_func_t)zil_prt_rec_create, "TX_MKXATTR " },
|
||||
{ (zil_prt_rec_func_t)zil_prt_rec_create, "TX_SYMLINK " },
|
||||
{ (zil_prt_rec_func_t)zil_prt_rec_remove, "TX_REMOVE " },
|
||||
{ (zil_prt_rec_func_t)zil_prt_rec_remove, "TX_RMDIR " },
|
||||
{ (zil_prt_rec_func_t)zil_prt_rec_link, "TX_LINK " },
|
||||
{ (zil_prt_rec_func_t)zil_prt_rec_rename, "TX_RENAME " },
|
||||
{ (zil_prt_rec_func_t)zil_prt_rec_write, "TX_WRITE " },
|
||||
{ (zil_prt_rec_func_t)zil_prt_rec_truncate, "TX_TRUNCATE " },
|
||||
{ (zil_prt_rec_func_t)zil_prt_rec_setattr, "TX_SETATTR " },
|
||||
{ (zil_prt_rec_func_t)zil_prt_rec_acl, "TX_ACL_V0 " },
|
||||
{ (zil_prt_rec_func_t)zil_prt_rec_acl, "TX_ACL_ACL " },
|
||||
{ (zil_prt_rec_func_t)zil_prt_rec_create, "TX_CREATE_ACL " },
|
||||
{ (zil_prt_rec_func_t)zil_prt_rec_create, "TX_CREATE_ATTR " },
|
||||
{ (zil_prt_rec_func_t)zil_prt_rec_create, "TX_CREATE_ACL_ATTR " },
|
||||
{ (zil_prt_rec_func_t)zil_prt_rec_create, "TX_MKDIR_ACL " },
|
||||
{ (zil_prt_rec_func_t)zil_prt_rec_create, "TX_MKDIR_ATTR " },
|
||||
{ (zil_prt_rec_func_t)zil_prt_rec_create, "TX_MKDIR_ACL_ATTR " },
|
||||
{ (zil_prt_rec_func_t)zil_prt_rec_write, "TX_WRITE2 " },
|
||||
};
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
print_log_record(zilog_t *zilog, const lr_t *lr, void *arg, uint64_t claim_txg)
|
||||
print_log_record(zilog_t *zilog, lr_t *lr, void *arg, uint64_t claim_txg)
|
||||
{
|
||||
int txtype;
|
||||
int verbose = MAX(dump_opt['d'], dump_opt['i']);
|
||||
@@ -327,13 +311,8 @@ print_log_record(zilog_t *zilog, const lr_t *lr, void *arg, uint64_t claim_txg)
|
||||
(u_longlong_t)lr->lrc_txg,
|
||||
(u_longlong_t)lr->lrc_seq);
|
||||
|
||||
if (txtype && verbose >= 3) {
|
||||
if (!zilog->zl_os->os_encrypted) {
|
||||
zil_rec_info[txtype].zri_print(zilog, txtype, lr);
|
||||
} else {
|
||||
(void) printf("%s(encrypted)\n", tab_prefix);
|
||||
}
|
||||
}
|
||||
if (txtype && verbose >= 3)
|
||||
zil_rec_info[txtype].zri_print(zilog, txtype, lr);
|
||||
|
||||
zil_rec_info[txtype].zri_count++;
|
||||
zil_rec_info[0].zri_count++;
|
||||
@@ -343,12 +322,11 @@ print_log_record(zilog_t *zilog, const lr_t *lr, void *arg, uint64_t claim_txg)
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
print_log_block(zilog_t *zilog, const blkptr_t *bp, void *arg,
|
||||
uint64_t claim_txg)
|
||||
print_log_block(zilog_t *zilog, blkptr_t *bp, void *arg, uint64_t claim_txg)
|
||||
{
|
||||
char blkbuf[BP_SPRINTF_LEN + 10];
|
||||
int verbose = MAX(dump_opt['d'], dump_opt['i']);
|
||||
const char *claim;
|
||||
char *claim;
|
||||
|
||||
if (verbose <= 3)
|
||||
return (0);
|
||||
@@ -363,7 +341,7 @@ print_log_block(zilog_t *zilog, const blkptr_t *bp, void *arg,
|
||||
|
||||
if (claim_txg != 0)
|
||||
claim = "already claimed";
|
||||
else if (bp->blk_birth >= spa_min_claim_txg(zilog->zl_spa))
|
||||
else if (bp->blk_birth >= spa_first_txg(zilog->zl_spa))
|
||||
claim = "will claim";
|
||||
else
|
||||
claim = "won't claim";
|
||||
@@ -377,7 +355,7 @@ print_log_block(zilog_t *zilog, const blkptr_t *bp, void *arg,
|
||||
static void
|
||||
print_log_stats(int verbose)
|
||||
{
|
||||
unsigned i, w, p10;
|
||||
int i, w, p10;
|
||||
|
||||
if (verbose > 3)
|
||||
(void) printf("\n");
|
||||
@@ -418,15 +396,10 @@ dump_intent_log(zilog_t *zilog)
|
||||
for (i = 0; i < TX_MAX_TYPE; i++)
|
||||
zil_rec_info[i].zri_count = 0;
|
||||
|
||||
/* see comment in zil_claim() or zil_check_log_chain() */
|
||||
if (zilog->zl_spa->spa_uberblock.ub_checkpoint_txg != 0 &&
|
||||
zh->zh_claim_txg == 0)
|
||||
return;
|
||||
|
||||
if (verbose >= 2) {
|
||||
(void) printf("\n");
|
||||
(void) zil_parse(zilog, print_log_block, print_log_record, NULL,
|
||||
zh->zh_claim_txg, B_FALSE);
|
||||
zh->zh_claim_txg);
|
||||
print_log_stats(verbose);
|
||||
}
|
||||
}
|
||||
|
||||
+53
-12
@@ -1,10 +1,10 @@
|
||||
include $(top_srcdir)/config/Rules.am
|
||||
include $(top_srcdir)/config/Shellcheck.am
|
||||
|
||||
AM_CFLAGS += $(LIBUDEV_CFLAGS) $(LIBUUID_CFLAGS)
|
||||
DEFAULT_INCLUDES += \
|
||||
-I$(top_srcdir)/include \
|
||||
-I$(top_srcdir)/lib/libspl/include
|
||||
|
||||
SUBDIRS = zed.d
|
||||
SHELLCHECKDIRS = $(SUBDIRS)
|
||||
EXTRA_DIST = zed.d/README
|
||||
|
||||
sbin_PROGRAMS = zed
|
||||
|
||||
@@ -40,14 +40,55 @@ FMA_SRC = \
|
||||
zed_SOURCES = $(ZED_SRC) $(FMA_SRC)
|
||||
|
||||
zed_LDADD = \
|
||||
$(abs_top_builddir)/lib/libzfs/libzfs.la \
|
||||
$(abs_top_builddir)/lib/libzfs_core/libzfs_core.la \
|
||||
$(abs_top_builddir)/lib/libnvpair/libnvpair.la \
|
||||
$(abs_top_builddir)/lib/libuutil/libuutil.la
|
||||
$(top_builddir)/lib/libavl/libavl.la \
|
||||
$(top_builddir)/lib/libnvpair/libnvpair.la \
|
||||
$(top_builddir)/lib/libspl/libspl.la \
|
||||
$(top_builddir)/lib/libuutil/libuutil.la \
|
||||
$(top_builddir)/lib/libzpool/libzpool.la \
|
||||
$(top_builddir)/lib/libzfs/libzfs.la \
|
||||
$(top_builddir)/lib/libzfs_core/libzfs_core.la
|
||||
|
||||
zed_LDADD += -lrt $(LIBATOMIC_LIBS) $(LIBUDEV_LIBS) $(LIBUUID_LIBS)
|
||||
zed_LDFLAGS = -pthread
|
||||
zed_LDFLAGS = -lrt -pthread
|
||||
|
||||
EXTRA_DIST = agents/README.md
|
||||
zedconfdir = $(sysconfdir)/zfs/zed.d
|
||||
|
||||
include $(top_srcdir)/config/CppCheck.am
|
||||
dist_zedconf_DATA = \
|
||||
zed.d/zed-functions.sh \
|
||||
zed.d/zed.rc
|
||||
|
||||
zedexecdir = $(libexecdir)/zfs/zed.d
|
||||
|
||||
dist_zedexec_SCRIPTS = \
|
||||
zed.d/all-debug.sh \
|
||||
zed.d/all-syslog.sh \
|
||||
zed.d/data-notify.sh \
|
||||
zed.d/generic-notify.sh \
|
||||
zed.d/resilver_finish-notify.sh \
|
||||
zed.d/scrub_finish-notify.sh \
|
||||
zed.d/statechange-led.sh \
|
||||
zed.d/statechange-notify.sh \
|
||||
zed.d/vdev_clear-led.sh \
|
||||
zed.d/vdev_attach-led.sh \
|
||||
zed.d/pool_import-led.sh \
|
||||
zed.d/resilver_finish-start-scrub.sh
|
||||
|
||||
zedconfdefaults = \
|
||||
all-syslog.sh \
|
||||
data-notify.sh \
|
||||
resilver_finish-notify.sh \
|
||||
scrub_finish-notify.sh \
|
||||
statechange-led.sh \
|
||||
statechange-notify.sh \
|
||||
vdev_clear-led.sh \
|
||||
vdev_attach-led.sh \
|
||||
pool_import-led.sh \
|
||||
resilver_finish-start-scrub.sh
|
||||
|
||||
install-data-hook:
|
||||
$(MKDIR_P) "$(DESTDIR)$(zedconfdir)"
|
||||
for f in $(zedconfdefaults); do \
|
||||
test -f "$(DESTDIR)$(zedconfdir)/$${f}" -o \
|
||||
-L "$(DESTDIR)$(zedconfdir)/$${f}" || \
|
||||
ln -s "$(zedexecdir)/$${f}" "$(DESTDIR)$(zedconfdir)"; \
|
||||
done
|
||||
chmod 0600 "$(DESTDIR)$(zedconfdir)/zed.rc"
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
* This file implements the minimal FMD module API required to support the
|
||||
* This file imlements the minimal FMD module API required to support the
|
||||
* fault logic modules in ZED. This support includes module registration,
|
||||
* memory allocation, module property accessors, basic case management,
|
||||
* one-shot timers and SERD engines.
|
||||
@@ -599,7 +599,6 @@ fmd_timer_install(fmd_hdl_t *hdl, void *arg, fmd_event_t *ep, hrtime_t delta)
|
||||
sev.sigev_notify_function = _timer_notify;
|
||||
sev.sigev_notify_attributes = NULL;
|
||||
sev.sigev_value.sival_ptr = ftp;
|
||||
sev.sigev_signo = 0;
|
||||
|
||||
timer_create(CLOCK_REALTIME, &sev, &ftp->ft_tid);
|
||||
timer_settime(ftp->ft_tid, 0, &its, NULL);
|
||||
|
||||
@@ -281,7 +281,7 @@ fmd_serd_eng_empty(fmd_serd_eng_t *sgp)
|
||||
void
|
||||
fmd_serd_eng_reset(fmd_serd_eng_t *sgp)
|
||||
{
|
||||
serd_log_msg(" SERD Engine: resetting %s", sgp->sg_name);
|
||||
serd_log_msg(" SERD Engine: reseting %s", sgp->sg_name);
|
||||
|
||||
while (sgp->sg_count != 0)
|
||||
fmd_serd_eng_discard(sgp, list_head(&sgp->sg_list));
|
||||
|
||||
+43
-131
@@ -12,8 +12,6 @@
|
||||
|
||||
/*
|
||||
* Copyright (c) 2016, Intel Corporation.
|
||||
* Copyright (c) 2018, loli10K <ezomori.nozomu@gmail.com>
|
||||
* Copyright (c) 2021 Hewlett Packard Enterprise Development LP
|
||||
*/
|
||||
|
||||
#include <libnvpair.h>
|
||||
@@ -55,95 +53,38 @@ pthread_t g_agents_tid;
|
||||
libzfs_handle_t *g_zfs_hdl;
|
||||
|
||||
/* guid search data */
|
||||
typedef enum device_type {
|
||||
DEVICE_TYPE_L2ARC, /* l2arc device */
|
||||
DEVICE_TYPE_SPARE, /* spare device */
|
||||
DEVICE_TYPE_PRIMARY /* any primary pool storage device */
|
||||
} device_type_t;
|
||||
|
||||
typedef struct guid_search {
|
||||
uint64_t gs_pool_guid;
|
||||
uint64_t gs_vdev_guid;
|
||||
char *gs_devid;
|
||||
device_type_t gs_vdev_type;
|
||||
uint64_t gs_vdev_expandtime; /* vdev expansion time */
|
||||
} guid_search_t;
|
||||
|
||||
/*
|
||||
* Walks the vdev tree recursively looking for a matching devid.
|
||||
* Returns B_TRUE as soon as a matching device is found, B_FALSE otherwise.
|
||||
*/
|
||||
static boolean_t
|
||||
static void
|
||||
zfs_agent_iter_vdev(zpool_handle_t *zhp, nvlist_t *nvl, void *arg)
|
||||
{
|
||||
guid_search_t *gsp = arg;
|
||||
char *path = NULL;
|
||||
uint_t c, children;
|
||||
nvlist_t **child;
|
||||
uint64_t vdev_guid;
|
||||
|
||||
/*
|
||||
* First iterate over any children.
|
||||
*/
|
||||
if (nvlist_lookup_nvlist_array(nvl, ZPOOL_CONFIG_CHILDREN,
|
||||
&child, &children) == 0) {
|
||||
for (c = 0; c < children; c++) {
|
||||
if (zfs_agent_iter_vdev(zhp, child[c], gsp)) {
|
||||
gsp->gs_vdev_type = DEVICE_TYPE_PRIMARY;
|
||||
return (B_TRUE);
|
||||
}
|
||||
}
|
||||
for (c = 0; c < children; c++)
|
||||
zfs_agent_iter_vdev(zhp, child[c], gsp);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* Iterate over any spares and cache devices
|
||||
* On a devid match, grab the vdev guid
|
||||
*/
|
||||
if (nvlist_lookup_nvlist_array(nvl, ZPOOL_CONFIG_SPARES,
|
||||
&child, &children) == 0) {
|
||||
for (c = 0; c < children; c++) {
|
||||
if (zfs_agent_iter_vdev(zhp, child[c], gsp)) {
|
||||
gsp->gs_vdev_type = DEVICE_TYPE_SPARE;
|
||||
return (B_TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nvlist_lookup_nvlist_array(nvl, ZPOOL_CONFIG_L2CACHE,
|
||||
&child, &children) == 0) {
|
||||
for (c = 0; c < children; c++) {
|
||||
if (zfs_agent_iter_vdev(zhp, child[c], gsp)) {
|
||||
gsp->gs_vdev_type = DEVICE_TYPE_L2ARC;
|
||||
return (B_TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* On a devid match, grab the vdev guid and expansion time, if any.
|
||||
*/
|
||||
if (gsp->gs_devid != NULL &&
|
||||
if ((gsp->gs_vdev_guid == 0) &&
|
||||
(nvlist_lookup_string(nvl, ZPOOL_CONFIG_DEVID, &path) == 0) &&
|
||||
(strcmp(gsp->gs_devid, path) == 0)) {
|
||||
(void) nvlist_lookup_uint64(nvl, ZPOOL_CONFIG_GUID,
|
||||
&gsp->gs_vdev_guid);
|
||||
(void) nvlist_lookup_uint64(nvl, ZPOOL_CONFIG_EXPANSION_TIME,
|
||||
&gsp->gs_vdev_expandtime);
|
||||
return (B_TRUE);
|
||||
}
|
||||
/*
|
||||
* Otherwise, on a vdev guid match, grab the devid and expansion
|
||||
* time. The devid might be missing on removal since its not part
|
||||
* of blkid cache and L2ARC VDEV does not contain pool guid in its
|
||||
* blkid, so this is a special case for L2ARC VDEV.
|
||||
*/
|
||||
else if (gsp->gs_vdev_guid != 0 && gsp->gs_devid == NULL &&
|
||||
nvlist_lookup_uint64(nvl, ZPOOL_CONFIG_GUID, &vdev_guid) == 0 &&
|
||||
gsp->gs_vdev_guid == vdev_guid) {
|
||||
(void) nvlist_lookup_string(nvl, ZPOOL_CONFIG_DEVID,
|
||||
&gsp->gs_devid);
|
||||
(void) nvlist_lookup_uint64(nvl, ZPOOL_CONFIG_EXPANSION_TIME,
|
||||
&gsp->gs_vdev_expandtime);
|
||||
return (B_TRUE);
|
||||
}
|
||||
|
||||
return (B_FALSE);
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -158,19 +99,19 @@ zfs_agent_iter_pool(zpool_handle_t *zhp, void *arg)
|
||||
if ((config = zpool_get_config(zhp, NULL)) != NULL) {
|
||||
if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
|
||||
&nvl) == 0) {
|
||||
(void) zfs_agent_iter_vdev(zhp, nvl, gsp);
|
||||
zfs_agent_iter_vdev(zhp, nvl, gsp);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* if a match was found then grab the pool guid
|
||||
*/
|
||||
if (gsp->gs_vdev_guid && gsp->gs_devid) {
|
||||
if (gsp->gs_vdev_guid) {
|
||||
(void) nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID,
|
||||
&gsp->gs_pool_guid);
|
||||
}
|
||||
|
||||
zpool_close(zhp);
|
||||
return (gsp->gs_devid != NULL && gsp->gs_vdev_guid != 0);
|
||||
return (gsp->gs_vdev_guid != 0);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -194,12 +135,10 @@ zfs_agent_post_event(const char *class, const char *subclass, nvlist_t *nvl)
|
||||
}
|
||||
|
||||
/*
|
||||
* On Linux, we don't get the expected FM_RESOURCE_REMOVED ereport
|
||||
* from the vdev_disk layer after a hot unplug. Fortunately we do
|
||||
* get an EC_DEV_REMOVE from our disk monitor and it is a suitable
|
||||
* On ZFS on Linux, we don't get the expected FM_RESOURCE_REMOVED
|
||||
* ereport from vdev_disk layer after a hot unplug. Fortunately we
|
||||
* get a EC_DEV_REMOVE from our disk monitor and it is a suitable
|
||||
* proxy so we remap it here for the benefit of the diagnosis engine.
|
||||
* Starting in OpenZFS 2.0, we do get FM_RESOURCE_REMOVED from the spa
|
||||
* layer. Processing multiple FM_RESOURCE_REMOVED events is not harmful.
|
||||
*/
|
||||
if ((strcmp(class, EC_DEV_REMOVE) == 0) &&
|
||||
(strcmp(subclass, ESC_DISK) == 0) &&
|
||||
@@ -209,79 +148,38 @@ zfs_agent_post_event(const char *class, const char *subclass, nvlist_t *nvl)
|
||||
struct timeval tv;
|
||||
int64_t tod[2];
|
||||
uint64_t pool_guid = 0, vdev_guid = 0;
|
||||
guid_search_t search = { 0 };
|
||||
device_type_t devtype = DEVICE_TYPE_PRIMARY;
|
||||
char *devid = NULL;
|
||||
|
||||
class = "resource.fs.zfs.removed";
|
||||
subclass = "";
|
||||
|
||||
(void) nvlist_add_string(payload, FM_CLASS, class);
|
||||
(void) nvlist_lookup_string(nvl, DEV_IDENTIFIER, &devid);
|
||||
(void) nvlist_lookup_uint64(nvl, ZFS_EV_POOL_GUID, &pool_guid);
|
||||
(void) nvlist_lookup_uint64(nvl, ZFS_EV_VDEV_GUID, &vdev_guid);
|
||||
|
||||
(void) gettimeofday(&tv, NULL);
|
||||
tod[0] = tv.tv_sec;
|
||||
tod[1] = tv.tv_usec;
|
||||
(void) nvlist_add_int64_array(payload, FM_EREPORT_TIME, tod, 2);
|
||||
|
||||
/*
|
||||
* If devid is missing but vdev_guid is available, find devid
|
||||
* and pool_guid from vdev_guid.
|
||||
* For multipath, spare and l2arc devices ZFS_EV_VDEV_GUID or
|
||||
* ZFS_EV_POOL_GUID may be missing so find them.
|
||||
* For multipath, ZFS_EV_VDEV_GUID is missing so find it.
|
||||
*/
|
||||
if (devid == NULL || pool_guid == 0 || vdev_guid == 0) {
|
||||
if (devid == NULL)
|
||||
search.gs_vdev_guid = vdev_guid;
|
||||
else
|
||||
search.gs_devid = devid;
|
||||
zpool_iter(g_zfs_hdl, zfs_agent_iter_pool, &search);
|
||||
if (devid == NULL)
|
||||
devid = search.gs_devid;
|
||||
if (pool_guid == 0)
|
||||
pool_guid = search.gs_pool_guid;
|
||||
if (vdev_guid == 0)
|
||||
vdev_guid = search.gs_vdev_guid;
|
||||
devtype = search.gs_vdev_type;
|
||||
}
|
||||
if (vdev_guid == 0) {
|
||||
guid_search_t search = { 0 };
|
||||
|
||||
/*
|
||||
* We want to avoid reporting "remove" events coming from
|
||||
* libudev for VDEVs which were expanded recently (10s) and
|
||||
* avoid activating spares in response to partitions being
|
||||
* deleted and created in rapid succession.
|
||||
*/
|
||||
if (search.gs_vdev_expandtime != 0 &&
|
||||
search.gs_vdev_expandtime + 10 > tv.tv_sec) {
|
||||
zed_log_msg(LOG_INFO, "agent post event: ignoring '%s' "
|
||||
"for recently expanded device '%s'", EC_DEV_REMOVE,
|
||||
devid);
|
||||
fnvlist_free(payload);
|
||||
free(event);
|
||||
goto out;
|
||||
(void) nvlist_lookup_string(nvl, DEV_IDENTIFIER,
|
||||
&search.gs_devid);
|
||||
|
||||
(void) zpool_iter(g_zfs_hdl, zfs_agent_iter_pool,
|
||||
&search);
|
||||
pool_guid = search.gs_pool_guid;
|
||||
vdev_guid = search.gs_vdev_guid;
|
||||
}
|
||||
|
||||
(void) nvlist_add_uint64(payload,
|
||||
FM_EREPORT_PAYLOAD_ZFS_POOL_GUID, pool_guid);
|
||||
(void) nvlist_add_uint64(payload,
|
||||
FM_EREPORT_PAYLOAD_ZFS_VDEV_GUID, vdev_guid);
|
||||
switch (devtype) {
|
||||
case DEVICE_TYPE_L2ARC:
|
||||
(void) nvlist_add_string(payload,
|
||||
FM_EREPORT_PAYLOAD_ZFS_VDEV_TYPE,
|
||||
VDEV_TYPE_L2CACHE);
|
||||
break;
|
||||
case DEVICE_TYPE_SPARE:
|
||||
(void) nvlist_add_string(payload,
|
||||
FM_EREPORT_PAYLOAD_ZFS_VDEV_TYPE, VDEV_TYPE_SPARE);
|
||||
break;
|
||||
case DEVICE_TYPE_PRIMARY:
|
||||
(void) nvlist_add_string(payload,
|
||||
FM_EREPORT_PAYLOAD_ZFS_VDEV_TYPE, VDEV_TYPE_DISK);
|
||||
break;
|
||||
}
|
||||
|
||||
(void) gettimeofday(&tv, NULL);
|
||||
tod[0] = tv.tv_sec;
|
||||
tod[1] = tv.tv_usec;
|
||||
(void) nvlist_add_int64_array(payload, FM_EREPORT_TIME, tod, 2);
|
||||
|
||||
zed_log_msg(LOG_INFO, "agent post event: mapping '%s' to '%s'",
|
||||
EC_DEV_REMOVE, class);
|
||||
@@ -295,7 +193,6 @@ zfs_agent_post_event(const char *class, const char *subclass, nvlist_t *nvl)
|
||||
list_insert_tail(&agent_events, event);
|
||||
(void) pthread_mutex_unlock(&agent_lock);
|
||||
|
||||
out:
|
||||
(void) pthread_cond_signal(&agent_cond);
|
||||
}
|
||||
|
||||
@@ -416,7 +313,6 @@ zfs_agent_init(libzfs_handle_t *zfs_hdl)
|
||||
list_destroy(&agent_events);
|
||||
zed_log_die("Failed to initialize agents");
|
||||
}
|
||||
pthread_setname_np(g_agents_tid, "agents");
|
||||
}
|
||||
|
||||
void
|
||||
@@ -454,3 +350,19 @@ zfs_agent_fini(void)
|
||||
|
||||
g_zfs_hdl = NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* In ZED context, all the FMA agents run in the same thread
|
||||
* and do not require a unique libzfs instance. Modules should
|
||||
* use these stubs.
|
||||
*/
|
||||
libzfs_handle_t *
|
||||
__libzfs_init(void)
|
||||
{
|
||||
return (g_zfs_hdl);
|
||||
}
|
||||
|
||||
void
|
||||
__libzfs_fini(libzfs_handle_t *hdl)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -39,6 +39,13 @@ extern int zfs_slm_init(void);
|
||||
extern void zfs_slm_fini(void);
|
||||
extern void zfs_slm_event(const char *, const char *, nvlist_t *);
|
||||
|
||||
/*
|
||||
* In ZED context, all the FMA agents run in the same thread
|
||||
* and do not require a unique libzfs instance.
|
||||
*/
|
||||
extern libzfs_handle_t *__libzfs_init(void);
|
||||
extern void __libzfs_fini(libzfs_handle_t *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <libuutil.h>
|
||||
#include <libzfs.h>
|
||||
@@ -35,7 +34,6 @@
|
||||
#include <sys/fs/zfs.h>
|
||||
#include <sys/fm/protocol.h>
|
||||
#include <sys/fm/fs/zfs.h>
|
||||
#include <sys/zio.h>
|
||||
|
||||
#include "zfs_agents.h"
|
||||
#include "fmd_api.h"
|
||||
@@ -169,12 +167,14 @@ zfs_case_unserialize(fmd_hdl_t *hdl, fmd_case_t *cp)
|
||||
static void
|
||||
zfs_mark_vdev(uint64_t pool_guid, nvlist_t *vd, er_timeval_t *loaded)
|
||||
{
|
||||
uint64_t vdev_guid = 0;
|
||||
uint64_t vdev_guid;
|
||||
uint_t c, children;
|
||||
nvlist_t **child;
|
||||
zfs_case_t *zcp;
|
||||
int ret;
|
||||
|
||||
(void) nvlist_lookup_uint64(vd, ZPOOL_CONFIG_GUID, &vdev_guid);
|
||||
ret = nvlist_lookup_uint64(vd, ZPOOL_CONFIG_GUID, &vdev_guid);
|
||||
assert(ret == 0);
|
||||
|
||||
/*
|
||||
* Mark any cases associated with this (pool, vdev) pair.
|
||||
@@ -253,10 +253,7 @@ zfs_mark_pool(zpool_handle_t *zhp, void *unused)
|
||||
}
|
||||
|
||||
ret = nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &vd);
|
||||
if (ret) {
|
||||
zpool_close(zhp);
|
||||
return (-1);
|
||||
}
|
||||
assert(ret == 0);
|
||||
|
||||
zfs_mark_vdev(pool_guid, vd, &loaded);
|
||||
|
||||
@@ -380,6 +377,11 @@ zfs_case_solve(fmd_hdl_t *hdl, zfs_case_t *zcp, const char *faultname,
|
||||
nvlist_t *detector, *fault;
|
||||
boolean_t serialize;
|
||||
nvlist_t *fru = NULL;
|
||||
#ifdef HAVE_LIBTOPO
|
||||
nvlist_t *fmri;
|
||||
topo_hdl_t *thp;
|
||||
int err;
|
||||
#endif
|
||||
fmd_hdl_debug(hdl, "solving fault '%s'", faultname);
|
||||
|
||||
/*
|
||||
@@ -398,6 +400,64 @@ zfs_case_solve(fmd_hdl_t *hdl, zfs_case_t *zcp, const char *faultname,
|
||||
zcp->zc_data.zc_vdev_guid);
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBTOPO
|
||||
/*
|
||||
* We also want to make sure that the detector (pool or vdev) properly
|
||||
* reflects the diagnosed state, when the fault corresponds to internal
|
||||
* ZFS state (i.e. not checksum or I/O error-induced). Otherwise, a
|
||||
* device which was unavailable early in boot (because the driver/file
|
||||
* wasn't available) and is now healthy will be mis-diagnosed.
|
||||
*/
|
||||
if (!fmd_nvl_fmri_present(hdl, detector) ||
|
||||
(checkunusable && !fmd_nvl_fmri_unusable(hdl, detector))) {
|
||||
fmd_case_close(hdl, zcp->zc_case);
|
||||
nvlist_free(detector);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
fru = NULL;
|
||||
if (zcp->zc_fru != NULL &&
|
||||
(thp = fmd_hdl_topo_hold(hdl, TOPO_VERSION)) != NULL) {
|
||||
/*
|
||||
* If the vdev had an associated FRU, then get the FRU nvlist
|
||||
* from the topo handle and use that in the suspect list. We
|
||||
* explicitly lookup the FRU because the fmri reported from the
|
||||
* kernel may not have up to date details about the disk itself
|
||||
* (serial, part, etc).
|
||||
*/
|
||||
if (topo_fmri_str2nvl(thp, zcp->zc_fru, &fmri, &err) == 0) {
|
||||
libzfs_handle_t *zhdl = fmd_hdl_getspecific(hdl);
|
||||
|
||||
/*
|
||||
* If the disk is part of the system chassis, but the
|
||||
* FRU indicates a different chassis ID than our
|
||||
* current system, then ignore the error. This
|
||||
* indicates that the device was part of another
|
||||
* cluster head, and for obvious reasons cannot be
|
||||
* imported on this system.
|
||||
*/
|
||||
if (libzfs_fru_notself(zhdl, zcp->zc_fru)) {
|
||||
fmd_case_close(hdl, zcp->zc_case);
|
||||
nvlist_free(fmri);
|
||||
fmd_hdl_topo_rele(hdl, thp);
|
||||
nvlist_free(detector);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* If the device is no longer present on the system, or
|
||||
* topo_fmri_fru() fails for other reasons, then fall
|
||||
* back to the fmri specified in the vdev.
|
||||
*/
|
||||
if (topo_fmri_fru(thp, fmri, &fru, &err) != 0)
|
||||
fru = fmd_nvl_dup(hdl, fmri, FMD_SLEEP);
|
||||
nvlist_free(fmri);
|
||||
}
|
||||
|
||||
fmd_hdl_topo_rele(hdl, thp);
|
||||
}
|
||||
#endif
|
||||
fault = fmd_nvl_create_fault(hdl, faultname, 100, detector,
|
||||
fru, detector);
|
||||
fmd_case_add_suspect(hdl, zcp->zc_case, fault);
|
||||
@@ -774,8 +834,6 @@ zfs_fm_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class)
|
||||
ZFS_MAKE_EREPORT(FM_EREPORT_ZFS_PROBE_FAILURE))) {
|
||||
char *failmode = NULL;
|
||||
boolean_t checkremove = B_FALSE;
|
||||
uint32_t pri = 0;
|
||||
int32_t flags = 0;
|
||||
|
||||
/*
|
||||
* If this is a checksum or I/O error, then toss it into the
|
||||
@@ -798,23 +856,6 @@ zfs_fm_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class)
|
||||
checkremove = B_TRUE;
|
||||
} else if (fmd_nvl_class_match(hdl, nvl,
|
||||
ZFS_MAKE_EREPORT(FM_EREPORT_ZFS_CHECKSUM))) {
|
||||
/*
|
||||
* We ignore ereports for checksum errors generated by
|
||||
* scrub/resilver I/O to avoid potentially further
|
||||
* degrading the pool while it's being repaired.
|
||||
*/
|
||||
if (((nvlist_lookup_uint32(nvl,
|
||||
FM_EREPORT_PAYLOAD_ZFS_ZIO_PRIORITY, &pri) == 0) &&
|
||||
(pri == ZIO_PRIORITY_SCRUB ||
|
||||
pri == ZIO_PRIORITY_REBUILD)) ||
|
||||
((nvlist_lookup_int32(nvl,
|
||||
FM_EREPORT_PAYLOAD_ZFS_ZIO_FLAGS, &flags) == 0) &&
|
||||
(flags & (ZIO_FLAG_SCRUB | ZIO_FLAG_RESILVER)))) {
|
||||
fmd_hdl_debug(hdl, "ignoring '%s' for "
|
||||
"scrub/resilver I/O", class);
|
||||
return;
|
||||
}
|
||||
|
||||
if (zcp->zc_data.zc_serd_checksum[0] == '\0') {
|
||||
zfs_serd_name(zcp->zc_data.zc_serd_checksum,
|
||||
pool_guid, vdev_guid, "checksum");
|
||||
@@ -941,27 +982,27 @@ _zfs_diagnosis_init(fmd_hdl_t *hdl)
|
||||
{
|
||||
libzfs_handle_t *zhdl;
|
||||
|
||||
if ((zhdl = libzfs_init()) == NULL)
|
||||
if ((zhdl = __libzfs_init()) == NULL)
|
||||
return;
|
||||
|
||||
if ((zfs_case_pool = uu_list_pool_create("zfs_case_pool",
|
||||
sizeof (zfs_case_t), offsetof(zfs_case_t, zc_node),
|
||||
NULL, UU_LIST_POOL_DEBUG)) == NULL) {
|
||||
libzfs_fini(zhdl);
|
||||
__libzfs_fini(zhdl);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((zfs_cases = uu_list_create(zfs_case_pool, NULL,
|
||||
UU_LIST_DEBUG)) == NULL) {
|
||||
uu_list_pool_destroy(zfs_case_pool);
|
||||
libzfs_fini(zhdl);
|
||||
__libzfs_fini(zhdl);
|
||||
return;
|
||||
}
|
||||
|
||||
if (fmd_hdl_register(hdl, FMD_API_VERSION, &fmd_info) != 0) {
|
||||
uu_list_destroy(zfs_cases);
|
||||
uu_list_pool_destroy(zfs_case_pool);
|
||||
libzfs_fini(zhdl);
|
||||
__libzfs_fini(zhdl);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -997,5 +1038,5 @@ _zfs_diagnosis_fini(fmd_hdl_t *hdl)
|
||||
uu_list_pool_destroy(zfs_case_pool);
|
||||
|
||||
zhdl = fmd_hdl_getspecific(hdl);
|
||||
libzfs_fini(zhdl);
|
||||
__libzfs_fini(zhdl);
|
||||
}
|
||||
|
||||
+105
-487
@@ -23,7 +23,6 @@
|
||||
* Copyright (c) 2012 by Delphix. All rights reserved.
|
||||
* Copyright 2014 Nexenta Systems, Inc. All rights reserved.
|
||||
* Copyright (c) 2016, 2017, Intel Corporation.
|
||||
* Copyright (c) 2017 Open-E, Inc. All Rights Reserved.
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -63,14 +62,17 @@
|
||||
* If the device could not be replaced, then the second online attempt will
|
||||
* trigger the FMA fault that we skipped earlier.
|
||||
*
|
||||
* On Linux udev provides a disk insert for both the disk and the partition.
|
||||
* ZFS on Linux porting notes:
|
||||
* In lieu of a thread pool, just spawn a thread on demmand.
|
||||
* Linux udev provides a disk insert for both the disk and the partition
|
||||
*
|
||||
*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <devid.h>
|
||||
#include <fcntl.h>
|
||||
#include <libnvpair.h>
|
||||
#include <libzfs.h>
|
||||
#include <libzutil.h>
|
||||
#include <limits.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
@@ -80,10 +82,8 @@
|
||||
#include <sys/sunddi.h>
|
||||
#include <sys/sysevent/eventdefs.h>
|
||||
#include <sys/sysevent/dev.h>
|
||||
#include <thread_pool.h>
|
||||
#include <pthread.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include "zfs_agents.h"
|
||||
#include "../zed_log.h"
|
||||
|
||||
@@ -96,12 +96,12 @@ typedef void (*zfs_process_func_t)(zpool_handle_t *, nvlist_t *, boolean_t);
|
||||
libzfs_handle_t *g_zfshdl;
|
||||
list_t g_pool_list; /* list of unavailable pools at initialization */
|
||||
list_t g_device_list; /* list of disks with asynchronous label request */
|
||||
tpool_t *g_tpool;
|
||||
boolean_t g_enumeration_done;
|
||||
pthread_t g_zfs_tid; /* zfs_enum_pools() thread */
|
||||
pthread_t g_zfs_tid;
|
||||
|
||||
typedef struct unavailpool {
|
||||
zpool_handle_t *uap_zhp;
|
||||
pthread_t uap_enable_tid; /* dataset enable thread if activated */
|
||||
list_node_t uap_node;
|
||||
} unavailpool_t;
|
||||
|
||||
@@ -134,6 +134,7 @@ zfs_unavail_pool(zpool_handle_t *zhp, void *data)
|
||||
unavailpool_t *uap;
|
||||
uap = malloc(sizeof (unavailpool_t));
|
||||
uap->uap_zhp = zhp;
|
||||
uap->uap_enable_tid = 0;
|
||||
list_insert_tail((list_t *)data, uap);
|
||||
} else {
|
||||
zpool_close(zhp);
|
||||
@@ -154,7 +155,7 @@ zfs_unavail_pool(zpool_handle_t *zhp, void *data)
|
||||
* 1. physical match with no fs, no partition
|
||||
* tag it top, partition disk
|
||||
*
|
||||
* 2. physical match again, see partition and tag
|
||||
* 2. physical match again, see partion and tag
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -183,16 +184,14 @@ zfs_process_add(zpool_handle_t *zhp, nvlist_t *vdev, boolean_t labeled)
|
||||
nvlist_t *nvroot, *newvd;
|
||||
pendingdev_t *device;
|
||||
uint64_t wholedisk = 0ULL;
|
||||
uint64_t offline = 0ULL, faulted = 0ULL;
|
||||
uint64_t offline = 0ULL;
|
||||
uint64_t guid = 0ULL;
|
||||
uint64_t is_spare = 0;
|
||||
char *physpath = NULL, *new_devid = NULL, *enc_sysfs_path = NULL;
|
||||
char rawpath[PATH_MAX], fullpath[PATH_MAX];
|
||||
char devpath[PATH_MAX];
|
||||
int ret;
|
||||
int online_flag = ZFS_ONLINE_CHECKREMOVE | ZFS_ONLINE_UNSPARE;
|
||||
boolean_t is_sd = B_FALSE;
|
||||
boolean_t is_mpath_wholedisk = B_FALSE;
|
||||
int is_dm = 0;
|
||||
int is_sd = 0;
|
||||
uint_t c;
|
||||
vdev_stat_t *vs;
|
||||
|
||||
@@ -213,74 +212,15 @@ zfs_process_add(zpool_handle_t *zhp, nvlist_t *vdev, boolean_t labeled)
|
||||
&enc_sysfs_path);
|
||||
(void) nvlist_lookup_uint64(vdev, ZPOOL_CONFIG_WHOLE_DISK, &wholedisk);
|
||||
(void) nvlist_lookup_uint64(vdev, ZPOOL_CONFIG_OFFLINE, &offline);
|
||||
(void) nvlist_lookup_uint64(vdev, ZPOOL_CONFIG_FAULTED, &faulted);
|
||||
|
||||
(void) nvlist_lookup_uint64(vdev, ZPOOL_CONFIG_GUID, &guid);
|
||||
(void) nvlist_lookup_uint64(vdev, ZPOOL_CONFIG_IS_SPARE, &is_spare);
|
||||
|
||||
/*
|
||||
* Special case:
|
||||
*
|
||||
* We've seen times where a disk won't have a ZPOOL_CONFIG_PHYS_PATH
|
||||
* entry in their config. For example, on this force-faulted disk:
|
||||
*
|
||||
* children[0]:
|
||||
* type: 'disk'
|
||||
* id: 0
|
||||
* guid: 14309659774640089719
|
||||
* path: '/dev/disk/by-vdev/L28'
|
||||
* whole_disk: 0
|
||||
* DTL: 654
|
||||
* create_txg: 4
|
||||
* com.delphix:vdev_zap_leaf: 1161
|
||||
* faulted: 1
|
||||
* aux_state: 'external'
|
||||
* children[1]:
|
||||
* type: 'disk'
|
||||
* id: 1
|
||||
* guid: 16002508084177980912
|
||||
* path: '/dev/disk/by-vdev/L29'
|
||||
* devid: 'dm-uuid-mpath-35000c500a61d68a3'
|
||||
* phys_path: 'L29'
|
||||
* vdev_enc_sysfs_path: '/sys/class/enclosure/0:0:1:0/SLOT 30 32'
|
||||
* whole_disk: 0
|
||||
* DTL: 1028
|
||||
* create_txg: 4
|
||||
* com.delphix:vdev_zap_leaf: 131
|
||||
*
|
||||
* If the disk's path is a /dev/disk/by-vdev/ path, then we can infer
|
||||
* the ZPOOL_CONFIG_PHYS_PATH from the by-vdev disk name.
|
||||
*/
|
||||
if (physpath == NULL && path != NULL) {
|
||||
/* If path begins with "/dev/disk/by-vdev/" ... */
|
||||
if (strncmp(path, DEV_BYVDEV_PATH,
|
||||
strlen(DEV_BYVDEV_PATH)) == 0) {
|
||||
/* Set physpath to the char after "/dev/disk/by-vdev" */
|
||||
physpath = &path[strlen(DEV_BYVDEV_PATH)];
|
||||
}
|
||||
}
|
||||
if (offline)
|
||||
return; /* don't intervene if it was taken offline */
|
||||
|
||||
/*
|
||||
* We don't want to autoreplace offlined disks. However, we do want to
|
||||
* replace force-faulted disks (`zpool offline -f`). Force-faulted
|
||||
* disks have both offline=1 and faulted=1 in the nvlist.
|
||||
*/
|
||||
if (offline && !faulted) {
|
||||
zed_log_msg(LOG_INFO, "%s: %s is offline, skip autoreplace",
|
||||
__func__, path);
|
||||
return;
|
||||
}
|
||||
|
||||
is_mpath_wholedisk = is_mpath_whole_disk(path);
|
||||
is_dm = zfs_dev_is_dm(path);
|
||||
zed_log_msg(LOG_INFO, "zfs_process_add: pool '%s' vdev '%s', phys '%s'"
|
||||
" %s blank disk, %s mpath blank disk, %s labeled, enc sysfs '%s', "
|
||||
"(guid %llu)",
|
||||
zpool_get_name(zhp), path,
|
||||
physpath ? physpath : "NULL",
|
||||
wholedisk ? "is" : "not",
|
||||
is_mpath_wholedisk? "is" : "not",
|
||||
labeled ? "is" : "not",
|
||||
enc_sysfs_path,
|
||||
" wholedisk %d, dm %d (%llu)", zpool_get_name(zhp), path,
|
||||
physpath ? physpath : "NULL", wholedisk, is_dm,
|
||||
(long long unsigned int)guid);
|
||||
|
||||
/*
|
||||
@@ -307,18 +247,15 @@ zfs_process_add(zpool_handle_t *zhp, nvlist_t *vdev, boolean_t labeled)
|
||||
}
|
||||
}
|
||||
|
||||
if (is_spare)
|
||||
online_flag |= ZFS_ONLINE_SPARE;
|
||||
|
||||
/*
|
||||
* Attempt to online the device.
|
||||
*/
|
||||
if (zpool_vdev_online(zhp, fullpath, online_flag, &newstate) == 0 &&
|
||||
if (zpool_vdev_online(zhp, fullpath,
|
||||
ZFS_ONLINE_CHECKREMOVE | ZFS_ONLINE_UNSPARE, &newstate) == 0 &&
|
||||
(newstate == VDEV_STATE_HEALTHY ||
|
||||
newstate == VDEV_STATE_DEGRADED)) {
|
||||
zed_log_msg(LOG_INFO,
|
||||
" zpool_vdev_online: vdev '%s' ('%s') is "
|
||||
"%s", fullpath, physpath, (newstate == VDEV_STATE_HEALTHY) ?
|
||||
zed_log_msg(LOG_INFO, " zpool_vdev_online: vdev %s is %s",
|
||||
fullpath, (newstate == VDEV_STATE_HEALTHY) ?
|
||||
"HEALTHY" : "DEGRADED");
|
||||
return;
|
||||
}
|
||||
@@ -328,19 +265,18 @@ zfs_process_add(zpool_handle_t *zhp, nvlist_t *vdev, boolean_t labeled)
|
||||
* testing)
|
||||
*/
|
||||
if (physpath != NULL && strcmp("scsidebug", physpath) == 0)
|
||||
is_sd = B_TRUE;
|
||||
is_sd = 1;
|
||||
|
||||
/*
|
||||
* If the pool doesn't have the autoreplace property set, then use
|
||||
* vdev online to trigger a FMA fault by posting an ereport.
|
||||
*/
|
||||
if (!zpool_get_prop_int(zhp, ZPOOL_PROP_AUTOREPLACE, NULL) ||
|
||||
!(wholedisk || is_mpath_wholedisk) || (physpath == NULL)) {
|
||||
!(wholedisk || is_dm) || (physpath == NULL)) {
|
||||
(void) zpool_vdev_online(zhp, fullpath, ZFS_ONLINE_FORCEFAULT,
|
||||
&newstate);
|
||||
zed_log_msg(LOG_INFO, "Pool's autoreplace is not enabled or "
|
||||
"not a blank disk for '%s' ('%s')", fullpath,
|
||||
physpath);
|
||||
"not a whole disk for '%s'", fullpath);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -352,7 +288,7 @@ zfs_process_add(zpool_handle_t *zhp, nvlist_t *vdev, boolean_t labeled)
|
||||
(void) snprintf(rawpath, sizeof (rawpath), "%s%s",
|
||||
is_sd ? DEV_BYVDEV_PATH : DEV_BYPATH_PATH, physpath);
|
||||
|
||||
if (realpath(rawpath, devpath) == NULL && !is_mpath_wholedisk) {
|
||||
if (realpath(rawpath, devpath) == NULL && !is_dm) {
|
||||
zed_log_msg(LOG_INFO, " realpath: %s failed (%s)",
|
||||
rawpath, strerror(errno));
|
||||
|
||||
@@ -368,14 +304,12 @@ zfs_process_add(zpool_handle_t *zhp, nvlist_t *vdev, boolean_t labeled)
|
||||
if ((vs->vs_state != VDEV_STATE_DEGRADED) &&
|
||||
(vs->vs_state != VDEV_STATE_FAULTED) &&
|
||||
(vs->vs_state != VDEV_STATE_CANT_OPEN)) {
|
||||
zed_log_msg(LOG_INFO, " not autoreplacing since disk isn't in "
|
||||
"a bad state (currently %d)", vs->vs_state);
|
||||
return;
|
||||
}
|
||||
|
||||
nvlist_lookup_string(vdev, "new_devid", &new_devid);
|
||||
|
||||
if (is_mpath_wholedisk) {
|
||||
if (is_dm) {
|
||||
/* Don't label device mapper or multipath disks. */
|
||||
} else if (!labeled) {
|
||||
/*
|
||||
@@ -492,25 +426,9 @@ zfs_process_add(zpool_handle_t *zhp, nvlist_t *vdev, boolean_t labeled)
|
||||
nvlist_free(newvd);
|
||||
|
||||
/*
|
||||
* Wait for udev to verify the links exist, then auto-replace
|
||||
* the leaf disk at same physical location.
|
||||
* auto replace a leaf disk at same physical location
|
||||
*/
|
||||
if (zpool_label_disk_wait(path, 3000) != 0) {
|
||||
zed_log_msg(LOG_WARNING, "zfs_mod: expected replacement "
|
||||
"disk %s is missing", path);
|
||||
nvlist_free(nvroot);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Prefer sequential resilvering when supported (mirrors and dRAID),
|
||||
* otherwise fallback to a traditional healing resilver.
|
||||
*/
|
||||
ret = zpool_vdev_attach(zhp, fullpath, path, nvroot, B_TRUE, B_TRUE);
|
||||
if (ret != 0) {
|
||||
ret = zpool_vdev_attach(zhp, fullpath, path, nvroot,
|
||||
B_TRUE, B_FALSE);
|
||||
}
|
||||
ret = zpool_vdev_attach(zhp, fullpath, path, nvroot, B_TRUE);
|
||||
|
||||
zed_log_msg(LOG_INFO, " zpool_vdev_replace: %s with %s (%s)",
|
||||
fullpath, path, (ret == 0) ? "no errors" :
|
||||
@@ -530,9 +448,7 @@ typedef struct dev_data {
|
||||
boolean_t dd_islabeled;
|
||||
uint64_t dd_pool_guid;
|
||||
uint64_t dd_vdev_guid;
|
||||
uint64_t dd_new_vdev_guid;
|
||||
const char *dd_new_devid;
|
||||
uint64_t dd_num_spares;
|
||||
} dev_data_t;
|
||||
|
||||
static void
|
||||
@@ -542,8 +458,6 @@ zfs_iter_vdev(zpool_handle_t *zhp, nvlist_t *nvl, void *data)
|
||||
char *path = NULL;
|
||||
uint_t c, children;
|
||||
nvlist_t **child;
|
||||
uint64_t guid = 0;
|
||||
uint64_t isspare = 0;
|
||||
|
||||
/*
|
||||
* First iterate over any children.
|
||||
@@ -552,33 +466,23 @@ zfs_iter_vdev(zpool_handle_t *zhp, nvlist_t *nvl, void *data)
|
||||
&child, &children) == 0) {
|
||||
for (c = 0; c < children; c++)
|
||||
zfs_iter_vdev(zhp, child[c], data);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over any spares and cache devices
|
||||
*/
|
||||
if (nvlist_lookup_nvlist_array(nvl, ZPOOL_CONFIG_SPARES,
|
||||
&child, &children) == 0) {
|
||||
for (c = 0; c < children; c++)
|
||||
zfs_iter_vdev(zhp, child[c], data);
|
||||
}
|
||||
if (nvlist_lookup_nvlist_array(nvl, ZPOOL_CONFIG_L2CACHE,
|
||||
&child, &children) == 0) {
|
||||
for (c = 0; c < children; c++)
|
||||
zfs_iter_vdev(zhp, child[c], data);
|
||||
return;
|
||||
}
|
||||
|
||||
/* once a vdev was matched and processed there is nothing left to do */
|
||||
if (dp->dd_found && dp->dd_num_spares == 0)
|
||||
if (dp->dd_found)
|
||||
return;
|
||||
(void) nvlist_lookup_uint64(nvl, ZPOOL_CONFIG_GUID, &guid);
|
||||
|
||||
/*
|
||||
* Match by GUID if available otherwise fallback to devid or physical
|
||||
*/
|
||||
if (dp->dd_vdev_guid != 0) {
|
||||
if (guid != dp->dd_vdev_guid)
|
||||
uint64_t guid;
|
||||
|
||||
if (nvlist_lookup_uint64(nvl, ZPOOL_CONFIG_GUID,
|
||||
&guid) != 0 || guid != dp->dd_vdev_guid) {
|
||||
return;
|
||||
}
|
||||
zed_log_msg(LOG_INFO, " zfs_iter_vdev: matched on %llu", guid);
|
||||
dp->dd_found = B_TRUE;
|
||||
|
||||
@@ -588,25 +492,10 @@ zfs_iter_vdev(zpool_handle_t *zhp, nvlist_t *nvl, void *data)
|
||||
* illumos, substring matching is not required to accommodate
|
||||
* the partition suffix. An exact match will be present in
|
||||
* the dp->dd_compare value.
|
||||
* If the attached disk already contains a vdev GUID, it means
|
||||
* the disk is not clean. In such a scenario, the physical path
|
||||
* would be a match that makes the disk faulted when trying to
|
||||
* online it. So, we would only want to proceed if either GUID
|
||||
* matches with the last attached disk or the disk is in clean
|
||||
* state.
|
||||
*/
|
||||
if (nvlist_lookup_string(nvl, dp->dd_prop, &path) != 0 ||
|
||||
strcmp(dp->dd_compare, path) != 0) {
|
||||
zed_log_msg(LOG_INFO, " %s: no match (%s != vdev %s)",
|
||||
__func__, dp->dd_compare, path);
|
||||
strcmp(dp->dd_compare, path) != 0)
|
||||
return;
|
||||
}
|
||||
if (dp->dd_new_vdev_guid != 0 && dp->dd_new_vdev_guid != guid) {
|
||||
zed_log_msg(LOG_INFO, " %s: no match (GUID:%llu"
|
||||
" != vdev GUID:%llu)", __func__,
|
||||
dp->dd_new_vdev_guid, guid);
|
||||
return;
|
||||
}
|
||||
|
||||
zed_log_msg(LOG_INFO, " zfs_iter_vdev: matched %s on %s",
|
||||
dp->dd_prop, path);
|
||||
@@ -619,21 +508,22 @@ zfs_iter_vdev(zpool_handle_t *zhp, nvlist_t *nvl, void *data)
|
||||
}
|
||||
}
|
||||
|
||||
if (dp->dd_found == B_TRUE && nvlist_lookup_uint64(nvl,
|
||||
ZPOOL_CONFIG_IS_SPARE, &isspare) == 0 && isspare)
|
||||
dp->dd_num_spares++;
|
||||
|
||||
(dp->dd_func)(zhp, nvl, dp->dd_islabeled);
|
||||
}
|
||||
|
||||
static void
|
||||
static void *
|
||||
zfs_enable_ds(void *arg)
|
||||
{
|
||||
unavailpool_t *pool = (unavailpool_t *)arg;
|
||||
|
||||
assert(pool->uap_enable_tid = pthread_self());
|
||||
|
||||
(void) zpool_enable_datasets(pool->uap_zhp, NULL, 0);
|
||||
zpool_close(pool->uap_zhp);
|
||||
free(pool);
|
||||
pool->uap_zhp = NULL;
|
||||
|
||||
/* Note: zfs_slm_fini() will cleanup this pool entry on exit */
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -658,8 +548,6 @@ zfs_iter_pool(zpool_handle_t *zhp, void *data)
|
||||
ZPOOL_CONFIG_VDEV_TREE, &nvl);
|
||||
zfs_iter_vdev(zhp, nvl, data);
|
||||
}
|
||||
} else {
|
||||
zed_log_msg(LOG_INFO, "%s: no config\n", __func__);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -670,22 +558,22 @@ zfs_iter_pool(zpool_handle_t *zhp, void *data)
|
||||
for (pool = list_head(&g_pool_list); pool != NULL;
|
||||
pool = list_next(&g_pool_list, pool)) {
|
||||
|
||||
if (pool->uap_enable_tid != 0)
|
||||
continue; /* entry already processed */
|
||||
if (strcmp(zpool_get_name(zhp),
|
||||
zpool_get_name(pool->uap_zhp)))
|
||||
continue;
|
||||
if (zfs_toplevel_state(zhp) >= VDEV_STATE_DEGRADED) {
|
||||
list_remove(&g_pool_list, pool);
|
||||
(void) tpool_dispatch(g_tpool, zfs_enable_ds,
|
||||
pool);
|
||||
/* send to a background thread; keep on list */
|
||||
(void) pthread_create(&pool->uap_enable_tid,
|
||||
NULL, zfs_enable_ds, pool);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
zpool_close(zhp);
|
||||
|
||||
/* cease iteration after a match */
|
||||
return (dp->dd_found && dp->dd_num_spares == 0);
|
||||
return (dp->dd_found); /* cease iteration after a match */
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -694,7 +582,7 @@ zfs_iter_pool(zpool_handle_t *zhp, void *data)
|
||||
*/
|
||||
static boolean_t
|
||||
devphys_iter(const char *physical, const char *devid, zfs_process_func_t func,
|
||||
boolean_t is_slice, uint64_t new_vdev_guid)
|
||||
boolean_t is_slice)
|
||||
{
|
||||
dev_data_t data = { 0 };
|
||||
|
||||
@@ -704,73 +592,6 @@ devphys_iter(const char *physical, const char *devid, zfs_process_func_t func,
|
||||
data.dd_found = B_FALSE;
|
||||
data.dd_islabeled = is_slice;
|
||||
data.dd_new_devid = devid; /* used by auto replace code */
|
||||
data.dd_new_vdev_guid = new_vdev_guid;
|
||||
|
||||
(void) zpool_iter(g_zfshdl, zfs_iter_pool, &data);
|
||||
|
||||
return (data.dd_found);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a device identifier, find any vdevs with a matching by-vdev
|
||||
* path. Normally we shouldn't need this as the comparison would be
|
||||
* made earlier in the devphys_iter(). For example, if we were replacing
|
||||
* /dev/disk/by-vdev/L28, normally devphys_iter() would match the
|
||||
* ZPOOL_CONFIG_PHYS_PATH of "L28" from the old disk config to "L28"
|
||||
* of the new disk config. However, we've seen cases where
|
||||
* ZPOOL_CONFIG_PHYS_PATH was not in the config for the old disk. Here's
|
||||
* an example of a real 2-disk mirror pool where one disk was force
|
||||
* faulted:
|
||||
*
|
||||
* com.delphix:vdev_zap_top: 129
|
||||
* children[0]:
|
||||
* type: 'disk'
|
||||
* id: 0
|
||||
* guid: 14309659774640089719
|
||||
* path: '/dev/disk/by-vdev/L28'
|
||||
* whole_disk: 0
|
||||
* DTL: 654
|
||||
* create_txg: 4
|
||||
* com.delphix:vdev_zap_leaf: 1161
|
||||
* faulted: 1
|
||||
* aux_state: 'external'
|
||||
* children[1]:
|
||||
* type: 'disk'
|
||||
* id: 1
|
||||
* guid: 16002508084177980912
|
||||
* path: '/dev/disk/by-vdev/L29'
|
||||
* devid: 'dm-uuid-mpath-35000c500a61d68a3'
|
||||
* phys_path: 'L29'
|
||||
* vdev_enc_sysfs_path: '/sys/class/enclosure/0:0:1:0/SLOT 30 32'
|
||||
* whole_disk: 0
|
||||
* DTL: 1028
|
||||
* create_txg: 4
|
||||
* com.delphix:vdev_zap_leaf: 131
|
||||
*
|
||||
* So in the case above, the only thing we could compare is the path.
|
||||
*
|
||||
* We can do this because we assume by-vdev paths are authoritative as physical
|
||||
* paths. We could not assume this for normal paths like /dev/sda since the
|
||||
* physical location /dev/sda points to could change over time.
|
||||
*/
|
||||
static boolean_t
|
||||
by_vdev_path_iter(const char *by_vdev_path, const char *devid,
|
||||
zfs_process_func_t func, boolean_t is_slice)
|
||||
{
|
||||
dev_data_t data = { 0 };
|
||||
|
||||
data.dd_compare = by_vdev_path;
|
||||
data.dd_func = func;
|
||||
data.dd_prop = ZPOOL_CONFIG_PATH;
|
||||
data.dd_found = B_FALSE;
|
||||
data.dd_islabeled = is_slice;
|
||||
data.dd_new_devid = devid;
|
||||
|
||||
if (strncmp(by_vdev_path, DEV_BYVDEV_PATH,
|
||||
strlen(DEV_BYVDEV_PATH)) != 0) {
|
||||
/* by_vdev_path doesn't start with "/dev/disk/by-vdev/" */
|
||||
return (B_FALSE);
|
||||
}
|
||||
|
||||
(void) zpool_iter(g_zfshdl, zfs_iter_pool, &data);
|
||||
|
||||
@@ -798,27 +619,6 @@ devid_iter(const char *devid, zfs_process_func_t func, boolean_t is_slice)
|
||||
return (data.dd_found);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a device guid, find any vdevs with a matching guid.
|
||||
*/
|
||||
static boolean_t
|
||||
guid_iter(uint64_t pool_guid, uint64_t vdev_guid, const char *devid,
|
||||
zfs_process_func_t func, boolean_t is_slice)
|
||||
{
|
||||
dev_data_t data = { 0 };
|
||||
|
||||
data.dd_func = func;
|
||||
data.dd_found = B_FALSE;
|
||||
data.dd_pool_guid = pool_guid;
|
||||
data.dd_vdev_guid = vdev_guid;
|
||||
data.dd_islabeled = is_slice;
|
||||
data.dd_new_devid = devid;
|
||||
|
||||
(void) zpool_iter(g_zfshdl, zfs_iter_pool, &data);
|
||||
|
||||
return (data.dd_found);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle a EC_DEV_ADD.ESC_DISK event.
|
||||
*
|
||||
@@ -841,21 +641,16 @@ guid_iter(uint64_t pool_guid, uint64_t vdev_guid, const char *devid,
|
||||
static int
|
||||
zfs_deliver_add(nvlist_t *nvl, boolean_t is_lofi)
|
||||
{
|
||||
char *devpath = NULL, *devid = NULL;
|
||||
uint64_t pool_guid = 0, vdev_guid = 0;
|
||||
char *devpath = NULL, *devid;
|
||||
boolean_t is_slice;
|
||||
|
||||
/*
|
||||
* Expecting a devid string and an optional physical location and guid
|
||||
* Expecting a devid string and an optional physical location
|
||||
*/
|
||||
if (nvlist_lookup_string(nvl, DEV_IDENTIFIER, &devid) != 0) {
|
||||
zed_log_msg(LOG_INFO, "%s: no dev identifier\n", __func__);
|
||||
if (nvlist_lookup_string(nvl, DEV_IDENTIFIER, &devid) != 0)
|
||||
return (-1);
|
||||
}
|
||||
|
||||
(void) nvlist_lookup_string(nvl, DEV_PHYS_PATH, &devpath);
|
||||
(void) nvlist_lookup_uint64(nvl, ZFS_EV_POOL_GUID, &pool_guid);
|
||||
(void) nvlist_lookup_uint64(nvl, ZFS_EV_VDEV_GUID, &vdev_guid);
|
||||
|
||||
is_slice = (nvlist_lookup_boolean(nvl, DEV_IS_PART) == 0);
|
||||
|
||||
@@ -863,31 +658,15 @@ zfs_deliver_add(nvlist_t *nvl, boolean_t is_lofi)
|
||||
devid, devpath ? devpath : "NULL", is_slice);
|
||||
|
||||
/*
|
||||
* Iterate over all vdevs looking for a match in the following order:
|
||||
* Iterate over all vdevs looking for a match in the folllowing order:
|
||||
* 1. ZPOOL_CONFIG_DEVID (identifies the unique disk)
|
||||
* 2. ZPOOL_CONFIG_PHYS_PATH (identifies disk physical location).
|
||||
* 3. ZPOOL_CONFIG_GUID (identifies unique vdev).
|
||||
* 4. ZPOOL_CONFIG_PATH for /dev/disk/by-vdev devices only (since
|
||||
* by-vdev paths represent physical paths).
|
||||
*
|
||||
* For disks, we only want to pay attention to vdevs marked as whole
|
||||
* disks or are a multipath device.
|
||||
*/
|
||||
if (devid_iter(devid, zfs_process_add, is_slice))
|
||||
return (0);
|
||||
if (devpath != NULL && devphys_iter(devpath, devid, zfs_process_add,
|
||||
is_slice, vdev_guid))
|
||||
return (0);
|
||||
if (vdev_guid != 0)
|
||||
(void) guid_iter(pool_guid, vdev_guid, devid, zfs_process_add,
|
||||
is_slice);
|
||||
|
||||
if (devpath != NULL) {
|
||||
/* Can we match a /dev/disk/by-vdev/ path? */
|
||||
char by_vdev_path[MAXPATHLEN];
|
||||
snprintf(by_vdev_path, sizeof (by_vdev_path),
|
||||
"/dev/disk/by-vdev/%s", devpath);
|
||||
if (by_vdev_path_iter(by_vdev_path, devid, zfs_process_add,
|
||||
is_slice))
|
||||
return (0);
|
||||
}
|
||||
if (!devid_iter(devid, zfs_process_add, is_slice) && devpath != NULL)
|
||||
(void) devphys_iter(devpath, devid, zfs_process_add, is_slice);
|
||||
|
||||
return (0);
|
||||
}
|
||||
@@ -919,88 +698,13 @@ zfs_deliver_check(nvlist_t *nvl)
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a path to a vdev, lookup the vdev's physical size from its
|
||||
* config nvlist.
|
||||
*
|
||||
* Returns the vdev's physical size in bytes on success, 0 on error.
|
||||
*/
|
||||
static uint64_t
|
||||
vdev_size_from_config(zpool_handle_t *zhp, const char *vdev_path)
|
||||
{
|
||||
nvlist_t *nvl = NULL;
|
||||
boolean_t avail_spare, l2cache, log;
|
||||
vdev_stat_t *vs = NULL;
|
||||
uint_t c;
|
||||
|
||||
nvl = zpool_find_vdev(zhp, vdev_path, &avail_spare, &l2cache, &log);
|
||||
if (!nvl)
|
||||
return (0);
|
||||
|
||||
verify(nvlist_lookup_uint64_array(nvl, ZPOOL_CONFIG_VDEV_STATS,
|
||||
(uint64_t **)&vs, &c) == 0);
|
||||
if (!vs) {
|
||||
zed_log_msg(LOG_INFO, "%s: no nvlist for '%s'", __func__,
|
||||
vdev_path);
|
||||
return (0);
|
||||
}
|
||||
|
||||
return (vs->vs_pspace);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a path to a vdev, lookup if the vdev is a "whole disk" in the
|
||||
* config nvlist. "whole disk" means that ZFS was passed a whole disk
|
||||
* at pool creation time, which it partitioned up and has full control over.
|
||||
* Thus a partition with wholedisk=1 set tells us that zfs created the
|
||||
* partition at creation time. A partition without whole disk set would have
|
||||
* been created by externally (like with fdisk) and passed to ZFS.
|
||||
*
|
||||
* Returns the whole disk value (either 0 or 1).
|
||||
*/
|
||||
static uint64_t
|
||||
vdev_whole_disk_from_config(zpool_handle_t *zhp, const char *vdev_path)
|
||||
{
|
||||
nvlist_t *nvl = NULL;
|
||||
boolean_t avail_spare, l2cache, log;
|
||||
uint64_t wholedisk = 0;
|
||||
|
||||
nvl = zpool_find_vdev(zhp, vdev_path, &avail_spare, &l2cache, &log);
|
||||
if (!nvl)
|
||||
return (0);
|
||||
|
||||
(void) nvlist_lookup_uint64(nvl, ZPOOL_CONFIG_WHOLE_DISK, &wholedisk);
|
||||
|
||||
return (wholedisk);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the device size grew more than 1% then return true.
|
||||
*/
|
||||
#define DEVICE_GREW(oldsize, newsize) \
|
||||
((newsize > oldsize) && \
|
||||
((newsize / (newsize - oldsize)) <= 100))
|
||||
|
||||
static int
|
||||
zfsdle_vdev_online(zpool_handle_t *zhp, void *data)
|
||||
{
|
||||
char *devname = data;
|
||||
boolean_t avail_spare, l2cache;
|
||||
nvlist_t *udev_nvl = data;
|
||||
vdev_state_t newstate;
|
||||
nvlist_t *tgt;
|
||||
int error;
|
||||
|
||||
char *tmp_devname, devname[MAXPATHLEN] = "";
|
||||
uint64_t guid;
|
||||
|
||||
if (nvlist_lookup_uint64(udev_nvl, ZFS_EV_VDEV_GUID, &guid) == 0) {
|
||||
sprintf(devname, "%llu", (u_longlong_t)guid);
|
||||
} else if (nvlist_lookup_string(udev_nvl, DEV_PHYS_PATH,
|
||||
&tmp_devname) == 0) {
|
||||
strlcpy(devname, tmp_devname, MAXPATHLEN);
|
||||
zfs_append_partition(devname, MAXPATHLEN);
|
||||
} else {
|
||||
zed_log_msg(LOG_INFO, "%s: no guid or physpath", __func__);
|
||||
}
|
||||
|
||||
zed_log_msg(LOG_INFO, "zfsdle_vdev_online: searching for '%s' in '%s'",
|
||||
devname, zpool_get_name(zhp));
|
||||
@@ -1008,119 +712,40 @@ zfsdle_vdev_online(zpool_handle_t *zhp, void *data)
|
||||
if ((tgt = zpool_find_vdev_by_physpath(zhp, devname,
|
||||
&avail_spare, &l2cache, NULL)) != NULL) {
|
||||
char *path, fullpath[MAXPATHLEN];
|
||||
uint64_t wholedisk = 0;
|
||||
uint64_t wholedisk = 0ULL;
|
||||
|
||||
error = nvlist_lookup_string(tgt, ZPOOL_CONFIG_PATH, &path);
|
||||
if (error) {
|
||||
zpool_close(zhp);
|
||||
return (0);
|
||||
}
|
||||
|
||||
(void) nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_WHOLE_DISK,
|
||||
&wholedisk);
|
||||
verify(nvlist_lookup_string(tgt, ZPOOL_CONFIG_PATH,
|
||||
&path) == 0);
|
||||
verify(nvlist_lookup_uint64(tgt, ZPOOL_CONFIG_WHOLE_DISK,
|
||||
&wholedisk) == 0);
|
||||
|
||||
(void) strlcpy(fullpath, path, sizeof (fullpath));
|
||||
if (wholedisk) {
|
||||
path = strrchr(path, '/');
|
||||
if (path != NULL) {
|
||||
path = zfs_strip_partition(path + 1);
|
||||
if (path == NULL) {
|
||||
zpool_close(zhp);
|
||||
return (0);
|
||||
}
|
||||
} else {
|
||||
zpool_close(zhp);
|
||||
char *spath = zfs_strip_partition(fullpath);
|
||||
if (!spath) {
|
||||
zed_log_msg(LOG_INFO, "%s: Can't alloc",
|
||||
__func__);
|
||||
return (0);
|
||||
}
|
||||
|
||||
(void) strlcpy(fullpath, path, sizeof (fullpath));
|
||||
free(path);
|
||||
(void) strlcpy(fullpath, spath, sizeof (fullpath));
|
||||
free(spath);
|
||||
|
||||
/*
|
||||
* We need to reopen the pool associated with this
|
||||
* device so that the kernel can update the size of
|
||||
* the expanded device. When expanding there is no
|
||||
* need to restart the scrub from the beginning.
|
||||
* device so that the kernel can update the size
|
||||
* of the expanded device.
|
||||
*/
|
||||
boolean_t scrub_restart = B_FALSE;
|
||||
(void) zpool_reopen_one(zhp, &scrub_restart);
|
||||
} else {
|
||||
(void) strlcpy(fullpath, path, sizeof (fullpath));
|
||||
(void) zpool_reopen(zhp);
|
||||
}
|
||||
|
||||
if (zpool_get_prop_int(zhp, ZPOOL_PROP_AUTOEXPAND, NULL)) {
|
||||
vdev_state_t newstate;
|
||||
|
||||
if (zpool_get_state(zhp) != POOL_STATE_UNAVAIL) {
|
||||
/*
|
||||
* If this disk size has not changed, then
|
||||
* there's no need to do an autoexpand. To
|
||||
* check we look at the disk's size in its
|
||||
* config, and compare it to the disk size
|
||||
* that udev is reporting.
|
||||
*/
|
||||
uint64_t udev_size = 0, conf_size = 0,
|
||||
wholedisk = 0, udev_parent_size = 0;
|
||||
|
||||
/*
|
||||
* Get the size of our disk that udev is
|
||||
* reporting.
|
||||
*/
|
||||
if (nvlist_lookup_uint64(udev_nvl, DEV_SIZE,
|
||||
&udev_size) != 0) {
|
||||
udev_size = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the size of our disk's parent device
|
||||
* from udev (where sda1's parent is sda).
|
||||
*/
|
||||
if (nvlist_lookup_uint64(udev_nvl,
|
||||
DEV_PARENT_SIZE, &udev_parent_size) != 0) {
|
||||
udev_parent_size = 0;
|
||||
}
|
||||
|
||||
conf_size = vdev_size_from_config(zhp,
|
||||
fullpath);
|
||||
|
||||
wholedisk = vdev_whole_disk_from_config(zhp,
|
||||
fullpath);
|
||||
|
||||
/*
|
||||
* Only attempt an autoexpand if the vdev size
|
||||
* changed. There are two different cases
|
||||
* to consider.
|
||||
*
|
||||
* 1. wholedisk=1
|
||||
* If you do a 'zpool create' on a whole disk
|
||||
* (like /dev/sda), then zfs will create
|
||||
* partitions on the disk (like /dev/sda1). In
|
||||
* that case, wholedisk=1 will be set in the
|
||||
* partition's nvlist config. So zed will need
|
||||
* to see if your parent device (/dev/sda)
|
||||
* expanded in size, and if so, then attempt
|
||||
* the autoexpand.
|
||||
*
|
||||
* 2. wholedisk=0
|
||||
* If you do a 'zpool create' on an existing
|
||||
* partition, or a device that doesn't allow
|
||||
* partitions, then wholedisk=0, and you will
|
||||
* simply need to check if the device itself
|
||||
* expanded in size.
|
||||
*/
|
||||
if (DEVICE_GREW(conf_size, udev_size) ||
|
||||
(wholedisk && DEVICE_GREW(conf_size,
|
||||
udev_parent_size))) {
|
||||
error = zpool_vdev_online(zhp, fullpath,
|
||||
0, &newstate);
|
||||
|
||||
zed_log_msg(LOG_INFO,
|
||||
"%s: autoexpanding '%s' from %llu"
|
||||
" to %llu bytes in pool '%s': %d",
|
||||
__func__, fullpath, conf_size,
|
||||
MAX(udev_size, udev_parent_size),
|
||||
zpool_get_name(zhp), error);
|
||||
}
|
||||
}
|
||||
zed_log_msg(LOG_INFO, "zfsdle_vdev_online: setting "
|
||||
"device '%s' to ONLINE state in pool '%s'",
|
||||
fullpath, zpool_get_name(zhp));
|
||||
if (zpool_get_state(zhp) != POOL_STATE_UNAVAIL)
|
||||
(void) zpool_vdev_online(zhp, fullpath, 0,
|
||||
&newstate);
|
||||
}
|
||||
zpool_close(zhp);
|
||||
return (1);
|
||||
@@ -1130,33 +755,23 @@ zfsdle_vdev_online(zpool_handle_t *zhp, void *data)
|
||||
}
|
||||
|
||||
/*
|
||||
* This function handles the ESC_DEV_DLE device change event. Use the
|
||||
* provided vdev guid when looking up a disk or partition, when the guid
|
||||
* is not present assume the entire disk is owned by ZFS and append the
|
||||
* expected -part1 partition information then lookup by physical path.
|
||||
* This function handles the ESC_DEV_DLE event.
|
||||
*/
|
||||
static int
|
||||
zfs_deliver_dle(nvlist_t *nvl)
|
||||
{
|
||||
char *devname, name[MAXPATHLEN];
|
||||
uint64_t guid;
|
||||
char *devname;
|
||||
|
||||
if (nvlist_lookup_uint64(nvl, ZFS_EV_VDEV_GUID, &guid) == 0) {
|
||||
sprintf(name, "%llu", (u_longlong_t)guid);
|
||||
} else if (nvlist_lookup_string(nvl, DEV_PHYS_PATH, &devname) == 0) {
|
||||
strlcpy(name, devname, MAXPATHLEN);
|
||||
zfs_append_partition(name, MAXPATHLEN);
|
||||
} else {
|
||||
sprintf(name, "unknown");
|
||||
zed_log_msg(LOG_INFO, "zfs_deliver_dle: no guid or physpath");
|
||||
if (nvlist_lookup_string(nvl, DEV_PHYS_PATH, &devname) != 0) {
|
||||
zed_log_msg(LOG_INFO, "zfs_deliver_dle: no physpath");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (zpool_iter(g_zfshdl, zfsdle_vdev_online, nvl) != 1) {
|
||||
if (zpool_iter(g_zfshdl, zfsdle_vdev_online, devname) != 1) {
|
||||
zed_log_msg(LOG_INFO, "zfs_deliver_dle: device '%s' not "
|
||||
"found", name);
|
||||
"found", devname);
|
||||
return (1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
@@ -1234,12 +849,12 @@ zfs_enum_pools(void *arg)
|
||||
*
|
||||
* sent messages from zevents or udev monitor
|
||||
*
|
||||
* For now, each agent has its own libzfs instance
|
||||
* For now, each agent has it's own libzfs instance
|
||||
*/
|
||||
int
|
||||
zfs_slm_init(void)
|
||||
zfs_slm_init()
|
||||
{
|
||||
if ((g_zfshdl = libzfs_init()) == NULL)
|
||||
if ((g_zfshdl = __libzfs_init()) == NULL)
|
||||
return (-1);
|
||||
|
||||
/*
|
||||
@@ -1251,11 +866,10 @@ zfs_slm_init(void)
|
||||
|
||||
if (pthread_create(&g_zfs_tid, NULL, zfs_enum_pools, NULL) != 0) {
|
||||
list_destroy(&g_pool_list);
|
||||
libzfs_fini(g_zfshdl);
|
||||
__libzfs_fini(g_zfshdl);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
pthread_setname_np(g_zfs_tid, "enum-pools");
|
||||
list_create(&g_device_list, sizeof (struct pendingdev),
|
||||
offsetof(struct pendingdev, pd_node));
|
||||
|
||||
@@ -1263,22 +877,26 @@ zfs_slm_init(void)
|
||||
}
|
||||
|
||||
void
|
||||
zfs_slm_fini(void)
|
||||
zfs_slm_fini()
|
||||
{
|
||||
unavailpool_t *pool;
|
||||
pendingdev_t *device;
|
||||
|
||||
/* wait for zfs_enum_pools thread to complete */
|
||||
(void) pthread_join(g_zfs_tid, NULL);
|
||||
/* destroy the thread pool */
|
||||
if (g_tpool != NULL) {
|
||||
tpool_wait(g_tpool);
|
||||
tpool_destroy(g_tpool);
|
||||
}
|
||||
|
||||
while ((pool = (list_head(&g_pool_list))) != NULL) {
|
||||
/*
|
||||
* each pool entry has two possibilities
|
||||
* 1. was made available (so wait for zfs_enable_ds thread)
|
||||
* 2. still unavailable (just close the pool)
|
||||
*/
|
||||
if (pool->uap_enable_tid)
|
||||
(void) pthread_join(pool->uap_enable_tid, NULL);
|
||||
else if (pool->uap_zhp != NULL)
|
||||
zpool_close(pool->uap_zhp);
|
||||
|
||||
list_remove(&g_pool_list, pool);
|
||||
zpool_close(pool->uap_zhp);
|
||||
free(pool);
|
||||
}
|
||||
list_destroy(&g_pool_list);
|
||||
@@ -1289,7 +907,7 @@ zfs_slm_fini(void)
|
||||
}
|
||||
list_destroy(&g_device_list);
|
||||
|
||||
libzfs_fini(g_zfshdl);
|
||||
__libzfs_fini(g_zfshdl);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
+162
-180
@@ -22,7 +22,6 @@
|
||||
* Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
*
|
||||
* Copyright (c) 2016, Intel Corporation.
|
||||
* Copyright (c) 2018, loli10K <ezomori.nozomu@gmail.com>
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -40,7 +39,6 @@
|
||||
#include <sys/fm/fs/zfs.h>
|
||||
#include <libzfs.h>
|
||||
#include <string.h>
|
||||
#include <libgen.h>
|
||||
|
||||
#include "zfs_agents.h"
|
||||
#include "fmd_api.h"
|
||||
@@ -73,10 +71,9 @@ zfs_retire_clear_data(fmd_hdl_t *hdl, zfs_retire_data_t *zdp)
|
||||
*/
|
||||
typedef struct find_cbdata {
|
||||
uint64_t cb_guid;
|
||||
const char *cb_fru;
|
||||
zpool_handle_t *cb_zhp;
|
||||
nvlist_t *cb_vdev;
|
||||
uint64_t cb_vdev_guid;
|
||||
uint64_t cb_num_spares;
|
||||
} find_cbdata_t;
|
||||
|
||||
static int
|
||||
@@ -98,18 +95,26 @@ find_pool(zpool_handle_t *zhp, void *data)
|
||||
* Find a vdev within a tree with a matching GUID.
|
||||
*/
|
||||
static nvlist_t *
|
||||
find_vdev(libzfs_handle_t *zhdl, nvlist_t *nv, uint64_t search_guid)
|
||||
find_vdev(libzfs_handle_t *zhdl, nvlist_t *nv, const char *search_fru,
|
||||
uint64_t search_guid)
|
||||
{
|
||||
uint64_t guid;
|
||||
nvlist_t **child;
|
||||
uint_t c, children;
|
||||
nvlist_t *ret;
|
||||
char *fru;
|
||||
|
||||
if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &guid) == 0 &&
|
||||
guid == search_guid) {
|
||||
fmd_hdl_debug(fmd_module_hdl("zfs-retire"),
|
||||
"matched vdev %llu", guid);
|
||||
return (nv);
|
||||
if (search_fru != NULL) {
|
||||
if (nvlist_lookup_string(nv, ZPOOL_CONFIG_FRU, &fru) == 0 &&
|
||||
libzfs_fru_compare(zhdl, fru, search_fru))
|
||||
return (nv);
|
||||
} else {
|
||||
if (nvlist_lookup_uint64(nv, ZPOOL_CONFIG_GUID, &guid) == 0 &&
|
||||
guid == search_guid) {
|
||||
fmd_hdl_debug(fmd_module_hdl("zfs-retire"),
|
||||
"matched vdev %llu", guid);
|
||||
return (nv);
|
||||
}
|
||||
}
|
||||
|
||||
if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_CHILDREN,
|
||||
@@ -117,7 +122,8 @@ find_vdev(libzfs_handle_t *zhdl, nvlist_t *nv, uint64_t search_guid)
|
||||
return (NULL);
|
||||
|
||||
for (c = 0; c < children; c++) {
|
||||
if ((ret = find_vdev(zhdl, child[c], search_guid)) != NULL)
|
||||
if ((ret = find_vdev(zhdl, child[c], search_fru,
|
||||
search_guid)) != NULL)
|
||||
return (ret);
|
||||
}
|
||||
|
||||
@@ -126,80 +132,14 @@ find_vdev(libzfs_handle_t *zhdl, nvlist_t *nv, uint64_t search_guid)
|
||||
return (NULL);
|
||||
|
||||
for (c = 0; c < children; c++) {
|
||||
if ((ret = find_vdev(zhdl, child[c], search_guid)) != NULL)
|
||||
return (ret);
|
||||
}
|
||||
|
||||
if (nvlist_lookup_nvlist_array(nv, ZPOOL_CONFIG_SPARES,
|
||||
&child, &children) != 0)
|
||||
return (NULL);
|
||||
|
||||
for (c = 0; c < children; c++) {
|
||||
if ((ret = find_vdev(zhdl, child[c], search_guid)) != NULL)
|
||||
if ((ret = find_vdev(zhdl, child[c], search_fru,
|
||||
search_guid)) != NULL)
|
||||
return (ret);
|
||||
}
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
static int
|
||||
remove_spares(zpool_handle_t *zhp, void *data)
|
||||
{
|
||||
nvlist_t *config, *nvroot;
|
||||
nvlist_t **spares;
|
||||
uint_t nspares;
|
||||
char *devname;
|
||||
find_cbdata_t *cbp = data;
|
||||
uint64_t spareguid = 0;
|
||||
vdev_stat_t *vs;
|
||||
unsigned int c;
|
||||
|
||||
config = zpool_get_config(zhp, NULL);
|
||||
if (nvlist_lookup_nvlist(config,
|
||||
ZPOOL_CONFIG_VDEV_TREE, &nvroot) != 0) {
|
||||
zpool_close(zhp);
|
||||
return (0);
|
||||
}
|
||||
|
||||
if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES,
|
||||
&spares, &nspares) != 0) {
|
||||
zpool_close(zhp);
|
||||
return (0);
|
||||
}
|
||||
|
||||
for (int i = 0; i < nspares; i++) {
|
||||
if (nvlist_lookup_uint64(spares[i], ZPOOL_CONFIG_GUID,
|
||||
&spareguid) == 0 && spareguid == cbp->cb_vdev_guid) {
|
||||
devname = zpool_vdev_name(NULL, zhp, spares[i],
|
||||
B_FALSE);
|
||||
nvlist_lookup_uint64_array(spares[i],
|
||||
ZPOOL_CONFIG_VDEV_STATS, (uint64_t **)&vs, &c);
|
||||
if (vs->vs_state != VDEV_STATE_REMOVED &&
|
||||
zpool_vdev_remove_wanted(zhp, devname) == 0)
|
||||
cbp->cb_num_spares++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
zpool_close(zhp);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a vdev guid, find and remove all spares associated with it.
|
||||
*/
|
||||
static int
|
||||
find_and_remove_spares(libzfs_handle_t *zhdl, uint64_t vdev_guid)
|
||||
{
|
||||
find_cbdata_t cb;
|
||||
|
||||
cb.cb_num_spares = 0;
|
||||
cb.cb_vdev_guid = vdev_guid;
|
||||
zpool_iter(zhdl, remove_spares, &cb);
|
||||
|
||||
return (cb.cb_num_spares);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a (pool, vdev) GUID pair, find the matching pool and vdev.
|
||||
*/
|
||||
@@ -227,7 +167,8 @@ find_by_guid(libzfs_handle_t *zhdl, uint64_t pool_guid, uint64_t vdev_guid,
|
||||
}
|
||||
|
||||
if (vdev_guid != 0) {
|
||||
if ((*vdevp = find_vdev(zhdl, nvroot, vdev_guid)) == NULL) {
|
||||
if ((*vdevp = find_vdev(zhdl, nvroot, NULL,
|
||||
vdev_guid)) == NULL) {
|
||||
zpool_close(zhp);
|
||||
return (NULL);
|
||||
}
|
||||
@@ -236,37 +177,72 @@ find_by_guid(libzfs_handle_t *zhdl, uint64_t pool_guid, uint64_t vdev_guid,
|
||||
return (zhp);
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBTOPO
|
||||
static int
|
||||
search_pool(zpool_handle_t *zhp, void *data)
|
||||
{
|
||||
find_cbdata_t *cbp = data;
|
||||
nvlist_t *config;
|
||||
nvlist_t *nvroot;
|
||||
|
||||
config = zpool_get_config(zhp, NULL);
|
||||
if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
|
||||
&nvroot) != 0) {
|
||||
zpool_close(zhp);
|
||||
return (0);
|
||||
}
|
||||
|
||||
if ((cbp->cb_vdev = find_vdev(zpool_get_handle(zhp), nvroot,
|
||||
cbp->cb_fru, 0)) != NULL) {
|
||||
cbp->cb_zhp = zhp;
|
||||
return (1);
|
||||
}
|
||||
|
||||
zpool_close(zhp);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a FRU FMRI, find the matching pool and vdev.
|
||||
*/
|
||||
static zpool_handle_t *
|
||||
find_by_fru(libzfs_handle_t *zhdl, const char *fru, nvlist_t **vdevp)
|
||||
{
|
||||
find_cbdata_t cb;
|
||||
|
||||
cb.cb_fru = fru;
|
||||
cb.cb_zhp = NULL;
|
||||
if (zpool_iter(zhdl, search_pool, &cb) != 1)
|
||||
return (NULL);
|
||||
|
||||
*vdevp = cb.cb_vdev;
|
||||
return (cb.cb_zhp);
|
||||
}
|
||||
#endif /* HAVE_LIBTOPO */
|
||||
|
||||
/*
|
||||
* Given a vdev, attempt to replace it with every known spare until one
|
||||
* succeeds or we run out of devices to try.
|
||||
* Return whether we were successful or not in replacing the device.
|
||||
* succeeds.
|
||||
*/
|
||||
static boolean_t
|
||||
static void
|
||||
replace_with_spare(fmd_hdl_t *hdl, zpool_handle_t *zhp, nvlist_t *vdev)
|
||||
{
|
||||
nvlist_t *config, *nvroot, *replacement;
|
||||
nvlist_t **spares;
|
||||
uint_t s, nspares;
|
||||
char *dev_name;
|
||||
zprop_source_t source;
|
||||
int ashift;
|
||||
|
||||
config = zpool_get_config(zhp, NULL);
|
||||
if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE,
|
||||
&nvroot) != 0)
|
||||
return (B_FALSE);
|
||||
return;
|
||||
|
||||
/*
|
||||
* Find out if there are any hot spares available in the pool.
|
||||
*/
|
||||
if (nvlist_lookup_nvlist_array(nvroot, ZPOOL_CONFIG_SPARES,
|
||||
&spares, &nspares) != 0)
|
||||
return (B_FALSE);
|
||||
|
||||
/*
|
||||
* lookup "ashift" pool property, we may need it for the replacement
|
||||
*/
|
||||
ashift = zpool_get_prop_int(zhp, ZPOOL_PROP_ASHIFT, &source);
|
||||
return;
|
||||
|
||||
replacement = fmd_nvl_alloc(hdl, FMD_SLEEP);
|
||||
|
||||
@@ -280,23 +256,12 @@ replace_with_spare(fmd_hdl_t *hdl, zpool_handle_t *zhp, nvlist_t *vdev)
|
||||
* replace it.
|
||||
*/
|
||||
for (s = 0; s < nspares; s++) {
|
||||
boolean_t rebuild = B_FALSE;
|
||||
char *spare_name, *type;
|
||||
char *spare_name;
|
||||
|
||||
if (nvlist_lookup_string(spares[s], ZPOOL_CONFIG_PATH,
|
||||
&spare_name) != 0)
|
||||
continue;
|
||||
|
||||
/* prefer sequential resilvering for distributed spares */
|
||||
if ((nvlist_lookup_string(spares[s], ZPOOL_CONFIG_TYPE,
|
||||
&type) == 0) && strcmp(type, VDEV_TYPE_DRAID_SPARE) == 0)
|
||||
rebuild = B_TRUE;
|
||||
|
||||
/* if set, add the "ashift" pool property to the spare nvlist */
|
||||
if (source != ZPROP_SRC_DEFAULT)
|
||||
(void) nvlist_add_uint64(spares[s],
|
||||
ZPOOL_CONFIG_ASHIFT, ashift);
|
||||
|
||||
(void) nvlist_add_nvlist_array(replacement,
|
||||
ZPOOL_CONFIG_CHILDREN, &spares[s], 1);
|
||||
|
||||
@@ -304,17 +269,12 @@ replace_with_spare(fmd_hdl_t *hdl, zpool_handle_t *zhp, nvlist_t *vdev)
|
||||
dev_name, basename(spare_name));
|
||||
|
||||
if (zpool_vdev_attach(zhp, dev_name, spare_name,
|
||||
replacement, B_TRUE, rebuild) == 0) {
|
||||
free(dev_name);
|
||||
nvlist_free(replacement);
|
||||
return (B_TRUE);
|
||||
}
|
||||
replacement, B_TRUE) == 0)
|
||||
break;
|
||||
}
|
||||
|
||||
free(dev_name);
|
||||
nvlist_free(replacement);
|
||||
|
||||
return (B_FALSE);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -329,6 +289,10 @@ zfs_vdev_repair(fmd_hdl_t *hdl, nvlist_t *nvl)
|
||||
zfs_retire_data_t *zdp = fmd_hdl_getspecific(hdl);
|
||||
zfs_retire_repaired_t *zrp;
|
||||
uint64_t pool_guid, vdev_guid;
|
||||
#ifdef HAVE_LIBTOPO
|
||||
nvlist_t *asru;
|
||||
#endif
|
||||
|
||||
if (nvlist_lookup_uint64(nvl, FM_EREPORT_PAYLOAD_ZFS_POOL_GUID,
|
||||
&pool_guid) != 0 || nvlist_lookup_uint64(nvl,
|
||||
FM_EREPORT_PAYLOAD_ZFS_VDEV_GUID, &vdev_guid) != 0)
|
||||
@@ -351,6 +315,47 @@ zfs_vdev_repair(fmd_hdl_t *hdl, nvlist_t *nvl)
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBTOPO
|
||||
asru = fmd_nvl_alloc(hdl, FMD_SLEEP);
|
||||
|
||||
(void) nvlist_add_uint8(asru, FM_VERSION, ZFS_SCHEME_VERSION0);
|
||||
(void) nvlist_add_string(asru, FM_FMRI_SCHEME, FM_FMRI_SCHEME_ZFS);
|
||||
(void) nvlist_add_uint64(asru, FM_FMRI_ZFS_POOL, pool_guid);
|
||||
(void) nvlist_add_uint64(asru, FM_FMRI_ZFS_VDEV, vdev_guid);
|
||||
|
||||
/*
|
||||
* We explicitly check for the unusable state here to make sure we
|
||||
* aren't responding to a transient state change. As part of opening a
|
||||
* vdev, it's possible to see the 'statechange' event, only to be
|
||||
* followed by a vdev failure later. If we don't check the current
|
||||
* state of the vdev (or pool) before marking it repaired, then we risk
|
||||
* generating spurious repair events followed immediately by the same
|
||||
* diagnosis.
|
||||
*
|
||||
* This assumes that the ZFS scheme code associated unusable (i.e.
|
||||
* isolated) with its own definition of faulty state. In the case of a
|
||||
* DEGRADED leaf vdev (due to checksum errors), this is not the case.
|
||||
* This works, however, because the transient state change is not
|
||||
* posted in this case. This could be made more explicit by not
|
||||
* relying on the scheme's unusable callback and instead directly
|
||||
* checking the vdev state, where we could correctly account for
|
||||
* DEGRADED state.
|
||||
*/
|
||||
if (!fmd_nvl_fmri_unusable(hdl, asru) && fmd_nvl_fmri_has_fault(hdl,
|
||||
asru, FMD_HAS_FAULT_ASRU, NULL)) {
|
||||
topo_hdl_t *thp;
|
||||
char *fmri = NULL;
|
||||
int err;
|
||||
|
||||
thp = fmd_hdl_topo_hold(hdl, TOPO_VERSION);
|
||||
if (topo_fmri_nvl2str(thp, asru, &fmri, &err) == 0)
|
||||
(void) fmd_repair_asru(hdl, fmri);
|
||||
fmd_hdl_topo_rele(hdl, thp);
|
||||
|
||||
topo_hdl_strfree(thp, fmri);
|
||||
}
|
||||
nvlist_free(asru);
|
||||
#endif
|
||||
zrp = fmd_hdl_alloc(hdl, sizeof (zfs_retire_repaired_t), FMD_SLEEP);
|
||||
zrp->zrr_next = zdp->zrd_repaired;
|
||||
zrp->zrr_pool = pool_guid;
|
||||
@@ -375,8 +380,6 @@ zfs_retire_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl,
|
||||
libzfs_handle_t *zhdl = zdp->zrd_hdl;
|
||||
boolean_t fault_device, degrade_device;
|
||||
boolean_t is_repair;
|
||||
boolean_t l2arc = B_FALSE;
|
||||
boolean_t spare = B_FALSE;
|
||||
char *scheme;
|
||||
nvlist_t *vdev = NULL;
|
||||
char *uuid;
|
||||
@@ -385,79 +388,26 @@ zfs_retire_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl,
|
||||
boolean_t is_disk;
|
||||
vdev_aux_t aux;
|
||||
uint64_t state = 0;
|
||||
vdev_stat_t *vs;
|
||||
unsigned int c;
|
||||
|
||||
fmd_hdl_debug(hdl, "zfs_retire_recv: '%s'", class);
|
||||
|
||||
nvlist_lookup_uint64(nvl, FM_EREPORT_PAYLOAD_ZFS_VDEV_STATE, &state);
|
||||
|
||||
/*
|
||||
* If this is a resource notifying us of device removal then simply
|
||||
* check for an available spare and continue unless the device is a
|
||||
* l2arc vdev, in which case we just offline it.
|
||||
* If this is a resource notifying us of device removal, then simply
|
||||
* check for an available spare and continue.
|
||||
*/
|
||||
if (strcmp(class, "resource.fs.zfs.removed") == 0 ||
|
||||
(strcmp(class, "resource.fs.zfs.statechange") == 0 &&
|
||||
(state == VDEV_STATE_REMOVED || state == VDEV_STATE_FAULTED))) {
|
||||
char *devtype;
|
||||
char *devname;
|
||||
|
||||
if (nvlist_lookup_string(nvl, FM_EREPORT_PAYLOAD_ZFS_VDEV_TYPE,
|
||||
&devtype) == 0) {
|
||||
if (strcmp(devtype, VDEV_TYPE_SPARE) == 0)
|
||||
spare = B_TRUE;
|
||||
else if (strcmp(devtype, VDEV_TYPE_L2CACHE) == 0)
|
||||
l2arc = B_TRUE;
|
||||
}
|
||||
|
||||
if (nvlist_lookup_uint64(nvl,
|
||||
FM_EREPORT_PAYLOAD_ZFS_VDEV_GUID, &vdev_guid) != 0)
|
||||
return;
|
||||
|
||||
if (spare) {
|
||||
int nspares = find_and_remove_spares(zhdl, vdev_guid);
|
||||
fmd_hdl_debug(hdl, "%d spares removed", nspares);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(class, "resource.fs.zfs.removed") == 0) {
|
||||
if (nvlist_lookup_uint64(nvl, FM_EREPORT_PAYLOAD_ZFS_POOL_GUID,
|
||||
&pool_guid) != 0)
|
||||
&pool_guid) != 0 ||
|
||||
nvlist_lookup_uint64(nvl, FM_EREPORT_PAYLOAD_ZFS_VDEV_GUID,
|
||||
&vdev_guid) != 0)
|
||||
return;
|
||||
|
||||
if ((zhp = find_by_guid(zhdl, pool_guid, vdev_guid,
|
||||
&vdev)) == NULL)
|
||||
return;
|
||||
|
||||
devname = zpool_vdev_name(NULL, zhp, vdev, B_FALSE);
|
||||
|
||||
nvlist_lookup_uint64_array(vdev, ZPOOL_CONFIG_VDEV_STATS,
|
||||
(uint64_t **)&vs, &c);
|
||||
|
||||
/*
|
||||
* If state removed is requested for already removed vdev,
|
||||
* its a loopback event from spa_async_remove(). Just
|
||||
* ignore it.
|
||||
*/
|
||||
if (vs->vs_state == VDEV_STATE_REMOVED &&
|
||||
state == VDEV_STATE_REMOVED)
|
||||
return;
|
||||
|
||||
/* Remove the vdev since device is unplugged */
|
||||
if (l2arc || (strcmp(class, "resource.fs.zfs.removed") == 0)) {
|
||||
int status = zpool_vdev_remove_wanted(zhp, devname);
|
||||
fmd_hdl_debug(hdl, "zpool_vdev_remove_wanted '%s'"
|
||||
", ret:%d", devname, status);
|
||||
}
|
||||
|
||||
/* Replace the vdev with a spare if its not a l2arc */
|
||||
if (!l2arc && (!fmd_prop_get_int32(hdl, "spare_on_remove") ||
|
||||
replace_with_spare(hdl, zhp, vdev) == B_FALSE)) {
|
||||
/* Could not handle with spare */
|
||||
fmd_hdl_debug(hdl, "no spare for '%s'", devname);
|
||||
}
|
||||
|
||||
free(devname);
|
||||
if (fmd_prop_get_int32(hdl, "spare_on_remove"))
|
||||
replace_with_spare(hdl, zhp, vdev);
|
||||
zpool_close(zhp);
|
||||
return;
|
||||
}
|
||||
@@ -466,11 +416,12 @@ zfs_retire_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl,
|
||||
return;
|
||||
|
||||
/*
|
||||
* Note: on Linux statechange events are more than just
|
||||
* Note: on zfsonlinux statechange events are more than just
|
||||
* healthy ones so we need to confirm the actual state value.
|
||||
*/
|
||||
if (strcmp(class, "resource.fs.zfs.statechange") == 0 &&
|
||||
state == VDEV_STATE_HEALTHY) {
|
||||
nvlist_lookup_uint64(nvl, FM_EREPORT_PAYLOAD_ZFS_VDEV_STATE,
|
||||
&state) == 0 && state == VDEV_STATE_HEALTHY) {
|
||||
zfs_vdev_repair(hdl, nvl);
|
||||
return;
|
||||
}
|
||||
@@ -526,7 +477,39 @@ zfs_retire_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl,
|
||||
}
|
||||
|
||||
if (is_disk) {
|
||||
#ifdef HAVE_LIBTOPO
|
||||
/*
|
||||
* This is a disk fault. Lookup the FRU, convert it to
|
||||
* an FMRI string, and attempt to find a matching vdev.
|
||||
*/
|
||||
if (nvlist_lookup_nvlist(fault, FM_FAULT_FRU,
|
||||
&fru) != 0 ||
|
||||
nvlist_lookup_string(fru, FM_FMRI_SCHEME,
|
||||
&scheme) != 0)
|
||||
continue;
|
||||
|
||||
if (strcmp(scheme, FM_FMRI_SCHEME_HC) != 0)
|
||||
continue;
|
||||
|
||||
thp = fmd_hdl_topo_hold(hdl, TOPO_VERSION);
|
||||
if (topo_fmri_nvl2str(thp, fru, &fmri, &err) != 0) {
|
||||
fmd_hdl_topo_rele(hdl, thp);
|
||||
continue;
|
||||
}
|
||||
|
||||
zhp = find_by_fru(zhdl, fmri, &vdev);
|
||||
topo_hdl_strfree(thp, fmri);
|
||||
fmd_hdl_topo_rele(hdl, thp);
|
||||
|
||||
if (zhp == NULL)
|
||||
continue;
|
||||
|
||||
(void) nvlist_lookup_uint64(vdev,
|
||||
ZPOOL_CONFIG_GUID, &vdev_guid);
|
||||
aux = VDEV_AUX_EXTERNAL;
|
||||
#else
|
||||
continue;
|
||||
#endif
|
||||
} else {
|
||||
/*
|
||||
* This is a ZFS fault. Lookup the resource, and
|
||||
@@ -600,8 +583,7 @@ zfs_retire_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl,
|
||||
/*
|
||||
* Attempt to substitute a hot spare.
|
||||
*/
|
||||
(void) replace_with_spare(hdl, zhp, vdev);
|
||||
|
||||
replace_with_spare(hdl, zhp, vdev);
|
||||
zpool_close(zhp);
|
||||
}
|
||||
|
||||
@@ -633,7 +615,7 @@ _zfs_retire_init(fmd_hdl_t *hdl)
|
||||
zfs_retire_data_t *zdp;
|
||||
libzfs_handle_t *zhdl;
|
||||
|
||||
if ((zhdl = libzfs_init()) == NULL)
|
||||
if ((zhdl = __libzfs_init()) == NULL)
|
||||
return;
|
||||
|
||||
if (fmd_hdl_register(hdl, FMD_API_VERSION, &fmd_info) != 0) {
|
||||
@@ -654,7 +636,7 @@ _zfs_retire_fini(fmd_hdl_t *hdl)
|
||||
|
||||
if (zdp != NULL) {
|
||||
zfs_retire_clear_data(hdl, zdp);
|
||||
libzfs_fini(zdp->zrd_hdl);
|
||||
__libzfs_fini(zdp->zrd_hdl);
|
||||
fmd_hdl_free(hdl, zdp, sizeof (zfs_retire_data_t));
|
||||
}
|
||||
}
|
||||
|
||||
+23
-50
@@ -1,9 +1,9 @@
|
||||
/*
|
||||
* This file is part of the ZFS Event Daemon (ZED).
|
||||
*
|
||||
* This file is part of the ZFS Event Daemon (ZED)
|
||||
* for ZFS on Linux (ZoL) <http://zfsonlinux.org/>.
|
||||
* Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
|
||||
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
|
||||
* Refer to the OpenZFS git commit log for authoritative copyright attribution.
|
||||
* Refer to the ZoL git commit log for authoritative copyright attribution.
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License Version 1.0 (CDDL-1.0).
|
||||
@@ -60,8 +60,8 @@ _setup_sig_handlers(void)
|
||||
zed_log_die("Failed to initialize sigset");
|
||||
|
||||
sa.sa_flags = SA_RESTART;
|
||||
|
||||
sa.sa_handler = SIG_IGN;
|
||||
|
||||
if (sigaction(SIGPIPE, &sa, NULL) < 0)
|
||||
zed_log_die("Failed to ignore SIGPIPE");
|
||||
|
||||
@@ -75,10 +75,6 @@ _setup_sig_handlers(void)
|
||||
sa.sa_handler = _hup_handler;
|
||||
if (sigaction(SIGHUP, &sa, NULL) < 0)
|
||||
zed_log_die("Failed to register SIGHUP handler");
|
||||
|
||||
(void) sigaddset(&sa.sa_mask, SIGCHLD);
|
||||
if (pthread_sigmask(SIG_BLOCK, &sa.sa_mask, NULL) < 0)
|
||||
zed_log_die("Failed to block SIGCHLD");
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -216,20 +212,22 @@ _finish_daemonize(void)
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct zed_conf zcp;
|
||||
struct zed_conf *zcp;
|
||||
uint64_t saved_eid;
|
||||
int64_t saved_etime[2];
|
||||
|
||||
zed_log_init(argv[0]);
|
||||
zed_log_stderr_open(LOG_NOTICE);
|
||||
zed_conf_init(&zcp);
|
||||
zed_conf_parse_opts(&zcp, argc, argv);
|
||||
if (zcp.do_verbose)
|
||||
zcp = zed_conf_create();
|
||||
zed_conf_parse_opts(zcp, argc, argv);
|
||||
if (zcp->do_verbose)
|
||||
zed_log_stderr_open(LOG_INFO);
|
||||
|
||||
if (geteuid() != 0)
|
||||
zed_log_die("Must be run as root");
|
||||
|
||||
zed_conf_parse_file(zcp);
|
||||
|
||||
zed_file_close_from(STDERR_FILENO + 1);
|
||||
|
||||
(void) umask(0);
|
||||
@@ -237,72 +235,47 @@ main(int argc, char *argv[])
|
||||
if (chdir("/") < 0)
|
||||
zed_log_die("Failed to change to root directory");
|
||||
|
||||
if (zed_conf_scan_dir(&zcp) < 0)
|
||||
if (zed_conf_scan_dir(zcp) < 0)
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
if (!zcp.do_foreground) {
|
||||
if (!zcp->do_foreground) {
|
||||
_start_daemonize();
|
||||
zed_log_syslog_open(LOG_DAEMON);
|
||||
}
|
||||
_setup_sig_handlers();
|
||||
|
||||
if (zcp.do_memlock)
|
||||
if (zcp->do_memlock)
|
||||
_lock_memory();
|
||||
|
||||
if ((zed_conf_write_pid(&zcp) < 0) && (!zcp.do_force))
|
||||
if ((zed_conf_write_pid(zcp) < 0) && (!zcp->do_force))
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
if (!zcp.do_foreground)
|
||||
if (!zcp->do_foreground)
|
||||
_finish_daemonize();
|
||||
|
||||
zed_log_msg(LOG_NOTICE,
|
||||
"ZFS Event Daemon %s-%s (PID %d)",
|
||||
ZFS_META_VERSION, ZFS_META_RELEASE, (int)getpid());
|
||||
|
||||
if (zed_conf_open_state(&zcp) < 0)
|
||||
if (zed_conf_open_state(zcp) < 0)
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
if (zed_conf_read_state(&zcp, &saved_eid, saved_etime) < 0)
|
||||
if (zed_conf_read_state(zcp, &saved_eid, saved_etime) < 0)
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
idle:
|
||||
/*
|
||||
* If -I is specified, attempt to open /dev/zfs repeatedly until
|
||||
* successful.
|
||||
*/
|
||||
do {
|
||||
if (!zed_event_init(&zcp))
|
||||
break;
|
||||
/* Wait for some time and try again. tunable? */
|
||||
sleep(30);
|
||||
} while (!_got_exit && zcp.do_idle);
|
||||
|
||||
if (_got_exit)
|
||||
goto out;
|
||||
|
||||
zed_event_seek(&zcp, saved_eid, saved_etime);
|
||||
zed_event_init(zcp);
|
||||
zed_event_seek(zcp, saved_eid, saved_etime);
|
||||
|
||||
while (!_got_exit) {
|
||||
int rv;
|
||||
if (_got_hup) {
|
||||
_got_hup = 0;
|
||||
(void) zed_conf_scan_dir(&zcp);
|
||||
(void) zed_conf_scan_dir(zcp);
|
||||
}
|
||||
rv = zed_event_service(&zcp);
|
||||
|
||||
/* ENODEV: When kernel module is unloaded (osx) */
|
||||
if (rv != 0)
|
||||
break;
|
||||
zed_event_service(zcp);
|
||||
}
|
||||
|
||||
zed_log_msg(LOG_NOTICE, "Exiting");
|
||||
zed_event_fini(&zcp);
|
||||
|
||||
if (zcp.do_idle && !_got_exit)
|
||||
goto idle;
|
||||
|
||||
out:
|
||||
zed_conf_destroy(&zcp);
|
||||
zed_event_fini(zcp);
|
||||
zed_conf_destroy(zcp);
|
||||
zed_log_fini();
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
history_event-zfs-list-cacher.sh
|
||||
@@ -1,57 +0,0 @@
|
||||
include $(top_srcdir)/config/Rules.am
|
||||
include $(top_srcdir)/config/Substfiles.am
|
||||
include $(top_srcdir)/config/Shellcheck.am
|
||||
|
||||
EXTRA_DIST += README
|
||||
|
||||
zedconfdir = $(sysconfdir)/zfs/zed.d
|
||||
|
||||
dist_zedconf_DATA = \
|
||||
zed-functions.sh \
|
||||
zed.rc
|
||||
|
||||
zedexecdir = $(zfsexecdir)/zed.d
|
||||
|
||||
dist_zedexec_SCRIPTS = \
|
||||
all-debug.sh \
|
||||
all-syslog.sh \
|
||||
data-notify.sh \
|
||||
generic-notify.sh \
|
||||
resilver_finish-notify.sh \
|
||||
scrub_finish-notify.sh \
|
||||
statechange-led.sh \
|
||||
statechange-notify.sh \
|
||||
vdev_clear-led.sh \
|
||||
vdev_attach-led.sh \
|
||||
pool_import-led.sh \
|
||||
resilver_finish-start-scrub.sh \
|
||||
trim_finish-notify.sh
|
||||
|
||||
nodist_zedexec_SCRIPTS = history_event-zfs-list-cacher.sh
|
||||
|
||||
SUBSTFILES += $(nodist_zedexec_SCRIPTS)
|
||||
|
||||
zedconfdefaults = \
|
||||
all-syslog.sh \
|
||||
data-notify.sh \
|
||||
history_event-zfs-list-cacher.sh \
|
||||
resilver_finish-notify.sh \
|
||||
scrub_finish-notify.sh \
|
||||
statechange-led.sh \
|
||||
statechange-notify.sh \
|
||||
vdev_clear-led.sh \
|
||||
vdev_attach-led.sh \
|
||||
pool_import-led.sh \
|
||||
resilver_finish-start-scrub.sh
|
||||
|
||||
install-data-hook:
|
||||
$(MKDIR_P) "$(DESTDIR)$(zedconfdir)"
|
||||
for f in $(zedconfdefaults); do \
|
||||
test -f "$(DESTDIR)$(zedconfdir)/$${f}" -o \
|
||||
-L "$(DESTDIR)$(zedconfdir)/$${f}" || \
|
||||
ln -s "$(zedexecdir)/$${f}" "$(DESTDIR)$(zedconfdir)"; \
|
||||
done
|
||||
chmod 0600 "$(DESTDIR)$(zedconfdir)/zed.rc"
|
||||
|
||||
# False positive: 1>&"${ZED_FLOCK_FD}" looks suspiciously similar to a >&filename bash extension
|
||||
CHECKBASHISMS_IGNORE = -e 'should be >word 2>&1' -e '&"$${ZED_FLOCK_FD}"'
|
||||
@@ -12,11 +12,15 @@
|
||||
|
||||
zed_exit_if_ignoring_this_event
|
||||
|
||||
zed_lock "${ZED_DEBUG_LOG}"
|
||||
{
|
||||
printenv | sort
|
||||
echo
|
||||
} 1>&"${ZED_FLOCK_FD}"
|
||||
zed_unlock "${ZED_DEBUG_LOG}"
|
||||
lockfile="$(basename -- "${ZED_DEBUG_LOG}").lock"
|
||||
|
||||
umask 077
|
||||
zed_lock "${lockfile}"
|
||||
exec >> "${ZED_DEBUG_LOG}"
|
||||
|
||||
printenv | sort
|
||||
echo
|
||||
|
||||
exec >&-
|
||||
zed_unlock "${lockfile}"
|
||||
exit 0
|
||||
|
||||
@@ -1,51 +1,14 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
|
||||
# Copyright (c) 2020 by Delphix. All rights reserved.
|
||||
#
|
||||
|
||||
#
|
||||
# Log the zevent via syslog.
|
||||
#
|
||||
|
||||
[ -f "${ZED_ZEDLET_DIR}/zed.rc" ] && . "${ZED_ZEDLET_DIR}/zed.rc"
|
||||
. "${ZED_ZEDLET_DIR}/zed-functions.sh"
|
||||
|
||||
zed_exit_if_ignoring_this_event
|
||||
|
||||
# build a string of name=value pairs for this event
|
||||
msg="eid=${ZEVENT_EID} class=${ZEVENT_SUBCLASS}"
|
||||
|
||||
if [ "${ZED_SYSLOG_DISPLAY_GUIDS}" = "1" ]; then
|
||||
[ -n "${ZEVENT_POOL_GUID}" ] && msg="${msg} pool_guid=${ZEVENT_POOL_GUID}"
|
||||
[ -n "${ZEVENT_VDEV_GUID}" ] && msg="${msg} vdev_guid=${ZEVENT_VDEV_GUID}"
|
||||
else
|
||||
[ -n "${ZEVENT_POOL}" ] && msg="${msg} pool='${ZEVENT_POOL}'"
|
||||
[ -n "${ZEVENT_VDEV_PATH}" ] && msg="${msg} vdev=${ZEVENT_VDEV_PATH##*/}"
|
||||
fi
|
||||
|
||||
# log pool state if state is anything other than 'ACTIVE'
|
||||
[ -n "${ZEVENT_POOL_STATE_STR}" ] && [ "$ZEVENT_POOL_STATE" -ne 0 ] && \
|
||||
msg="${msg} pool_state=${ZEVENT_POOL_STATE_STR}"
|
||||
|
||||
# Log the following payload nvpairs if they are present
|
||||
[ -n "${ZEVENT_VDEV_STATE_STR}" ] && msg="${msg} vdev_state=${ZEVENT_VDEV_STATE_STR}"
|
||||
[ -n "${ZEVENT_CKSUM_ALGORITHM}" ] && msg="${msg} algorithm=${ZEVENT_CKSUM_ALGORITHM}"
|
||||
[ -n "${ZEVENT_ZIO_SIZE}" ] && msg="${msg} size=${ZEVENT_ZIO_SIZE}"
|
||||
[ -n "${ZEVENT_ZIO_OFFSET}" ] && msg="${msg} offset=${ZEVENT_ZIO_OFFSET}"
|
||||
[ -n "${ZEVENT_ZIO_PRIORITY}" ] && msg="${msg} priority=${ZEVENT_ZIO_PRIORITY}"
|
||||
[ -n "${ZEVENT_ZIO_ERR}" ] && msg="${msg} err=${ZEVENT_ZIO_ERR}"
|
||||
[ -n "${ZEVENT_ZIO_FLAGS}" ] && msg="${msg} flags=$(printf '0x%x' "${ZEVENT_ZIO_FLAGS}")"
|
||||
|
||||
# log delays that are >= 10 milisec
|
||||
[ -n "${ZEVENT_ZIO_DELAY}" ] && [ "$ZEVENT_ZIO_DELAY" -gt 10000000 ] && \
|
||||
msg="${msg} delay=$((ZEVENT_ZIO_DELAY / 1000000))ms"
|
||||
|
||||
# list the bookmark data together
|
||||
# shellcheck disable=SC2153
|
||||
[ -n "${ZEVENT_ZIO_OBJSET}" ] && \
|
||||
msg="${msg} bookmark=${ZEVENT_ZIO_OBJSET}:${ZEVENT_ZIO_OBJECT}:${ZEVENT_ZIO_LEVEL}:${ZEVENT_ZIO_BLKID}"
|
||||
|
||||
zed_log_msg "${msg}"
|
||||
|
||||
zed_log_msg "eid=${ZEVENT_EID}" "class=${ZEVENT_SUBCLASS}" \
|
||||
"${ZEVENT_POOL_GUID:+"pool_guid=${ZEVENT_POOL_GUID}"}" \
|
||||
"${ZEVENT_VDEV_PATH:+"vdev_path=${ZEVENT_VDEV_PATH}"}" \
|
||||
"${ZEVENT_VDEV_STATE_STR:+"vdev_state=${ZEVENT_VDEV_STATE_STR}"}"
|
||||
exit 0
|
||||
|
||||
@@ -25,7 +25,7 @@ zed_rate_limit "${rate_limit_tag}" || exit 3
|
||||
|
||||
umask 077
|
||||
note_subject="ZFS ${ZEVENT_SUBCLASS} error for ${ZEVENT_POOL} on $(hostname)"
|
||||
note_pathname="$(mktemp)"
|
||||
note_pathname="${TMPDIR:="/tmp"}/$(basename -- "$0").${ZEVENT_EID}.$$"
|
||||
{
|
||||
echo "ZFS has detected a data error:"
|
||||
echo
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
# Rate-limit the notification based in part on the filename.
|
||||
#
|
||||
rate_limit_tag="${ZEVENT_POOL};${ZEVENT_SUBCLASS};${0##*/}"
|
||||
rate_limit_tag="${ZEVENT_POOL};${ZEVENT_SUBCLASS};$(basename -- "$0")"
|
||||
rate_limit_interval="${ZED_NOTIFY_INTERVAL_SECS}"
|
||||
zed_rate_limit "${rate_limit_tag}" "${rate_limit_interval}" || exit 3
|
||||
|
||||
@@ -31,7 +31,7 @@ umask 077
|
||||
pool_str="${ZEVENT_POOL:+" for ${ZEVENT_POOL}"}"
|
||||
host_str=" on $(hostname)"
|
||||
note_subject="ZFS ${ZEVENT_SUBCLASS} event${pool_str}${host_str}"
|
||||
note_pathname="$(mktemp)"
|
||||
note_pathname="${TMPDIR:="/tmp"}/$(basename -- "$0").${ZEVENT_EID}.$$"
|
||||
{
|
||||
echo "ZFS has posted the following event:"
|
||||
echo
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Track changes to enumerated pools for use in early-boot
|
||||
set -ef
|
||||
|
||||
FSLIST="@sysconfdir@/zfs/zfs-list.cache/${ZEVENT_POOL}"
|
||||
FSLIST_TMP="@runstatedir@/zfs-list.cache@${ZEVENT_POOL}"
|
||||
|
||||
# If the pool specific cache file is not writeable, abort
|
||||
[ -w "${FSLIST}" ] || exit 0
|
||||
|
||||
[ -f "${ZED_ZEDLET_DIR}/zed.rc" ] && . "${ZED_ZEDLET_DIR}/zed.rc"
|
||||
. "${ZED_ZEDLET_DIR}/zed-functions.sh"
|
||||
|
||||
[ "$ZEVENT_SUBCLASS" != "history_event" ] && exit 0
|
||||
zed_check_cmd "${ZFS}" sort diff
|
||||
|
||||
# If we are acting on a snapshot, we have nothing to do
|
||||
[ "${ZEVENT_HISTORY_DSNAME%@*}" = "${ZEVENT_HISTORY_DSNAME}" ] || exit 0
|
||||
|
||||
# We lock the output file to avoid simultaneous writes.
|
||||
# If we run into trouble, log and drop the lock
|
||||
abort_alter() {
|
||||
zed_log_msg "Error updating zfs-list.cache for ${ZEVENT_POOL}!"
|
||||
zed_unlock "${FSLIST}"
|
||||
}
|
||||
|
||||
finished() {
|
||||
zed_unlock "${FSLIST}"
|
||||
trap - EXIT
|
||||
exit 0
|
||||
}
|
||||
|
||||
case "${ZEVENT_HISTORY_INTERNAL_NAME}" in
|
||||
create|"finish receiving"|import|destroy|rename)
|
||||
;;
|
||||
|
||||
export)
|
||||
zed_lock "${FSLIST}"
|
||||
trap abort_alter EXIT
|
||||
echo > "${FSLIST}"
|
||||
finished
|
||||
;;
|
||||
|
||||
set|inherit)
|
||||
# Only act if one of the tracked properties is altered.
|
||||
case "${ZEVENT_HISTORY_INTERNAL_STR%%=*}" in
|
||||
canmount|mountpoint|atime|relatime|devices|exec|readonly| \
|
||||
setuid|nbmand|encroot|keylocation|org.openzfs.systemd:requires| \
|
||||
org.openzfs.systemd:requires-mounts-for| \
|
||||
org.openzfs.systemd:before|org.openzfs.systemd:after| \
|
||||
org.openzfs.systemd:wanted-by|org.openzfs.systemd:required-by| \
|
||||
org.openzfs.systemd:nofail|org.openzfs.systemd:ignore \
|
||||
) ;;
|
||||
*) exit 0 ;;
|
||||
esac
|
||||
;;
|
||||
|
||||
*)
|
||||
# Ignore all other events.
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
zed_lock "${FSLIST}"
|
||||
trap abort_alter EXIT
|
||||
|
||||
PROPS="name,mountpoint,canmount,atime,relatime,devices,exec\
|
||||
,readonly,setuid,nbmand,encroot,keylocation\
|
||||
,org.openzfs.systemd:requires,org.openzfs.systemd:requires-mounts-for\
|
||||
,org.openzfs.systemd:before,org.openzfs.systemd:after\
|
||||
,org.openzfs.systemd:wanted-by,org.openzfs.systemd:required-by\
|
||||
,org.openzfs.systemd:nofail,org.openzfs.systemd:ignore"
|
||||
|
||||
"${ZFS}" list -H -t filesystem -o $PROPS -r "${ZEVENT_POOL}" > "${FSLIST_TMP}"
|
||||
|
||||
# Sort the output so that it is stable
|
||||
sort "${FSLIST_TMP}" -o "${FSLIST_TMP}"
|
||||
|
||||
# Don't modify the file if it hasn't changed
|
||||
diff -q "${FSLIST_TMP}" "${FSLIST}" || cat "${FSLIST_TMP}" > "${FSLIST}"
|
||||
rm -f "${FSLIST_TMP}"
|
||||
|
||||
finished
|
||||
@@ -5,12 +5,10 @@
|
||||
# Exit codes:
|
||||
# 1: Internal error
|
||||
# 2: Script wasn't enabled in zed.rc
|
||||
# 3: Scrubs are automatically started for sequential resilvers
|
||||
[ -f "${ZED_ZEDLET_DIR}/zed.rc" ] && . "${ZED_ZEDLET_DIR}/zed.rc"
|
||||
. "${ZED_ZEDLET_DIR}/zed-functions.sh"
|
||||
|
||||
[ "${ZED_SCRUB_AFTER_RESILVER}" = "1" ] || exit 2
|
||||
[ "${ZEVENT_RESILVER_TYPE}" != "sequential" ] || exit 3
|
||||
[ -n "${ZEVENT_POOL}" ] || exit 1
|
||||
[ -n "${ZEVENT_SUBCLASS}" ] || exit 1
|
||||
zed_check_cmd "${ZPOOL}" || exit 1
|
||||
|
||||
@@ -41,7 +41,7 @@ fi
|
||||
|
||||
umask 077
|
||||
note_subject="ZFS ${ZEVENT_SUBCLASS} event for ${ZEVENT_POOL} on $(hostname)"
|
||||
note_pathname="$(mktemp)"
|
||||
note_pathname="${TMPDIR:="/tmp"}/$(basename -- "$0").${ZEVENT_EID}.$$"
|
||||
{
|
||||
echo "ZFS has finished a ${action}:"
|
||||
echo
|
||||
|
||||
@@ -1,26 +1,26 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Turn off/on vdevs' enclosure fault LEDs when their pool's state changes.
|
||||
# Turn off/on the VDEV's enclosure fault LEDs when the pool's state changes.
|
||||
#
|
||||
# Turn a vdev's fault LED on if it becomes FAULTED, DEGRADED or UNAVAIL.
|
||||
# Turn its LED off when it's back ONLINE again.
|
||||
# Turn the VDEV's fault LED on if it becomes FAULTED, DEGRADED or UNAVAIL.
|
||||
# Turn the LED off when it's back ONLINE again.
|
||||
#
|
||||
# This script run in two basic modes:
|
||||
#
|
||||
# 1. If $ZEVENT_VDEV_ENC_SYSFS_PATH and $ZEVENT_VDEV_STATE_STR are set, then
|
||||
# only set the LED for that particular vdev. This is the case for statechange
|
||||
# only set the LED for that particular VDEV. This is the case for statechange
|
||||
# events and some vdev_* events.
|
||||
#
|
||||
# 2. If those vars are not set, then check the state of all vdevs in the pool
|
||||
# 2. If those vars are not set, then check the state of all VDEVs in the pool
|
||||
# and set the LEDs accordingly. This is the case for pool_import events.
|
||||
#
|
||||
# Note that this script requires that your enclosure be supported by the
|
||||
# Linux SCSI Enclosure services (SES) driver. The script will do nothing
|
||||
# Linux SCSI enclosure services (ses) driver. The script will do nothing
|
||||
# if you have no enclosure, or if your enclosure isn't supported.
|
||||
#
|
||||
# Exit codes:
|
||||
# 0: enclosure led successfully set
|
||||
# 1: enclosure leds not available
|
||||
# 1: enclosure leds not not available
|
||||
# 2: enclosure leds administratively disabled
|
||||
# 3: The led sysfs path passed from ZFS does not exist
|
||||
# 4: $ZPOOL not set
|
||||
@@ -29,8 +29,7 @@
|
||||
[ -f "${ZED_ZEDLET_DIR}/zed.rc" ] && . "${ZED_ZEDLET_DIR}/zed.rc"
|
||||
. "${ZED_ZEDLET_DIR}/zed-functions.sh"
|
||||
|
||||
if [ ! -d /sys/class/enclosure ] && [ ! -d /sys/bus/pci/slots ] ; then
|
||||
# No JBOD enclosure or NVMe slots
|
||||
if [ ! -d /sys/class/enclosure ] ; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -60,10 +59,6 @@ check_and_set_led()
|
||||
file="$1"
|
||||
val="$2"
|
||||
|
||||
if [ -z "$val" ]; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
if [ ! -e "$file" ] ; then
|
||||
return 3
|
||||
fi
|
||||
@@ -71,11 +66,11 @@ check_and_set_led()
|
||||
# If another process is accessing the LED when we attempt to update it,
|
||||
# the update will be lost so retry until the LED actually changes or we
|
||||
# timeout.
|
||||
for _ in 1 2 3 4 5; do
|
||||
for _ in $(seq 1 5); do
|
||||
# We want to check the current state first, since writing to the
|
||||
# 'fault' entry always causes a SES command, even if the
|
||||
# 'fault' entry always always causes a SES command, even if the
|
||||
# current state is already what you want.
|
||||
read -r current < "${file}"
|
||||
current=$(cat "${file}")
|
||||
|
||||
# On some enclosures if you write 1 to fault, and read it back,
|
||||
# it will return 2. Treat all non-zero values as 1 for
|
||||
@@ -90,84 +85,27 @@ check_and_set_led()
|
||||
else
|
||||
break
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
# Fault LEDs for JBODs and NVMe drives are handled a little differently.
|
||||
#
|
||||
# On JBODs the fault LED is called 'fault' and on a path like this:
|
||||
#
|
||||
# /sys/class/enclosure/0:0:1:0/SLOT 10/fault
|
||||
#
|
||||
# On NVMe it's called 'attention' and on a path like this:
|
||||
#
|
||||
# /sys/bus/pci/slot/0/attention
|
||||
#
|
||||
# This function returns the full path to the fault LED file for a given
|
||||
# enclosure/slot directory.
|
||||
#
|
||||
path_to_led()
|
||||
{
|
||||
dir=$1
|
||||
if [ -f "$dir/fault" ] ; then
|
||||
echo "$dir/fault"
|
||||
elif [ -f "$dir/attention" ] ; then
|
||||
echo "$dir/attention"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
state_to_val()
|
||||
{
|
||||
state="$1"
|
||||
case "$state" in
|
||||
FAULTED|DEGRADED|UNAVAIL)
|
||||
echo 1
|
||||
;;
|
||||
ONLINE)
|
||||
echo 0
|
||||
;;
|
||||
esac
|
||||
if [ "$state" = "FAULTED" ] || [ "$state" = "DEGRADED" ] || \
|
||||
[ "$state" = "UNAVAIL" ] ; then
|
||||
echo 1
|
||||
elif [ "$state" = "ONLINE" ] ; then
|
||||
echo 0
|
||||
fi
|
||||
}
|
||||
|
||||
# process_pool ([pool])
|
||||
#
|
||||
# Given a nvme name like 'nvme0n1', pass back its slot directory
|
||||
# like "/sys/bus/pci/slots/0"
|
||||
#
|
||||
nvme_dev_to_slot()
|
||||
{
|
||||
dev="$1"
|
||||
|
||||
# Get the address "0000:01:00.0"
|
||||
address=$(cat "/sys/class/block/$dev/device/address")
|
||||
|
||||
# For each /sys/bus/pci/slots subdir that is an actual number
|
||||
# (rather than weird directories like "1-3/").
|
||||
# shellcheck disable=SC2010
|
||||
for i in $(ls /sys/bus/pci/slots/ | grep -E "^[0-9]+$") ; do
|
||||
this_address=$(cat "/sys/bus/pci/slots/$i/address")
|
||||
|
||||
# The format of address is a little different between
|
||||
# /sys/class/block/$dev/device/address and
|
||||
# /sys/bus/pci/slots/
|
||||
#
|
||||
# address= "0000:01:00.0"
|
||||
# this_address = "0000:01:00"
|
||||
#
|
||||
if echo "$address" | grep -Eq ^"$this_address" ; then
|
||||
echo "/sys/bus/pci/slots/$i"
|
||||
break
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
# process_pool (pool)
|
||||
#
|
||||
# Iterate through a pool and set the vdevs' enclosure slot LEDs to
|
||||
# those vdevs' state.
|
||||
# Iterate through a pool (or pools) and set the VDEV's enclosure slot LEDs to
|
||||
# the VDEV's state.
|
||||
#
|
||||
# Arguments
|
||||
# pool: Pool name.
|
||||
# pool: Optional pool name. If not specified, iterate though all pools.
|
||||
#
|
||||
# Return
|
||||
# 0 on success, 3 on missing sysfs path
|
||||
@@ -175,27 +113,19 @@ nvme_dev_to_slot()
|
||||
process_pool()
|
||||
{
|
||||
pool="$1"
|
||||
|
||||
# The output will be the vdevs only (from "grep '/dev/'"):
|
||||
#
|
||||
# U45 ONLINE 0 0 0 /dev/sdk 0
|
||||
# U46 ONLINE 0 0 0 /dev/sdm 0
|
||||
# U47 ONLINE 0 0 0 /dev/sdn 0
|
||||
# U50 ONLINE 0 0 0 /dev/sdbn 0
|
||||
#
|
||||
ZPOOL_SCRIPTS_AS_ROOT=1 $ZPOOL status -c upath,fault_led "$pool" | grep '/dev/' | (
|
||||
rc=0
|
||||
while read -r vdev state _ _ _ therest; do
|
||||
# Read out current LED value and path
|
||||
# Get dev name (like 'sda')
|
||||
dev=$(basename "$(echo "$therest" | awk '{print $(NF-1)}')")
|
||||
vdev_enc_sysfs_path=$(realpath "/sys/class/block/$dev/device/enclosure_device"*)
|
||||
if [ ! -d "$vdev_enc_sysfs_path" ] ; then
|
||||
# This is not a JBOD disk, but it could be a PCI NVMe drive
|
||||
vdev_enc_sysfs_path=$(nvme_dev_to_slot "$dev")
|
||||
fi
|
||||
|
||||
current_val=$(echo "$therest" | awk '{print $NF}')
|
||||
# Lookup all the current LED values and paths in parallel
|
||||
#shellcheck disable=SC2016
|
||||
cmd='echo led_token=$(cat "$VDEV_ENC_SYSFS_PATH/fault"),"$VDEV_ENC_SYSFS_PATH",'
|
||||
out=$($ZPOOL status -vc "$cmd" "$pool" | grep 'led_token=')
|
||||
|
||||
#shellcheck disable=SC2034
|
||||
echo "$out" | while read -r vdev state read write chksum therest; do
|
||||
# Read out current LED value and path
|
||||
tmp=$(echo "$therest" | sed 's/^.*led_token=//g')
|
||||
vdev_enc_sysfs_path=$(echo "$tmp" | awk -F ',' '{print $2}')
|
||||
current_val=$(echo "$tmp" | awk -F ',' '{print $1}')
|
||||
|
||||
if [ "$current_val" != "0" ] ; then
|
||||
current_val=1
|
||||
@@ -206,33 +136,40 @@ process_pool()
|
||||
continue
|
||||
fi
|
||||
|
||||
led_path=$(path_to_led "$vdev_enc_sysfs_path")
|
||||
if [ ! -e "$led_path" ] ; then
|
||||
rc=3
|
||||
zed_log_msg "vdev $vdev '$led_path' doesn't exist"
|
||||
continue
|
||||
if [ ! -e "$vdev_enc_sysfs_path/fault" ] ; then
|
||||
#shellcheck disable=SC2030
|
||||
rc=1
|
||||
zed_log_msg "vdev $vdev '$file/fault' doesn't exist"
|
||||
continue;
|
||||
fi
|
||||
|
||||
val=$(state_to_val "$state")
|
||||
|
||||
if [ "$current_val" = "$val" ] ; then
|
||||
# LED is already set correctly
|
||||
continue
|
||||
continue;
|
||||
fi
|
||||
|
||||
if ! check_and_set_led "$led_path" "$val"; then
|
||||
rc=3
|
||||
if ! check_and_set_led "$vdev_enc_sysfs_path/fault" "$val"; then
|
||||
rc=1
|
||||
fi
|
||||
|
||||
done
|
||||
exit "$rc"; )
|
||||
|
||||
#shellcheck disable=SC2031
|
||||
if [ "$rc" = "0" ] ; then
|
||||
return 0
|
||||
else
|
||||
# We didn't see a sysfs entry that we wanted to set
|
||||
return 3
|
||||
fi
|
||||
}
|
||||
|
||||
if [ -n "$ZEVENT_VDEV_ENC_SYSFS_PATH" ] && [ -n "$ZEVENT_VDEV_STATE_STR" ] ; then
|
||||
# Got a statechange for an individual vdev
|
||||
if [ ! -z "$ZEVENT_VDEV_ENC_SYSFS_PATH" ] && [ ! -z "$ZEVENT_VDEV_STATE_STR" ] ; then
|
||||
# Got a statechange for an individual VDEV
|
||||
val=$(state_to_val "$ZEVENT_VDEV_STATE_STR")
|
||||
vdev=$(basename "$ZEVENT_VDEV_PATH")
|
||||
ledpath=$(path_to_led "$ZEVENT_VDEV_ENC_SYSFS_PATH")
|
||||
check_and_set_led "$ledpath" "$val"
|
||||
check_and_set_led "$ZEVENT_VDEV_ENC_SYSFS_PATH/fault" "$val"
|
||||
else
|
||||
# Process the entire pool
|
||||
poolname=$(zed_guid_to_pool "$ZEVENT_POOL_GUID")
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
# Send notification in response to a fault induced statechange
|
||||
#
|
||||
# ZEVENT_SUBCLASS: 'statechange'
|
||||
# ZEVENT_VDEV_STATE_STR: 'DEGRADED', 'FAULTED', 'REMOVED', or 'UNAVAIL'
|
||||
# ZEVENT_VDEV_STATE_STR: 'DEGRADED', 'FAULTED' or 'REMOVED'
|
||||
#
|
||||
# Exit codes:
|
||||
# 0: notification sent
|
||||
@@ -31,14 +31,13 @@
|
||||
|
||||
if [ "${ZEVENT_VDEV_STATE_STR}" != "FAULTED" ] \
|
||||
&& [ "${ZEVENT_VDEV_STATE_STR}" != "DEGRADED" ] \
|
||||
&& [ "${ZEVENT_VDEV_STATE_STR}" != "REMOVED" ] \
|
||||
&& [ "${ZEVENT_VDEV_STATE_STR}" != "UNAVAIL" ]; then
|
||||
&& [ "${ZEVENT_VDEV_STATE_STR}" != "REMOVED" ]; then
|
||||
exit 3
|
||||
fi
|
||||
|
||||
umask 077
|
||||
note_subject="ZFS device fault for pool ${ZEVENT_POOL} on $(hostname)"
|
||||
note_pathname="$(mktemp)"
|
||||
note_subject="ZFS device fault for pool ${ZEVENT_POOL_GUID} on $(hostname)"
|
||||
note_pathname="${TMPDIR:="/tmp"}/$(basename -- "$0").${ZEVENT_EID}.$$"
|
||||
{
|
||||
if [ "${ZEVENT_VDEV_STATE_STR}" = "FAULTED" ] ; then
|
||||
echo "The number of I/O errors associated with a ZFS device exceeded"
|
||||
@@ -65,7 +64,7 @@ note_pathname="$(mktemp)"
|
||||
[ -n "${ZEVENT_VDEV_GUID}" ] && echo " vguid: ${ZEVENT_VDEV_GUID}"
|
||||
[ -n "${ZEVENT_VDEV_DEVID}" ] && echo " devid: ${ZEVENT_VDEV_DEVID}"
|
||||
|
||||
echo " pool: ${ZEVENT_POOL} (${ZEVENT_POOL_GUID})"
|
||||
echo " pool: ${ZEVENT_POOL_GUID}"
|
||||
|
||||
} > "${note_pathname}"
|
||||
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Send notification in response to a TRIM_FINISH. The event
|
||||
# will be received for each vdev in the pool which was trimmed.
|
||||
#
|
||||
# Exit codes:
|
||||
# 0: notification sent
|
||||
# 1: notification failed
|
||||
# 2: notification not configured
|
||||
# 9: internal error
|
||||
|
||||
[ -f "${ZED_ZEDLET_DIR}/zed.rc" ] && . "${ZED_ZEDLET_DIR}/zed.rc"
|
||||
. "${ZED_ZEDLET_DIR}/zed-functions.sh"
|
||||
|
||||
[ -n "${ZEVENT_POOL}" ] || exit 9
|
||||
[ -n "${ZEVENT_SUBCLASS}" ] || exit 9
|
||||
|
||||
zed_check_cmd "${ZPOOL}" || exit 9
|
||||
|
||||
umask 077
|
||||
note_subject="ZFS ${ZEVENT_SUBCLASS} event for ${ZEVENT_POOL} on $(hostname)"
|
||||
note_pathname="$(mktemp)"
|
||||
{
|
||||
echo "ZFS has finished a trim:"
|
||||
echo
|
||||
echo " eid: ${ZEVENT_EID}"
|
||||
echo " class: ${ZEVENT_SUBCLASS}"
|
||||
echo " host: $(hostname)"
|
||||
echo " time: ${ZEVENT_TIME_STRING}"
|
||||
|
||||
"${ZPOOL}" status -t "${ZEVENT_POOL}"
|
||||
|
||||
} > "${note_pathname}"
|
||||
|
||||
zed_notify "${note_subject}" "${note_pathname}"; rv=$?
|
||||
rm -f "${note_pathname}"
|
||||
exit "${rv}"
|
||||
+18
-185
@@ -77,7 +77,7 @@ zed_log_msg()
|
||||
zed_log_err()
|
||||
{
|
||||
logger -p "${ZED_SYSLOG_PRIORITY}" -t "${ZED_SYSLOG_TAG}" -- "error:" \
|
||||
"${0##*/}:""${ZEVENT_EID:+" eid=${ZEVENT_EID}:"}" "$@"
|
||||
"$(basename -- "$0"):""${ZEVENT_EID:+" eid=${ZEVENT_EID}:"}" "$@"
|
||||
}
|
||||
|
||||
|
||||
@@ -126,8 +126,10 @@ zed_lock()
|
||||
|
||||
# Obtain a lock on the file bound to the given file descriptor.
|
||||
#
|
||||
eval "exec ${fd}>> '${lockfile}'"
|
||||
if ! err="$(flock --exclusive "${fd}" 2>&1)"; then
|
||||
eval "exec ${fd}> '${lockfile}'"
|
||||
err="$(flock --exclusive "${fd}" 2>&1)"
|
||||
# shellcheck disable=SC2181
|
||||
if [ $? -ne 0 ]; then
|
||||
zed_log_err "failed to lock \"${lockfile}\": ${err}"
|
||||
fi
|
||||
|
||||
@@ -163,7 +165,9 @@ zed_unlock()
|
||||
fi
|
||||
|
||||
# Release the lock and close the file descriptor.
|
||||
if ! err="$(flock --unlock "${fd}" 2>&1)"; then
|
||||
err="$(flock --unlock "${fd}" 2>&1)"
|
||||
# shellcheck disable=SC2181
|
||||
if [ $? -ne 0 ]; then
|
||||
zed_log_err "failed to unlock \"${lockfile}\": ${err}"
|
||||
fi
|
||||
eval "exec ${fd}>&-"
|
||||
@@ -198,14 +202,6 @@ zed_notify()
|
||||
[ "${rv}" -eq 0 ] && num_success=$((num_success + 1))
|
||||
[ "${rv}" -eq 1 ] && num_failure=$((num_failure + 1))
|
||||
|
||||
zed_notify_slack_webhook "${subject}" "${pathname}"; rv=$?
|
||||
[ "${rv}" -eq 0 ] && num_success=$((num_success + 1))
|
||||
[ "${rv}" -eq 1 ] && num_failure=$((num_failure + 1))
|
||||
|
||||
zed_notify_pushover "${subject}" "${pathname}"; rv=$?
|
||||
[ "${rv}" -eq 0 ] && num_success=$((num_success + 1))
|
||||
[ "${rv}" -eq 1 ] && num_failure=$((num_failure + 1))
|
||||
|
||||
[ "${num_success}" -gt 0 ] && return 0
|
||||
[ "${num_failure}" -gt 0 ] && return 1
|
||||
return 2
|
||||
@@ -224,8 +220,6 @@ zed_notify()
|
||||
# ZED_EMAIL_OPTS. This undergoes the following keyword substitutions:
|
||||
# - @ADDRESS@ is replaced with the space-delimited recipient email address(es)
|
||||
# - @SUBJECT@ is replaced with the notification subject
|
||||
# If @SUBJECT@ was omited here, a "Subject: ..." header will be added to notification
|
||||
#
|
||||
#
|
||||
# Arguments
|
||||
# subject: notification subject
|
||||
@@ -243,7 +237,7 @@ zed_notify()
|
||||
#
|
||||
zed_notify_email()
|
||||
{
|
||||
local subject="${1:-"ZED notification"}"
|
||||
local subject="$1"
|
||||
local pathname="${2:-"/dev/null"}"
|
||||
|
||||
: "${ZED_EMAIL_PROG:="mail"}"
|
||||
@@ -260,30 +254,19 @@ zed_notify_email()
|
||||
[ -n "${subject}" ] || return 1
|
||||
if [ ! -r "${pathname}" ]; then
|
||||
zed_log_err \
|
||||
"${ZED_EMAIL_PROG##*/} cannot read \"${pathname}\""
|
||||
"$(basename "${ZED_EMAIL_PROG}") cannot read \"${pathname}\""
|
||||
return 1
|
||||
fi
|
||||
|
||||
# construct cmdline options
|
||||
ZED_EMAIL_OPTS_PARSED="$(echo "${ZED_EMAIL_OPTS}" \
|
||||
ZED_EMAIL_OPTS="$(echo "${ZED_EMAIL_OPTS}" \
|
||||
| sed -e "s/@ADDRESS@/${ZED_EMAIL_ADDR}/g" \
|
||||
-e "s/@SUBJECT@/${subject}/g")"
|
||||
|
||||
# pipe message to email prog
|
||||
# shellcheck disable=SC2086,SC2248
|
||||
{
|
||||
# no subject passed as option?
|
||||
if [ "${ZED_EMAIL_OPTS%@SUBJECT@*}" = "${ZED_EMAIL_OPTS}" ] ; then
|
||||
# inject subject header
|
||||
printf "Subject: %s\n" "${subject}"
|
||||
fi
|
||||
# output message
|
||||
cat "${pathname}"
|
||||
} |
|
||||
eval ${ZED_EMAIL_PROG} ${ZED_EMAIL_OPTS_PARSED} >/dev/null 2>&1
|
||||
# shellcheck disable=SC2086
|
||||
eval "${ZED_EMAIL_PROG}" ${ZED_EMAIL_OPTS} < "${pathname}" >/dev/null 2>&1
|
||||
rv=$?
|
||||
if [ "${rv}" -ne 0 ]; then
|
||||
zed_log_err "${ZED_EMAIL_PROG##*/} exit=${rv}"
|
||||
zed_log_err "$(basename "${ZED_EMAIL_PROG}") exit=${rv}"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
@@ -376,158 +359,6 @@ zed_notify_pushbullet()
|
||||
}
|
||||
|
||||
|
||||
# zed_notify_slack_webhook (subject, pathname)
|
||||
#
|
||||
# Notification via Slack Webhook <https://api.slack.com/incoming-webhooks>.
|
||||
# The Webhook URL (ZED_SLACK_WEBHOOK_URL) identifies this client to the
|
||||
# Slack channel.
|
||||
#
|
||||
# Requires awk, curl, and sed executables to be installed in the standard PATH.
|
||||
#
|
||||
# References
|
||||
# https://api.slack.com/incoming-webhooks
|
||||
#
|
||||
# Arguments
|
||||
# subject: notification subject
|
||||
# pathname: pathname containing the notification message (OPTIONAL)
|
||||
#
|
||||
# Globals
|
||||
# ZED_SLACK_WEBHOOK_URL
|
||||
#
|
||||
# Return
|
||||
# 0: notification sent
|
||||
# 1: notification failed
|
||||
# 2: not configured
|
||||
#
|
||||
zed_notify_slack_webhook()
|
||||
{
|
||||
[ -n "${ZED_SLACK_WEBHOOK_URL}" ] || return 2
|
||||
|
||||
local subject="$1"
|
||||
local pathname="${2:-"/dev/null"}"
|
||||
local msg_body
|
||||
local msg_tag
|
||||
local msg_json
|
||||
local msg_out
|
||||
local msg_err
|
||||
local url="${ZED_SLACK_WEBHOOK_URL}"
|
||||
|
||||
[ -n "${subject}" ] || return 1
|
||||
if [ ! -r "${pathname}" ]; then
|
||||
zed_log_err "slack webhook cannot read \"${pathname}\""
|
||||
return 1
|
||||
fi
|
||||
|
||||
zed_check_cmd "awk" "curl" "sed" || return 1
|
||||
|
||||
# Escape the following characters in the message body for JSON:
|
||||
# newline, backslash, double quote, horizontal tab, vertical tab,
|
||||
# and carriage return.
|
||||
#
|
||||
msg_body="$(awk '{ ORS="\\n" } { gsub(/\\/, "\\\\"); gsub(/"/, "\\\"");
|
||||
gsub(/\t/, "\\t"); gsub(/\f/, "\\f"); gsub(/\r/, "\\r"); print }' \
|
||||
"${pathname}")"
|
||||
|
||||
# Construct the JSON message for posting.
|
||||
#
|
||||
msg_json="$(printf '{"text": "*%s*\\n%s"}' "${subject}" "${msg_body}" )"
|
||||
|
||||
# Send the POST request and check for errors.
|
||||
#
|
||||
msg_out="$(curl -X POST "${url}" \
|
||||
--header "Content-Type: application/json" --data-binary "${msg_json}" \
|
||||
2>/dev/null)"; rv=$?
|
||||
if [ "${rv}" -ne 0 ]; then
|
||||
zed_log_err "curl exit=${rv}"
|
||||
return 1
|
||||
fi
|
||||
msg_err="$(echo "${msg_out}" \
|
||||
| sed -n -e 's/.*"error" *:.*"message" *: *"\([^"]*\)".*/\1/p')"
|
||||
if [ -n "${msg_err}" ]; then
|
||||
zed_log_err "slack webhook \"${msg_err}"\"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
# zed_notify_pushover (subject, pathname)
|
||||
#
|
||||
# Send a notification via Pushover <https://pushover.net/>.
|
||||
# The access token (ZED_PUSHOVER_TOKEN) identifies this client to the
|
||||
# Pushover server. The user token (ZED_PUSHOVER_USER) defines the user or
|
||||
# group to which the notification will be sent.
|
||||
#
|
||||
# Requires curl and sed executables to be installed in the standard PATH.
|
||||
#
|
||||
# References
|
||||
# https://pushover.net/api
|
||||
#
|
||||
# Arguments
|
||||
# subject: notification subject
|
||||
# pathname: pathname containing the notification message (OPTIONAL)
|
||||
#
|
||||
# Globals
|
||||
# ZED_PUSHOVER_TOKEN
|
||||
# ZED_PUSHOVER_USER
|
||||
#
|
||||
# Return
|
||||
# 0: notification sent
|
||||
# 1: notification failed
|
||||
# 2: not configured
|
||||
#
|
||||
zed_notify_pushover()
|
||||
{
|
||||
local subject="$1"
|
||||
local pathname="${2:-"/dev/null"}"
|
||||
local msg_body
|
||||
local msg_out
|
||||
local msg_err
|
||||
local url="https://api.pushover.net/1/messages.json"
|
||||
|
||||
[ -n "${ZED_PUSHOVER_TOKEN}" ] && [ -n "${ZED_PUSHOVER_USER}" ] || return 2
|
||||
|
||||
if [ ! -r "${pathname}" ]; then
|
||||
zed_log_err "pushover cannot read \"${pathname}\""
|
||||
return 1
|
||||
fi
|
||||
|
||||
zed_check_cmd "curl" "sed" || return 1
|
||||
|
||||
# Read the message body in.
|
||||
#
|
||||
msg_body="$(cat "${pathname}")"
|
||||
|
||||
if [ -z "${msg_body}" ]
|
||||
then
|
||||
msg_body=$subject
|
||||
subject=""
|
||||
fi
|
||||
|
||||
# Send the POST request and check for errors.
|
||||
#
|
||||
msg_out="$( \
|
||||
curl \
|
||||
--form-string "token=${ZED_PUSHOVER_TOKEN}" \
|
||||
--form-string "user=${ZED_PUSHOVER_USER}" \
|
||||
--form-string "message=${msg_body}" \
|
||||
--form-string "title=${subject}" \
|
||||
"${url}" \
|
||||
2>/dev/null \
|
||||
)"; rv=$?
|
||||
if [ "${rv}" -ne 0 ]; then
|
||||
zed_log_err "curl exit=${rv}"
|
||||
return 1
|
||||
fi
|
||||
msg_err="$(echo "${msg_out}" \
|
||||
| sed -n -e 's/.*"errors" *:.*\[\(.*\)\].*/\1/p')"
|
||||
if [ -n "${msg_err}" ]; then
|
||||
zed_log_err "pushover \"${msg_err}"\"
|
||||
return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
# zed_rate_limit (tag, [interval])
|
||||
#
|
||||
# Check whether an event of a given type [tag] has already occurred within the
|
||||
@@ -602,8 +433,10 @@ zed_guid_to_pool()
|
||||
return
|
||||
fi
|
||||
|
||||
guid="$(printf "%u" "$1")"
|
||||
$ZPOOL get -H -ovalue,name guid | awk '$1 == '"$guid"' {print $2; exit}'
|
||||
guid=$(printf "%llu" "$1")
|
||||
if [ ! -z "$guid" ] ; then
|
||||
$ZPOOL get -H -ovalue,name guid | awk '$1=='"$guid"' {print $2}'
|
||||
fi
|
||||
}
|
||||
|
||||
# zed_exit_if_ignoring_this_event
|
||||
|
||||
+8
-40
@@ -13,9 +13,9 @@
|
||||
# Email address of the zpool administrator for receipt of notifications;
|
||||
# multiple addresses can be specified if they are delimited by whitespace.
|
||||
# Email will only be sent if ZED_EMAIL_ADDR is defined.
|
||||
# Enabled by default; comment to disable.
|
||||
# Disabled by default; uncomment to enable.
|
||||
#
|
||||
ZED_EMAIL_ADDR="root"
|
||||
#ZED_EMAIL_ADDR="root"
|
||||
|
||||
##
|
||||
# Name or path of executable responsible for sending notifications via email;
|
||||
@@ -30,7 +30,6 @@ ZED_EMAIL_ADDR="root"
|
||||
# The string @SUBJECT@ will be replaced with the notification subject;
|
||||
# this should be protected with quotes to prevent word-splitting.
|
||||
# Email will only be sent if ZED_EMAIL_ADDR is defined.
|
||||
# If @SUBJECT@ was omited here, a "Subject: ..." header will be added to notification
|
||||
#
|
||||
#ZED_EMAIL_OPTS="-s '@SUBJECT@' @ADDRESS@"
|
||||
|
||||
@@ -53,9 +52,9 @@ ZED_EMAIL_ADDR="root"
|
||||
|
||||
##
|
||||
# Send notifications for 'ereport.fs.zfs.data' events.
|
||||
# Disabled by default, any non-empty value will enable the feature.
|
||||
# Disabled by default
|
||||
#
|
||||
#ZED_NOTIFY_DATA=
|
||||
#ZED_NOTIFY_DATA=1
|
||||
|
||||
##
|
||||
# Pushbullet access token.
|
||||
@@ -75,31 +74,6 @@ ZED_EMAIL_ADDR="root"
|
||||
#
|
||||
#ZED_PUSHBULLET_CHANNEL_TAG=""
|
||||
|
||||
##
|
||||
# Slack Webhook URL.
|
||||
# This allows posting to the given channel and includes an access token.
|
||||
# <https://api.slack.com/incoming-webhooks>
|
||||
# Disabled by default; uncomment to enable.
|
||||
#
|
||||
#ZED_SLACK_WEBHOOK_URL=""
|
||||
|
||||
##
|
||||
# Pushover token.
|
||||
# This defines the application from which the notification will be sent.
|
||||
# <https://pushover.net/api#registration>
|
||||
# Disabled by default; uncomment to enable.
|
||||
# ZED_PUSHOVER_USER, below, must also be configured.
|
||||
#
|
||||
#ZED_PUSHOVER_TOKEN=""
|
||||
|
||||
##
|
||||
# Pushover user key.
|
||||
# This defines which user or group will receive Pushover notifications.
|
||||
# <https://pushover.net/api#identifiers>
|
||||
# Disabled by default; uncomment to enable.
|
||||
# ZED_PUSHOVER_TOKEN, above, must also be configured.
|
||||
#ZED_PUSHOVER_USER=""
|
||||
|
||||
##
|
||||
# Default directory for zed state files.
|
||||
#
|
||||
@@ -107,15 +81,14 @@ ZED_EMAIL_ADDR="root"
|
||||
|
||||
##
|
||||
# Turn on/off enclosure LEDs when drives get DEGRADED/FAULTED. This works for
|
||||
# device mapper and multipath devices as well. This works with JBOD enclosures
|
||||
# and NVMe PCI drives (assuming they're supported by Linux in sysfs).
|
||||
# device mapper and multipath devices as well. Your enclosure must be
|
||||
# supported by the Linux SES driver for this to work.
|
||||
#
|
||||
ZED_USE_ENCLOSURE_LEDS=1
|
||||
|
||||
##
|
||||
# Run a scrub after every resilver
|
||||
# Disabled by default, 1 to enable and 0 to disable.
|
||||
#ZED_SCRUB_AFTER_RESILVER=0
|
||||
#ZED_SCRUB_AFTER_RESILVER=1
|
||||
|
||||
##
|
||||
# The syslog priority (e.g., specified as a "facility.level" pair).
|
||||
@@ -136,10 +109,5 @@ ZED_USE_ENCLOSURE_LEDS=1
|
||||
# Otherwise, if ZED_SYSLOG_SUBCLASS_EXCLUDE is set, the
|
||||
# matching subclasses are excluded from logging.
|
||||
#ZED_SYSLOG_SUBCLASS_INCLUDE="checksum|scrub_*|vdev.*"
|
||||
ZED_SYSLOG_SUBCLASS_EXCLUDE="history_event"
|
||||
|
||||
##
|
||||
# Use GUIDs instead of names when logging pool and vdevs
|
||||
# Disabled by default, 1 to enable and 0 to disable.
|
||||
#ZED_SYSLOG_DISPLAY_GUIDS=1
|
||||
#ZED_SYSLOG_SUBCLASS_EXCLUDE="statechange|config_*|history_event"
|
||||
|
||||
|
||||
+18
-3
@@ -1,9 +1,9 @@
|
||||
/*
|
||||
* This file is part of the ZFS Event Daemon (ZED).
|
||||
*
|
||||
* This file is part of the ZFS Event Daemon (ZED)
|
||||
* for ZFS on Linux (ZoL) <http://zfsonlinux.org/>.
|
||||
* Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
|
||||
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
|
||||
* Refer to the OpenZFS git commit log for authoritative copyright attribution.
|
||||
* Refer to the ZoL git commit log for authoritative copyright attribution.
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License Version 1.0 (CDDL-1.0).
|
||||
@@ -15,6 +15,11 @@
|
||||
#ifndef ZED_H
|
||||
#define ZED_H
|
||||
|
||||
/*
|
||||
* Absolute path for the default zed configuration file.
|
||||
*/
|
||||
#define ZED_CONF_FILE SYSCONFDIR "/zfs/zed.conf"
|
||||
|
||||
/*
|
||||
* Absolute path for the default zed pid file.
|
||||
*/
|
||||
@@ -30,6 +35,16 @@
|
||||
*/
|
||||
#define ZED_ZEDLET_DIR SYSCONFDIR "/zfs/zed.d"
|
||||
|
||||
/*
|
||||
* Reserved for future use.
|
||||
*/
|
||||
#define ZED_MAX_EVENTS 0
|
||||
|
||||
/*
|
||||
* Reserved for future use.
|
||||
*/
|
||||
#define ZED_MIN_EVENTS 0
|
||||
|
||||
/*
|
||||
* String prefix for ZED variables passed via environment variables.
|
||||
*/
|
||||
|
||||
+124
-100
@@ -1,9 +1,9 @@
|
||||
/*
|
||||
* This file is part of the ZFS Event Daemon (ZED).
|
||||
*
|
||||
* This file is part of the ZFS Event Daemon (ZED)
|
||||
* for ZFS on Linux (ZoL) <http://zfsonlinux.org/>.
|
||||
* Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
|
||||
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
|
||||
* Refer to the OpenZFS git commit log for authoritative copyright attribution.
|
||||
* Refer to the ZoL git commit log for authoritative copyright attribution.
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License Version 1.0 (CDDL-1.0).
|
||||
@@ -22,7 +22,6 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/uio.h>
|
||||
#include <unistd.h>
|
||||
@@ -33,26 +32,43 @@
|
||||
#include "zed_strings.h"
|
||||
|
||||
/*
|
||||
* Initialise the configuration with default values.
|
||||
* Return a new configuration with default values.
|
||||
*/
|
||||
void
|
||||
zed_conf_init(struct zed_conf *zcp)
|
||||
struct zed_conf *
|
||||
zed_conf_create(void)
|
||||
{
|
||||
memset(zcp, 0, sizeof (*zcp));
|
||||
struct zed_conf *zcp;
|
||||
|
||||
/* zcp->zfs_hdl opened in zed_event_init() */
|
||||
/* zcp->zedlets created in zed_conf_scan_dir() */
|
||||
zcp = calloc(1, sizeof (*zcp));
|
||||
if (!zcp)
|
||||
goto nomem;
|
||||
|
||||
zcp->pid_fd = -1; /* opened in zed_conf_write_pid() */
|
||||
zcp->state_fd = -1; /* opened in zed_conf_open_state() */
|
||||
zcp->zevent_fd = -1; /* opened in zed_event_init() */
|
||||
zcp->syslog_facility = LOG_DAEMON;
|
||||
zcp->min_events = ZED_MIN_EVENTS;
|
||||
zcp->max_events = ZED_MAX_EVENTS;
|
||||
zcp->pid_fd = -1;
|
||||
zcp->zedlets = NULL; /* created via zed_conf_scan_dir() */
|
||||
zcp->state_fd = -1; /* opened via zed_conf_open_state() */
|
||||
zcp->zfs_hdl = NULL; /* opened via zed_event_init() */
|
||||
zcp->zevent_fd = -1; /* opened via zed_event_init() */
|
||||
|
||||
zcp->max_jobs = 16;
|
||||
if (!(zcp->conf_file = strdup(ZED_CONF_FILE)))
|
||||
goto nomem;
|
||||
|
||||
if (!(zcp->pid_file = strdup(ZED_PID_FILE)) ||
|
||||
!(zcp->zedlet_dir = strdup(ZED_ZEDLET_DIR)) ||
|
||||
!(zcp->state_file = strdup(ZED_STATE_FILE)))
|
||||
zed_log_die("Failed to create conf: %s", strerror(errno));
|
||||
if (!(zcp->pid_file = strdup(ZED_PID_FILE)))
|
||||
goto nomem;
|
||||
|
||||
if (!(zcp->zedlet_dir = strdup(ZED_ZEDLET_DIR)))
|
||||
goto nomem;
|
||||
|
||||
if (!(zcp->state_file = strdup(ZED_STATE_FILE)))
|
||||
goto nomem;
|
||||
|
||||
return (zcp);
|
||||
|
||||
nomem:
|
||||
zed_log_die("Failed to create conf: %s", strerror(errno));
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -63,6 +79,9 @@ zed_conf_init(struct zed_conf *zcp)
|
||||
void
|
||||
zed_conf_destroy(struct zed_conf *zcp)
|
||||
{
|
||||
if (!zcp)
|
||||
return;
|
||||
|
||||
if (zcp->state_fd >= 0) {
|
||||
if (close(zcp->state_fd) < 0)
|
||||
zed_log_msg(LOG_WARNING,
|
||||
@@ -83,6 +102,10 @@ zed_conf_destroy(struct zed_conf *zcp)
|
||||
zcp->pid_file, strerror(errno));
|
||||
zcp->pid_fd = -1;
|
||||
}
|
||||
if (zcp->conf_file) {
|
||||
free(zcp->conf_file);
|
||||
zcp->conf_file = NULL;
|
||||
}
|
||||
if (zcp->pid_file) {
|
||||
free(zcp->pid_file);
|
||||
zcp->pid_file = NULL;
|
||||
@@ -99,6 +122,7 @@ zed_conf_destroy(struct zed_conf *zcp)
|
||||
zed_strings_destroy(zcp->zedlets);
|
||||
zcp->zedlets = NULL;
|
||||
}
|
||||
free(zcp);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -108,52 +132,44 @@ zed_conf_destroy(struct zed_conf *zcp)
|
||||
* otherwise, output to stderr and exit with a failure status.
|
||||
*/
|
||||
static void
|
||||
_zed_conf_display_help(const char *prog, boolean_t got_err)
|
||||
_zed_conf_display_help(const char *prog, int got_err)
|
||||
{
|
||||
struct opt { const char *o, *d, *v; };
|
||||
|
||||
FILE *fp = got_err ? stderr : stdout;
|
||||
|
||||
struct opt *oo;
|
||||
struct opt iopts[] = {
|
||||
{ .o = "-h", .d = "Display help" },
|
||||
{ .o = "-L", .d = "Display license information" },
|
||||
{ .o = "-V", .d = "Display version information" },
|
||||
{},
|
||||
};
|
||||
struct opt nopts[] = {
|
||||
{ .o = "-v", .d = "Be verbose" },
|
||||
{ .o = "-f", .d = "Force daemon to run" },
|
||||
{ .o = "-F", .d = "Run daemon in the foreground" },
|
||||
{ .o = "-I",
|
||||
.d = "Idle daemon until kernel module is (re)loaded" },
|
||||
{ .o = "-M", .d = "Lock all pages in memory" },
|
||||
{ .o = "-P", .d = "$PATH for ZED to use (only used by ZTS)" },
|
||||
{ .o = "-Z", .d = "Zero state file" },
|
||||
{},
|
||||
};
|
||||
struct opt vopts[] = {
|
||||
{ .o = "-d DIR", .d = "Read enabled ZEDLETs from DIR.",
|
||||
.v = ZED_ZEDLET_DIR },
|
||||
{ .o = "-p FILE", .d = "Write daemon's PID to FILE.",
|
||||
.v = ZED_PID_FILE },
|
||||
{ .o = "-s FILE", .d = "Write daemon's state to FILE.",
|
||||
.v = ZED_STATE_FILE },
|
||||
{ .o = "-j JOBS", .d = "Start at most JOBS at once.",
|
||||
.v = "16" },
|
||||
{},
|
||||
};
|
||||
int w1 = 4; /* width of leading whitespace */
|
||||
int w2 = 8; /* width of L-justified option field */
|
||||
|
||||
fprintf(fp, "Usage: %s [OPTION]...\n", (prog ? prog : "zed"));
|
||||
fprintf(fp, "\n");
|
||||
for (oo = iopts; oo->o; ++oo)
|
||||
fprintf(fp, " %*s %s\n", -8, oo->o, oo->d);
|
||||
fprintf(fp, "%*c%*s %s\n", w1, 0x20, -w2, "-h",
|
||||
"Display help.");
|
||||
fprintf(fp, "%*c%*s %s\n", w1, 0x20, -w2, "-L",
|
||||
"Display license information.");
|
||||
fprintf(fp, "%*c%*s %s\n", w1, 0x20, -w2, "-V",
|
||||
"Display version information.");
|
||||
fprintf(fp, "\n");
|
||||
for (oo = nopts; oo->o; ++oo)
|
||||
fprintf(fp, " %*s %s\n", -8, oo->o, oo->d);
|
||||
fprintf(fp, "%*c%*s %s\n", w1, 0x20, -w2, "-v",
|
||||
"Be verbose.");
|
||||
fprintf(fp, "%*c%*s %s\n", w1, 0x20, -w2, "-f",
|
||||
"Force daemon to run.");
|
||||
fprintf(fp, "%*c%*s %s\n", w1, 0x20, -w2, "-F",
|
||||
"Run daemon in the foreground.");
|
||||
fprintf(fp, "%*c%*s %s\n", w1, 0x20, -w2, "-M",
|
||||
"Lock all pages in memory.");
|
||||
fprintf(fp, "%*c%*s %s\n", w1, 0x20, -w2, "-P",
|
||||
"$PATH for ZED to use (only used by ZTS).");
|
||||
fprintf(fp, "%*c%*s %s\n", w1, 0x20, -w2, "-Z",
|
||||
"Zero state file.");
|
||||
fprintf(fp, "\n");
|
||||
for (oo = vopts; oo->o; ++oo)
|
||||
fprintf(fp, " %*s %s [%s]\n", -8, oo->o, oo->d, oo->v);
|
||||
#if 0
|
||||
fprintf(fp, "%*c%*s %s [%s]\n", w1, 0x20, -w2, "-c FILE",
|
||||
"Read configuration from FILE.", ZED_CONF_FILE);
|
||||
#endif
|
||||
fprintf(fp, "%*c%*s %s [%s]\n", w1, 0x20, -w2, "-d DIR",
|
||||
"Read enabled ZEDLETs from DIR.", ZED_ZEDLET_DIR);
|
||||
fprintf(fp, "%*c%*s %s [%s]\n", w1, 0x20, -w2, "-p FILE",
|
||||
"Write daemon's PID to FILE.", ZED_PID_FILE);
|
||||
fprintf(fp, "%*c%*s %s [%s]\n", w1, 0x20, -w2, "-s FILE",
|
||||
"Write daemon's state to FILE.", ZED_STATE_FILE);
|
||||
fprintf(fp, "\n");
|
||||
|
||||
exit(got_err ? EXIT_FAILURE : EXIT_SUCCESS);
|
||||
@@ -165,14 +181,20 @@ _zed_conf_display_help(const char *prog, boolean_t got_err)
|
||||
static void
|
||||
_zed_conf_display_license(void)
|
||||
{
|
||||
printf(
|
||||
"The ZFS Event Daemon (ZED) is distributed under the terms of the\n"
|
||||
" Common Development and Distribution License (CDDL-1.0)\n"
|
||||
" <http://opensource.org/licenses/CDDL-1.0>.\n"
|
||||
"\n"
|
||||
const char **pp;
|
||||
const char *text[] = {
|
||||
"The ZFS Event Daemon (ZED) is distributed under the terms of the",
|
||||
" Common Development and Distribution License (CDDL-1.0)",
|
||||
" <http://opensource.org/licenses/CDDL-1.0>.",
|
||||
"",
|
||||
"Developed at Lawrence Livermore National Laboratory"
|
||||
" (LLNL-CODE-403049).\n"
|
||||
"\n");
|
||||
" (LLNL-CODE-403049).",
|
||||
"",
|
||||
NULL
|
||||
};
|
||||
|
||||
for (pp = text; *pp; pp++)
|
||||
printf("%s\n", *pp);
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
@@ -207,19 +229,16 @@ _zed_conf_parse_path(char **resultp, const char *path)
|
||||
|
||||
if (path[0] == '/') {
|
||||
*resultp = strdup(path);
|
||||
} else if (!getcwd(buf, sizeof (buf))) {
|
||||
zed_log_die("Failed to get current working dir: %s",
|
||||
strerror(errno));
|
||||
} else if (strlcat(buf, "/", sizeof (buf)) >= sizeof (buf)) {
|
||||
zed_log_die("Failed to copy path: %s", strerror(ENAMETOOLONG));
|
||||
} else if (strlcat(buf, path, sizeof (buf)) >= sizeof (buf)) {
|
||||
zed_log_die("Failed to copy path: %s", strerror(ENAMETOOLONG));
|
||||
} else {
|
||||
if (!getcwd(buf, sizeof (buf)))
|
||||
zed_log_die("Failed to get current working dir: %s",
|
||||
strerror(errno));
|
||||
|
||||
if (strlcat(buf, "/", sizeof (buf)) >= sizeof (buf) ||
|
||||
strlcat(buf, path, sizeof (buf)) >= sizeof (buf))
|
||||
zed_log_die("Failed to copy path: %s",
|
||||
strerror(ENAMETOOLONG));
|
||||
|
||||
*resultp = strdup(buf);
|
||||
}
|
||||
|
||||
if (!*resultp)
|
||||
zed_log_die("Failed to copy path: %s", strerror(ENOMEM));
|
||||
}
|
||||
@@ -230,9 +249,8 @@ _zed_conf_parse_path(char **resultp, const char *path)
|
||||
void
|
||||
zed_conf_parse_opts(struct zed_conf *zcp, int argc, char **argv)
|
||||
{
|
||||
const char * const opts = ":hLVd:p:P:s:vfFMZIj:";
|
||||
const char * const opts = ":hLVc:d:p:P:s:vfFMZ";
|
||||
int opt;
|
||||
unsigned long raw;
|
||||
|
||||
if (!zcp || !argv || !argv[0])
|
||||
zed_log_die("Failed to parse options: Internal error");
|
||||
@@ -242,7 +260,7 @@ zed_conf_parse_opts(struct zed_conf *zcp, int argc, char **argv)
|
||||
while ((opt = getopt(argc, argv, opts)) != -1) {
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
_zed_conf_display_help(argv[0], B_FALSE);
|
||||
_zed_conf_display_help(argv[0], EXIT_SUCCESS);
|
||||
break;
|
||||
case 'L':
|
||||
_zed_conf_display_license();
|
||||
@@ -250,12 +268,12 @@ zed_conf_parse_opts(struct zed_conf *zcp, int argc, char **argv)
|
||||
case 'V':
|
||||
_zed_conf_display_version();
|
||||
break;
|
||||
case 'c':
|
||||
_zed_conf_parse_path(&zcp->conf_file, optarg);
|
||||
break;
|
||||
case 'd':
|
||||
_zed_conf_parse_path(&zcp->zedlet_dir, optarg);
|
||||
break;
|
||||
case 'I':
|
||||
zcp->do_idle = 1;
|
||||
break;
|
||||
case 'p':
|
||||
_zed_conf_parse_path(&zcp->pid_file, optarg);
|
||||
break;
|
||||
@@ -280,30 +298,31 @@ zed_conf_parse_opts(struct zed_conf *zcp, int argc, char **argv)
|
||||
case 'Z':
|
||||
zcp->do_zero = 1;
|
||||
break;
|
||||
case 'j':
|
||||
errno = 0;
|
||||
raw = strtoul(optarg, NULL, 0);
|
||||
if (errno == ERANGE || raw > INT16_MAX) {
|
||||
zed_log_die("%lu is too many jobs", raw);
|
||||
} if (raw == 0) {
|
||||
zed_log_die("0 jobs makes no sense");
|
||||
} else {
|
||||
zcp->max_jobs = raw;
|
||||
}
|
||||
break;
|
||||
case '?':
|
||||
default:
|
||||
if (optopt == '?')
|
||||
_zed_conf_display_help(argv[0], B_FALSE);
|
||||
_zed_conf_display_help(argv[0], EXIT_SUCCESS);
|
||||
|
||||
fprintf(stderr, "%s: Invalid option '-%c'\n\n",
|
||||
argv[0], optopt);
|
||||
_zed_conf_display_help(argv[0], B_TRUE);
|
||||
fprintf(stderr, "%s: %s '-%c'\n\n", argv[0],
|
||||
"Invalid option", optopt);
|
||||
_zed_conf_display_help(argv[0], EXIT_FAILURE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the configuration file into the configuration [zcp].
|
||||
*
|
||||
* FIXME: Not yet implemented.
|
||||
*/
|
||||
void
|
||||
zed_conf_parse_file(struct zed_conf *zcp)
|
||||
{
|
||||
if (!zcp)
|
||||
zed_log_die("Failed to parse config: %s", strerror(EINVAL));
|
||||
}
|
||||
|
||||
/*
|
||||
* Scan the [zcp] zedlet_dir for files to exec based on the event class.
|
||||
* Files must be executable by user, but not writable by group or other.
|
||||
@@ -311,6 +330,8 @@ zed_conf_parse_opts(struct zed_conf *zcp, int argc, char **argv)
|
||||
*
|
||||
* Return 0 on success with an updated set of zedlets,
|
||||
* or -1 on error with errno set.
|
||||
*
|
||||
* FIXME: Check if zedlet_dir and all parent dirs are secure.
|
||||
*/
|
||||
int
|
||||
zed_conf_scan_dir(struct zed_conf *zcp)
|
||||
@@ -426,6 +447,8 @@ zed_conf_scan_dir(struct zed_conf *zcp)
|
||||
int
|
||||
zed_conf_write_pid(struct zed_conf *zcp)
|
||||
{
|
||||
const mode_t dirmode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
|
||||
const mode_t filemode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
|
||||
char buf[PATH_MAX];
|
||||
int n;
|
||||
char *p;
|
||||
@@ -453,7 +476,7 @@ zed_conf_write_pid(struct zed_conf *zcp)
|
||||
if (p)
|
||||
*p = '\0';
|
||||
|
||||
if ((mkdirp(buf, 0755) < 0) && (errno != EEXIST)) {
|
||||
if ((mkdirp(buf, dirmode) < 0) && (errno != EEXIST)) {
|
||||
zed_log_msg(LOG_ERR, "Failed to create directory \"%s\": %s",
|
||||
buf, strerror(errno));
|
||||
goto err;
|
||||
@@ -463,7 +486,7 @@ zed_conf_write_pid(struct zed_conf *zcp)
|
||||
*/
|
||||
mask = umask(0);
|
||||
umask(mask | 022);
|
||||
zcp->pid_fd = open(zcp->pid_file, O_RDWR | O_CREAT | O_CLOEXEC, 0644);
|
||||
zcp->pid_fd = open(zcp->pid_file, (O_RDWR | O_CREAT), filemode);
|
||||
umask(mask);
|
||||
if (zcp->pid_fd < 0) {
|
||||
zed_log_msg(LOG_ERR, "Failed to open PID file \"%s\": %s",
|
||||
@@ -500,7 +523,7 @@ zed_conf_write_pid(struct zed_conf *zcp)
|
||||
errno = ERANGE;
|
||||
zed_log_msg(LOG_ERR, "Failed to write PID file \"%s\": %s",
|
||||
zcp->pid_file, strerror(errno));
|
||||
} else if (write(zcp->pid_fd, buf, n) != n) {
|
||||
} else if (zed_file_write_n(zcp->pid_fd, buf, n) != n) {
|
||||
zed_log_msg(LOG_ERR, "Failed to write PID file \"%s\": %s",
|
||||
zcp->pid_file, strerror(errno));
|
||||
} else if (fdatasync(zcp->pid_fd) < 0) {
|
||||
@@ -528,6 +551,7 @@ int
|
||||
zed_conf_open_state(struct zed_conf *zcp)
|
||||
{
|
||||
char dirbuf[PATH_MAX];
|
||||
mode_t dirmode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
|
||||
int n;
|
||||
char *p;
|
||||
int rv;
|
||||
@@ -549,7 +573,7 @@ zed_conf_open_state(struct zed_conf *zcp)
|
||||
if (p)
|
||||
*p = '\0';
|
||||
|
||||
if ((mkdirp(dirbuf, 0755) < 0) && (errno != EEXIST)) {
|
||||
if ((mkdirp(dirbuf, dirmode) < 0) && (errno != EEXIST)) {
|
||||
zed_log_msg(LOG_WARNING,
|
||||
"Failed to create directory \"%s\": %s",
|
||||
dirbuf, strerror(errno));
|
||||
@@ -567,7 +591,7 @@ zed_conf_open_state(struct zed_conf *zcp)
|
||||
(void) unlink(zcp->state_file);
|
||||
|
||||
zcp->state_fd = open(zcp->state_file,
|
||||
O_RDWR | O_CREAT | O_CLOEXEC, 0644);
|
||||
(O_RDWR | O_CREAT), (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH));
|
||||
if (zcp->state_fd < 0) {
|
||||
zed_log_msg(LOG_WARNING, "Failed to open state file \"%s\": %s",
|
||||
zcp->state_file, strerror(errno));
|
||||
|
||||
+23
-20
@@ -1,9 +1,9 @@
|
||||
/*
|
||||
* This file is part of the ZFS Event Daemon (ZED).
|
||||
*
|
||||
* This file is part of the ZFS Event Daemon (ZED)
|
||||
* for ZFS on Linux (ZoL) <http://zfsonlinux.org/>.
|
||||
* Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
|
||||
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
|
||||
* Refer to the OpenZFS git commit log for authoritative copyright attribution.
|
||||
* Refer to the ZoL git commit log for authoritative copyright attribution.
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License Version 1.0 (CDDL-1.0).
|
||||
@@ -20,39 +20,42 @@
|
||||
#include "zed_strings.h"
|
||||
|
||||
struct zed_conf {
|
||||
unsigned do_force:1; /* true if force enabled */
|
||||
unsigned do_foreground:1; /* true if run in foreground */
|
||||
unsigned do_memlock:1; /* true if locking memory */
|
||||
unsigned do_verbose:1; /* true if verbosity enabled */
|
||||
unsigned do_zero:1; /* true if zeroing state */
|
||||
int syslog_facility; /* syslog facility value */
|
||||
int min_events; /* RESERVED FOR FUTURE USE */
|
||||
int max_events; /* RESERVED FOR FUTURE USE */
|
||||
char *conf_file; /* abs path to config file */
|
||||
char *pid_file; /* abs path to pid file */
|
||||
char *zedlet_dir; /* abs path to zedlet dir */
|
||||
char *state_file; /* abs path to state file */
|
||||
|
||||
libzfs_handle_t *zfs_hdl; /* handle to libzfs */
|
||||
zed_strings_t *zedlets; /* names of enabled zedlets */
|
||||
char *path; /* custom $PATH for zedlets to use */
|
||||
|
||||
int pid_fd; /* fd to pid file for lock */
|
||||
char *zedlet_dir; /* abs path to zedlet dir */
|
||||
zed_strings_t *zedlets; /* names of enabled zedlets */
|
||||
char *state_file; /* abs path to state file */
|
||||
int state_fd; /* fd to state file */
|
||||
libzfs_handle_t *zfs_hdl; /* handle to libzfs */
|
||||
int zevent_fd; /* fd for access to zevents */
|
||||
|
||||
int16_t max_jobs; /* max zedlets to run at one time */
|
||||
|
||||
boolean_t do_force:1; /* true if force enabled */
|
||||
boolean_t do_foreground:1; /* true if run in foreground */
|
||||
boolean_t do_memlock:1; /* true if locking memory */
|
||||
boolean_t do_verbose:1; /* true if verbosity enabled */
|
||||
boolean_t do_zero:1; /* true if zeroing state */
|
||||
boolean_t do_idle:1; /* true if idle enabled */
|
||||
char *path; /* custom $PATH for zedlets to use */
|
||||
};
|
||||
|
||||
void zed_conf_init(struct zed_conf *zcp);
|
||||
struct zed_conf *zed_conf_create(void);
|
||||
|
||||
void zed_conf_destroy(struct zed_conf *zcp);
|
||||
|
||||
void zed_conf_parse_opts(struct zed_conf *zcp, int argc, char **argv);
|
||||
|
||||
void zed_conf_parse_file(struct zed_conf *zcp);
|
||||
|
||||
int zed_conf_scan_dir(struct zed_conf *zcp);
|
||||
|
||||
int zed_conf_write_pid(struct zed_conf *zcp);
|
||||
|
||||
int zed_conf_open_state(struct zed_conf *zcp);
|
||||
|
||||
int zed_conf_read_state(struct zed_conf *zcp, uint64_t *eidp, int64_t etime[]);
|
||||
|
||||
int zed_conf_write_state(struct zed_conf *zcp, uint64_t eid, int64_t etime[]);
|
||||
|
||||
#endif /* !ZED_CONF_H */
|
||||
|
||||
+10
-64
@@ -21,7 +21,6 @@
|
||||
#include <libnvpair.h>
|
||||
#include <libudev.h>
|
||||
#include <libzfs.h>
|
||||
#include <libzutil.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -38,7 +37,7 @@
|
||||
* A libudev monitor is established to monitor block device actions and pass
|
||||
* them on to internal ZED logic modules. Initially, zfs_mod.c is the only
|
||||
* consumer and is the Linux equivalent for the illumos syseventd ZFS SLM
|
||||
* module responsible for handling disk events for ZFS.
|
||||
* module responsible for handeling disk events for ZFS.
|
||||
*/
|
||||
|
||||
pthread_t g_mon_tid;
|
||||
@@ -72,14 +71,10 @@ zed_udev_event(const char *class, const char *subclass, nvlist_t *nvl)
|
||||
zed_log_msg(LOG_INFO, "\t%s: %s", DEV_PATH, strval);
|
||||
if (nvlist_lookup_string(nvl, DEV_IDENTIFIER, &strval) == 0)
|
||||
zed_log_msg(LOG_INFO, "\t%s: %s", DEV_IDENTIFIER, strval);
|
||||
if (nvlist_lookup_boolean(nvl, DEV_IS_PART) == B_TRUE)
|
||||
zed_log_msg(LOG_INFO, "\t%s: B_TRUE", DEV_IS_PART);
|
||||
if (nvlist_lookup_string(nvl, DEV_PHYS_PATH, &strval) == 0)
|
||||
zed_log_msg(LOG_INFO, "\t%s: %s", DEV_PHYS_PATH, strval);
|
||||
if (nvlist_lookup_uint64(nvl, DEV_SIZE, &numval) == 0)
|
||||
zed_log_msg(LOG_INFO, "\t%s: %llu", DEV_SIZE, numval);
|
||||
if (nvlist_lookup_uint64(nvl, DEV_PARENT_SIZE, &numval) == 0)
|
||||
zed_log_msg(LOG_INFO, "\t%s: %llu", DEV_PARENT_SIZE, numval);
|
||||
if (nvlist_lookup_uint64(nvl, ZFS_EV_POOL_GUID, &numval) == 0)
|
||||
zed_log_msg(LOG_INFO, "\t%s: %llu", ZFS_EV_POOL_GUID, numval);
|
||||
if (nvlist_lookup_uint64(nvl, ZFS_EV_VDEV_GUID, &numval) == 0)
|
||||
@@ -132,20 +127,6 @@ dev_event_nvlist(struct udev_device *dev)
|
||||
|
||||
numval *= strtoull(value, NULL, 10);
|
||||
(void) nvlist_add_uint64(nvl, DEV_SIZE, numval);
|
||||
|
||||
/*
|
||||
* If the device has a parent, then get the parent block
|
||||
* device's size as well. For example, /dev/sda1's parent
|
||||
* is /dev/sda.
|
||||
*/
|
||||
struct udev_device *parent_dev = udev_device_get_parent(dev);
|
||||
if ((value = udev_device_get_sysattr_value(parent_dev, "size"))
|
||||
!= NULL) {
|
||||
uint64_t numval = DEV_BSIZE;
|
||||
|
||||
numval *= strtoull(value, NULL, 10);
|
||||
(void) nvlist_add_uint64(nvl, DEV_PARENT_SIZE, numval);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -185,7 +166,7 @@ zed_udev_monitor(void *arg)
|
||||
while (1) {
|
||||
struct udev_device *dev;
|
||||
const char *action, *type, *part, *sectors;
|
||||
const char *bus, *uuid, *devpath;
|
||||
const char *bus, *uuid;
|
||||
const char *class, *subclass;
|
||||
nvlist_t *nvl;
|
||||
boolean_t is_zfs = B_FALSE;
|
||||
@@ -224,12 +205,6 @@ zed_udev_monitor(void *arg)
|
||||
* if this is a disk and it is partitioned, then the
|
||||
* zfs label will reside in a DEVTYPE=partition and
|
||||
* we can skip passing this event
|
||||
*
|
||||
* Special case: Blank disks are sometimes reported with
|
||||
* an erroneous 'atari' partition, and should not be
|
||||
* excluded from being used as an autoreplace disk:
|
||||
*
|
||||
* https://github.com/openzfs/zfs/issues/13497
|
||||
*/
|
||||
type = udev_device_get_property_value(dev, "DEVTYPE");
|
||||
part = udev_device_get_property_value(dev,
|
||||
@@ -237,23 +212,9 @@ zed_udev_monitor(void *arg)
|
||||
if (type != NULL && type[0] != '\0' &&
|
||||
strcmp(type, "disk") == 0 &&
|
||||
part != NULL && part[0] != '\0') {
|
||||
const char *devname =
|
||||
udev_device_get_property_value(dev, "DEVNAME");
|
||||
|
||||
if (strcmp(part, "atari") == 0) {
|
||||
zed_log_msg(LOG_INFO,
|
||||
"%s: %s is reporting an atari partition, "
|
||||
"but we're going to assume it's a false "
|
||||
"positive and still use it (issue #13497)",
|
||||
__func__, devname);
|
||||
} else {
|
||||
zed_log_msg(LOG_INFO,
|
||||
"%s: skip %s since it has a %s partition "
|
||||
"already", __func__, devname, part);
|
||||
/* skip and wait for partition event */
|
||||
udev_device_unref(dev);
|
||||
continue;
|
||||
}
|
||||
/* skip and wait for partition event */
|
||||
udev_device_unref(dev);
|
||||
continue;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -265,11 +226,6 @@ zed_udev_monitor(void *arg)
|
||||
sectors = udev_device_get_sysattr_value(dev, "size");
|
||||
if (sectors != NULL &&
|
||||
strtoull(sectors, NULL, 10) < MINIMUM_SECTORS) {
|
||||
zed_log_msg(LOG_INFO,
|
||||
"%s: %s sectors %s < %llu (minimum)",
|
||||
__func__,
|
||||
udev_device_get_property_value(dev, "DEVNAME"),
|
||||
sectors, MINIMUM_SECTORS);
|
||||
udev_device_unref(dev);
|
||||
continue;
|
||||
}
|
||||
@@ -279,19 +235,10 @@ zed_udev_monitor(void *arg)
|
||||
* device id string is required in the message schema
|
||||
* for matching with vdevs. Preflight here for expected
|
||||
* udev information.
|
||||
*
|
||||
* Special case:
|
||||
* NVMe devices don't have ID_BUS set (at least on RHEL 7-8),
|
||||
* but they are valid for autoreplace. Add a special case for
|
||||
* them by searching for "/nvme/" in the udev DEVPATH:
|
||||
*
|
||||
* DEVPATH=/devices/pci0000:00/0000:00:1e.0/nvme/nvme2/nvme2n1
|
||||
*/
|
||||
bus = udev_device_get_property_value(dev, "ID_BUS");
|
||||
uuid = udev_device_get_property_value(dev, "DM_UUID");
|
||||
devpath = udev_device_get_devpath(dev);
|
||||
if (!is_zfs && (bus == NULL && uuid == NULL &&
|
||||
strstr(devpath, "/nvme/") == NULL)) {
|
||||
if (!is_zfs && (bus == NULL && uuid == NULL)) {
|
||||
zed_log_msg(LOG_INFO, "zed_udev_monitor: %s no devid "
|
||||
"source", udev_device_get_devnode(dev));
|
||||
udev_device_unref(dev);
|
||||
@@ -402,7 +349,7 @@ zed_udev_monitor(void *arg)
|
||||
}
|
||||
|
||||
int
|
||||
zed_disk_event_init(void)
|
||||
zed_disk_event_init()
|
||||
{
|
||||
int fd, fflags;
|
||||
|
||||
@@ -431,14 +378,13 @@ zed_disk_event_init(void)
|
||||
return (-1);
|
||||
}
|
||||
|
||||
pthread_setname_np(g_mon_tid, "udev monitor");
|
||||
zed_log_msg(LOG_INFO, "zed_disk_event_init");
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
zed_disk_event_fini(void)
|
||||
zed_disk_event_fini()
|
||||
{
|
||||
/* cancel monitor thread at recvmsg() */
|
||||
(void) pthread_cancel(g_mon_tid);
|
||||
@@ -456,13 +402,13 @@ zed_disk_event_fini(void)
|
||||
#include "zed_disk_event.h"
|
||||
|
||||
int
|
||||
zed_disk_event_init(void)
|
||||
zed_disk_event_init()
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
zed_disk_event_fini(void)
|
||||
zed_disk_event_fini()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
+39
-74
@@ -1,9 +1,9 @@
|
||||
/*
|
||||
* This file is part of the ZFS Event Daemon (ZED).
|
||||
*
|
||||
* This file is part of the ZFS Event Daemon (ZED)
|
||||
* for ZFS on Linux (ZoL) <http://zfsonlinux.org/>.
|
||||
* Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
|
||||
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
|
||||
* Refer to the OpenZFS git commit log for authoritative copyright attribution.
|
||||
* Refer to the ZoL git commit log for authoritative copyright attribution.
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License Version 1.0 (CDDL-1.0).
|
||||
@@ -15,7 +15,7 @@
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <libzfs_core.h>
|
||||
#include <libzfs.h> /* FIXME: Replace with libzfs_core. */
|
||||
#include <paths.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
@@ -28,7 +28,6 @@
|
||||
#include "zed.h"
|
||||
#include "zed_conf.h"
|
||||
#include "zed_disk_event.h"
|
||||
#include "zed_event.h"
|
||||
#include "zed_exec.h"
|
||||
#include "zed_file.h"
|
||||
#include "zed_log.h"
|
||||
@@ -41,36 +40,25 @@
|
||||
/*
|
||||
* Open the libzfs interface.
|
||||
*/
|
||||
int
|
||||
void
|
||||
zed_event_init(struct zed_conf *zcp)
|
||||
{
|
||||
if (!zcp)
|
||||
zed_log_die("Failed zed_event_init: %s", strerror(EINVAL));
|
||||
|
||||
zcp->zfs_hdl = libzfs_init();
|
||||
if (!zcp->zfs_hdl) {
|
||||
if (zcp->do_idle)
|
||||
return (-1);
|
||||
if (!zcp->zfs_hdl)
|
||||
zed_log_die("Failed to initialize libzfs");
|
||||
}
|
||||
|
||||
zcp->zevent_fd = open(ZFS_DEV, O_RDWR | O_CLOEXEC);
|
||||
if (zcp->zevent_fd < 0) {
|
||||
if (zcp->do_idle)
|
||||
return (-1);
|
||||
zcp->zevent_fd = open(ZFS_DEV, O_RDWR);
|
||||
if (zcp->zevent_fd < 0)
|
||||
zed_log_die("Failed to open \"%s\": %s",
|
||||
ZFS_DEV, strerror(errno));
|
||||
}
|
||||
|
||||
zfs_agent_init(zcp->zfs_hdl);
|
||||
|
||||
if (zed_disk_event_init() != 0) {
|
||||
if (zcp->do_idle)
|
||||
return (-1);
|
||||
if (zed_disk_event_init() != 0)
|
||||
zed_log_die("Failed to initialize disk events");
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -96,47 +84,6 @@ zed_event_fini(struct zed_conf *zcp)
|
||||
libzfs_fini(zcp->zfs_hdl);
|
||||
zcp->zfs_hdl = NULL;
|
||||
}
|
||||
|
||||
zed_exec_fini();
|
||||
}
|
||||
|
||||
static void
|
||||
_bump_event_queue_length(void)
|
||||
{
|
||||
int zzlm = -1, wr;
|
||||
char qlen_buf[12] = {0}; /* parameter is int => max "-2147483647\n" */
|
||||
long int qlen;
|
||||
|
||||
zzlm = open("/sys/module/zfs/parameters/zfs_zevent_len_max", O_RDWR);
|
||||
if (zzlm < 0)
|
||||
goto done;
|
||||
|
||||
if (read(zzlm, qlen_buf, sizeof (qlen_buf)) < 0)
|
||||
goto done;
|
||||
qlen_buf[sizeof (qlen_buf) - 1] = '\0';
|
||||
|
||||
errno = 0;
|
||||
qlen = strtol(qlen_buf, NULL, 10);
|
||||
if (errno == ERANGE)
|
||||
goto done;
|
||||
|
||||
if (qlen <= 0)
|
||||
qlen = 512; /* default zfs_zevent_len_max value */
|
||||
else
|
||||
qlen *= 2;
|
||||
|
||||
if (qlen > INT_MAX)
|
||||
qlen = INT_MAX;
|
||||
wr = snprintf(qlen_buf, sizeof (qlen_buf), "%ld", qlen);
|
||||
|
||||
if (pwrite(zzlm, qlen_buf, wr, 0) < 0)
|
||||
goto done;
|
||||
|
||||
zed_log_msg(LOG_WARNING, "Bumping queue length to %ld", qlen);
|
||||
|
||||
done:
|
||||
if (zzlm > -1)
|
||||
(void) close(zzlm);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -177,7 +124,10 @@ zed_event_seek(struct zed_conf *zcp, uint64_t saved_eid, int64_t saved_etime[])
|
||||
|
||||
if (n_dropped > 0) {
|
||||
zed_log_msg(LOG_WARNING, "Missed %d events", n_dropped);
|
||||
_bump_event_queue_length();
|
||||
/*
|
||||
* FIXME: Increase max size of event nvlist in
|
||||
* /sys/module/zfs/parameters/zfs_zevent_len_max ?
|
||||
*/
|
||||
}
|
||||
if (nvlist_lookup_uint64(nvl, "eid", &eid) != 0) {
|
||||
zed_log_msg(LOG_WARNING, "Failed to lookup zevent eid");
|
||||
@@ -249,7 +199,7 @@ _zed_event_value_is_hex(const char *name)
|
||||
*
|
||||
* All environment variables in [zsp] should be added through this function.
|
||||
*/
|
||||
static __attribute__((format(printf, 5, 6))) int
|
||||
static int
|
||||
_zed_event_add_var(uint64_t eid, zed_strings_t *zsp,
|
||||
const char *prefix, const char *name, const char *fmt, ...)
|
||||
{
|
||||
@@ -624,6 +574,8 @@ _zed_event_add_string_array(uint64_t eid, zed_strings_t *zsp,
|
||||
* Convert the nvpair [nvp] to a string which is added to the environment
|
||||
* of the child process.
|
||||
* Return 0 on success, -1 on error.
|
||||
*
|
||||
* FIXME: Refactor with cmd/zpool/zpool_main.c:zpool_do_events_nvprint()?
|
||||
*/
|
||||
static void
|
||||
_zed_event_add_nvpair(uint64_t eid, zed_strings_t *zsp, nvpair_t *nvp)
|
||||
@@ -722,11 +674,23 @@ _zed_event_add_nvpair(uint64_t eid, zed_strings_t *zsp, nvpair_t *nvp)
|
||||
_zed_event_add_var(eid, zsp, prefix, name,
|
||||
"%llu", (u_longlong_t)i64);
|
||||
break;
|
||||
case DATA_TYPE_NVLIST:
|
||||
_zed_event_add_var(eid, zsp, prefix, name,
|
||||
"%s", "_NOT_IMPLEMENTED_"); /* FIXME */
|
||||
break;
|
||||
case DATA_TYPE_STRING:
|
||||
(void) nvpair_value_string(nvp, &str);
|
||||
_zed_event_add_var(eid, zsp, prefix, name,
|
||||
"%s", (str ? str : "<NULL>"));
|
||||
break;
|
||||
case DATA_TYPE_BOOLEAN_ARRAY:
|
||||
_zed_event_add_var(eid, zsp, prefix, name,
|
||||
"%s", "_NOT_IMPLEMENTED_"); /* FIXME */
|
||||
break;
|
||||
case DATA_TYPE_BYTE_ARRAY:
|
||||
_zed_event_add_var(eid, zsp, prefix, name,
|
||||
"%s", "_NOT_IMPLEMENTED_"); /* FIXME */
|
||||
break;
|
||||
case DATA_TYPE_INT8_ARRAY:
|
||||
_zed_event_add_int8_array(eid, zsp, prefix, nvp);
|
||||
break;
|
||||
@@ -754,11 +718,9 @@ _zed_event_add_nvpair(uint64_t eid, zed_strings_t *zsp, nvpair_t *nvp)
|
||||
case DATA_TYPE_STRING_ARRAY:
|
||||
_zed_event_add_string_array(eid, zsp, prefix, nvp);
|
||||
break;
|
||||
case DATA_TYPE_NVLIST:
|
||||
case DATA_TYPE_BOOLEAN_ARRAY:
|
||||
case DATA_TYPE_BYTE_ARRAY:
|
||||
case DATA_TYPE_NVLIST_ARRAY:
|
||||
_zed_event_add_var(eid, zsp, prefix, name, "_NOT_IMPLEMENTED_");
|
||||
_zed_event_add_var(eid, zsp, prefix, name,
|
||||
"%s", "_NOT_IMPLEMENTED_"); /* FIXME */
|
||||
break;
|
||||
default:
|
||||
errno = EINVAL;
|
||||
@@ -910,7 +872,7 @@ _zed_event_add_time_strings(uint64_t eid, zed_strings_t *zsp, int64_t etime[])
|
||||
/*
|
||||
* Service the next zevent, blocking until one is available.
|
||||
*/
|
||||
int
|
||||
void
|
||||
zed_event_service(struct zed_conf *zcp)
|
||||
{
|
||||
nvlist_t *nvl;
|
||||
@@ -928,17 +890,20 @@ zed_event_service(struct zed_conf *zcp)
|
||||
errno = EINVAL;
|
||||
zed_log_msg(LOG_ERR, "Failed to service zevent: %s",
|
||||
strerror(errno));
|
||||
return (EINVAL);
|
||||
return;
|
||||
}
|
||||
rv = zpool_events_next(zcp->zfs_hdl, &nvl, &n_dropped, ZEVENT_NONE,
|
||||
zcp->zevent_fd);
|
||||
|
||||
if ((rv != 0) || !nvl)
|
||||
return (errno);
|
||||
return;
|
||||
|
||||
if (n_dropped > 0) {
|
||||
zed_log_msg(LOG_WARNING, "Missed %d events", n_dropped);
|
||||
_bump_event_queue_length();
|
||||
/*
|
||||
* FIXME: Increase max size of event nvlist in
|
||||
* /sys/module/zfs/parameters/zfs_zevent_len_max ?
|
||||
*/
|
||||
}
|
||||
if (nvlist_lookup_uint64(nvl, "eid", &eid) != 0) {
|
||||
zed_log_msg(LOG_WARNING, "Failed to lookup zevent eid");
|
||||
@@ -976,12 +941,12 @@ zed_event_service(struct zed_conf *zcp)
|
||||
|
||||
_zed_event_add_time_strings(eid, zsp, etime);
|
||||
|
||||
zed_exec_process(eid, class, subclass, zcp, zsp);
|
||||
zed_exec_process(eid, class, subclass,
|
||||
zcp->zedlet_dir, zcp->zedlets, zsp, zcp->zevent_fd);
|
||||
|
||||
zed_conf_write_state(zcp, eid, etime);
|
||||
|
||||
zed_strings_destroy(zsp);
|
||||
}
|
||||
nvlist_free(nvl);
|
||||
return (0);
|
||||
}
|
||||
|
||||
+5
-5
@@ -1,9 +1,9 @@
|
||||
/*
|
||||
* This file is part of the ZFS Event Daemon (ZED).
|
||||
*
|
||||
* This file is part of the ZFS Event Daemon (ZED)
|
||||
* for ZFS on Linux (ZoL) <http://zfsonlinux.org/>.
|
||||
* Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
|
||||
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
|
||||
* Refer to the OpenZFS git commit log for authoritative copyright attribution.
|
||||
* Refer to the ZoL git commit log for authoritative copyright attribution.
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License Version 1.0 (CDDL-1.0).
|
||||
@@ -17,13 +17,13 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
int zed_event_init(struct zed_conf *zcp);
|
||||
void zed_event_init(struct zed_conf *zcp);
|
||||
|
||||
void zed_event_fini(struct zed_conf *zcp);
|
||||
|
||||
int zed_event_seek(struct zed_conf *zcp, uint64_t saved_eid,
|
||||
int64_t saved_etime[]);
|
||||
|
||||
int zed_event_service(struct zed_conf *zcp);
|
||||
void zed_event_service(struct zed_conf *zcp);
|
||||
|
||||
#endif /* !ZED_EVENT_H */
|
||||
|
||||
+61
-200
@@ -1,9 +1,9 @@
|
||||
/*
|
||||
* This file is part of the ZFS Event Daemon (ZED).
|
||||
*
|
||||
* This file is part of the ZFS Event Daemon (ZED)
|
||||
* for ZFS on Linux (ZoL) <http://zfsonlinux.org/>.
|
||||
* Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
|
||||
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
|
||||
* Refer to the OpenZFS git commit log for authoritative copyright attribution.
|
||||
* Refer to the ZoL git commit log for authoritative copyright attribution.
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License Version 1.0 (CDDL-1.0).
|
||||
@@ -18,55 +18,16 @@
|
||||
#include <fcntl.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/avl.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include "zed_exec.h"
|
||||
#include "zed_file.h"
|
||||
#include "zed_log.h"
|
||||
#include "zed_strings.h"
|
||||
|
||||
#define ZEVENT_FILENO 3
|
||||
|
||||
struct launched_process_node {
|
||||
avl_node_t node;
|
||||
pid_t pid;
|
||||
uint64_t eid;
|
||||
char *name;
|
||||
};
|
||||
|
||||
static int
|
||||
_launched_process_node_compare(const void *x1, const void *x2)
|
||||
{
|
||||
pid_t p1;
|
||||
pid_t p2;
|
||||
|
||||
assert(x1 != NULL);
|
||||
assert(x2 != NULL);
|
||||
|
||||
p1 = ((const struct launched_process_node *) x1)->pid;
|
||||
p2 = ((const struct launched_process_node *) x2)->pid;
|
||||
|
||||
if (p1 < p2)
|
||||
return (-1);
|
||||
else if (p1 == p2)
|
||||
return (0);
|
||||
else
|
||||
return (1);
|
||||
}
|
||||
|
||||
static pthread_t _reap_children_tid = (pthread_t)-1;
|
||||
static volatile boolean_t _reap_children_stop;
|
||||
static avl_tree_t _launched_processes;
|
||||
static pthread_mutex_t _launched_processes_lock = PTHREAD_MUTEX_INITIALIZER;
|
||||
static int16_t _launched_processes_limit;
|
||||
|
||||
/*
|
||||
* Create an environment string array for passing to execve() using the
|
||||
* NAME=VALUE strings in container [zsp].
|
||||
@@ -117,26 +78,20 @@ _zed_exec_create_env(zed_strings_t *zsp)
|
||||
*/
|
||||
static void
|
||||
_zed_exec_fork_child(uint64_t eid, const char *dir, const char *prog,
|
||||
char *env[], int zfd, boolean_t in_foreground)
|
||||
char *env[], int zfd)
|
||||
{
|
||||
char path[PATH_MAX];
|
||||
int n;
|
||||
pid_t pid;
|
||||
int fd;
|
||||
struct launched_process_node *node;
|
||||
sigset_t mask;
|
||||
struct timespec launch_timeout =
|
||||
{ .tv_sec = 0, .tv_nsec = 200 * 1000 * 1000, };
|
||||
pid_t wpid;
|
||||
int status;
|
||||
|
||||
assert(dir != NULL);
|
||||
assert(prog != NULL);
|
||||
assert(env != NULL);
|
||||
assert(zfd >= 0);
|
||||
|
||||
while (__atomic_load_n(&_launched_processes_limit,
|
||||
__ATOMIC_SEQ_CST) <= 0)
|
||||
(void) nanosleep(&launch_timeout, NULL);
|
||||
|
||||
n = snprintf(path, sizeof (path), "%s/%s", dir, prog);
|
||||
if ((n < 0) || (n >= sizeof (path))) {
|
||||
zed_log_msg(LOG_WARNING,
|
||||
@@ -144,179 +99,100 @@ _zed_exec_fork_child(uint64_t eid, const char *dir, const char *prog,
|
||||
prog, eid, strerror(ENAMETOOLONG));
|
||||
return;
|
||||
}
|
||||
(void) pthread_mutex_lock(&_launched_processes_lock);
|
||||
pid = fork();
|
||||
if (pid < 0) {
|
||||
(void) pthread_mutex_unlock(&_launched_processes_lock);
|
||||
zed_log_msg(LOG_WARNING,
|
||||
"Failed to fork \"%s\" for eid=%llu: %s",
|
||||
prog, eid, strerror(errno));
|
||||
return;
|
||||
} else if (pid == 0) {
|
||||
(void) sigemptyset(&mask);
|
||||
(void) sigprocmask(SIG_SETMASK, &mask, NULL);
|
||||
|
||||
(void) umask(022);
|
||||
if (in_foreground && /* we're already devnulled if daemonised */
|
||||
(fd = open("/dev/null", O_RDWR | O_CLOEXEC)) != -1) {
|
||||
if ((fd = open("/dev/null", O_RDWR)) != -1) {
|
||||
(void) dup2(fd, STDIN_FILENO);
|
||||
(void) dup2(fd, STDOUT_FILENO);
|
||||
(void) dup2(fd, STDERR_FILENO);
|
||||
}
|
||||
(void) dup2(zfd, ZEVENT_FILENO);
|
||||
zed_file_close_from(ZEVENT_FILENO + 1);
|
||||
execle(path, prog, NULL, env);
|
||||
_exit(127);
|
||||
}
|
||||
|
||||
/* parent process */
|
||||
|
||||
node = calloc(1, sizeof (*node));
|
||||
if (node) {
|
||||
node->pid = pid;
|
||||
node->eid = eid;
|
||||
node->name = strdup(prog);
|
||||
|
||||
avl_add(&_launched_processes, node);
|
||||
}
|
||||
(void) pthread_mutex_unlock(&_launched_processes_lock);
|
||||
|
||||
__atomic_sub_fetch(&_launched_processes_limit, 1, __ATOMIC_SEQ_CST);
|
||||
zed_log_msg(LOG_INFO, "Invoking \"%s\" eid=%llu pid=%d",
|
||||
prog, eid, pid);
|
||||
}
|
||||
|
||||
static void
|
||||
_nop(int sig)
|
||||
{}
|
||||
/* FIXME: Timeout rogue child processes with sigalarm? */
|
||||
|
||||
static void *
|
||||
_reap_children(void *arg)
|
||||
{
|
||||
struct launched_process_node node, *pnode;
|
||||
pid_t pid;
|
||||
int status;
|
||||
struct rusage usage;
|
||||
struct sigaction sa = {};
|
||||
/*
|
||||
* Wait for child process using WNOHANG to limit
|
||||
* the time spent waiting to 10 seconds (10,000ms).
|
||||
*/
|
||||
for (n = 0; n < 1000; n++) {
|
||||
wpid = waitpid(pid, &status, WNOHANG);
|
||||
if (wpid == (pid_t)-1) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
zed_log_msg(LOG_WARNING,
|
||||
"Failed to wait for \"%s\" eid=%llu pid=%d",
|
||||
prog, eid, pid);
|
||||
break;
|
||||
} else if (wpid == 0) {
|
||||
struct timespec t;
|
||||
|
||||
(void) sigfillset(&sa.sa_mask);
|
||||
(void) sigdelset(&sa.sa_mask, SIGCHLD);
|
||||
(void) pthread_sigmask(SIG_SETMASK, &sa.sa_mask, NULL);
|
||||
|
||||
(void) sigemptyset(&sa.sa_mask);
|
||||
sa.sa_handler = _nop;
|
||||
sa.sa_flags = SA_NOCLDSTOP;
|
||||
(void) sigaction(SIGCHLD, &sa, NULL);
|
||||
|
||||
for (_reap_children_stop = B_FALSE; !_reap_children_stop; ) {
|
||||
(void) pthread_mutex_lock(&_launched_processes_lock);
|
||||
pid = wait4(0, &status, WNOHANG, &usage);
|
||||
|
||||
if (pid == 0 || pid == (pid_t)-1) {
|
||||
(void) pthread_mutex_unlock(&_launched_processes_lock);
|
||||
if (pid == 0 || errno == ECHILD)
|
||||
pause();
|
||||
else if (errno != EINTR)
|
||||
zed_log_msg(LOG_WARNING,
|
||||
"Failed to wait for children: %s",
|
||||
strerror(errno));
|
||||
} else {
|
||||
memset(&node, 0, sizeof (node));
|
||||
node.pid = pid;
|
||||
pnode = avl_find(&_launched_processes, &node, NULL);
|
||||
if (pnode) {
|
||||
memcpy(&node, pnode, sizeof (node));
|
||||
|
||||
avl_remove(&_launched_processes, pnode);
|
||||
free(pnode);
|
||||
}
|
||||
(void) pthread_mutex_unlock(&_launched_processes_lock);
|
||||
__atomic_add_fetch(&_launched_processes_limit, 1,
|
||||
__ATOMIC_SEQ_CST);
|
||||
|
||||
usage.ru_utime.tv_sec += usage.ru_stime.tv_sec;
|
||||
usage.ru_utime.tv_usec += usage.ru_stime.tv_usec;
|
||||
usage.ru_utime.tv_sec +=
|
||||
usage.ru_utime.tv_usec / (1000 * 1000);
|
||||
usage.ru_utime.tv_usec %= 1000 * 1000;
|
||||
|
||||
if (WIFEXITED(status)) {
|
||||
zed_log_msg(LOG_INFO,
|
||||
"Finished \"%s\" eid=%llu pid=%d "
|
||||
"time=%llu.%06us exit=%d",
|
||||
node.name, node.eid, pid,
|
||||
(unsigned long long) usage.ru_utime.tv_sec,
|
||||
(unsigned int) usage.ru_utime.tv_usec,
|
||||
WEXITSTATUS(status));
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
zed_log_msg(LOG_INFO,
|
||||
"Finished \"%s\" eid=%llu pid=%d "
|
||||
"time=%llu.%06us sig=%d/%s",
|
||||
node.name, node.eid, pid,
|
||||
(unsigned long long) usage.ru_utime.tv_sec,
|
||||
(unsigned int) usage.ru_utime.tv_usec,
|
||||
WTERMSIG(status),
|
||||
strsignal(WTERMSIG(status)));
|
||||
} else {
|
||||
zed_log_msg(LOG_INFO,
|
||||
"Finished \"%s\" eid=%llu pid=%d "
|
||||
"time=%llu.%06us status=0x%X",
|
||||
node.name, node.eid,
|
||||
(unsigned long long) usage.ru_utime.tv_sec,
|
||||
(unsigned int) usage.ru_utime.tv_usec,
|
||||
(unsigned int) status);
|
||||
}
|
||||
|
||||
free(node.name);
|
||||
/* child still running */
|
||||
t.tv_sec = 0;
|
||||
t.tv_nsec = 10000000; /* 10ms */
|
||||
(void) nanosleep(&t, NULL);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (WIFEXITED(status)) {
|
||||
zed_log_msg(LOG_INFO,
|
||||
"Finished \"%s\" eid=%llu pid=%d exit=%d",
|
||||
prog, eid, pid, WEXITSTATUS(status));
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
zed_log_msg(LOG_INFO,
|
||||
"Finished \"%s\" eid=%llu pid=%d sig=%d/%s",
|
||||
prog, eid, pid, WTERMSIG(status),
|
||||
strsignal(WTERMSIG(status)));
|
||||
} else {
|
||||
zed_log_msg(LOG_INFO,
|
||||
"Finished \"%s\" eid=%llu pid=%d status=0x%X",
|
||||
prog, eid, (unsigned int) status);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
void
|
||||
zed_exec_fini(void)
|
||||
{
|
||||
struct launched_process_node *node;
|
||||
void *ck = NULL;
|
||||
|
||||
if (_reap_children_tid == (pthread_t)-1)
|
||||
return;
|
||||
|
||||
_reap_children_stop = B_TRUE;
|
||||
(void) pthread_kill(_reap_children_tid, SIGCHLD);
|
||||
(void) pthread_join(_reap_children_tid, NULL);
|
||||
|
||||
while ((node = avl_destroy_nodes(&_launched_processes, &ck)) != NULL) {
|
||||
free(node->name);
|
||||
free(node);
|
||||
/*
|
||||
* kill child process after 10 seconds
|
||||
*/
|
||||
if (wpid == 0) {
|
||||
zed_log_msg(LOG_WARNING, "Killing hung \"%s\" pid=%d",
|
||||
prog, pid);
|
||||
(void) kill(pid, SIGKILL);
|
||||
}
|
||||
avl_destroy(&_launched_processes);
|
||||
|
||||
(void) pthread_mutex_destroy(&_launched_processes_lock);
|
||||
(void) pthread_mutex_init(&_launched_processes_lock, NULL);
|
||||
|
||||
_reap_children_tid = (pthread_t)-1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Process the event [eid] by synchronously invoking all zedlets with a
|
||||
* matching class prefix.
|
||||
*
|
||||
* Each executable in [zcp->zedlets] from the directory [zcp->zedlet_dir]
|
||||
* is matched against the event's [class], [subclass], and the "all" class
|
||||
* (which matches all events).
|
||||
* Every zedlet with a matching class prefix is invoked.
|
||||
* Each executable in [zedlets] from the directory [dir] is matched against
|
||||
* the event's [class], [subclass], and the "all" class (which matches
|
||||
* all events). Every zedlet with a matching class prefix is invoked.
|
||||
* The NAME=VALUE strings in [envs] will be passed to the zedlet as
|
||||
* environment variables.
|
||||
*
|
||||
* The file descriptor [zcp->zevent_fd] is the zevent_fd used to track the
|
||||
* The file descriptor [zfd] is the zevent_fd used to track the
|
||||
* current cursor location within the zevent nvlist.
|
||||
*
|
||||
* Return 0 on success, -1 on error.
|
||||
*/
|
||||
int
|
||||
zed_exec_process(uint64_t eid, const char *class, const char *subclass,
|
||||
struct zed_conf *zcp, zed_strings_t *envs)
|
||||
const char *dir, zed_strings_t *zedlets, zed_strings_t *envs, int zfd)
|
||||
{
|
||||
const char *class_strings[4];
|
||||
const char *allclass = "all";
|
||||
@@ -325,22 +201,9 @@ zed_exec_process(uint64_t eid, const char *class, const char *subclass,
|
||||
char **e;
|
||||
int n;
|
||||
|
||||
if (!zcp->zedlet_dir || !zcp->zedlets || !envs || zcp->zevent_fd < 0)
|
||||
if (!dir || !zedlets || !envs || zfd < 0)
|
||||
return (-1);
|
||||
|
||||
if (_reap_children_tid == (pthread_t)-1) {
|
||||
_launched_processes_limit = zcp->max_jobs;
|
||||
|
||||
if (pthread_create(&_reap_children_tid, NULL,
|
||||
_reap_children, NULL) != 0)
|
||||
return (-1);
|
||||
pthread_setname_np(_reap_children_tid, "reap ZEDLETs");
|
||||
|
||||
avl_create(&_launched_processes, _launched_process_node_compare,
|
||||
sizeof (struct launched_process_node),
|
||||
offsetof(struct launched_process_node, node));
|
||||
}
|
||||
|
||||
csp = class_strings;
|
||||
|
||||
if (class)
|
||||
@@ -356,13 +219,11 @@ zed_exec_process(uint64_t eid, const char *class, const char *subclass,
|
||||
|
||||
e = _zed_exec_create_env(envs);
|
||||
|
||||
for (z = zed_strings_first(zcp->zedlets); z;
|
||||
z = zed_strings_next(zcp->zedlets)) {
|
||||
for (z = zed_strings_first(zedlets); z; z = zed_strings_next(zedlets)) {
|
||||
for (csp = class_strings; *csp; csp++) {
|
||||
n = strlen(*csp);
|
||||
if ((strncmp(z, *csp, n) == 0) && !isalpha(z[n]))
|
||||
_zed_exec_fork_child(eid, zcp->zedlet_dir,
|
||||
z, e, zcp->zevent_fd, zcp->do_foreground);
|
||||
_zed_exec_fork_child(eid, dir, z, e, zfd);
|
||||
}
|
||||
}
|
||||
free(e);
|
||||
|
||||
+5
-8
@@ -1,9 +1,9 @@
|
||||
/*
|
||||
* This file is part of the ZFS Event Daemon (ZED).
|
||||
*
|
||||
* This file is part of the ZFS Event Daemon (ZED)
|
||||
* for ZFS on Linux (ZoL) <http://zfsonlinux.org/>.
|
||||
* Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
|
||||
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
|
||||
* Refer to the OpenZFS git commit log for authoritative copyright attribution.
|
||||
* Refer to the ZoL git commit log for authoritative copyright attribution.
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License Version 1.0 (CDDL-1.0).
|
||||
@@ -16,12 +16,9 @@
|
||||
#define ZED_EXEC_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include "zed_strings.h"
|
||||
#include "zed_conf.h"
|
||||
|
||||
void zed_exec_fini(void);
|
||||
|
||||
int zed_exec_process(uint64_t eid, const char *class, const char *subclass,
|
||||
struct zed_conf *zcp, zed_strings_t *envs);
|
||||
const char *dir, zed_strings_t *zedlets, zed_strings_t *envs,
|
||||
int zevent_fd);
|
||||
|
||||
#endif /* !ZED_EXEC_H */
|
||||
|
||||
+99
-24
@@ -1,9 +1,9 @@
|
||||
/*
|
||||
* This file is part of the ZFS Event Daemon (ZED).
|
||||
*
|
||||
* This file is part of the ZFS Event Daemon (ZED)
|
||||
* for ZFS on Linux (ZoL) <http://zfsonlinux.org/>.
|
||||
* Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
|
||||
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
|
||||
* Refer to the OpenZFS git commit log for authoritative copyright attribution.
|
||||
* Refer to the ZoL git commit log for authoritative copyright attribution.
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License Version 1.0 (CDDL-1.0).
|
||||
@@ -12,17 +12,72 @@
|
||||
* You may not use this file except in compliance with the license.
|
||||
*/
|
||||
|
||||
#include <dirent.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <string.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include "zed_file.h"
|
||||
#include "zed_log.h"
|
||||
|
||||
/*
|
||||
* Read up to [n] bytes from [fd] into [buf].
|
||||
* Return the number of bytes read, 0 on EOF, or -1 on error.
|
||||
*/
|
||||
ssize_t
|
||||
zed_file_read_n(int fd, void *buf, size_t n)
|
||||
{
|
||||
unsigned char *p;
|
||||
size_t n_left;
|
||||
ssize_t n_read;
|
||||
|
||||
p = buf;
|
||||
n_left = n;
|
||||
while (n_left > 0) {
|
||||
if ((n_read = read(fd, p, n_left)) < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
else
|
||||
return (-1);
|
||||
|
||||
} else if (n_read == 0) {
|
||||
break;
|
||||
}
|
||||
n_left -= n_read;
|
||||
p += n_read;
|
||||
}
|
||||
return (n - n_left);
|
||||
}
|
||||
|
||||
/*
|
||||
* Write [n] bytes from [buf] out to [fd].
|
||||
* Return the number of bytes written, or -1 on error.
|
||||
*/
|
||||
ssize_t
|
||||
zed_file_write_n(int fd, void *buf, size_t n)
|
||||
{
|
||||
const unsigned char *p;
|
||||
size_t n_left;
|
||||
ssize_t n_written;
|
||||
|
||||
p = buf;
|
||||
n_left = n;
|
||||
while (n_left > 0) {
|
||||
if ((n_written = write(fd, p, n_left)) < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
else
|
||||
return (-1);
|
||||
|
||||
}
|
||||
n_left -= n_written;
|
||||
p += n_written;
|
||||
}
|
||||
return (n);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set an exclusive advisory lock on the open file descriptor [fd].
|
||||
* Return 0 on success, 1 if a conflicting lock is held by another process,
|
||||
@@ -104,13 +159,6 @@ zed_file_is_locked(int fd)
|
||||
return (lock.l_pid);
|
||||
}
|
||||
|
||||
|
||||
#if __APPLE__
|
||||
#define PROC_SELF_FD "/dev/fd"
|
||||
#else /* Linux-compatible layout */
|
||||
#define PROC_SELF_FD "/proc/self/fd"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Close all open file descriptors greater than or equal to [lowfd].
|
||||
* Any errors encountered while closing file descriptors are ignored.
|
||||
@@ -118,24 +166,51 @@ zed_file_is_locked(int fd)
|
||||
void
|
||||
zed_file_close_from(int lowfd)
|
||||
{
|
||||
int errno_bak = errno;
|
||||
int maxfd = 0;
|
||||
const int maxfd_def = 256;
|
||||
int errno_bak;
|
||||
struct rlimit rl;
|
||||
int maxfd;
|
||||
int fd;
|
||||
DIR *fddir;
|
||||
struct dirent *fdent;
|
||||
|
||||
if ((fddir = opendir(PROC_SELF_FD)) != NULL) {
|
||||
while ((fdent = readdir(fddir)) != NULL) {
|
||||
fd = atoi(fdent->d_name);
|
||||
if (fd > maxfd && fd != dirfd(fddir))
|
||||
maxfd = fd;
|
||||
}
|
||||
(void) closedir(fddir);
|
||||
errno_bak = errno;
|
||||
|
||||
if (getrlimit(RLIMIT_NOFILE, &rl) < 0) {
|
||||
maxfd = maxfd_def;
|
||||
} else if (rl.rlim_max == RLIM_INFINITY) {
|
||||
maxfd = maxfd_def;
|
||||
} else {
|
||||
maxfd = sysconf(_SC_OPEN_MAX);
|
||||
maxfd = rl.rlim_max;
|
||||
}
|
||||
for (fd = lowfd; fd < maxfd; fd++)
|
||||
(void) close(fd);
|
||||
|
||||
errno = errno_bak;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the CLOEXEC flag on file descriptor [fd] so it will be automatically
|
||||
* closed upon successful execution of one of the exec functions.
|
||||
* Return 0 on success, or -1 on error.
|
||||
*
|
||||
* FIXME: No longer needed?
|
||||
*/
|
||||
int
|
||||
zed_file_close_on_exec(int fd)
|
||||
{
|
||||
int flags;
|
||||
|
||||
if (fd < 0) {
|
||||
errno = EBADF;
|
||||
return (-1);
|
||||
}
|
||||
flags = fcntl(fd, F_GETFD);
|
||||
if (flags == -1)
|
||||
return (-1);
|
||||
|
||||
flags |= FD_CLOEXEC;
|
||||
|
||||
if (fcntl(fd, F_SETFD, flags) == -1)
|
||||
return (-1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
+9
-3
@@ -1,9 +1,9 @@
|
||||
/*
|
||||
* This file is part of the ZFS Event Daemon (ZED).
|
||||
*
|
||||
* This file is part of the ZFS Event Daemon (ZED)
|
||||
* for ZFS on Linux (ZoL) <http://zfsonlinux.org/>.
|
||||
* Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
|
||||
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
|
||||
* Refer to the OpenZFS git commit log for authoritative copyright attribution.
|
||||
* Refer to the ZoL git commit log for authoritative copyright attribution.
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License Version 1.0 (CDDL-1.0).
|
||||
@@ -18,6 +18,10 @@
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
ssize_t zed_file_read_n(int fd, void *buf, size_t n);
|
||||
|
||||
ssize_t zed_file_write_n(int fd, void *buf, size_t n);
|
||||
|
||||
int zed_file_lock(int fd);
|
||||
|
||||
int zed_file_unlock(int fd);
|
||||
@@ -26,4 +30,6 @@ pid_t zed_file_is_locked(int fd);
|
||||
|
||||
void zed_file_close_from(int fd);
|
||||
|
||||
int zed_file_close_on_exec(int fd);
|
||||
|
||||
#endif /* !ZED_FILE_H */
|
||||
|
||||
+3
-3
@@ -1,9 +1,9 @@
|
||||
/*
|
||||
* This file is part of the ZFS Event Daemon (ZED).
|
||||
*
|
||||
* This file is part of the ZFS Event Daemon (ZED)
|
||||
* for ZFS on Linux (ZoL) <http://zfsonlinux.org/>.
|
||||
* Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
|
||||
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
|
||||
* Refer to the OpenZFS git commit log for authoritative copyright attribution.
|
||||
* Refer to the ZoL git commit log for authoritative copyright attribution.
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License Version 1.0 (CDDL-1.0).
|
||||
|
||||
+3
-3
@@ -1,9 +1,9 @@
|
||||
/*
|
||||
* This file is part of the ZFS Event Daemon (ZED).
|
||||
*
|
||||
* This file is part of the ZFS Event Daemon (ZED)
|
||||
* for ZFS on Linux (ZoL) <http://zfsonlinux.org/>.
|
||||
* Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
|
||||
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
|
||||
* Refer to the OpenZFS git commit log for authoritative copyright attribution.
|
||||
* Refer to the ZoL git commit log for authoritative copyright attribution.
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License Version 1.0 (CDDL-1.0).
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/*
|
||||
* This file is part of the ZFS Event Daemon (ZED).
|
||||
*
|
||||
* This file is part of the ZFS Event Daemon (ZED)
|
||||
* for ZFS on Linux (ZoL) <http://zfsonlinux.org/>.
|
||||
* Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
|
||||
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
|
||||
* Refer to the OpenZFS git commit log for authoritative copyright attribution.
|
||||
* Refer to the ZoL git commit log for authoritative copyright attribution.
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License Version 1.0 (CDDL-1.0).
|
||||
@@ -108,7 +108,7 @@ _zed_strings_node_destroy(zed_strings_node_t *np)
|
||||
* If [key] is specified, it will be used to index the node; otherwise,
|
||||
* the string [val] will be used.
|
||||
*/
|
||||
static zed_strings_node_t *
|
||||
zed_strings_node_t *
|
||||
_zed_strings_node_create(const char *key, const char *val)
|
||||
{
|
||||
zed_strings_node_t *np;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
/*
|
||||
* This file is part of the ZFS Event Daemon (ZED).
|
||||
*
|
||||
* This file is part of the ZFS Event Daemon (ZED)
|
||||
* for ZFS on Linux (ZoL) <http://zfsonlinux.org/>.
|
||||
* Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
|
||||
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
|
||||
* Refer to the OpenZFS git commit log for authoritative copyright attribution.
|
||||
* Refer to the ZoL git commit log for authoritative copyright attribution.
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License Version 1.0 (CDDL-1.0).
|
||||
|
||||
+11
-14
@@ -1,25 +1,22 @@
|
||||
include $(top_srcdir)/config/Rules.am
|
||||
|
||||
DEFAULT_INCLUDES += \
|
||||
-I$(top_srcdir)/include \
|
||||
-I$(top_srcdir)/lib/libspl/include
|
||||
|
||||
sbin_PROGRAMS = zfs
|
||||
|
||||
zfs_SOURCES = \
|
||||
zfs_iter.c \
|
||||
zfs_iter.h \
|
||||
zfs_main.c \
|
||||
zfs_util.h \
|
||||
zfs_project.c \
|
||||
zfs_projectutil.h
|
||||
zfs_util.h
|
||||
|
||||
zfs_LDADD = \
|
||||
$(abs_top_builddir)/lib/libzfs/libzfs.la \
|
||||
$(abs_top_builddir)/lib/libzfs_core/libzfs_core.la \
|
||||
$(abs_top_builddir)/lib/libnvpair/libnvpair.la \
|
||||
$(abs_top_builddir)/lib/libuutil/libuutil.la
|
||||
$(top_builddir)/lib/libnvpair/libnvpair.la \
|
||||
$(top_builddir)/lib/libuutil/libuutil.la \
|
||||
$(top_builddir)/lib/libzpool/libzpool.la \
|
||||
$(top_builddir)/lib/libzfs/libzfs.la \
|
||||
$(top_builddir)/lib/libzfs_core/libzfs_core.la
|
||||
|
||||
zfs_LDADD += $(LTLIBINTL)
|
||||
|
||||
if BUILD_FREEBSD
|
||||
zfs_LDADD += -lgeom -ljail
|
||||
endif
|
||||
|
||||
include $(top_srcdir)/config/CppCheck.am
|
||||
zfs_LDFLAGS = -pthread
|
||||
|
||||
+6
-22
@@ -31,7 +31,6 @@
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
|
||||
#include <libzfs.h>
|
||||
@@ -134,31 +133,16 @@ zfs_callback(zfs_handle_t *zhp, void *data)
|
||||
((cb->cb_flags & ZFS_ITER_DEPTH_LIMIT) == 0 ||
|
||||
cb->cb_depth < cb->cb_depth_limit)) {
|
||||
cb->cb_depth++;
|
||||
|
||||
/*
|
||||
* If we are not looking for filesystems, we don't need to
|
||||
* recurse into filesystems when we are at our depth limit.
|
||||
*/
|
||||
if ((cb->cb_depth < cb->cb_depth_limit ||
|
||||
(cb->cb_flags & ZFS_ITER_DEPTH_LIMIT) == 0 ||
|
||||
(cb->cb_types &
|
||||
(ZFS_TYPE_FILESYSTEM | ZFS_TYPE_VOLUME))) &&
|
||||
zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM) {
|
||||
if (zfs_get_type(zhp) == ZFS_TYPE_FILESYSTEM)
|
||||
(void) zfs_iter_filesystems(zhp, zfs_callback, data);
|
||||
}
|
||||
|
||||
if (((zfs_get_type(zhp) & (ZFS_TYPE_SNAPSHOT |
|
||||
ZFS_TYPE_BOOKMARK)) == 0) && include_snaps) {
|
||||
ZFS_TYPE_BOOKMARK)) == 0) && include_snaps)
|
||||
(void) zfs_iter_snapshots(zhp,
|
||||
(cb->cb_flags & ZFS_ITER_SIMPLE) != 0,
|
||||
zfs_callback, data, 0, 0);
|
||||
}
|
||||
|
||||
(cb->cb_flags & ZFS_ITER_SIMPLE) != 0, zfs_callback,
|
||||
data);
|
||||
if (((zfs_get_type(zhp) & (ZFS_TYPE_SNAPSHOT |
|
||||
ZFS_TYPE_BOOKMARK)) == 0) && include_bmarks) {
|
||||
ZFS_TYPE_BOOKMARK)) == 0) && include_bmarks)
|
||||
(void) zfs_iter_bookmarks(zhp, zfs_callback, data);
|
||||
}
|
||||
|
||||
cb->cb_depth--;
|
||||
}
|
||||
|
||||
@@ -240,7 +224,7 @@ zfs_compare(const void *larg, const void *rarg, void *unused)
|
||||
*rat = '\0';
|
||||
|
||||
ret = strcmp(lname, rname);
|
||||
if (ret == 0 && (lat != NULL || rat != NULL)) {
|
||||
if (ret == 0) {
|
||||
/*
|
||||
* If we're comparing a dataset to one of its snapshots, we
|
||||
* always make the full dataset first.
|
||||
|
||||
+315
-2025
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user