mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2026-05-24 11:18:52 +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 |
-21
@@ -1,21 +0,0 @@
|
||||
env:
|
||||
CIRRUS_CLONE_DEPTH: 1
|
||||
ARCH: amd64
|
||||
|
||||
build_task:
|
||||
matrix:
|
||||
freebsd_instance:
|
||||
image_family: freebsd-12-4
|
||||
freebsd_instance:
|
||||
image_family: freebsd-13-2
|
||||
freebsd_instance:
|
||||
image_family: freebsd-14-0-snap
|
||||
prepare_script:
|
||||
- pkg install -y autoconf automake libtool gettext-runtime gmake ksh93 py39-packaging py39-cffi py39-sysctl
|
||||
configure_script:
|
||||
- env MAKE=gmake ./autogen.sh
|
||||
- env MAKE=gmake ./configure --with-config="user" --with-python=3.9
|
||||
build_script:
|
||||
- gmake -j `sysctl -n kern.smp.cpus`
|
||||
install_script:
|
||||
- gmake install
|
||||
@@ -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,4 +0,0 @@
|
||||
name: "Custom CodeQL Analysis"
|
||||
|
||||
queries:
|
||||
- uses: ./.github/codeql/custom-queries/cpp/deprecatedFunctionUsage.ql
|
||||
@@ -1,4 +0,0 @@
|
||||
name: "Custom CodeQL Analysis"
|
||||
|
||||
paths-ignore:
|
||||
- tests
|
||||
@@ -1,59 +0,0 @@
|
||||
/**
|
||||
* @name Deprecated function usage detection
|
||||
* @description Detects functions whose usage is banned from the OpenZFS
|
||||
* codebase due to QA concerns.
|
||||
* @kind problem
|
||||
* @severity error
|
||||
* @id cpp/deprecated-function-usage
|
||||
*/
|
||||
|
||||
import cpp
|
||||
|
||||
predicate isDeprecatedFunction(Function f) {
|
||||
f.getName() = "strtok" or
|
||||
f.getName() = "__xpg_basename" or
|
||||
f.getName() = "basename" or
|
||||
f.getName() = "dirname" or
|
||||
f.getName() = "bcopy" or
|
||||
f.getName() = "bcmp" or
|
||||
f.getName() = "bzero" or
|
||||
f.getName() = "asctime" or
|
||||
f.getName() = "asctime_r" or
|
||||
f.getName() = "gmtime" or
|
||||
f.getName() = "localtime" or
|
||||
f.getName() = "strncpy"
|
||||
|
||||
}
|
||||
|
||||
string getReplacementMessage(Function f) {
|
||||
if f.getName() = "strtok" then
|
||||
result = "Use strtok_r(3) instead!"
|
||||
else if f.getName() = "__xpg_basename" then
|
||||
result = "basename(3) is underspecified. Use zfs_basename() instead!"
|
||||
else if f.getName() = "basename" then
|
||||
result = "basename(3) is underspecified. Use zfs_basename() instead!"
|
||||
else if f.getName() = "dirname" then
|
||||
result = "dirname(3) is underspecified. Use zfs_dirnamelen() instead!"
|
||||
else if f.getName() = "bcopy" then
|
||||
result = "bcopy(3) is deprecated. Use memcpy(3)/memmove(3) instead!"
|
||||
else if f.getName() = "bcmp" then
|
||||
result = "bcmp(3) is deprecated. Use memcmp(3) instead!"
|
||||
else if f.getName() = "bzero" then
|
||||
result = "bzero(3) is deprecated. Use memset(3) instead!"
|
||||
else if f.getName() = "asctime" then
|
||||
result = "Use strftime(3) instead!"
|
||||
else if f.getName() = "asctime_r" then
|
||||
result = "Use strftime(3) instead!"
|
||||
else if f.getName() = "gmtime" then
|
||||
result = "gmtime(3) isn't thread-safe. Use gmtime_r(3) instead!"
|
||||
else if f.getName() = "localtime" then
|
||||
result = "localtime(3) isn't thread-safe. Use localtime_r(3) instead!"
|
||||
else
|
||||
result = "strncpy(3) is deprecated. Use strlcpy(3) instead!"
|
||||
}
|
||||
|
||||
from FunctionCall fc, Function f
|
||||
where
|
||||
fc.getTarget() = f and
|
||||
isDeprecatedFunction(f)
|
||||
select fc, getReplacementMessage(f)
|
||||
@@ -1,4 +0,0 @@
|
||||
name: openzfs-cpp-queries
|
||||
version: 0.0.0
|
||||
libraryPathDependencies: codeql-cpp
|
||||
suites: openzfs-cpp-suite
|
||||
@@ -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,61 +0,0 @@
|
||||
|
||||
## The testings are done this way
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph CleanUp and Summary
|
||||
CleanUp+Summary
|
||||
end
|
||||
|
||||
subgraph Functional Testings
|
||||
sanity-checks-20.04
|
||||
zloop-checks-20.04
|
||||
functional-testing-20.04-->Part1-20.04
|
||||
functional-testing-20.04-->Part2-20.04
|
||||
functional-testing-20.04-->Part3-20.04
|
||||
functional-testing-20.04-->Part4-20.04
|
||||
functional-testing-22.04-->Part1-22.04
|
||||
functional-testing-22.04-->Part2-22.04
|
||||
functional-testing-22.04-->Part3-22.04
|
||||
functional-testing-22.04-->Part4-22.04
|
||||
sanity-checks-22.04
|
||||
zloop-checks-22.04
|
||||
end
|
||||
|
||||
subgraph Code Checking + Building
|
||||
Build-Ubuntu-20.04
|
||||
codeql.yml
|
||||
checkstyle.yml
|
||||
Build-Ubuntu-22.04
|
||||
end
|
||||
|
||||
Build-Ubuntu-20.04-->sanity-checks-20.04
|
||||
Build-Ubuntu-20.04-->zloop-checks-20.04
|
||||
Build-Ubuntu-20.04-->functional-testing-20.04
|
||||
Build-Ubuntu-22.04-->sanity-checks-22.04
|
||||
Build-Ubuntu-22.04-->zloop-checks-22.04
|
||||
Build-Ubuntu-22.04-->functional-testing-22.04
|
||||
|
||||
sanity-checks-20.04-->CleanUp+Summary
|
||||
Part1-20.04-->CleanUp+Summary
|
||||
Part2-20.04-->CleanUp+Summary
|
||||
Part3-20.04-->CleanUp+Summary
|
||||
Part4-20.04-->CleanUp+Summary
|
||||
Part1-22.04-->CleanUp+Summary
|
||||
Part2-22.04-->CleanUp+Summary
|
||||
Part3-22.04-->CleanUp+Summary
|
||||
Part4-22.04-->CleanUp+Summary
|
||||
sanity-checks-22.04-->CleanUp+Summary
|
||||
```
|
||||
|
||||
|
||||
1) build zfs modules for Ubuntu 20.04 and 22.04 (~15m)
|
||||
2) 2x zloop test (~10m) + 2x sanity test (~25m)
|
||||
3) 4x functional testings in parts 1..4 (each ~1h)
|
||||
4) cleanup and create summary
|
||||
- content of summary depends on the results of the steps
|
||||
|
||||
When everything runs fine, the full run should be done in
|
||||
about 2 hours.
|
||||
|
||||
The codeql.yml and checkstyle.yml are not part in this circle.
|
||||
@@ -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,5 +0,0 @@
|
||||
cppcheck
|
||||
devscripts
|
||||
mandoc
|
||||
pax-utils
|
||||
shellcheck
|
||||
@@ -1,59 +0,0 @@
|
||||
name: checkstyle
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
checkstyle:
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
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
|
||||
checkbashisms --version
|
||||
cppcheck --version
|
||||
flake8 --version
|
||||
scanelf --version
|
||||
shellcheck --version
|
||||
- name: Prepare
|
||||
run: |
|
||||
./autogen.sh
|
||||
./configure
|
||||
make -j$(nproc) --no-print-directory --silent
|
||||
- name: Checkstyle
|
||||
run: |
|
||||
make -j$(nproc) --no-print-directory --silent checkstyle
|
||||
- name: Lint
|
||||
run: |
|
||||
make -j$(nproc) --no-print-directory --silent lint
|
||||
- name: CheckABI
|
||||
id: CheckABI
|
||||
run: |
|
||||
docker run -v $PWD:/source ghcr.io/openzfs/libabigail make -j$(nproc) --no-print-directory --silent checkabi
|
||||
- name: StoreABI
|
||||
if: failure() && steps.CheckABI.outcome == 'failure'
|
||||
run: |
|
||||
docker run -v $PWD:/source ghcr.io/openzfs/libabigail make -j$(nproc) --no-print-directory --silent storeabi
|
||||
- name: Prepare artifacts
|
||||
if: failure() && steps.CheckABI.outcome == 'failure'
|
||||
run: |
|
||||
find -name *.abi | tar -cf abi_files.tar -T -
|
||||
- uses: actions/upload-artifact@v4
|
||||
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,41 +0,0 @@
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'cpp', 'python' ]
|
||||
|
||||
steps:
|
||||
- name: Set make jobs
|
||||
run: |
|
||||
echo "MAKEFLAGS=-j$(nproc)" >> $GITHUB_ENV
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
config-file: .github/codeql-${{ matrix.language }}.yml
|
||||
languages: ${{ matrix.language }}
|
||||
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
||||
@@ -1,119 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# for runtime reasons we split functional testings into N parts
|
||||
# - use a define to check for missing tarfiles
|
||||
FUNCTIONAL_PARTS="4"
|
||||
|
||||
ZTS_REPORT="tests/test-runner/bin/zts-report.py"
|
||||
chmod +x $ZTS_REPORT
|
||||
|
||||
function output() {
|
||||
echo -e $* >> Summary.md
|
||||
}
|
||||
|
||||
function error() {
|
||||
output ":bangbang: $* :bangbang:\n"
|
||||
}
|
||||
|
||||
# this function generates the real summary
|
||||
# - expects a logfile "log" in current directory
|
||||
function generate() {
|
||||
# we issued some error already
|
||||
test ! -s log && return
|
||||
|
||||
# for overview and zts-report
|
||||
cat log | grep '^Test' > list
|
||||
|
||||
# error details
|
||||
awk '/\[FAIL\]|\[KILLED\]/{ show=1; print; next; }
|
||||
/\[SKIP\]|\[PASS\]/{ show=0; } show' log > err
|
||||
|
||||
# summary of errors
|
||||
if [ -s err ]; then
|
||||
output "<pre>"
|
||||
$ZTS_REPORT --no-maybes ./list >> Summary.md
|
||||
output "</pre>"
|
||||
|
||||
# generate seperate error logfile
|
||||
ERRLOGS=$((ERRLOGS+1))
|
||||
errfile="err-$ERRLOGS.md"
|
||||
echo -e "\n## $headline (debugging)\n" >> $errfile
|
||||
echo "<details><summary>Error Listing - with dmesg and dbgmsg</summary><pre>" >> $errfile
|
||||
dd if=err bs=999k count=1 >> $errfile
|
||||
echo "</pre></details>" >> $errfile
|
||||
else
|
||||
output "All tests passed :thumbsup:"
|
||||
fi
|
||||
|
||||
output "<details><summary>Full Listing</summary><pre>"
|
||||
cat list >> Summary.md
|
||||
output "</pre></details>"
|
||||
|
||||
# remove tmp files
|
||||
rm -f err list log
|
||||
}
|
||||
|
||||
# check tarfiles and untar
|
||||
function check_tarfile() {
|
||||
if [ -f "$1" ]; then
|
||||
tar xf "$1" || error "Tarfile $1 returns some error"
|
||||
else
|
||||
error "Tarfile $1 not found"
|
||||
fi
|
||||
}
|
||||
|
||||
# check logfile and concatenate test results
|
||||
function check_logfile() {
|
||||
if [ -f "$1" ]; then
|
||||
cat "$1" >> log
|
||||
else
|
||||
error "Logfile $1 not found"
|
||||
fi
|
||||
}
|
||||
|
||||
# sanity
|
||||
function summarize_s() {
|
||||
headline="$1"
|
||||
output "\n## $headline\n"
|
||||
rm -rf testfiles
|
||||
check_tarfile "$2/sanity.tar"
|
||||
check_logfile "testfiles/log"
|
||||
generate
|
||||
}
|
||||
|
||||
# functional
|
||||
function summarize_f() {
|
||||
headline="$1"
|
||||
output "\n## $headline\n"
|
||||
rm -rf testfiles
|
||||
for i in $(seq 1 $FUNCTIONAL_PARTS); do
|
||||
tarfile="$2-part$i/part$i.tar"
|
||||
check_tarfile "$tarfile"
|
||||
check_logfile "testfiles/log"
|
||||
done
|
||||
generate
|
||||
}
|
||||
|
||||
# https://docs.github.com/en/enterprise-server@3.6/actions/using-workflows/workflow-commands-for-github-actions#step-isolation-and-limits
|
||||
# Job summaries are isolated between steps and each step is restricted to a maximum size of 1MiB.
|
||||
# [ ] can not show all error findings here
|
||||
# [x] split files into smaller ones and create additional steps
|
||||
|
||||
ERRLOGS=0
|
||||
if [ ! -f Summary/Summary.md ]; then
|
||||
# first call, we do the default summary (~500k)
|
||||
echo -n > Summary.md
|
||||
summarize_s "Sanity Tests Ubuntu 20.04" Logs-20.04-sanity
|
||||
summarize_s "Sanity Tests Ubuntu 22.04" Logs-22.04-sanity
|
||||
summarize_f "Functional Tests Ubuntu 20.04" Logs-20.04-functional
|
||||
summarize_f "Functional Tests Ubuntu 22.04" Logs-22.04-functional
|
||||
|
||||
cat Summary.md >> $GITHUB_STEP_SUMMARY
|
||||
mkdir -p Summary
|
||||
mv *.md Summary
|
||||
else
|
||||
# here we get, when errors where returned in first call
|
||||
test -f Summary/err-$1.md && cat Summary/err-$1.md >> $GITHUB_STEP_SUMMARY
|
||||
fi
|
||||
|
||||
exit 0
|
||||
@@ -1,88 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -eu
|
||||
|
||||
function prerun() {
|
||||
echo "::group::Install build dependencies"
|
||||
# remove snap things, update+upgrade will be faster then
|
||||
for x in lxd core20 snapd; do sudo snap remove $x; done
|
||||
sudo apt-get purge snapd google-chrome-stable firefox
|
||||
# https://github.com/orgs/community/discussions/47863
|
||||
sudo apt-get remove grub-efi-amd64-bin grub-efi-amd64-signed shim-signed --allow-remove-essential
|
||||
sudo apt-get update
|
||||
sudo apt upgrade
|
||||
sudo xargs --arg-file=.github/workflows/build-dependencies.txt apt-get install -qq
|
||||
sudo apt-get clean
|
||||
sudo dmesg -c > /var/tmp/dmesg-prerun
|
||||
echo "::endgroup::"
|
||||
}
|
||||
|
||||
function mod_build() {
|
||||
echo "::group::Generate debian packages"
|
||||
./autogen.sh
|
||||
./configure --enable-debug --enable-debuginfo --enable-asan --enable-ubsan
|
||||
make --no-print-directory --silent native-deb-utils native-deb-kmod
|
||||
mv ../*.deb .
|
||||
rm ./openzfs-zfs-dracut*.deb ./openzfs-zfs-dkms*.deb
|
||||
echo "$ImageOS-$ImageVersion" > tests/ImageOS.txt
|
||||
echo "::endgroup::"
|
||||
}
|
||||
|
||||
function mod_install() {
|
||||
# install the pre-built module only on the same runner image
|
||||
MOD=`cat tests/ImageOS.txt`
|
||||
if [ "$MOD" != "$ImageOS-$ImageVersion" ]; then
|
||||
rm -f *.deb
|
||||
mod_build
|
||||
fi
|
||||
|
||||
echo "::group::Install and load modules"
|
||||
# don't use kernel-shipped zfs modules
|
||||
sudo sed -i.bak 's/updates/extra updates/' /etc/depmod.d/ubuntu.conf
|
||||
sudo apt-get install --fix-missing ./*.deb
|
||||
|
||||
# Native Debian packages enable and start the services
|
||||
# Stop zfs-zed daemon, as it may interfere with some ZTS test cases
|
||||
sudo systemctl stop zfs-zed
|
||||
sudo depmod -a
|
||||
sudo modprobe zfs
|
||||
sudo dmesg
|
||||
sudo dmesg -c > /var/tmp/dmesg-module-load
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Report CPU information"
|
||||
lscpu
|
||||
cat /proc/spl/kstat/zfs/chksum_bench
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Optimize storage for ZFS testings"
|
||||
# remove swap and umount fast storage
|
||||
# 89GiB -> rootfs + bootfs with ~80MB/s -> don't care
|
||||
# 64GiB -> /mnt with 420MB/s -> new testing ssd
|
||||
sudo swapoff -a
|
||||
|
||||
# this one is fast and mounted @ /mnt
|
||||
# -> we reformat with ext4 + move it to /var/tmp
|
||||
DEV="/dev/disk/azure/resource-part1"
|
||||
sudo umount /mnt
|
||||
sudo mkfs.ext4 -O ^has_journal -F $DEV
|
||||
sudo mount -o noatime,barrier=0 $DEV /var/tmp
|
||||
sudo chmod 1777 /var/tmp
|
||||
|
||||
# disk usage afterwards
|
||||
sudo df -h /
|
||||
sudo df -h /var/tmp
|
||||
sudo fstrim -a
|
||||
echo "::endgroup::"
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
build)
|
||||
prerun
|
||||
mod_build
|
||||
;;
|
||||
tests)
|
||||
prerun
|
||||
mod_install
|
||||
;;
|
||||
esac
|
||||
@@ -1,24 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -eu
|
||||
|
||||
TDIR="/usr/share/zfs/zfs-tests/tests/functional"
|
||||
echo -n "TODO="
|
||||
case "$1" in
|
||||
part1)
|
||||
# ~1h 20m
|
||||
echo "cli_root"
|
||||
;;
|
||||
part2)
|
||||
# ~1h
|
||||
ls $TDIR|grep '^[a-m]'|grep -v "cli_root"|xargs|tr -s ' ' ','
|
||||
;;
|
||||
part3)
|
||||
# ~1h
|
||||
ls $TDIR|grep '^[n-qs-z]'|xargs|tr -s ' ' ','
|
||||
;;
|
||||
part4)
|
||||
# ~1h
|
||||
ls $TDIR|grep '^r'|xargs|tr -s ' ' ','
|
||||
;;
|
||||
esac
|
||||
@@ -1,124 +0,0 @@
|
||||
name: zfs-linux-tests
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
os:
|
||||
description: 'The ubuntu version: 20.02 or 22.04'
|
||||
required: true
|
||||
type: string
|
||||
|
||||
jobs:
|
||||
|
||||
zloop:
|
||||
runs-on: ubuntu-${{ inputs.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: modules-${{ inputs.os }}
|
||||
- name: Install modules
|
||||
run: |
|
||||
tar xzf modules-${{ inputs.os }}.tgz
|
||||
.github/workflows/scripts/setup-dependencies.sh tests
|
||||
- name: Tests
|
||||
timeout-minutes: 30
|
||||
run: |
|
||||
sudo mkdir -p /var/tmp/zloop
|
||||
# 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 /var/tmp/zloop/
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: Zpool-logs-${{ inputs.os }}
|
||||
path: |
|
||||
/var/tmp/zloop/*/
|
||||
!/var/tmp/zloop/*/vdev/
|
||||
retention-days: 14
|
||||
if-no-files-found: ignore
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: failure()
|
||||
with:
|
||||
name: Zpool-files-${{ inputs.os }}
|
||||
path: |
|
||||
/var/tmp/zloop/*/vdev/
|
||||
retention-days: 14
|
||||
if-no-files-found: ignore
|
||||
|
||||
sanity:
|
||||
runs-on: ubuntu-${{ inputs.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: modules-${{ inputs.os }}
|
||||
- name: Install modules
|
||||
run: |
|
||||
tar xzf modules-${{ inputs.os }}.tgz
|
||||
.github/workflows/scripts/setup-dependencies.sh tests
|
||||
- name: Tests
|
||||
timeout-minutes: 60
|
||||
shell: bash
|
||||
run: |
|
||||
set -o pipefail
|
||||
/usr/share/zfs/zfs-tests.sh -vKR -s 3G -r sanity | scripts/zfs-tests-color.sh
|
||||
- name: Prepare artifacts
|
||||
if: success() || failure()
|
||||
run: |
|
||||
RESPATH="/var/tmp/test_results"
|
||||
mv -f $RESPATH/current $RESPATH/testfiles
|
||||
tar cf $RESPATH/sanity.tar -h -C $RESPATH testfiles
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: success() || failure()
|
||||
with:
|
||||
name: Logs-${{ inputs.os }}-sanity
|
||||
path: /var/tmp/test_results/sanity.tar
|
||||
if-no-files-found: ignore
|
||||
|
||||
functional:
|
||||
runs-on: ubuntu-${{ inputs.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
tests: [ part1, part2, part3, part4 ]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- uses: actions/download-artifact@v4
|
||||
with:
|
||||
name: modules-${{ inputs.os }}
|
||||
- name: Install modules
|
||||
run: |
|
||||
tar xzf modules-${{ inputs.os }}.tgz
|
||||
.github/workflows/scripts/setup-dependencies.sh tests
|
||||
- name: Setup tests
|
||||
run: |
|
||||
.github/workflows/scripts/setup-functional.sh ${{ matrix.tests }} >> $GITHUB_ENV
|
||||
- name: Tests
|
||||
timeout-minutes: 120
|
||||
shell: bash
|
||||
run: |
|
||||
set -o pipefail
|
||||
/usr/share/zfs/zfs-tests.sh -vKR -s 3G -T ${{ env.TODO }} | scripts/zfs-tests-color.sh
|
||||
- name: Prepare artifacts
|
||||
if: success() || failure()
|
||||
run: |
|
||||
RESPATH="/var/tmp/test_results"
|
||||
mv -f $RESPATH/current $RESPATH/testfiles
|
||||
tar cf $RESPATH/${{ matrix.tests }}.tar -h -C $RESPATH testfiles
|
||||
- uses: actions/upload-artifact@v4
|
||||
if: success() || failure()
|
||||
with:
|
||||
name: Logs-${{ inputs.os }}-functional-${{ matrix.tests }}
|
||||
path: /var/tmp/test_results/${{ matrix.tests }}.tar
|
||||
if-no-files-found: ignore
|
||||
@@ -1,64 +0,0 @@
|
||||
name: zfs-linux
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
|
||||
build:
|
||||
name: Build
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [20.04, 22.04]
|
||||
runs-on: ubuntu-${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }}
|
||||
- name: Build modules
|
||||
run: .github/workflows/scripts/setup-dependencies.sh build
|
||||
- name: Prepare modules upload
|
||||
run: tar czf modules-${{ matrix.os }}.tgz *.deb .github tests/test-runner tests/ImageOS.txt
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: modules-${{ matrix.os }}
|
||||
path: modules-${{ matrix.os }}.tgz
|
||||
retention-days: 14
|
||||
|
||||
testings:
|
||||
name: Testing
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [20.04, 22.04]
|
||||
needs: build
|
||||
uses: ./.github/workflows/zfs-linux-tests.yml
|
||||
with:
|
||||
os: ${{ matrix.os }}
|
||||
|
||||
cleanup:
|
||||
if: always()
|
||||
name: Cleanup
|
||||
runs-on: ubuntu-22.04
|
||||
needs: testings
|
||||
steps:
|
||||
- uses: actions/download-artifact@v4
|
||||
- name: Generating summary
|
||||
run: |
|
||||
tar xzf modules-22.04/modules-22.04.tgz .github tests
|
||||
.github/workflows/scripts/generate-summary.sh
|
||||
# up to 4 steps, each can have 1 MiB output (for debugging log files)
|
||||
- name: Summary for errors #1
|
||||
run: .github/workflows/scripts/generate-summary.sh 1
|
||||
- name: Summary for errors #2
|
||||
run: .github/workflows/scripts/generate-summary.sh 2
|
||||
- name: Summary for errors #3
|
||||
run: .github/workflows/scripts/generate-summary.sh 3
|
||||
- name: Summary for errors #4
|
||||
run: .github/workflows/scripts/generate-summary.sh 4
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: Summary Files
|
||||
path: Summary/
|
||||
+31
-60
@@ -1,7 +1,6 @@
|
||||
#
|
||||
# This is the top-level .gitignore file:
|
||||
# ignore everything except a list of allowed files.
|
||||
#
|
||||
# N.B.
|
||||
# This is the toplevel .gitignore file.
|
||||
# This is not the place for entries that are specific to
|
||||
# a subdirectory. Instead add those files to the
|
||||
# .gitignore file in that subdirectory.
|
||||
@@ -11,62 +10,10 @@
|
||||
# command after changing this file, to see if there are
|
||||
# any tracked files which get ignored after the change.
|
||||
|
||||
*
|
||||
|
||||
!.github
|
||||
!cmd
|
||||
!config
|
||||
!contrib
|
||||
!etc
|
||||
!include
|
||||
!lib
|
||||
!man
|
||||
!module
|
||||
!rpm
|
||||
!scripts
|
||||
!tests
|
||||
!udev
|
||||
|
||||
!.github/**
|
||||
!cmd/**
|
||||
!config/**
|
||||
!contrib/**
|
||||
!etc/**
|
||||
!include/**
|
||||
!lib/**
|
||||
!man/**
|
||||
!module/**
|
||||
!rpm/**
|
||||
!scripts/**
|
||||
!tests/**
|
||||
!udev/**
|
||||
|
||||
!.editorconfig
|
||||
!.cirrus.yml
|
||||
!.gitignore
|
||||
!.gitmodules
|
||||
!.mailmap
|
||||
!AUTHORS
|
||||
!autogen.sh
|
||||
!CODE_OF_CONDUCT.md
|
||||
!configure.ac
|
||||
!copy-builtin
|
||||
!COPYRIGHT
|
||||
!LICENSE
|
||||
!Makefile.am
|
||||
!META
|
||||
!NEWS
|
||||
!NOTICE
|
||||
!README.md
|
||||
!RELEASES.md
|
||||
!TEST
|
||||
!zfs.release.in
|
||||
|
||||
#
|
||||
# Normal rules
|
||||
#
|
||||
*.[oa]
|
||||
*.o.ur-safe
|
||||
*.lo
|
||||
*.la
|
||||
*.mod.c
|
||||
@@ -74,8 +21,6 @@
|
||||
*.swp
|
||||
*.gcno
|
||||
*.gcda
|
||||
*.pyc
|
||||
*.pyo
|
||||
.deps
|
||||
.libs
|
||||
.dirstamp
|
||||
@@ -83,8 +28,34 @@
|
||||
modules.order
|
||||
Makefile
|
||||
Makefile.in
|
||||
changelog
|
||||
|
||||
#
|
||||
# Top level generated files specific to this top level dir
|
||||
#
|
||||
/bin
|
||||
/configure
|
||||
/config.log
|
||||
/config.status
|
||||
/libtool
|
||||
/zfs_config.h
|
||||
/zfs_config.h.in
|
||||
/zfs.release
|
||||
/stamp-h1
|
||||
/.script-config
|
||||
/zfs-script-config.sh
|
||||
/aclocal.m4
|
||||
/autom4te.cache
|
||||
|
||||
#
|
||||
# Top level generic files
|
||||
#
|
||||
!.gitignore
|
||||
tags
|
||||
TAGS
|
||||
current
|
||||
cscope.*
|
||||
*.rpm
|
||||
*.deb
|
||||
*.tar.gz
|
||||
*.patch
|
||||
*.orig
|
||||
*.tmp
|
||||
*.log
|
||||
|
||||
+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,207 +0,0 @@
|
||||
# This file maps the name+email seen in a commit back to a canonical
|
||||
# name+email. Git will replace the commit name/email with the canonical version
|
||||
# wherever it sees it.
|
||||
#
|
||||
# If there is a commit in the history with a "wrong" name or email, list it
|
||||
# here. If you regularly commit with an alternate name or email address and
|
||||
# would like to ensure that you are always listed consistently in the repo, add
|
||||
# mapping here.
|
||||
#
|
||||
# On the other hand, if you use multiple names or email addresses legitimately
|
||||
# (eg you use a company email address for your paid OpenZFS work, and a
|
||||
# personal address for your evening side projects), then don't map one to the
|
||||
# other here.
|
||||
#
|
||||
# The most common formats are:
|
||||
#
|
||||
# Canonical Name <canonical-email>
|
||||
# Canonical Name <canonical-email> <commit-email>
|
||||
# Canonical Name <canonical-email> Commit Name <commit-email>
|
||||
#
|
||||
# See https://git-scm.com/docs/gitmailmap for more info.
|
||||
|
||||
# These maps are making names consistent where they have varied but the email
|
||||
# address has never changed. In most cases, the full name is in the
|
||||
# Signed-off-by of a commit with a matching author.
|
||||
Ahelenia Ziemiańska <nabijaczleweli@gmail.com>
|
||||
Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>
|
||||
Alex John <alex@stty.io>
|
||||
Andreas Dilger <adilger@dilger.ca>
|
||||
Andrew Walker <awalker@ixsystems.com>
|
||||
Benedikt Neuffer <github@itfriend.de>
|
||||
Chengfei Zhu <chengfeix.zhu@intel.com>
|
||||
ChenHao Lu <18302010006@fudan.edu.cn>
|
||||
Chris Lindee <chris.lindee+github@gmail.com>
|
||||
Colm Buckley <colm@tuatha.org>
|
||||
Crag Wang <crag0715@gmail.com>
|
||||
Damian Szuberski <szuberskidamian@gmail.com>
|
||||
Daniel Kolesa <daniel@octaforge.org>
|
||||
Debabrata Banerjee <dbavatar@gmail.com>
|
||||
Finix Yan <yanchongwen@hotmail.com>
|
||||
Gaurav Kumar <gauravk.18@gmail.com>
|
||||
Gionatan Danti <g.danti@assyoma.it>
|
||||
Glenn Washburn <development@efficientek.com>
|
||||
Gordan Bobic <gordan.bobic@gmail.com>
|
||||
Gregory Bartholomew <gregory.lee.bartholomew@gmail.com>
|
||||
hedong zhang <h_d_zhang@163.com>
|
||||
Ilkka Sovanto <github@ilkka.kapsi.fi>
|
||||
InsanePrawn <Insane.Prawny@gmail.com>
|
||||
Jason Cohen <jwittlincohen@gmail.com>
|
||||
Jason Harmening <jason.harmening@gmail.com>
|
||||
Jeremy Faulkner <gldisater@gmail.com>
|
||||
Jinshan Xiong <jinshan.xiong@gmail.com>
|
||||
John Poduska <jpoduska@datto.com>
|
||||
Justin Scholz <git@justinscholz.de>
|
||||
Ka Ho Ng <khng300@gmail.com>
|
||||
Kash Pande <github@tripleback.net>
|
||||
Kay Pedersen <christianpe96@gmail.com>
|
||||
KernelOfTruth <kerneloftruth@gmail.com>
|
||||
Liu Hua <liu.hua130@zte.com.cn>
|
||||
Liu Qing <winglq@gmail.com>
|
||||
loli10K <ezomori.nozomu@gmail.com>
|
||||
Mart Frauenlob <allkind@fastest.cc>
|
||||
Matthias Blankertz <matthias@blankertz.org>
|
||||
Michael Gmelin <grembo@FreeBSD.org>
|
||||
Olivier Mazouffre <olivier.mazouffre@ims-bordeaux.fr>
|
||||
Piotr Kubaj <pkubaj@anongoth.pl>
|
||||
Quentin Zdanis <zdanisq@gmail.com>
|
||||
Roberto Ricci <ricci@disroot.org>
|
||||
Rob Norris <robn@despairlabs.com>
|
||||
Rob Norris <rob.norris@klarasystems.com>
|
||||
Sam Lunt <samuel.j.lunt@gmail.com>
|
||||
Sanjeev Bagewadi <sanjeev.bagewadi@gmail.com>
|
||||
Stoiko Ivanov <github@nomore.at>
|
||||
Tamas TEVESZ <ice@extreme.hu>
|
||||
WHR <msl0000023508@gmail.com>
|
||||
Yanping Gao <yanping.gao@xtaotech.com>
|
||||
Youzhong Yang <youzhong@gmail.com>
|
||||
|
||||
# Signed-off-by: overriding Author:
|
||||
Yuxin Wang <yuxinwang9999@gmail.com> <Bi11gates9999@gmail.com>
|
||||
|
||||
# Commits from strange places, long ago
|
||||
Brian Behlendorf <behlendorf1@llnl.gov> <behlendo@7e1ea52c-4ff2-0310-8f11-9dd32ca42a1c>
|
||||
Brian Behlendorf <behlendorf1@llnl.gov> <behlendo@fedora-17-amd64.(none)>
|
||||
Brian Behlendorf <behlendorf1@llnl.gov> <behlendo@myhost.(none)>
|
||||
Brian Behlendorf <behlendorf1@llnl.gov> <ubuntu@ip-172-31-16-145.us-west-1.compute.internal>
|
||||
Brian Behlendorf <behlendorf1@llnl.gov> <ubuntu@ip-172-31-20-6.us-west-1.compute.internal>
|
||||
Herb Wartens <wartens2@llnl.gov> <wartens2@7e1ea52c-4ff2-0310-8f11-9dd32ca42a1c>
|
||||
Ned Bass <bass6@llnl.gov> <bass6@zeno1.(none)>
|
||||
Tulsi Jain <tulsi.jain@delphix.com> <tulsi.jain@Tulsi-Jains-MacBook-Pro.local>
|
||||
|
||||
# Mappings from Github no-reply addresses
|
||||
ajs124 <git@ajs124.de> <ajs124@users.noreply.github.com>
|
||||
Alek Pinchuk <apinchuk@axcient.com> <alek-p@users.noreply.github.com>
|
||||
Alexander Lobakin <alobakin@pm.me> <solbjorn@users.noreply.github.com>
|
||||
Alexey Smirnoff <fling@member.fsf.org> <fling-@users.noreply.github.com>
|
||||
Allen Holl <allen.m.holl@gmail.com> <65494904+allen-4@users.noreply.github.com>
|
||||
Ameer Hamza <ahamza@ixsystems.com> <106930537+ixhamza@users.noreply.github.com>
|
||||
Andrew J. Hesford <ajh@sideband.org> <48421688+ahesford@users.noreply.github.com>>
|
||||
Andrew Sun <me@andrewsun.com> <as-com@users.noreply.github.com>
|
||||
Aron Xu <happyaron.xu@gmail.com> <happyaron@users.noreply.github.com>
|
||||
Arun KV <arun.kv@datacore.com> <65647132+arun-kv@users.noreply.github.com>
|
||||
Ben Wolsieffer <benwolsieffer@gmail.com> <lopsided98@users.noreply.github.com>
|
||||
bernie1995 <bernie.pikes@gmail.com> <42413912+bernie1995@users.noreply.github.com>
|
||||
Boris Protopopov <boris.protopopov@actifio.com> <bprotopopov@users.noreply.github.com>
|
||||
Brad Forschinger <github@bnjf.id.au> <bnjf@users.noreply.github.com>
|
||||
Brandon Thetford <brandon@dodecatec.com> <dodexahedron@users.noreply.github.com>
|
||||
buzzingwires <buzzingwires@outlook.com> <131118055+buzzingwires@users.noreply.github.com>
|
||||
Cedric Maunoury <cedric.maunoury@gmail.com> <38213715+cedricmaunoury@users.noreply.github.com>
|
||||
Charles Suh <charles.suh@gmail.com> <charlessuh@users.noreply.github.com>
|
||||
Chris Peredun <chris.peredun@ixsystems.com> <126915832+chrisperedun@users.noreply.github.com>
|
||||
Dacian Reece-Stremtan <dacianstremtan@gmail.com> <35844628+dacianstremtan@users.noreply.github.com>
|
||||
Damian Szuberski <szuberskidamian@gmail.com> <30863496+szubersk@users.noreply.github.com>
|
||||
Daniel Hiepler <d-git@coderdu.de> <32984777+heeplr@users.noreply.github.com>
|
||||
Daniel Kobras <d.kobras@science-computing.de> <sckobras@users.noreply.github.com>
|
||||
Daniel Reichelt <hacking@nachtgeist.net> <nachtgeist@users.noreply.github.com>
|
||||
David Quigley <david.quigley@intel.com> <dpquigl@users.noreply.github.com>
|
||||
Dennis R. Friedrichsen <dennis.r.friedrichsen@gmail.com> <31087738+dennisfriedrichsen@users.noreply.github.com>
|
||||
Dex Wood <slash2314@gmail.com> <slash2314@users.noreply.github.com>
|
||||
DHE <git@dehacked.net> <DeHackEd@users.noreply.github.com>
|
||||
Dmitri John Ledkov <dimitri.ledkov@canonical.com> <19779+xnox@users.noreply.github.com>
|
||||
Dries Michiels <driesm.michiels@gmail.com> <32487486+driesmp@users.noreply.github.com>
|
||||
Edmund Nadolski <edmund.nadolski@ixsystems.com> <137826107+ednadolski-ix@users.noreply.github.com>
|
||||
Érico Nogueira <erico.erc@gmail.com> <34201958+ericonr@users.noreply.github.com>
|
||||
Fedor Uporov <fuporov.vstack@gmail.com> <60701163+fuporovvStack@users.noreply.github.com>
|
||||
Felix Dörre <felix@dogcraft.de> <felixdoerre@users.noreply.github.com>
|
||||
Felix Neumärker <xdch47@posteo.de> <34678034+xdch47@users.noreply.github.com>
|
||||
Finix Yan <yancw@info2soft.com> <Finix1979@users.noreply.github.com>
|
||||
Gaurav Kumar <gauravk.18@gmail.com> <gaurkuma@users.noreply.github.com>
|
||||
George Gaydarov <git@gg7.io> <gg7@users.noreply.github.com>
|
||||
Georgy Yakovlev <gyakovlev@gentoo.org> <168902+gyakovlev@users.noreply.github.com>
|
||||
Gerardwx <gerardw@alum.mit.edu> <Gerardwx@users.noreply.github.com>
|
||||
Gian-Carlo DeFazio <defazio1@llnl.gov> <defaziogiancarlo@users.noreply.github.com>
|
||||
Giuseppe Di Natale <dinatale2@llnl.gov> <dinatale2@users.noreply.github.com>
|
||||
Hajo Möller <dasjoe@gmail.com> <dasjoe@users.noreply.github.com>
|
||||
Harry Mallon <hjmallon@gmail.com> <1816667+hjmallon@users.noreply.github.com>
|
||||
Hiếu Lê <leorize+oss@disroot.org> <alaviss@users.noreply.github.com>
|
||||
Jake Howard <git@theorangeone.net> <RealOrangeOne@users.noreply.github.com>
|
||||
James Cowgill <james.cowgill@mips.com> <jcowgill@users.noreply.github.com>
|
||||
Jaron Kent-Dobias <jaron@kent-dobias.com> <kentdobias@users.noreply.github.com>
|
||||
Jason King <jason.king@joyent.com> <jasonbking@users.noreply.github.com>
|
||||
Jeff Dike <jdike@akamai.com> <52420226+jdike@users.noreply.github.com>
|
||||
Jitendra Patidar <jitendra.patidar@nutanix.com> <53164267+jsai20@users.noreply.github.com>
|
||||
João Carlos Mendes Luís <jonny@jonny.eng.br> <dioni21@users.noreply.github.com>
|
||||
John Eismeier <john.eismeier@gmail.com> <32205350+jeis2497052@users.noreply.github.com>
|
||||
John L. Hammond <john.hammond@intel.com> <35266395+jhammond-intel@users.noreply.github.com>
|
||||
John-Mark Gurney <jmg@funkthat.com> <jmgurney@users.noreply.github.com>
|
||||
John Ramsden <johnramsden@riseup.net> <johnramsden@users.noreply.github.com>
|
||||
Jonathon Fernyhough <jonathon@m2x.dev> <559369+jonathonf@users.noreply.github.com>
|
||||
Jose Luis Duran <jlduran@gmail.com> <jlduran@users.noreply.github.com>
|
||||
Justin Hibbits <chmeeedalf@gmail.com> <chmeeedalf@users.noreply.github.com>
|
||||
Kevin Greene <kevin.greene@delphix.com> <104801862+kxgreene@users.noreply.github.com>
|
||||
Kevin Jin <lostking2008@hotmail.com> <33590050+jxdking@users.noreply.github.com>
|
||||
Kevin P. Fleming <kevin@km6g.us> <kpfleming@users.noreply.github.com>
|
||||
Krzysztof Piecuch <piecuch@kpiecuch.pl> <3964215+pikrzysztof@users.noreply.github.com>
|
||||
Kyle Evans <kevans@FreeBSD.org> <kevans91@users.noreply.github.com>
|
||||
Laurențiu Nicola <lnicola@dend.ro> <lnicola@users.noreply.github.com>
|
||||
loli10K <ezomori.nozomu@gmail.com> <loli10K@users.noreply.github.com>
|
||||
Lorenz Hüdepohl <dev@stellardeath.org> <lhuedepohl@users.noreply.github.com>
|
||||
Luís Henriques <henrix@camandro.org> <73643340+lumigch@users.noreply.github.com>
|
||||
Marcin Skarbek <git@skarbek.name> <mskarbek@users.noreply.github.com>
|
||||
Matt Fiddaman <github@m.fiddaman.uk> <81489167+matt-fidd@users.noreply.github.com>
|
||||
Maxim Filimonov <che@bein.link> <part1zano@users.noreply.github.com>
|
||||
Max Zettlmeißl <max@zettlmeissl.de> <6818198+maxz@users.noreply.github.com>
|
||||
Michael Niewöhner <foss@mniewoehner.de> <c0d3z3r0@users.noreply.github.com>
|
||||
Michael Zhivich <mzhivich@akamai.com> <33133421+mzhivich@users.noreply.github.com>
|
||||
MigeljanImeri <ImeriMigel@gmail.com> <78048439+MigeljanImeri@users.noreply.github.com>
|
||||
Mo Zhou <cdluminate@gmail.com> <5723047+cdluminate@users.noreply.github.com>
|
||||
Nick Mattis <nickm970@gmail.com> <nmattis@users.noreply.github.com>
|
||||
omni <omni+vagant@hack.org> <79493359+omnivagant@users.noreply.github.com>
|
||||
Pablo Correa Gómez <ablocorrea@hotmail.com> <32678034+pablofsf@users.noreply.github.com>
|
||||
Paul Zuchowski <pzuchowski@datto.com> <31706010+PaulZ-98@users.noreply.github.com>
|
||||
Peter Ashford <ashford@accs.com> <pashford@users.noreply.github.com>
|
||||
Peter Dave Hello <hsu@peterdavehello.org> <PeterDaveHello@users.noreply.github.com>
|
||||
Peter Wirdemo <peter.wirdemo@gmail.com> <4224155+pewo@users.noreply.github.com>
|
||||
Petros Koutoupis <petros@petroskoutoupis.com> <pkoutoupis@users.noreply.github.com>
|
||||
Ping Huang <huangping@smartx.com> <101400146+hpingfs@users.noreply.github.com>
|
||||
Piotr P. Stefaniak <pstef@freebsd.org> <pstef@users.noreply.github.com>
|
||||
Richard Allen <belperite@gmail.com> <33836503+belperite@users.noreply.github.com>
|
||||
Rich Ercolani <rincebrain@gmail.com> <214141+rincebrain@users.noreply.github.com>
|
||||
Rick Macklem <rmacklem@uoguelph.ca> <64620010+rmacklem@users.noreply.github.com>
|
||||
Rob Wing <rob.wing@klarasystems.com> <98866084+rob-wing@users.noreply.github.com>
|
||||
Roman Strashkin <roman.strashkin@nexenta.com> <Ramzec@users.noreply.github.com>
|
||||
Ryan Hirasaki <ryanhirasaki@gmail.com> <4690732+RyanHir@users.noreply.github.com>
|
||||
Samuel Wycliffe J <samwyc@hpe.com> <115969550+samwyc@users.noreply.github.com>
|
||||
Samuel Wycliffe <samuelwycliffe@gmail.com> <50765275+npc203@users.noreply.github.com>
|
||||
Savyasachee Jha <hi@savyasacheejha.com> <savyajha@users.noreply.github.com>
|
||||
Scott Colby <scott@scolby.com> <scolby33@users.noreply.github.com>
|
||||
Sean Eric Fagan <kithrup@mac.com> <kithrup@users.noreply.github.com>
|
||||
Spencer Kinny <spencerkinny1995@gmail.com> <30333052+Spencer-Kinny@users.noreply.github.com>
|
||||
Srikanth N S <srikanth.nagasubbaraoseetharaman@hpe.com> <75025422+nssrikanth@users.noreply.github.com>
|
||||
Stefan Lendl <s.lendl@proxmox.com> <1321542+stfl@users.noreply.github.com>
|
||||
Thomas Bertschinger <bertschinger@lanl.gov> <101425190+bertschinger@users.noreply.github.com>
|
||||
Thomas Geppert <geppi@digitx.de> <geppi@users.noreply.github.com>
|
||||
Tim Crawford <tcrawford@datto.com> <crawfxrd@users.noreply.github.com>
|
||||
Tom Matthews <tom@axiom-partners.com> <tomtastic@users.noreply.github.com>
|
||||
Tony Perkins <tperkins@datto.com> <62951051+tony-zfs@users.noreply.github.com>
|
||||
Torsten Wörtwein <twoertwein@gmail.com> <twoertwein@users.noreply.github.com>
|
||||
Tulsi Jain <tulsi.jain@delphix.com> <TulsiJain@users.noreply.github.com>
|
||||
Václav Skála <skala@vshosting.cz> <33496485+vaclavskala@users.noreply.github.com>
|
||||
Vaibhav Bhanawat <vaibhav.bhanawat@delphix.com> <88050553+vaibhav-delphix@users.noreply.github.com>
|
||||
Violet Purcell <vimproved@inventati.org> <66446404+vimproved@users.noreply.github.com>
|
||||
Vipin Kumar Verma <vipin.verma@hpe.com> <75025470+vermavipinkumar@users.noreply.github.com>
|
||||
Wolfgang Bumiller <w.bumiller@proxmox.com> <Blub@users.noreply.github.com>
|
||||
xtouqh <xtouqh@hotmail.com> <72357159+xtouqh@users.noreply.github.com>
|
||||
Yuri Pankov <yuripv@FreeBSD.org> <113725409+yuripv@users.noreply.github.com>
|
||||
Yuri Pankov <yuripv@FreeBSD.org> <82001006+yuripv@users.noreply.github.com>
|
||||
@@ -1,659 +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 D. Moss <c@yotes.com>
|
||||
Adam Leventhal <ahl@delphix.com>
|
||||
Adam Stevko <adam.stevko@gmail.com>
|
||||
adisbladis <adis@blad.is>
|
||||
Adrian Chadd <adrian@freebsd.org>
|
||||
Ahelenia Ziemiańska <nabijaczleweli@nabijaczleweli.xyz>
|
||||
Ahmed G <ahmedg@delphix.com>
|
||||
Aidan Harris <me@aidanharr.is>
|
||||
AJ Jordan <alex@strugee.net>
|
||||
ajs124 <git@ajs124.de>
|
||||
Akash Ayare <aayare@delphix.com>
|
||||
Akash B <akash-b@hpe.com>
|
||||
Alan Somers <asomers@gmail.com>
|
||||
Alar Aun <spamtoaun@gmail.com>
|
||||
Albert Lee <trisk@nexenta.com>
|
||||
Alec Salazar <alec.j.salazar@gmail.com>
|
||||
Alejandro Colomar <Colomar.6.4.3@GMail.com>
|
||||
Alejandro R. Sedeño <asedeno@mit.edu>
|
||||
Alek Pinchuk <alek@nexenta.com>
|
||||
Aleksa Sarai <cyphar@cyphar.com>
|
||||
Alexander Eremin <a.eremin@nexenta.com>
|
||||
Alexander Lobakin <alobakin@pm.me>
|
||||
Alexander Motin <mav@freebsd.org>
|
||||
Alexander Pyhalov <apyhalov@gmail.com>
|
||||
Alexander Richardson <Alexander.Richardson@cl.cam.ac.uk>
|
||||
Alexander Stetsenko <ams@nexenta.com>
|
||||
Alex Braunegg <alex.braunegg@gmail.com>
|
||||
Alexey Shvetsov <alexxy@gentoo.org>
|
||||
Alexey Smirnoff <fling@member.fsf.org>
|
||||
Alex John <alex@stty.io>
|
||||
Alex McWhirter <alexmcwhirter@triadic.us>
|
||||
Alex Reece <alex@delphix.com>
|
||||
Alex Wilson <alex.wilson@joyent.com>
|
||||
Alex Zhuravlev <alexey.zhuravlev@intel.com>
|
||||
Allan Jude <allanjude@freebsd.org>
|
||||
Allen Holl <allen.m.holl@gmail.com>
|
||||
alteriks <alteriks@gmail.com>
|
||||
Alyssa Ross <hi@alyssa.is>
|
||||
Ameer Hamza <ahamza@ixsystems.com>
|
||||
Anatoly Borodin <anatoly.borodin@gmail.com>
|
||||
AndCycle <andcycle@andcycle.idv.tw>
|
||||
Andrea Gelmini <andrea.gelmini@gelma.net>
|
||||
Andrea Righi <andrea.righi@canonical.com>
|
||||
Andreas Buschmann <andreas.buschmann@tech.net.de>
|
||||
Andreas Dilger <adilger@intel.com>
|
||||
Andreas Vögele <andreas@andreasvoegele.com>
|
||||
Andrew Barnes <barnes333@gmail.com>
|
||||
Andrew Hamilton <ahamilto@tjhsst.edu>
|
||||
Andrew Innes <andrew.c12@gmail.com>
|
||||
Andrew J. Hesford <ajh@sideband.org>
|
||||
Andrew Reid <ColdCanuck@nailedtotheperch.com>
|
||||
Andrew Stormont <andrew.stormont@nexenta.com>
|
||||
Andrew Sun <me@andrewsun.com>
|
||||
Andrew Tselischev <andrewtselischev@gmail.com>
|
||||
Andrew Turner <andrew@fubar.geek.nz>
|
||||
Andrew Walker <awalker@ixsystems.com>
|
||||
Andrey Prokopenko <job@terem.fr>
|
||||
Andrey Vesnovaty <andrey.vesnovaty@gmail.com>
|
||||
Andriy Gapon <avg@freebsd.org>
|
||||
Andy Bakun <github@thwartedefforts.org>
|
||||
Andy Fiddaman <omnios@citrus-it.co.uk>
|
||||
Aniruddha Shankar <k@191a.net>
|
||||
Anton Gubarkov <anton.gubarkov@gmail.com>
|
||||
Antonio Russo <antonio.e.russo@gmail.com>
|
||||
Arkadiusz Bubała <arkadiusz.bubala@open-e.com>
|
||||
Armin Wehrfritz <dkxls23@gmail.com>
|
||||
Arne Jansen <arne@die-jansens.de>
|
||||
Aron Xu <happyaron.xu@gmail.com>
|
||||
Arshad Hussain <arshad.hussain@aeoncomputing.com>
|
||||
Arun KV <arun.kv@datacore.com>
|
||||
Arvind Sankar <nivedita@alum.mit.edu>
|
||||
Attila Fülöp <attila@fueloep.org>
|
||||
Avatat <kontakt@avatat.pl>
|
||||
Bart Coddens <bart.coddens@gmail.com>
|
||||
Basil Crow <basil.crow@delphix.com>
|
||||
Bassu <bassu@phi9.com>
|
||||
Ben Allen <bsallen@alcf.anl.gov>
|
||||
Ben Cordero <bencord0@condi.me>
|
||||
Benda Xu <orv@debian.org>
|
||||
Benedikt Neuffer <github@itfriend.de>
|
||||
Benjamin Albrecht <git@albrecht.io>
|
||||
Benjamin Gentil <benjgentil.pro@gmail.com>
|
||||
Benjamin Sherman <benjamin@holyarmy.org>
|
||||
Ben McGough <bmcgough@fredhutch.org>
|
||||
Ben Rubson <ben.rubson@gmail.com>
|
||||
Ben Wolsieffer <benwolsieffer@gmail.com>
|
||||
bernie1995 <bernie.pikes@gmail.com>
|
||||
Bill McGonigle <bill-github.com-public1@bfccomputing.com>
|
||||
Bill Pijewski <wdp@joyent.com>
|
||||
Boris Protopopov <boris.protopopov@nexenta.com>
|
||||
Brad Forschinger <github@bnjf.id.au>
|
||||
Brad Lewis <brad.lewis@delphix.com>
|
||||
Brandon Thetford <brandon@dodecatec.com>
|
||||
Brian Atkinson <bwa@g.clemson.edu>
|
||||
Brian Behlendorf <behlendorf1@llnl.gov>
|
||||
Brian J. Murrell <brian@sun.com>
|
||||
Brooks Davis <brooks@one-eyed-alien.net>
|
||||
BtbN <btbn@btbn.de>
|
||||
bunder2015 <omfgbunder@gmail.com>
|
||||
buzzingwires <buzzingwires@outlook.com>
|
||||
bzzz77 <bzzz.tomas@gmail.com>
|
||||
cable2999 <cable2999@users.noreply.github.com>
|
||||
Caleb James DeLisle <calebdelisle@lavabit.com>
|
||||
Cameron Harr <harr1@llnl.gov>
|
||||
Cao Xuewen <cao.xuewen@zte.com.cn>
|
||||
Carlo Landmeter <clandmeter@gmail.com>
|
||||
Carlos Alberto Lopez Perez <clopez@igalia.com>
|
||||
Cedric Maunoury <cedric.maunoury@gmail.com>
|
||||
Chaoyu Zhang <zhang.chaoyu@zte.com.cn>
|
||||
Charles Suh <charles.suh@gmail.com>
|
||||
Chen Can <chen.can2@zte.com.cn>
|
||||
Chengfei Zhu <chengfeix.zhu@intel.com>
|
||||
Chen Haiquan <oc@yunify.com>
|
||||
ChenHao Lu <18302010006@fudan.edu.cn>
|
||||
Chip Parker <aparker@enthought.com>
|
||||
Chris Burroughs <chris.burroughs@gmail.com>
|
||||
Chris Davidson <christopher.davidson@gmail.com>
|
||||
Chris Dunlap <cdunlap@llnl.gov>
|
||||
Chris Dunlop <chris@onthe.net.au>
|
||||
Chris Lindee <chris.lindee+github@gmail.com>
|
||||
Chris McDonough <chrism@plope.com>
|
||||
Chris Peredun <chris.peredun@ixsystems.com>
|
||||
Chris Siden <chris.siden@delphix.com>
|
||||
Chris Siebenmann <cks.github@cs.toronto.edu>
|
||||
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>
|
||||
Christ Schlacta <aarcane@aarcane.info>
|
||||
Chris Wedgwood <cw@f00f.org>
|
||||
Chris Williamson <chris.williamson@delphix.com>
|
||||
Chris Zubrzycki <github@mid-earth.net>
|
||||
Chuck Tuffli <ctuffli@gmail.com>
|
||||
Chunwei Chen <david.chen@nutanix.com>
|
||||
Clemens Fruhwirth <clemens@endorphin.org>
|
||||
Clemens Lang <cl@clang.name>
|
||||
Clint Armstrong <clint@clintarmstrong.net>
|
||||
Coleman Kane <ckane@colemankane.org>
|
||||
Colin Ian King <colin.king@canonical.com>
|
||||
Colin Percival <cperciva@tarsnap.com>
|
||||
Colm Buckley <colm@tuatha.org>
|
||||
Crag Wang <crag0715@gmail.com>
|
||||
Craig Loomis <cloomis@astro.princeton.edu>
|
||||
Craig Sanders <github@taz.net.au>
|
||||
Cyril Plisko <cyril.plisko@infinidat.com>
|
||||
Cy Schubert <cy@FreeBSD.org>
|
||||
Cédric Berger <cedric@precidata.com>
|
||||
Dacian Reece-Stremtan <dacianstremtan@gmail.com>
|
||||
Dag-Erling Smørgrav <des@FreeBSD.org>
|
||||
Damiano Albani <damiano.albani@gmail.com>
|
||||
Damian Szuberski <szuberskidamian@gmail.com>
|
||||
Damian Wojsław <damian@wojslaw.pl>
|
||||
Daniel Berlin <dberlin@dberlin.org>
|
||||
Daniel Hiepler <d-git@coderdu.de>
|
||||
Daniel Hoffman <dj.hoffman@delphix.com>
|
||||
Daniel Kobras <d.kobras@science-computing.de>
|
||||
Daniel Kolesa <daniel@octaforge.org>
|
||||
Daniel Reichelt <hacking@nachtgeist.net>
|
||||
Daniel Stevenson <bot@dstev.net>
|
||||
Daniel Verite <daniel@verite.pro>
|
||||
Daniil Lunev <d.lunev.mail@gmail.com>
|
||||
Dan Kimmel <dan.kimmel@delphix.com>
|
||||
Dan McDonald <danmcd@nexenta.com>
|
||||
Dan Swartzendruber <dswartz@druber.com>
|
||||
Dan Vatca <dan.vatca@gmail.com>
|
||||
Darik Horn <dajhorn@vanadac.com>
|
||||
Dave Eddy <dave@daveeddy.com>
|
||||
David Hedberg <david@qzx.se>
|
||||
David Lamparter <equinox@diac24.net>
|
||||
David Qian <david.qian@intel.com>
|
||||
David Quigley <david.quigley@intel.com>
|
||||
Debabrata Banerjee <dbanerje@akamai.com>
|
||||
D. Ebdrup <debdrup@freebsd.org>
|
||||
Dennis R. Friedrichsen <dennis.r.friedrichsen@gmail.com>
|
||||
Denys Rtveliashvili <denys@rtveliashvili.name>
|
||||
Derek Dai <daiderek@gmail.com>
|
||||
Dex Wood <slash2314@gmail.com>
|
||||
DHE <git@dehacked.net>
|
||||
Didier Roche <didrocks@ubuntu.com>
|
||||
Dimitri John Ledkov <xnox@ubuntu.com>
|
||||
Dimitry Andric <dimitry@andric.com>
|
||||
Dirkjan Bussink <d.bussink@gmail.com>
|
||||
Dmitry Khasanov <pik4ez@gmail.com>
|
||||
Dominic Pearson <dsp@technoanimal.net>
|
||||
Dominik Hassler <hadfl@omniosce.org>
|
||||
Dominik Honnef <dominikh@fork-bomb.org>
|
||||
Don Brady <don.brady@delphix.com>
|
||||
Doug Rabson <dfr@rabson.org>
|
||||
Dr. András Korn <korn-github.com@elan.rulez.org>
|
||||
Dries Michiels <driesm.michiels@gmail.com>
|
||||
Edmund Nadolski <edmund.nadolski@ixsystems.com>
|
||||
Eitan Adler <lists@eitanadler.com>
|
||||
Eli Rosenthal <eli.rosenthal@delphix.com>
|
||||
Eli Schwartz <eschwartz93@gmail.com>
|
||||
Eric Desrochers <eric.desrochers@canonical.com>
|
||||
Eric Dillmann <eric@jave.fr>
|
||||
Eric Schrock <Eric.Schrock@delphix.com>
|
||||
Ethan Coe-Renner <coerenner1@llnl.gov>
|
||||
Etienne Dechamps <etienne@edechamps.fr>
|
||||
Evan Allrich <eallrich@gmail.com>
|
||||
Evan Harris <eharris@puremagic.com>
|
||||
Evan Susarret <evansus@gmail.com>
|
||||
Fabian Grünbichler <f.gruenbichler@proxmox.com>
|
||||
Fabio Buso <dev.siroibaf@gmail.com>
|
||||
Fabio Scaccabarozzi <fsvm88@gmail.com>
|
||||
Fajar A. Nugraha <github@fajar.net>
|
||||
Fan Yong <fan.yong@intel.com>
|
||||
fbynite <fbynite@users.noreply.github.com>
|
||||
Fedor Uporov <fuporov.vstack@gmail.com>
|
||||
Felix Dörre <felix@dogcraft.de>
|
||||
Felix Neumärker <xdch47@posteo.de>
|
||||
Feng Sun <loyou85@gmail.com>
|
||||
Finix Yan <yancw@info2soft.com>
|
||||
Francesco Mazzoli <f@mazzo.li>
|
||||
Frederik Wessels <wessels147@gmail.com>
|
||||
Frédéric Vanniere <f.vanniere@planet-work.com>
|
||||
Gabriel A. Devenyi <gdevenyi@gmail.com>
|
||||
Garrett D'Amore <garrett@nexenta.com>
|
||||
Garrett Fields <ghfields@gmail.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 Diamantopoulos <georgediam@gmail.com>
|
||||
George Gaydarov <git@gg7.io>
|
||||
George Melikov <mail@gmelikov.ru>
|
||||
George Wilson <gwilson@delphix.com>
|
||||
Georgy Yakovlev <ya@sysdump.net>
|
||||
Gerardwx <gerardw@alum.mit.edu>
|
||||
Gian-Carlo DeFazio <defazio1@llnl.gov>
|
||||
Gionatan Danti <g.danti@assyoma.it>
|
||||
Giuseppe Di Natale <guss80@gmail.com>
|
||||
Glenn Washburn <development@efficientek.com>
|
||||
gofaster <felix.gofaster@gmail.com>
|
||||
Gordan Bobic <gordan@redsleeve.org>
|
||||
Gordon Bergling <gbergling@googlemail.com>
|
||||
Gordon Ross <gwr@nexenta.com>
|
||||
Gordon Tetlow <gordon@freebsd.org>
|
||||
Graham Christensen <graham@grahamc.com>
|
||||
Graham Perrin <grahamperrin@gmail.com>
|
||||
Gregor Kopka <gregor@kopka.net>
|
||||
Gregory Bartholomew <gregory.lee.bartholomew@gmail.com>
|
||||
grembo <freebsd@grem.de>
|
||||
Grischa Zengel <github.zfsonlinux@zengel.info>
|
||||
grodik <pat@litke.dev>
|
||||
Gunnar Beutner <gunnar@beutner.name>
|
||||
Gvozden Neskovic <neskovic@gmail.com>
|
||||
Hajo Möller <dasjoe@gmail.com>
|
||||
Han Gao <rabenda.cn@gmail.com>
|
||||
Hans Rosenfeld <hans.rosenfeld@nexenta.com>
|
||||
Harald van Dijk <harald@gigawatt.nl>
|
||||
Harry Mallon <hjmallon@gmail.com>
|
||||
Harry Sintonen <github-piru@kyber.fi>
|
||||
HC <mmttdebbcc@yahoo.com>
|
||||
hedong zhang <h_d_zhang@163.com>
|
||||
Heitor Alves de Siqueira <halves@canonical.com>
|
||||
Henrik Riomar <henrik.riomar@gmail.com>
|
||||
Herb Wartens <wartens2@llnl.gov>
|
||||
Hiếu Lê <leorize+oss@disroot.org>
|
||||
Huang Liu <liu.huang@zte.com.cn>
|
||||
Håkan Johansson <f96hajo@chalmers.se>
|
||||
Igor K <igor@dilos.org>
|
||||
Igor Kozhukhov <ikozhukhov@gmail.com>
|
||||
Igor Lvovsky <ilvovsky@gmail.com>
|
||||
ilbsmart <wgqimut@gmail.com>
|
||||
Ilkka Sovanto <github@ilkka.kapsi.fi>
|
||||
illiliti <illiliti@protonmail.com>
|
||||
ilovezfs <ilovezfs@icloud.com>
|
||||
InsanePrawn <Insane.Prawny@gmail.com>
|
||||
Isaac Huang <he.huang@intel.com>
|
||||
Jacek Fefliński <feflik@gmail.com>
|
||||
Jacob Adams <tookmund@gmail.com>
|
||||
Jake Howard <git@theorangeone.net>
|
||||
James Cowgill <james.cowgill@mips.com>
|
||||
James H <james@kagisoft.co.uk>
|
||||
James Lee <jlee@thestaticvoid.com>
|
||||
James Pan <jiaming.pan@yahoo.com>
|
||||
James Wah <james@laird-wah.net>
|
||||
Jan Engelhardt <jengelh@inai.de>
|
||||
Jan Kryl <jan.kryl@nexenta.com>
|
||||
Jan Sanislo <oystr@cs.washington.edu>
|
||||
Jaron Kent-Dobias <jaron@kent-dobias.com>
|
||||
Jason Cohen <jwittlincohen@gmail.com>
|
||||
Jason Harmening <jason.harmening@gmail.com>
|
||||
Jason King <jason.brian.king@gmail.com>
|
||||
Jason Lee <jasonlee@lanl.gov>
|
||||
Jason Zaman <jasonzaman@gmail.com>
|
||||
Javen Wu <wu.javen@gmail.com>
|
||||
Jean-Baptiste Lallement <jean-baptiste@ubuntu.com>
|
||||
Jeff Dike <jdike@akamai.com>
|
||||
Jeremy Faulkner <gldisater@gmail.com>
|
||||
Jeremy Gill <jgill@parallax-innovations.com>
|
||||
Jeremy Jones <jeremy@delphix.com>
|
||||
Jeremy Visser <jeremy.visser@gmail.com>
|
||||
Jerry Jelinek <jerry.jelinek@joyent.com>
|
||||
Jessica Clarke <jrtc27@jrtc27.com>
|
||||
Jinshan Xiong <jinshan.xiong@intel.com>
|
||||
Jitendra Patidar <jitendra.patidar@nutanix.com>
|
||||
JK Dingwall <james@dingwall.me.uk>
|
||||
Joe Stein <joe.stein@delphix.com>
|
||||
John-Mark Gurney <jmg@funkthat.com>
|
||||
John Albietz <inthecloud247@gmail.com>
|
||||
John Eismeier <john.eismeier@gmail.com>
|
||||
John Gallagher <john.gallagher@delphix.com>
|
||||
John Layman <jlayman@sagecloud.com>
|
||||
John L. Hammond <john.hammond@intel.com>
|
||||
John M. Layman <jml@frijid.net>
|
||||
Johnny Stenback <github@jstenback.com>
|
||||
John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>
|
||||
John Poduska <jpoduska@datto.com>
|
||||
John Ramsden <johnramsden@riseup.net>
|
||||
John Wren Kennedy <john.kennedy@delphix.com>
|
||||
jokersus <lolivampireslave@gmail.com>
|
||||
Jonathon Fernyhough <jonathon@m2x.dev>
|
||||
Jorgen Lundman <lundman@lundman.net>
|
||||
Josef 'Jeff' Sipek <josef.sipek@nexenta.com>
|
||||
Jose Luis Duran <jlduran@gmail.com>
|
||||
Josh Soref <jsoref@users.noreply.github.com>
|
||||
Joshua M. Clulow <josh@sysmgr.org>
|
||||
José Luis Salvador Rufo <salvador.joseluis@gmail.com>
|
||||
João Carlos Mendes Luís <jonny@jonny.eng.br>
|
||||
Julian Brunner <julian.brunner@gmail.com>
|
||||
Julian Heuking <JulianH@beckhoff.com>
|
||||
jumbi77 <jumbi77@users.noreply.github.com>
|
||||
Justin Bedő <cu@cua0.org>
|
||||
Justin Gottula <justin@jgottula.com>
|
||||
Justin Hibbits <chmeeedalf@gmail.com>
|
||||
Justin Keogh <github.com@v6y.net>
|
||||
Justin Lecher <jlec@gentoo.org>
|
||||
Justin Scholz <git@justinscholz.de>
|
||||
Justin T. Gibbs <gibbs@FreeBSD.org>
|
||||
jyxent <jordanp@gmail.com>
|
||||
Jörg Thalheim <joerg@higgsboson.tk>
|
||||
ka7 <ka7@la-evento.com>
|
||||
Ka Ho Ng <khng@FreeBSD.org>
|
||||
Kamil Domański <kamil@domanski.co>
|
||||
Karsten Kretschmer <kkretschmer@gmail.com>
|
||||
Kash Pande <kash@tripleback.net>
|
||||
Kay Pedersen <christianpe96@gmail.com>
|
||||
Keith M Wesolowski <wesolows@foobazco.org>
|
||||
Kent Ross <k@mad.cash>
|
||||
KernelOfTruth <kerneloftruth@gmail.com>
|
||||
Kevin Bowling <kevin.bowling@kev009.com>
|
||||
Kevin Greene <kevin.greene@delphix.com>
|
||||
Kevin Jin <lostking2008@hotmail.com>
|
||||
Kevin P. Fleming <kevin@km6g.us>
|
||||
Kevin Tanguy <kevin.tanguy@ovh.net>
|
||||
KireinaHoro <i@jsteward.moe>
|
||||
Kjeld Schouten-Lebbing <kjeld@schouten-lebbing.nl>
|
||||
Kleber Tarcísio <klebertarcisio@yahoo.com.br>
|
||||
Kody A Kantor <kody.kantor@gmail.com>
|
||||
Kohsuke Kawaguchi <kk@kohsuke.org>
|
||||
Konstantin Khorenko <khorenko@virtuozzo.com>
|
||||
KORN Andras <korn@elan.rulez.org>
|
||||
Kristof Provost <github@sigsegv.be>
|
||||
Krzysztof Piecuch <piecuch@kpiecuch.pl>
|
||||
Kyle Blatter <kyleblatter@llnl.gov>
|
||||
Kyle Evans <kevans@FreeBSD.org>
|
||||
Kyle Fuller <inbox@kylefuller.co.uk>
|
||||
Laevos <Laevos@users.noreply.github.com>
|
||||
Lalufu <Lalufu@users.noreply.github.com>
|
||||
Lars Johannsen <laj@it.dk>
|
||||
Laura Hild <lsh@jlab.org>
|
||||
Laurențiu Nicola <lnicola@dend.ro>
|
||||
Lauri Tirkkonen <lauri@hacktheplanet.fi>
|
||||
liaoyuxiangqin <guo.yong33@zte.com.cn>
|
||||
Li Dongyang <dongyang.li@anu.edu.au>
|
||||
Liu Hua <liu.hua130@zte.com.cn>
|
||||
Liu Qing <winglq@gmail.com>
|
||||
Li Wei <W.Li@Sun.COM>
|
||||
Loli <ezomori.nozomu@gmail.com>
|
||||
lorddoskias <lorddoskias@gmail.com>
|
||||
Lorenz Brun <lorenz@dolansoft.org>
|
||||
Lorenz Hüdepohl <dev@stellardeath.org>
|
||||
louwrentius <louwrentius@gmail.com>
|
||||
Lukas Wunner <lukas@wunner.de>
|
||||
luozhengzheng <luo.zhengzheng@zte.com.cn>
|
||||
Luís Henriques <henrix@camandro.org>
|
||||
Madhav Suresh <madhav.suresh@delphix.com>
|
||||
manfromafar <jonsonb10@gmail.com>
|
||||
Manoj Joseph <manoj.joseph@delphix.com>
|
||||
Manuel Amador (Rudd-O) <rudd-o@rudd-o.com>
|
||||
Marcel Huber <marcelhuberfoo@gmail.com>
|
||||
Marcel Menzel <mail@mcl.gg>
|
||||
Marcel Schilling <marcel.schilling@uni-luebeck.de>
|
||||
Marcel Telka <marcel.telka@nexenta.com>
|
||||
Marcel Wysocki <maci.stgn@gmail.com>
|
||||
Marcin Skarbek <git@skarbek.name>
|
||||
Mariusz Zaborski <mariusz.zaborski@klarasystems.com>
|
||||
Mark Johnston <markj@FreeBSD.org>
|
||||
Mark Maybee <mark.maybee@delphix.com>
|
||||
Mark Roper <markroper@gmail.com>
|
||||
Mark Shellenbaum <Mark.Shellenbaum@Oracle.COM>
|
||||
marku89 <mar42@kola.li>
|
||||
Mark Wright <markwright@internode.on.net>
|
||||
Mart Frauenlob <allkind@fastest.cc>
|
||||
Martin Matuska <mm@FreeBSD.org>
|
||||
Martin Rüegg <martin.rueegg@metaworx.ch>
|
||||
Massimo Maggi <me@massimo-maggi.eu>
|
||||
Mateusz Guzik <mjguzik@gmail.com>
|
||||
Mateusz Piotrowski <0mp@FreeBSD.org>
|
||||
Mathieu Velten <matmaul@gmail.com>
|
||||
Matt Fiddaman <github@m.fiddaman.uk>
|
||||
Matthew Ahrens <matt@delphix.com>
|
||||
Matthew Thode <mthode@mthode.org>
|
||||
Matthias Blankertz <matthias@blankertz.org>
|
||||
Matt Johnston <matt@fugro-fsi.com.au>
|
||||
Matt Kemp <matt@mattikus.com>
|
||||
Matt Macy <mmacy@freebsd.org>
|
||||
Matus Kral <matuskral@me.com>
|
||||
Mauricio Faria de Oliveira <mfo@canonical.com>
|
||||
Max Grossman <max.grossman@delphix.com>
|
||||
Maxim Filimonov <che@bein.link>
|
||||
Maximilian Mehnert <maximilian.mehnert@gmx.de>
|
||||
Max Zettlmeißl <max@zettlmeissl.de>
|
||||
Md Islam <mdnahian@outlook.com>
|
||||
megari <megari@iki.fi>
|
||||
Michael D Labriola <michael.d.labriola@gmail.com>
|
||||
Michael Franzl <michael@franzl.name>
|
||||
Michael Gebetsroither <michael@mgeb.org>
|
||||
Michael Kjorling <michael@kjorling.se>
|
||||
Michael Martin <mgmartin.mgm@gmail.com>
|
||||
Michael Niewöhner <foss@mniewoehner.de>
|
||||
Michael Zhivich <mzhivich@akamai.com>
|
||||
Michal Vasilek <michal@vasilek.cz>
|
||||
MigeljanImeri <ImeriMigel@gmail.com>
|
||||
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>
|
||||
Minsoo Choo <minsoochoo0122@proton.me>
|
||||
Mohamed Tawfik <m_tawfik@aucegypt.edu>
|
||||
Morgan Jones <mjones@rice.edu>
|
||||
Moritz Maxeiner <moritz@ucworks.org>
|
||||
Mo Zhou <cdluminate@gmail.com>
|
||||
naivekun <naivekun@outlook.com>
|
||||
nathancheek <myself@nathancheek.com>
|
||||
Nathaniel Clark <Nathaniel.Clark@misrule.us>
|
||||
Nathaniel Wesley Filardo <nwf@cs.jhu.edu>
|
||||
Nathan Lewis <linux.robotdude@gmail.com>
|
||||
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 Black <dank@qemfd.net>
|
||||
Nick Garvey <garvey.nick@gmail.com>
|
||||
Nick Mattis <nickm970@gmail.com>
|
||||
Nick Terrell <terrelln@fb.com>
|
||||
Niklas Haas <github-c6e1c8@haasn.xyz>
|
||||
Nikolay Borisov <n.borisov.lkml@gmail.com>
|
||||
nordaux <nordaux@gmail.com>
|
||||
ofthesun9 <olivier@ofthesun.net>
|
||||
Olaf Faaland <faaland1@llnl.gov>
|
||||
Oleg Drokin <green@linuxhacker.ru>
|
||||
Oleg Stepura <oleg@stepura.com>
|
||||
Olivier Certner <olce.freebsd@certner.fr>
|
||||
Olivier Mazouffre <olivier.mazouffre@ims-bordeaux.fr>
|
||||
omni <omni+vagant@hack.org>
|
||||
Orivej Desh <orivej@gmx.fr>
|
||||
Pablo Correa Gómez <ablocorrea@hotmail.com>
|
||||
Palash Gandhi <pbg4930@rit.edu>
|
||||
Patrick Mooney <pmooney@pfmooney.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 Snajdr <snajpa@snajpa.net>
|
||||
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>
|
||||
Peter Dave Hello <hsu@peterdavehello.org>
|
||||
Peter Levine <plevine457@gmail.com>
|
||||
Peter Wirdemo <peter.wirdemo@gmail.com>
|
||||
Petros Koutoupis <petros@petroskoutoupis.com>
|
||||
Philip Pokorny <ppokorny@penguincomputing.com>
|
||||
Philipp Riederer <pt@philipptoelke.de>
|
||||
Phil Kauffman <philip@kauffman.me>
|
||||
Ping Huang <huangping@smartx.com>
|
||||
Piotr Kubaj <pkubaj@anongoth.pl>
|
||||
Piotr P. Stefaniak <pstef@freebsd.org>
|
||||
Prakash Surya <prakash.surya@delphix.com>
|
||||
Prasad Joshi <prasadjoshi124@gmail.com>
|
||||
privb0x23 <privb0x23@users.noreply.github.com>
|
||||
P.SCH <p88@yahoo.com>
|
||||
Quartz <yyhran@163.com>
|
||||
Quentin Zdanis <zdanisq@gmail.com>
|
||||
Rafael Kitover <rkitover@gmail.com>
|
||||
RageLtMan <sempervictus@users.noreply.github.com>
|
||||
Ralf Ertzinger <ralf@skytale.net>
|
||||
Randall Mason <ClashTheBunny@gmail.com>
|
||||
Remy Blank <remy.blank@pobox.com>
|
||||
renelson <bnelson@nelsonbe.com>
|
||||
Reno Reckling <e-github@wthack.de>
|
||||
Ricardo M. Correia <ricardo.correia@oracle.com>
|
||||
Riccardo Schirone <rschirone91@gmail.com>
|
||||
Richard Allen <belperite@gmail.com>
|
||||
Richard Elling <Richard.Elling@RichardElling.com>
|
||||
Richard Kojedzinszky <richard@kojedz.in>
|
||||
Richard Laager <rlaager@wiktel.com>
|
||||
Richard Lowe <richlowe@richlowe.net>
|
||||
Richard Sharpe <rsharpe@samba.org>
|
||||
Richard Yao <ryao@gentoo.org>
|
||||
Rich Ercolani <rincebrain@gmail.com>
|
||||
Rick Macklem <rmacklem@uoguelph.ca>
|
||||
rilysh <nightquick@proton.me>
|
||||
Robert Evans <evansr@google.com>
|
||||
Robert Novak <sailnfool@gmail.com>
|
||||
Roberto Ricci <ricci@disroot.org>
|
||||
Rob Norris <robn@despairlabs.com>
|
||||
Rob Wing <rew@FreeBSD.org>
|
||||
Rohan Puri <rohan.puri15@gmail.com>
|
||||
Romain Dolbeau <romain.dolbeau@atos.net>
|
||||
Roman Strashkin <roman.strashkin@nexenta.com>
|
||||
Ross Williams <ross@ross-williams.net>
|
||||
Ruben Kerkhof <ruben@rubenkerkhof.com>
|
||||
Ryan Hirasaki <ryanhirasaki@gmail.com>
|
||||
Ryan Lahfa <masterancpp@gmail.com>
|
||||
Ryan Libby <rlibby@FreeBSD.org>
|
||||
Ryan Moeller <freqlabs@FreeBSD.org>
|
||||
Sam Atkinson <samatk@amazon.com>
|
||||
Sam Hathaway <github.com@munkynet.org>
|
||||
Sam James <sam@gentoo.org>
|
||||
Sam Lunt <samuel.j.lunt@gmail.com>
|
||||
Samuel VERSCHELDE <stormi-github@ylix.fr>
|
||||
Samuel Wycliffe <samuelwycliffe@gmail.com>
|
||||
Samuel Wycliffe J <samwyc@hpe.com>
|
||||
Sanjeev Bagewadi <sanjeev.bagewadi@gmail.com>
|
||||
Sara Hartse <sara.hartse@delphix.com>
|
||||
Saso Kiselkov <saso.kiselkov@nexenta.com>
|
||||
Satadru Pramanik <satadru@gmail.com>
|
||||
Savyasachee Jha <genghizkhan91@hawkradius.com>
|
||||
Scott Colby <scott@scolby.com>
|
||||
Scot W. Stevenson <scot.stevenson@gmail.com>
|
||||
Sean Eric Fagan <sef@ixsystems.com>
|
||||
Sebastian Gottschall <s.gottschall@dd-wrt.com>
|
||||
Sebastien Roy <seb@delphix.com>
|
||||
Sen Haerens <sen@senhaerens.be>
|
||||
Serapheim Dimitropoulos <serapheim@delphix.com>
|
||||
Seth Forshee <seth.forshee@canonical.com>
|
||||
Shaan Nobee <sniper111@gmail.com>
|
||||
Shampavman <sham.pavman@nexenta.com>
|
||||
Shaun Tancheff <shaun@aeonazure.com>
|
||||
Shawn Bayern <sbayern@law.fsu.edu>
|
||||
Shengqi Chen <harry-chen@outlook.com>
|
||||
Shen Yan <shenyanxxxy@qq.com>
|
||||
Simon Guest <simon.guest@tesujimath.org>
|
||||
Simon Klinkert <simon.klinkert@gmail.com>
|
||||
Sowrabha Gopal <sowrabha.gopal@delphix.com>
|
||||
Spencer Kinny <spencerkinny1995@gmail.com>
|
||||
Srikanth N S <srikanth.nagasubbaraoseetharaman@hpe.com>
|
||||
Stanislav Seletskiy <s.seletskiy@gmail.com>
|
||||
Stefan Lendl <s.lendl@proxmox.com>
|
||||
Steffen Müthing <steffen.muething@iwr.uni-heidelberg.de>
|
||||
Stephen Blinick <stephen.blinick@delphix.com>
|
||||
sterlingjensen <sterlingjensen@users.noreply.github.com>
|
||||
Steve Dougherty <sdougherty@barracuda.com>
|
||||
Steve Mokris <smokris@softpixel.com>
|
||||
Steven Burgess <sburgess@dattobackup.com>
|
||||
Steven Hartland <smh@freebsd.org>
|
||||
Steven Johnson <sjohnson@sakuraindustries.com>
|
||||
Steven Noonan <steven@uplinklabs.net>
|
||||
stf <s@ctrlc.hu>
|
||||
Stian Ellingsen <stian@plaimi.net>
|
||||
Stoiko Ivanov <github@nomore.at>
|
||||
Stéphane Lesimple <speed47_github@speed47.net>
|
||||
Suman Chakravartula <schakrava@gmail.com>
|
||||
Sydney Vanda <sydney.m.vanda@intel.com>
|
||||
Sören Tempel <soeren+git@soeren-tempel.net>
|
||||
Tamas TEVESZ <ice@extreme.hu>
|
||||
Teodor Spæren <teodor_spaeren@riseup.net>
|
||||
TerraTech <TerraTech@users.noreply.github.com>
|
||||
Thijs Cramer <thijs.cramer@gmail.com>
|
||||
Thomas Bertschinger <bertschinger@lanl.gov>
|
||||
Thomas Geppert <geppi@digitx.de>
|
||||
Thomas Lamprecht <guggentom@hotmail.de>
|
||||
Till Maas <opensource@till.name>
|
||||
Tim Chase <tim@chase2k.com>
|
||||
Tim Connors <tconnors@rather.puzzling.org>
|
||||
Tim Crawford <tcrawford@datto.com>
|
||||
Tim Haley <Tim.Haley@Sun.COM>
|
||||
timor <timor.dd@googlemail.com>
|
||||
Timothy Day <tday141@gmail.com>
|
||||
Tim Schumacher <timschumi@gmx.de>
|
||||
Tino Reichardt <milky-zfs@mcmilk.de>
|
||||
Tobin Harding <me@tobin.cc>
|
||||
Tom Caputi <tcaputi@datto.com>
|
||||
Tom Matthews <tom@axiom-partners.com>
|
||||
Tomohiro Kusumi <kusumi.tomohiro@gmail.com>
|
||||
Tom Prince <tom.prince@ualberta.net>
|
||||
Tony Hutter <hutter2@llnl.gov>
|
||||
Tony Nguyen <tony.nguyen@delphix.com>
|
||||
Tony Perkins <tperkins@datto.com>
|
||||
Toomas Soome <tsoome@me.com>
|
||||
Torsten Wörtwein <twoertwein@gmail.com>
|
||||
Toyam Cox <aviator45003@gmail.com>
|
||||
Trevor Bautista <trevrb@trevrb.net>
|
||||
Trey Dockendorf <treydock@gmail.com>
|
||||
Troels Nørgaard <tnn@tradeshift.com>
|
||||
Tulsi Jain <tulsi.jain@delphix.com>
|
||||
Turbo Fredriksson <turbo@bayour.com>
|
||||
Tyler J. Stachecki <stachecki.tyler@gmail.com>
|
||||
Umer Saleem <usaleem@ixsystems.com>
|
||||
Vaibhav Bhanawat <vaibhav.bhanawat@delphix.com>
|
||||
Valmiky Arquissandas <kayvlim@gmail.com>
|
||||
Val Packett <val@packett.cool>
|
||||
Vince van Oosten <techhazard@codeforyouand.me>
|
||||
Violet Purcell <vimproved@inventati.org>
|
||||
Vipin Kumar Verma <vipin.verma@hpe.com>
|
||||
Vitaut Bajaryn <vitaut.bayaryn@gmail.com>
|
||||
Volker Mauel <volkermauel@gmail.com>
|
||||
Václav Skála <skala@vshosting.cz>
|
||||
Walter Huf <hufman@gmail.com>
|
||||
Warner Losh <imp@bsdimp.com>
|
||||
Weigang Li <weigang.li@intel.com>
|
||||
WHR <msl0000023508@gmail.com>
|
||||
Will Andrews <will@freebsd.org>
|
||||
Will Rouesnel <w.rouesnel@gmail.com>
|
||||
Windel Bouwman <windel@windel.nl>
|
||||
Wojciech Małota-Wójcik <outofforest@users.noreply.github.com>
|
||||
Wolfgang Bumiller <w.bumiller@proxmox.com>
|
||||
Xin Li <delphij@FreeBSD.org>
|
||||
Xinliang Liu <xinliang.liu@linaro.org>
|
||||
xtouqh <xtouqh@hotmail.com>
|
||||
Yann Collet <cyan@fb.com>
|
||||
Yanping Gao <yanping.gao@xtaotech.com>
|
||||
Ying Zhu <casualfisher@gmail.com>
|
||||
Youzhong Yang <youzhong@gmail.com>
|
||||
yparitcher <y@paritcher.com>
|
||||
yuina822 <ayuichi@club.kyutech.ac.jp>
|
||||
YunQiang Su <syq@debian.org>
|
||||
Yuri Pankov <yuri.pankov@gmail.com>
|
||||
Yuxin Wang <yuxinwang9999@gmail.com>
|
||||
Yuxuan Shui <yshuiv7@gmail.com>
|
||||
Zachary Bedell <zac@thebedells.org>
|
||||
Zach Dykstra <dykstra.zachary@gmail.com>
|
||||
zgock <zgock@nuc.base.zgock-lab.net>
|
||||
Zhu Chuang <chuang@melty.land>
|
||||
Érico Nogueira <erico.erc@gmail.com>
|
||||
Đoàn Trần Công Danh <congdanhqx@gmail.com>
|
||||
韩朴宇 <w12101111@gmail.com>
|
||||
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.2.4
|
||||
Release: 1
|
||||
Release-Tags: relext
|
||||
License: CDDL
|
||||
Author: OpenZFS
|
||||
Linux-Maximum: 6.8
|
||||
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
|
||||
|
||||
+46
-154
@@ -1,83 +1,32 @@
|
||||
CLEANFILES =
|
||||
dist_noinst_DATA =
|
||||
INSTALL_DATA_HOOKS =
|
||||
ALL_LOCAL =
|
||||
CLEAN_LOCAL =
|
||||
CHECKS = shellcheck checkbashisms
|
||||
|
||||
include $(top_srcdir)/config/Rules.am
|
||||
include $(top_srcdir)/config/CppCheck.am
|
||||
include $(top_srcdir)/config/Shellcheck.am
|
||||
include $(top_srcdir)/config/Substfiles.am
|
||||
include $(top_srcdir)/scripts/Makefile.am
|
||||
|
||||
ACLOCAL_AMFLAGS = -I config
|
||||
|
||||
SUBDIRS = include
|
||||
if BUILD_LINUX
|
||||
include $(srcdir)/%D%/rpm/Makefile.am
|
||||
endif
|
||||
include config/rpm.am
|
||||
include config/deb.am
|
||||
include config/tgz.am
|
||||
|
||||
SUBDIRS = include rpm
|
||||
if CONFIG_USER
|
||||
include $(srcdir)/%D%/cmd/Makefile.am
|
||||
include $(srcdir)/%D%/contrib/Makefile.am
|
||||
include $(srcdir)/%D%/etc/Makefile.am
|
||||
include $(srcdir)/%D%/lib/Makefile.am
|
||||
include $(srcdir)/%D%/man/Makefile.am
|
||||
include $(srcdir)/%D%/tests/Makefile.am
|
||||
if BUILD_LINUX
|
||||
include $(srcdir)/%D%/udev/Makefile.am
|
||||
SUBDIRS += udev etc man scripts lib tests cmd contrib
|
||||
endif
|
||||
endif
|
||||
CPPCHECKDIRS += module
|
||||
if CONFIG_KERNEL
|
||||
SUBDIRS += module
|
||||
|
||||
extradir = $(prefix)/src/zfs-$(VERSION)
|
||||
extradir = @prefix@/src/zfs-$(VERSION)
|
||||
extra_HEADERS = zfs.release.in zfs_config.h.in
|
||||
|
||||
kerneldir = @prefix@/src/zfs-$(VERSION)/$(LINUX_VERSION)
|
||||
nodist_kernel_HEADERS = zfs.release zfs_config.h module/$(LINUX_SYMBOLS)
|
||||
endif
|
||||
|
||||
dist_noinst_DATA += autogen.sh copy-builtin
|
||||
dist_noinst_DATA += AUTHORS CODE_OF_CONDUCT.md COPYRIGHT LICENSE META NEWS NOTICE
|
||||
dist_noinst_DATA += README.md RELEASES.md
|
||||
dist_noinst_DATA += module/lua/README.zfs module/os/linux/spl/README.md
|
||||
|
||||
# Include all the extra licensing information for modules
|
||||
dist_noinst_DATA += module/icp/algs/skein/THIRDPARTYLICENSE
|
||||
dist_noinst_DATA += module/icp/algs/skein/THIRDPARTYLICENSE.descrip
|
||||
dist_noinst_DATA += module/icp/asm-x86_64/aes/THIRDPARTYLICENSE.gladman
|
||||
dist_noinst_DATA += module/icp/asm-x86_64/aes/THIRDPARTYLICENSE.gladman.descrip
|
||||
dist_noinst_DATA += module/icp/asm-x86_64/aes/THIRDPARTYLICENSE.openssl
|
||||
dist_noinst_DATA += module/icp/asm-x86_64/aes/THIRDPARTYLICENSE.openssl.descrip
|
||||
dist_noinst_DATA += module/icp/asm-x86_64/modes/THIRDPARTYLICENSE.cryptogams
|
||||
dist_noinst_DATA += module/icp/asm-x86_64/modes/THIRDPARTYLICENSE.cryptogams.descrip
|
||||
dist_noinst_DATA += module/icp/asm-x86_64/modes/THIRDPARTYLICENSE.openssl
|
||||
dist_noinst_DATA += module/icp/asm-x86_64/modes/THIRDPARTYLICENSE.openssl.descrip
|
||||
dist_noinst_DATA += module/os/linux/spl/THIRDPARTYLICENSE.gplv2
|
||||
dist_noinst_DATA += module/os/linux/spl/THIRDPARTYLICENSE.gplv2.descrip
|
||||
dist_noinst_DATA += module/zfs/THIRDPARTYLICENSE.cityhash
|
||||
dist_noinst_DATA += module/zfs/THIRDPARTYLICENSE.cityhash.descrip
|
||||
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 += META DISCLAIMER COPYRIGHT README.markdown OPENSOLARIS.LICENSE
|
||||
|
||||
@CODE_COVERAGE_RULES@
|
||||
|
||||
GITREV = include/zfs_gitrev.h
|
||||
CLEANFILES += $(GITREV)
|
||||
PHONY += gitrev
|
||||
gitrev:
|
||||
$(AM_V_GEN)$(top_srcdir)/scripts/make_gitrev.sh $(GITREV)
|
||||
|
||||
all: gitrev
|
||||
|
||||
PHONY += install-data-hook $(INSTALL_DATA_HOOKS)
|
||||
install-data-hook: $(INSTALL_DATA_HOOKS)
|
||||
|
||||
PHONY += maintainer-clean-local
|
||||
maintainer-clean-local:
|
||||
-$(RM) $(GITREV)
|
||||
|
||||
PHONY += distclean-local
|
||||
distclean-local:
|
||||
-$(RM) -R autom4te*.cache build
|
||||
distclean-local::
|
||||
-$(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 '*~' \
|
||||
@@ -86,122 +35,65 @@ distclean-local:
|
||||
-o -name 'core' -o -name 'Makefile' -o -name 'Module.symvers' \
|
||||
-o -name '*.order' -o -name '*.markers' -o -name '*.gcda' \
|
||||
-o -name '*.gcno' \) \
|
||||
-type f -delete
|
||||
|
||||
PHONY += $(CLEAN_LOCAL)
|
||||
clean-local: $(CLEAN_LOCAL)
|
||||
|
||||
PHONY += $(ALL_LOCAL)
|
||||
all-local: $(ALL_LOCAL)
|
||||
-type f -print | xargs $(RM)
|
||||
|
||||
dist-hook:
|
||||
$(top_srcdir)/scripts/make_gitrev.sh -D $(distdir) $(GITREV)
|
||||
$(SED) $(ac_inplace) 's/\(Release:[[:space:]]*\).*/\1$(RELEASE)/' $(distdir)/META
|
||||
sed -i 's/Release:[[:print:]]*/Release: $(RELEASE)/' \
|
||||
$(distdir)/META
|
||||
|
||||
PHONY += codecheck $(CHECKS)
|
||||
codecheck: $(CHECKS)
|
||||
checkstyle: cstyle shellcheck flake8 commitcheck
|
||||
|
||||
SHELLCHECKSCRIPTS += autogen.sh
|
||||
|
||||
PHONY += checkstyle
|
||||
checkstyle: codecheck commitcheck
|
||||
|
||||
PHONY += commitcheck
|
||||
commitcheck:
|
||||
$(AM_V_at)if git rev-parse --git-dir > /dev/null 2>&1; then \
|
||||
${top_srcdir}/scripts/commitcheck.sh; \
|
||||
@if git rev-parse --git-dir > /dev/null 2>&1; then \
|
||||
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
|
||||
CHECKS += cstyle
|
||||
cstyle:
|
||||
$(AM_V_at)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/*' \
|
||||
! -path './include/sys/lua/*' \
|
||||
! -path './module/lua/l*.[ch]' \
|
||||
! -path './module/zfs/lz4.c' \
|
||||
$(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
|
||||
CHECKS += testscheck
|
||||
testscheck:
|
||||
$(AM_V_at)[ $$(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) \) | \
|
||||
tee /dev/stderr | wc -l) -eq 0 ]
|
||||
|
||||
CHECKS += vcscheck
|
||||
vcscheck:
|
||||
$(AM_V_at)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
|
||||
|
||||
CHECKS += zstdcheck
|
||||
zstdcheck:
|
||||
@$(MAKE) -C module check-zstd-symbols
|
||||
|
||||
PHONY += lint
|
||||
lint: cppcheck paxcheck
|
||||
|
||||
PHONY += paxcheck
|
||||
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
|
||||
|
||||
paxcheck:
|
||||
$(AM_V_at)if type scanelf > /dev/null 2>&1; then \
|
||||
$(top_srcdir)/scripts/paxcheck.sh $(top_builddir); \
|
||||
else \
|
||||
echo "skipping paxcheck because scanelf is not installed"; \
|
||||
@if type scanelf > /dev/null 2>&1; then \
|
||||
scripts/paxcheck.sh ${top_srcdir}; \
|
||||
fi
|
||||
|
||||
CHECKS += flake8
|
||||
flake8:
|
||||
$(AM_V_at)if type flake8 > /dev/null 2>&1; then \
|
||||
flake8 $(top_srcdir); \
|
||||
else \
|
||||
echo "skipping flake8 because flake8 is not installed"; \
|
||||
@if type flake8 > /dev/null 2>&1; then \
|
||||
flake8 ${top_srcdir}; \
|
||||
fi
|
||||
|
||||
PHONY += regen-tests
|
||||
regen-tests:
|
||||
@$(MAKE) -C tests/zfs-tests/tests regen
|
||||
|
||||
PHONY += ctags
|
||||
ctags:
|
||||
$(RM) tags
|
||||
find $(top_srcdir) -name '.?*' -prune \
|
||||
-o -type f -name '*.[hcS]' -exec 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]' -exec 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.4-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
|
||||
|
||||
+2
-60
@@ -1,62 +1,4 @@
|
||||
#!/bin/sh
|
||||
[ "${0%/*}" = "$0" ] || cd "${0%/*}" || exit
|
||||
|
||||
# %reldir%/%canon_reldir% (%D%/%C%) only appeared in automake 1.14, but RHEL/CentOS 7 has 1.13.4
|
||||
# This is an (overly) simplistic preprocessor that papers around this for the duration of the generation step,
|
||||
# and can be removed once support for CentOS 7 is dropped
|
||||
automake --version | awk '{print $NF; exit}' | (
|
||||
IFS=. read -r AM_MAJ AM_MIN _
|
||||
[ "$AM_MAJ" -gt 1 ] || [ "$AM_MIN" -ge 14 ]
|
||||
) || {
|
||||
process_root() {
|
||||
root="$1"; shift
|
||||
|
||||
grep -q '%[CD]%' "$root/Makefile.am" || return
|
||||
find "$root" -name Makefile.am "$@" | while read -r dir; do
|
||||
dir="${dir%/Makefile.am}"
|
||||
grep -q '%[CD]%' "$dir/Makefile.am" || continue
|
||||
|
||||
reldir="${dir#"$root"}"
|
||||
reldir="${reldir#/}"
|
||||
|
||||
canon_reldir="$(printf '%s' "$reldir" | tr -C 'a-zA-Z0-9@_' '_')"
|
||||
|
||||
reldir_slash="$reldir/"
|
||||
canon_reldir_slash="${canon_reldir}_"
|
||||
[ -z "$reldir" ] && reldir_slash=
|
||||
[ -z "$reldir" ] && canon_reldir_slash=
|
||||
|
||||
echo "$dir/Makefile.am" >&3
|
||||
sed -i~ -e "s:%D%/:$reldir_slash:g" -e "s:%D%:$reldir:g" \
|
||||
-e "s:%C%_:$canon_reldir_slash:g" -e "s:%C%:$canon_reldir:g" "$dir/Makefile.am"
|
||||
done 3>>"$substituted_files"
|
||||
}
|
||||
|
||||
rollback() {
|
||||
while read -r f; do
|
||||
mv "$f~" "$f"
|
||||
done < "$substituted_files"
|
||||
rm -f "$substituted_files"
|
||||
}
|
||||
|
||||
|
||||
echo "Automake <1.14; papering over missing %reldir%/%canon_reldir% support" >&2
|
||||
|
||||
substituted_files="$(mktemp)"
|
||||
trap rollback EXIT
|
||||
|
||||
roots="$(sed '/Makefile$/!d;/module/d;s:^\s*:./:;s:/Makefile::;/^\.$/d' configure.ac)"
|
||||
|
||||
IFS="
|
||||
"
|
||||
for root in $roots; do
|
||||
root="${root#./}"
|
||||
process_root "$root"
|
||||
done
|
||||
|
||||
set -f
|
||||
# shellcheck disable=SC2086,SC2046
|
||||
process_root . $(printf '!\n-path\n%s/*\n' $roots)
|
||||
}
|
||||
|
||||
autoreconf -fiv && rm -rf autom4te.cache
|
||||
autoreconf -fiv
|
||||
rm -Rf autom4te.cache
|
||||
|
||||
+3
-116
@@ -1,116 +1,3 @@
|
||||
bin_SCRIPTS =
|
||||
bin_PROGRAMS =
|
||||
sbin_SCRIPTS =
|
||||
sbin_PROGRAMS =
|
||||
dist_bin_SCRIPTS =
|
||||
zfsexec_PROGRAMS =
|
||||
mounthelper_PROGRAMS =
|
||||
|
||||
|
||||
sbin_SCRIPTS += fsck.zfs
|
||||
SHELLCHECKSCRIPTS += fsck.zfs
|
||||
CLEANFILES += fsck.zfs
|
||||
dist_noinst_DATA += %D%/fsck.zfs.in
|
||||
$(call SUBST,fsck.zfs,%D%/)
|
||||
|
||||
|
||||
sbin_PROGRAMS += zfs_ids_to_path
|
||||
CPPCHECKTARGETS += zfs_ids_to_path
|
||||
|
||||
zfs_ids_to_path_SOURCES = \
|
||||
%D%/zfs_ids_to_path.c
|
||||
|
||||
zfs_ids_to_path_LDADD = \
|
||||
libzfs.la
|
||||
|
||||
|
||||
zhack_CPPFLAGS = $(AM_CPPFLAGS) $(FORCEDEBUG_CPPFLAGS)
|
||||
|
||||
sbin_PROGRAMS += zhack
|
||||
CPPCHECKTARGETS += zhack
|
||||
|
||||
zhack_SOURCES = \
|
||||
%D%/zhack.c
|
||||
|
||||
zhack_LDADD = \
|
||||
libzpool.la \
|
||||
libzfs_core.la \
|
||||
libnvpair.la
|
||||
|
||||
|
||||
ztest_CFLAGS = $(AM_CFLAGS) $(KERNEL_CFLAGS)
|
||||
# Get rid of compiler warning for unchecked truncating snprintfs on gcc 7.1.1
|
||||
ztest_CFLAGS += $(NO_FORMAT_TRUNCATION)
|
||||
ztest_CPPFLAGS = $(AM_CPPFLAGS) $(FORCEDEBUG_CPPFLAGS)
|
||||
|
||||
sbin_PROGRAMS += ztest
|
||||
CPPCHECKTARGETS += ztest
|
||||
|
||||
ztest_SOURCES = \
|
||||
%D%/ztest.c
|
||||
|
||||
ztest_LDADD = \
|
||||
libzpool.la \
|
||||
libzfs_core.la \
|
||||
libnvpair.la
|
||||
|
||||
ztest_LDADD += -lm
|
||||
ztest_LDFLAGS = -pthread
|
||||
|
||||
|
||||
include $(srcdir)/%D%/raidz_test/Makefile.am
|
||||
include $(srcdir)/%D%/zdb/Makefile.am
|
||||
include $(srcdir)/%D%/zfs/Makefile.am
|
||||
include $(srcdir)/%D%/zinject/Makefile.am
|
||||
include $(srcdir)/%D%/zpool/Makefile.am
|
||||
include $(srcdir)/%D%/zpool_influxdb/Makefile.am
|
||||
include $(srcdir)/%D%/zstream/Makefile.am
|
||||
|
||||
|
||||
if BUILD_LINUX
|
||||
mounthelper_PROGRAMS += mount.zfs
|
||||
CPPCHECKTARGETS += mount.zfs
|
||||
|
||||
mount_zfs_SOURCES = \
|
||||
%D%/mount_zfs.c
|
||||
|
||||
mount_zfs_LDADD = \
|
||||
libzfs.la \
|
||||
libzfs_core.la \
|
||||
libnvpair.la
|
||||
|
||||
mount_zfs_LDADD += $(LTLIBINTL)
|
||||
|
||||
CPPCHECKTARGETS += raidz_test
|
||||
|
||||
|
||||
sbin_PROGRAMS += zgenhostid
|
||||
CPPCHECKTARGETS += zgenhostid
|
||||
|
||||
zgenhostid_SOURCES = \
|
||||
%D%/zgenhostid.c
|
||||
|
||||
|
||||
dist_bin_SCRIPTS += %D%/zvol_wait
|
||||
SHELLCHECKSCRIPTS += %D%/zvol_wait
|
||||
|
||||
|
||||
include $(srcdir)/%D%/zed/Makefile.am
|
||||
endif
|
||||
|
||||
|
||||
if USING_PYTHON
|
||||
bin_SCRIPTS += arc_summary arcstat dbufstat zilstat
|
||||
CLEANFILES += arc_summary arcstat dbufstat zilstat
|
||||
dist_noinst_DATA += %D%/arc_summary %D%/arcstat.in %D%/dbufstat.in %D%/zilstat.in
|
||||
|
||||
$(call SUBST,arcstat,%D%/)
|
||||
$(call SUBST,dbufstat,%D%/)
|
||||
$(call SUBST,zilstat,%D%/)
|
||||
arc_summary: %D%/arc_summary
|
||||
$(AM_V_at)cp $< $@
|
||||
endif
|
||||
|
||||
|
||||
PHONY += cmd
|
||||
cmd: $(bin_SCRIPTS) $(bin_PROGRAMS) $(sbin_SCRIPTS) $(sbin_PROGRAMS) $(dist_bin_SCRIPTS) $(zfsexec_PROGRAMS) $(mounthelper_PROGRAMS)
|
||||
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
|
||||
|
||||
-1043
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
||||
dist_bin_SCRIPTS = arc_summary.py
|
||||
Executable
+1039
File diff suppressed because it is too large
Load Diff
-680
@@ -1,680 +0,0 @@
|
||||
#!/usr/bin/env @PYTHON_SHEBANG@
|
||||
#
|
||||
# Print out ZFS ARC Statistics exported via kstat(1)
|
||||
# For a definition of fields, or usage, use arcstat -v
|
||||
#
|
||||
# This script was originally 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:
|
||||
# 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.
|
||||
#
|
||||
# CDDL HEADER START
|
||||
#
|
||||
# The contents of this file are subject to the terms of the
|
||||
# Common Development and Distribution License, Version 1.0 only
|
||||
# (the "License"). You may not use this file except in compliance
|
||||
# with the License.
|
||||
#
|
||||
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
# or https://opensource.org/licenses/CDDL-1.0.
|
||||
# See the License for the specific language governing permissions
|
||||
# and limitations under the License.
|
||||
#
|
||||
# When distributing Covered Code, include this CDDL HEADER in each
|
||||
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
# If applicable, add the following below this CDDL HEADER, with the
|
||||
# fields enclosed by brackets "[]" replaced with your own identifying
|
||||
# information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
#
|
||||
# CDDL HEADER END
|
||||
#
|
||||
#
|
||||
# Fields have a fixed width. Every interval, we fill the "v"
|
||||
# hash with its corresponding value (v[field]=value) using calculate().
|
||||
# @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
|
||||
import getopt
|
||||
import re
|
||||
import copy
|
||||
|
||||
from signal import signal, SIGINT, SIGWINCH, SIG_DFL
|
||||
|
||||
|
||||
cols = {
|
||||
# HDR: [Size, Scale, Description]
|
||||
"time": [8, -1, "Time"],
|
||||
"hits": [4, 1000, "ARC hits per second"],
|
||||
"iohs": [4, 1000, "ARC I/O hits per second"],
|
||||
"miss": [4, 1000, "ARC misses per second"],
|
||||
"read": [4, 1000, "Total ARC accesses per second"],
|
||||
"hit%": [4, 100, "ARC hit percentage"],
|
||||
"ioh%": [4, 100, "ARC I/O hit percentage"],
|
||||
"miss%": [5, 100, "ARC miss percentage"],
|
||||
"dhit": [4, 1000, "Demand hits per second"],
|
||||
"dioh": [4, 1000, "Demand I/O hits per second"],
|
||||
"dmis": [4, 1000, "Demand misses per second"],
|
||||
"dh%": [3, 100, "Demand hit percentage"],
|
||||
"di%": [3, 100, "Demand I/O hit percentage"],
|
||||
"dm%": [3, 100, "Demand miss percentage"],
|
||||
"ddhit": [5, 1000, "Demand data hits per second"],
|
||||
"ddioh": [5, 1000, "Demand data I/O hits per second"],
|
||||
"ddmis": [5, 1000, "Demand data misses per second"],
|
||||
"ddh%": [4, 100, "Demand data hit percentage"],
|
||||
"ddi%": [4, 100, "Demand data I/O hit percentage"],
|
||||
"ddm%": [4, 100, "Demand data miss percentage"],
|
||||
"dmhit": [5, 1000, "Demand metadata hits per second"],
|
||||
"dmioh": [5, 1000, "Demand metadata I/O hits per second"],
|
||||
"dmmis": [5, 1000, "Demand metadata misses per second"],
|
||||
"dmh%": [4, 100, "Demand metadata hit percentage"],
|
||||
"dmi%": [4, 100, "Demand metadata I/O hit percentage"],
|
||||
"dmm%": [4, 100, "Demand metadata miss percentage"],
|
||||
"phit": [4, 1000, "Prefetch hits per second"],
|
||||
"pioh": [4, 1000, "Prefetch I/O hits per second"],
|
||||
"pmis": [4, 1000, "Prefetch misses per second"],
|
||||
"ph%": [3, 100, "Prefetch hits percentage"],
|
||||
"pi%": [3, 100, "Prefetch I/O hits percentage"],
|
||||
"pm%": [3, 100, "Prefetch miss percentage"],
|
||||
"pdhit": [5, 1000, "Prefetch data hits per second"],
|
||||
"pdioh": [5, 1000, "Prefetch data I/O hits per second"],
|
||||
"pdmis": [5, 1000, "Prefetch data misses per second"],
|
||||
"pdh%": [4, 100, "Prefetch data hits percentage"],
|
||||
"pdi%": [4, 100, "Prefetch data I/O hits percentage"],
|
||||
"pdm%": [4, 100, "Prefetch data miss percentage"],
|
||||
"pmhit": [5, 1000, "Prefetch metadata hits per second"],
|
||||
"pmioh": [5, 1000, "Prefetch metadata I/O hits per second"],
|
||||
"pmmis": [5, 1000, "Prefetch metadata misses per second"],
|
||||
"pmh%": [4, 100, "Prefetch metadata hits percentage"],
|
||||
"pmi%": [4, 100, "Prefetch metadata I/O hits percentage"],
|
||||
"pmm%": [4, 100, "Prefetch metadata miss percentage"],
|
||||
"mhit": [4, 1000, "Metadata hits per second"],
|
||||
"mioh": [4, 1000, "Metadata I/O hits per second"],
|
||||
"mmis": [4, 1000, "Metadata misses per second"],
|
||||
"mread": [5, 1000, "Metadata accesses per second"],
|
||||
"mh%": [3, 100, "Metadata hit percentage"],
|
||||
"mi%": [3, 100, "Metadata I/O hit percentage"],
|
||||
"mm%": [3, 100, "Metadata miss percentage"],
|
||||
"arcsz": [5, 1024, "ARC size"],
|
||||
"size": [5, 1024, "ARC size"],
|
||||
"c": [5, 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"],
|
||||
"unc": [4, 1000, "Uncached 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"],
|
||||
"ddread": [6, 1000, "Demand data accesses per second"],
|
||||
"dmread": [6, 1000, "Demand metadata accesses per second"],
|
||||
"pread": [5, 1000, "Prefetch accesses per second"],
|
||||
"pdread": [6, 1000, "Prefetch data accesses per second"],
|
||||
"pmread": [6, 1000, "Prefetch metadata accesses per second"],
|
||||
"l2hits": [6, 1000, "L2ARC hits per second"],
|
||||
"l2miss": [6, 1000, "L2ARC misses per second"],
|
||||
"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": [5, 1024, "ARC reclaim need"],
|
||||
"free": [5, 1024, "ARC free memory"],
|
||||
"avail": [5, 1024, "ARC available memory"],
|
||||
"waste": [5, 1024, "Wasted memory due to round up to pagesize"],
|
||||
"ztotal": [6, 1000, "zfetch total prefetcher calls per second"],
|
||||
"zhits": [5, 1000, "zfetch stream hits per second"],
|
||||
"zahead": [6, 1000, "zfetch hits ahead of streams per second"],
|
||||
"zpast": [5, 1000, "zfetch hits behind streams per second"],
|
||||
"zmisses": [7, 1000, "zfetch stream misses per second"],
|
||||
"zmax": [4, 1000, "zfetch limit reached per second"],
|
||||
"zfuture": [7, 1000, "zfetch stream future per second"],
|
||||
"zstride": [7, 1000, "zfetch stream strides per second"],
|
||||
"zissued": [7, 1000, "zfetch prefetches issued per second"],
|
||||
"zactive": [7, 1000, "zfetch prefetches active per second"],
|
||||
}
|
||||
|
||||
v = {}
|
||||
hdr = ["time", "read", "ddread", "ddh%", "dmread", "dmh%", "pread", "ph%",
|
||||
"size", "c", "avail"]
|
||||
xhdr = ["time", "mfu", "mru", "mfug", "mrug", "unc", "eskip", "mtxmis",
|
||||
"dread", "pread", "read"]
|
||||
zhdr = ["time", "ztotal", "zhits", "zahead", "zpast", "zmisses", "zmax",
|
||||
"zfuture", "zstride", "zissued", "zactive"]
|
||||
sint = 1 # Default interval is 1 second
|
||||
count = 1 # Default count is 1
|
||||
hdr_intr = 20 # Print header every 20 lines of output
|
||||
opfile = None
|
||||
sep = " " # Default separator is 2 spaces
|
||||
l2exist = False
|
||||
cmd = ("Usage: arcstat [-havxp] [-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]
|
||||
k += [ctl for ctl in sysctl.filter('kstat.zfs.misc.zfetchstats')
|
||||
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
|
||||
|
||||
if "arcstats" in name:
|
||||
# Trims 'kstat.zfs.misc.arcstats' from the name
|
||||
kstat[name[24:]] = int(value)
|
||||
else:
|
||||
kstat["zfetch_" + name[27:]] = int(value)
|
||||
|
||||
elif sys.platform.startswith('linux'):
|
||||
def kstat_update():
|
||||
global kstat
|
||||
|
||||
k1 = [line.strip() for line in open('/proc/spl/kstat/zfs/arcstats')]
|
||||
|
||||
k2 = ["zfetch_" + line.strip() for line in
|
||||
open('/proc/spl/kstat/zfs/zfetchstats')]
|
||||
|
||||
if k1 is None or k2 is None:
|
||||
sys.exit(1)
|
||||
|
||||
del k1[0:2]
|
||||
del k2[0:2]
|
||||
k = k1 + k2
|
||||
kstat = {}
|
||||
|
||||
for s in k:
|
||||
if not s:
|
||||
continue
|
||||
|
||||
name, unused, value = s.split()
|
||||
kstat[name] = int(value)
|
||||
|
||||
|
||||
def detailed_usage():
|
||||
sys.stderr.write("%s\n" % cmd)
|
||||
sys.stderr.write("Field definitions are as follows:\n")
|
||||
for key in cols:
|
||||
sys.stderr.write("%11s : %s\n" % (key, cols[key][2]))
|
||||
sys.stderr.write("\n")
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
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")
|
||||
sys.stderr.write("\t -z : Print zfetch stats\n")
|
||||
sys.stderr.write("\t -f : Specify specific fields to print (see -v)\n")
|
||||
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("\n")
|
||||
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def snap_stats():
|
||||
global cur
|
||||
global kstat
|
||||
|
||||
prev = copy.deepcopy(cur)
|
||||
kstat_update()
|
||||
|
||||
cur = kstat
|
||||
for key in cur:
|
||||
if re.match(key, "class"):
|
||||
continue
|
||||
if key in prev:
|
||||
d[key] = cur[key] - prev[key]
|
||||
else:
|
||||
d[key] = cur[key]
|
||||
|
||||
|
||||
def prettynum(sz, scale, num=0):
|
||||
suffix = [' ', 'K', 'M', 'G', 'T', 'P', 'E', 'Z']
|
||||
index = 0
|
||||
save = 0
|
||||
|
||||
# Special case for date field
|
||||
if scale == -1:
|
||||
return "%s" % num
|
||||
|
||||
# Rounding error, return 0
|
||||
elif 0 < num < 1:
|
||||
num = 0
|
||||
|
||||
while abs(num) > scale and index < 5:
|
||||
save = num
|
||||
num = num / scale
|
||||
index += 1
|
||||
|
||||
if index == 0:
|
||||
return "%*d" % (sz, num)
|
||||
|
||||
if abs(save / scale) < 10:
|
||||
return "%*.1f%s" % (sz - 1, num, suffix[index])
|
||||
else:
|
||||
return "%*d%s" % (sz - 1, num, suffix[index])
|
||||
|
||||
|
||||
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))
|
||||
sys.stdout.write("\n")
|
||||
sys.stdout.flush()
|
||||
|
||||
|
||||
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))
|
||||
sys.stdout.write("\n")
|
||||
|
||||
|
||||
def get_terminal_lines():
|
||||
try:
|
||||
import fcntl
|
||||
import termios
|
||||
import struct
|
||||
data = fcntl.ioctl(sys.stdout.fileno(), termios.TIOCGWINSZ, '1234')
|
||||
sz = struct.unpack('hh', data)
|
||||
return sz[0]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def update_hdr_intr():
|
||||
global hdr_intr
|
||||
|
||||
lines = get_terminal_lines()
|
||||
if lines and lines > 3:
|
||||
hdr_intr = lines - 3
|
||||
|
||||
|
||||
def resize_handler(signum, frame):
|
||||
update_hdr_intr()
|
||||
|
||||
|
||||
def init():
|
||||
global sint
|
||||
global count
|
||||
global hdr
|
||||
global xhdr
|
||||
global zhdr
|
||||
global opfile
|
||||
global sep
|
||||
global out
|
||||
global l2exist
|
||||
global pretty_print
|
||||
|
||||
desired_cols = None
|
||||
aflag = False
|
||||
xflag = False
|
||||
hflag = False
|
||||
vflag = False
|
||||
zflag = False
|
||||
i = 1
|
||||
|
||||
try:
|
||||
opts, args = getopt.getopt(
|
||||
sys.argv[1:],
|
||||
"axzo:hvs:f:p",
|
||||
[
|
||||
"all",
|
||||
"extended",
|
||||
"zfetch",
|
||||
"outfile",
|
||||
"help",
|
||||
"verbose",
|
||||
"separator",
|
||||
"columns",
|
||||
"parsable"
|
||||
]
|
||||
)
|
||||
except getopt.error as msg:
|
||||
sys.stderr.write("Error: %s\n" % str(msg))
|
||||
usage()
|
||||
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'):
|
||||
opfile = arg
|
||||
i += 1
|
||||
if opt in ('-h', '--help'):
|
||||
hflag = True
|
||||
if opt in ('-v', '--verbose'):
|
||||
vflag = True
|
||||
if opt in ('-s', '--separator'):
|
||||
sep = arg
|
||||
i += 1
|
||||
if opt in ('-f', '--columns'):
|
||||
desired_cols = arg
|
||||
i += 1
|
||||
if opt in ('-p', '--parsable'):
|
||||
pretty_print = False
|
||||
if opt in ('-z', '--zfetch'):
|
||||
zflag = True
|
||||
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)
|
||||
|
||||
if hflag or (xflag and zflag) or ((zflag or xflag) and desired_cols):
|
||||
usage()
|
||||
|
||||
if vflag:
|
||||
detailed_usage()
|
||||
|
||||
if xflag:
|
||||
hdr = xhdr
|
||||
|
||||
if zflag:
|
||||
hdr = zhdr
|
||||
|
||||
update_hdr_intr()
|
||||
|
||||
# check if L2ARC exists
|
||||
snap_stats()
|
||||
l2_size = cur.get("l2_size")
|
||||
if l2_size:
|
||||
l2exist = True
|
||||
|
||||
if desired_cols:
|
||||
hdr = desired_cols.split(",")
|
||||
|
||||
invalid = []
|
||||
incompat = []
|
||||
for ele in hdr:
|
||||
if ele not in cols:
|
||||
invalid.append(ele)
|
||||
elif not l2exist and ele.startswith("l2"):
|
||||
sys.stdout.write("No L2ARC Here\n%s\n" % ele)
|
||||
incompat.append(ele)
|
||||
|
||||
if len(invalid) > 0:
|
||||
sys.stderr.write("Invalid column definition! -- %s\n" % invalid)
|
||||
usage()
|
||||
|
||||
if len(incompat) > 0:
|
||||
sys.stderr.write("Incompatible field specified! -- %s\n" %
|
||||
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")
|
||||
sys.stdout = out
|
||||
|
||||
except IOError:
|
||||
sys.stderr.write("Cannot open %s for writing\n" % opfile)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def calculate():
|
||||
global d
|
||||
global v
|
||||
global l2exist
|
||||
|
||||
v = dict()
|
||||
v["time"] = time.strftime("%H:%M:%S", time.localtime())
|
||||
v["hits"] = d["hits"] // sint
|
||||
v["iohs"] = d["iohits"] // sint
|
||||
v["miss"] = d["misses"] // sint
|
||||
v["read"] = v["hits"] + v["iohs"] + v["miss"]
|
||||
v["hit%"] = 100 * v["hits"] // v["read"] if v["read"] > 0 else 0
|
||||
v["ioh%"] = 100 * v["iohs"] // v["read"] if v["read"] > 0 else 0
|
||||
v["miss%"] = 100 - v["hit%"] - v["ioh%"] if v["read"] > 0 else 0
|
||||
|
||||
v["dhit"] = (d["demand_data_hits"] + d["demand_metadata_hits"]) // sint
|
||||
v["dioh"] = (d["demand_data_iohits"] + d["demand_metadata_iohits"]) // sint
|
||||
v["dmis"] = (d["demand_data_misses"] + d["demand_metadata_misses"]) // sint
|
||||
|
||||
v["dread"] = v["dhit"] + v["dioh"] + v["dmis"]
|
||||
v["dh%"] = 100 * v["dhit"] // v["dread"] if v["dread"] > 0 else 0
|
||||
v["di%"] = 100 * v["dioh"] // v["dread"] if v["dread"] > 0 else 0
|
||||
v["dm%"] = 100 - v["dh%"] - v["di%"] if v["dread"] > 0 else 0
|
||||
|
||||
v["ddhit"] = d["demand_data_hits"] // sint
|
||||
v["ddioh"] = d["demand_data_iohits"] // sint
|
||||
v["ddmis"] = d["demand_data_misses"] // sint
|
||||
|
||||
v["ddread"] = v["ddhit"] + v["ddioh"] + v["ddmis"]
|
||||
v["ddh%"] = 100 * v["ddhit"] // v["ddread"] if v["ddread"] > 0 else 0
|
||||
v["ddi%"] = 100 * v["ddioh"] // v["ddread"] if v["ddread"] > 0 else 0
|
||||
v["ddm%"] = 100 - v["ddh%"] - v["ddi%"] if v["ddread"] > 0 else 0
|
||||
|
||||
v["dmhit"] = d["demand_metadata_hits"] // sint
|
||||
v["dmioh"] = d["demand_metadata_iohits"] // sint
|
||||
v["dmmis"] = d["demand_metadata_misses"] // sint
|
||||
|
||||
v["dmread"] = v["dmhit"] + v["dmioh"] + v["dmmis"]
|
||||
v["dmh%"] = 100 * v["dmhit"] // v["dmread"] if v["dmread"] > 0 else 0
|
||||
v["dmi%"] = 100 * v["dmioh"] // v["dmread"] if v["dmread"] > 0 else 0
|
||||
v["dmm%"] = 100 - v["dmh%"] - v["dmi%"] if v["dmread"] > 0 else 0
|
||||
|
||||
v["phit"] = (d["prefetch_data_hits"] + d["prefetch_metadata_hits"]) // sint
|
||||
v["pioh"] = (d["prefetch_data_iohits"] +
|
||||
d["prefetch_metadata_iohits"]) // sint
|
||||
v["pmis"] = (d["prefetch_data_misses"] +
|
||||
d["prefetch_metadata_misses"]) // sint
|
||||
|
||||
v["pread"] = v["phit"] + v["pioh"] + v["pmis"]
|
||||
v["ph%"] = 100 * v["phit"] // v["pread"] if v["pread"] > 0 else 0
|
||||
v["pi%"] = 100 * v["pioh"] // v["pread"] if v["pread"] > 0 else 0
|
||||
v["pm%"] = 100 - v["ph%"] - v["pi%"] if v["pread"] > 0 else 0
|
||||
|
||||
v["pdhit"] = d["prefetch_data_hits"] // sint
|
||||
v["pdioh"] = d["prefetch_data_iohits"] // sint
|
||||
v["pdmis"] = d["prefetch_data_misses"] // sint
|
||||
|
||||
v["pdread"] = v["pdhit"] + v["pdioh"] + v["pdmis"]
|
||||
v["pdh%"] = 100 * v["pdhit"] // v["pdread"] if v["pdread"] > 0 else 0
|
||||
v["pdi%"] = 100 * v["pdioh"] // v["pdread"] if v["pdread"] > 0 else 0
|
||||
v["pdm%"] = 100 - v["pdh%"] - v["pdi%"] if v["pdread"] > 0 else 0
|
||||
|
||||
v["pmhit"] = d["prefetch_metadata_hits"] // sint
|
||||
v["pmioh"] = d["prefetch_metadata_iohits"] // sint
|
||||
v["pmmis"] = d["prefetch_metadata_misses"] // sint
|
||||
|
||||
v["pmread"] = v["pmhit"] + v["pmioh"] + v["pmmis"]
|
||||
v["pmh%"] = 100 * v["pmhit"] // v["pmread"] if v["pmread"] > 0 else 0
|
||||
v["pmi%"] = 100 * v["pmioh"] // v["pmread"] if v["pmread"] > 0 else 0
|
||||
v["pmm%"] = 100 - v["pmh%"] - v["pmi%"] if v["pmread"] > 0 else 0
|
||||
|
||||
v["mhit"] = (d["prefetch_metadata_hits"] +
|
||||
d["demand_metadata_hits"]) // sint
|
||||
v["mioh"] = (d["prefetch_metadata_iohits"] +
|
||||
d["demand_metadata_iohits"]) // sint
|
||||
v["mmis"] = (d["prefetch_metadata_misses"] +
|
||||
d["demand_metadata_misses"]) // sint
|
||||
|
||||
v["mread"] = v["mhit"] + v["mioh"] + v["mmis"]
|
||||
v["mh%"] = 100 * v["mhit"] // v["mread"] if v["mread"] > 0 else 0
|
||||
v["mi%"] = 100 * v["mioh"] // v["mread"] if v["mread"] > 0 else 0
|
||||
v["mm%"] = 100 - v["mh%"] - v["mi%"] 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["unc"] = d["uncached_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
|
||||
v["ztotal"] = (d["zfetch_hits"] + d["zfetch_future"] + d["zfetch_stride"] +
|
||||
d["zfetch_past"] + d["zfetch_misses"]) // sint
|
||||
v["zhits"] = d["zfetch_hits"] // sint
|
||||
v["zahead"] = (d["zfetch_future"] + d["zfetch_stride"]) // sint
|
||||
v["zpast"] = d["zfetch_past"] // sint
|
||||
v["zmisses"] = d["zfetch_misses"] // sint
|
||||
v["zmax"] = d["zfetch_max_streams"] // sint
|
||||
v["zfuture"] = d["zfetch_future"] // sint
|
||||
v["zstride"] = d["zfetch_stride"] // sint
|
||||
v["zissued"] = d["zfetch_io_issued"] // sint
|
||||
v["zactive"] = d["zfetch_io_active"] // sint
|
||||
|
||||
if l2exist:
|
||||
v["l2hits"] = d["l2_hits"] // sint
|
||||
v["l2miss"] = d["l2_misses"] // sint
|
||||
v["l2read"] = v["l2hits"] + v["l2miss"]
|
||||
v["l2hit%"] = 100 * v["l2hits"] // v["l2read"] if v["l2read"] > 0 else 0
|
||||
|
||||
v["l2miss%"] = 100 - v["l2hit%"] if v["l2read"] > 0 else 0
|
||||
v["l2asize"] = cur["l2_asize"]
|
||||
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
|
||||
global count
|
||||
global hdr_intr
|
||||
|
||||
i = 0
|
||||
count_flag = 0
|
||||
|
||||
init()
|
||||
if count > 0:
|
||||
count_flag = 1
|
||||
|
||||
signal(SIGINT, SIG_DFL)
|
||||
signal(SIGWINCH, resize_handler)
|
||||
while True:
|
||||
if i == 0:
|
||||
print_header()
|
||||
|
||||
snap_stats()
|
||||
calculate()
|
||||
print_values()
|
||||
|
||||
if count_flag == 1:
|
||||
if count <= 1:
|
||||
break
|
||||
count -= 1
|
||||
|
||||
i = 0 if i >= hdr_intr else i + 1
|
||||
time.sleep(sint)
|
||||
|
||||
if out:
|
||||
out.close()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1 @@
|
||||
dist_bin_SCRIPTS = arcstat.py
|
||||
Executable
+462
@@ -0,0 +1,462 @@
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Print out ZFS ARC Statistics exported via kstat(1)
|
||||
# For a definition of fields, or usage, use arctstat.pl -v
|
||||
#
|
||||
# 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
|
||||
#
|
||||
# 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
|
||||
#
|
||||
# Comments, Questions, or Suggestions are always welcome.
|
||||
# Contact the maintainer at ( mike at harschsystems dot com )
|
||||
#
|
||||
# CDDL HEADER START
|
||||
#
|
||||
# The contents of this file are subject to the terms of the
|
||||
# Common Development and Distribution License, Version 1.0 only
|
||||
# (the "License"). You may not use this file except in compliance
|
||||
# with the License.
|
||||
#
|
||||
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
# or http://www.opensolaris.org/os/licensing.
|
||||
# See the License for the specific language governing permissions
|
||||
# and limitations under the License.
|
||||
#
|
||||
# When distributing Covered Code, include this CDDL HEADER in each
|
||||
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
# If applicable, add the following below this CDDL HEADER, with the
|
||||
# fields enclosed by brackets "[]" replaced with your own identifying
|
||||
# information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
#
|
||||
# CDDL HEADER END
|
||||
#
|
||||
#
|
||||
# Fields have a fixed width. Every interval, we fill the "v"
|
||||
# hash with its corresponding value (v[field]=value) using calculate().
|
||||
# @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.
|
||||
#
|
||||
|
||||
|
||||
import sys
|
||||
import time
|
||||
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"],
|
||||
"miss%": [5, 100, "ARC miss percentage"],
|
||||
"dhit": [4, 1000, "Demand hits per second"],
|
||||
"dmis": [4, 1000, "Demand misses per second"],
|
||||
"dh%": [3, 100, "Demand hit percentage"],
|
||||
"dm%": [3, 100, "Demand miss percentage"],
|
||||
"phit": [4, 1000, "Prefetch hits per second"],
|
||||
"pmis": [4, 1000, "Prefetch misses per second"],
|
||||
"ph%": [3, 100, "Prefetch hits percentage"],
|
||||
"pm%": [3, 100, "Prefetch miss percentage"],
|
||||
"mhit": [4, 1000, "Metadata hits per second"],
|
||||
"mmis": [4, 1000, "Metadata misses 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"],
|
||||
"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"],
|
||||
"mtxmis": [6, 1000, "mutex_miss per second"],
|
||||
"dread": [5, 1000, "Demand accesses per second"],
|
||||
"pread": [5, 1000, "Prefetch accesses per second"],
|
||||
"l2hits": [6, 1000, "L2ARC hits per second"],
|
||||
"l2miss": [6, 1000, "L2ARC misses per second"],
|
||||
"l2read": [6, 1000, "Total L2ARC accesses per second"],
|
||||
"l2hit%": [6, 100, "L2ARC access hit percentage"],
|
||||
"l2miss%": [7, 100, "L2ARC access miss 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"],
|
||||
}
|
||||
|
||||
v = {}
|
||||
hdr = ["time", "read", "miss", "miss%", "dmis", "dm%", "pmis", "pm%", "mmis",
|
||||
"mm%", "arcsz", "c"]
|
||||
xhdr = ["time", "mfu", "mru", "mfug", "mrug", "eskip", "mtxmis", "dread",
|
||||
"pread", "read"]
|
||||
sint = 1 # Default interval is 1 second
|
||||
count = 1 # Default count is 1
|
||||
hdr_intr = 20 # Print header every 20 lines of output
|
||||
opfile = None
|
||||
sep = " " # Default separator is 2 spaces
|
||||
version = "0.4"
|
||||
l2exist = False
|
||||
cmd = ("Usage: arcstat.py [-hvx] [-f fields] [-o file] [-s string] [interval "
|
||||
"[count]]\n")
|
||||
cur = {}
|
||||
d = {}
|
||||
out = None
|
||||
kstat = None
|
||||
|
||||
|
||||
def detailed_usage():
|
||||
sys.stderr.write("%s\n" % cmd)
|
||||
sys.stderr.write("Field definitions are as follows:\n")
|
||||
for key in cols:
|
||||
sys.stderr.write("%11s : %s\n" % (key, cols[key][2]))
|
||||
sys.stderr.write("\n")
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
def usage():
|
||||
sys.stderr.write("%s\n" % cmd)
|
||||
sys.stderr.write("\t -h : Print this help message\n")
|
||||
sys.stderr.write("\t -v : List all possible field headers and definitions"
|
||||
"\n")
|
||||
sys.stderr.write("\t -x : Print extended stats\n")
|
||||
sys.stderr.write("\t -f : Specify specific fields to print (see -v)\n")
|
||||
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("\nExamples:\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
|
||||
|
||||
prev = copy.deepcopy(cur)
|
||||
kstat_update()
|
||||
|
||||
cur = kstat
|
||||
for key in cur:
|
||||
if re.match(key, "class"):
|
||||
continue
|
||||
if key in prev:
|
||||
d[key] = cur[key] - prev[key]
|
||||
else:
|
||||
d[key] = cur[key]
|
||||
|
||||
|
||||
def prettynum(sz, scale, num=0):
|
||||
suffix = [' ', 'K', 'M', 'G', 'T', 'P', 'E', 'Z']
|
||||
index = 0
|
||||
save = 0
|
||||
|
||||
# Special case for date field
|
||||
if scale == -1:
|
||||
return "%s" % num
|
||||
|
||||
# Rounding error, return 0
|
||||
elif 0 < num < 1:
|
||||
num = 0
|
||||
|
||||
while num > scale and index < 5:
|
||||
save = num
|
||||
num = num / scale
|
||||
index += 1
|
||||
|
||||
if index == 0:
|
||||
return "%*d" % (sz, num)
|
||||
|
||||
if (save / scale) < 10:
|
||||
return "%*.1f%s" % (sz - 1, num, suffix[index])
|
||||
else:
|
||||
return "%*d%s" % (sz - 1, num, suffix[index])
|
||||
|
||||
|
||||
def print_values():
|
||||
global hdr
|
||||
global sep
|
||||
global v
|
||||
|
||||
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()
|
||||
|
||||
|
||||
def print_header():
|
||||
global hdr
|
||||
global sep
|
||||
|
||||
for col in hdr:
|
||||
sys.stdout.write("%*s%s" % (cols[col][0], col, sep))
|
||||
sys.stdout.write("\n")
|
||||
|
||||
|
||||
def get_terminal_lines():
|
||||
try:
|
||||
import fcntl
|
||||
import termios
|
||||
import struct
|
||||
data = fcntl.ioctl(sys.stdout.fileno(), termios.TIOCGWINSZ, '1234')
|
||||
sz = struct.unpack('hh', data)
|
||||
return sz[0]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def update_hdr_intr():
|
||||
global hdr_intr
|
||||
|
||||
lines = get_terminal_lines()
|
||||
if lines and lines > 3:
|
||||
hdr_intr = lines - 3
|
||||
|
||||
|
||||
def resize_handler(signum, frame):
|
||||
update_hdr_intr()
|
||||
|
||||
|
||||
def init():
|
||||
global sint
|
||||
global count
|
||||
global hdr
|
||||
global xhdr
|
||||
global opfile
|
||||
global sep
|
||||
global out
|
||||
global l2exist
|
||||
|
||||
desired_cols = None
|
||||
xflag = False
|
||||
hflag = False
|
||||
vflag = False
|
||||
i = 1
|
||||
|
||||
try:
|
||||
opts, args = getopt.getopt(
|
||||
sys.argv[1:],
|
||||
"xo:hvs:f:",
|
||||
[
|
||||
"extended",
|
||||
"outfile",
|
||||
"help",
|
||||
"verbose",
|
||||
"separator",
|
||||
"columns"
|
||||
]
|
||||
)
|
||||
except getopt.error as msg:
|
||||
sys.stderr.write("Error: %s\n" % str(msg))
|
||||
usage()
|
||||
opts = None
|
||||
|
||||
for opt, arg in opts:
|
||||
if opt in ('-x', '--extended'):
|
||||
xflag = True
|
||||
if opt in ('-o', '--outfile'):
|
||||
opfile = arg
|
||||
i += 1
|
||||
if opt in ('-h', '--help'):
|
||||
hflag = True
|
||||
if opt in ('-v', '--verbose'):
|
||||
vflag = True
|
||||
if opt in ('-s', '--separator'):
|
||||
sep = arg
|
||||
i += 1
|
||||
if opt in ('-f', '--columns'):
|
||||
desired_cols = arg
|
||||
i += 1
|
||||
i += 1
|
||||
|
||||
argv = sys.argv[i:]
|
||||
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()
|
||||
|
||||
if vflag:
|
||||
detailed_usage()
|
||||
|
||||
if xflag:
|
||||
hdr = xhdr
|
||||
|
||||
update_hdr_intr()
|
||||
|
||||
# check if L2ARC exists
|
||||
snap_stats()
|
||||
l2_size = cur.get("l2_size")
|
||||
if l2_size:
|
||||
l2exist = True
|
||||
|
||||
if desired_cols:
|
||||
hdr = desired_cols.split(",")
|
||||
|
||||
invalid = []
|
||||
incompat = []
|
||||
for ele in hdr:
|
||||
if ele not in cols:
|
||||
invalid.append(ele)
|
||||
elif not l2exist and ele.startswith("l2"):
|
||||
sys.stdout.write("No L2ARC Here\n%s\n" % ele)
|
||||
incompat.append(ele)
|
||||
|
||||
if len(invalid) > 0:
|
||||
sys.stderr.write("Invalid column definition! -- %s\n" % invalid)
|
||||
usage()
|
||||
|
||||
if len(incompat) > 0:
|
||||
sys.stderr.write("Incompatible field specified! -- %s\n" %
|
||||
incompat)
|
||||
usage()
|
||||
|
||||
if opfile:
|
||||
try:
|
||||
out = open(opfile, "w")
|
||||
sys.stdout = out
|
||||
|
||||
except IOError:
|
||||
sys.stderr.write("Cannot open %s for writing\n" % opfile)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def calculate():
|
||||
global d
|
||||
global v
|
||||
global l2exist
|
||||
|
||||
v = dict()
|
||||
v["time"] = time.strftime("%H:%M:%S", time.localtime())
|
||||
v["hits"] = d["hits"] / sint
|
||||
v["miss"] = d["misses"] / sint
|
||||
v["read"] = v["hits"] + v["miss"]
|
||||
v["hit%"] = 100 * v["hits"] / v["read"] if v["read"] > 0 else 0
|
||||
v["miss%"] = 100 - v["hit%"] if v["read"] > 0 else 0
|
||||
|
||||
v["dhit"] = (d["demand_data_hits"] + d["demand_metadata_hits"]) / sint
|
||||
v["dmis"] = (d["demand_data_misses"] + d["demand_metadata_misses"]) / sint
|
||||
|
||||
v["dread"] = v["dhit"] + v["dmis"]
|
||||
v["dh%"] = 100 * v["dhit"] / v["dread"] if v["dread"] > 0 else 0
|
||||
v["dm%"] = 100 - v["dh%"] if v["dread"] > 0 else 0
|
||||
|
||||
v["phit"] = (d["prefetch_data_hits"] + d["prefetch_metadata_hits"]) / sint
|
||||
v["pmis"] = (d["prefetch_data_misses"] +
|
||||
d["prefetch_metadata_misses"]) / sint
|
||||
|
||||
v["pread"] = v["phit"] + v["pmis"]
|
||||
v["ph%"] = 100 * v["phit"] / v["pread"] if v["pread"] > 0 else 0
|
||||
v["pm%"] = 100 - v["ph%"] if v["pread"] > 0 else 0
|
||||
|
||||
v["mhit"] = (d["prefetch_metadata_hits"] +
|
||||
d["demand_metadata_hits"]) / sint
|
||||
v["mmis"] = (d["prefetch_metadata_misses"] +
|
||||
d["demand_metadata_misses"]) / sint
|
||||
|
||||
v["mread"] = v["mhit"] + v["mmis"]
|
||||
v["mh%"] = 100 * v["mhit"] / v["mread"] if v["mread"] > 0 else 0
|
||||
v["mm%"] = 100 - v["mh%"] if v["mread"] > 0 else 0
|
||||
|
||||
v["arcsz"] = 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["mtxmis"] = d["mutex_miss"] / sint
|
||||
|
||||
if l2exist:
|
||||
v["l2hits"] = d["l2_hits"] / sint
|
||||
v["l2miss"] = d["l2_misses"] / sint
|
||||
v["l2read"] = v["l2hits"] + v["l2miss"]
|
||||
v["l2hit%"] = 100 * v["l2hits"] / v["l2read"] if v["l2read"] > 0 else 0
|
||||
|
||||
v["l2miss%"] = 100 - v["l2hit%"] if v["l2read"] > 0 else 0
|
||||
v["l2asize"] = cur["l2_asize"]
|
||||
v["l2size"] = cur["l2_size"]
|
||||
v["l2bytes"] = d["l2_read_bytes"] / sint
|
||||
|
||||
|
||||
def main():
|
||||
global sint
|
||||
global count
|
||||
global hdr_intr
|
||||
|
||||
i = 0
|
||||
count_flag = 0
|
||||
|
||||
init()
|
||||
if count > 0:
|
||||
count_flag = 1
|
||||
|
||||
signal(SIGINT, SIG_DFL)
|
||||
signal(SIGWINCH, resize_handler)
|
||||
while True:
|
||||
if i == 0:
|
||||
print_header()
|
||||
|
||||
snap_stats()
|
||||
calculate()
|
||||
print_values()
|
||||
|
||||
if count_flag == 1:
|
||||
if count <= 1:
|
||||
break
|
||||
count -= 1
|
||||
|
||||
i = 0 if i >= hdr_intr else i + 1
|
||||
time.sleep(sint)
|
||||
|
||||
if out:
|
||||
out.close()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
-684
@@ -1,684 +0,0 @@
|
||||
#!/usr/bin/env @PYTHON_SHEBANG@
|
||||
#
|
||||
# Print out statistics for all cached dmu buffers. This information
|
||||
# is available through the dbufs kstat and may be post-processed as
|
||||
# needed by the script.
|
||||
#
|
||||
# CDDL HEADER START
|
||||
#
|
||||
# The contents of this file are subject to the terms of the
|
||||
# Common Development and Distribution License, Version 1.0 only
|
||||
# (the "License"). You may not use this file except in compliance
|
||||
# with the License.
|
||||
#
|
||||
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
# or https://opensource.org/licenses/CDDL-1.0.
|
||||
# See the License for the specific language governing permissions
|
||||
# and limitations under the License.
|
||||
#
|
||||
# When distributing Covered Code, include this CDDL HEADER in each
|
||||
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
# If applicable, add the following below this CDDL HEADER, with the
|
||||
# fields enclosed by brackets "[]" replaced with your own identifying
|
||||
# information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
#
|
||||
# CDDL HEADER END
|
||||
#
|
||||
# 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",
|
||||
"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"]
|
||||
bincompat = ["cached", "direct", "indirect", "bonus", "spill"]
|
||||
|
||||
dhdr = ["pool", "objset", "object", "dtype", "cached"]
|
||||
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",
|
||||
"mru", "gmru", "mfu", "gmfu", "l2", "l2_dattr", "l2_asize",
|
||||
"l2_comp", "aholds"]
|
||||
|
||||
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",
|
||||
"access", "mru", "gmru", "mfu", "gmfu", "l2", "l2_dattr",
|
||||
"l2_asize", "l2_comp", "aholds", "btype", "data_bs", "meta_bs",
|
||||
"bsize", "lvls", "dholds", "blocks", "dsize"]
|
||||
|
||||
cols = {
|
||||
# hdr: [size, scale, description]
|
||||
"pool": [15, -1, "pool name"],
|
||||
"objset": [6, -1, "dataset identification number"],
|
||||
"object": [10, -1, "object number"],
|
||||
"level": [5, -1, "indirection level of buffer"],
|
||||
"blkid": [8, -1, "block number of buffer"],
|
||||
"offset": [12, 1024, "offset in object of buffer"],
|
||||
"dbsize": [7, 1024, "size of buffer"],
|
||||
"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"],
|
||||
"count": [5, -1, "ARC data count"],
|
||||
"asize": [7, 1024, "size of this ARC buffer"],
|
||||
"access": [10, -1, "time this ARC buffer was last accessed"],
|
||||
"mru": [5, 1000, "hits while on the ARC's MRU list"],
|
||||
"gmru": [5, 1000, "hits while on the ARC's MRU ghost list"],
|
||||
"mfu": [5, 1000, "hits while on the ARC's MFU list"],
|
||||
"gmfu": [5, 1000, "hits while on the ARC's MFU ghost list"],
|
||||
"l2": [5, 1000, "hits while on the L2ARC"],
|
||||
"l2_dattr": [8, -1, "L2ARC disk address/offset"],
|
||||
"l2_asize": [8, 1024, "L2ARC alloc'd size (depending on compression)"],
|
||||
"l2_comp": [21, -1, "L2ARC compression algorithm for buffer"],
|
||||
"aholds": [6, 1000, "number of holds on this ARC buffer"],
|
||||
"dtype": [27, -1, "dnode type"],
|
||||
"btype": [27, -1, "bonus buffer type"],
|
||||
"data_bs": [7, 1024, "data block size"],
|
||||
"meta_bs": [7, 1024, "metadata block size"],
|
||||
"bsize": [6, 1024, "bonus buffer size"],
|
||||
"lvls": [6, -1, "number of indirection levels"],
|
||||
"dholds": [6, 1000, "number of holds on dnode"],
|
||||
"blocks": [8, 1000, "number of allocated blocks"],
|
||||
"dsize": [12, 1024, "size of dnode"],
|
||||
"cached": [6, 1024, "bytes cached for all blocks"],
|
||||
"direct": [6, 1024, "bytes cached for direct blocks"],
|
||||
"indirect": [8, 1024, "bytes cached for indirect blocks"],
|
||||
"bonus": [5, 1024, "bytes cached for bonus buffer"],
|
||||
"spill": [5, 1024, "bytes cached for spill block"],
|
||||
}
|
||||
|
||||
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")
|
||||
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:
|
||||
sys.stderr.write("\t")
|
||||
elif cnt > 8:
|
||||
sys.stderr.write(",\n\t")
|
||||
cnt = 0
|
||||
else:
|
||||
sys.stderr.write(", ")
|
||||
|
||||
sys.stderr.write("%s" % key)
|
||||
cnt += 1
|
||||
|
||||
sys.stderr.write("\n\n")
|
||||
|
||||
|
||||
def detailed_usage():
|
||||
sys.stderr.write("%s\n" % cmd)
|
||||
|
||||
sys.stderr.write("Field definitions incompatible with '-b' option:\n")
|
||||
print_incompat_helper(bincompat)
|
||||
|
||||
sys.stderr.write("Field definitions incompatible with '-d' option:\n")
|
||||
print_incompat_helper(dincompat)
|
||||
|
||||
sys.stderr.write("Field definitions incompatible with '-t' option:\n")
|
||||
print_incompat_helper(tincompat)
|
||||
|
||||
sys.stderr.write("Field definitions are as follows:\n")
|
||||
for key in sorted(cols.keys()):
|
||||
sys.stderr.write("%11s : %s\n" % (key, cols[key][2]))
|
||||
sys.stderr.write("\n")
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
def usage():
|
||||
sys.stderr.write("%s\n" % cmd)
|
||||
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")
|
||||
sys.stderr.write("\t -v : List all possible field headers and definitions"
|
||||
"\n")
|
||||
sys.stderr.write("\t -x : Print extended stats\n")
|
||||
sys.stderr.write("\t -i : Redirect input from the specified file\n")
|
||||
sys.stderr.write("\t -f : Specify specific fields to print (see -v)\n")
|
||||
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("\n")
|
||||
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def prettynum(sz, scale, num=0):
|
||||
global raw
|
||||
|
||||
suffix = [' ', 'K', 'M', 'G', 'T', 'P', 'E', 'Z']
|
||||
index = 0
|
||||
save = 0
|
||||
|
||||
if raw or scale == -1:
|
||||
return "%*s" % (sz, num)
|
||||
|
||||
# Rounding error, return 0
|
||||
elif 0 < num < 1:
|
||||
num = 0
|
||||
|
||||
while num > scale and index < 5:
|
||||
save = num
|
||||
num = num / scale
|
||||
index += 1
|
||||
|
||||
if index == 0:
|
||||
return "%*d" % (sz, num)
|
||||
|
||||
if (save / scale) < 10:
|
||||
return "%*.1f%s" % (sz - 1, num, suffix[index])
|
||||
else:
|
||||
return "%*d%s" % (sz - 1, num, suffix[index])
|
||||
|
||||
|
||||
def print_values(v):
|
||||
global hdr
|
||||
global sep
|
||||
|
||||
try:
|
||||
for col in hdr:
|
||||
sys.stdout.write("%s%s" % (
|
||||
prettynum(cols[col][0], cols[col][1], v[col]), sep))
|
||||
sys.stdout.write("\n")
|
||||
except IOError as e:
|
||||
if e.errno == errno.EPIPE:
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def print_header():
|
||||
global hdr
|
||||
global sep
|
||||
|
||||
try:
|
||||
for col in hdr:
|
||||
sys.stdout.write("%*s%s" % (cols[col][0], col, sep))
|
||||
sys.stdout.write("\n")
|
||||
except IOError as e:
|
||||
if e.errno == errno.EPIPE:
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def get_typestring(t):
|
||||
ot_strings = [
|
||||
"DMU_OT_NONE",
|
||||
# general:
|
||||
"DMU_OT_OBJECT_DIRECTORY",
|
||||
"DMU_OT_OBJECT_ARRAY",
|
||||
"DMU_OT_PACKED_NVLIST",
|
||||
"DMU_OT_PACKED_NVLIST_SIZE",
|
||||
"DMU_OT_BPOBJ",
|
||||
"DMU_OT_BPOBJ_HDR",
|
||||
# spa:
|
||||
"DMU_OT_SPACE_MAP_HEADER",
|
||||
"DMU_OT_SPACE_MAP",
|
||||
# zil:
|
||||
"DMU_OT_INTENT_LOG",
|
||||
# dmu:
|
||||
"DMU_OT_DNODE",
|
||||
"DMU_OT_OBJSET",
|
||||
# dsl:
|
||||
"DMU_OT_DSL_DIR",
|
||||
"DMU_OT_DSL_DIR_CHILD_MAP",
|
||||
"DMU_OT_DSL_DS_SNAP_MAP",
|
||||
"DMU_OT_DSL_PROPS",
|
||||
"DMU_OT_DSL_DATASET",
|
||||
# zpl:
|
||||
"DMU_OT_ZNODE",
|
||||
"DMU_OT_OLDACL",
|
||||
"DMU_OT_PLAIN_FILE_CONTENTS",
|
||||
"DMU_OT_DIRECTORY_CONTENTS",
|
||||
"DMU_OT_MASTER_NODE",
|
||||
"DMU_OT_UNLINKED_SET",
|
||||
# zvol:
|
||||
"DMU_OT_ZVOL",
|
||||
"DMU_OT_ZVOL_PROP",
|
||||
# other; for testing only!
|
||||
"DMU_OT_PLAIN_OTHER",
|
||||
"DMU_OT_UINT64_OTHER",
|
||||
"DMU_OT_ZAP_OTHER",
|
||||
# new object types:
|
||||
"DMU_OT_ERROR_LOG",
|
||||
"DMU_OT_SPA_HISTORY",
|
||||
"DMU_OT_SPA_HISTORY_OFFSETS",
|
||||
"DMU_OT_POOL_PROPS",
|
||||
"DMU_OT_DSL_PERMS",
|
||||
"DMU_OT_ACL",
|
||||
"DMU_OT_SYSACL",
|
||||
"DMU_OT_FUID",
|
||||
"DMU_OT_FUID_SIZE",
|
||||
"DMU_OT_NEXT_CLONES",
|
||||
"DMU_OT_SCAN_QUEUE",
|
||||
"DMU_OT_USERGROUP_USED",
|
||||
"DMU_OT_USERGROUP_QUOTA",
|
||||
"DMU_OT_USERREFS",
|
||||
"DMU_OT_DDT_ZAP",
|
||||
"DMU_OT_DDT_STATS",
|
||||
"DMU_OT_SA",
|
||||
"DMU_OT_SA_MASTER_NODE",
|
||||
"DMU_OT_SA_ATTR_REGISTRATION",
|
||||
"DMU_OT_SA_ATTR_LAYOUTS",
|
||||
"DMU_OT_SCAN_XLATE",
|
||||
"DMU_OT_DEDUP",
|
||||
"DMU_OT_DEADLIST",
|
||||
"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)"
|
||||
|
||||
|
||||
def get_compstring(c):
|
||||
comp_strings = ["ZIO_COMPRESS_INHERIT", "ZIO_COMPRESS_ON",
|
||||
"ZIO_COMPRESS_OFF", "ZIO_COMPRESS_LZJB",
|
||||
"ZIO_COMPRESS_EMPTY", "ZIO_COMPRESS_GZIP_1",
|
||||
"ZIO_COMPRESS_GZIP_2", "ZIO_COMPRESS_GZIP_3",
|
||||
"ZIO_COMPRESS_GZIP_4", "ZIO_COMPRESS_GZIP_5",
|
||||
"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"]
|
||||
|
||||
# If "-rr" option is used, don't convert to string representation
|
||||
if raw > 1:
|
||||
return "%i" % c
|
||||
|
||||
try:
|
||||
return comp_strings[c]
|
||||
except IndexError:
|
||||
return "%i" % c
|
||||
|
||||
|
||||
def parse_line(line, labels):
|
||||
global hdr
|
||||
|
||||
new = dict()
|
||||
val = None
|
||||
for col in hdr:
|
||||
# These are "special" fields computed in the update_dict
|
||||
# function, prevent KeyError exception on labels[col] for these.
|
||||
if col not in ['bonus', 'cached', 'direct', 'indirect', 'spill']:
|
||||
val = line[labels[col]]
|
||||
|
||||
if col in ['pool', 'flags']:
|
||||
new[col] = str(val)
|
||||
elif col in ['dtype', 'btype']:
|
||||
new[col] = get_typestring(int(val))
|
||||
elif col in ['l2_comp']:
|
||||
new[col] = get_compstring(int(val))
|
||||
else:
|
||||
new[col] = int(val)
|
||||
|
||||
return new
|
||||
|
||||
|
||||
def update_dict(d, k, line, labels):
|
||||
pool = line[labels['pool']]
|
||||
objset = line[labels['objset']]
|
||||
key = line[labels[k]]
|
||||
|
||||
dbsize = int(line[labels['dbsize']])
|
||||
blkid = int(line[labels['blkid']])
|
||||
level = int(line[labels['level']])
|
||||
|
||||
if pool not in d:
|
||||
d[pool] = dict()
|
||||
|
||||
if objset not in d[pool]:
|
||||
d[pool][objset] = dict()
|
||||
|
||||
if key not in d[pool][objset]:
|
||||
d[pool][objset][key] = parse_line(line, labels)
|
||||
d[pool][objset][key]['bonus'] = 0
|
||||
d[pool][objset][key]['cached'] = 0
|
||||
d[pool][objset][key]['direct'] = 0
|
||||
d[pool][objset][key]['indirect'] = 0
|
||||
d[pool][objset][key]['spill'] = 0
|
||||
|
||||
d[pool][objset][key]['cached'] += dbsize
|
||||
|
||||
if blkid == -1:
|
||||
d[pool][objset][key]['bonus'] += dbsize
|
||||
elif blkid == -2:
|
||||
d[pool][objset][key]['spill'] += dbsize
|
||||
else:
|
||||
if level == 0:
|
||||
d[pool][objset][key]['direct'] += dbsize
|
||||
else:
|
||||
d[pool][objset][key]['indirect'] += dbsize
|
||||
|
||||
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()
|
||||
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)
|
||||
|
||||
|
||||
def dnodes_build_dict(filehandle):
|
||||
labels = dict()
|
||||
dnodes = dict()
|
||||
|
||||
# First 3 lines are header information, skip the first two
|
||||
for i in range(2):
|
||||
next(filehandle)
|
||||
|
||||
# The third line contains the labels and index locations
|
||||
for i, v in enumerate(next(filehandle).split()):
|
||||
labels[v] = i
|
||||
|
||||
# The rest of the file is buffer information
|
||||
for line in filehandle:
|
||||
update_dict(dnodes, 'object', line.split(), labels)
|
||||
|
||||
return dnodes
|
||||
|
||||
|
||||
def types_build_dict(filehandle):
|
||||
labels = dict()
|
||||
types = dict()
|
||||
|
||||
# First 3 lines are header information, skip the first two
|
||||
for i in range(2):
|
||||
next(filehandle)
|
||||
|
||||
# The third line contains the labels and index locations
|
||||
for i, v in enumerate(next(filehandle).split()):
|
||||
labels[v] = i
|
||||
|
||||
# The rest of the file is buffer information
|
||||
for line in filehandle:
|
||||
update_dict(types, 'dtype', line.split(), labels)
|
||||
|
||||
return types
|
||||
|
||||
|
||||
def buffers_print_all(filehandle, filters, noheader):
|
||||
labels = dict()
|
||||
|
||||
# First 3 lines are header information, skip the first two
|
||||
for i in range(2):
|
||||
next(filehandle)
|
||||
|
||||
# The third line contains the labels and index locations
|
||||
for i, v in enumerate(next(filehandle).split()):
|
||||
labels[v] = i
|
||||
|
||||
if not noheader:
|
||||
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)
|
||||
|
||||
|
||||
def main():
|
||||
global hdr
|
||||
global sep
|
||||
global raw
|
||||
|
||||
desired_cols = None
|
||||
bflag = False
|
||||
dflag = False
|
||||
hflag = False
|
||||
ifile = None
|
||||
ofile = None
|
||||
tflag = False
|
||||
vflag = False
|
||||
xflag = False
|
||||
nflag = False
|
||||
filters = dict()
|
||||
|
||||
try:
|
||||
opts, args = getopt.getopt(
|
||||
sys.argv[1:],
|
||||
"bdf:hi:o:rs:tvxF:n",
|
||||
[
|
||||
"buffers",
|
||||
"dnodes",
|
||||
"columns",
|
||||
"help",
|
||||
"infile",
|
||||
"outfile",
|
||||
"separator",
|
||||
"types",
|
||||
"verbose",
|
||||
"extended",
|
||||
"filter"
|
||||
]
|
||||
)
|
||||
except getopt.error:
|
||||
usage()
|
||||
opts = None
|
||||
|
||||
for opt, arg in opts:
|
||||
if opt in ('-b', '--buffers'):
|
||||
bflag = True
|
||||
if opt in ('-d', '--dnodes'):
|
||||
dflag = True
|
||||
if opt in ('-f', '--columns'):
|
||||
desired_cols = arg
|
||||
if opt in ('-h', '--help'):
|
||||
hflag = True
|
||||
if opt in ('-i', '--infile'):
|
||||
ifile = arg
|
||||
if opt in ('-o', '--outfile'):
|
||||
ofile = arg
|
||||
if opt in ('-r', '--raw'):
|
||||
raw += 1
|
||||
if opt in ('-s', '--separator'):
|
||||
sep = arg
|
||||
if opt in ('-t', '--types'):
|
||||
tflag = True
|
||||
if opt in ('-v', '--verbose'):
|
||||
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()
|
||||
|
||||
if vflag:
|
||||
detailed_usage()
|
||||
|
||||
# Ensure at most only one of b, d, or t flags are set
|
||||
if (bflag and dflag) or (bflag and tflag) or (dflag and tflag):
|
||||
usage()
|
||||
|
||||
if bflag:
|
||||
hdr = bxhdr if xflag else bhdr
|
||||
elif tflag:
|
||||
hdr = txhdr if xflag else thdr
|
||||
else: # Even if dflag is False, it's the default if none set
|
||||
dflag = True
|
||||
hdr = dxhdr if xflag else dhdr
|
||||
|
||||
if desired_cols:
|
||||
hdr = desired_cols.split(",")
|
||||
|
||||
invalid = []
|
||||
incompat = []
|
||||
for ele in hdr:
|
||||
if ele not in cols:
|
||||
invalid.append(ele)
|
||||
elif ((bflag and bincompat and ele in bincompat) or
|
||||
(dflag and dincompat and ele in dincompat) or
|
||||
(tflag and tincompat and ele in tincompat)):
|
||||
incompat.append(ele)
|
||||
|
||||
if len(invalid) > 0:
|
||||
sys.stderr.write("Invalid column definition! -- %s\n" % invalid)
|
||||
usage()
|
||||
|
||||
if len(incompat) > 0:
|
||||
sys.stderr.write("Incompatible field specified! -- %s\n" %
|
||||
incompat)
|
||||
usage()
|
||||
|
||||
if ofile:
|
||||
try:
|
||||
tmp = open(ofile, "w")
|
||||
sys.stdout = tmp
|
||||
|
||||
except IOError:
|
||||
sys.stderr.write("Cannot open %s for writing\n" % ofile)
|
||||
sys.exit(1)
|
||||
|
||||
if not ifile:
|
||||
ifile = default_ifile()
|
||||
|
||||
if ifile != "-":
|
||||
try:
|
||||
tmp = open(ifile, "r")
|
||||
sys.stdin = tmp
|
||||
except IOError:
|
||||
sys.stderr.write("Cannot open %s for reading\n" % ifile)
|
||||
sys.exit(1)
|
||||
|
||||
if bflag:
|
||||
buffers_print_all(sys.stdin, filters, nflag)
|
||||
|
||||
if dflag:
|
||||
print_dict(dnodes_build_dict(sys.stdin), filters, nflag)
|
||||
|
||||
if tflag:
|
||||
print_dict(types_build_dict(sys.stdin), filters, nflag)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1 @@
|
||||
dist_bin_SCRIPTS = dbufstat.py
|
||||
Executable
+582
@@ -0,0 +1,582 @@
|
||||
#!/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
|
||||
# needed by the script.
|
||||
#
|
||||
# CDDL HEADER START
|
||||
#
|
||||
# The contents of this file are subject to the terms of the
|
||||
# Common Development and Distribution License, Version 1.0 only
|
||||
# (the "License"). You may not use this file except in compliance
|
||||
# with the License.
|
||||
#
|
||||
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
# or http://www.opensolaris.org/os/licensing.
|
||||
# See the License for the specific language governing permissions
|
||||
# and limitations under the License.
|
||||
#
|
||||
# When distributing Covered Code, include this CDDL HEADER in each
|
||||
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
# If applicable, add the following below this CDDL HEADER, with the
|
||||
# fields enclosed by brackets "[]" replaced with your own identifying
|
||||
# information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
#
|
||||
# CDDL HEADER END
|
||||
#
|
||||
# Copyright (C) 2013 Lawrence Livermore National Security, LLC.
|
||||
# Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
|
||||
#
|
||||
|
||||
import sys
|
||||
import getopt
|
||||
import errno
|
||||
|
||||
bhdr = ["pool", "objset", "object", "level", "blkid", "offset", "dbsize"]
|
||||
bxhdr = ["pool", "objset", "object", "level", "blkid", "offset", "dbsize",
|
||||
"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"]
|
||||
bincompat = ["cached", "direct", "indirect", "bonus", "spill"]
|
||||
|
||||
dhdr = ["pool", "objset", "object", "dtype", "cached"]
|
||||
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",
|
||||
"list", "atype", "flags", "count", "asize", "access",
|
||||
"mru", "gmru", "mfu", "gmfu", "l2", "l2_dattr", "l2_asize",
|
||||
"l2_comp", "aholds"]
|
||||
|
||||
thdr = ["pool", "objset", "dtype", "cached"]
|
||||
txhdr = ["pool", "objset", "dtype", "cached", "direct", "indirect",
|
||||
"bonus", "spill"]
|
||||
tincompat = ["object", "level", "blkid", "offset", "dbsize", "meta", "state",
|
||||
"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"]
|
||||
|
||||
cols = {
|
||||
# hdr: [size, scale, description]
|
||||
"pool": [15, -1, "pool name"],
|
||||
"objset": [6, -1, "dataset identification number"],
|
||||
"object": [10, -1, "object number"],
|
||||
"level": [5, -1, "indirection level of buffer"],
|
||||
"blkid": [8, -1, "block number of buffer"],
|
||||
"offset": [12, 1024, "offset in object of buffer"],
|
||||
"dbsize": [7, 1024, "size of buffer"],
|
||||
"meta": [4, -1, "is this buffer metadata?"],
|
||||
"state": [5, -1, "state of buffer (read, cached, etc)"],
|
||||
"dbholds": [7, 1000, "number of holds on buffer"],
|
||||
"list": [4, -1, "which ARC list contains this buffer"],
|
||||
"atype": [7, -1, "ARC header type (data or metadata)"],
|
||||
"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"],
|
||||
"mru": [5, 1000, "hits while on the ARC's MRU list"],
|
||||
"gmru": [5, 1000, "hits while on the ARC's MRU ghost list"],
|
||||
"mfu": [5, 1000, "hits while on the ARC's MFU list"],
|
||||
"gmfu": [5, 1000, "hits while on the ARC's MFU ghost list"],
|
||||
"l2": [5, 1000, "hits while on the L2ARC"],
|
||||
"l2_dattr": [8, -1, "L2ARC disk address/offset"],
|
||||
"l2_asize": [8, 1024, "L2ARC alloc'd size (depending on compression)"],
|
||||
"l2_comp": [21, -1, "L2ARC compression algorithm for buffer"],
|
||||
"aholds": [6, 1000, "number of holds on this ARC buffer"],
|
||||
"dtype": [27, -1, "dnode type"],
|
||||
"btype": [27, -1, "bonus buffer type"],
|
||||
"data_bs": [7, 1024, "data block size"],
|
||||
"meta_bs": [7, 1024, "metadata block size"],
|
||||
"bsize": [6, 1024, "bonus buffer size"],
|
||||
"lvls": [6, -1, "number of indirection levels"],
|
||||
"dholds": [6, 1000, "number of holds on dnode"],
|
||||
"blocks": [8, 1000, "number of allocated blocks"],
|
||||
"dsize": [12, 1024, "size of dnode"],
|
||||
"cached": [6, 1024, "bytes cached for all blocks"],
|
||||
"direct": [6, 1024, "bytes cached for direct blocks"],
|
||||
"indirect": [8, 1024, "bytes cached for indirect blocks"],
|
||||
"bonus": [5, 1024, "bytes cached for bonus buffer"],
|
||||
"spill": [5, 1024, "bytes cached for spill block"],
|
||||
}
|
||||
|
||||
hdr = None
|
||||
xhdr = None
|
||||
sep = " " # Default separator is 2 spaces
|
||||
cmd = ("Usage: dbufstat.py [-bdhrtvx] [-i file] [-f fields] [-o file] "
|
||||
"[-s string]\n")
|
||||
raw = 0
|
||||
|
||||
|
||||
def print_incompat_helper(incompat):
|
||||
cnt = 0
|
||||
for key in sorted(incompat):
|
||||
if cnt is 0:
|
||||
sys.stderr.write("\t")
|
||||
elif cnt > 8:
|
||||
sys.stderr.write(",\n\t")
|
||||
cnt = 0
|
||||
else:
|
||||
sys.stderr.write(", ")
|
||||
|
||||
sys.stderr.write("%s" % key)
|
||||
cnt += 1
|
||||
|
||||
sys.stderr.write("\n\n")
|
||||
|
||||
|
||||
def detailed_usage():
|
||||
sys.stderr.write("%s\n" % cmd)
|
||||
|
||||
sys.stderr.write("Field definitions incompatible with '-b' option:\n")
|
||||
print_incompat_helper(bincompat)
|
||||
|
||||
sys.stderr.write("Field definitions incompatible with '-d' option:\n")
|
||||
print_incompat_helper(dincompat)
|
||||
|
||||
sys.stderr.write("Field definitions incompatible with '-t' option:\n")
|
||||
print_incompat_helper(tincompat)
|
||||
|
||||
sys.stderr.write("Field definitions are as follows:\n")
|
||||
for key in sorted(cols.keys()):
|
||||
sys.stderr.write("%11s : %s\n" % (key, cols[key][2]))
|
||||
sys.stderr.write("\n")
|
||||
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
def usage():
|
||||
sys.stderr.write("%s\n" % cmd)
|
||||
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 -r : Print raw values\n")
|
||||
sys.stderr.write("\t -t : Print table of information for each dnode type"
|
||||
"\n")
|
||||
sys.stderr.write("\t -v : List all possible field headers and definitions"
|
||||
"\n")
|
||||
sys.stderr.write("\t -x : Print extended stats\n")
|
||||
sys.stderr.write("\t -i : Redirect input from the specified file\n")
|
||||
sys.stderr.write("\t -f : Specify specific fields to print (see -v)\n")
|
||||
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("\nExamples:\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)
|
||||
|
||||
|
||||
def prettynum(sz, scale, num=0):
|
||||
global raw
|
||||
|
||||
suffix = [' ', 'K', 'M', 'G', 'T', 'P', 'E', 'Z']
|
||||
index = 0
|
||||
save = 0
|
||||
|
||||
if raw or scale == -1:
|
||||
return "%*s" % (sz, num)
|
||||
|
||||
# Rounding error, return 0
|
||||
elif 0 < num < 1:
|
||||
num = 0
|
||||
|
||||
while num > scale and index < 5:
|
||||
save = num
|
||||
num = num / scale
|
||||
index += 1
|
||||
|
||||
if index == 0:
|
||||
return "%*d" % (sz, num)
|
||||
|
||||
if (save / scale) < 10:
|
||||
return "%*.1f%s" % (sz - 1, num, suffix[index])
|
||||
else:
|
||||
return "%*d%s" % (sz - 1, num, suffix[index])
|
||||
|
||||
|
||||
def print_values(v):
|
||||
global hdr
|
||||
global sep
|
||||
|
||||
try:
|
||||
for col in hdr:
|
||||
sys.stdout.write("%s%s" % (
|
||||
prettynum(cols[col][0], cols[col][1], v[col]), sep))
|
||||
sys.stdout.write("\n")
|
||||
except IOError as e:
|
||||
if e.errno == errno.EPIPE:
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def print_header():
|
||||
global hdr
|
||||
global sep
|
||||
|
||||
try:
|
||||
for col in hdr:
|
||||
sys.stdout.write("%*s%s" % (cols[col][0], col, sep))
|
||||
sys.stdout.write("\n")
|
||||
except IOError as e:
|
||||
if e.errno == errno.EPIPE:
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
def get_typestring(t):
|
||||
type_strings = ["DMU_OT_NONE",
|
||||
# general:
|
||||
"DMU_OT_OBJECT_DIRECTORY",
|
||||
"DMU_OT_OBJECT_ARRAY",
|
||||
"DMU_OT_PACKED_NVLIST",
|
||||
"DMU_OT_PACKED_NVLIST_SIZE",
|
||||
"DMU_OT_BPOBJ",
|
||||
"DMU_OT_BPOBJ_HDR",
|
||||
# spa:
|
||||
"DMU_OT_SPACE_MAP_HEADER",
|
||||
"DMU_OT_SPACE_MAP",
|
||||
# zil:
|
||||
"DMU_OT_INTENT_LOG",
|
||||
# dmu:
|
||||
"DMU_OT_DNODE",
|
||||
"DMU_OT_OBJSET",
|
||||
# dsl:
|
||||
"DMU_OT_DSL_DIR",
|
||||
"DMU_OT_DSL_DIR_CHILD_MAP",
|
||||
"DMU_OT_DSL_DS_SNAP_MAP",
|
||||
"DMU_OT_DSL_PROPS",
|
||||
"DMU_OT_DSL_DATASET",
|
||||
# zpl:
|
||||
"DMU_OT_ZNODE",
|
||||
"DMU_OT_OLDACL",
|
||||
"DMU_OT_PLAIN_FILE_CONTENTS",
|
||||
"DMU_OT_DIRECTORY_CONTENTS",
|
||||
"DMU_OT_MASTER_NODE",
|
||||
"DMU_OT_UNLINKED_SET",
|
||||
# zvol:
|
||||
"DMU_OT_ZVOL",
|
||||
"DMU_OT_ZVOL_PROP",
|
||||
# other; for testing only!
|
||||
"DMU_OT_PLAIN_OTHER",
|
||||
"DMU_OT_UINT64_OTHER",
|
||||
"DMU_OT_ZAP_OTHER",
|
||||
# new object types:
|
||||
"DMU_OT_ERROR_LOG",
|
||||
"DMU_OT_SPA_HISTORY",
|
||||
"DMU_OT_SPA_HISTORY_OFFSETS",
|
||||
"DMU_OT_POOL_PROPS",
|
||||
"DMU_OT_DSL_PERMS",
|
||||
"DMU_OT_ACL",
|
||||
"DMU_OT_SYSACL",
|
||||
"DMU_OT_FUID",
|
||||
"DMU_OT_FUID_SIZE",
|
||||
"DMU_OT_NEXT_CLONES",
|
||||
"DMU_OT_SCAN_QUEUE",
|
||||
"DMU_OT_USERGROUP_USED",
|
||||
"DMU_OT_USERGROUP_QUOTA",
|
||||
"DMU_OT_USERREFS",
|
||||
"DMU_OT_DDT_ZAP",
|
||||
"DMU_OT_DDT_STATS",
|
||||
"DMU_OT_SA",
|
||||
"DMU_OT_SA_MASTER_NODE",
|
||||
"DMU_OT_SA_ATTR_REGISTRATION",
|
||||
"DMU_OT_SA_ATTR_LAYOUTS",
|
||||
"DMU_OT_SCAN_XLATE",
|
||||
"DMU_OT_DEDUP",
|
||||
"DMU_OT_DEADLIST",
|
||||
"DMU_OT_DEADLIST_HDR",
|
||||
"DMU_OT_DSL_CLONES",
|
||||
"DMU_OT_BPOBJ_SUBOBJ"]
|
||||
|
||||
# If "-rr" option is used, don't convert to string representation
|
||||
if raw > 1:
|
||||
return "%i" % t
|
||||
|
||||
try:
|
||||
return type_strings[t]
|
||||
except IndexError:
|
||||
return "%i" % t
|
||||
|
||||
|
||||
def get_compstring(c):
|
||||
comp_strings = ["ZIO_COMPRESS_INHERIT", "ZIO_COMPRESS_ON",
|
||||
"ZIO_COMPRESS_OFF", "ZIO_COMPRESS_LZJB",
|
||||
"ZIO_COMPRESS_EMPTY", "ZIO_COMPRESS_GZIP_1",
|
||||
"ZIO_COMPRESS_GZIP_2", "ZIO_COMPRESS_GZIP_3",
|
||||
"ZIO_COMPRESS_GZIP_4", "ZIO_COMPRESS_GZIP_5",
|
||||
"ZIO_COMPRESS_GZIP_6", "ZIO_COMPRESS_GZIP_7",
|
||||
"ZIO_COMPRESS_GZIP_8", "ZIO_COMPRESS_GZIP_9",
|
||||
"ZIO_COMPRESS_ZLE", "ZIO_COMPRESS_LZ4",
|
||||
"ZIO_COMPRESS_FUNCTION"]
|
||||
|
||||
# If "-rr" option is used, don't convert to string representation
|
||||
if raw > 1:
|
||||
return "%i" % c
|
||||
|
||||
try:
|
||||
return comp_strings[c]
|
||||
except IndexError:
|
||||
return "%i" % c
|
||||
|
||||
|
||||
def parse_line(line, labels):
|
||||
global hdr
|
||||
|
||||
new = dict()
|
||||
val = None
|
||||
for col in hdr:
|
||||
# These are "special" fields computed in the update_dict
|
||||
# function, prevent KeyError exception on labels[col] for these.
|
||||
if col not in ['bonus', 'cached', 'direct', 'indirect', 'spill']:
|
||||
val = line[labels[col]]
|
||||
|
||||
if col in ['pool', 'flags']:
|
||||
new[col] = str(val)
|
||||
elif col in ['dtype', 'btype']:
|
||||
new[col] = get_typestring(int(val))
|
||||
elif col in ['l2_comp']:
|
||||
new[col] = get_compstring(int(val))
|
||||
else:
|
||||
new[col] = int(val)
|
||||
|
||||
return new
|
||||
|
||||
|
||||
def update_dict(d, k, line, labels):
|
||||
pool = line[labels['pool']]
|
||||
objset = line[labels['objset']]
|
||||
key = line[labels[k]]
|
||||
|
||||
dbsize = int(line[labels['dbsize']])
|
||||
blkid = int(line[labels['blkid']])
|
||||
level = int(line[labels['level']])
|
||||
|
||||
if pool not in d:
|
||||
d[pool] = dict()
|
||||
|
||||
if objset not in d[pool]:
|
||||
d[pool][objset] = dict()
|
||||
|
||||
if key not in d[pool][objset]:
|
||||
d[pool][objset][key] = parse_line(line, labels)
|
||||
d[pool][objset][key]['bonus'] = 0
|
||||
d[pool][objset][key]['cached'] = 0
|
||||
d[pool][objset][key]['direct'] = 0
|
||||
d[pool][objset][key]['indirect'] = 0
|
||||
d[pool][objset][key]['spill'] = 0
|
||||
|
||||
d[pool][objset][key]['cached'] += dbsize
|
||||
|
||||
if blkid == -1:
|
||||
d[pool][objset][key]['bonus'] += dbsize
|
||||
elif blkid == -2:
|
||||
d[pool][objset][key]['spill'] += dbsize
|
||||
else:
|
||||
if level == 0:
|
||||
d[pool][objset][key]['direct'] += dbsize
|
||||
else:
|
||||
d[pool][objset][key]['indirect'] += dbsize
|
||||
|
||||
return d
|
||||
|
||||
|
||||
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()):
|
||||
print_values(v)
|
||||
|
||||
|
||||
def dnodes_build_dict(filehandle):
|
||||
labels = dict()
|
||||
dnodes = dict()
|
||||
|
||||
# First 3 lines are header information, skip the first two
|
||||
for i in range(2):
|
||||
next(filehandle)
|
||||
|
||||
# The third line contains the labels and index locations
|
||||
for i, v in enumerate(next(filehandle).split()):
|
||||
labels[v] = i
|
||||
|
||||
# The rest of the file is buffer information
|
||||
for line in filehandle:
|
||||
update_dict(dnodes, 'object', line.split(), labels)
|
||||
|
||||
return dnodes
|
||||
|
||||
|
||||
def types_build_dict(filehandle):
|
||||
labels = dict()
|
||||
types = dict()
|
||||
|
||||
# First 3 lines are header information, skip the first two
|
||||
for i in range(2):
|
||||
next(filehandle)
|
||||
|
||||
# The third line contains the labels and index locations
|
||||
for i, v in enumerate(next(filehandle).split()):
|
||||
labels[v] = i
|
||||
|
||||
# The rest of the file is buffer information
|
||||
for line in filehandle:
|
||||
update_dict(types, 'dtype', line.split(), labels)
|
||||
|
||||
return types
|
||||
|
||||
|
||||
def buffers_print_all(filehandle):
|
||||
labels = dict()
|
||||
|
||||
# First 3 lines are header information, skip the first two
|
||||
for i in range(2):
|
||||
next(filehandle)
|
||||
|
||||
# The third line contains the labels and index locations
|
||||
for i, v in enumerate(next(filehandle).split()):
|
||||
labels[v] = i
|
||||
|
||||
print_header()
|
||||
|
||||
# The rest of the file is buffer information
|
||||
for line in filehandle:
|
||||
print_values(parse_line(line.split(), labels))
|
||||
|
||||
|
||||
def main():
|
||||
global hdr
|
||||
global sep
|
||||
global raw
|
||||
|
||||
desired_cols = None
|
||||
bflag = False
|
||||
dflag = False
|
||||
hflag = False
|
||||
ifile = None
|
||||
ofile = None
|
||||
tflag = False
|
||||
vflag = False
|
||||
xflag = False
|
||||
|
||||
try:
|
||||
opts, args = getopt.getopt(
|
||||
sys.argv[1:],
|
||||
"bdf:hi:o:rs:tvx",
|
||||
[
|
||||
"buffers",
|
||||
"dnodes",
|
||||
"columns",
|
||||
"help",
|
||||
"infile",
|
||||
"outfile",
|
||||
"separator",
|
||||
"types",
|
||||
"verbose",
|
||||
"extended"
|
||||
]
|
||||
)
|
||||
except getopt.error:
|
||||
usage()
|
||||
opts = None
|
||||
|
||||
for opt, arg in opts:
|
||||
if opt in ('-b', '--buffers'):
|
||||
bflag = True
|
||||
if opt in ('-d', '--dnodes'):
|
||||
dflag = True
|
||||
if opt in ('-f', '--columns'):
|
||||
desired_cols = arg
|
||||
if opt in ('-h', '--help'):
|
||||
hflag = True
|
||||
if opt in ('-i', '--infile'):
|
||||
ifile = arg
|
||||
if opt in ('-o', '--outfile'):
|
||||
ofile = arg
|
||||
if opt in ('-r', '--raw'):
|
||||
raw += 1
|
||||
if opt in ('-s', '--separator'):
|
||||
sep = arg
|
||||
if opt in ('-t', '--types'):
|
||||
tflag = True
|
||||
if opt in ('-v', '--verbose'):
|
||||
vflag = True
|
||||
if opt in ('-x', '--extended'):
|
||||
xflag = True
|
||||
|
||||
if hflag or (xflag and desired_cols):
|
||||
usage()
|
||||
|
||||
if vflag:
|
||||
detailed_usage()
|
||||
|
||||
# Ensure at most only one of b, d, or t flags are set
|
||||
if (bflag and dflag) or (bflag and tflag) or (dflag and tflag):
|
||||
usage()
|
||||
|
||||
if bflag:
|
||||
hdr = bxhdr if xflag else bhdr
|
||||
elif tflag:
|
||||
hdr = txhdr if xflag else thdr
|
||||
else: # Even if dflag is False, it's the default if none set
|
||||
dflag = True
|
||||
hdr = dxhdr if xflag else dhdr
|
||||
|
||||
if desired_cols:
|
||||
hdr = desired_cols.split(",")
|
||||
|
||||
invalid = []
|
||||
incompat = []
|
||||
for ele in hdr:
|
||||
if ele not in cols:
|
||||
invalid.append(ele)
|
||||
elif ((bflag and bincompat and ele in bincompat) or
|
||||
(dflag and dincompat and ele in dincompat) or
|
||||
(tflag and tincompat and ele in tincompat)):
|
||||
incompat.append(ele)
|
||||
|
||||
if len(invalid) > 0:
|
||||
sys.stderr.write("Invalid column definition! -- %s\n" % invalid)
|
||||
usage()
|
||||
|
||||
if len(incompat) > 0:
|
||||
sys.stderr.write("Incompatible field specified! -- %s\n" %
|
||||
incompat)
|
||||
usage()
|
||||
|
||||
if ofile:
|
||||
try:
|
||||
tmp = open(ofile, "w")
|
||||
sys.stdout = tmp
|
||||
|
||||
except IOError:
|
||||
sys.stderr.write("Cannot open %s for writing\n" % ofile)
|
||||
sys.exit(1)
|
||||
|
||||
if not ifile:
|
||||
ifile = '/proc/spl/kstat/zfs/dbufs'
|
||||
|
||||
if ifile is not "-":
|
||||
try:
|
||||
tmp = open(ifile, "r")
|
||||
sys.stdin = tmp
|
||||
except IOError:
|
||||
sys.stderr.write("Cannot open %s for reading\n" % ifile)
|
||||
sys.exit(1)
|
||||
|
||||
if bflag:
|
||||
buffers_print_all(sys.stdin)
|
||||
|
||||
if dflag:
|
||||
print_dict(dnodes_build_dict(sys.stdin))
|
||||
|
||||
if tflag:
|
||||
print_dict(types_build_dict(sys.stdin))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -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 [ $# -eq 0 ]; then
|
||||
echo "Usage: $0 [options] dataset…" >&2
|
||||
exit 16
|
||||
fi
|
||||
|
||||
ret=0
|
||||
for dataset; 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"
|
||||
@@ -0,0 +1 @@
|
||||
dist_sbin_SCRIPTS = fsck.zfs
|
||||
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
|
||||
-410
@@ -1,410 +0,0 @@
|
||||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or https://opensource.org/licenses/CDDL-1.0.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011 Lawrence Livermore National Security, LLC.
|
||||
*/
|
||||
|
||||
#include <libintl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/mount.h>
|
||||
#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;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
static void
|
||||
parse_dataset(const char *target, char **dataset)
|
||||
{
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
char cwd[PATH_MAX];
|
||||
if (getcwd(cwd, PATH_MAX) == NULL) {
|
||||
perror("getcwd");
|
||||
return;
|
||||
}
|
||||
int len = strlen(cwd);
|
||||
if (strncmp(cwd, target, len) == 0)
|
||||
target += len;
|
||||
|
||||
/* Assume pool/dataset is more likely */
|
||||
strlcpy(*dataset, target, PATH_MAX);
|
||||
|
||||
int fd = open(target, O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0)
|
||||
return;
|
||||
|
||||
nvlist_t *cfg = NULL;
|
||||
if (zpool_read_label(fd, &cfg, NULL) == 0) {
|
||||
const 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");
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the mtab_* code to use the libmount library when it is commonly
|
||||
* available otherwise fallback to legacy mode. The mount(8) utility will
|
||||
* manage the lock file for us to prevent racing updates to /etc/mtab.
|
||||
*/
|
||||
static int
|
||||
mtab_is_writeable(void)
|
||||
{
|
||||
struct stat st;
|
||||
int error, fd;
|
||||
|
||||
error = lstat("/etc/mtab", &st);
|
||||
if (error || S_ISLNK(st.st_mode))
|
||||
return (0);
|
||||
|
||||
fd = open("/etc/mtab", O_RDWR | O_CREAT, 0644);
|
||||
if (fd < 0)
|
||||
return (0);
|
||||
|
||||
close(fd);
|
||||
return (1);
|
||||
}
|
||||
|
||||
static int
|
||||
mtab_update(const char *dataset, const char *mntpoint, const char *type,
|
||||
const char *mntopts)
|
||||
{
|
||||
struct mntent mnt;
|
||||
FILE *fp;
|
||||
int error;
|
||||
|
||||
mnt.mnt_fsname = (char *)dataset;
|
||||
mnt.mnt_dir = (char *)mntpoint;
|
||||
mnt.mnt_type = (char *)type;
|
||||
mnt.mnt_opts = (char *)(mntopts ?: "");
|
||||
mnt.mnt_freq = 0;
|
||||
mnt.mnt_passno = 0;
|
||||
|
||||
fp = setmntent("/etc/mtab", "a+e");
|
||||
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));
|
||||
return (MOUNT_FILEIO);
|
||||
}
|
||||
|
||||
error = addmntent(fp, &mnt);
|
||||
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));
|
||||
return (MOUNT_FILEIO);
|
||||
}
|
||||
|
||||
(void) endmntent(fp);
|
||||
|
||||
return (MOUNT_SUCCESS);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
zfs_handle_t *zhp;
|
||||
char prop[ZFS_MAXPROPLEN];
|
||||
uint64_t zfs_version = 0;
|
||||
char mntopts[MNT_LINE_MAX] = { '\0' };
|
||||
char badopt[MNT_LINE_MAX] = { '\0' };
|
||||
char mtabopt[MNT_LINE_MAX] = { '\0' };
|
||||
char mntpoint[PATH_MAX];
|
||||
char dataset[PATH_MAX], *pdataset = 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;
|
||||
|
||||
/* check options */
|
||||
while ((c = getopt_long(argc, argv, "sfnvo:h?", 0, 0)) != -1) {
|
||||
switch (c) {
|
||||
case 's':
|
||||
sloppy = 1;
|
||||
break;
|
||||
case 'f':
|
||||
fake = 1;
|
||||
break;
|
||||
case 'n':
|
||||
nomtab = 1;
|
||||
break;
|
||||
case 'v':
|
||||
verbose++;
|
||||
break;
|
||||
case 'o':
|
||||
(void) strlcpy(mntopts, optarg, sizeof (mntopts));
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
if (optopt)
|
||||
(void) fprintf(stderr,
|
||||
gettext("Invalid option '%c'\n"), optopt);
|
||||
(void) fprintf(stderr, gettext("Usage: mount.zfs "
|
||||
"[-sfnvh] [-o options] <dataset> <mountpoint>\n"));
|
||||
return (MOUNT_USAGE);
|
||||
}
|
||||
}
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
/* check that we only have two arguments */
|
||||
if (argc != 2) {
|
||||
if (argc == 0)
|
||||
(void) fprintf(stderr, gettext("missing dataset "
|
||||
"argument\n"));
|
||||
else if (argc == 1)
|
||||
(void) fprintf(stderr,
|
||||
gettext("missing mountpoint argument\n"));
|
||||
else
|
||||
(void) fprintf(stderr, gettext("too many arguments\n"));
|
||||
(void) fprintf(stderr, "usage: mount <dataset> <mountpoint>\n");
|
||||
return (MOUNT_USAGE);
|
||||
}
|
||||
|
||||
parse_dataset(argv[0], &pdataset);
|
||||
|
||||
/* 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));
|
||||
return (MOUNT_SYSERR);
|
||||
}
|
||||
|
||||
/* validate mount options and set mntflags */
|
||||
error = zfs_parse_mount_options(mntopts, &mntflags, &zfsflags, sloppy,
|
||||
badopt, mtabopt);
|
||||
if (error) {
|
||||
switch (error) {
|
||||
case ENOMEM:
|
||||
(void) fprintf(stderr, gettext("filesystem '%s' "
|
||||
"cannot be mounted due to a memory allocation "
|
||||
"failure.\n"), dataset);
|
||||
return (MOUNT_SYSERR);
|
||||
case ENOENT:
|
||||
(void) fprintf(stderr, gettext("filesystem '%s' "
|
||||
"cannot be mounted due to invalid option "
|
||||
"'%s'.\n"), dataset, badopt);
|
||||
(void) fprintf(stderr, gettext("Use the '-s' option "
|
||||
"to ignore the bad mount option.\n"));
|
||||
return (MOUNT_USAGE);
|
||||
default:
|
||||
(void) fprintf(stderr, gettext("filesystem '%s' "
|
||||
"cannot be mounted due to internal error %d.\n"),
|
||||
dataset, error);
|
||||
return (MOUNT_SOFTWARE);
|
||||
}
|
||||
}
|
||||
|
||||
if (mntflags & MS_REMOUNT) {
|
||||
nomtab = 1;
|
||||
remount = 1;
|
||||
}
|
||||
|
||||
if (zfsflags & ZS_ZFSUTIL)
|
||||
zfsutil = 1;
|
||||
|
||||
if ((g_zfs = libzfs_init()) == NULL) {
|
||||
(void) fprintf(stderr, "%s\n", libzfs_error_init(errno));
|
||||
return (MOUNT_SYSERR);
|
||||
}
|
||||
|
||||
/* try to open the dataset to access the mount point */
|
||||
if ((zhp = zfs_open(g_zfs, dataset,
|
||||
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT)) == NULL) {
|
||||
(void) fprintf(stderr, gettext("filesystem '%s' cannot be "
|
||||
"mounted, unable to open the dataset\n"), dataset);
|
||||
libzfs_fini(g_zfs);
|
||||
return (MOUNT_USAGE);
|
||||
}
|
||||
|
||||
if (!zfsutil || sloppy ||
|
||||
libzfs_envvar_is_set("ZFS_MOUNT_HELPER")) {
|
||||
zfs_adjust_mount_options(zhp, mntpoint, mntopts, mtabopt);
|
||||
}
|
||||
|
||||
/* treat all snapshots as legacy mount points */
|
||||
if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT)
|
||||
(void) strlcpy(prop, ZFS_MOUNTPOINT_LEGACY, ZFS_MAXPROPLEN);
|
||||
else
|
||||
(void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, prop,
|
||||
sizeof (prop), NULL, NULL, 0, B_FALSE);
|
||||
|
||||
/*
|
||||
* Fetch the max supported zfs version in case we get ENOTSUP
|
||||
* back from the mount command, since we need the zfs handle
|
||||
* to do so.
|
||||
*/
|
||||
zfs_version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Legacy mount points may only be mounted using 'mount', never using
|
||||
* 'zfs mount'. However, since 'zfs mount' actually invokes 'mount'
|
||||
* we differentiate the two cases using the 'zfsutil' mount option.
|
||||
* This mount option should only be supplied by the 'zfs mount' util.
|
||||
*
|
||||
* The only exception to the above rule is '-o remount' which is
|
||||
* always allowed for non-legacy datasets. This is done because when
|
||||
* using zfs as your root file system both rc.sysinit/umountroot and
|
||||
* systemd depend on 'mount -o remount <mountpoint>' to work.
|
||||
*/
|
||||
if (zfsutil && (strcmp(prop, ZFS_MOUNTPOINT_LEGACY) == 0)) {
|
||||
(void) fprintf(stderr, gettext(
|
||||
"filesystem '%s' cannot be mounted using 'zfs mount'.\n"
|
||||
"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);
|
||||
}
|
||||
|
||||
if (!zfsutil && !(remount || fake) &&
|
||||
strcmp(prop, ZFS_MOUNTPOINT_LEGACY)) {
|
||||
(void) fprintf(stderr, gettext(
|
||||
"filesystem '%s' cannot be mounted using 'mount'.\n"
|
||||
"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);
|
||||
}
|
||||
}
|
||||
|
||||
zfs_close(zhp);
|
||||
libzfs_fini(g_zfs);
|
||||
|
||||
if (error) {
|
||||
switch (errno) {
|
||||
case ENOENT:
|
||||
(void) fprintf(stderr, gettext("mount point "
|
||||
"'%s' does not exist\n"), mntpoint);
|
||||
return (MOUNT_SYSERR);
|
||||
case EBUSY:
|
||||
(void) fprintf(stderr, gettext("filesystem "
|
||||
"'%s' is already mounted\n"), dataset);
|
||||
return (MOUNT_BUSY);
|
||||
case ENOTSUP:
|
||||
if (zfs_version > ZPL_VERSION) {
|
||||
(void) fprintf(stderr,
|
||||
gettext("filesystem '%s' (v%d) is not "
|
||||
"supported by this implementation of "
|
||||
"ZFS (max v%d).\n"), dataset,
|
||||
(int)zfs_version, (int)ZPL_VERSION);
|
||||
} else {
|
||||
(void) fprintf(stderr,
|
||||
gettext("filesystem '%s' mount "
|
||||
"failed for unknown reason.\n"), dataset);
|
||||
}
|
||||
return (MOUNT_SYSERR);
|
||||
#ifdef MS_MANDLOCK
|
||||
case EPERM:
|
||||
if (mntflags & MS_MANDLOCK) {
|
||||
(void) fprintf(stderr, gettext("filesystem "
|
||||
"'%s' has the 'nbmand=on' property set, "
|
||||
"this mount\noption may be disabled in "
|
||||
"your kernel. Use 'zfs set nbmand=off'\n"
|
||||
"to disable this option and try to "
|
||||
"mount the filesystem again.\n"), dataset);
|
||||
return (MOUNT_SYSERR);
|
||||
}
|
||||
#endif
|
||||
zfs_fallthrough;
|
||||
default:
|
||||
(void) fprintf(stderr, gettext("filesystem "
|
||||
"'%s' can not be mounted: %s\n"), dataset,
|
||||
strerror(errno));
|
||||
return (MOUNT_USAGE);
|
||||
}
|
||||
}
|
||||
|
||||
if (!nomtab && mtab_is_writeable()) {
|
||||
error = mtab_update(dataset, mntpoint, MNTTYPE_ZFS, mtabopt);
|
||||
if (error)
|
||||
return (error);
|
||||
}
|
||||
|
||||
return (MOUNT_SUCCESS);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
mount.zfs
|
||||
@@ -0,0 +1,24 @@
|
||||
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.
|
||||
# However, if needed, the configure option --with-mounthelperdir= can be used
|
||||
# to override the default install location.
|
||||
#
|
||||
sbindir=$(mounthelperdir)
|
||||
sbin_PROGRAMS = mount.zfs
|
||||
|
||||
mount_zfs_SOURCES = \
|
||||
mount_zfs.c
|
||||
|
||||
mount_zfs_LDADD = \
|
||||
$(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
|
||||
@@ -0,0 +1,638 @@
|
||||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2011 Lawrence Livermore National Security, LLC.
|
||||
*/
|
||||
|
||||
#include <libintl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/mount.h>
|
||||
#include <sys/mntent.h>
|
||||
#include <sys/stat.h>
|
||||
#include <libzfs.h>
|
||||
#include <locale.h>
|
||||
#include <getopt.h>
|
||||
#include <fcntl.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 } };
|
||||
|
||||
/*
|
||||
* Break the mount option in to a name/value pair. The name is
|
||||
* validated against the option map and mount flags set accordingly.
|
||||
*/
|
||||
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;
|
||||
|
||||
/*
|
||||
* 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];
|
||||
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);
|
||||
}
|
||||
}
|
||||
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);
|
||||
|
||||
len = strlen(cwd);
|
||||
|
||||
/* Do not add one when cwd already ends in a trailing '/' */
|
||||
if (strncmp(cwd, dataset, len) == 0)
|
||||
return (dataset + len + (cwd[len-1] != '/'));
|
||||
|
||||
return (dataset);
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the mtab_* code to use the libmount library when it is commonly
|
||||
* available otherwise fallback to legacy mode. The mount(8) utility will
|
||||
* manage the lock file for us to prevent racing updates to /etc/mtab.
|
||||
*/
|
||||
static int
|
||||
mtab_is_writeable(void)
|
||||
{
|
||||
struct stat st;
|
||||
int error, fd;
|
||||
|
||||
error = lstat("/etc/mtab", &st);
|
||||
if (error || S_ISLNK(st.st_mode))
|
||||
return (0);
|
||||
|
||||
fd = open("/etc/mtab", O_RDWR | O_CREAT, 0644);
|
||||
if (fd < 0)
|
||||
return (0);
|
||||
|
||||
close(fd);
|
||||
return (1);
|
||||
}
|
||||
|
||||
static int
|
||||
mtab_update(char *dataset, char *mntpoint, char *type, char *mntopts)
|
||||
{
|
||||
struct mntent mnt;
|
||||
FILE *fp;
|
||||
int error;
|
||||
|
||||
mnt.mnt_fsname = dataset;
|
||||
mnt.mnt_dir = mntpoint;
|
||||
mnt.mnt_type = type;
|
||||
mnt.mnt_opts = mntopts ? mntopts : "";
|
||||
mnt.mnt_freq = 0;
|
||||
mnt.mnt_passno = 0;
|
||||
|
||||
fp = setmntent("/etc/mtab", "a+");
|
||||
if (!fp) {
|
||||
(void) fprintf(stderr, gettext(
|
||||
"filesystem '%s' was mounted, but /etc/mtab "
|
||||
"could not be opened due to error %d\n"),
|
||||
dataset, errno);
|
||||
return (MOUNT_FILEIO);
|
||||
}
|
||||
|
||||
error = addmntent(fp, &mnt);
|
||||
if (error) {
|
||||
(void) fprintf(stderr, gettext(
|
||||
"filesystem '%s' was mounted, but /etc/mtab "
|
||||
"could not be updated due to error %d\n"),
|
||||
dataset, errno);
|
||||
return (MOUNT_FILEIO);
|
||||
}
|
||||
|
||||
(void) endmntent(fp);
|
||||
|
||||
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)
|
||||
{
|
||||
zfs_handle_t *zhp;
|
||||
char prop[ZFS_MAXPROPLEN];
|
||||
uint64_t zfs_version = 0;
|
||||
char mntopts[MNT_LINE_MAX] = { '\0' };
|
||||
char badopt[MNT_LINE_MAX] = { '\0' };
|
||||
char mtabopt[MNT_LINE_MAX] = { '\0' };
|
||||
char mntpoint[PATH_MAX];
|
||||
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) textdomain(TEXT_DOMAIN);
|
||||
|
||||
opterr = 0;
|
||||
|
||||
/* check options */
|
||||
while ((c = getopt_long(argc, argv, "sfnvo:h?", 0, 0)) != -1) {
|
||||
switch (c) {
|
||||
case 's':
|
||||
sloppy = 1;
|
||||
break;
|
||||
case 'f':
|
||||
fake = 1;
|
||||
break;
|
||||
case 'n':
|
||||
nomtab = 1;
|
||||
break;
|
||||
case 'v':
|
||||
verbose++;
|
||||
break;
|
||||
case 'o':
|
||||
(void) strlcpy(mntopts, optarg, sizeof (mntopts));
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
(void) fprintf(stderr, gettext("Invalid option '%c'\n"),
|
||||
optopt);
|
||||
(void) fprintf(stderr, gettext("Usage: mount.zfs "
|
||||
"[-sfnv] [-o options] <dataset> <mountpoint>\n"));
|
||||
return (MOUNT_USAGE);
|
||||
}
|
||||
}
|
||||
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
|
||||
/* check that we only have two arguments */
|
||||
if (argc != 2) {
|
||||
if (argc == 0)
|
||||
(void) fprintf(stderr, gettext("missing dataset "
|
||||
"argument\n"));
|
||||
else if (argc == 1)
|
||||
(void) fprintf(stderr,
|
||||
gettext("missing mountpoint argument\n"));
|
||||
else
|
||||
(void) fprintf(stderr, gettext("too many arguments\n"));
|
||||
(void) fprintf(stderr, "usage: mount <dataset> <mountpoint>\n");
|
||||
return (MOUNT_USAGE);
|
||||
}
|
||||
|
||||
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 %d.\n"),
|
||||
dataset, argv[1], errno);
|
||||
return (MOUNT_SYSERR);
|
||||
}
|
||||
|
||||
/* validate mount options and set mntflags */
|
||||
error = parse_options(mntopts, &mntflags, &zfsflags, sloppy,
|
||||
badopt, mtabopt);
|
||||
if (error) {
|
||||
switch (error) {
|
||||
case ENOMEM:
|
||||
(void) fprintf(stderr, gettext("filesystem '%s' "
|
||||
"cannot be mounted due to a memory allocation "
|
||||
"failure.\n"), dataset);
|
||||
return (MOUNT_SYSERR);
|
||||
case ENOENT:
|
||||
(void) fprintf(stderr, gettext("filesystem '%s' "
|
||||
"cannot be mounted due to invalid option "
|
||||
"'%s'.\n"), dataset, badopt);
|
||||
(void) fprintf(stderr, gettext("Use the '-s' option "
|
||||
"to ignore the bad mount option.\n"));
|
||||
return (MOUNT_USAGE);
|
||||
default:
|
||||
(void) fprintf(stderr, gettext("filesystem '%s' "
|
||||
"cannot be mounted due to internal error %d.\n"),
|
||||
dataset, error);
|
||||
return (MOUNT_SOFTWARE);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (zfsflags & ZS_ZFSUTIL)
|
||||
zfsutil = 1;
|
||||
|
||||
if ((g_zfs = libzfs_init()) == NULL) {
|
||||
(void) fprintf(stderr, "%s", libzfs_error_init(errno));
|
||||
return (MOUNT_SYSERR);
|
||||
}
|
||||
|
||||
/* try to open the dataset to access the mount point */
|
||||
if ((zhp = zfs_open(g_zfs, dataset,
|
||||
ZFS_TYPE_FILESYSTEM | ZFS_TYPE_SNAPSHOT)) == NULL) {
|
||||
(void) fprintf(stderr, gettext("filesystem '%s' cannot be "
|
||||
"mounted, unable to open the dataset\n"), dataset);
|
||||
libzfs_fini(g_zfs);
|
||||
return (MOUNT_USAGE);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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);
|
||||
else
|
||||
(void) zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT, prop,
|
||||
sizeof (prop), NULL, NULL, 0, B_FALSE);
|
||||
|
||||
/*
|
||||
* Fetch the max supported zfs version in case we get ENOTSUP
|
||||
* back from the mount command, since we need the zfs handle
|
||||
* to do so.
|
||||
*/
|
||||
zfs_version = zfs_prop_get_int(zhp, ZFS_PROP_VERSION);
|
||||
if (zfs_version == 0) {
|
||||
fprintf(stderr, gettext("unable to fetch "
|
||||
"ZFS version for filesystem '%s'\n"), dataset);
|
||||
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'
|
||||
* we differentiate the two cases using the 'zfsutil' mount option.
|
||||
* This mount option should only be supplied by the 'zfs mount' util.
|
||||
*
|
||||
* The only exception to the above rule is '-o remount' which is
|
||||
* always allowed for non-legacy datasets. This is done because when
|
||||
* using zfs as your root file system both rc.sysinit/umountroot and
|
||||
* systemd depend on 'mount -o remount <mountpoint>' to work.
|
||||
*/
|
||||
if (zfsutil && (strcmp(prop, ZFS_MOUNTPOINT_LEGACY) == 0)) {
|
||||
(void) fprintf(stderr, gettext(
|
||||
"filesystem '%s' cannot be mounted using 'zfs mount'.\n"
|
||||
"Use 'zfs set mountpoint=%s' or 'mount -t zfs %s %s'.\n"
|
||||
"See zfs(8) for more information.\n"),
|
||||
dataset, mntpoint, dataset, mntpoint);
|
||||
return (MOUNT_USAGE);
|
||||
}
|
||||
|
||||
if (!zfsutil && !(remount || fake) &&
|
||||
strcmp(prop, ZFS_MOUNTPOINT_LEGACY)) {
|
||||
(void) fprintf(stderr, gettext(
|
||||
"filesystem '%s' cannot be mounted using 'mount'.\n"
|
||||
"Use 'zfs set mountpoint=%s' or 'zfs mount %s'.\n"
|
||||
"See zfs(8) for more information.\n"),
|
||||
dataset, "legacy", dataset);
|
||||
return (MOUNT_USAGE);
|
||||
}
|
||||
|
||||
if (!fake) {
|
||||
error = mount(dataset, mntpoint, MNTTYPE_ZFS,
|
||||
mntflags, mntopts);
|
||||
}
|
||||
|
||||
if (error) {
|
||||
switch (errno) {
|
||||
case ENOENT:
|
||||
(void) fprintf(stderr, gettext("mount point "
|
||||
"'%s' does not exist\n"), mntpoint);
|
||||
return (MOUNT_SYSERR);
|
||||
case EBUSY:
|
||||
(void) fprintf(stderr, gettext("filesystem "
|
||||
"'%s' is already mounted\n"), dataset);
|
||||
return (MOUNT_BUSY);
|
||||
case ENOTSUP:
|
||||
if (zfs_version > ZPL_VERSION) {
|
||||
(void) fprintf(stderr,
|
||||
gettext("filesystem '%s' (v%d) is not "
|
||||
"supported by this implementation of "
|
||||
"ZFS (max v%d).\n"), dataset,
|
||||
(int)zfs_version, (int)ZPL_VERSION);
|
||||
} else {
|
||||
(void) fprintf(stderr,
|
||||
gettext("filesystem '%s' mount "
|
||||
"failed for unknown reason.\n"), dataset);
|
||||
}
|
||||
return (MOUNT_SYSERR);
|
||||
#ifdef MS_MANDLOCK
|
||||
case EPERM:
|
||||
if (mntflags & MS_MANDLOCK) {
|
||||
(void) fprintf(stderr, gettext("filesystem "
|
||||
"'%s' has the 'nbmand=on' property set, "
|
||||
"this mount\noption may be disabled in "
|
||||
"your kernel. Use 'zfs set nbmand=off'\n"
|
||||
"to disable this option and try to "
|
||||
"mount the filesystem again.\n"), dataset);
|
||||
return (MOUNT_SYSERR);
|
||||
}
|
||||
/* fallthru */
|
||||
#endif
|
||||
default:
|
||||
(void) fprintf(stderr, gettext("filesystem "
|
||||
"'%s' can not be mounted: %s\n"), dataset,
|
||||
strerror(errno));
|
||||
return (MOUNT_USAGE);
|
||||
}
|
||||
}
|
||||
|
||||
if (!nomtab && mtab_is_writeable()) {
|
||||
error = mtab_update(dataset, mntpoint, MNTTYPE_ZFS, mtabopt);
|
||||
if (error)
|
||||
return (error);
|
||||
}
|
||||
|
||||
return (MOUNT_SUCCESS);
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
/raidz_test
|
||||
+16
-10
@@ -1,16 +1,22 @@
|
||||
raidz_test_CFLAGS = $(AM_CFLAGS) $(KERNEL_CFLAGS)
|
||||
raidz_test_CPPFLAGS = $(AM_CPPFLAGS) $(FORCEDEBUG_CPPFLAGS)
|
||||
include $(top_srcdir)/config/Rules.am
|
||||
|
||||
bin_PROGRAMS += raidz_test
|
||||
CPPCHECKTARGETS += raidz_test
|
||||
AM_CFLAGS += $(DEBUG_STACKFLAGS) $(FRAME_LARGER_THAN)
|
||||
AM_CPPFLAGS += -DDEBUG
|
||||
|
||||
DEFAULT_INCLUDES += \
|
||||
-I$(top_srcdir)/include \
|
||||
-I$(top_srcdir)/lib/libspl/include
|
||||
|
||||
bin_PROGRAMS = raidz_test
|
||||
|
||||
raidz_test_SOURCES = \
|
||||
%D%/raidz_bench.c \
|
||||
%D%/raidz_test.c \
|
||||
%D%/raidz_test.h
|
||||
raidz_test.h \
|
||||
raidz_test.c \
|
||||
raidz_bench.c
|
||||
|
||||
raidz_test_LDADD = \
|
||||
libzpool.la \
|
||||
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
|
||||
raidz_test_LDADD += -lm -ldl
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or https://opensource.org/licenses/CDDL-1.0.
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
@@ -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)
|
||||
@@ -63,7 +65,7 @@ bench_fini_raidz_maps(void)
|
||||
{
|
||||
/* tear down golden zio */
|
||||
raidz_free(zio_bench.io_abd, max_data_size);
|
||||
memset(&zio_bench, 0, sizeof (zio_t));
|
||||
bzero(&zio_bench, sizeof (zio_t));
|
||||
}
|
||||
|
||||
static inline void
|
||||
@@ -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;
|
||||
|
||||
+74
-314
@@ -6,7 +6,7 @@
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or https://opensource.org/licenses/CDDL-1.0.
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
@@ -37,11 +37,11 @@
|
||||
static int *rand_data;
|
||||
raidz_test_opts_t rto_opts;
|
||||
|
||||
static char pid_s[16];
|
||||
static char gdb[256];
|
||||
static const char gdb_tmpl[] = "gdb -ex \"set pagination 0\" -p %d";
|
||||
|
||||
static void sig_handler(int signo)
|
||||
{
|
||||
int old_errno = errno;
|
||||
struct sigaction action;
|
||||
/*
|
||||
* Restore default action and re-raise signal so SIGSEGV and
|
||||
@@ -52,32 +52,22 @@ static void sig_handler(int signo)
|
||||
action.sa_flags = 0;
|
||||
(void) sigaction(signo, &action, NULL);
|
||||
|
||||
if (rto_opts.rto_gdb) {
|
||||
pid_t pid = fork();
|
||||
if (pid == 0) {
|
||||
execlp("gdb", "gdb", "-ex", "set pagination 0",
|
||||
"-p", pid_s, NULL);
|
||||
_exit(-1);
|
||||
} else if (pid > 0)
|
||||
while (waitpid(pid, NULL, 0) == -1 && errno == EINTR)
|
||||
;
|
||||
}
|
||||
if (rto_opts.rto_gdb)
|
||||
if (system(gdb)) { }
|
||||
|
||||
raise(signo);
|
||||
errno = old_errno;
|
||||
}
|
||||
|
||||
static void print_opts(raidz_test_opts_t *opts, boolean_t force)
|
||||
{
|
||||
const char *verbose;
|
||||
char *verbose;
|
||||
switch (opts->rto_v) {
|
||||
case D_ALL:
|
||||
case 0:
|
||||
verbose = "no";
|
||||
break;
|
||||
case D_INFO:
|
||||
case 1:
|
||||
verbose = "info";
|
||||
break;
|
||||
case D_DEBUG:
|
||||
default:
|
||||
verbose = "debug";
|
||||
break;
|
||||
@@ -87,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 */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,9 +104,7 @@ 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: %d)]\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"
|
||||
"\t[-D debug (attach gdb on SIGSEGV)]\n"
|
||||
@@ -130,9 +114,7 @@ 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); /* -v */
|
||||
o->rto_v); /* -d */
|
||||
|
||||
exit(requested ? 0 : 1);
|
||||
}
|
||||
@@ -141,22 +123,19 @@ static void process_options(int argc, char **argv)
|
||||
{
|
||||
size_t value;
|
||||
int opt;
|
||||
|
||||
raidz_test_opts_t *o = &rto_opts;
|
||||
|
||||
memcpy(o, &rto_opts_defaults, sizeof (*o));
|
||||
bcopy(&rto_opts_defaults, o, sizeof (*o));
|
||||
|
||||
while ((opt = getopt(argc, argv, "TDBSvha:o:d:s:t:")) != -1) {
|
||||
value = 0;
|
||||
|
||||
while ((opt = getopt(argc, argv, "TDBSvha:er:o:d:s:t:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'a':
|
||||
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;
|
||||
@@ -200,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);
|
||||
@@ -236,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);
|
||||
@@ -264,21 +224,24 @@ cmp_data(raidz_test_opts_t *opts, raidz_map_t *rm)
|
||||
static int
|
||||
init_rand(void *data, size_t size, void *private)
|
||||
{
|
||||
(void) private;
|
||||
memcpy(data, rand_data, size);
|
||||
int i;
|
||||
int *dst = (int *)data;
|
||||
|
||||
for (i = 0; i < size / sizeof (int); i++)
|
||||
dst[i] = rand_data[i];
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -325,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);
|
||||
@@ -361,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)
|
||||
{
|
||||
@@ -560,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 */
|
||||
@@ -657,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 */
|
||||
@@ -682,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 */
|
||||
@@ -713,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 */
|
||||
@@ -837,7 +598,7 @@ static kcondvar_t sem_cv;
|
||||
static int max_free_slots;
|
||||
static int free_slots;
|
||||
|
||||
static __attribute__((noreturn)) void
|
||||
static void
|
||||
sweep_thread(void *arg)
|
||||
{
|
||||
int err = 0;
|
||||
@@ -937,14 +698,14 @@ run_sweep(void)
|
||||
opts = umem_zalloc(sizeof (raidz_test_opts_t), UMEM_NOFAIL);
|
||||
opts->rto_ashift = ashift_v[a];
|
||||
opts->rto_dcols = dcols_v[d];
|
||||
opts->rto_offset = (1ULL << ashift_v[a]) * rand();
|
||||
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:
|
||||
@@ -973,7 +734,6 @@ exit:
|
||||
return (sweep_state == SWEEP_ERROR ? SWEEP_ERROR : 0);
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
@@ -981,8 +741,8 @@ main(int argc, char **argv)
|
||||
struct sigaction action;
|
||||
int err = 0;
|
||||
|
||||
/* init gdb pid string early */
|
||||
(void) sprintf(pid_s, "%d", getpid());
|
||||
/* init gdb string early */
|
||||
(void) sprintf(gdb, gdb_tmpl, getpid());
|
||||
|
||||
action.sa_handler = sig_handler;
|
||||
sigemptyset(&action.sa_mask);
|
||||
@@ -999,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);
|
||||
|
||||
+14
-24
@@ -6,7 +6,7 @@
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or https://opensource.org/licenses/CDDL-1.0.
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
@@ -28,7 +28,7 @@
|
||||
|
||||
#include <sys/spa.h>
|
||||
|
||||
static const char *const raidz_impl_names[] = {
|
||||
static const char *raidz_impl_names[] = {
|
||||
"original",
|
||||
"scalar",
|
||||
"sse2",
|
||||
@@ -38,27 +38,18 @@ static const char *const raidz_impl_names[] = {
|
||||
"avx512bw",
|
||||
"aarch64_neon",
|
||||
"aarch64_neonx2",
|
||||
"powerpc_altivec",
|
||||
NULL
|
||||
};
|
||||
|
||||
enum raidz_verbosity {
|
||||
D_ALL,
|
||||
D_INFO,
|
||||
D_DEBUG,
|
||||
};
|
||||
|
||||
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;
|
||||
enum raidz_verbosity rto_v;
|
||||
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;
|
||||
|
||||
@@ -74,11 +65,9 @@ static const raidz_test_opts_t rto_opts_defaults = {
|
||||
.rto_offset = 1ULL << 0,
|
||||
.rto_dcols = 8,
|
||||
.rto_dsize = 1<<19,
|
||||
.rto_v = D_ALL,
|
||||
.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
|
||||
@@ -92,19 +81,23 @@ static inline size_t ilog2(size_t a)
|
||||
}
|
||||
|
||||
|
||||
#define LOG(lvl, ...) \
|
||||
#define D_ALL 0
|
||||
#define D_INFO 1
|
||||
#define D_DEBUG 2
|
||||
|
||||
#define LOG(lvl, a...) \
|
||||
{ \
|
||||
if (rto_opts.rto_v >= lvl) \
|
||||
(void) fprintf(stdout, __VA_ARGS__); \
|
||||
(void) fprintf(stdout, a); \
|
||||
} \
|
||||
|
||||
#define LOG_OPT(lvl, opt, ...) \
|
||||
#define LOG_OPT(lvl, opt, a...) \
|
||||
{ \
|
||||
if (opt->rto_v >= lvl) \
|
||||
(void) fprintf(stdout, __VA_ARGS__); \
|
||||
(void) fprintf(stdout, a); \
|
||||
} \
|
||||
|
||||
#define ERR(...) (void) fprintf(stderr, __VA_ARGS__)
|
||||
#define ERR(a...) (void) fprintf(stderr, a)
|
||||
|
||||
|
||||
#define DBLSEP "================\n"
|
||||
@@ -119,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 */
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
dist_udev_SCRIPTS = vdev_id
|
||||
Executable
+606
@@ -0,0 +1,606 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# vdev_id: udev helper to generate user-friendly names for JBOD disks
|
||||
#
|
||||
# This script parses the file /etc/zfs/vdev_id.conf to map a
|
||||
# physical path in a storage topology to a channel name. The
|
||||
# channel name is combined with a disk enclosure slot number to
|
||||
# create an alias that reflects the physical location of the drive.
|
||||
# This is particularly helpful when it comes to tasks like replacing
|
||||
# failed drives. Slot numbers may also be re-mapped in case the
|
||||
# default numbering is unsatisfactory. The drive aliases will be
|
||||
# created as symbolic links in /dev/disk/by-vdev.
|
||||
#
|
||||
# The currently supported topologies are sas_direct and sas_switch.
|
||||
# A multipath mode is supported in which dm-mpath devices are
|
||||
# handled by examining the first-listed running component disk. In
|
||||
# multipath mode the configuration file should contain a channel
|
||||
# definition with the same name for each path to a given enclosure.
|
||||
#
|
||||
# The alias keyword provides a simple way to map already-existing
|
||||
# device symlinks to more convenient names. It is suitable for
|
||||
# small, static configurations or for sites that have some automated
|
||||
# way to generate the mapping file.
|
||||
#
|
||||
#
|
||||
# Some example configuration files are given below.
|
||||
|
||||
# #
|
||||
# # Example vdev_id.conf - sas_direct.
|
||||
# #
|
||||
#
|
||||
# multipath no
|
||||
# topology sas_direct
|
||||
# phys_per_port 4
|
||||
# slot bay
|
||||
#
|
||||
# # PCI_ID HBA PORT CHANNEL NAME
|
||||
# channel 85:00.0 1 A
|
||||
# channel 85:00.0 0 B
|
||||
# channel 86:00.0 1 C
|
||||
# channel 86:00.0 0 D
|
||||
#
|
||||
# # Custom mapping for Channel A
|
||||
#
|
||||
# # Linux Mapped
|
||||
# # Slot Slot Channel
|
||||
# slot 1 7 A
|
||||
# slot 2 10 A
|
||||
# slot 3 3 A
|
||||
# slot 4 6 A
|
||||
#
|
||||
# # Default mapping for B, C, and D
|
||||
# slot 1 4
|
||||
# slot 2 2
|
||||
# slot 3 1
|
||||
# slot 4 3
|
||||
|
||||
# #
|
||||
# # Example vdev_id.conf - sas_switch
|
||||
# #
|
||||
#
|
||||
# topology sas_switch
|
||||
#
|
||||
# # SWITCH PORT CHANNEL NAME
|
||||
# channel 1 A
|
||||
# channel 2 B
|
||||
# channel 3 C
|
||||
# channel 4 D
|
||||
|
||||
# #
|
||||
# # Example vdev_id.conf - multipath
|
||||
# #
|
||||
#
|
||||
# multipath 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 - alias
|
||||
# #
|
||||
#
|
||||
# # by-vdev
|
||||
# # name fully qualified or base name of device link
|
||||
# alias d1 /dev/disk/by-id/wwn-0x5000c5002de3b9ca
|
||||
# alias d2 wwn-0x5000c5002def789e
|
||||
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin
|
||||
CONFIG=/etc/zfs/vdev_id.conf
|
||||
PHYS_PER_PORT=
|
||||
DEV=
|
||||
MULTIPATH=
|
||||
TOPOLOGY=
|
||||
BAY=
|
||||
|
||||
usage() {
|
||||
cat << EOF
|
||||
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 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
|
||||
-p number of phy's per switch port [default=$PHYS_PER_PORT]
|
||||
-h show this summary
|
||||
EOF
|
||||
exit 0
|
||||
}
|
||||
|
||||
map_slot() {
|
||||
local LINUX_SLOT=$1
|
||||
local CHANNEL=$2
|
||||
local MAPPED_SLOT=
|
||||
|
||||
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}
|
||||
}
|
||||
|
||||
map_channel() {
|
||||
local MAPPED_CHAN=
|
||||
local PCI_ID=$1
|
||||
local PORT=$2
|
||||
|
||||
case $TOPOLOGY in
|
||||
"sas_switch")
|
||||
MAPPED_CHAN=`awk "\\$1 == \"channel\" && \\$2 == ${PORT} \
|
||||
{ print \\$3; exit }" $CONFIG`
|
||||
;;
|
||||
"sas_direct"|"scsi")
|
||||
MAPPED_CHAN=`awk "\\$1 == \"channel\" && \
|
||||
\\$2 == \"${PCI_ID}\" && \\$3 == ${PORT} \
|
||||
{ print \\$4; exit }" $CONFIG`
|
||||
;;
|
||||
esac
|
||||
printf "%s" ${MAPPED_CHAN}
|
||||
}
|
||||
|
||||
sas_handler() {
|
||||
if [ -z "$PHYS_PER_PORT" ] ; then
|
||||
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
|
||||
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
|
||||
|
||||
# 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 |
|
||||
awk "/\/$DEV$/{print \\$9}"`
|
||||
fi
|
||||
|
||||
# For raw disks udev exports DEVTYPE=partition when
|
||||
# handling partitions, and the rules can be written to
|
||||
# take advantage of this to append a -part suffix. For
|
||||
# dm devices we get DEVTYPE=disk even for partitions so
|
||||
# we have to append the -part suffix directly in the
|
||||
# helper.
|
||||
if [ "$DEVTYPE" != "partition" ] ; then
|
||||
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]*$//'`
|
||||
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}'`
|
||||
if [ -z "$DEV" ] ; then
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
if echo $DEV | grep -q ^/devices/ ; then
|
||||
sys_path=$DEV
|
||||
else
|
||||
sys_path=`udevadm info -q path -p /sys/block/$DEV 2>/dev/null`
|
||||
fi
|
||||
|
||||
# Use positional parameters as an ad-hoc array
|
||||
set -- $(echo "$sys_path" | tr / ' ')
|
||||
num_dirs=$#
|
||||
scsi_host_dir="/sys"
|
||||
|
||||
# Get path up to /sys/.../hostX
|
||||
i=1
|
||||
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))
|
||||
done
|
||||
|
||||
if [ $i = $num_dirs ] ; then
|
||||
return
|
||||
fi
|
||||
|
||||
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)) ;;
|
||||
esac
|
||||
|
||||
i=$(($i + 1))
|
||||
while [ $i -le $j ] ; do
|
||||
port_dir="$port_dir/$(eval echo \${$i})"
|
||||
i=$(($i + 1))
|
||||
done
|
||||
|
||||
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 ))
|
||||
|
||||
# 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})
|
||||
end_device_dir="$end_device_dir/$d"
|
||||
if echo $d | grep -q '^end_device' ; then
|
||||
end_device_dir="$end_device_dir/sas_device/$d"
|
||||
break
|
||||
fi
|
||||
i=$(($i + 1))
|
||||
done
|
||||
|
||||
SLOT=
|
||||
case $BAY in
|
||||
"bay")
|
||||
SLOT=`cat $end_device_dir/bay_identifier 2>/dev/null`
|
||||
;;
|
||||
"phy")
|
||||
SLOT=`cat $end_device_dir/phy_identifier 2>/dev/null`
|
||||
;;
|
||||
"port")
|
||||
d=$(eval echo \${$i})
|
||||
SLOT=`echo $d | sed -e 's/^.*://'`
|
||||
;;
|
||||
"id")
|
||||
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/^.*://'`
|
||||
;;
|
||||
"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'`
|
||||
for enclosure in $enclosures; do
|
||||
set -- $(sg_ses -p aes $enclosure | \
|
||||
awk "/device slot number:/{slot=\$12} \
|
||||
/SAS address: $sas_address/\
|
||||
{print slot}")
|
||||
SLOT=$1
|
||||
if [ -n "$SLOT" ] ; then
|
||||
break
|
||||
fi
|
||||
done
|
||||
;;
|
||||
esac
|
||||
if [ -z "$SLOT" ] ; then
|
||||
return
|
||||
fi
|
||||
|
||||
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`
|
||||
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`
|
||||
fi
|
||||
PHYS_PER_PORT=${PHYS_PER_PORT:-4}
|
||||
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
|
||||
|
||||
# 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 |
|
||||
awk "/\/$DEV$/{print \\$9}"`
|
||||
fi
|
||||
|
||||
# For raw disks udev exports DEVTYPE=partition when
|
||||
# handling partitions, and the rules can be written to
|
||||
# take advantage of this to append a -part suffix. For
|
||||
# dm devices we get DEVTYPE=disk even for partitions so
|
||||
# we have to append the -part suffix directly in the
|
||||
# helper.
|
||||
if [ "$DEVTYPE" != "partition" ] ; then
|
||||
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]*$//'`
|
||||
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}'`
|
||||
if [ -z "$DEV" ] ; then
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
if echo $DEV | grep -q ^/devices/ ; then
|
||||
sys_path=$DEV
|
||||
else
|
||||
sys_path=`udevadm info -q path -p /sys/block/$DEV 2>/dev/null`
|
||||
fi
|
||||
|
||||
# expect sys_path like this, for example:
|
||||
# /devices/pci0000:00/0000:00:0b.0/0000:09:00.0/0000:0a:05.0/0000:0c:00.0/host3/target3:1:0/3:1:0:21/block/sdv
|
||||
|
||||
# Use positional parameters as an ad-hoc array
|
||||
set -- $(echo "$sys_path" | tr / ' ')
|
||||
num_dirs=$#
|
||||
scsi_host_dir="/sys"
|
||||
|
||||
# Get path up to /sys/.../hostX
|
||||
i=1
|
||||
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))
|
||||
done
|
||||
|
||||
if [ $i = $num_dirs ] ; then
|
||||
return
|
||||
fi
|
||||
|
||||
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))
|
||||
|
||||
i=$(($i + 1))
|
||||
while [ $i -le $j ] ; do
|
||||
port_dir="$port_dir/$(eval echo \${$i})"
|
||||
i=$(($i + 1))
|
||||
done
|
||||
|
||||
set -- $(echo $port_dir | sed -e 's/^.*:\([^:]*\):\([^:]*\)$/\1 \2/')
|
||||
PORT=$1
|
||||
SLOT=$(($2 + $FIRST_BAY_NUMBER))
|
||||
|
||||
if [ -z "$SLOT" ] ; then
|
||||
return
|
||||
fi
|
||||
|
||||
CHAN=`map_channel $PCI_ID $PORT`
|
||||
SLOT=`map_slot $SLOT $CHAN`
|
||||
if [ -z "$CHAN" ] ; then
|
||||
return
|
||||
fi
|
||||
echo ${CHAN}${SLOT}${PART}
|
||||
}
|
||||
|
||||
# Figure out the name for the enclosure symlink
|
||||
enclosure_handler () {
|
||||
# We get all the info we need from udev's DEVPATH variable:
|
||||
#
|
||||
# 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=$(basename $(readlink -m "/sys/$DEVPATH/../.."))
|
||||
if [ ! -d /sys/class/enclosure/$ENC ] ; then
|
||||
# Not an enclosure, bail out
|
||||
return
|
||||
fi
|
||||
|
||||
# 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)
|
||||
|
||||
# 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]+')
|
||||
|
||||
# Get the port number
|
||||
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=$(basename $(readlink -m "/sys/$PORT_DIR/../.."))
|
||||
|
||||
# Strip down the PCI address from 0000:05:00.0 to 05:00.0
|
||||
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 \$4int(count[\$4])}; count[\$4]++}" $CONFIG)
|
||||
|
||||
echo "${NAME}"
|
||||
}
|
||||
|
||||
alias_handler () {
|
||||
# Special handling is needed to correctly append a -part suffix
|
||||
# to partitions of device mapper devices. The DEVTYPE attribute
|
||||
# is normally set to "disk" instead of "partition" in this case,
|
||||
# so the udev rules won't handle that for us as they do for
|
||||
# "plain" block devices.
|
||||
#
|
||||
# For example, we may have the following links for a device and its
|
||||
# partitions,
|
||||
#
|
||||
# /dev/disk/by-id/dm-name-isw_dibgbfcije_ARRAY0 -> ../../dm-0
|
||||
# /dev/disk/by-id/dm-name-isw_dibgbfcije_ARRAY0p1 -> ../../dm-1
|
||||
# /dev/disk/by-id/dm-name-isw_dibgbfcije_ARRAY0p2 -> ../../dm-3
|
||||
#
|
||||
# and the following alias in vdev_id.conf.
|
||||
#
|
||||
# alias A0 dm-name-isw_dibgbfcije_ARRAY0
|
||||
#
|
||||
# The desired outcome is for the following links to be created
|
||||
# without having explicitly defined aliases for the partitions.
|
||||
#
|
||||
# /dev/disk/by-vdev/A0 -> ../../dm-0
|
||||
# /dev/disk/by-vdev/A0-part1 -> ../../dm-1
|
||||
# /dev/disk/by-vdev/A0-part2 -> ../../dm-3
|
||||
#
|
||||
# Warning: The following grep pattern will misidentify whole-disk
|
||||
# devices whose names end with 'p' followed by a string of
|
||||
# digits as partitions, causing alias creation to fail. This
|
||||
# ambiguity seems unavoidable, so devices using this facility
|
||||
# must not use such names.
|
||||
local DM_PART=
|
||||
if echo $DM_NAME | grep -q -E 'p[0-9][0-9]*$' ; then
|
||||
if [ "$DEVTYPE" != "partition" ] ; then
|
||||
DM_PART=`echo $DM_NAME | awk -Fp '/p/{print "-part"$2}'`
|
||||
fi
|
||||
fi
|
||||
|
||||
# DEVLINKS attribute must have been populated by already-run udev rules.
|
||||
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]*$//'`
|
||||
fi
|
||||
# Check both the fully qualified and the base name of link.
|
||||
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
|
||||
}
|
||||
|
||||
while getopts 'c:d:eg:mp:h' OPTION; do
|
||||
case ${OPTION} in
|
||||
c)
|
||||
CONFIG=${OPTARG}
|
||||
;;
|
||||
d)
|
||||
DEV=${OPTARG}
|
||||
;;
|
||||
e)
|
||||
# When udev sees a scsi_generic device, it calls this script with -e to
|
||||
# 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)
|
||||
if [ "$ENCLOSURE_MODE" != "yes" ] ; then
|
||||
exit 0
|
||||
fi
|
||||
;;
|
||||
g)
|
||||
TOPOLOGY=$OPTARG
|
||||
;;
|
||||
p)
|
||||
PHYS_PER_PORT=${OPTARG}
|
||||
;;
|
||||
m)
|
||||
MULTIPATH_MODE=yes
|
||||
;;
|
||||
h)
|
||||
usage
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ ! -r $CONFIG ] ; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
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`
|
||||
fi
|
||||
|
||||
if [ -z "$BAY" ] ; then
|
||||
BAY=`awk "\\$1 == \"slot\" {print \\$2; exit}" $CONFIG`
|
||||
fi
|
||||
|
||||
TOPOLOGY=${TOPOLOGY:-sas_direct}
|
||||
|
||||
# Should we create /dev/by-enclosure symlinks?
|
||||
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)
|
||||
if [ -z "$ENCLOSURE_PREFIX" ] ; then
|
||||
ENCLOSURE_PREFIX="enc"
|
||||
fi
|
||||
echo "ID_ENCLOSURE=$ID_ENCLOSURE"
|
||||
echo "ID_ENCLOSURE_PATH=by-enclosure/$ENCLOSURE_PREFIX-$ID_ENCLOSURE"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# First check if an alias was defined for this device.
|
||||
ID_VDEV=`alias_handler`
|
||||
|
||||
if [ -z "$ID_VDEV" ] ; then
|
||||
BAY=${BAY:-bay}
|
||||
case $TOPOLOGY in
|
||||
sas_direct|sas_switch)
|
||||
ID_VDEV=`sas_handler`
|
||||
;;
|
||||
scsi)
|
||||
ID_VDEV=`scsi_handler`
|
||||
;;
|
||||
*)
|
||||
echo "Error: unknown topology $TOPOLOGY"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
if [ -n "$ID_VDEV" ] ; then
|
||||
echo "ID_VDEV=${ID_VDEV}"
|
||||
echo "ID_VDEV_PATH=disk/by-vdev/${ID_VDEV}"
|
||||
fi
|
||||
@@ -0,0 +1 @@
|
||||
/zdb
|
||||
+15
-12
@@ -1,17 +1,20 @@
|
||||
zdb_CPPFLAGS = $(AM_CPPFLAGS) $(FORCEDEBUG_CPPFLAGS)
|
||||
zdb_CFLAGS = $(AM_CFLAGS) $(LIBCRYPTO_CFLAGS)
|
||||
include $(top_srcdir)/config/Rules.am
|
||||
|
||||
sbin_PROGRAMS += zdb
|
||||
CPPCHECKTARGETS += zdb
|
||||
AM_CPPFLAGS += -DDEBUG
|
||||
|
||||
DEFAULT_INCLUDES += \
|
||||
-I$(top_srcdir)/include \
|
||||
-I$(top_srcdir)/lib/libspl/include
|
||||
|
||||
sbin_PROGRAMS = zdb
|
||||
|
||||
zdb_SOURCES = \
|
||||
%D%/zdb.c \
|
||||
%D%/zdb.h \
|
||||
%D%/zdb_il.c
|
||||
zdb.c \
|
||||
zdb_il.c
|
||||
|
||||
zdb_LDADD = \
|
||||
libzpool.la \
|
||||
libzfs_core.la \
|
||||
libnvpair.la
|
||||
|
||||
zdb_LDADD += $(LIBCRYPTO_LIBS)
|
||||
$(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
|
||||
|
||||
+766
-5764
File diff suppressed because it is too large
Load Diff
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* CDDL HEADER START
|
||||
*
|
||||
* The contents of this file are subject to the terms of the
|
||||
* Common Development and Distribution License (the "License").
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or https://opensource.org/licenses/CDDL-1.0.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
* When distributing Covered Code, include this CDDL HEADER in each
|
||||
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||
* If applicable, add the following below this CDDL HEADER, with the
|
||||
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||
*
|
||||
* CDDL HEADER END
|
||||
*/
|
||||
/*
|
||||
* Copyright 2017 Spectra Logic Corp Inc. All rights reserved.
|
||||
* Use is subject to license terms.
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _ZDB_H
|
||||
#define _ZDB_H
|
||||
|
||||
void dump_intent_log(zilog_t *);
|
||||
extern uint8_t dump_opt[256];
|
||||
|
||||
#endif /* _ZDB_H */
|
||||
+84
-220
@@ -6,7 +6,7 @@
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or https://opensource.org/licenses/CDDL-1.0.
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
@@ -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)
|
||||
@@ -60,11 +57,10 @@ print_log_bp(const blkptr_t *bp, const char *prefix)
|
||||
(void) printf("%s%s\n", prefix, blkbuf);
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
(void) zilog;
|
||||
const lr_create_t *lr = arg;
|
||||
time_t crtime = lr->lr_crtime[0];
|
||||
char *name, *link;
|
||||
lr_attr_t *lrattr;
|
||||
@@ -79,72 +75,59 @@ 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)
|
||||
{
|
||||
(void) zilog, (void) txtype;
|
||||
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)
|
||||
{
|
||||
(void) zilog, (void) txtype;
|
||||
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)
|
||||
{
|
||||
(void) zilog, (void) txtype;
|
||||
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);
|
||||
switch (txtype) {
|
||||
case TX_RENAME_EXCHANGE:
|
||||
(void) printf("%sflags RENAME_EXCHANGE\n", tab_prefix);
|
||||
break;
|
||||
case TX_RENAME_WHITEOUT:
|
||||
(void) printf("%sflags RENAME_WHITEOUT\n", tab_prefix);
|
||||
break;
|
||||
}
|
||||
(void) printf("%ssrc %s tgt %s\n", prefix, snm, tnm);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static int
|
||||
zil_prt_rec_write_cb(void *data, size_t len, void *unused)
|
||||
{
|
||||
(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
|
||||
@@ -154,45 +137,41 @@ zil_prt_rec_write_cb(void *data, size_t len, void *unused)
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
|
||||
if (txtype == TX_WRITE2 || verbose < 4)
|
||||
if (txtype == TX_WRITE2 || verbose < 5)
|
||||
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 (verbose < 5)
|
||||
return;
|
||||
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;
|
||||
}
|
||||
|
||||
ASSERT3U(BP_GET_LSIZE(bp), !=, 0);
|
||||
SET_BOOKMARK(&zb, dmu_objset_id(zilog->zl_os),
|
||||
lr->lr_foid, ZB_ZIL_LEVEL,
|
||||
lr->lr_offset / BP_GET_LSIZE(bp));
|
||||
@@ -204,15 +183,12 @@ zil_prt_rec_write(zilog_t *zilog, int txtype, const void *arg)
|
||||
if (error)
|
||||
goto out;
|
||||
} else {
|
||||
if (verbose < 5)
|
||||
return;
|
||||
|
||||
/* data is stored after the end of the lr_write record */
|
||||
data = abd_alloc(lr->lr_length, B_FALSE);
|
||||
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);
|
||||
@@ -222,203 +198,103 @@ out:
|
||||
abd_free(data);
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
zil_prt_rec_write_enc(zilog_t *zilog, int txtype, const void *arg)
|
||||
zil_prt_rec_truncate(zilog_t *zilog, int txtype, lr_truncate_t *lr)
|
||||
{
|
||||
(void) txtype;
|
||||
const lr_write_t *lr = arg;
|
||||
const blkptr_t *bp = &lr->lr_blkptr;
|
||||
int verbose = MAX(dump_opt['d'], dump_opt['i']);
|
||||
|
||||
(void) printf("%s(encrypted)\n", tab_prefix);
|
||||
|
||||
if (verbose < 4)
|
||||
return;
|
||||
|
||||
if (lr->lr_common.lrc_reclen == sizeof (lr_write_t)) {
|
||||
(void) printf("%shas blkptr, %s\n", tab_prefix,
|
||||
!BP_IS_HOLE(bp) &&
|
||||
bp->blk_birth >= spa_min_claim_txg(zilog->zl_spa) ?
|
||||
"will claim" : "won't claim");
|
||||
print_log_bp(bp, tab_prefix);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
zil_prt_rec_truncate(zilog_t *zilog, int txtype, const void *arg)
|
||||
{
|
||||
(void) zilog, (void) txtype;
|
||||
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)
|
||||
{
|
||||
(void) zilog, (void) txtype;
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
zil_prt_rec_setsaxattr(zilog_t *zilog, int txtype, const void *arg)
|
||||
zil_prt_rec_acl(zilog_t *zilog, int txtype, lr_acl_t *lr)
|
||||
{
|
||||
(void) zilog, (void) txtype;
|
||||
const lr_setsaxattr_t *lr = arg;
|
||||
|
||||
char *name = (char *)(lr + 1);
|
||||
(void) printf("%sfoid %llu\n", tab_prefix,
|
||||
(u_longlong_t)lr->lr_foid);
|
||||
|
||||
(void) printf("%sXAT_NAME %s\n", tab_prefix, name);
|
||||
if (lr->lr_size == 0) {
|
||||
(void) printf("%sXAT_VALUE NULL\n", tab_prefix);
|
||||
} else {
|
||||
(void) printf("%sXAT_VALUE ", tab_prefix);
|
||||
char *val = name + (strlen(name) + 1);
|
||||
for (int i = 0; i < lr->lr_size; i++) {
|
||||
(void) printf("%c", *val);
|
||||
val++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
zil_prt_rec_acl(zilog_t *zilog, int txtype, const void *arg)
|
||||
{
|
||||
(void) zilog, (void) txtype;
|
||||
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);
|
||||
}
|
||||
|
||||
static void
|
||||
zil_prt_rec_clone_range(zilog_t *zilog, int txtype, const void *arg)
|
||||
{
|
||||
(void) zilog, (void) txtype;
|
||||
const lr_clone_range_t *lr = arg;
|
||||
int verbose = MAX(dump_opt['d'], dump_opt['i']);
|
||||
|
||||
(void) printf("%sfoid %llu, offset %llx, length %llx, blksize %llx\n",
|
||||
tab_prefix, (u_longlong_t)lr->lr_foid, (u_longlong_t)lr->lr_offset,
|
||||
(u_longlong_t)lr->lr_length, (u_longlong_t)lr->lr_blksz);
|
||||
|
||||
if (verbose < 4)
|
||||
return;
|
||||
|
||||
for (unsigned int i = 0; i < lr->lr_nbps; i++) {
|
||||
(void) printf("%s[%u/%llu] ", tab_prefix, i + 1,
|
||||
(u_longlong_t)lr->lr_nbps);
|
||||
print_log_bp(&lr->lr_bps[i], "");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
zil_prt_rec_clone_range_enc(zilog_t *zilog, int txtype, const void *arg)
|
||||
{
|
||||
(void) zilog, (void) txtype;
|
||||
const lr_clone_range_t *lr = arg;
|
||||
int verbose = MAX(dump_opt['d'], dump_opt['i']);
|
||||
|
||||
(void) printf("%s(encrypted)\n", tab_prefix);
|
||||
|
||||
if (verbose < 4)
|
||||
return;
|
||||
|
||||
for (unsigned int i = 0; i < lr->lr_nbps; i++) {
|
||||
(void) printf("%s[%u/%llu] ", tab_prefix, i + 1,
|
||||
(u_longlong_t)lr->lr_nbps);
|
||||
print_log_bp(&lr->lr_bps[i], "");
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
zil_prt_rec_func_t zri_print_enc;
|
||||
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_print_enc = zil_prt_rec_write_enc,
|
||||
.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 "},
|
||||
{.zri_print = zil_prt_rec_setsaxattr,
|
||||
.zri_name = "TX_SETSAXATTR "},
|
||||
{.zri_print = zil_prt_rec_rename, .zri_name = "TX_RENAME_EXCHANGE "},
|
||||
{.zri_print = zil_prt_rec_rename, .zri_name = "TX_RENAME_WHITEOUT "},
|
||||
{.zri_print = zil_prt_rec_clone_range,
|
||||
.zri_print_enc = zil_prt_rec_clone_range_enc,
|
||||
.zri_name = "TX_CLONE_RANGE "},
|
||||
{ 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)
|
||||
{
|
||||
(void) arg, (void) claim_txg;
|
||||
int txtype;
|
||||
int verbose = MAX(dump_opt['d'], dump_opt['i']);
|
||||
|
||||
@@ -435,15 +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 if (zil_rec_info[txtype].zri_print_enc) {
|
||||
zil_rec_info[txtype].zri_print_enc(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++;
|
||||
@@ -451,14 +320,13 @@ print_log_record(zilog_t *zilog, const lr_t *lr, void *arg, uint64_t claim_txg)
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
(void) arg;
|
||||
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);
|
||||
@@ -473,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";
|
||||
@@ -487,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");
|
||||
@@ -506,6 +374,7 @@ print_log_stats(int verbose)
|
||||
(void) printf("\n");
|
||||
}
|
||||
|
||||
/* ARGSUSED */
|
||||
void
|
||||
dump_intent_log(zilog_t *zilog)
|
||||
{
|
||||
@@ -527,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);
|
||||
}
|
||||
}
|
||||
|
||||
+87
-39
@@ -1,46 +1,94 @@
|
||||
include $(srcdir)/%D%/zed.d/Makefile.am
|
||||
include $(top_srcdir)/config/Rules.am
|
||||
|
||||
zed_CFLAGS = $(AM_CFLAGS)
|
||||
zed_CFLAGS += $(LIBUDEV_CFLAGS) $(LIBUUID_CFLAGS)
|
||||
DEFAULT_INCLUDES += \
|
||||
-I$(top_srcdir)/include \
|
||||
-I$(top_srcdir)/lib/libspl/include
|
||||
|
||||
sbin_PROGRAMS += zed
|
||||
CPPCHECKTARGETS += zed
|
||||
EXTRA_DIST = zed.d/README
|
||||
|
||||
zed_SOURCES = \
|
||||
%D%/zed.c \
|
||||
%D%/zed.h \
|
||||
%D%/zed_conf.c \
|
||||
%D%/zed_conf.h \
|
||||
%D%/zed_disk_event.c \
|
||||
%D%/zed_disk_event.h \
|
||||
%D%/zed_event.c \
|
||||
%D%/zed_event.h \
|
||||
%D%/zed_exec.c \
|
||||
%D%/zed_exec.h \
|
||||
%D%/zed_file.c \
|
||||
%D%/zed_file.h \
|
||||
%D%/zed_log.c \
|
||||
%D%/zed_log.h \
|
||||
%D%/zed_strings.c \
|
||||
%D%/zed_strings.h \
|
||||
\
|
||||
%D%/agents/fmd_api.c \
|
||||
%D%/agents/fmd_api.h \
|
||||
%D%/agents/fmd_serd.c \
|
||||
%D%/agents/fmd_serd.h \
|
||||
%D%/agents/zfs_agents.c \
|
||||
%D%/agents/zfs_agents.h \
|
||||
%D%/agents/zfs_diagnosis.c \
|
||||
%D%/agents/zfs_mod.c \
|
||||
%D%/agents/zfs_retire.c
|
||||
sbin_PROGRAMS = zed
|
||||
|
||||
ZED_SRC = \
|
||||
zed.c \
|
||||
zed.h \
|
||||
zed_conf.c \
|
||||
zed_conf.h \
|
||||
zed_disk_event.c \
|
||||
zed_disk_event.h \
|
||||
zed_event.c \
|
||||
zed_event.h \
|
||||
zed_exec.c \
|
||||
zed_exec.h \
|
||||
zed_file.c \
|
||||
zed_file.h \
|
||||
zed_log.c \
|
||||
zed_log.h \
|
||||
zed_strings.c \
|
||||
zed_strings.h
|
||||
|
||||
FMA_SRC = \
|
||||
agents/zfs_agents.c \
|
||||
agents/zfs_agents.h \
|
||||
agents/zfs_diagnosis.c \
|
||||
agents/zfs_mod.c \
|
||||
agents/zfs_retire.c \
|
||||
agents/fmd_api.c \
|
||||
agents/fmd_api.h \
|
||||
agents/fmd_serd.c \
|
||||
agents/fmd_serd.h
|
||||
|
||||
zed_SOURCES = $(ZED_SRC) $(FMA_SRC)
|
||||
|
||||
zed_LDADD = \
|
||||
libzfs.la \
|
||||
libzfs_core.la \
|
||||
libnvpair.la \
|
||||
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
|
||||
|
||||
dist_noinst_DATA += %D%/agents/README.md
|
||||
zedconfdir = $(sysconfdir)/zfs/zed.d
|
||||
|
||||
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"
|
||||
|
||||
+46
-62
@@ -6,7 +6,7 @@
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or https://opensource.org/licenses/CDDL-1.0.
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
@@ -22,11 +22,10 @@
|
||||
* Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
*
|
||||
* Copyright (c) 2016, Intel Corporation.
|
||||
* Copyright (c) 2023, Klara Inc.
|
||||
*/
|
||||
|
||||
/*
|
||||
* 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.
|
||||
@@ -39,7 +38,7 @@
|
||||
#include <sys/fm/protocol.h>
|
||||
#include <uuid/uuid.h>
|
||||
#include <signal.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "fmd_api.h"
|
||||
@@ -98,7 +97,6 @@ _umem_logging_init(void)
|
||||
int
|
||||
fmd_hdl_register(fmd_hdl_t *hdl, int version, const fmd_hdl_info_t *mip)
|
||||
{
|
||||
(void) version;
|
||||
fmd_module_t *mp = (fmd_module_t *)hdl;
|
||||
|
||||
mp->mod_info = mip;
|
||||
@@ -181,21 +179,18 @@ fmd_hdl_getspecific(fmd_hdl_t *hdl)
|
||||
void *
|
||||
fmd_hdl_alloc(fmd_hdl_t *hdl, size_t size, int flags)
|
||||
{
|
||||
(void) hdl;
|
||||
return (umem_alloc(size, flags));
|
||||
}
|
||||
|
||||
void *
|
||||
fmd_hdl_zalloc(fmd_hdl_t *hdl, size_t size, int flags)
|
||||
{
|
||||
(void) hdl;
|
||||
return (umem_zalloc(size, flags));
|
||||
}
|
||||
|
||||
void
|
||||
fmd_hdl_free(fmd_hdl_t *hdl, void *data, size_t size)
|
||||
{
|
||||
(void) hdl;
|
||||
umem_free(data, size);
|
||||
}
|
||||
|
||||
@@ -222,8 +217,6 @@ fmd_hdl_debug(fmd_hdl_t *hdl, const char *format, ...)
|
||||
int32_t
|
||||
fmd_prop_get_int32(fmd_hdl_t *hdl, const char *name)
|
||||
{
|
||||
(void) hdl;
|
||||
|
||||
/*
|
||||
* These can be looked up in mp->modinfo->fmdi_props
|
||||
* For now we just hard code for phase 2. In the
|
||||
@@ -232,6 +225,26 @@ fmd_prop_get_int32(fmd_hdl_t *hdl, const char *name)
|
||||
if (strcmp(name, "spare_on_remove") == 0)
|
||||
return (1);
|
||||
|
||||
if (strcmp(name, "io_N") == 0 || strcmp(name, "checksum_N") == 0)
|
||||
return (10); /* N = 10 events */
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int64_t
|
||||
fmd_prop_get_int64(fmd_hdl_t *hdl, const char *name)
|
||||
{
|
||||
/*
|
||||
* These can be looked up in mp->modinfo->fmdi_props
|
||||
* For now we just hard code for phase 2. In the
|
||||
* future, there can be a ZED based override.
|
||||
*/
|
||||
if (strcmp(name, "remove_timeout") == 0)
|
||||
return (15ULL * 1000ULL * 1000ULL * 1000ULL); /* 15 sec */
|
||||
|
||||
if (strcmp(name, "io_T") == 0 || strcmp(name, "checksum_T") == 0)
|
||||
return (1000ULL * 1000ULL * 1000ULL * 600ULL); /* 10 min */
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
@@ -321,24 +334,22 @@ fmd_case_uuresolved(fmd_hdl_t *hdl, const char *uuid)
|
||||
fmd_hdl_debug(hdl, "case resolved by uuid (%s)", uuid);
|
||||
}
|
||||
|
||||
boolean_t
|
||||
int
|
||||
fmd_case_solved(fmd_hdl_t *hdl, fmd_case_t *cp)
|
||||
{
|
||||
(void) hdl;
|
||||
return (cp->ci_state >= FMD_CASE_SOLVED);
|
||||
return ((cp->ci_state >= FMD_CASE_SOLVED) ? FMD_B_TRUE : FMD_B_FALSE);
|
||||
}
|
||||
|
||||
void
|
||||
fmd_case_add_ereport(fmd_hdl_t *hdl, fmd_case_t *cp, fmd_event_t *ep)
|
||||
{
|
||||
(void) hdl, (void) cp, (void) ep;
|
||||
}
|
||||
|
||||
static void
|
||||
zed_log_fault(nvlist_t *nvl, const char *uuid, const char *code)
|
||||
{
|
||||
nvlist_t *rsrc;
|
||||
const char *strval;
|
||||
char *strval;
|
||||
uint64_t guid;
|
||||
uint8_t byte;
|
||||
|
||||
@@ -351,7 +362,7 @@ zed_log_fault(nvlist_t *nvl, const char *uuid, const char *code)
|
||||
if (code != NULL)
|
||||
zed_log_msg(LOG_INFO, "\t%s: %s", FM_SUSPECT_DIAG_CODE, code);
|
||||
if (nvlist_lookup_uint8(nvl, FM_FAULT_CERTAINTY, &byte) == 0)
|
||||
zed_log_msg(LOG_INFO, "\t%s: %hhu", FM_FAULT_CERTAINTY, byte);
|
||||
zed_log_msg(LOG_INFO, "\t%s: %llu", FM_FAULT_CERTAINTY, byte);
|
||||
if (nvlist_lookup_nvlist(nvl, FM_FAULT_RESOURCE, &rsrc) == 0) {
|
||||
if (nvlist_lookup_string(rsrc, FM_FMRI_SCHEME, &strval) == 0)
|
||||
zed_log_msg(LOG_INFO, "\t%s: %s", FM_FMRI_SCHEME,
|
||||
@@ -368,8 +379,7 @@ zed_log_fault(nvlist_t *nvl, const char *uuid, const char *code)
|
||||
static const char *
|
||||
fmd_fault_mkcode(nvlist_t *fault)
|
||||
{
|
||||
const char *class;
|
||||
const char *code = "-";
|
||||
char *class, *code = "-";
|
||||
|
||||
/*
|
||||
* Note: message codes come from: openzfs/usr/src/cmd/fm/dicts/ZFS.po
|
||||
@@ -418,8 +428,7 @@ fmd_case_add_suspect(fmd_hdl_t *hdl, fmd_case_t *cp, nvlist_t *fault)
|
||||
err |= nvlist_add_string(nvl, FM_SUSPECT_DIAG_CODE, code);
|
||||
err |= nvlist_add_int64_array(nvl, FM_SUSPECT_DIAG_TIME, tod, 2);
|
||||
err |= nvlist_add_uint32(nvl, FM_SUSPECT_FAULT_SZ, 1);
|
||||
err |= nvlist_add_nvlist_array(nvl, FM_SUSPECT_FAULT_LIST,
|
||||
(const nvlist_t **)&fault, 1);
|
||||
err |= nvlist_add_nvlist_array(nvl, FM_SUSPECT_FAULT_LIST, &fault, 1);
|
||||
|
||||
if (err)
|
||||
zed_log_die("failed to populate nvlist");
|
||||
@@ -434,21 +443,19 @@ fmd_case_add_suspect(fmd_hdl_t *hdl, fmd_case_t *cp, nvlist_t *fault)
|
||||
void
|
||||
fmd_case_setspecific(fmd_hdl_t *hdl, fmd_case_t *cp, void *data)
|
||||
{
|
||||
(void) hdl;
|
||||
cp->ci_data = data;
|
||||
}
|
||||
|
||||
void *
|
||||
fmd_case_getspecific(fmd_hdl_t *hdl, fmd_case_t *cp)
|
||||
{
|
||||
(void) hdl;
|
||||
return (cp->ci_data);
|
||||
}
|
||||
|
||||
void
|
||||
fmd_buf_create(fmd_hdl_t *hdl, fmd_case_t *cp, const char *name, size_t size)
|
||||
{
|
||||
assert(strcmp(name, "data") == 0), (void) name;
|
||||
assert(strcmp(name, "data") == 0);
|
||||
assert(cp->ci_bufptr == NULL);
|
||||
assert(size < (1024 * 1024));
|
||||
|
||||
@@ -460,24 +467,22 @@ void
|
||||
fmd_buf_read(fmd_hdl_t *hdl, fmd_case_t *cp,
|
||||
const char *name, void *buf, size_t size)
|
||||
{
|
||||
(void) hdl;
|
||||
assert(strcmp(name, "data") == 0), (void) name;
|
||||
assert(strcmp(name, "data") == 0);
|
||||
assert(cp->ci_bufptr != NULL);
|
||||
assert(size <= cp->ci_bufsiz);
|
||||
|
||||
memcpy(buf, cp->ci_bufptr, size);
|
||||
bcopy(cp->ci_bufptr, buf, size);
|
||||
}
|
||||
|
||||
void
|
||||
fmd_buf_write(fmd_hdl_t *hdl, fmd_case_t *cp,
|
||||
const char *name, const void *buf, size_t size)
|
||||
{
|
||||
(void) hdl;
|
||||
assert(strcmp(name, "data") == 0), (void) name;
|
||||
assert(strcmp(name, "data") == 0);
|
||||
assert(cp->ci_bufptr != NULL);
|
||||
assert(cp->ci_bufsiz >= size);
|
||||
|
||||
memcpy(cp->ci_bufptr, buf, size);
|
||||
bcopy(buf, cp->ci_bufptr, size);
|
||||
}
|
||||
|
||||
/* SERD Engines */
|
||||
@@ -514,19 +519,6 @@ fmd_serd_exists(fmd_hdl_t *hdl, const char *name)
|
||||
return (fmd_serd_eng_lookup(&mp->mod_serds, name) != NULL);
|
||||
}
|
||||
|
||||
int
|
||||
fmd_serd_active(fmd_hdl_t *hdl, const char *name)
|
||||
{
|
||||
fmd_module_t *mp = (fmd_module_t *)hdl;
|
||||
fmd_serd_eng_t *sgp;
|
||||
|
||||
if ((sgp = fmd_serd_eng_lookup(&mp->mod_serds, name)) == NULL) {
|
||||
zed_log_msg(LOG_ERR, "serd engine '%s' does not exist", name);
|
||||
return (0);
|
||||
}
|
||||
return (fmd_serd_eng_fired(sgp) || !fmd_serd_eng_empty(sgp));
|
||||
}
|
||||
|
||||
void
|
||||
fmd_serd_reset(fmd_hdl_t *hdl, const char *name)
|
||||
{
|
||||
@@ -535,10 +527,12 @@ fmd_serd_reset(fmd_hdl_t *hdl, const char *name)
|
||||
|
||||
if ((sgp = fmd_serd_eng_lookup(&mp->mod_serds, name)) == NULL) {
|
||||
zed_log_msg(LOG_ERR, "serd engine '%s' does not exist", name);
|
||||
} else {
|
||||
fmd_serd_eng_reset(sgp);
|
||||
fmd_hdl_debug(hdl, "serd_reset %s", name);
|
||||
return;
|
||||
}
|
||||
|
||||
fmd_serd_eng_reset(sgp);
|
||||
|
||||
fmd_hdl_debug(hdl, "serd_reset %s", name);
|
||||
}
|
||||
|
||||
int
|
||||
@@ -546,21 +540,16 @@ fmd_serd_record(fmd_hdl_t *hdl, const char *name, fmd_event_t *ep)
|
||||
{
|
||||
fmd_module_t *mp = (fmd_module_t *)hdl;
|
||||
fmd_serd_eng_t *sgp;
|
||||
int err;
|
||||
|
||||
if ((sgp = fmd_serd_eng_lookup(&mp->mod_serds, name)) == NULL) {
|
||||
zed_log_msg(LOG_ERR, "failed to add record to SERD engine '%s'",
|
||||
name);
|
||||
return (0);
|
||||
return (FMD_B_FALSE);
|
||||
}
|
||||
return (fmd_serd_eng_record(sgp, ep->ev_hrt));
|
||||
}
|
||||
err = fmd_serd_eng_record(sgp, ep->ev_hrt);
|
||||
|
||||
void
|
||||
fmd_serd_gc(fmd_hdl_t *hdl)
|
||||
{
|
||||
fmd_module_t *mp = (fmd_module_t *)hdl;
|
||||
|
||||
fmd_serd_hash_apply(&mp->mod_serds, fmd_serd_eng_gc, NULL);
|
||||
return (err);
|
||||
}
|
||||
|
||||
/* FMD Timers */
|
||||
@@ -574,10 +563,10 @@ _timer_notify(union sigval sv)
|
||||
const fmd_hdl_ops_t *ops = mp->mod_info->fmdi_ops;
|
||||
struct itimerspec its;
|
||||
|
||||
fmd_hdl_debug(hdl, "%s timer fired (%p)", mp->mod_name, ftp->ft_tid);
|
||||
fmd_hdl_debug(hdl, "timer fired (%p)", ftp->ft_tid);
|
||||
|
||||
/* disarm the timer */
|
||||
memset(&its, 0, sizeof (struct itimerspec));
|
||||
bzero(&its, sizeof (struct itimerspec));
|
||||
timer_settime(ftp->ft_tid, 0, &its, NULL);
|
||||
|
||||
/* Note that the fmdo_timeout can remove this timer */
|
||||
@@ -593,7 +582,6 @@ _timer_notify(union sigval sv)
|
||||
fmd_timer_t *
|
||||
fmd_timer_install(fmd_hdl_t *hdl, void *arg, fmd_event_t *ep, hrtime_t delta)
|
||||
{
|
||||
(void) ep;
|
||||
struct sigevent sev;
|
||||
struct itimerspec its;
|
||||
fmd_timer_t *ftp;
|
||||
@@ -611,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);
|
||||
@@ -638,7 +625,6 @@ nvlist_t *
|
||||
fmd_nvl_create_fault(fmd_hdl_t *hdl, const char *class, uint8_t certainty,
|
||||
nvlist_t *asru, nvlist_t *fru, nvlist_t *resource)
|
||||
{
|
||||
(void) hdl;
|
||||
nvlist_t *nvl;
|
||||
int err = 0;
|
||||
|
||||
@@ -702,8 +688,7 @@ fmd_strmatch(const char *s, const char *p)
|
||||
int
|
||||
fmd_nvl_class_match(fmd_hdl_t *hdl, nvlist_t *nvl, const char *pattern)
|
||||
{
|
||||
(void) hdl;
|
||||
const char *class;
|
||||
char *class;
|
||||
|
||||
return (nvl != NULL &&
|
||||
nvlist_lookup_string(nvl, FM_CLASS, &class) == 0 &&
|
||||
@@ -713,7 +698,6 @@ fmd_nvl_class_match(fmd_hdl_t *hdl, nvlist_t *nvl, const char *pattern)
|
||||
nvlist_t *
|
||||
fmd_nvl_alloc(fmd_hdl_t *hdl, int flags)
|
||||
{
|
||||
(void) hdl, (void) flags;
|
||||
nvlist_t *nvl = NULL;
|
||||
|
||||
if (nvlist_alloc(&nvl, NV_UNIQUE_NAME, 0) != 0)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or https://opensource.org/licenses/CDDL-1.0.
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
@@ -72,6 +72,10 @@ typedef struct fmd_case {
|
||||
} fmd_case_t;
|
||||
|
||||
|
||||
#define FMD_B_FALSE 0 /* false value for booleans as int */
|
||||
#define FMD_B_TRUE 1 /* true value for booleans as int */
|
||||
|
||||
|
||||
#define FMD_CASE_UNSOLVED 0 /* case is not yet solved (waiting) */
|
||||
#define FMD_CASE_SOLVED 1 /* case is solved (suspects added) */
|
||||
#define FMD_CASE_CLOSE_WAIT 2 /* case is executing fmdo_close() */
|
||||
@@ -151,6 +155,7 @@ extern void fmd_hdl_vdebug(fmd_hdl_t *, const char *, va_list);
|
||||
extern void fmd_hdl_debug(fmd_hdl_t *, const char *, ...);
|
||||
|
||||
extern int32_t fmd_prop_get_int32(fmd_hdl_t *, const char *);
|
||||
extern int64_t fmd_prop_get_int64(fmd_hdl_t *, const char *);
|
||||
|
||||
#define FMD_STAT_NOALLOC 0x0 /* fmd should use caller's memory */
|
||||
#define FMD_STAT_ALLOC 0x1 /* fmd should allocate stats memory */
|
||||
@@ -171,7 +176,8 @@ extern int fmd_case_uuclosed(fmd_hdl_t *, const char *);
|
||||
extern int fmd_case_uuisresolved(fmd_hdl_t *, const char *);
|
||||
extern void fmd_case_uuresolved(fmd_hdl_t *, const char *);
|
||||
|
||||
extern boolean_t fmd_case_solved(fmd_hdl_t *, fmd_case_t *);
|
||||
extern int fmd_case_solved(fmd_hdl_t *, fmd_case_t *);
|
||||
extern int fmd_case_closed(fmd_hdl_t *, fmd_case_t *);
|
||||
|
||||
extern void fmd_case_add_ereport(fmd_hdl_t *, fmd_case_t *, fmd_event_t *);
|
||||
extern void fmd_case_add_serd(fmd_hdl_t *, fmd_case_t *, const char *);
|
||||
@@ -194,12 +200,10 @@ extern size_t fmd_buf_size(fmd_hdl_t *, fmd_case_t *, const char *);
|
||||
extern void fmd_serd_create(fmd_hdl_t *, const char *, uint_t, hrtime_t);
|
||||
extern void fmd_serd_destroy(fmd_hdl_t *, const char *);
|
||||
extern int fmd_serd_exists(fmd_hdl_t *, const char *);
|
||||
extern int fmd_serd_active(fmd_hdl_t *, const char *);
|
||||
extern void fmd_serd_reset(fmd_hdl_t *, const char *);
|
||||
extern int fmd_serd_record(fmd_hdl_t *, const char *, fmd_event_t *);
|
||||
extern int fmd_serd_fired(fmd_hdl_t *, const char *);
|
||||
extern int fmd_serd_empty(fmd_hdl_t *, const char *);
|
||||
extern void fmd_serd_gc(fmd_hdl_t *);
|
||||
|
||||
extern id_t fmd_timer_install(fmd_hdl_t *, void *, fmd_event_t *, hrtime_t);
|
||||
extern void fmd_timer_remove(fmd_hdl_t *, id_t);
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or https://opensource.org/licenses/CDDL-1.0.
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
@@ -29,7 +29,7 @@
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <sys/list.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
@@ -74,18 +74,9 @@ fmd_serd_eng_alloc(const char *name, uint64_t n, hrtime_t t)
|
||||
fmd_serd_eng_t *sgp;
|
||||
|
||||
sgp = malloc(sizeof (fmd_serd_eng_t));
|
||||
if (sgp == NULL) {
|
||||
perror("malloc");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
memset(sgp, 0, sizeof (fmd_serd_eng_t));
|
||||
bzero(sgp, sizeof (fmd_serd_eng_t));
|
||||
|
||||
sgp->sg_name = strdup(name);
|
||||
if (sgp->sg_name == NULL) {
|
||||
perror("strdup");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
sgp->sg_flags = FMD_SERD_DIRTY;
|
||||
sgp->sg_n = n;
|
||||
sgp->sg_t = t;
|
||||
@@ -132,12 +123,6 @@ fmd_serd_hash_create(fmd_serd_hash_t *shp)
|
||||
shp->sh_hashlen = FMD_STR_BUCKETS;
|
||||
shp->sh_hash = calloc(shp->sh_hashlen, sizeof (void *));
|
||||
shp->sh_count = 0;
|
||||
|
||||
if (shp->sh_hash == NULL) {
|
||||
perror("calloc");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
@@ -154,7 +139,7 @@ fmd_serd_hash_destroy(fmd_serd_hash_t *shp)
|
||||
}
|
||||
|
||||
free(shp->sh_hash);
|
||||
memset(shp, 0, sizeof (fmd_serd_hash_t));
|
||||
bzero(shp, sizeof (fmd_serd_hash_t));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -249,17 +234,13 @@ fmd_serd_eng_record(fmd_serd_eng_t *sgp, hrtime_t hrt)
|
||||
if (sgp->sg_flags & FMD_SERD_FIRED) {
|
||||
serd_log_msg(" SERD Engine: record %s already fired!",
|
||||
sgp->sg_name);
|
||||
return (B_FALSE);
|
||||
return (FMD_B_FALSE);
|
||||
}
|
||||
|
||||
while (sgp->sg_count >= sgp->sg_n)
|
||||
fmd_serd_eng_discard(sgp, list_tail(&sgp->sg_list));
|
||||
|
||||
sep = malloc(sizeof (fmd_serd_elem_t));
|
||||
if (sep == NULL) {
|
||||
perror("malloc");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
sep->se_hrt = hrt;
|
||||
|
||||
list_insert_head(&sgp->sg_list, sep);
|
||||
@@ -278,11 +259,11 @@ fmd_serd_eng_record(fmd_serd_eng_t *sgp, hrtime_t hrt)
|
||||
fmd_event_delta(oep->se_hrt, sep->se_hrt) <= sgp->sg_t) {
|
||||
sgp->sg_flags |= FMD_SERD_FIRED | FMD_SERD_DIRTY;
|
||||
serd_log_msg(" SERD Engine: fired %s", sgp->sg_name);
|
||||
return (B_TRUE);
|
||||
return (FMD_B_TRUE);
|
||||
}
|
||||
|
||||
sgp->sg_flags |= FMD_SERD_DIRTY;
|
||||
return (B_FALSE);
|
||||
return (FMD_B_FALSE);
|
||||
}
|
||||
|
||||
int
|
||||
@@ -300,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));
|
||||
@@ -310,9 +291,8 @@ fmd_serd_eng_reset(fmd_serd_eng_t *sgp)
|
||||
}
|
||||
|
||||
void
|
||||
fmd_serd_eng_gc(fmd_serd_eng_t *sgp, void *arg)
|
||||
fmd_serd_eng_gc(fmd_serd_eng_t *sgp)
|
||||
{
|
||||
(void) arg;
|
||||
fmd_serd_elem_t *sep, *nep;
|
||||
hrtime_t hrt;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or https://opensource.org/licenses/CDDL-1.0.
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
@@ -77,7 +77,7 @@ extern int fmd_serd_eng_fired(fmd_serd_eng_t *);
|
||||
extern int fmd_serd_eng_empty(fmd_serd_eng_t *);
|
||||
|
||||
extern void fmd_serd_eng_reset(fmd_serd_eng_t *);
|
||||
extern void fmd_serd_eng_gc(fmd_serd_eng_t *, void *);
|
||||
extern void fmd_serd_eng_gc(fmd_serd_eng_t *);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
+50
-137
@@ -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;
|
||||
const char *gs_devid;
|
||||
device_type_t gs_vdev_type;
|
||||
uint64_t gs_vdev_expandtime; /* vdev expansion time */
|
||||
char *gs_devid;
|
||||
} 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;
|
||||
const char *path = NULL;
|
||||
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;
|
||||
const 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);
|
||||
}
|
||||
|
||||
@@ -351,8 +248,6 @@ zfs_agent_dispatch(const char *class, const char *subclass, nvlist_t *nvl)
|
||||
static void *
|
||||
zfs_agent_consumer_thread(void *arg)
|
||||
{
|
||||
(void) arg;
|
||||
|
||||
for (;;) {
|
||||
agent_event_t *event;
|
||||
|
||||
@@ -369,7 +264,9 @@ zfs_agent_consumer_thread(void *arg)
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
if ((event = list_remove_head(&agent_events)) != NULL) {
|
||||
if ((event = (list_head(&agent_events))) != NULL) {
|
||||
list_remove(&agent_events, event);
|
||||
|
||||
(void) pthread_mutex_unlock(&agent_lock);
|
||||
|
||||
/* dispatch to all event subscribers */
|
||||
@@ -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
|
||||
@@ -432,7 +328,8 @@ zfs_agent_fini(void)
|
||||
(void) pthread_join(g_agents_tid, NULL);
|
||||
|
||||
/* drain any pending events */
|
||||
while ((event = list_remove_head(&agent_events)) != NULL) {
|
||||
while ((event = (list_head(&agent_events))) != NULL) {
|
||||
list_remove(&agent_events, event);
|
||||
nvlist_free(event->ae_nvl);
|
||||
free(event);
|
||||
}
|
||||
@@ -453,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
|
||||
|
||||
+127
-204
@@ -6,7 +6,7 @@
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or https://opensource.org/licenses/CDDL-1.0.
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
@@ -23,11 +23,10 @@
|
||||
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright 2015 Nexenta Systems, Inc. All rights reserved.
|
||||
* Copyright (c) 2016, Intel Corporation.
|
||||
* Copyright (c) 2023, Klara Inc.
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
#include <libuutil.h>
|
||||
#include <libzfs.h>
|
||||
#include <sys/types.h>
|
||||
@@ -35,29 +34,14 @@
|
||||
#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"
|
||||
|
||||
/*
|
||||
* Default values for the serd engine when processing checksum or io errors. The
|
||||
* semantics are N <events> in T <seconds>.
|
||||
*/
|
||||
#define DEFAULT_CHECKSUM_N 10 /* events */
|
||||
#define DEFAULT_CHECKSUM_T 600 /* seconds */
|
||||
#define DEFAULT_IO_N 10 /* events */
|
||||
#define DEFAULT_IO_T 600 /* seconds */
|
||||
#define DEFAULT_SLOW_IO_N 10 /* events */
|
||||
#define DEFAULT_SLOW_IO_T 30 /* seconds */
|
||||
|
||||
#define CASE_GC_TIMEOUT_SECS 43200 /* 12 hours */
|
||||
|
||||
/*
|
||||
* Our serd engines are named in the following format:
|
||||
* 'zfs_<pool_guid>_<vdev_guid>_{checksum,io,slow_io}'
|
||||
* This #define reserves enough space for two 64-bit hex values plus the
|
||||
* length of the longest string.
|
||||
* Our serd engines are named 'zfs_<pool_guid>_<vdev_guid>_{checksum,io}'. This
|
||||
* #define reserves enough space for two 64-bit hex values plus the length of
|
||||
* the longest string.
|
||||
*/
|
||||
#define MAX_SERDLEN (16 * 2 + sizeof ("zfs___checksum"))
|
||||
|
||||
@@ -74,7 +58,6 @@ typedef struct zfs_case_data {
|
||||
int zc_pool_state;
|
||||
char zc_serd_checksum[MAX_SERDLEN];
|
||||
char zc_serd_io[MAX_SERDLEN];
|
||||
char zc_serd_slow_io[MAX_SERDLEN];
|
||||
int zc_has_remove_timer;
|
||||
} zfs_case_data_t;
|
||||
|
||||
@@ -121,8 +104,7 @@ zfs_de_stats_t zfs_stats = {
|
||||
{ "resource_drops", FMD_TYPE_UINT64, "resource related ereports" }
|
||||
};
|
||||
|
||||
/* wait 15 seconds after a removal */
|
||||
static hrtime_t zfs_remove_timeout = SEC2NSEC(15);
|
||||
static hrtime_t zfs_remove_timeout;
|
||||
|
||||
uu_list_pool_t *zfs_case_pool;
|
||||
uu_list_t *zfs_cases;
|
||||
@@ -132,13 +114,11 @@ uu_list_t *zfs_cases;
|
||||
#define ZFS_MAKE_EREPORT(type) \
|
||||
FM_EREPORT_CLASS "." ZFS_ERROR_CLASS "." type
|
||||
|
||||
static void zfs_purge_cases(fmd_hdl_t *hdl);
|
||||
|
||||
/*
|
||||
* Write out the persistent representation of an active case.
|
||||
*/
|
||||
static void
|
||||
zfs_case_serialize(zfs_case_t *zcp)
|
||||
zfs_case_serialize(fmd_hdl_t *hdl, zfs_case_t *zcp)
|
||||
{
|
||||
zcp->zc_data.zc_version = CASE_DATA_VERSION_SERD;
|
||||
}
|
||||
@@ -180,42 +160,6 @@ zfs_case_unserialize(fmd_hdl_t *hdl, fmd_case_t *cp)
|
||||
return (zcp);
|
||||
}
|
||||
|
||||
/*
|
||||
* count other unique slow-io cases in a pool
|
||||
*/
|
||||
static uint_t
|
||||
zfs_other_slow_cases(fmd_hdl_t *hdl, const zfs_case_data_t *zfs_case)
|
||||
{
|
||||
zfs_case_t *zcp;
|
||||
uint_t cases = 0;
|
||||
static hrtime_t next_check = 0;
|
||||
|
||||
/*
|
||||
* Note that plumbing in some external GC would require adding locking,
|
||||
* since most of this module code is not thread safe and assumes there
|
||||
* is only one thread running against the module. So we perform GC here
|
||||
* inline periodically so that future delay induced faults will be
|
||||
* possible once the issue causing multiple vdev delays is resolved.
|
||||
*/
|
||||
if (gethrestime_sec() > next_check) {
|
||||
/* Periodically purge old SERD entries and stale cases */
|
||||
fmd_serd_gc(hdl);
|
||||
zfs_purge_cases(hdl);
|
||||
next_check = gethrestime_sec() + CASE_GC_TIMEOUT_SECS;
|
||||
}
|
||||
|
||||
for (zcp = uu_list_first(zfs_cases); zcp != NULL;
|
||||
zcp = uu_list_next(zfs_cases, zcp)) {
|
||||
if (zcp->zc_data.zc_pool_guid == zfs_case->zc_pool_guid &&
|
||||
zcp->zc_data.zc_vdev_guid != zfs_case->zc_vdev_guid &&
|
||||
zcp->zc_data.zc_serd_slow_io[0] != '\0' &&
|
||||
fmd_serd_active(hdl, zcp->zc_data.zc_serd_slow_io)) {
|
||||
cases++;
|
||||
}
|
||||
}
|
||||
return (cases);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over any active cases. If any cases are associated with a pool or
|
||||
* vdev which is no longer present on the system, close the associated case.
|
||||
@@ -223,12 +167,14 @@ zfs_other_slow_cases(fmd_hdl_t *hdl, const zfs_case_data_t *zfs_case)
|
||||
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.
|
||||
@@ -264,10 +210,10 @@ zfs_mark_vdev(uint64_t pool_guid, nvlist_t *vd, er_timeval_t *loaded)
|
||||
}
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
static int
|
||||
zfs_mark_pool(zpool_handle_t *zhp, void *unused)
|
||||
{
|
||||
(void) unused;
|
||||
zfs_case_t *zcp;
|
||||
uint64_t pool_guid;
|
||||
uint64_t *tod;
|
||||
@@ -307,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);
|
||||
|
||||
@@ -422,25 +365,23 @@ zfs_serd_name(char *buf, uint64_t pool_guid, uint64_t vdev_guid,
|
||||
(long long unsigned int)vdev_guid, type);
|
||||
}
|
||||
|
||||
static void
|
||||
zfs_case_retire(fmd_hdl_t *hdl, zfs_case_t *zcp)
|
||||
{
|
||||
fmd_hdl_debug(hdl, "retiring case");
|
||||
|
||||
fmd_case_close(hdl, zcp->zc_case);
|
||||
}
|
||||
|
||||
/*
|
||||
* Solve a given ZFS case. This first checks to make sure the diagnosis is
|
||||
* still valid, as well as cleaning up any pending timer associated with the
|
||||
* case.
|
||||
*/
|
||||
static void
|
||||
zfs_case_solve(fmd_hdl_t *hdl, zfs_case_t *zcp, const char *faultname)
|
||||
zfs_case_solve(fmd_hdl_t *hdl, zfs_case_t *zcp, const char *faultname,
|
||||
boolean_t checkunusable)
|
||||
{
|
||||
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);
|
||||
|
||||
/*
|
||||
@@ -459,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);
|
||||
@@ -474,7 +473,7 @@ zfs_case_solve(fmd_hdl_t *hdl, zfs_case_t *zcp, const char *faultname)
|
||||
serialize = B_TRUE;
|
||||
}
|
||||
if (serialize)
|
||||
zfs_case_serialize(zcp);
|
||||
zfs_case_serialize(hdl, zcp);
|
||||
|
||||
nvlist_free(detector);
|
||||
}
|
||||
@@ -486,10 +485,10 @@ timeval_earlier(er_timeval_t *a, er_timeval_t *b)
|
||||
(a->ertv_sec == b->ertv_sec && a->ertv_nsec < b->ertv_nsec));
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
static void
|
||||
zfs_ereport_when(fmd_hdl_t *hdl, nvlist_t *nvl, er_timeval_t *when)
|
||||
{
|
||||
(void) hdl;
|
||||
int64_t *tod;
|
||||
uint_t nelem;
|
||||
|
||||
@@ -505,20 +504,19 @@ zfs_ereport_when(fmd_hdl_t *hdl, nvlist_t *nvl, er_timeval_t *when)
|
||||
/*
|
||||
* Main fmd entry point.
|
||||
*/
|
||||
/*ARGSUSED*/
|
||||
static void
|
||||
zfs_fm_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class)
|
||||
{
|
||||
zfs_case_t *zcp, *dcp;
|
||||
int32_t pool_state;
|
||||
uint64_t ena, pool_guid, vdev_guid;
|
||||
uint64_t checksum_n, checksum_t;
|
||||
uint64_t io_n, io_t;
|
||||
er_timeval_t pool_load;
|
||||
er_timeval_t er_when;
|
||||
nvlist_t *detector;
|
||||
boolean_t pool_found = B_FALSE;
|
||||
boolean_t isresource;
|
||||
const char *type;
|
||||
char *type;
|
||||
|
||||
/*
|
||||
* We subscribe to notifications for vdev or pool removal. In these
|
||||
@@ -686,7 +684,9 @@ zfs_fm_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class)
|
||||
if (strcmp(class,
|
||||
ZFS_MAKE_EREPORT(FM_EREPORT_ZFS_DATA)) == 0 ||
|
||||
strcmp(class,
|
||||
ZFS_MAKE_EREPORT(FM_EREPORT_ZFS_CONFIG_CACHE_WRITE)) == 0) {
|
||||
ZFS_MAKE_EREPORT(FM_EREPORT_ZFS_CONFIG_CACHE_WRITE)) == 0 ||
|
||||
strcmp(class,
|
||||
ZFS_MAKE_EREPORT(FM_EREPORT_ZFS_DELAY)) == 0) {
|
||||
zfs_stats.resource_drops.fmds_value.ui64++;
|
||||
return;
|
||||
}
|
||||
@@ -747,16 +747,13 @@ zfs_fm_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class)
|
||||
if (zcp->zc_data.zc_has_remove_timer) {
|
||||
fmd_timer_remove(hdl, zcp->zc_remove_timer);
|
||||
zcp->zc_data.zc_has_remove_timer = 0;
|
||||
zfs_case_serialize(zcp);
|
||||
zfs_case_serialize(hdl, zcp);
|
||||
}
|
||||
if (zcp->zc_data.zc_serd_io[0] != '\0')
|
||||
fmd_serd_reset(hdl, zcp->zc_data.zc_serd_io);
|
||||
if (zcp->zc_data.zc_serd_checksum[0] != '\0')
|
||||
fmd_serd_reset(hdl,
|
||||
zcp->zc_data.zc_serd_checksum);
|
||||
if (zcp->zc_data.zc_serd_slow_io[0] != '\0')
|
||||
fmd_serd_reset(hdl,
|
||||
zcp->zc_data.zc_serd_slow_io);
|
||||
} else if (fmd_nvl_class_match(hdl, nvl,
|
||||
ZFS_MAKE_RSRC(FM_RESOURCE_STATECHANGE))) {
|
||||
uint64_t state = 0;
|
||||
@@ -785,11 +782,7 @@ zfs_fm_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class)
|
||||
if (fmd_case_solved(hdl, zcp->zc_case))
|
||||
return;
|
||||
|
||||
if (vdev_guid)
|
||||
fmd_hdl_debug(hdl, "error event '%s', vdev %llu", class,
|
||||
vdev_guid);
|
||||
else
|
||||
fmd_hdl_debug(hdl, "error event '%s'", class);
|
||||
fmd_hdl_debug(hdl, "error event '%s'", class);
|
||||
|
||||
/*
|
||||
* Determine if we should solve the case and generate a fault. We solve
|
||||
@@ -819,18 +812,18 @@ zfs_fm_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class)
|
||||
fmd_case_close(hdl, dcp->zc_case);
|
||||
}
|
||||
|
||||
zfs_case_solve(hdl, zcp, "fault.fs.zfs.pool");
|
||||
zfs_case_solve(hdl, zcp, "fault.fs.zfs.pool", B_TRUE);
|
||||
} else if (fmd_nvl_class_match(hdl, nvl,
|
||||
ZFS_MAKE_EREPORT(FM_EREPORT_ZFS_LOG_REPLAY))) {
|
||||
/*
|
||||
* Pool level fault for reading the intent logs.
|
||||
*/
|
||||
zfs_case_solve(hdl, zcp, "fault.fs.zfs.log_replay");
|
||||
zfs_case_solve(hdl, zcp, "fault.fs.zfs.log_replay", B_TRUE);
|
||||
} else if (fmd_nvl_class_match(hdl, nvl, "ereport.fs.zfs.vdev.*")) {
|
||||
/*
|
||||
* Device fault.
|
||||
*/
|
||||
zfs_case_solve(hdl, zcp, "fault.fs.zfs.device");
|
||||
zfs_case_solve(hdl, zcp, "fault.fs.zfs.device", B_TRUE);
|
||||
} else if (fmd_nvl_class_match(hdl, nvl,
|
||||
ZFS_MAKE_EREPORT(FM_EREPORT_ZFS_IO)) ||
|
||||
fmd_nvl_class_match(hdl, nvl,
|
||||
@@ -838,13 +831,9 @@ zfs_fm_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class)
|
||||
fmd_nvl_class_match(hdl, nvl,
|
||||
ZFS_MAKE_EREPORT(FM_EREPORT_ZFS_IO_FAILURE)) ||
|
||||
fmd_nvl_class_match(hdl, nvl,
|
||||
ZFS_MAKE_EREPORT(FM_EREPORT_ZFS_DELAY)) ||
|
||||
fmd_nvl_class_match(hdl, nvl,
|
||||
ZFS_MAKE_EREPORT(FM_EREPORT_ZFS_PROBE_FAILURE))) {
|
||||
const char *failmode = NULL;
|
||||
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
|
||||
@@ -856,113 +845,30 @@ zfs_fm_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class)
|
||||
if (fmd_nvl_class_match(hdl, nvl,
|
||||
ZFS_MAKE_EREPORT(FM_EREPORT_ZFS_IO))) {
|
||||
if (zcp->zc_data.zc_serd_io[0] == '\0') {
|
||||
if (nvlist_lookup_uint64(nvl,
|
||||
FM_EREPORT_PAYLOAD_ZFS_VDEV_IO_N,
|
||||
&io_n) != 0) {
|
||||
io_n = DEFAULT_IO_N;
|
||||
}
|
||||
if (nvlist_lookup_uint64(nvl,
|
||||
FM_EREPORT_PAYLOAD_ZFS_VDEV_IO_T,
|
||||
&io_t) != 0) {
|
||||
io_t = DEFAULT_IO_T;
|
||||
}
|
||||
zfs_serd_name(zcp->zc_data.zc_serd_io,
|
||||
pool_guid, vdev_guid, "io");
|
||||
fmd_serd_create(hdl, zcp->zc_data.zc_serd_io,
|
||||
io_n,
|
||||
SEC2NSEC(io_t));
|
||||
zfs_case_serialize(zcp);
|
||||
fmd_prop_get_int32(hdl, "io_N"),
|
||||
fmd_prop_get_int64(hdl, "io_T"));
|
||||
zfs_case_serialize(hdl, zcp);
|
||||
}
|
||||
if (fmd_serd_record(hdl, zcp->zc_data.zc_serd_io, ep))
|
||||
checkremove = B_TRUE;
|
||||
} else if (fmd_nvl_class_match(hdl, nvl,
|
||||
ZFS_MAKE_EREPORT(FM_EREPORT_ZFS_DELAY))) {
|
||||
uint64_t slow_io_n, slow_io_t;
|
||||
|
||||
/*
|
||||
* Create a slow io SERD engine when the VDEV has the
|
||||
* 'vdev_slow_io_n' and 'vdev_slow_io_n' properties.
|
||||
*/
|
||||
if (zcp->zc_data.zc_serd_slow_io[0] == '\0' &&
|
||||
nvlist_lookup_uint64(nvl,
|
||||
FM_EREPORT_PAYLOAD_ZFS_VDEV_SLOW_IO_N,
|
||||
&slow_io_n) == 0 &&
|
||||
nvlist_lookup_uint64(nvl,
|
||||
FM_EREPORT_PAYLOAD_ZFS_VDEV_SLOW_IO_T,
|
||||
&slow_io_t) == 0) {
|
||||
zfs_serd_name(zcp->zc_data.zc_serd_slow_io,
|
||||
pool_guid, vdev_guid, "slow_io");
|
||||
fmd_serd_create(hdl,
|
||||
zcp->zc_data.zc_serd_slow_io,
|
||||
slow_io_n,
|
||||
SEC2NSEC(slow_io_t));
|
||||
zfs_case_serialize(zcp);
|
||||
}
|
||||
/* Pass event to SERD engine and see if this triggers */
|
||||
if (zcp->zc_data.zc_serd_slow_io[0] != '\0' &&
|
||||
fmd_serd_record(hdl, zcp->zc_data.zc_serd_slow_io,
|
||||
ep)) {
|
||||
/*
|
||||
* Ignore a slow io diagnosis when other
|
||||
* VDEVs in the pool show signs of being slow.
|
||||
*/
|
||||
if (zfs_other_slow_cases(hdl, &zcp->zc_data)) {
|
||||
zfs_case_retire(hdl, zcp);
|
||||
fmd_hdl_debug(hdl, "pool %llu has "
|
||||
"multiple slow io cases -- skip "
|
||||
"degrading vdev %llu",
|
||||
(u_longlong_t)
|
||||
zcp->zc_data.zc_pool_guid,
|
||||
(u_longlong_t)
|
||||
zcp->zc_data.zc_vdev_guid);
|
||||
} else {
|
||||
zfs_case_solve(hdl, zcp,
|
||||
"fault.fs.zfs.vdev.slow_io");
|
||||
}
|
||||
}
|
||||
} 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') {
|
||||
if (nvlist_lookup_uint64(nvl,
|
||||
FM_EREPORT_PAYLOAD_ZFS_VDEV_CKSUM_N,
|
||||
&checksum_n) != 0) {
|
||||
checksum_n = DEFAULT_CHECKSUM_N;
|
||||
}
|
||||
if (nvlist_lookup_uint64(nvl,
|
||||
FM_EREPORT_PAYLOAD_ZFS_VDEV_CKSUM_T,
|
||||
&checksum_t) != 0) {
|
||||
checksum_t = DEFAULT_CHECKSUM_T;
|
||||
}
|
||||
|
||||
zfs_serd_name(zcp->zc_data.zc_serd_checksum,
|
||||
pool_guid, vdev_guid, "checksum");
|
||||
fmd_serd_create(hdl,
|
||||
zcp->zc_data.zc_serd_checksum,
|
||||
checksum_n,
|
||||
SEC2NSEC(checksum_t));
|
||||
zfs_case_serialize(zcp);
|
||||
fmd_prop_get_int32(hdl, "checksum_N"),
|
||||
fmd_prop_get_int64(hdl, "checksum_T"));
|
||||
zfs_case_serialize(hdl, zcp);
|
||||
}
|
||||
if (fmd_serd_record(hdl,
|
||||
zcp->zc_data.zc_serd_checksum, ep)) {
|
||||
zfs_case_solve(hdl, zcp,
|
||||
"fault.fs.zfs.vdev.checksum");
|
||||
"fault.fs.zfs.vdev.checksum", B_FALSE);
|
||||
}
|
||||
} else if (fmd_nvl_class_match(hdl, nvl,
|
||||
ZFS_MAKE_EREPORT(FM_EREPORT_ZFS_IO_FAILURE)) &&
|
||||
@@ -972,11 +878,12 @@ zfs_fm_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class)
|
||||
if (strncmp(failmode, FM_EREPORT_FAILMODE_CONTINUE,
|
||||
strlen(FM_EREPORT_FAILMODE_CONTINUE)) == 0) {
|
||||
zfs_case_solve(hdl, zcp,
|
||||
"fault.fs.zfs.io_failure_continue");
|
||||
"fault.fs.zfs.io_failure_continue",
|
||||
B_FALSE);
|
||||
} else if (strncmp(failmode, FM_EREPORT_FAILMODE_WAIT,
|
||||
strlen(FM_EREPORT_FAILMODE_WAIT)) == 0) {
|
||||
zfs_case_solve(hdl, zcp,
|
||||
"fault.fs.zfs.io_failure_wait");
|
||||
"fault.fs.zfs.io_failure_wait", B_FALSE);
|
||||
}
|
||||
} else if (fmd_nvl_class_match(hdl, nvl,
|
||||
ZFS_MAKE_EREPORT(FM_EREPORT_ZFS_PROBE_FAILURE))) {
|
||||
@@ -998,7 +905,7 @@ zfs_fm_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class)
|
||||
zfs_remove_timeout);
|
||||
if (!zcp->zc_data.zc_has_remove_timer) {
|
||||
zcp->zc_data.zc_has_remove_timer = 1;
|
||||
zfs_case_serialize(zcp);
|
||||
zfs_case_serialize(hdl, zcp);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1008,13 +915,14 @@ zfs_fm_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl, const char *class)
|
||||
* The timeout is fired when we diagnosed an I/O error, and it was not due to
|
||||
* device removal (which would cause the timeout to be cancelled).
|
||||
*/
|
||||
/* ARGSUSED */
|
||||
static void
|
||||
zfs_fm_timeout(fmd_hdl_t *hdl, id_t id, void *data)
|
||||
{
|
||||
zfs_case_t *zcp = data;
|
||||
|
||||
if (id == zcp->zc_remove_timer)
|
||||
zfs_case_solve(hdl, zcp, "fault.fs.zfs.vdev.io");
|
||||
zfs_case_solve(hdl, zcp, "fault.fs.zfs.vdev.io", B_FALSE);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -1030,8 +938,6 @@ zfs_fm_close(fmd_hdl_t *hdl, fmd_case_t *cs)
|
||||
fmd_serd_destroy(hdl, zcp->zc_data.zc_serd_checksum);
|
||||
if (zcp->zc_data.zc_serd_io[0] != '\0')
|
||||
fmd_serd_destroy(hdl, zcp->zc_data.zc_serd_io);
|
||||
if (zcp->zc_data.zc_serd_slow_io[0] != '\0')
|
||||
fmd_serd_destroy(hdl, zcp->zc_data.zc_serd_slow_io);
|
||||
if (zcp->zc_data.zc_has_remove_timer)
|
||||
fmd_timer_remove(hdl, zcp->zc_remove_timer);
|
||||
|
||||
@@ -1040,15 +946,30 @@ zfs_fm_close(fmd_hdl_t *hdl, fmd_case_t *cs)
|
||||
fmd_hdl_free(hdl, zcp, sizeof (zfs_case_t));
|
||||
}
|
||||
|
||||
/*
|
||||
* We use the fmd gc entry point to look for old cases that no longer apply.
|
||||
* This allows us to keep our set of case data small in a long running system.
|
||||
*/
|
||||
static void
|
||||
zfs_fm_gc(fmd_hdl_t *hdl)
|
||||
{
|
||||
zfs_purge_cases(hdl);
|
||||
}
|
||||
|
||||
static const fmd_hdl_ops_t fmd_ops = {
|
||||
zfs_fm_recv, /* fmdo_recv */
|
||||
zfs_fm_timeout, /* fmdo_timeout */
|
||||
zfs_fm_close, /* fmdo_close */
|
||||
NULL, /* fmdo_stats */
|
||||
NULL, /* fmdo_gc */
|
||||
zfs_fm_gc, /* fmdo_gc */
|
||||
};
|
||||
|
||||
static const fmd_prop_t fmd_props[] = {
|
||||
{ "checksum_N", FMD_TYPE_UINT32, "10" },
|
||||
{ "checksum_T", FMD_TYPE_TIME, "10min" },
|
||||
{ "io_N", FMD_TYPE_UINT32, "10" },
|
||||
{ "io_T", FMD_TYPE_TIME, "10min" },
|
||||
{ "remove_timeout", FMD_TYPE_TIME, "15sec" },
|
||||
{ NULL, 0, NULL }
|
||||
};
|
||||
|
||||
@@ -1061,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;
|
||||
}
|
||||
|
||||
@@ -1089,6 +1010,8 @@ _zfs_diagnosis_init(fmd_hdl_t *hdl)
|
||||
|
||||
(void) fmd_stat_create(hdl, FMD_STAT_NOALLOC, sizeof (zfs_stats) /
|
||||
sizeof (fmd_stat_t), (fmd_stat_t *)&zfs_stats);
|
||||
|
||||
zfs_remove_timeout = fmd_prop_get_int64(hdl, "remove_timeout");
|
||||
}
|
||||
|
||||
void
|
||||
@@ -1115,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);
|
||||
}
|
||||
|
||||
+141
-593
File diff suppressed because it is too large
Load Diff
+169
-198
@@ -6,7 +6,7 @@
|
||||
* You may not use this file except in compliance with the License.
|
||||
*
|
||||
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||
* or https://opensource.org/licenses/CDDL-1.0.
|
||||
* or http://www.opensolaris.org/os/licensing.
|
||||
* See the License for the specific language governing permissions
|
||||
* and limitations under the License.
|
||||
*
|
||||
@@ -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>
|
||||
*/
|
||||
|
||||
/*
|
||||
@@ -38,10 +37,8 @@
|
||||
#include <sys/fs/zfs.h>
|
||||
#include <sys/fm/protocol.h>
|
||||
#include <sys/fm/fs/zfs.h>
|
||||
#include <libzutil.h>
|
||||
#include <libzfs.h>
|
||||
#include <string.h>
|
||||
#include <libgen.h>
|
||||
|
||||
#include "zfs_agents.h"
|
||||
#include "fmd_api.h"
|
||||
@@ -74,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
|
||||
@@ -99,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,
|
||||
@@ -118,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);
|
||||
}
|
||||
|
||||
@@ -127,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.
|
||||
*/
|
||||
@@ -228,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);
|
||||
}
|
||||
@@ -237,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);
|
||||
|
||||
@@ -281,41 +256,25 @@ 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;
|
||||
const 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, (const nvlist_t **)&spares[s], 1);
|
||||
ZPOOL_CONFIG_CHILDREN, &spares[s], 1);
|
||||
|
||||
fmd_hdl_debug(hdl, "zpool_vdev_replace '%s' with spare '%s'",
|
||||
dev_name, zfs_basename(spare_name));
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -323,12 +282,17 @@ replace_with_spare(fmd_hdl_t *hdl, zpool_handle_t *zhp, nvlist_t *vdev)
|
||||
* ASRU is now usable. ZFS has found the device to be present and
|
||||
* functioning.
|
||||
*/
|
||||
/*ARGSUSED*/
|
||||
static void
|
||||
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;
|
||||
@@ -361,11 +366,11 @@ zfs_vdev_repair(fmd_hdl_t *hdl, nvlist_t *nvl)
|
||||
vdev_guid, pool_guid);
|
||||
}
|
||||
|
||||
/*ARGSUSED*/
|
||||
static void
|
||||
zfs_retire_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl,
|
||||
const char *class)
|
||||
{
|
||||
(void) ep;
|
||||
uint64_t pool_guid, vdev_guid;
|
||||
zpool_handle_t *zhp;
|
||||
nvlist_t *resource, *fault;
|
||||
@@ -375,97 +380,34 @@ 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;
|
||||
const char *scheme;
|
||||
char *scheme;
|
||||
nvlist_t *vdev = NULL;
|
||||
const char *uuid;
|
||||
char *uuid;
|
||||
int repair_done = 0;
|
||||
boolean_t retire;
|
||||
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);
|
||||
|
||||
(void) 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))) {
|
||||
const 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 (vdev_guid == 0) {
|
||||
fmd_hdl_debug(hdl, "Got a zero GUID");
|
||||
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 */
|
||||
int remove_status = 0;
|
||||
if (l2arc || (strcmp(class, "resource.fs.zfs.removed") == 0)) {
|
||||
remove_status = zpool_vdev_remove_wanted(zhp, devname);
|
||||
fmd_hdl_debug(hdl, "zpool_vdev_remove_wanted '%s'"
|
||||
", err:%d", devname, libzfs_errno(zhdl));
|
||||
}
|
||||
|
||||
/* Replace the vdev with a spare if its not a l2arc */
|
||||
if (!l2arc && !remove_status &&
|
||||
(!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;
|
||||
}
|
||||
@@ -474,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;
|
||||
}
|
||||
@@ -523,9 +466,6 @@ zfs_retire_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl,
|
||||
} else if (fmd_nvl_class_match(hdl, fault,
|
||||
"fault.fs.zfs.vdev.checksum")) {
|
||||
degrade_device = B_TRUE;
|
||||
} else if (fmd_nvl_class_match(hdl, fault,
|
||||
"fault.fs.zfs.vdev.slow_io")) {
|
||||
degrade_device = B_TRUE;
|
||||
} else if (fmd_nvl_class_match(hdl, fault,
|
||||
"fault.fs.zfs.device")) {
|
||||
fault_device = B_FALSE;
|
||||
@@ -537,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
|
||||
@@ -611,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);
|
||||
}
|
||||
|
||||
@@ -644,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) {
|
||||
@@ -665,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
-52
@@ -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).
|
||||
@@ -36,7 +36,6 @@ static volatile sig_atomic_t _got_hup = 0;
|
||||
static void
|
||||
_exit_handler(int signum)
|
||||
{
|
||||
(void) signum;
|
||||
_got_exit = 1;
|
||||
}
|
||||
|
||||
@@ -46,7 +45,6 @@ _exit_handler(int signum)
|
||||
static void
|
||||
_hup_handler(int signum)
|
||||
{
|
||||
(void) signum;
|
||||
_got_hup = 1;
|
||||
}
|
||||
|
||||
@@ -62,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");
|
||||
|
||||
@@ -77,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");
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -218,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);
|
||||
@@ -239,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 @@
|
||||
zedconfdir = $(sysconfdir)/zfs/zed.d
|
||||
dist_zedconf_DATA = \
|
||||
%D%/zed-functions.sh \
|
||||
%D%/zed.rc
|
||||
|
||||
zedexecdir = $(zfsexecdir)/zed.d
|
||||
|
||||
dist_zedexec_SCRIPTS = \
|
||||
%D%/all-debug.sh \
|
||||
%D%/all-syslog.sh \
|
||||
%D%/data-notify.sh \
|
||||
%D%/generic-notify.sh \
|
||||
%D%/pool_import-led.sh \
|
||||
%D%/resilver_finish-notify.sh \
|
||||
%D%/resilver_finish-start-scrub.sh \
|
||||
%D%/scrub_finish-notify.sh \
|
||||
%D%/statechange-led.sh \
|
||||
%D%/statechange-notify.sh \
|
||||
%D%/statechange-slot_off.sh \
|
||||
%D%/trim_finish-notify.sh \
|
||||
%D%/vdev_attach-led.sh \
|
||||
%D%/vdev_clear-led.sh
|
||||
|
||||
nodist_zedexec_SCRIPTS = \
|
||||
%D%/history_event-zfs-list-cacher.sh
|
||||
|
||||
SUBSTFILES += $(nodist_zedexec_SCRIPTS)
|
||||
|
||||
zedconfdefaults = \
|
||||
all-syslog.sh \
|
||||
data-notify.sh \
|
||||
history_event-zfs-list-cacher.sh \
|
||||
pool_import-led.sh \
|
||||
resilver_finish-notify.sh \
|
||||
resilver_finish-start-scrub.sh \
|
||||
scrub_finish-notify.sh \
|
||||
statechange-led.sh \
|
||||
statechange-notify.sh \
|
||||
statechange-slot_off.sh \
|
||||
vdev_attach-led.sh \
|
||||
vdev_clear-led.sh
|
||||
|
||||
dist_noinst_DATA += %D%/README
|
||||
|
||||
INSTALL_DATA_HOOKS += zed-install-data-hook
|
||||
zed-install-data-hook:
|
||||
$(MKDIR_P) "$(DESTDIR)$(zedconfdir)"
|
||||
set -x; for f in $(zedconfdefaults); do \
|
||||
[ -f "$(DESTDIR)$(zedconfdir)/$${f}" ] ||\
|
||||
[ -L "$(DESTDIR)$(zedconfdir)/$${f}" ] || \
|
||||
$(LN_S) "$(zedexecdir)/$${f}" "$(DESTDIR)$(zedconfdir)"; \
|
||||
done
|
||||
|
||||
SHELLCHECKSCRIPTS += $(dist_zedconf_DATA) $(dist_zedexec_SCRIPTS) $(nodist_zedexec_SCRIPTS)
|
||||
$(call SHELLCHECK_OPTS,$(dist_zedconf_DATA) $(dist_zedexec_SCRIPTS) $(nodist_zedexec_SCRIPTS)): SHELLCHECK_SHELL = sh
|
||||
# False positive: 1>&"${ZED_FLOCK_FD}" looks suspiciously similar to a >&filename bash extension
|
||||
$(call SHELLCHECK_OPTS,$(dist_zedconf_DATA) $(dist_zedexec_SCRIPTS) $(nodist_zedexec_SCRIPTS)): CHECKBASHISMS_IGNORE = -e 'should be >word 2>&1' -e '&"$${ZED_FLOCK_FD}"'
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/bin/sh
|
||||
# shellcheck disable=SC2154
|
||||
#
|
||||
# Log all environment variables to ZED_DEBUG_LOG.
|
||||
#
|
||||
@@ -13,11 +12,15 @@
|
||||
|
||||
zed_exit_if_ignoring_this_event
|
||||
|
||||
zed_lock "${ZED_DEBUG_LOG}"
|
||||
{
|
||||
env | 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,52 +1,14 @@
|
||||
#!/bin/sh
|
||||
# shellcheck disable=SC2154
|
||||
#
|
||||
# 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
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/bin/sh
|
||||
# shellcheck disable=SC2154
|
||||
#
|
||||
# Send notification in response to a DATA error.
|
||||
#
|
||||
@@ -26,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
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/bin/sh
|
||||
# shellcheck disable=SC2154
|
||||
#
|
||||
# Send notification in response to a given zevent.
|
||||
#
|
||||
@@ -24,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
|
||||
|
||||
@@ -32,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,85 +0,0 @@
|
||||
#!/bin/sh
|
||||
# shellcheck disable=SC2154
|
||||
#
|
||||
# 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
|
||||
@@ -1,17 +1,14 @@
|
||||
#!/bin/sh
|
||||
# shellcheck disable=SC2154
|
||||
# resilver_finish-start-scrub.sh
|
||||
# Run a scrub after a resilver
|
||||
#
|
||||
# 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
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/bin/sh
|
||||
# shellcheck disable=SC2154
|
||||
#
|
||||
# Send notification in response to a RESILVER_FINISH or SCRUB_FINISH.
|
||||
#
|
||||
@@ -42,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,27 +1,26 @@
|
||||
#!/bin/sh
|
||||
# shellcheck disable=SC2154
|
||||
#
|
||||
# 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
|
||||
@@ -30,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
|
||||
|
||||
@@ -61,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
|
||||
@@ -72,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
|
||||
@@ -91,85 +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|REMOVED)
|
||||
echo 1
|
||||
;;
|
||||
ONLINE)
|
||||
echo 0
|
||||
;;
|
||||
*)
|
||||
echo "invalid state: $state"
|
||||
;;
|
||||
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"
|
||||
read -r address < "/sys/class/block/$dev/device/address"
|
||||
|
||||
find /sys/bus/pci/slots -regex '.*/[0-9]+/address$' | \
|
||||
while read -r sys_addr; do
|
||||
read -r this_address < "$sys_addr"
|
||||
|
||||
# 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_addr%/*}"
|
||||
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
|
||||
@@ -177,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
|
||||
@@ -208,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")
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/bin/sh
|
||||
# shellcheck disable=SC2154
|
||||
#
|
||||
# CDDL HEADER START
|
||||
#
|
||||
@@ -16,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
|
||||
@@ -32,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"
|
||||
@@ -66,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,64 +0,0 @@
|
||||
#!/bin/sh
|
||||
# shellcheck disable=SC3014,SC2154,SC2086,SC2034
|
||||
#
|
||||
# Turn off disk's enclosure slot if it becomes FAULTED.
|
||||
#
|
||||
# Bad SCSI disks can often "disappear and reappear" causing all sorts of chaos
|
||||
# as they flip between FAULTED and ONLINE. If
|
||||
# ZED_POWER_OFF_ENCLOSURE_SLOT_ON_FAULT is set in zed.rc, and the disk gets
|
||||
# FAULTED, then power down the slot via sysfs:
|
||||
#
|
||||
# /sys/class/enclosure/<enclosure>/<slot>/power_status
|
||||
#
|
||||
# We assume the user will be responsible for turning the slot back on again.
|
||||
#
|
||||
# Note that this script requires that your enclosure be supported by the
|
||||
# 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: slot successfully powered off
|
||||
# 1: enclosure not available
|
||||
# 2: ZED_POWER_OFF_ENCLOSURE_SLOT_ON_FAULT disabled
|
||||
# 3: vdev was not FAULTED
|
||||
# 4: The enclosure sysfs path passed from ZFS does not exist
|
||||
# 5: Enclosure slot didn't actually turn off after we told it to
|
||||
|
||||
[ -f "${ZED_ZEDLET_DIR}/zed.rc" ] && . "${ZED_ZEDLET_DIR}/zed.rc"
|
||||
. "${ZED_ZEDLET_DIR}/zed-functions.sh"
|
||||
|
||||
if [ ! -d /sys/class/enclosure ] ; then
|
||||
# No JBOD enclosure or NVMe slots
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "${ZED_POWER_OFF_ENCLOSURE_SLOT_ON_FAULT}" != "1" ] ; then
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [ "$ZEVENT_VDEV_STATE_STR" != "FAULTED" ] ; then
|
||||
exit 3
|
||||
fi
|
||||
|
||||
if [ ! -f "$ZEVENT_VDEV_ENC_SYSFS_PATH/power_status" ] ; then
|
||||
exit 4
|
||||
fi
|
||||
|
||||
# Turn off the slot and wait for sysfs to report that the slot is off.
|
||||
# It can take ~400ms on some enclosures and multiple retries may be needed.
|
||||
for i in $(seq 1 20) ; do
|
||||
echo "off" | tee "$ZEVENT_VDEV_ENC_SYSFS_PATH/power_status"
|
||||
|
||||
for j in $(seq 1 5) ; do
|
||||
if [ "$(cat $ZEVENT_VDEV_ENC_SYSFS_PATH/power_status)" == "off" ] ; then
|
||||
break 2
|
||||
fi
|
||||
sleep 0.1
|
||||
done
|
||||
done
|
||||
|
||||
if [ "$(cat $ZEVENT_VDEV_ENC_SYSFS_PATH/power_status)" != "off" ] ; then
|
||||
exit 5
|
||||
fi
|
||||
|
||||
zed_log_msg "powered down slot $ZEVENT_VDEV_ENC_SYSFS_PATH for $ZEVENT_VDEV_PATH"
|
||||
@@ -1,38 +0,0 @@
|
||||
#!/bin/sh
|
||||
# shellcheck disable=SC2154
|
||||
#
|
||||
# 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}"
|
||||
+20
-284
@@ -1,5 +1,5 @@
|
||||
#!/bin/sh
|
||||
# shellcheck disable=SC2154,SC3043
|
||||
# shellcheck disable=SC2039
|
||||
# zed-functions.sh
|
||||
#
|
||||
# ZED helper functions for use in ZEDLETs
|
||||
@@ -76,7 +76,8 @@ zed_log_msg()
|
||||
#
|
||||
zed_log_err()
|
||||
{
|
||||
zed_log_msg "error: ${0##*/}:""${ZEVENT_EID:+" eid=${ZEVENT_EID}:"}" "$@"
|
||||
logger -p "${ZED_SYSLOG_PRIORITY}" -t "${ZED_SYSLOG_TAG}" -- "error:" \
|
||||
"$(basename -- "$0"):""${ZEVENT_EID:+" eid=${ZEVENT_EID}:"}" "$@"
|
||||
}
|
||||
|
||||
|
||||
@@ -125,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
|
||||
|
||||
@@ -162,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}>&-"
|
||||
@@ -197,18 +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))
|
||||
|
||||
zed_notify_ntfy "${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
|
||||
@@ -227,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
|
||||
@@ -246,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"}"
|
||||
@@ -263,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
|
||||
@@ -379,252 +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_notify_ntfy (subject, pathname)
|
||||
#
|
||||
# Send a notification via Ntfy.sh <https://ntfy.sh/>.
|
||||
# The ntfy topic (ZED_NTFY_TOPIC) identifies the topic that the notification
|
||||
# will be sent to Ntfy.sh server. The ntfy url (ZED_NTFY_URL) defines the
|
||||
# self-hosted or provided hosted ntfy service location. The ntfy access token
|
||||
# <https://docs.ntfy.sh/publish/#access-tokens> (ZED_NTFY_ACCESS_TOKEN) reprsents an
|
||||
# access token that could be used if a topic is read/write protected. If a
|
||||
# topic can be written to publicaly, a ZED_NTFY_ACCESS_TOKEN is not required.
|
||||
#
|
||||
# Requires curl and sed executables to be installed in the standard PATH.
|
||||
#
|
||||
# References
|
||||
# https://docs.ntfy.sh
|
||||
#
|
||||
# Arguments
|
||||
# subject: notification subject
|
||||
# pathname: pathname containing the notification message (OPTIONAL)
|
||||
#
|
||||
# Globals
|
||||
# ZED_NTFY_TOPIC
|
||||
# ZED_NTFY_ACCESS_TOKEN (OPTIONAL)
|
||||
# ZED_NTFY_URL
|
||||
#
|
||||
# Return
|
||||
# 0: notification sent
|
||||
# 1: notification failed
|
||||
# 2: not configured
|
||||
#
|
||||
zed_notify_ntfy()
|
||||
{
|
||||
local subject="$1"
|
||||
local pathname="${2:-"/dev/null"}"
|
||||
local msg_body
|
||||
local msg_out
|
||||
local msg_err
|
||||
|
||||
[ -n "${ZED_NTFY_TOPIC}" ] || return 2
|
||||
local url="${ZED_NTFY_URL:-"https://ntfy.sh"}/${ZED_NTFY_TOPIC}"
|
||||
|
||||
if [ ! -r "${pathname}" ]; then
|
||||
zed_log_err "ntfy 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.
|
||||
#
|
||||
if [ -n "${ZED_NTFY_ACCESS_TOKEN}" ]; then
|
||||
msg_out="$( \
|
||||
curl \
|
||||
-u ":${ZED_NTFY_ACCESS_TOKEN}" \
|
||||
-H "Title: ${subject}" \
|
||||
-d "${msg_body}" \
|
||||
-H "Priority: high" \
|
||||
"${url}" \
|
||||
2>/dev/null \
|
||||
)"; rv=$?
|
||||
else
|
||||
msg_out="$( \
|
||||
curl \
|
||||
-H "Title: ${subject}" \
|
||||
-d "${msg_body}" \
|
||||
-H "Priority: high" \
|
||||
"${url}" \
|
||||
2>/dev/null \
|
||||
)"; rv=$?
|
||||
fi
|
||||
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 "ntfy \"${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
|
||||
@@ -699,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
|
||||
|
||||
+11
-69
@@ -1,7 +1,8 @@
|
||||
##
|
||||
# zed.rc – ZEDLET configuration.
|
||||
# zed.rc
|
||||
#
|
||||
# This file should be owned by root and permissioned 0600.
|
||||
##
|
||||
# shellcheck disable=SC2034
|
||||
|
||||
##
|
||||
# Absolute path to the debug output file.
|
||||
@@ -12,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;
|
||||
@@ -29,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@"
|
||||
|
||||
@@ -52,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.
|
||||
@@ -74,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.
|
||||
#
|
||||
@@ -106,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).
|
||||
@@ -135,37 +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"
|
||||
#ZED_SYSLOG_SUBCLASS_EXCLUDE="statechange|config_*|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
|
||||
|
||||
##
|
||||
# Power off the drive's slot in the enclosure if it becomes FAULTED. This can
|
||||
# help silence misbehaving drives. This assumes your drive enclosure fully
|
||||
# supports slot power control via sysfs.
|
||||
#ZED_POWER_OFF_ENCLOSURE_SLOT_ON_FAULT=1
|
||||
|
||||
##
|
||||
# Ntfy topic
|
||||
# This defines which topic will receive the ntfy notification.
|
||||
# <https://docs.ntfy.sh/publish/>
|
||||
# Disabled by default; uncomment to enable.
|
||||
#ZED_NTFY_TOPIC=""
|
||||
|
||||
##
|
||||
# Ntfy access token (optional for public topics)
|
||||
# This defines an access token which can be used
|
||||
# to allow you to authenticate when sending to topics
|
||||
# <https://docs.ntfy.sh/publish/#access-tokens>
|
||||
# Disabled by default; uncomment to enable.
|
||||
#ZED_NTFY_ACCESS_TOKEN=""
|
||||
|
||||
##
|
||||
# Ntfy Service URL
|
||||
# This defines which service the ntfy call will be directed toward
|
||||
# <https://docs.ntfy.sh/install/>
|
||||
# https://ntfy.sh by default; uncomment to enable an alternative service url.
|
||||
#ZED_NTFY_URL="https://ntfy.sh"
|
||||
|
||||
+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.
|
||||
*/
|
||||
|
||||
+126
-116
@@ -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,27 +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;
|
||||
zcp->max_zevent_buf_len = 1 << 20;
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -64,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,
|
||||
@@ -84,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;
|
||||
@@ -100,6 +122,7 @@ zed_conf_destroy(struct zed_conf *zcp)
|
||||
zed_strings_destroy(zcp->zedlets);
|
||||
zcp->zedlets = NULL;
|
||||
}
|
||||
free(zcp);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -109,54 +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" },
|
||||
{ .o = "-b LEN", .d = "Cap kernel event buffer at LEN entries.",
|
||||
.v = "1048576" },
|
||||
{},
|
||||
};
|
||||
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);
|
||||
@@ -168,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);
|
||||
}
|
||||
@@ -210,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));
|
||||
}
|
||||
@@ -233,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:b:";
|
||||
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");
|
||||
@@ -245,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();
|
||||
@@ -253,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;
|
||||
@@ -283,41 +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 'b':
|
||||
errno = 0;
|
||||
raw = strtoul(optarg, NULL, 0);
|
||||
if (errno == ERANGE || raw > INT32_MAX) {
|
||||
zed_log_die("%lu is too large", raw);
|
||||
} if (raw == 0) {
|
||||
zcp->max_zevent_buf_len = INT32_MAX;
|
||||
} else {
|
||||
zcp->max_zevent_buf_len = 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.
|
||||
@@ -325,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)
|
||||
@@ -440,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;
|
||||
@@ -467,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;
|
||||
@@ -477,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",
|
||||
@@ -514,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) {
|
||||
@@ -542,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;
|
||||
@@ -563,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));
|
||||
@@ -581,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));
|
||||
@@ -657,7 +667,7 @@ zed_conf_read_state(struct zed_conf *zcp, uint64_t *eidp, int64_t etime[])
|
||||
} else if (n != len) {
|
||||
errno = EIO;
|
||||
zed_log_msg(LOG_WARNING,
|
||||
"Failed to read state file \"%s\": Read %zd of %zd bytes",
|
||||
"Failed to read state file \"%s\": Read %d of %d bytes",
|
||||
zcp->state_file, n, len);
|
||||
return (-1);
|
||||
}
|
||||
@@ -706,7 +716,7 @@ zed_conf_write_state(struct zed_conf *zcp, uint64_t eid, int64_t etime[])
|
||||
if (n != len) {
|
||||
errno = EIO;
|
||||
zed_log_msg(LOG_WARNING,
|
||||
"Failed to write state file \"%s\": Wrote %zd of %zd bytes",
|
||||
"Failed to write state file \"%s\": Wrote %d of %d bytes",
|
||||
zcp->state_file, n, len);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user