Merge branch 'master' into issue-8478

This commit is contained in:
Josh Soref 2021-11-09 21:09:34 -05:00 committed by GitHub
commit 9ab9471aa6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2550 changed files with 244744 additions and 70447 deletions

10
.editorconfig Normal file
View File

@ -0,0 +1,10 @@
root = true
[*]
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.{c,h}]
tab_width = 8
indent_style = tab

View File

@ -1,10 +1,12 @@
# Contributing to ZFS on Linux # Contributing to OpenZFS
<p align="center"><img src="http://zfsonlinux.org/images/zfs-linux.png"/></p> <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>
*First of all, thank you for taking the time to contribute!* *First of all, thank you for taking the time to contribute!*
By using the following guidelines, you can help us make ZFS on Linux even By using the following guidelines, you can help us make OpenZFS even better.
better.
## Table Of Contents ## Table Of Contents
[What should I know before I get [What should I know before I get
@ -32,17 +34,17 @@ started?](#what-should-i-know-before-i-get-started)
Helpful resources Helpful resources
* [ZFS on Linux wiki](https://github.com/zfsonlinux/zfs/wiki) * [OpenZFS Documentation](https://openzfs.github.io/openzfs-docs/)
* [OpenZFS Documentation](http://open-zfs.org/wiki/Developer_resources) * [OpenZFS Developer Resources](http://open-zfs.org/wiki/Developer_resources)
* [Git and GitHub for beginners](https://github.com/zfsonlinux/zfs/wiki/Git-and-GitHub-for-beginners) * [Git and GitHub for beginners](https://openzfs.github.io/openzfs-docs/Developer%20Resources/Git%20and%20GitHub%20for%20beginners.html)
## What should I know before I get started? ## What should I know before I get started?
### Get ZFS ### Get ZFS
You can build zfs packages by following [these You can build zfs packages by following [these
instructions](https://github.com/zfsonlinux/zfs/wiki/Building-ZFS), instructions](https://openzfs.github.io/openzfs-docs/Developer%20Resources/Building%20ZFS.html),
or install stable packages from [your distribution's or install stable packages from [your distribution's
repository](https://github.com/zfsonlinux/zfs/wiki/Getting-Started). repository](https://openzfs.github.io/openzfs-docs/Getting%20Started/index.html).
### Debug ZFS ### Debug ZFS
A variety of methods and tools are available to aid ZFS developers. A variety of methods and tools are available to aid ZFS developers.
@ -51,29 +53,30 @@ configure option should be set. This will enable additional correctness
checks and all the ASSERTs to help quickly catch potential issues. checks and all the ASSERTs to help quickly catch potential issues.
In addition, there are numerous utilities and debugging files which In addition, there are numerous utilities and debugging files which
provide visibility in to the inner workings of ZFS. The most useful provide visibility into the inner workings of ZFS. The most useful
of these tools are discussed in detail on the [debugging ZFS wiki of these tools are discussed in detail on the [Troubleshooting
page](https://github.com/zfsonlinux/zfs/wiki/Debugging). page](https://openzfs.github.io/openzfs-docs/Basic%20Concepts/Troubleshooting.html).
### Where can I ask for help? ### Where can I ask for help?
[The zfs-discuss mailing list or IRC](http://list.zfsonlinux.org) The [zfs-discuss mailing
are the best places to ask for help. Please do not file support requests list](https://openzfs.github.io/openzfs-docs/Project%20and%20Community/Mailing%20Lists.html)
on the GitHub issue tracker. or IRC are the best places to ask for help. Please do not file
support requests on the GitHub issue tracker.
## How Can I Contribute? ## How Can I Contribute?
### Reporting Bugs ### Reporting Bugs
*Please* contact us via the [zfs-discuss mailing *Please* contact us via the [zfs-discuss mailing
list or IRC](http://list.zfsonlinux.org) if you aren't list](https://openzfs.github.io/openzfs-docs/Project%20and%20Community/Mailing%20Lists.html)
certain that you are experiencing a bug. or IRC if you aren't certain that you are experiencing a bug.
If you run into an issue, please search our [issue If you run into an issue, please search our [issue
tracker](https://github.com/zfsonlinux/zfs/issues) *first* to ensure the tracker](https://github.com/openzfs/zfs/issues) *first* to ensure the
issue hasn't been reported before. Open a new issue only if you haven't issue hasn't been reported before. Open a new issue only if you haven't
found anything similar to your issue. found anything similar to your issue.
You can open a new issue and search existing issues using the public [issue You can open a new issue and search existing issues using the public [issue
tracker](https://github.com/zfsonlinux/zfs/issues). tracker](https://github.com/openzfs/zfs/issues).
#### When opening a new issue, please include the following information at the top of the issue: #### When opening a new issue, please include the following information at the top of the issue:
* What distribution (with version) you are using. * What distribution (with version) you are using.
@ -105,13 +108,13 @@ information like:
* Stack traces which may be logged to `dmesg`. * Stack traces which may be logged to `dmesg`.
### Suggesting Enhancements ### Suggesting Enhancements
ZFS on Linux is a widely deployed production filesystem which is under OpenZFS is a widely deployed production filesystem which is under active
active development. The team's primary focus is on fixing known issues, development. The team's primary focus is on fixing known issues, improving
improving performance, and adding compelling new features. performance, and adding compelling new features.
You can view the list of proposed features You can view the list of proposed features
by filtering the issue tracker by the ["Feature" by filtering the issue tracker by the ["Type: Feature"
label](https://github.com/zfsonlinux/zfs/issues?q=is%3Aopen+is%3Aissue+label%3AFeature). label](https://github.com/openzfs/zfs/issues?q=is%3Aopen+is%3Aissue+label%3A%22Type%3A+Feature%22).
If you have an idea for a feature first check this list. If your idea already 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 appears then add a +1 to the top most comment, this helps us gauge interest
in that feature. in that feature.
@ -120,8 +123,11 @@ Otherwise, open a new issue and describe your proposed feature. Why is this
feature needed? What problem does it solve? feature needed? What problem does it solve?
### Pull Requests ### Pull Requests
* All pull requests must be based on the current master branch and apply
without conflicts. #### General
* All pull requests, except backports and releases, must be based on the current master branch
and should apply without conflicts.
* Please attempt to limit pull requests to a single commit which resolves * Please attempt to limit pull requests to a single commit which resolves
one specific issue. one specific issue.
* Make sure your commit messages are in the correct format. See the * Make sure your commit messages are in the correct format. See the
@ -133,16 +139,28 @@ logically independent patches which build on each other. This makes large
changes easier to review and approve which speeds up the merging process. 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 * Try to keep pull requests simple. Simple code with comments is much easier
to review and approve. 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. * 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. * If your pull request improves performance, please include some benchmarks.
* The pull request must pass all required [ZFS * The pull request must pass all required [ZFS
Buildbot](http://build.zfsonlinux.org/) builders before Buildbot](http://build.zfsonlinux.org/) builders before
being accepted. If you are experiencing intermittent TEST being accepted. If you are experiencing intermittent TEST
builder failures, you may be experiencing a [test suite builder failures, you may be experiencing a [test suite
issue](https://github.com/zfsonlinux/zfs/issues?q=is%3Aissue+is%3Aopen+label%3A%22Test+Suite%22). 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://github.com/zfsonlinux/zfs/wiki/Buildbot-Options) There are also various [buildbot options](https://openzfs.github.io/openzfs-docs/Developer%20Resources/Buildbot%20Options.html)
to control how changes are tested. to control how changes are tested.
* All proposed changes must be approved by a ZFS on Linux organization member.
### Testing ### Testing
All help is appreciated! If you're in a position to run the latest code All help is appreciated! If you're in a position to run the latest code
@ -152,16 +170,41 @@ range of realistic workloads, configurations and architectures we're better
able quickly identify and resolve potential issues. able quickly identify and resolve potential issues.
Users can also run the [ZFS Test Users can also run the [ZFS Test
Suite](https://github.com/zfsonlinux/zfs/tree/master/tests) on their systems Suite](https://github.com/openzfs/zfs/tree/master/tests) on their systems
to verify ZFS is behaving as intended. to verify ZFS is behaving as intended.
## Style Guides ## 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 ### Coding Conventions
We currently use [C Style and Coding Standards for We currently use [C Style and Coding Standards for
SunOS](http://www.cis.upenn.edu/%7Elee/06cse480/data/cstyle.ms.pdf) as our SunOS](http://www.cis.upenn.edu/%7Elee/06cse480/data/cstyle.ms.pdf) as our
coding convention. 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 ### Commit Message Formats
#### New Changes #### New Changes
Commit messages for new changes must meet the following guidelines: Commit messages for new changes must meet the following guidelines:
@ -187,70 +230,6 @@ attempting to solve.
Signed-off-by: Contributor <contributor@email.com> Signed-off-by: Contributor <contributor@email.com>
``` ```
#### OpenZFS Patch Ports
If you are porting OpenZFS patches, the commit message must meet
the following guidelines:
* The first line must be the summary line from the most important OpenZFS commit being ported.
It must begin with `OpenZFS dddd, dddd - ` where `dddd` are OpenZFS issue numbers.
* Provides a `Authored by:` line to attribute each patch for each original author.
* Provides the `Reviewed by:` and `Approved by:` lines from each original
OpenZFS commit.
* Provides a `Ported-by:` line with the developer's name followed by
their email for each OpenZFS commit.
* Provides a `OpenZFS-issue:` line with link for each original illumos
issue.
* Provides a `OpenZFS-commit:` line with link for each original OpenZFS commit.
* If necessary, provide some porting notes to describe any deviations from
the original OpenZFS commits.
An example OpenZFS patch port commit message for a single patch is provided
below.
```
OpenZFS 1234 - Summary from the original OpenZFS commit
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>
Provide some porting notes here if necessary.
OpenZFS-issue: https://www.illumos.org/issues/1234
OpenZFS-commit: https://github.com/openzfs/openzfs/commit/abcd1234
```
If necessary, multiple OpenZFS patches can be combined in a single port.
This is useful when you are porting a new patch and its subsequent bug
fixes. An example commit message is provided below.
```
OpenZFS 1234, 5678 - Summary of most important OpenZFS commit
1234 Summary from original OpenZFS commit for 1234
Authored by: Original Author <original@email.com>
Reviewed by: Reviewer Two <reviewer2@email.com>
Approved by: Approver One <approver1@email.com>
Ported-by: ZFS Contributor <contributor@email.com>
Provide some porting notes here for 1234 if necessary.
OpenZFS-issue: https://www.illumos.org/issues/1234
OpenZFS-commit: https://github.com/openzfs/openzfs/commit/abcd1234
5678 Summary from original OpenZFS commit for 5678
Authored by: Original Author2 <original2@email.com>
Reviewed by: Reviewer One <reviewer1@email.com>
Approved by: Approver Two <approver2@email.com>
Ported-by: ZFS Contributor <contributor@email.com>
Provide some porting notes here for 5678 if necessary.
OpenZFS-issue: https://www.illumos.org/issues/5678
OpenZFS-commit: https://github.com/openzfs/openzfs/commit/efgh5678
```
#### Coverity Defect Fixes #### Coverity Defect Fixes
If you are submitting a fix to a If you are submitting a fix to a
[Coverity defect](https://scan.coverity.com/projects/zfsonlinux-zfs), [Coverity defect](https://scan.coverity.com/projects/zfsonlinux-zfs),
@ -290,3 +269,13 @@ Git can append the `Signed-off-by` line to your commit messages. Simply
provide the `-s` or `--signoff` option when performing a `git commit`. provide the `-s` or `--signoff` option when performing a `git commit`.
For more information about writing commit messages, visit [How to Write For more information about writing commit messages, visit [How to Write
a Git Commit Message](https://chris.beams.io/posts/git-commit/). 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>`

View File

@ -1,48 +0,0 @@
<!-- Please fill out the following template, which will help other contributors address your issue. -->
<!--
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 ```)
```
-->

55
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,55 @@
---
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 ```)
```
-->

14
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@ -0,0 +1,14 @@
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

View File

@ -0,0 +1,33 @@
---
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?
-->

View File

@ -4,7 +4,7 @@
<!--- <!---
Documentation on ZFS Buildbot options can be found at Documentation on ZFS Buildbot options can be found at
https://github.com/zfsonlinux/zfs/wiki/Buildbot-Options https://openzfs.github.io/openzfs-docs/Developer%20Resources/Buildbot%20Options.html
--> -->
### Motivation and Context ### Motivation and Context
@ -19,6 +19,7 @@ https://github.com/zfsonlinux/zfs/wiki/Buildbot-Options
<!--- Include details of your testing environment, and the tests you ran to --> <!--- Include details of your testing environment, and the tests you ran to -->
<!--- see how your change affects other areas of the code, etc. --> <!--- see how your change affects other areas of the code, etc. -->
<!--- If your change is a performance enhancement, please provide benchmarks here. --> <!--- If your change is a performance enhancement, please provide benchmarks here. -->
<!--- Please think about using the draft PR feature if appropriate -->
### Types of changes ### Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: --> <!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->
@ -27,14 +28,15 @@ https://github.com/zfsonlinux/zfs/wiki/Buildbot-Options
- [ ] Performance enhancement (non-breaking change which improves efficiency) - [ ] Performance enhancement (non-breaking change which improves efficiency)
- [ ] Code cleanup (non-breaking change which makes code smaller or more readable) - [ ] Code cleanup (non-breaking change which makes code smaller or more readable)
- [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] 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) - [ ] Documentation (a change to man pages or other documentation)
### Checklist: ### Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes that apply. --> <!--- 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! --> <!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
- [ ] My code follows the ZFS on Linux [code style requirements](https://github.com/zfsonlinux/zfs/blob/master/.github/CONTRIBUTING.md#coding-conventions). - [ ] My code follows the OpenZFS [code style requirements](https://github.com/openzfs/zfs/blob/master/.github/CONTRIBUTING.md#coding-conventions).
- [ ] I have updated the documentation accordingly. - [ ] I have updated the documentation accordingly.
- [ ] I have read the [**contributing** document](https://github.com/zfsonlinux/zfs/blob/master/.github/CONTRIBUTING.md). - [ ] I have read the [**contributing** document](https://github.com/openzfs/zfs/blob/master/.github/CONTRIBUTING.md).
- [ ] I have added [tests](https://github.com/zfsonlinux/zfs/tree/master/tests) to cover my changes. - [ ] I have added [tests](https://github.com/openzfs/zfs/tree/master/tests) to cover my changes.
- [ ] All new and existing tests passed. - [ ] 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/zfsonlinux/zfs/blob/master/.github/CONTRIBUTING.md#signed-off-by). - [ ] All commit messages are properly formatted and contain [`Signed-off-by`](https://github.com/openzfs/zfs/blob/master/.github/CONTRIBUTING.md#signed-off-by).

5
.github/codecov.yml vendored
View File

@ -4,7 +4,8 @@ codecov:
after_n_builds: 2 # user and kernel after_n_builds: 2 # user and kernel
coverage: coverage:
precision: 2 # 2 digits of precision precision: 0 # 0 decimals of precision
round: nearest # Round to nearest precision point
range: "50...90" # red -> yellow -> green range: "50...90" # red -> yellow -> green
status: status:
@ -20,3 +21,5 @@ comment:
layout: "reach, diff, flags, footer" layout: "reach, diff, flags, footer"
behavior: once # update if exists; post new; skip if deleted behavior: once # update if exists; post new; skip if deleted
require_changes: yes # only post when coverage changes require_changes: yes # only post when coverage changes
# ignore: Please place any ignores in config/ax_code_coverage.m4 instead

13
.github/no-response.yml vendored Normal file
View File

@ -0,0 +1,13 @@
# 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.

26
.github/stale.yml vendored Normal file
View File

@ -0,0 +1,26 @@
# 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

View File

@ -1,3 +0,0 @@
preprocessorErrorDirective:./module/zfs/vdev_raidz_math_avx512f.c:243
preprocessorErrorDirective:./module/zfs/vdev_raidz_math_sse2.c:266

50
.github/workflows/checkstyle.yaml vendored Normal file
View File

@ -0,0 +1,50 @@
name: checkstyle
on:
push:
pull_request:
jobs:
checkstyle:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install --yes -qq build-essential autoconf libtool gawk alien fakeroot linux-headers-$(uname -r)
sudo apt-get install --yes -qq zlib1g-dev uuid-dev libattr1-dev libblkid-dev libselinux-dev libudev-dev libssl-dev python-dev python-setuptools python-cffi python3 python3-dev python3-setuptools python3-cffi
# packages for tests
sudo apt-get install --yes -qq parted lsscsi ksh attr acl nfs-kernel-server fio
sudo apt-get install --yes -qq mandoc cppcheck pax-utils devscripts
sudo -E pip --quiet install flake8
- name: Prepare
run: |
sh ./autogen.sh
./configure
make -j$(nproc)
- name: Checkstyle
run: |
make checkstyle
- name: Lint
run: |
make lint
- name: CheckABI
id: CheckABI
run: |
sudo docker run -v $(pwd):/source ghcr.io/openzfs/libabigail make checkabi
- name: StoreABI
if: failure() && steps.CheckABI.outcome == 'failure'
run: |
sudo docker run -v $(pwd):/source ghcr.io/openzfs/libabigail make storeabi
- name: Prepare artifacts
if: failure() && steps.CheckABI.outcome == 'failure'
run: |
find -name *.abi | tar -cf abi_files.tar -T -
- uses: actions/upload-artifact@v2
if: failure() && steps.CheckABI.outcome == 'failure'
with:
name: New ABI files (use only if you're sure about interface changes)
path: abi_files.tar

View File

@ -0,0 +1,82 @@
name: zfs-tests-functional
on:
push:
pull_request:
jobs:
tests-functional-ubuntu:
strategy:
fail-fast: false
matrix:
os: [18.04, 20.04]
runs-on: ubuntu-${{ matrix.os }}
steps:
- uses: actions/checkout@v2
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install --yes -qq build-essential autoconf libtool gdb lcov \
git alien fakeroot wget curl bc fio acl \
sysstat mdadm lsscsi parted gdebi attr dbench watchdog ksh \
nfs-kernel-server samba rng-tools xz-utils \
zlib1g-dev uuid-dev libblkid-dev libselinux-dev \
xfslibs-dev libattr1-dev libacl1-dev libudev-dev libdevmapper-dev \
libssl-dev libffi-dev libaio-dev libelf-dev libmount-dev \
libpam0g-dev pamtester python-dev python-setuptools python-cffi \
python-packaging python3 python3-dev python3-setuptools python3-cffi \
libcurl4-openssl-dev python3-packaging
- name: Autogen.sh
run: |
sh autogen.sh
- name: Configure
run: |
./configure --enable-debug --enable-debuginfo
- name: Make
run: |
make --no-print-directory -s pkg-utils pkg-kmod
- name: Install
run: |
sudo dpkg -i *.deb
# Update order of directories to search for modules, otherwise
# Ubuntu will load kernel-shipped ones.
sudo sed -i.bak 's/updates/extra updates/' /etc/depmod.d/ubuntu.conf
sudo depmod
sudo modprobe zfs
# Workaround for cloud-init bug
# see https://github.com/openzfs/zfs/issues/12644
FILE=/lib/udev/rules.d/10-cloud-init-hook-hotplug.rules
if [ -r "${FILE}" ]; then
HASH=$(md5sum "${FILE}" | awk '{ print $1 }')
if [ "${HASH}" = "121ff0ef1936cd2ef65aec0458a35772" ]; then
# Just shove a zd* exclusion right above the hotplug hook...
sudo sed -i -e s/'LABEL="cloudinit_hook"'/'KERNEL=="zd*", GOTO="cloudinit_end"\n&'/ "${FILE}"
sudo udevadm control --reload-rules
fi
fi
# Workaround to provide additional free space for testing.
# https://github.com/actions/virtual-environments/issues/2840
sudo rm -rf /usr/share/dotnet
sudo rm -rf /opt/ghc
sudo rm -rf "/usr/local/share/boost"
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
- name: Tests
run: |
/usr/share/zfs/zfs-tests.sh -v -s 3G
- name: Prepare artifacts
if: failure()
run: |
RESULTS_PATH=$(readlink -f /var/tmp/test_results/current)
sudo dmesg > $RESULTS_PATH/dmesg
sudo cp /var/log/syslog $RESULTS_PATH/
sudo chmod +r $RESULTS_PATH/*
# Replace ':' in dir names, actions/upload-artifact doesn't support it
for f in $(find $RESULTS_PATH -name '*:*'); do mv "$f" "${f//:/__}"; done
- uses: actions/upload-artifact@v2
if: failure()
with:
name: Test logs Ubuntu-${{ matrix.os }}
path: /var/tmp/test_results/20*/
if-no-files-found: ignore

78
.github/workflows/zfs-tests-sanity.yml vendored Normal file
View File

@ -0,0 +1,78 @@
name: zfs-tests-sanity
on:
push:
pull_request:
jobs:
tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install --yes -qq build-essential autoconf libtool gdb lcov \
git alien fakeroot wget curl bc fio acl \
sysstat mdadm lsscsi parted gdebi attr dbench watchdog ksh \
nfs-kernel-server samba rng-tools xz-utils \
zlib1g-dev uuid-dev libblkid-dev libselinux-dev \
xfslibs-dev libattr1-dev libacl1-dev libudev-dev libdevmapper-dev \
libssl-dev libffi-dev libaio-dev libelf-dev libmount-dev \
libpam0g-dev pamtester python-dev python-setuptools python-cffi \
python-packaging python3 python3-dev python3-setuptools python3-cffi \
python3-packaging libcurl4-openssl-dev
- name: Autogen.sh
run: |
sh autogen.sh
- name: Configure
run: |
./configure --enable-debug --enable-debuginfo
- name: Make
run: |
make --no-print-directory -s pkg-utils pkg-kmod
- name: Install
run: |
sudo dpkg -i *.deb
# Update order of directories to search for modules, otherwise
# Ubuntu will load kernel-shipped ones.
sudo sed -i.bak 's/updates/extra updates/' /etc/depmod.d/ubuntu.conf
sudo depmod
sudo modprobe zfs
# Workaround for cloud-init bug
# see https://github.com/openzfs/zfs/issues/12644
FILE=/lib/udev/rules.d/10-cloud-init-hook-hotplug.rules
if [ -r "${FILE}" ]; then
HASH=$(md5sum "${FILE}" | awk '{ print $1 }')
if [ "${HASH}" = "121ff0ef1936cd2ef65aec0458a35772" ]; then
# Just shove a zd* exclusion right above the hotplug hook...
sudo sed -i -e s/'LABEL="cloudinit_hook"'/'KERNEL=="zd*", GOTO="cloudinit_end"\n&'/ "${FILE}"
sudo udevadm control --reload-rules
fi
fi
# Workaround to provide additional free space for testing.
# https://github.com/actions/virtual-environments/issues/2840
sudo rm -rf /usr/share/dotnet
sudo rm -rf /opt/ghc
sudo rm -rf "/usr/local/share/boost"
sudo rm -rf "$AGENT_TOOLSDIRECTORY"
- name: Tests
run: |
/usr/share/zfs/zfs-tests.sh -v -s 3G -r sanity
- name: Prepare artifacts
if: failure()
run: |
RESULTS_PATH=$(readlink -f /var/tmp/test_results/current)
sudo dmesg > $RESULTS_PATH/dmesg
sudo cp /var/log/syslog $RESULTS_PATH/
sudo chmod +r $RESULTS_PATH/*
# Replace ':' in dir names, actions/upload-artifact doesn't support it
for f in $(find $RESULTS_PATH -name '*:*'); do mv "$f" "${f//:/__}"; done
- uses: actions/upload-artifact@v2
if: failure()
with:
name: Test logs
path: /var/tmp/test_results/20*/
if-no-files-found: ignore

67
.github/workflows/zloop.yml vendored Normal file
View File

@ -0,0 +1,67 @@
name: zloop
on:
push:
pull_request:
jobs:
tests:
runs-on: ubuntu-latest
env:
TEST_DIR: /var/tmp/zloop
steps:
- uses: actions/checkout@v2
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install --yes -qq build-essential autoconf libtool gdb \
git alien fakeroot \
zlib1g-dev uuid-dev libblkid-dev libselinux-dev \
xfslibs-dev libattr1-dev libacl1-dev libudev-dev libdevmapper-dev \
libssl-dev libffi-dev libaio-dev libelf-dev libmount-dev \
libpam0g-dev \
python-dev python-setuptools python-cffi python-packaging \
python3 python3-dev python3-setuptools python3-cffi python3-packaging
- name: Autogen.sh
run: |
sh autogen.sh
- name: Configure
run: |
./configure --enable-debug --enable-debuginfo
- name: Make
run: |
make --no-print-directory -s pkg-utils pkg-kmod
- name: Install
run: |
sudo dpkg -i *.deb
# Update order of directories to search for modules, otherwise
# Ubuntu will load kernel-shipped ones.
sudo sed -i.bak 's/updates/extra updates/' /etc/depmod.d/ubuntu.conf
sudo depmod
sudo modprobe zfs
- name: Tests
run: |
sudo mkdir -p $TEST_DIR
# run for 20 minutes to have a total runner time of 30 minutes
sudo /usr/share/zfs/zloop.sh -t 1200 -l -m1 -- -T 120 -P 60
- name: Prepare artifacts
if: failure()
run: |
sudo chmod +r -R $TEST_DIR/
- uses: actions/upload-artifact@v2
if: failure()
with:
name: Logs
path: |
/var/tmp/zloop/*/
!/var/tmp/zloop/*/vdev/
if-no-files-found: ignore
- uses: actions/upload-artifact@v2
if: failure()
with:
name: Pool files
path: |
/var/tmp/zloop/*/vdev/
if-no-files-found: ignore

5
.gitignore vendored
View File

@ -36,6 +36,7 @@ Makefile.in
# Top level generated files specific to this top level dir # Top level generated files specific to this top level dir
# #
/bin /bin
/build
/configure /configure
/config.log /config.log
/config.status /config.status
@ -61,5 +62,9 @@ cscope.*
*.patch *.patch
*.orig *.orig
*.log *.log
*.tmp
venv venv
*.so
*.so.debug
*.so.full

2
.gitmodules vendored
View File

@ -1,3 +1,3 @@
[submodule "scripts/zfs-images"] [submodule "scripts/zfs-images"]
path = scripts/zfs-images path = scripts/zfs-images
url = https://github.com/zfsonlinux/zfs-images url = https://github.com/openzfs/zfs-images

View File

@ -1,38 +0,0 @@
language: c
sudo: required
env:
global:
# Travis limits maximum log size, we have to cut tests output
- ZFS_TEST_TRAVIS_LOG_MAX_LENGTH=800
matrix:
# tags are mainly in ascending order
- ZFS_TEST_TAGS='acl,atime,bootfs,cachefile,casenorm,chattr,checksum,clean_mirror,compression,ctime,delegate,devices,events,exec,fault,features,grow_pool,zdb,zfs,zfs_bookmark,zfs_change-key,zfs_clone,zfs_copies,zfs_create,zfs_diff,zfs_get,zfs_inherit,zfs_load-key,zfs_rename'
- ZFS_TEST_TAGS='cache,history,hkdf,inuse,zfs_property,zfs_receive,zfs_reservation,zfs_send,zfs_set,zfs_share,zfs_snapshot,zfs_unload-key,zfs_unmount,zfs_unshare,zfs_upgrade,zpool,zpool_add,zpool_attach,zpool_clear,zpool_create,zpool_destroy,zpool_detach'
- ZFS_TEST_TAGS='grow_replicas,mv_files,cli_user,zfs_mount,zfs_promote,zfs_rollback,zpool_events,zpool_expand,zpool_export,zpool_get,zpool_history,zpool_import,zpool_labelclear,zpool_offline,zpool_online,zpool_remove,zpool_reopen,zpool_replace,zpool_scrub,zpool_set,zpool_status,zpool_sync,zpool_upgrade'
- ZFS_TEST_TAGS='zfs_destroy,large_files,largest_pool,link_count,migration,mmap,mmp,mount,nestedfs,no_space,nopwrite,online_offline,pool_names,poolversion,privilege,quota,raidz,redundancy,rsend'
- ZFS_TEST_TAGS='inheritance,refquota,refreserv,rename_dirs,replacement,reservation,rootpool,scrub_mirror,slog,snapshot,snapused,sparse,threadsappend,tmpfile,truncate,upgrade,userquota,vdev_zaps,write_dirs,xattr,zvol,libzfs'
before_install:
- sudo apt-get -qq update
- sudo apt-get install --yes -qq build-essential autoconf libtool gawk alien fakeroot linux-headers-$(uname -r)
- sudo apt-get install --yes -qq zlib1g-dev uuid-dev libattr1-dev libblkid-dev libselinux-dev libudev-dev libssl-dev
# packages for tests
- sudo apt-get install --yes -qq parted lsscsi ksh attr acl nfs-kernel-server fio
install:
- git clone --depth=1 https://github.com/zfsonlinux/spl
- cd spl
- git checkout master
- sh autogen.sh
- ./configure
- make --no-print-directory -s pkg-utils pkg-kmod
- sudo dpkg -i *.deb
- cd ..
- sh autogen.sh
- ./configure
- make --no-print-directory -s pkg-utils pkg-kmod
- sudo dpkg -i *.deb
script:
- travis_wait 50 /usr/share/zfs/zfs-tests.sh -v -T $ZFS_TEST_TAGS
after_failure:
- find /var/tmp/test_results/current/log -type f -name '*' -printf "%f\n" -exec cut -c -$ZFS_TEST_TRAVIS_LOG_MAX_LENGTH {} \;
after_success:
- find /var/tmp/test_results/current/log -type f -name '*' -printf "%f\n" -exec cut -c -$ZFS_TEST_TRAVIS_LOG_MAX_LENGTH {} \;

View File

@ -83,6 +83,7 @@ CONTRIBUTORS:
Christopher Voltz <cjunk@voltz.ws> Christopher Voltz <cjunk@voltz.ws>
Chunwei Chen <david.chen@nutanix.com> Chunwei Chen <david.chen@nutanix.com>
Clemens Fruhwirth <clemens@endorphin.org> Clemens Fruhwirth <clemens@endorphin.org>
Coleman Kane <ckane@colemankane.org>
Colin Ian King <colin.king@canonical.com> Colin Ian King <colin.king@canonical.com>
Craig Loomis <cloomis@astro.princeton.edu> Craig Loomis <cloomis@astro.princeton.edu>
Craig Sanders <github@taz.net.au> Craig Sanders <github@taz.net.au>
@ -181,6 +182,7 @@ CONTRIBUTORS:
Keith M Wesolowski <wesolows@foobazco.org> Keith M Wesolowski <wesolows@foobazco.org>
Kevin Tanguy <kevin.tanguy@ovh.net> Kevin Tanguy <kevin.tanguy@ovh.net>
KireinaHoro <i@jsteward.moe> KireinaHoro <i@jsteward.moe>
Kjeld Schouten-Lebbing <kjeld@schouten-lebbing.nl>
Kohsuke Kawaguchi <kk@kohsuke.org> Kohsuke Kawaguchi <kk@kohsuke.org>
Kyle Blatter <kyleblatter@llnl.gov> Kyle Blatter <kyleblatter@llnl.gov>
Kyle Fuller <inbox@kylefuller.co.uk> Kyle Fuller <inbox@kylefuller.co.uk>
@ -209,6 +211,7 @@ CONTRIBUTORS:
Michael Gebetsroither <michael@mgeb.org> Michael Gebetsroither <michael@mgeb.org>
Michael Kjorling <michael@kjorling.se> Michael Kjorling <michael@kjorling.se>
Michael Martin <mgmartin.mgm@gmail.com> Michael Martin <mgmartin.mgm@gmail.com>
Michael Niewöhner <foss@mniewoehner.de>
Mike Gerdts <mike.gerdts@joyent.com> Mike Gerdts <mike.gerdts@joyent.com>
Mike Harsch <mike@harschsystems.com> Mike Harsch <mike@harschsystems.com>
Mike Leddy <mike.leddy@gmail.com> Mike Leddy <mike.leddy@gmail.com>
@ -257,6 +260,7 @@ CONTRIBUTORS:
Saso Kiselkov <saso.kiselkov@nexenta.com> Saso Kiselkov <saso.kiselkov@nexenta.com>
Scot W. Stevenson <scot.stevenson@gmail.com> Scot W. Stevenson <scot.stevenson@gmail.com>
Sean Eric Fagan <sef@ixsystems.com> Sean Eric Fagan <sef@ixsystems.com>
Sebastian Gottschall <s.gottschall@dd-wrt.com>
Sen Haerens <sen@senhaerens.be> Sen Haerens <sen@senhaerens.be>
Serapheim Dimitropoulos <serapheim@delphix.com> Serapheim Dimitropoulos <serapheim@delphix.com>
Seth Forshee <seth.forshee@canonical.com> Seth Forshee <seth.forshee@canonical.com>

View File

@ -1,2 +1,2 @@
The [OpenZFS Code of Conduct](http://www.open-zfs.org/wiki/Code_of_Conduct) The [OpenZFS Code of Conduct](http://www.open-zfs.org/wiki/Code_of_Conduct)
applies to spaces associated with the ZFS on Linux project, including GitHub. applies to spaces associated with the OpenZFS project, including GitHub.

View File

@ -19,7 +19,11 @@ notable exceptions and their respective licenses include:
* AES Implementation: module/icp/asm-x86_64/aes/THIRDPARTYLICENSE.gladman * AES Implementation: module/icp/asm-x86_64/aes/THIRDPARTYLICENSE.gladman
* AES Implementation: module/icp/asm-x86_64/aes/THIRDPARTYLICENSE.openssl * AES Implementation: module/icp/asm-x86_64/aes/THIRDPARTYLICENSE.openssl
* PBKDF2 Implementation: lib/libzfs/THIRDPARTYLICENSE.openssl * PBKDF2 Implementation: lib/libzfs/THIRDPARTYLICENSE.openssl
* SPL Implementation: module/spl/THIRDPARTYLICENSE.gplv2 * 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 This product includes software developed by the OpenSSL Project for use
in the OpenSSL Toolkit (http://www.openssl.org/) in the OpenSSL Toolkit (http://www.openssl.org/)

8
META
View File

@ -1,10 +1,10 @@
Meta: 1 Meta: 1
Name: zfs Name: zfs
Branch: 1.0 Branch: 1.0
Version: 0.8.0 Version: 2.1.99
Release: 1 Release: 1
Release-Tags: relext Release-Tags: relext
License: CDDL License: CDDL
Author: OpenZFS on Linux Author: OpenZFS
Linux-Maximum: 5.1 Linux-Maximum: 5.14
Linux-Minimum: 2.6.32 Linux-Minimum: 3.10

View File

@ -1,12 +1,17 @@
include $(top_srcdir)/config/Shellcheck.am
ACLOCAL_AMFLAGS = -I config ACLOCAL_AMFLAGS = -I config
include config/rpm.am SUBDIRS = include
include config/deb.am if BUILD_LINUX
include config/tgz.am SUBDIRS += rpm
endif
SUBDIRS = include rpm
if CONFIG_USER if CONFIG_USER
SUBDIRS += udev etc man scripts lib tests cmd contrib SUBDIRS += man scripts lib tests cmd etc contrib
if BUILD_LINUX
SUBDIRS += udev
endif
endif endif
if CONFIG_KERNEL if CONFIG_KERNEL
SUBDIRS += module SUBDIRS += module
@ -14,33 +19,51 @@ SUBDIRS += module
extradir = $(prefix)/src/zfs-$(VERSION) extradir = $(prefix)/src/zfs-$(VERSION)
extra_HEADERS = zfs.release.in zfs_config.h.in extra_HEADERS = zfs.release.in zfs_config.h.in
if BUILD_LINUX
kerneldir = $(prefix)/src/zfs-$(VERSION)/$(LINUX_VERSION) kerneldir = $(prefix)/src/zfs-$(VERSION)/$(LINUX_VERSION)
nodist_kernel_HEADERS = zfs.release zfs_config.h module/$(LINUX_SYMBOLS) nodist_kernel_HEADERS = zfs.release zfs_config.h module/$(LINUX_SYMBOLS)
endif endif
endif
AUTOMAKE_OPTIONS = foreign AUTOMAKE_OPTIONS = foreign
EXTRA_DIST = autogen.sh copy-builtin EXTRA_DIST = autogen.sh copy-builtin
EXTRA_DIST += config/config.awk config/rpm.am config/deb.am config/tgz.am EXTRA_DIST += config/config.awk config/rpm.am config/deb.am config/tgz.am
EXTRA_DIST += META AUTHORS COPYRIGHT LICENSE NEWS NOTICE README.md EXTRA_DIST += AUTHORS CODE_OF_CONDUCT.md COPYRIGHT LICENSE META NEWS NOTICE
EXTRA_DIST += CODE_OF_CONDUCT.md EXTRA_DIST += README.md RELEASES.md
EXTRA_DIST += module/lua/README.zfs module/os/linux/spl/README.md
# Include all the extra licensing information for modules # Include all the extra licensing information for modules
EXTRA_DIST += module/icp/algs/skein/THIRDPARTYLICENSE module/icp/algs/skein/THIRDPARTYLICENSE.descrip EXTRA_DIST += module/icp/algs/skein/THIRDPARTYLICENSE
EXTRA_DIST += module/icp/asm-x86_64/aes/THIRDPARTYLICENSE.gladman module/icp/asm-x86_64/aes/THIRDPARTYLICENSE.gladman.descrip EXTRA_DIST += module/icp/algs/skein/THIRDPARTYLICENSE.descrip
EXTRA_DIST += module/icp/asm-x86_64/aes/THIRDPARTYLICENSE.openssl module/icp/asm-x86_64/aes/THIRDPARTYLICENSE.openssl.descrip EXTRA_DIST += module/icp/asm-x86_64/aes/THIRDPARTYLICENSE.gladman
EXTRA_DIST += module/spl/THIRDPARTYLICENSE.gplv2 module/spl/THIRDPARTYLICENSE.gplv2.descrip EXTRA_DIST += module/icp/asm-x86_64/aes/THIRDPARTYLICENSE.gladman.descrip
EXTRA_DIST += module/zfs/THIRDPARTYLICENSE.cityhash module/zfs/THIRDPARTYLICENSE.cityhash.descrip EXTRA_DIST += module/icp/asm-x86_64/aes/THIRDPARTYLICENSE.openssl
EXTRA_DIST += module/icp/asm-x86_64/aes/THIRDPARTYLICENSE.openssl.descrip
EXTRA_DIST += module/icp/asm-x86_64/modes/THIRDPARTYLICENSE.cryptogams
EXTRA_DIST += module/icp/asm-x86_64/modes/THIRDPARTYLICENSE.cryptogams.descrip
EXTRA_DIST += module/icp/asm-x86_64/modes/THIRDPARTYLICENSE.openssl
EXTRA_DIST += module/icp/asm-x86_64/modes/THIRDPARTYLICENSE.openssl.descrip
EXTRA_DIST += module/os/linux/spl/THIRDPARTYLICENSE.gplv2
EXTRA_DIST += module/os/linux/spl/THIRDPARTYLICENSE.gplv2.descrip
EXTRA_DIST += module/zfs/THIRDPARTYLICENSE.cityhash
EXTRA_DIST += module/zfs/THIRDPARTYLICENSE.cityhash.descrip
@CODE_COVERAGE_RULES@ @CODE_COVERAGE_RULES@
.PHONY: gitrev GITREV = include/zfs_gitrev.h
gitrev:
-${top_srcdir}/scripts/make_gitrev.sh
BUILT_SOURCES = gitrev PHONY = gitrev
gitrev:
$(AM_V_GEN)$(top_srcdir)/scripts/make_gitrev.sh $(GITREV)
all: gitrev
# Double-colon rules are allowed; there are multiple independent definitions.
maintainer-clean-local::
-$(RM) $(GITREV)
distclean-local:: distclean-local::
-$(RM) -R autom4te*.cache -$(RM) -R autom4te*.cache build
-find . \( -name SCCS -o -name BitKeeper -o -name .svn -o -name CVS \ -find . \( -name SCCS -o -name BitKeeper -o -name .svn -o -name CVS \
-o -name .pc -o -name .hg -o -name .git \) -prune -o \ -o -name .pc -o -name .hg -o -name .git \) -prune -o \
\( -name '*.orig' -o -name '*.rej' -o -name '*~' \ \( -name '*.orig' -o -name '*.rej' -o -name '*~' \
@ -52,13 +75,15 @@ distclean-local::
-type f -print | xargs $(RM) -type f -print | xargs $(RM)
all-local: all-local:
-${top_srcdir}/scripts/zfs-tests.sh -c -[ -x ${top_builddir}/scripts/zfs-tests.sh ] && \
${top_builddir}/scripts/zfs-tests.sh -c
dist-hook: gitrev dist-hook:
cp ${top_srcdir}/include/zfs_gitrev.h $(distdir)/include; \ $(AM_V_GEN)$(top_srcdir)/scripts/make_gitrev.sh -D $(distdir) $(GITREV)
sed -i 's/Release:[[:print:]]*/Release: $(RELEASE)/' \ $(SED) ${ac_inplace} -e 's/Release:[[:print:]]*/Release: $(RELEASE)/' \
$(distdir)/META $(distdir)/META
if BUILD_LINUX
# For compatibility, create a matching spl-x.y.z directly which contains # For compatibility, create a matching spl-x.y.z directly which contains
# symlinks to the updated header and object file locations. These # symlinks to the updated header and object file locations. These
# compatibility links will be removed in the next major release. # compatibility links will be removed in the next major release.
@ -75,75 +100,102 @@ install-data-hook:
ln -fs zfs_config.h spl_config.h && \ ln -fs zfs_config.h spl_config.h && \
ln -fs zfs.release spl.release ln -fs zfs.release spl.release
endif endif
endif
codecheck: cstyle shellcheck flake8 mancheck testscheck vcscheck PHONY += codecheck
codecheck: cstyle shellcheck checkbashisms flake8 mancheck testscheck vcscheck
PHONY += checkstyle
checkstyle: codecheck commitcheck checkstyle: codecheck commitcheck
PHONY += commitcheck
commitcheck: commitcheck:
@if git rev-parse --git-dir > /dev/null 2>&1; then \ @if git rev-parse --git-dir > /dev/null 2>&1; then \
${top_srcdir}/scripts/commitcheck.sh; \ ${top_srcdir}/scripts/commitcheck.sh; \
fi fi
PHONY += cstyle
cstyle: cstyle:
@find ${top_srcdir} -name '*.[hc]' ! -name 'zfs_config.*' \ @find ${top_srcdir} -name build -prune \
! -name '*.mod.c' -type f \ -o -type f -name '*.[hc]' \
! -name 'zfs_config.*' ! -name '*.mod.c' \
! -name 'opt_global.h' ! -name '*_if*.h' \
! -path './module/zstd/lib/*' \
-exec ${top_srcdir}/scripts/cstyle.pl -cpP {} \+ -exec ${top_srcdir}/scripts/cstyle.pl -cpP {} \+
shellcheck: filter_executable = -exec test -x '{}' \; -print
@if type shellcheck > /dev/null 2>&1; then \
shellcheck --exclude=SC1090 --format=gcc \
$$(find ${top_srcdir}/scripts/*.sh -type f) \
$$(find ${top_srcdir}/cmd/zed/zed.d/*.sh -type f) \
$$(find ${top_srcdir}/cmd/zpool/zpool.d/* -executable); \
else \
echo "skipping shellcheck because shellcheck is not installed"; \
fi
SHELLCHECKDIRS = cmd contrib etc scripts tests
SHELLCHECKSCRIPTS = autogen.sh
PHONY += checkabi storeabi
checklibabiversion:
libabiversion=`abidw -v | $(SED) 's/[^0-9]//g'`; \
if test $$libabiversion -lt "200"; then \
/bin/echo -e "\n" \
"*** Please use libabigail 2.0.0 version or newer;\n" \
"*** otherwise results are not consistent!\n" \
"(or see https://github.com/openzfs/libabigail-docker )\n"; \
exit 1; \
fi;
checkabi: checklibabiversion lib
$(MAKE) -C lib checkabi
storeabi: checklibabiversion lib
$(MAKE) -C lib storeabi
PHONY += mancheck
mancheck: mancheck:
@if type mandoc > /dev/null 2>&1; then \ ${top_srcdir}/scripts/mancheck.sh ${top_srcdir}/man ${top_srcdir}/tests/test-runner/man
find ${top_srcdir}/man/man8 -type f -name 'zfs.8' \
-o -name 'zpool.8' -o -name 'zdb.8' \
-o -name 'zgenhostid.8' | \
xargs mandoc -Tlint -Werror; \
else \
echo "skipping mancheck because mandoc is not installed"; \
fi
if BUILD_LINUX
stat_fmt = -c '%A %n'
else
stat_fmt = -f '%Sp %N'
endif
PHONY += testscheck
testscheck: testscheck:
@find ${top_srcdir}/tests/zfs-tests -type f \ @find ${top_srcdir}/tests/zfs-tests -type f \
\( -name '*.ksh' -not -executable \) -o \ \( -name '*.ksh' -not ${filter_executable} \) -o \
\( -name '*.kshlib' -executable \) -o \ \( -name '*.kshlib' ${filter_executable} \) -o \
\( -name '*.shlib' -executable \) -o \ \( -name '*.shlib' ${filter_executable} \) -o \
\( -name '*.cfg' -executable \) | \ \( -name '*.cfg' ${filter_executable} \) | \
xargs -r stat -c '%A %n' | \ xargs -r stat ${stat_fmt} | \
awk '{c++; print} END {if(c>0) exit 1}' awk '{c++; print} END {if(c>0) exit 1}'
PHONY += vcscheck
vcscheck: vcscheck:
@if git rev-parse --git-dir > /dev/null 2>&1; then \ @if git rev-parse --git-dir > /dev/null 2>&1; then \
git ls-files . --exclude-standard --others | \ git ls-files . --exclude-standard --others | \
awk '{c++; print} END {if(c>0) exit 1}' ; \ awk '{c++; print} END {if(c>0) exit 1}' ; \
fi fi
PHONY += lint
lint: cppcheck paxcheck lint: cppcheck paxcheck
cppcheck: CPPCHECKDIRS = cmd lib module
@if type cppcheck > /dev/null 2>&1; then \ PHONY += cppcheck
cppcheck --quiet --force --error-exitcode=2 --inline-suppr \ cppcheck: $(CPPCHECKDIRS)
--suppressions-list=.github/suppressions.txt \ @if test -n "$(CPPCHECK)"; then \
-UHAVE_SSE2 -UHAVE_AVX512F -UHAVE_UIO_ZEROCOPY \ set -e ; for dir in $(CPPCHECKDIRS) ; do \
${top_srcdir}; \ $(MAKE) -C $$dir cppcheck ; \
done \
else \ else \
echo "skipping cppcheck because cppcheck is not installed"; \ echo "skipping cppcheck because cppcheck is not installed"; \
fi fi
PHONY += paxcheck
paxcheck: paxcheck:
@if type scanelf > /dev/null 2>&1; then \ @if type scanelf > /dev/null 2>&1; then \
${top_srcdir}/scripts/paxcheck.sh ${top_srcdir}; \ ${top_srcdir}/scripts/paxcheck.sh ${top_builddir}; \
else \ else \
echo "skipping paxcheck because scanelf is not installed"; \ echo "skipping paxcheck because scanelf is not installed"; \
fi fi
PHONY += flake8
flake8: flake8:
@if type flake8 > /dev/null 2>&1; then \ @if type flake8 > /dev/null 2>&1; then \
flake8 ${top_srcdir}; \ flake8 ${top_srcdir}; \
@ -151,17 +203,34 @@ flake8:
echo "skipping flake8 because flake8 is not installed"; \ echo "skipping flake8 because flake8 is not installed"; \
fi fi
PHONY += ctags
ctags: ctags:
$(RM) tags $(RM) tags
find $(top_srcdir) -name .git -prune -o -name '*.[hc]' | xargs ctags find $(top_srcdir) -name '.?*' -prune \
-o -type f -name '*.[hcS]' -print | xargs ctags -a
PHONY += etags
etags: etags:
$(RM) TAGS $(RM) TAGS
find $(top_srcdir) -name .pc -prune -o -name '*.[hc]' | xargs etags -a find $(top_srcdir) -name '.?*' -prune \
-o -type f -name '*.[hcS]' -print | xargs etags -a
PHONY += cscopelist
cscopelist:
find $(top_srcdir) -name '.?*' -prune \
-o -type f -name '*.[hc]' -print >cscope.files
PHONY += tags
tags: ctags etags tags: ctags etags
PHONY += pkg pkg-dkms pkg-kmod pkg-utils
pkg: @DEFAULT_PACKAGE@ pkg: @DEFAULT_PACKAGE@
pkg-dkms: @DEFAULT_PACKAGE@-dkms pkg-dkms: @DEFAULT_PACKAGE@-dkms
pkg-kmod: @DEFAULT_PACKAGE@-kmod pkg-kmod: @DEFAULT_PACKAGE@-kmod
pkg-utils: @DEFAULT_PACKAGE@-utils pkg-utils: @DEFAULT_PACKAGE@-utils
include config/rpm.am
include config/deb.am
include config/tgz.am
.PHONY: $(PHONY)

2
NEWS
View File

@ -1,3 +1,3 @@
Descriptions of all releases can be found on github: Descriptions of all releases can be found on github:
https://github.com/zfsonlinux/zfs/releases https://github.com/openzfs/zfs/releases

View File

@ -1,31 +1,35 @@
![img](http://zfsonlinux.org/images/zfs-linux.png) ![img](https://openzfs.github.io/openzfs-docs/_static/img/logo/480px-Open-ZFS-Secondary-Logo-Colour-halfsize.png)
ZFS on Linux is an advanced file system and volume manager which was originally OpenZFS is an advanced file system and volume manager which was originally
developed for Solaris and is now maintained by the OpenZFS community. developed for Solaris and is now maintained by the OpenZFS community.
This repository contains the code for running OpenZFS on Linux and FreeBSD.
[![codecov](https://codecov.io/gh/zfsonlinux/zfs/branch/master/graph/badge.svg)](https://codecov.io/gh/zfsonlinux/zfs) [![codecov](https://codecov.io/gh/openzfs/zfs/branch/master/graph/badge.svg)](https://codecov.io/gh/openzfs/zfs)
[![coverity](https://scan.coverity.com/projects/1973/badge.svg)](https://scan.coverity.com/projects/zfsonlinux-zfs) [![coverity](https://scan.coverity.com/projects/1973/badge.svg)](https://scan.coverity.com/projects/openzfs-zfs)
# Official Resources # Official Resources
* [Site](http://zfsonlinux.org) * [Documentation](https://openzfs.github.io/openzfs-docs/) - for using and developing this repo
* [Wiki](https://github.com/zfsonlinux/zfs/wiki) * [ZoL Site](https://zfsonlinux.org) - Linux release info & links
* [Mailing lists](https://github.com/zfsonlinux/zfs/wiki/Mailing-Lists) * [Mailing lists](https://openzfs.github.io/openzfs-docs/Project%20and%20Community/Mailing%20Lists.html)
* [OpenZFS site](http://open-zfs.org/) * [OpenZFS site](http://open-zfs.org/) - for conference videos and info on other platforms (illumos, OSX, Windows, etc)
# Installation # Installation
Full documentation for installing ZoL on your favorite Linux distribution can Full documentation for installing OpenZFS on your favorite operating system can
be found at [our site](http://zfsonlinux.org/). be found at the [Getting Started Page](https://openzfs.github.io/openzfs-docs/Getting%20Started/index.html).
# Contribute & Develop # Contribute & Develop
We have a separate document with [contribution guidelines](./.github/CONTRIBUTING.md). We have a separate document with [contribution guidelines](./.github/CONTRIBUTING.md).
We have a [Code of Conduct](./CODE_OF_CONDUCT.md).
# Release # Release
ZFS on Linux is released under a CDDL license. OpenZFS is released under a CDDL license.
For more details see the NOTICE, LICENSE and COPYRIGHT files; `UCRL-CODE-235197` For more details see the NOTICE, LICENSE and COPYRIGHT files; `UCRL-CODE-235197`
# Supported Kernels # Supported Kernels
* The `META` file contains the officially recognized supported kernel versions. * The `META` file contains the officially recognized supported Linux kernel versions.
* Supported FreeBSD versions are any supported branches and releases starting from 12.2-RELEASE.

37
RELEASES.md Normal file
View File

@ -0,0 +1,37 @@
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.

61
TEST
View File

@ -48,64 +48,3 @@
#TEST_ZFSSTRESS_VDEV="/var/tmp/vdev" #TEST_ZFSSTRESS_VDEV="/var/tmp/vdev"
#TEST_ZFSSTRESS_DIR="/$TEST_ZFSSTRESS_POOL/$TEST_ZFSSTRESS_FS" #TEST_ZFSSTRESS_DIR="/$TEST_ZFSSTRESS_POOL/$TEST_ZFSSTRESS_FS"
#TEST_ZFSSTRESS_OPTIONS="" #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
###
#
# Run ztest longer on the "coverage" builders to gain more code coverage
# data out of ztest, libzpool, etc.
#
case "$BB_NAME" in
*coverage*)
TEST_ZTEST_TIMEOUT=3600
;;
*)
TEST_ZTEST_TIMEOUT=900
;;
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

View File

@ -1,3 +1,27 @@
SUBDIRS = zfs zpool zdb zhack zinject zstreamdump ztest include $(top_srcdir)/config/Shellcheck.am
SUBDIRS += mount_zfs fsck_zfs zvol_id vdev_id arcstat dbufstat zed
SUBDIRS += arc_summary raidz_test zgenhostid SUBDIRS = zfs zpool zdb zhack zinject zstream ztest
SUBDIRS += fsck_zfs vdev_id raidz_test zfs_ids_to_path
SUBDIRS += zpool_influxdb
CPPCHECKDIRS = zfs zpool zdb zhack zinject zstream ztest
CPPCHECKDIRS += raidz_test zfs_ids_to_path zpool_influxdb
# TODO: #12084: SHELLCHECKDIRS = fsck_zfs vdev_id zpool
SHELLCHECKDIRS = fsck_zfs zpool
if USING_PYTHON
SUBDIRS += arcstat arc_summary dbufstat
endif
if BUILD_LINUX
SUBDIRS += mount_zfs zed zgenhostid zvol_id zvol_wait
CPPCHECKDIRS += mount_zfs zed zgenhostid zvol_id
SHELLCHECKDIRS += zed
endif
PHONY = cppcheck
cppcheck: $(CPPCHECKDIRS)
set -e ; for dir in $(CPPCHECKDIRS) ; do \
$(MAKE) -C $$dir cppcheck ; \
done

1
cmd/arc_summary/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
arc_summary

View File

@ -1,13 +1,13 @@
bin_SCRIPTS = arc_summary
CLEANFILES = arc_summary
EXTRA_DIST = arc_summary2 arc_summary3 EXTRA_DIST = arc_summary2 arc_summary3
if USING_PYTHON_2 if USING_PYTHON_2
dist_bin_SCRIPTS = arc_summary2 SCRIPT = arc_summary2
install-exec-hook: else
mv $(DESTDIR)$(bindir)/arc_summary2 $(DESTDIR)$(bindir)/arc_summary SCRIPT = arc_summary3
endif endif
if USING_PYTHON_3 arc_summary: $(SCRIPT)
dist_bin_SCRIPTS = arc_summary3 cp $< $@
install-exec-hook:
mv $(DESTDIR)$(bindir)/arc_summary3 $(DESTDIR)$(bindir)/arc_summary
endif

View File

@ -1,4 +1,4 @@
#!/usr/bin/python2 #!/usr/bin/env python2
# #
# $Id: arc_summary.pl,v 388:e27800740aa2 2011-07-08 02:53:29Z jhell $ # $Id: arc_summary.pl,v 388:e27800740aa2 2011-07-08 02:53:29Z jhell $
# #
@ -42,7 +42,7 @@
Provides basic information on the ARC, its efficiency, the L2ARC (if present), Provides basic information on the ARC, its efficiency, the L2ARC (if present),
the Data Management Unit (DMU), Virtual Devices (VDEVs), and tunables. See the the Data Management Unit (DMU), Virtual Devices (VDEVs), and tunables. See the
in-source documentation and code at in-source documentation and code at
https://github.com/zfsonlinux/zfs/blob/master/module/zfs/arc.c for details. https://github.com/openzfs/zfs/blob/master/module/zfs/arc.c for details.
""" """
import getopt import getopt
@ -54,46 +54,64 @@ import errno
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
from decimal import Decimal as D from decimal import Decimal as D
if sys.platform.startswith('freebsd'):
# Requires py27-sysctl on FreeBSD
import sysctl
def is_value(ctl):
return ctl.type != sysctl.CTLTYPE_NODE
def load_kstats(namespace):
"""Collect information on a specific subsystem of the ARC"""
base = 'kstat.zfs.misc.%s.' % namespace
fmt = lambda kstat: (kstat.name, D(kstat.value))
kstats = sysctl.filter(base)
return [fmt(kstat) for kstat in kstats if is_value(kstat)]
def load_tunables():
ctls = sysctl.filter('vfs.zfs')
return dict((ctl.name, ctl.value) for ctl in ctls if is_value(ctl))
elif sys.platform.startswith('linux'):
def load_kstats(namespace):
"""Collect information on a specific subsystem of the ARC"""
kstat = 'kstat.zfs.misc.%s.%%s' % namespace
path = '/proc/spl/kstat/zfs/%s' % namespace
with open(path) as f:
entries = [line.strip().split() for line in f][2:] # Skip header
return [(kstat % name, D(value)) for name, _, value in entries]
def load_tunables():
basepath = '/sys/module/zfs/parameters'
tunables = {}
for name in os.listdir(basepath):
if not name:
continue
path = '%s/%s' % (basepath, name)
with open(path) as f:
value = f.read()
tunables[name] = value.strip()
return tunables
show_tunable_descriptions = False show_tunable_descriptions = False
alternate_tunable_layout = False alternate_tunable_layout = False
def handle_Exception(ex_cls, ex, tb):
if ex is IOError:
if ex.errno == errno.EPIPE:
sys.exit()
if ex is KeyboardInterrupt:
sys.exit()
sys.excepthook = handle_Exception
def get_Kstat(): def get_Kstat():
"""Collect information on the ZFS subsystem from the /proc virtual """Collect information on the ZFS subsystem from the /proc virtual
file system. The name "kstat" is a holdover from the Solaris utility file system. The name "kstat" is a holdover from the Solaris utility
of the same name. of the same name.
""" """
def load_proc_kstats(fn, namespace):
"""Collect information on a specific subsystem of the ARC"""
kstats = [line.strip() for line in open(fn)]
del kstats[0:2]
for kstat in kstats:
kstat = kstat.strip()
name, _, value = kstat.split()
Kstat[namespace + name] = D(value)
Kstat = {} Kstat = {}
load_proc_kstats('/proc/spl/kstat/zfs/arcstats', Kstat.update(load_kstats('arcstats'))
'kstat.zfs.misc.arcstats.') Kstat.update(load_kstats('zfetchstats'))
load_proc_kstats('/proc/spl/kstat/zfs/zfetchstats', Kstat.update(load_kstats('vdev_cache_stats'))
'kstat.zfs.misc.zfetchstats.')
load_proc_kstats('/proc/spl/kstat/zfs/vdev_cache_stats',
'kstat.zfs.misc.vdev_cache_stats.')
return Kstat return Kstat
@ -195,12 +213,30 @@ def get_arc_summary(Kstat):
deleted = Kstat["kstat.zfs.misc.arcstats.deleted"] deleted = Kstat["kstat.zfs.misc.arcstats.deleted"]
mutex_miss = Kstat["kstat.zfs.misc.arcstats.mutex_miss"] mutex_miss = Kstat["kstat.zfs.misc.arcstats.mutex_miss"]
evict_skip = Kstat["kstat.zfs.misc.arcstats.evict_skip"] evict_skip = Kstat["kstat.zfs.misc.arcstats.evict_skip"]
evict_l2_cached = Kstat["kstat.zfs.misc.arcstats.evict_l2_cached"]
evict_l2_eligible = Kstat["kstat.zfs.misc.arcstats.evict_l2_eligible"]
evict_l2_eligible_mfu = Kstat["kstat.zfs.misc.arcstats.evict_l2_eligible_mfu"]
evict_l2_eligible_mru = Kstat["kstat.zfs.misc.arcstats.evict_l2_eligible_mru"]
evict_l2_ineligible = Kstat["kstat.zfs.misc.arcstats.evict_l2_ineligible"]
evict_l2_skip = Kstat["kstat.zfs.misc.arcstats.evict_l2_skip"]
# ARC Misc. # ARC Misc.
output["arc_misc"] = {} output["arc_misc"] = {}
output["arc_misc"]["deleted"] = fHits(deleted) output["arc_misc"]["deleted"] = fHits(deleted)
output["arc_misc"]['mutex_miss'] = fHits(mutex_miss) output["arc_misc"]["mutex_miss"] = fHits(mutex_miss)
output["arc_misc"]['evict_skips'] = fHits(evict_skip) output["arc_misc"]["evict_skips"] = fHits(evict_skip)
output["arc_misc"]["evict_l2_skip"] = fHits(evict_l2_skip)
output["arc_misc"]["evict_l2_cached"] = fBytes(evict_l2_cached)
output["arc_misc"]["evict_l2_eligible"] = fBytes(evict_l2_eligible)
output["arc_misc"]["evict_l2_eligible_mfu"] = {
'per': fPerc(evict_l2_eligible_mfu, evict_l2_eligible),
'num': fBytes(evict_l2_eligible_mfu),
}
output["arc_misc"]["evict_l2_eligible_mru"] = {
'per': fPerc(evict_l2_eligible_mru, evict_l2_eligible),
'num': fBytes(evict_l2_eligible_mru),
}
output["arc_misc"]["evict_l2_ineligible"] = fBytes(evict_l2_ineligible)
# ARC Sizing # ARC Sizing
arc_size = Kstat["kstat.zfs.misc.arcstats.size"] arc_size = Kstat["kstat.zfs.misc.arcstats.size"]
@ -316,8 +352,26 @@ def _arc_summary(Kstat):
sys.stdout.write("\tDeleted:\t\t\t\t%s\n" % arc['arc_misc']['deleted']) sys.stdout.write("\tDeleted:\t\t\t\t%s\n" % arc['arc_misc']['deleted'])
sys.stdout.write("\tMutex Misses:\t\t\t\t%s\n" % sys.stdout.write("\tMutex Misses:\t\t\t\t%s\n" %
arc['arc_misc']['mutex_miss']) arc['arc_misc']['mutex_miss'])
sys.stdout.write("\tEvict Skips:\t\t\t\t%s\n" % sys.stdout.write("\tEviction Skips:\t\t\t\t%s\n" %
arc['arc_misc']['evict_skips']) arc['arc_misc']['evict_skips'])
sys.stdout.write("\tEviction Skips Due to L2 Writes:\t%s\n" %
arc['arc_misc']['evict_l2_skip'])
sys.stdout.write("\tL2 Cached Evictions:\t\t\t%s\n" %
arc['arc_misc']['evict_l2_cached'])
sys.stdout.write("\tL2 Eligible Evictions:\t\t\t%s\n" %
arc['arc_misc']['evict_l2_eligible'])
sys.stdout.write("\tL2 Eligible MFU Evictions:\t%s\t%s\n" % (
arc['arc_misc']['evict_l2_eligible_mfu']['per'],
arc['arc_misc']['evict_l2_eligible_mfu']['num'],
)
)
sys.stdout.write("\tL2 Eligible MRU Evictions:\t%s\t%s\n" % (
arc['arc_misc']['evict_l2_eligible_mru']['per'],
arc['arc_misc']['evict_l2_eligible_mru']['num'],
)
)
sys.stdout.write("\tL2 Ineligible Evictions:\t\t%s\n" %
arc['arc_misc']['evict_l2_ineligible'])
sys.stdout.write("\n") sys.stdout.write("\n")
# ARC Sizing # ARC Sizing
@ -653,6 +707,11 @@ def get_l2arc_summary(Kstat):
l2_writes_done = Kstat["kstat.zfs.misc.arcstats.l2_writes_done"] l2_writes_done = Kstat["kstat.zfs.misc.arcstats.l2_writes_done"]
l2_writes_error = Kstat["kstat.zfs.misc.arcstats.l2_writes_error"] l2_writes_error = Kstat["kstat.zfs.misc.arcstats.l2_writes_error"]
l2_writes_sent = Kstat["kstat.zfs.misc.arcstats.l2_writes_sent"] l2_writes_sent = Kstat["kstat.zfs.misc.arcstats.l2_writes_sent"]
l2_mfu_asize = Kstat["kstat.zfs.misc.arcstats.l2_mfu_asize"]
l2_mru_asize = Kstat["kstat.zfs.misc.arcstats.l2_mru_asize"]
l2_prefetch_asize = Kstat["kstat.zfs.misc.arcstats.l2_prefetch_asize"]
l2_bufc_data_asize = Kstat["kstat.zfs.misc.arcstats.l2_bufc_data_asize"]
l2_bufc_metadata_asize = Kstat["kstat.zfs.misc.arcstats.l2_bufc_metadata_asize"]
l2_access_total = (l2_hits + l2_misses) l2_access_total = (l2_hits + l2_misses)
output['l2_health_count'] = (l2_writes_error + l2_cksum_bad + l2_io_error) output['l2_health_count'] = (l2_writes_error + l2_cksum_bad + l2_io_error)
@ -675,7 +734,7 @@ def get_l2arc_summary(Kstat):
output["io_errors"] = fHits(l2_io_error) output["io_errors"] = fHits(l2_io_error)
output["l2_arc_size"] = {} output["l2_arc_size"] = {}
output["l2_arc_size"]["adative"] = fBytes(l2_size) output["l2_arc_size"]["adaptive"] = fBytes(l2_size)
output["l2_arc_size"]["actual"] = { output["l2_arc_size"]["actual"] = {
'per': fPerc(l2_asize, l2_size), 'per': fPerc(l2_asize, l2_size),
'num': fBytes(l2_asize) 'num': fBytes(l2_asize)
@ -684,6 +743,26 @@ def get_l2arc_summary(Kstat):
'per': fPerc(l2_hdr_size, l2_size), 'per': fPerc(l2_hdr_size, l2_size),
'num': fBytes(l2_hdr_size), 'num': fBytes(l2_hdr_size),
} }
output["l2_arc_size"]["mfu_asize"] = {
'per': fPerc(l2_mfu_asize, l2_asize),
'num': fBytes(l2_mfu_asize),
}
output["l2_arc_size"]["mru_asize"] = {
'per': fPerc(l2_mru_asize, l2_asize),
'num': fBytes(l2_mru_asize),
}
output["l2_arc_size"]["prefetch_asize"] = {
'per': fPerc(l2_prefetch_asize, l2_asize),
'num': fBytes(l2_prefetch_asize),
}
output["l2_arc_size"]["bufc_data_asize"] = {
'per': fPerc(l2_bufc_data_asize, l2_asize),
'num': fBytes(l2_bufc_data_asize),
}
output["l2_arc_size"]["bufc_metadata_asize"] = {
'per': fPerc(l2_bufc_metadata_asize, l2_asize),
'num': fBytes(l2_bufc_metadata_asize),
}
output["l2_arc_evicts"] = {} output["l2_arc_evicts"] = {}
output["l2_arc_evicts"]['lock_retries'] = fHits(l2_evict_lock_retry) output["l2_arc_evicts"]['lock_retries'] = fHits(l2_evict_lock_retry)
@ -748,7 +827,7 @@ def _l2arc_summary(Kstat):
sys.stdout.write("\n") sys.stdout.write("\n")
sys.stdout.write("L2 ARC Size: (Adaptive)\t\t\t\t%s\n" % sys.stdout.write("L2 ARC Size: (Adaptive)\t\t\t\t%s\n" %
arc["l2_arc_size"]["adative"]) arc["l2_arc_size"]["adaptive"])
sys.stdout.write("\tCompressed:\t\t\t%s\t%s\n" % ( sys.stdout.write("\tCompressed:\t\t\t%s\t%s\n" % (
arc["l2_arc_size"]["actual"]["per"], arc["l2_arc_size"]["actual"]["per"],
arc["l2_arc_size"]["actual"]["num"], arc["l2_arc_size"]["actual"]["num"],
@ -759,11 +838,36 @@ def _l2arc_summary(Kstat):
arc["l2_arc_size"]["head_size"]["num"], arc["l2_arc_size"]["head_size"]["num"],
) )
) )
sys.stdout.write("\tMFU Alloc. Size:\t\t%s\t%s\n" % (
arc["l2_arc_size"]["mfu_asize"]["per"],
arc["l2_arc_size"]["mfu_asize"]["num"],
)
)
sys.stdout.write("\tMRU Alloc. Size:\t\t%s\t%s\n" % (
arc["l2_arc_size"]["mru_asize"]["per"],
arc["l2_arc_size"]["mru_asize"]["num"],
)
)
sys.stdout.write("\tPrefetch Alloc. Size:\t\t%s\t%s\n" % (
arc["l2_arc_size"]["prefetch_asize"]["per"],
arc["l2_arc_size"]["prefetch_asize"]["num"],
)
)
sys.stdout.write("\tData (buf content) Alloc. Size:\t%s\t%s\n" % (
arc["l2_arc_size"]["bufc_data_asize"]["per"],
arc["l2_arc_size"]["bufc_data_asize"]["num"],
)
)
sys.stdout.write("\tMetadata (buf content) Size:\t%s\t%s\n" % (
arc["l2_arc_size"]["bufc_metadata_asize"]["per"],
arc["l2_arc_size"]["bufc_metadata_asize"]["num"],
)
)
sys.stdout.write("\n") sys.stdout.write("\n")
if arc["l2_arc_evicts"]['lock_retries'] != '0' or \ if arc["l2_arc_evicts"]['lock_retries'] != '0' or \
arc["l2_arc_evicts"]["reading"] != '0': arc["l2_arc_evicts"]["reading"] != '0':
sys.stdout.write("L2 ARC Evicts:\n") sys.stdout.write("L2 ARC Evictions:\n")
sys.stdout.write("\tLock Retries:\t\t\t\t%s\n" % sys.stdout.write("\tLock Retries:\t\t\t\t%s\n" %
arc["l2_arc_evicts"]['lock_retries']) arc["l2_arc_evicts"]['lock_retries'])
sys.stdout.write("\tUpon Reading:\t\t\t\t%s\n" % sys.stdout.write("\tUpon Reading:\t\t\t\t%s\n" %
@ -921,14 +1025,7 @@ def _tunable_summary(Kstat):
global show_tunable_descriptions global show_tunable_descriptions
global alternate_tunable_layout global alternate_tunable_layout
names = os.listdir("/sys/module/zfs/parameters/") tunables = load_tunables()
values = {}
for name in names:
with open("/sys/module/zfs/parameters/" + name) as f:
value = f.read()
values[name] = value.strip()
descriptions = {} descriptions = {}
if show_tunable_descriptions: if show_tunable_descriptions:
@ -966,22 +1063,17 @@ def _tunable_summary(Kstat):
sys.stderr.write("Tunable descriptions will be disabled.\n") sys.stderr.write("Tunable descriptions will be disabled.\n")
sys.stdout.write("ZFS Tunables:\n") sys.stdout.write("ZFS Tunables:\n")
names.sort()
if alternate_tunable_layout: if alternate_tunable_layout:
fmt = "\t%s=%s\n" fmt = "\t%s=%s\n"
else: else:
fmt = "\t%-50s%s\n" fmt = "\t%-50s%s\n"
for name in names: for name in sorted(tunables.keys()):
if not name:
continue
if show_tunable_descriptions and name in descriptions: if show_tunable_descriptions and name in descriptions:
sys.stdout.write("\t# %s\n" % descriptions[name]) sys.stdout.write("\t# %s\n" % descriptions[name])
sys.stdout.write(fmt % (name, values[name])) sys.stdout.write(fmt % (name, tunables[name]))
unSub = [ unSub = [
@ -1033,48 +1125,55 @@ def main():
global alternate_tunable_layout global alternate_tunable_layout
try: try:
opts, args = getopt.getopt(
sys.argv[1:],
"adp:h", ["alternate", "description", "page=", "help"]
)
except getopt.error as e:
sys.stderr.write("Error: %s\n" % e.msg)
usage()
sys.exit(1)
args = {}
for opt, arg in opts:
if opt in ('-a', '--alternate'):
args['a'] = True
if opt in ('-d', '--description'):
args['d'] = True
if opt in ('-p', '--page'):
args['p'] = arg
if opt in ('-h', '--help'):
usage()
sys.exit(0)
Kstat = get_Kstat()
alternate_tunable_layout = 'a' in args
show_tunable_descriptions = 'd' in args
pages = []
if 'p' in args:
try: try:
pages.append(unSub[int(args['p']) - 1]) opts, args = getopt.getopt(
except IndexError: sys.argv[1:],
sys.stderr.write('the argument to -p must be between 1 and ' + "adp:h", ["alternate", "description", "page=", "help"]
str(len(unSub)) + '\n') )
except getopt.error as e:
sys.stderr.write("Error: %s\n" % e.msg)
usage()
sys.exit(1) sys.exit(1)
else:
pages = unSub
zfs_header() args = {}
for page in pages: for opt, arg in opts:
page(Kstat) if opt in ('-a', '--alternate'):
sys.stdout.write("\n") args['a'] = True
if opt in ('-d', '--description'):
args['d'] = True
if opt in ('-p', '--page'):
args['p'] = arg
if opt in ('-h', '--help'):
usage()
sys.exit(0)
Kstat = get_Kstat()
alternate_tunable_layout = 'a' in args
show_tunable_descriptions = 'd' in args
pages = []
if 'p' in args:
try:
pages.append(unSub[int(args['p']) - 1])
except IndexError:
sys.stderr.write('the argument to -p must be between 1 and ' +
str(len(unSub)) + '\n')
sys.exit(1)
else:
pages = unSub
zfs_header()
for page in pages:
page(Kstat)
sys.stdout.write("\n")
except IOError as ex:
if (ex.errno == errno.EPIPE):
sys.exit(0)
raise
except KeyboardInterrupt:
sys.exit(0)
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -1,4 +1,4 @@
#!/usr/bin/python3 #!/usr/bin/env python3
# #
# Copyright (c) 2008 Ben Rockwood <benr@cuddletech.com>, # Copyright (c) 2008 Ben Rockwood <benr@cuddletech.com>,
# Copyright (c) 2010 Martin Matuska <mm@FreeBSD.org>, # Copyright (c) 2010 Martin Matuska <mm@FreeBSD.org>,
@ -32,7 +32,7 @@
Provides basic information on the ARC, its efficiency, the L2ARC (if present), Provides basic information on the ARC, its efficiency, the L2ARC (if present),
the Data Management Unit (DMU), Virtual Devices (VDEVs), and tunables. See the Data Management Unit (DMU), Virtual Devices (VDEVs), and tunables. See
the in-source documentation and code at the in-source documentation and code at
https://github.com/zfsonlinux/zfs/blob/master/module/zfs/arc.c for details. https://github.com/openzfs/zfs/blob/master/module/zfs/arc.c for details.
The original introduction to arc_summary can be found at The original introduction to arc_summary can be found at
http://cuddletech.com/?p=454 http://cuddletech.com/?p=454
""" """
@ -42,13 +42,17 @@ import os
import subprocess import subprocess
import sys import sys
import time import time
import errno
DECRIPTION = 'Print ARC and other statistics for ZFS on Linux' # We can't use env -S portably, and we need python3 -u to handle pipes in
# the shell abruptly closing the way we want to, so...
import io
if isinstance(sys.__stderr__.buffer, io.BufferedWriter):
os.execv(sys.executable, [sys.executable, "-u"] + sys.argv)
DESCRIPTION = 'Print ARC and other statistics for OpenZFS'
INDENT = ' '*8 INDENT = ' '*8
LINE_LENGTH = 72 LINE_LENGTH = 72
PROC_PATH = '/proc/spl/kstat/zfs/'
SPL_PATH = '/sys/module/spl/parameters/'
TUNABLES_PATH = '/sys/module/zfs/parameters/'
DATE_FORMAT = '%a %b %d %H:%M:%S %Y' DATE_FORMAT = '%a %b %d %H:%M:%S %Y'
TITLE = 'ZFS Subsystem Report' TITLE = 'ZFS Subsystem Report'
@ -61,11 +65,10 @@ SECTION_PATHS = {'arc': 'arcstats',
'dmu': 'dmu_tx', 'dmu': 'dmu_tx',
'l2arc': 'arcstats', # L2ARC stuff lives in arcstats 'l2arc': 'arcstats', # L2ARC stuff lives in arcstats
'vdev': 'vdev_cache_stats', 'vdev': 'vdev_cache_stats',
'xuio': 'xuio_stats',
'zfetch': 'zfetchstats', 'zfetch': 'zfetchstats',
'zil': 'zil'} 'zil': 'zil'}
parser = argparse.ArgumentParser(description=DECRIPTION) parser = argparse.ArgumentParser(description=DESCRIPTION)
parser.add_argument('-a', '--alternate', action='store_true', default=False, parser.add_argument('-a', '--alternate', action='store_true', default=False,
help='use alternate formatting for tunables and SPL', help='use alternate formatting for tunables and SPL',
dest='alt') dest='alt')
@ -83,6 +86,172 @@ parser.add_argument('-s', '--section', dest='section', help=SECTION_HELP)
ARGS = parser.parse_args() ARGS = parser.parse_args()
if sys.platform.startswith('freebsd'):
# Requires py36-sysctl on FreeBSD
import sysctl
VDEV_CACHE_SIZE = 'vdev.cache_size'
def is_value(ctl):
return ctl.type != sysctl.CTLTYPE_NODE
def namefmt(ctl, base='vfs.zfs.'):
# base is removed from the name
cut = len(base)
return ctl.name[cut:]
def load_kstats(section):
base = 'kstat.zfs.misc.{section}.'.format(section=section)
fmt = lambda kstat: '{name} : {value}'.format(name=namefmt(kstat, base),
value=kstat.value)
kstats = sysctl.filter(base)
return [fmt(kstat) for kstat in kstats if is_value(kstat)]
def get_params(base):
ctls = sysctl.filter(base)
return {namefmt(ctl): str(ctl.value) for ctl in ctls if is_value(ctl)}
def get_tunable_params():
return get_params('vfs.zfs')
def get_vdev_params():
return get_params('vfs.zfs.vdev')
def get_version_impl(request):
# FreeBSD reports versions for zpl and spa instead of zfs and spl.
name = {'zfs': 'zpl',
'spl': 'spa'}[request]
mib = 'vfs.zfs.version.{}'.format(name)
version = sysctl.filter(mib)[0].value
return '{} version {}'.format(name, version)
def get_descriptions(_request):
ctls = sysctl.filter('vfs.zfs')
return {namefmt(ctl): ctl.description for ctl in ctls if is_value(ctl)}
elif sys.platform.startswith('linux'):
KSTAT_PATH = '/proc/spl/kstat/zfs'
SPL_PATH = '/sys/module/spl/parameters'
TUNABLES_PATH = '/sys/module/zfs/parameters'
VDEV_CACHE_SIZE = 'zfs_vdev_cache_size'
def load_kstats(section):
path = os.path.join(KSTAT_PATH, section)
with open(path) as f:
return list(f)[2:] # Get rid of header
def get_params(basepath):
"""Collect information on the Solaris Porting Layer (SPL) or the
tunables, depending on the PATH given. Does not check if PATH is
legal.
"""
result = {}
for name in os.listdir(basepath):
path = os.path.join(basepath, name)
with open(path) as f:
value = f.read()
result[name] = value.strip()
return result
def get_spl_params():
return get_params(SPL_PATH)
def get_tunable_params():
return get_params(TUNABLES_PATH)
def get_vdev_params():
return get_params(TUNABLES_PATH)
def get_version_impl(request):
# The original arc_summary called /sbin/modinfo/{spl,zfs} to get
# the version information. We switch to /sys/module/{spl,zfs}/version
# to make sure we get what is really loaded in the kernel
try:
with open("/sys/module/{}/version".format(request)) as f:
return f.read().strip()
except:
return "(unknown)"
def get_descriptions(request):
"""Get the descriptions of the Solaris Porting Layer (SPL) or the
tunables, return with minimal formatting.
"""
if request not in ('spl', 'zfs'):
print('ERROR: description of "{0}" requested)'.format(request))
sys.exit(1)
descs = {}
target_prefix = 'parm:'
# We would prefer to do this with /sys/modules -- see the discussion at
# get_version() -- but there isn't a way to get the descriptions from
# there, so we fall back on modinfo
command = ["/sbin/modinfo", request, "-0"]
# The recommended way to do this is with subprocess.run(). However,
# some installed versions of Python are < 3.5, so we offer them
# the option of doing it the old way (for now)
info = ''
try:
if 'run' in dir(subprocess):
info = subprocess.run(command, stdout=subprocess.PIPE,
universal_newlines=True)
raw_output = info.stdout.split('\0')
else:
info = subprocess.check_output(command,
universal_newlines=True)
raw_output = info.split('\0')
except subprocess.CalledProcessError:
print("Error: Descriptions not available",
"(can't access kernel module)")
sys.exit(1)
for line in raw_output:
if not line.startswith(target_prefix):
continue
line = line[len(target_prefix):].strip()
name, raw_desc = line.split(':', 1)
desc = raw_desc.rsplit('(', 1)[0]
if desc == '':
desc = '(No description found)'
descs[name.strip()] = desc.strip()
return descs
def handle_unraisableException(exc_type, exc_value=None, exc_traceback=None,
err_msg=None, object=None):
handle_Exception(exc_type, object, exc_traceback)
def handle_Exception(ex_cls, ex, tb):
if ex_cls is KeyboardInterrupt:
sys.exit()
if ex_cls is BrokenPipeError:
# It turns out that while sys.exit() triggers an exception
# not handled message on Python 3.8+, os._exit() does not.
os._exit(0)
if ex_cls is OSError:
if ex.errno == errno.ENOTCONN:
sys.exit()
raise ex
if hasattr(sys,'unraisablehook'): # Python 3.8+
sys.unraisablehook = handle_unraisableException
sys.excepthook = handle_Exception
def cleanup_line(single_line): def cleanup_line(single_line):
"""Format a raw line of data from /proc and isolate the name value """Format a raw line of data from /proc and isolate the name value
part, returning a tuple with each. Currently, this gets rid of the part, returning a tuple with each. Currently, this gets rid of the
@ -238,139 +407,48 @@ def format_raw_line(name, value):
if ARGS.alt: if ARGS.alt:
result = '{0}{1}={2}'.format(INDENT, name, value) result = '{0}{1}={2}'.format(INDENT, name, value)
else: else:
spc = LINE_LENGTH-(len(INDENT)+len(value)) # Right-align the value within the line length if it fits,
result = '{0}{1:<{spc}}{2}'.format(INDENT, name, value, spc=spc) # otherwise just separate it from the name by a single space.
fit = LINE_LENGTH - len(INDENT) - len(name)
overflow = len(value) + 1
w = max(fit, overflow)
result = '{0}{1}{2:>{w}}'.format(INDENT, name, value, w=w)
return result return result
def get_kstats(): def get_kstats():
"""Collect information on the ZFS subsystem from the /proc Linux virtual """Collect information on the ZFS subsystem. The step does not perform any
file system. The step does not perform any further processing, giving us further processing, giving us the option to only work on what is actually
the option to only work on what is actually needed. The name "kstat" is a needed. The name "kstat" is a holdover from the Solaris utility of the same
holdover from the Solaris utility of the same name. name.
""" """
result = {} result = {}
secs = SECTION_PATHS.values()
for section in secs: for section in SECTION_PATHS.values():
if section not in result:
with open(PROC_PATH+section, 'r') as proc_location: result[section] = load_kstats(section)
lines = [line for line in proc_location]
del lines[0:2] # Get rid of header
result[section] = lines
return result return result
def get_spl_tunables(PATH):
"""Collect information on the Solaris Porting Layer (SPL) or the
tunables, depending on the PATH given. Does not check if PATH is
legal.
"""
result = {}
parameters = os.listdir(PATH)
for name in parameters:
with open(PATH+name, 'r') as para_file:
value = para_file.read()
result[name] = value.strip()
return result
def get_descriptions(request):
"""Get the decriptions of the Solaris Porting Layer (SPL) or the
tunables, return with minimal formatting.
"""
if request not in ('spl', 'zfs'):
print('ERROR: description of "{0}" requested)'.format(request))
sys.exit(1)
descs = {}
target_prefix = 'parm:'
# We would prefer to do this with /sys/modules -- see the discussion at
# get_version() -- but there isn't a way to get the descriptions from
# there, so we fall back on modinfo
command = ["/sbin/modinfo", request, "-0"]
# The recommended way to do this is with subprocess.run(). However,
# some installed versions of Python are < 3.5, so we offer them
# the option of doing it the old way (for now)
info = ''
try:
if 'run' in dir(subprocess):
info = subprocess.run(command, stdout=subprocess.PIPE,
universal_newlines=True)
raw_output = info.stdout.split('\0')
else:
info = subprocess.check_output(command, universal_newlines=True)
raw_output = info.split('\0')
except subprocess.CalledProcessError:
print("Error: Descriptions not available (can't access kernel module)")
sys.exit(1)
for line in raw_output:
if not line.startswith(target_prefix):
continue
line = line[len(target_prefix):].strip()
name, raw_desc = line.split(':', 1)
desc = raw_desc.rsplit('(', 1)[0]
if desc == '':
desc = '(No description found)'
descs[name.strip()] = desc.strip()
return descs
def get_version(request): def get_version(request):
"""Get the version number of ZFS or SPL on this machine for header. """Get the version number of ZFS or SPL on this machine for header.
Returns an error string, but does not raise an error, if we can't Returns an error string, but does not raise an error, if we can't
get the ZFS/SPL version via modinfo. get the ZFS/SPL version.
""" """
if request not in ('spl', 'zfs'): if request not in ('spl', 'zfs'):
error_msg = '(ERROR: "{0}" requested)'.format(request) error_msg = '(ERROR: "{0}" requested)'.format(request)
return error_msg return error_msg
# The original arc_summary called /sbin/modinfo/{spl,zfs} to get return get_version_impl(request)
# the version information. We switch to /sys/module/{spl,zfs}/version
# to make sure we get what is really loaded in the kernel
command = ["cat", "/sys/module/{0}/version".format(request)]
req = request.upper()
version = "(Can't get {0} version)".format(req)
# The recommended way to do this is with subprocess.run(). However,
# some installed versions of Python are < 3.5, so we offer them
# the option of doing it the old way (for now)
info = ''
if 'run' in dir(subprocess):
info = subprocess.run(command, stdout=subprocess.PIPE,
universal_newlines=True)
version = info.stdout.strip()
else:
info = subprocess.check_output(command, universal_newlines=True)
version = info.strip()
return version
def print_header(): def print_header():
"""Print the initial heading with date and time as well as info on the """Print the initial heading with date and time as well as info on the
Linux and ZFS versions. This is not called for the graph. kernel and ZFS versions. This is not called for the graph.
""" """
# datetime is now recommended over time but we keep the exact formatting # datetime is now recommended over time but we keep the exact formatting
@ -534,6 +612,20 @@ def section_arc(kstats_dict):
prt_i1('Deleted:', f_hits(arc_stats['deleted'])) prt_i1('Deleted:', f_hits(arc_stats['deleted']))
prt_i1('Mutex misses:', f_hits(arc_stats['mutex_miss'])) prt_i1('Mutex misses:', f_hits(arc_stats['mutex_miss']))
prt_i1('Eviction skips:', f_hits(arc_stats['evict_skip'])) prt_i1('Eviction skips:', f_hits(arc_stats['evict_skip']))
prt_i1('Eviction skips due to L2 writes:',
f_hits(arc_stats['evict_l2_skip']))
prt_i1('L2 cached evictions:', f_bytes(arc_stats['evict_l2_cached']))
prt_i1('L2 eligible evictions:', f_bytes(arc_stats['evict_l2_eligible']))
prt_i2('L2 eligible MFU evictions:',
f_perc(arc_stats['evict_l2_eligible_mfu'],
arc_stats['evict_l2_eligible']),
f_bytes(arc_stats['evict_l2_eligible_mfu']))
prt_i2('L2 eligible MRU evictions:',
f_perc(arc_stats['evict_l2_eligible_mru'],
arc_stats['evict_l2_eligible']),
f_bytes(arc_stats['evict_l2_eligible_mru']))
prt_i1('L2 ineligible evictions:',
f_bytes(arc_stats['evict_l2_ineligible']))
print() print()
@ -672,15 +764,30 @@ def section_l2arc(kstats_dict):
prt_i2('Header size:', prt_i2('Header size:',
f_perc(arc_stats['l2_hdr_size'], arc_stats['l2_size']), f_perc(arc_stats['l2_hdr_size'], arc_stats['l2_size']),
f_bytes(arc_stats['l2_hdr_size'])) f_bytes(arc_stats['l2_hdr_size']))
prt_i2('MFU allocated size:',
f_perc(arc_stats['l2_mfu_asize'], arc_stats['l2_asize']),
f_bytes(arc_stats['l2_mfu_asize']))
prt_i2('MRU allocated size:',
f_perc(arc_stats['l2_mru_asize'], arc_stats['l2_asize']),
f_bytes(arc_stats['l2_mru_asize']))
prt_i2('Prefetch allocated size:',
f_perc(arc_stats['l2_prefetch_asize'], arc_stats['l2_asize']),
f_bytes(arc_stats['l2_prefetch_asize']))
prt_i2('Data (buffer content) allocated size:',
f_perc(arc_stats['l2_bufc_data_asize'], arc_stats['l2_asize']),
f_bytes(arc_stats['l2_bufc_data_asize']))
prt_i2('Metadata (buffer content) allocated size:',
f_perc(arc_stats['l2_bufc_metadata_asize'], arc_stats['l2_asize']),
f_bytes(arc_stats['l2_bufc_metadata_asize']))
print() print()
prt_1('L2ARC breakdown:', f_hits(l2_access_total)) prt_1('L2ARC breakdown:', f_hits(l2_access_total))
prt_i2('Hit ratio:', prt_i2('Hit ratio:',
f_perc(arc_stats['l2_hits'], l2_access_total), f_perc(arc_stats['l2_hits'], l2_access_total),
f_bytes(arc_stats['l2_hits'])) f_hits(arc_stats['l2_hits']))
prt_i2('Miss ratio:', prt_i2('Miss ratio:',
f_perc(arc_stats['l2_misses'], l2_access_total), f_perc(arc_stats['l2_misses'], l2_access_total),
f_bytes(arc_stats['l2_misses'])) f_hits(arc_stats['l2_misses']))
prt_i1('Feeds:', f_hits(arc_stats['l2_feeds'])) prt_i1('Feeds:', f_hits(arc_stats['l2_feeds']))
print() print()
@ -691,13 +798,13 @@ def section_l2arc(kstats_dict):
prt_i2('Done ratio:', prt_i2('Done ratio:',
f_perc(arc_stats['l2_writes_done'], f_perc(arc_stats['l2_writes_done'],
arc_stats['l2_writes_sent']), arc_stats['l2_writes_sent']),
f_bytes(arc_stats['l2_writes_done'])) f_hits(arc_stats['l2_writes_done']))
prt_i2('Error ratio:', prt_i2('Error ratio:',
f_perc(arc_stats['l2_writes_error'], f_perc(arc_stats['l2_writes_error'],
arc_stats['l2_writes_sent']), arc_stats['l2_writes_sent']),
f_bytes(arc_stats['l2_writes_error'])) f_hits(arc_stats['l2_writes_error']))
else: else:
prt_i2('Writes sent:', '100 %', f_bytes(arc_stats['l2_writes_sent'])) prt_i2('Writes sent:', '100 %', f_hits(arc_stats['l2_writes_sent']))
print() print()
print('L2ARC evicts:') print('L2ARC evicts:')
@ -708,10 +815,14 @@ def section_l2arc(kstats_dict):
def section_spl(*_): def section_spl(*_):
"""Print the SPL parameters, if requested with alternative format """Print the SPL parameters, if requested with alternative format
and/or decriptions. This does not use kstats. and/or descriptions. This does not use kstats.
""" """
spls = get_spl_tunables(SPL_PATH) if sys.platform.startswith('freebsd'):
# No SPL support in FreeBSD
return
spls = get_spl_params()
keylist = sorted(spls.keys()) keylist = sorted(spls.keys())
print('Solaris Porting Layer (SPL):') print('Solaris Porting Layer (SPL):')
@ -725,7 +836,7 @@ def section_spl(*_):
try: try:
print(INDENT+'#', descriptions[key]) print(INDENT+'#', descriptions[key])
except KeyError: except KeyError:
print(INDENT+'# (No decription found)') # paranoid print(INDENT+'# (No description found)') # paranoid
print(format_raw_line(key, value)) print(format_raw_line(key, value))
@ -734,10 +845,10 @@ def section_spl(*_):
def section_tunables(*_): def section_tunables(*_):
"""Print the tunables, if requested with alternative format and/or """Print the tunables, if requested with alternative format and/or
decriptions. This does not use kstasts. descriptions. This does not use kstasts.
""" """
tunables = get_spl_tunables(TUNABLES_PATH) tunables = get_tunable_params()
keylist = sorted(tunables.keys()) keylist = sorted(tunables.keys())
print('Tunables:') print('Tunables:')
@ -751,7 +862,7 @@ def section_tunables(*_):
try: try:
print(INDENT+'#', descriptions[key]) print(INDENT+'#', descriptions[key])
except KeyError: except KeyError:
print(INDENT+'# (No decription found)') # paranoid print(INDENT+'# (No description found)') # paranoid
print(format_raw_line(key, value)) print(format_raw_line(key, value))
@ -763,11 +874,11 @@ def section_vdev(kstats_dict):
# Currently [Nov 2017] the VDEV cache is disabled, because it is actually # Currently [Nov 2017] the VDEV cache is disabled, because it is actually
# harmful. When this is the case, we just skip the whole entry. See # harmful. When this is the case, we just skip the whole entry. See
# https://github.com/zfsonlinux/zfs/blob/master/module/zfs/vdev_cache.c # https://github.com/openzfs/zfs/blob/master/module/zfs/vdev_cache.c
# for details # for details
tunables = get_spl_tunables(TUNABLES_PATH) tunables = get_vdev_params()
if tunables['zfs_vdev_cache_size'] == '0': if tunables[VDEV_CACHE_SIZE] == '0':
print('VDEV cache disabled, skipping section\n') print('VDEV cache disabled, skipping section\n')
return return
@ -789,7 +900,7 @@ def section_vdev(kstats_dict):
def section_zil(kstats_dict): def section_zil(kstats_dict):
"""Collect information on the ZFS Intent Log. Some of the information """Collect information on the ZFS Intent Log. Some of the information
taken from https://github.com/zfsonlinux/zfs/blob/master/include/sys/zil.h taken from https://github.com/openzfs/zfs/blob/master/include/sys/zil.h
""" """
zil_stats = isolate_section('zil', kstats_dict) zil_stats = isolate_section('zil', kstats_dict)

1
cmd/arcstat/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
arcstat

View File

@ -1,13 +1,5 @@
dist_bin_SCRIPTS = arcstat include $(top_srcdir)/config/Substfiles.am
# bin_SCRIPTS = arcstat
# The arcstat script is compatibile with both Python 2.6 and 3.4.
# As such the python 3 shebang can be replaced at install time when SUBSTFILES += $(bin_SCRIPTS)
# targeting a python 2 system. This allows us to maintain a single
# version of the source.
#
if USING_PYTHON_2
install-exec-hook:
sed --in-place 's|^#!/usr/bin/python3|#!/usr/bin/python2|' \
$(DESTDIR)$(bindir)/arcstat
endif

View File

@ -1,20 +1,25 @@
#!/usr/bin/python3 #!/usr/bin/env @PYTHON_SHEBANG@
# #
# Print out ZFS ARC Statistics exported via kstat(1) # Print out ZFS ARC Statistics exported via kstat(1)
# For a definition of fields, or usage, use arctstat.pl -v # For a definition of fields, or usage, use arcstat -v
# #
# This script is a fork of the original arcstat.pl (0.1) by # This script was originally a fork of the original arcstat.pl (0.1)
# Neelakanth Nadgir, originally published on his Sun blog on # by Neelakanth Nadgir, originally published on his Sun blog on
# 09/18/2007 # 09/18/2007
# http://blogs.sun.com/realneel/entry/zfs_arc_statistics # http://blogs.sun.com/realneel/entry/zfs_arc_statistics
# #
# This version aims to improve upon the original by adding features # A new version aimed to improve upon the original by adding features
# and fixing bugs as needed. This version is maintained by # and fixing bugs as needed. This version was maintained by Mike
# Mike Harsch and is hosted in a public open source repository: # Harsch and was hosted in a public open source repository:
# http://github.com/mharsch/arcstat # http://github.com/mharsch/arcstat
# #
# Comments, Questions, or Suggestions are always welcome. # but has since moved to the illumos-gate repository.
# Contact the maintainer at ( mike at harschsystems dot com ) #
# 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 # CDDL HEADER START
# #
@ -51,16 +56,16 @@ import getopt
import re import re
import copy import copy
from decimal import Decimal
from signal import signal, SIGINT, SIGWINCH, SIG_DFL from signal import signal, SIGINT, SIGWINCH, SIG_DFL
cols = { cols = {
# HDR: [Size, Scale, Description] # HDR: [Size, Scale, Description]
"time": [8, -1, "Time"], "time": [8, -1, "Time"],
"hits": [4, 1000, "ARC reads per second"], "hits": [4, 1000, "ARC reads per second"],
"miss": [4, 1000, "ARC misses per second"], "miss": [4, 1000, "ARC misses per second"],
"read": [4, 1000, "Total ARC accesses per second"], "read": [4, 1000, "Total ARC accesses per second"],
"hit%": [4, 100, "ARC Hit percentage"], "hit%": [4, 100, "ARC hit percentage"],
"miss%": [5, 100, "ARC miss percentage"], "miss%": [5, 100, "ARC miss percentage"],
"dhit": [4, 1000, "Demand hits per second"], "dhit": [4, 1000, "Demand hits per second"],
"dmis": [4, 1000, "Demand misses per second"], "dmis": [4, 1000, "Demand misses per second"],
@ -75,13 +80,20 @@ cols = {
"mread": [5, 1000, "Metadata accesses per second"], "mread": [5, 1000, "Metadata accesses per second"],
"mh%": [3, 100, "Metadata hit percentage"], "mh%": [3, 100, "Metadata hit percentage"],
"mm%": [3, 100, "Metadata miss percentage"], "mm%": [3, 100, "Metadata miss percentage"],
"arcsz": [5, 1024, "ARC Size"], "arcsz": [5, 1024, "ARC size"],
"c": [4, 1024, "ARC Target Size"], "size": [4, 1024, "ARC size"],
"mfu": [4, 1000, "MFU List hits per second"], "c": [4, 1024, "ARC target size"],
"mru": [4, 1000, "MRU List hits per second"], "mfu": [4, 1000, "MFU list hits per second"],
"mfug": [4, 1000, "MFU Ghost List hits per second"], "mru": [4, 1000, "MRU list hits per second"],
"mrug": [4, 1000, "MRU Ghost 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"], "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"], "mtxmis": [6, 1000, "mutex_miss per second"],
"dread": [5, 1000, "Demand accesses per second"], "dread": [5, 1000, "Demand accesses per second"],
"pread": [5, 1000, "Prefetch accesses per second"], "pread": [5, 1000, "Prefetch accesses per second"],
@ -90,17 +102,29 @@ cols = {
"l2read": [6, 1000, "Total L2ARC accesses per second"], "l2read": [6, 1000, "Total L2ARC accesses per second"],
"l2hit%": [6, 100, "L2ARC access hit percentage"], "l2hit%": [6, 100, "L2ARC access hit percentage"],
"l2miss%": [7, 100, "L2ARC access miss 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"], "l2asize": [7, 1024, "Actual (compressed) size of the L2ARC"],
"l2size": [6, 1024, "Size of the L2ARC"], "l2size": [6, 1024, "Size of the L2ARC"],
"l2bytes": [7, 1024, "bytes read per second from the L2ARC"], "l2bytes": [7, 1024, "Bytes read per second from the L2ARC"],
"grow": [4, 1000, "ARC Grow disabled"], "grow": [4, 1000, "ARC grow disabled"],
"need": [4, 1024, "ARC Reclaim need"], "need": [4, 1024, "ARC reclaim need"],
"free": [4, 1024, "ARC Free memory"], "free": [4, 1024, "ARC free memory"],
"avail": [5, 1024, "ARC available memory"],
"waste": [5, 1024, "Wasted memory due to round up to pagesize"],
} }
v = {} v = {}
hdr = ["time", "read", "miss", "miss%", "dmis", "dm%", "pmis", "pm%", "mmis", hdr = ["time", "read", "miss", "miss%", "dmis", "dm%", "pmis", "pm%", "mmis",
"mm%", "arcsz", "c"] "mm%", "size", "c", "avail"]
xhdr = ["time", "mfu", "mru", "mfug", "mrug", "eskip", "mtxmis", "dread", xhdr = ["time", "mfu", "mru", "mfug", "mrug", "eskip", "mtxmis", "dread",
"pread", "read"] "pread", "read"]
sint = 1 # Default interval is 1 second sint = 1 # Default interval is 1 second
@ -110,12 +134,56 @@ opfile = None
sep = " " # Default separator is 2 spaces sep = " " # Default separator is 2 spaces
version = "0.4" version = "0.4"
l2exist = False l2exist = False
cmd = ("Usage: arcstat [-hvx] [-f fields] [-o file] [-s string] [interval " cmd = ("Usage: arcstat [-havxp] [-f fields] [-o file] [-s string] [interval "
"[count]]\n") "[count]]\n")
cur = {} cur = {}
d = {} d = {}
out = None out = None
kstat = None kstat = None
pretty_print = True
if sys.platform.startswith('freebsd'):
# Requires py-sysctl on FreeBSD
import sysctl
def kstat_update():
global kstat
k = [ctl for ctl in sysctl.filter('kstat.zfs.misc.arcstats')
if ctl.type != sysctl.CTLTYPE_NODE]
if not k:
sys.exit(1)
kstat = {}
for s in k:
if not s:
continue
name, value = s.name, s.value
# Trims 'kstat.zfs.misc.arcstats' from the name
kstat[name[24:]] = int(value)
elif sys.platform.startswith('linux'):
def kstat_update():
global kstat
k = [line.strip() for line in open('/proc/spl/kstat/zfs/arcstats')]
if not k:
sys.exit(1)
del k[0:2]
kstat = {}
for s in k:
if not s:
continue
name, unused, value = s.split()
kstat[name] = int(value)
def detailed_usage(): def detailed_usage():
@ -131,6 +199,7 @@ def detailed_usage():
def usage(): def usage():
sys.stderr.write("%s\n" % cmd) sys.stderr.write("%s\n" % cmd)
sys.stderr.write("\t -h : Print this help message\n") 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" sys.stderr.write("\t -v : List all possible field headers and definitions"
"\n") "\n")
sys.stderr.write("\t -x : Print extended stats\n") sys.stderr.write("\t -x : Print extended stats\n")
@ -138,6 +207,7 @@ def usage():
sys.stderr.write("\t -o : Redirect output to the specified file\n") sys.stderr.write("\t -o : Redirect output to the specified file\n")
sys.stderr.write("\t -s : Override default field separator with custom " sys.stderr.write("\t -s : Override default field separator with custom "
"character or string\n") "character or string\n")
sys.stderr.write("\t -p : Disable auto-scaling of numerical fields\n")
sys.stderr.write("\nExamples:\n") sys.stderr.write("\nExamples:\n")
sys.stderr.write("\tarcstat -o /tmp/a.log 2 10\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 -s \",\" -o /tmp/a.log 2 10\n")
@ -148,25 +218,6 @@ def usage():
sys.exit(1) 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(): def snap_stats():
global cur global cur
global kstat global kstat
@ -197,7 +248,7 @@ def prettynum(sz, scale, num=0):
elif 0 < num < 1: elif 0 < num < 1:
num = 0 num = 0
while num > scale and index < 5: while abs(num) > scale and index < 5:
save = num save = num
num = num / scale num = num / scale
index += 1 index += 1
@ -205,7 +256,7 @@ def prettynum(sz, scale, num=0):
if index == 0: if index == 0:
return "%*d" % (sz, num) return "%*d" % (sz, num)
if (save / scale) < 10: if abs(save / scale) < 10:
return "%*.1f%s" % (sz - 1, num, suffix[index]) return "%*.1f%s" % (sz - 1, num, suffix[index])
else: else:
return "%*d%s" % (sz - 1, num, suffix[index]) return "%*d%s" % (sz - 1, num, suffix[index])
@ -215,12 +266,14 @@ def print_values():
global hdr global hdr
global sep global sep
global v global v
global pretty_print
for col in hdr: if pretty_print:
sys.stdout.write("%s%s" % ( fmt = lambda col: prettynum(cols[col][0], cols[col][1], v[col])
prettynum(cols[col][0], cols[col][1], v[col]), else:
sep fmt = lambda col: v[col]
))
sys.stdout.write(sep.join(fmt(col) for col in hdr))
sys.stdout.write("\n") sys.stdout.write("\n")
sys.stdout.flush() sys.stdout.flush()
@ -228,9 +281,14 @@ def print_values():
def print_header(): def print_header():
global hdr global hdr
global sep global sep
global pretty_print
for col in hdr: if pretty_print:
sys.stdout.write("%*s%s" % (cols[col][0], col, sep)) 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") sys.stdout.write("\n")
@ -267,8 +325,10 @@ def init():
global sep global sep
global out global out
global l2exist global l2exist
global pretty_print
desired_cols = None desired_cols = None
aflag = False
xflag = False xflag = False
hflag = False hflag = False
vflag = False vflag = False
@ -277,14 +337,16 @@ def init():
try: try:
opts, args = getopt.getopt( opts, args = getopt.getopt(
sys.argv[1:], sys.argv[1:],
"xo:hvs:f:", "axo:hvs:f:p",
[ [
"all",
"extended", "extended",
"outfile", "outfile",
"help", "help",
"verbose", "verbose",
"separator", "separator",
"columns" "columns",
"parsable"
] ]
) )
except getopt.error as msg: except getopt.error as msg:
@ -293,6 +355,8 @@ def init():
opts = None opts = None
for opt, arg in opts: for opt, arg in opts:
if opt in ('-a', '--all'):
aflag = True
if opt in ('-x', '--extended'): if opt in ('-x', '--extended'):
xflag = True xflag = True
if opt in ('-o', '--outfile'): if opt in ('-o', '--outfile'):
@ -308,19 +372,13 @@ def init():
if opt in ('-f', '--columns'): if opt in ('-f', '--columns'):
desired_cols = arg desired_cols = arg
i += 1 i += 1
if opt in ('-p', '--parsable'):
pretty_print = False
i += 1 i += 1
argv = sys.argv[i:] argv = sys.argv[i:]
sint = Decimal(argv[0]) if argv else sint sint = int(argv[0]) if argv else sint
count = int(argv[1]) if len(argv) > 1 else count count = int(argv[1]) if len(argv) > 1 else (0 if len(argv) > 0 else 1)
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): if hflag or (xflag and desired_cols):
usage() usage()
@ -360,6 +418,12 @@ def init():
incompat) incompat)
usage() usage()
if aflag:
if l2exist:
hdr = cols.keys()
else:
hdr = [col for col in cols.keys() if not col.startswith("l2")]
if opfile: if opfile:
try: try:
out = open(opfile, "w") out = open(opfile, "w")
@ -377,59 +441,79 @@ def calculate():
v = dict() v = dict()
v["time"] = time.strftime("%H:%M:%S", time.localtime()) v["time"] = time.strftime("%H:%M:%S", time.localtime())
v["hits"] = d["hits"] / sint v["hits"] = d["hits"] // sint
v["miss"] = d["misses"] / sint v["miss"] = d["misses"] // sint
v["read"] = v["hits"] + v["miss"] v["read"] = v["hits"] + v["miss"]
v["hit%"] = 100 * v["hits"] / v["read"] if v["read"] > 0 else 0 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["miss%"] = 100 - v["hit%"] if v["read"] > 0 else 0
v["dhit"] = (d["demand_data_hits"] + d["demand_metadata_hits"]) / sint v["dhit"] = (d["demand_data_hits"] + d["demand_metadata_hits"]) // sint
v["dmis"] = (d["demand_data_misses"] + d["demand_metadata_misses"]) / sint v["dmis"] = (d["demand_data_misses"] + d["demand_metadata_misses"]) // sint
v["dread"] = v["dhit"] + v["dmis"] v["dread"] = v["dhit"] + v["dmis"]
v["dh%"] = 100 * v["dhit"] / v["dread"] if v["dread"] > 0 else 0 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["dm%"] = 100 - v["dh%"] if v["dread"] > 0 else 0
v["phit"] = (d["prefetch_data_hits"] + d["prefetch_metadata_hits"]) / sint v["phit"] = (d["prefetch_data_hits"] + d["prefetch_metadata_hits"]) // sint
v["pmis"] = (d["prefetch_data_misses"] + v["pmis"] = (d["prefetch_data_misses"] +
d["prefetch_metadata_misses"]) / sint d["prefetch_metadata_misses"]) // sint
v["pread"] = v["phit"] + v["pmis"] v["pread"] = v["phit"] + v["pmis"]
v["ph%"] = 100 * v["phit"] / v["pread"] if v["pread"] > 0 else 0 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["pm%"] = 100 - v["ph%"] if v["pread"] > 0 else 0
v["mhit"] = (d["prefetch_metadata_hits"] + v["mhit"] = (d["prefetch_metadata_hits"] +
d["demand_metadata_hits"]) / sint d["demand_metadata_hits"]) // sint
v["mmis"] = (d["prefetch_metadata_misses"] + v["mmis"] = (d["prefetch_metadata_misses"] +
d["demand_metadata_misses"]) / sint d["demand_metadata_misses"]) // sint
v["mread"] = v["mhit"] + v["mmis"] v["mread"] = v["mhit"] + v["mmis"]
v["mh%"] = 100 * v["mhit"] / v["mread"] if v["mread"] > 0 else 0 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["mm%"] = 100 - v["mh%"] if v["mread"] > 0 else 0
v["arcsz"] = cur["size"] v["arcsz"] = cur["size"]
v["size"] = cur["size"]
v["c"] = cur["c"] v["c"] = cur["c"]
v["mfu"] = d["mfu_hits"] / sint v["mfu"] = d["mfu_hits"] // sint
v["mru"] = d["mru_hits"] / sint v["mru"] = d["mru_hits"] // sint
v["mrug"] = d["mru_ghost_hits"] / sint v["mrug"] = d["mru_ghost_hits"] // sint
v["mfug"] = d["mfu_ghost_hits"] / sint v["mfug"] = d["mfu_ghost_hits"] // sint
v["eskip"] = d["evict_skip"] / sint v["eskip"] = d["evict_skip"] // sint
v["mtxmis"] = d["mutex_miss"] / sint v["el2skip"] = d["evict_l2_skip"] // sint
v["el2cach"] = d["evict_l2_cached"] // sint
v["el2el"] = d["evict_l2_eligible"] // sint
v["el2mfu"] = d["evict_l2_eligible_mfu"] // sint
v["el2mru"] = d["evict_l2_eligible_mru"] // sint
v["el2inel"] = d["evict_l2_ineligible"] // sint
v["mtxmis"] = d["mutex_miss"] // sint
if l2exist: if l2exist:
v["l2hits"] = d["l2_hits"] / sint v["l2hits"] = d["l2_hits"] // sint
v["l2miss"] = d["l2_misses"] / sint v["l2miss"] = d["l2_misses"] // sint
v["l2read"] = v["l2hits"] + v["l2miss"] v["l2read"] = v["l2hits"] + v["l2miss"]
v["l2hit%"] = 100 * v["l2hits"] / v["l2read"] if v["l2read"] > 0 else 0 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["l2miss%"] = 100 - v["l2hit%"] if v["l2read"] > 0 else 0
v["l2asize"] = cur["l2_asize"] v["l2asize"] = cur["l2_asize"]
v["l2size"] = cur["l2_size"] v["l2size"] = cur["l2_size"]
v["l2bytes"] = d["l2_read_bytes"] / sint 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["grow"] = 0 if cur["arc_no_grow"] else 1
v["need"] = cur["arc_need_free"] v["need"] = cur["arc_need_free"]
v["free"] = cur["arc_sys_free"] v["free"] = cur["memory_free_bytes"]
v["avail"] = cur["memory_available_bytes"]
v["waste"] = cur["abd_chunk_waste_size"]
def main(): def main():

1
cmd/dbufstat/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
dbufstat

View File

@ -1,13 +1,5 @@
dist_bin_SCRIPTS = dbufstat include $(top_srcdir)/config/Substfiles.am
# bin_SCRIPTS = dbufstat
# The dbufstat script is compatibile with both Python 2.6 and 3.4.
# As such the python 3 shebang can be replaced at install time when SUBSTFILES += $(bin_SCRIPTS)
# targeting a python 2 system. This allows us to maintain a single
# version of the source.
#
if USING_PYTHON_2
install-exec-hook:
sed --in-place 's|^#!/usr/bin/python3|#!/usr/bin/python2|' \
$(DESTDIR)$(bindir)/dbufstat
endif

View File

@ -1,4 +1,4 @@
#!/usr/bin/python3 #!/usr/bin/env @PYTHON_SHEBANG@
# #
# Print out statistics for all cached dmu buffers. This information # Print out statistics for all cached dmu buffers. This information
# is available through the dbufs kstat and may be post-processed as # is available through the dbufs kstat and may be post-processed as
@ -113,10 +113,25 @@ cmd = ("Usage: dbufstat [-bdhnrtvx] [-i file] [-f fields] [-o file] "
raw = 0 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): def print_incompat_helper(incompat):
cnt = 0 cnt = 0
for key in sorted(incompat): for key in sorted(incompat):
if cnt is 0: if cnt == 0:
sys.stderr.write("\t") sys.stderr.write("\t")
elif cnt > 8: elif cnt > 8:
sys.stderr.write(",\n\t") sys.stderr.write(",\n\t")
@ -343,7 +358,7 @@ def get_compstring(c):
"ZIO_COMPRESS_GZIP_6", "ZIO_COMPRESS_GZIP_7", "ZIO_COMPRESS_GZIP_6", "ZIO_COMPRESS_GZIP_7",
"ZIO_COMPRESS_GZIP_8", "ZIO_COMPRESS_GZIP_9", "ZIO_COMPRESS_GZIP_8", "ZIO_COMPRESS_GZIP_9",
"ZIO_COMPRESS_ZLE", "ZIO_COMPRESS_LZ4", "ZIO_COMPRESS_ZLE", "ZIO_COMPRESS_LZ4",
"ZIO_COMPRESS_FUNCTION"] "ZIO_COMPRESS_ZSTD", "ZIO_COMPRESS_FUNCTION"]
# If "-rr" option is used, don't convert to string representation # If "-rr" option is used, don't convert to string representation
if raw > 1: if raw > 1:
@ -645,9 +660,9 @@ def main():
sys.exit(1) sys.exit(1)
if not ifile: if not ifile:
ifile = '/proc/spl/kstat/zfs/dbufs' ifile = default_ifile()
if ifile is not "-": if ifile != "-":
try: try:
tmp = open(ifile, "r") tmp = open(ifile, "r")
sys.stdin = tmp sys.stdin = tmp

1
cmd/fsck_zfs/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/fsck.zfs

View File

@ -1 +1,6 @@
include $(top_srcdir)/config/Substfiles.am
include $(top_srcdir)/config/Shellcheck.am
dist_sbin_SCRIPTS = fsck.zfs dist_sbin_SCRIPTS = fsck.zfs
SUBSTFILES += $(dist_sbin_SCRIPTS)

View File

@ -1,9 +0,0 @@
#!/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

44
cmd/fsck_zfs/fsck.zfs.in Executable file
View File

@ -0,0 +1,44 @@
#!/bin/sh
#
# fsck.zfs: A fsck helper to accommodate distributions that expect
# to be able to execute a fsck on all filesystem types.
#
# This script simply bubbles up some already-known-about errors,
# see fsck.zfs(8)
#
if [ "$#" = "0" ]; then
echo "Usage: $0 [options] dataset…" >&2
exit 16
fi
ret=0
for dataset in "$@"; do
case "$dataset" in
-*)
continue
;;
*)
;;
esac
pool="${dataset%%/*}"
case "$(@sbindir@/zpool list -Ho health "$pool")" in
DEGRADED)
ret=$(( ret | 4 ))
;;
FAULTED)
awk '!/^([[:space:]]*#.*)?$/ && $1 == "'"$dataset"'" && $3 == "zfs" {exit 1}' /etc/fstab || \
ret=$(( ret | 8 ))
;;
"")
# Pool not found, error printed by zpool(8)
ret=$(( ret | 8 ))
;;
*)
;;
esac
done
exit "$ret"

View File

@ -1,9 +1,5 @@
include $(top_srcdir)/config/Rules.am 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/ # 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. # because this path is hardcoded in the mount(8) for security reasons.
@ -17,5 +13,10 @@ mount_zfs_SOURCES = \
mount_zfs.c mount_zfs.c
mount_zfs_LDADD = \ mount_zfs_LDADD = \
$(top_builddir)/lib/libnvpair/libnvpair.la \ $(abs_top_builddir)/lib/libzfs/libzfs.la \
$(top_builddir)/lib/libzfs/libzfs.la $(abs_top_builddir)/lib/libzfs_core/libzfs_core.la \
$(abs_top_builddir)/lib/libnvpair/libnvpair.la
mount_zfs_LDADD += $(LTLIBINTL)
include $(top_srcdir)/config/CppCheck.am

View File

@ -42,247 +42,46 @@
libzfs_handle_t *g_zfs; 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 * Opportunistically convert a target string into a pool name. If the
* validated against the option map and mount flags set accordingly. * string does not represent a block device with a valid zfs label
* then it is passed through without modification.
*/ */
static int static void
parse_option(char *mntopt, unsigned long *mntflags, parse_dataset(const char *target, char **dataset)
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. * Prior to util-linux 2.36.2, if a file or directory in the
* We must be careful to notice regions which are double quoted * current working directory was named 'dataset' then mount(8)
* and skip commas in these regions. Each option is then checked * would prepend the current working directory to the dataset.
* to determine if it is a known option. * Check for it and strip the prepended path when it is added.
*/ */
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]; char cwd[PATH_MAX];
struct stat64 statbuf; if (getcwd(cwd, PATH_MAX) == NULL) {
int error; perror("getcwd");
int len; return;
/*
* 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: int len = strlen(cwd);
/* if (strncmp(cwd, target, len) == 0)
* If a file or directory in your current working directory is target += len;
* 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); /* Assume pool/dataset is more likely */
strlcpy(*dataset, target, PATH_MAX);
/* Do not add one when cwd already ends in a trailing '/' */ int fd = open(target, O_RDONLY | O_CLOEXEC);
if (strncmp(cwd, dataset, len) == 0) if (fd < 0)
return (dataset + len + (cwd[len-1] != '/')); return;
return (dataset); nvlist_t *cfg = NULL;
if (zpool_read_label(fd, &cfg, NULL) == 0) {
char *nm = NULL;
if (!nvlist_lookup_string(cfg, ZPOOL_CONFIG_POOL_NAME, &nm))
strlcpy(*dataset, nm, PATH_MAX);
nvlist_free(cfg);
}
if (close(fd))
perror("close");
} }
/* /*
@ -326,8 +125,8 @@ mtab_update(char *dataset, char *mntpoint, char *type, char *mntopts)
if (!fp) { if (!fp) {
(void) fprintf(stderr, gettext( (void) fprintf(stderr, gettext(
"filesystem '%s' was mounted, but /etc/mtab " "filesystem '%s' was mounted, but /etc/mtab "
"could not be opened due to error %d\n"), "could not be opened due to error: %s\n"),
dataset, errno); dataset, strerror(errno));
return (MOUNT_FILEIO); return (MOUNT_FILEIO);
} }
@ -335,8 +134,8 @@ mtab_update(char *dataset, char *mntpoint, char *type, char *mntopts)
if (error) { if (error) {
(void) fprintf(stderr, gettext( (void) fprintf(stderr, gettext(
"filesystem '%s' was mounted, but /etc/mtab " "filesystem '%s' was mounted, but /etc/mtab "
"could not be updated due to error %d\n"), "could not be updated due to error: %s\n"),
dataset, errno); dataset, strerror(errno));
return (MOUNT_FILEIO); return (MOUNT_FILEIO);
} }
@ -345,34 +144,6 @@ mtab_update(char *dataset, char *mntpoint, char *type, char *mntopts)
return (MOUNT_SUCCESS); 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 int
main(int argc, char **argv) main(int argc, char **argv)
{ {
@ -383,12 +154,13 @@ main(int argc, char **argv)
char badopt[MNT_LINE_MAX] = { '\0' }; char badopt[MNT_LINE_MAX] = { '\0' };
char mtabopt[MNT_LINE_MAX] = { '\0' }; char mtabopt[MNT_LINE_MAX] = { '\0' };
char mntpoint[PATH_MAX]; char mntpoint[PATH_MAX];
char *dataset; char dataset[PATH_MAX], *pdataset = dataset;
unsigned long mntflags = 0, zfsflags = 0, remount = 0; unsigned long mntflags = 0, zfsflags = 0, remount = 0;
int sloppy = 0, fake = 0, verbose = 0, nomtab = 0, zfsutil = 0; int sloppy = 0, fake = 0, verbose = 0, nomtab = 0, zfsutil = 0;
int error, c; int error, c;
(void) setlocale(LC_ALL, ""); (void) setlocale(LC_ALL, "");
(void) setlocale(LC_NUMERIC, "C");
(void) textdomain(TEXT_DOMAIN); (void) textdomain(TEXT_DOMAIN);
opterr = 0; opterr = 0;
@ -413,10 +185,11 @@ main(int argc, char **argv)
break; break;
case 'h': case 'h':
case '?': case '?':
(void) fprintf(stderr, gettext("Invalid option '%c'\n"), if (optopt)
optopt); (void) fprintf(stderr,
gettext("Invalid option '%c'\n"), optopt);
(void) fprintf(stderr, gettext("Usage: mount.zfs " (void) fprintf(stderr, gettext("Usage: mount.zfs "
"[-sfnv] [-o options] <dataset> <mountpoint>\n")); "[-sfnvh] [-o options] <dataset> <mountpoint>\n"));
return (MOUNT_USAGE); return (MOUNT_USAGE);
} }
} }
@ -438,18 +211,18 @@ main(int argc, char **argv)
return (MOUNT_USAGE); return (MOUNT_USAGE);
} }
dataset = parse_dataset(argv[0]); parse_dataset(argv[0], &pdataset);
/* canonicalize the mount point */ /* canonicalize the mount point */
if (realpath(argv[1], mntpoint) == NULL) { if (realpath(argv[1], mntpoint) == NULL) {
(void) fprintf(stderr, gettext("filesystem '%s' cannot be " (void) fprintf(stderr, gettext("filesystem '%s' cannot be "
"mounted at '%s' due to canonicalization error %d.\n"), "mounted at '%s' due to canonicalization error: %s\n"),
dataset, argv[1], errno); dataset, argv[1], strerror(errno));
return (MOUNT_SYSERR); return (MOUNT_SYSERR);
} }
/* validate mount options and set mntflags */ /* validate mount options and set mntflags */
error = parse_options(mntopts, &mntflags, &zfsflags, sloppy, error = zfs_parse_mount_options(mntopts, &mntflags, &zfsflags, sloppy,
badopt, mtabopt); badopt, mtabopt);
if (error) { if (error) {
switch (error) { switch (error) {
@ -489,7 +262,7 @@ main(int argc, char **argv)
zfsutil = 1; zfsutil = 1;
if ((g_zfs = libzfs_init()) == NULL) { if ((g_zfs = libzfs_init()) == NULL) {
(void) fprintf(stderr, "%s", libzfs_error_init(errno)); (void) fprintf(stderr, "%s\n", libzfs_error_init(errno));
return (MOUNT_SYSERR); return (MOUNT_SYSERR);
} }
@ -502,32 +275,7 @@ main(int argc, char **argv)
return (MOUNT_USAGE); return (MOUNT_USAGE);
} }
/* zfs_adjust_mount_options(zhp, mntpoint, mntopts, mtabopt);
* Checks to see if the ZFS_PROP_SELINUX_CONTEXT exists
* if it does, create a tmp variable in case it's needed
* checks to see if the selinux context is set to the default
* if it is, allow the setting of the other context properties
* this is needed because the 'context' property overrides others
* if it is not the default, set the 'context' property
*/
if (zfs_prop_get(zhp, ZFS_PROP_SELINUX_CONTEXT, prop, sizeof (prop),
NULL, NULL, 0, B_FALSE) == 0) {
if (strcmp(prop, "none") == 0) {
zfs_selinux_setcontext(zhp, ZFS_PROP_SELINUX_FSCONTEXT,
MNTOPT_FSCONTEXT, mntopts, mtabopt);
zfs_selinux_setcontext(zhp, ZFS_PROP_SELINUX_DEFCONTEXT,
MNTOPT_DEFCONTEXT, mntopts, mtabopt);
zfs_selinux_setcontext(zhp,
ZFS_PROP_SELINUX_ROOTCONTEXT, MNTOPT_ROOTCONTEXT,
mntopts, mtabopt);
} else {
append_mntopt(MNTOPT_CONTEXT, prop,
mntopts, mtabopt, B_TRUE);
}
}
/* A hint used to determine an auto-mounted snapshot mount point */
append_mntopt(MNTOPT_MNTPOINT, mntpoint, mntopts, NULL, B_FALSE);
/* treat all snapshots as legacy mount points */ /* treat all snapshots as legacy mount points */
if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT) if (zfs_get_type(zhp) == ZFS_TYPE_SNAPSHOT)
@ -620,8 +368,8 @@ main(int argc, char **argv)
"mount the filesystem again.\n"), dataset); "mount the filesystem again.\n"), dataset);
return (MOUNT_SYSERR); return (MOUNT_SYSERR);
} }
/* fallthru */
#endif #endif
fallthrough;
default: default:
(void) fprintf(stderr, gettext("filesystem " (void) fprintf(stderr, gettext("filesystem "
"'%s' can not be mounted: %s\n"), dataset, "'%s' can not be mounted: %s\n"), dataset,

View File

@ -4,11 +4,7 @@ include $(top_srcdir)/config/Rules.am
AM_CFLAGS += $(FRAME_LARGER_THAN) AM_CFLAGS += $(FRAME_LARGER_THAN)
# Unconditionally enable ASSERTs # Unconditionally enable ASSERTs
AM_CPPFLAGS += -DDEBUG -UNDEBUG AM_CPPFLAGS += -DDEBUG -UNDEBUG -DZFS_DEBUG
DEFAULT_INCLUDES += \
-I$(top_srcdir)/include \
-I$(top_srcdir)/lib/libspl/include
bin_PROGRAMS = raidz_test bin_PROGRAMS = raidz_test
@ -18,6 +14,9 @@ raidz_test_SOURCES = \
raidz_bench.c raidz_bench.c
raidz_test_LDADD = \ raidz_test_LDADD = \
$(top_builddir)/lib/libzpool/libzpool.la $(abs_top_builddir)/lib/libzpool/libzpool.la \
$(abs_top_builddir)/lib/libzfs_core/libzfs_core.la
raidz_test_LDADD += -lm -ldl raidz_test_LDADD += -lm
include $(top_srcdir)/config/CppCheck.am

View File

@ -31,8 +31,6 @@
#include <sys/vdev_raidz_impl.h> #include <sys/vdev_raidz_impl.h>
#include <stdio.h> #include <stdio.h>
#include <sys/time.h>
#include "raidz_test.h" #include "raidz_test.h"
#define GEN_BENCH_MEMORY (((uint64_t)1ULL)<<32) #define GEN_BENCH_MEMORY (((uint64_t)1ULL)<<32)
@ -83,8 +81,17 @@ run_gen_bench_impl(const char *impl)
/* create suitable raidz_map */ /* create suitable raidz_map */
ncols = rto_opts.rto_dcols + fn + 1; ncols = rto_opts.rto_dcols + fn + 1;
zio_bench.io_size = 1ULL << ds; zio_bench.io_size = 1ULL << ds;
rm_bench = vdev_raidz_map_alloc(&zio_bench,
BENCH_ASHIFT, ncols, fn+1); 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);
}
/* estimate iteration count */ /* estimate iteration count */
iter_cnt = GEN_BENCH_MEMORY; iter_cnt = GEN_BENCH_MEMORY;
@ -113,7 +120,7 @@ run_gen_bench_impl(const char *impl)
} }
} }
void static void
run_gen_bench(void) run_gen_bench(void)
{ {
char **impl_name; char **impl_name;
@ -163,8 +170,16 @@ run_rec_bench_impl(const char *impl)
(1ULL << BENCH_ASHIFT)) (1ULL << BENCH_ASHIFT))
continue; continue;
rm_bench = vdev_raidz_map_alloc(&zio_bench, if (rto_opts.rto_expand) {
BENCH_ASHIFT, ncols, PARITY_PQR); 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);
}
/* estimate iteration count */ /* estimate iteration count */
iter_cnt = (REC_BENCH_MEMORY); iter_cnt = (REC_BENCH_MEMORY);
@ -197,7 +212,7 @@ run_rec_bench_impl(const char *impl)
} }
} }
void static void
run_rec_bench(void) run_rec_bench(void)
{ {
char **impl_name; char **impl_name;

View File

@ -37,11 +37,11 @@
static int *rand_data; static int *rand_data;
raidz_test_opts_t rto_opts; raidz_test_opts_t rto_opts;
static char gdb[256]; static char pid_s[16];
static const char gdb_tmpl[] = "gdb -ex \"set pagination 0\" -p %d";
static void sig_handler(int signo) static void sig_handler(int signo)
{ {
int old_errno = errno;
struct sigaction action; struct sigaction action;
/* /*
* Restore default action and re-raise signal so SIGSEGV and * Restore default action and re-raise signal so SIGSEGV and
@ -52,10 +52,19 @@ static void sig_handler(int signo)
action.sa_flags = 0; action.sa_flags = 0;
(void) sigaction(signo, &action, NULL); (void) sigaction(signo, &action, NULL);
if (rto_opts.rto_gdb) if (rto_opts.rto_gdb) {
if (system(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)
;
}
raise(signo); raise(signo);
errno = old_errno;
} }
static void print_opts(raidz_test_opts_t *opts, boolean_t force) static void print_opts(raidz_test_opts_t *opts, boolean_t force)
@ -77,16 +86,20 @@ static void print_opts(raidz_test_opts_t *opts, boolean_t force)
(void) fprintf(stdout, DBLSEP "Running with options:\n" (void) fprintf(stdout, DBLSEP "Running with options:\n"
" (-a) zio ashift : %zu\n" " (-a) zio ashift : %zu\n"
" (-o) zio offset : 1 << %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" " (-d) number of raidz data columns : %zu\n"
" (-s) size of DATA : 1 << %zu\n" " (-s) size of DATA : 1 << %zu\n"
" (-S) sweep parameters : %s \n" " (-S) sweep parameters : %s \n"
" (-v) verbose : %s \n\n", " (-v) verbose : %s \n\n",
opts->rto_ashift, /* -a */ opts->rto_ashift, /* -a */
ilog2(opts->rto_offset), /* -o */ ilog2(opts->rto_offset), /* -o */
opts->rto_dcols, /* -d */ opts->rto_expand ? "yes" : "no", /* -e */
ilog2(opts->rto_dsize), /* -s */ (u_longlong_t)opts->rto_expand_offset, /* -r */
opts->rto_sweep ? "yes" : "no", /* -S */ opts->rto_dcols, /* -d */
verbose); /* -v */ ilog2(opts->rto_dsize), /* -s */
opts->rto_sweep ? "yes" : "no", /* -S */
verbose); /* -v */
} }
} }
@ -104,6 +117,8 @@ static void usage(boolean_t requested)
"\t[-S parameter sweep (default: %s)]\n" "\t[-S parameter sweep (default: %s)]\n"
"\t[-t timeout for parameter sweep test]\n" "\t[-t timeout for parameter sweep test]\n"
"\t[-B benchmark all raidz implementations]\n" "\t[-B benchmark all raidz implementations]\n"
"\t[-e use expanded raidz map (default: %s)]\n"
"\t[-r expanded raidz map reflow offset (default: %llx)]\n"
"\t[-v increase verbosity (default: %zu)]\n" "\t[-v increase verbosity (default: %zu)]\n"
"\t[-h (print help)]\n" "\t[-h (print help)]\n"
"\t[-T test the test, see if failure would be detected]\n" "\t[-T test the test, see if failure would be detected]\n"
@ -114,6 +129,8 @@ static void usage(boolean_t requested)
o->rto_dcols, /* -d */ o->rto_dcols, /* -d */
ilog2(o->rto_dsize), /* -s */ ilog2(o->rto_dsize), /* -s */
rto_opts.rto_sweep ? "yes" : "no", /* -S */ rto_opts.rto_sweep ? "yes" : "no", /* -S */
rto_opts.rto_expand ? "yes" : "no", /* -e */
(u_longlong_t)o->rto_expand_offset, /* -r */
o->rto_v); /* -d */ o->rto_v); /* -d */
exit(requested ? 0 : 1); exit(requested ? 0 : 1);
@ -128,7 +145,7 @@ static void process_options(int argc, char **argv)
bcopy(&rto_opts_defaults, o, sizeof (*o)); bcopy(&rto_opts_defaults, o, sizeof (*o));
while ((opt = getopt(argc, argv, "TDBSvha:o:d:s:t:")) != -1) { while ((opt = getopt(argc, argv, "TDBSvha:er:o:d:s:t:")) != -1) {
value = 0; value = 0;
switch (opt) { switch (opt) {
@ -136,6 +153,12 @@ static void process_options(int argc, char **argv)
value = strtoull(optarg, NULL, 0); value = strtoull(optarg, NULL, 0);
o->rto_ashift = MIN(13, MAX(9, value)); o->rto_ashift = MIN(13, MAX(9, value));
break; break;
case 'e':
o->rto_expand = 1;
break;
case 'r':
o->rto_expand_offset = strtoull(optarg, NULL, 0);
break;
case 'o': case 'o':
value = strtoull(optarg, NULL, 0); value = strtoull(optarg, NULL, 0);
o->rto_offset = ((1ULL << MIN(12, value)) >> 9) << 9; o->rto_offset = ((1ULL << MIN(12, value)) >> 9) << 9;
@ -179,25 +202,34 @@ static void process_options(int argc, char **argv)
} }
} }
#define DATA_COL(rm, i) ((rm)->rm_col[raidz_parity(rm) + (i)].rc_abd) #define DATA_COL(rr, i) ((rr)->rr_col[rr->rr_firstdatacol + (i)].rc_abd)
#define DATA_COL_SIZE(rm, i) ((rm)->rm_col[raidz_parity(rm) + (i)].rc_size) #define DATA_COL_SIZE(rr, i) ((rr)->rr_col[rr->rr_firstdatacol + (i)].rc_size)
#define CODE_COL(rm, i) ((rm)->rm_col[(i)].rc_abd) #define CODE_COL(rr, i) ((rr)->rr_col[(i)].rc_abd)
#define CODE_COL_SIZE(rm, i) ((rm)->rm_col[(i)].rc_size) #define CODE_COL_SIZE(rr, i) ((rr)->rr_col[(i)].rc_size)
static int static int
cmp_code(raidz_test_opts_t *opts, const raidz_map_t *rm, const int parity) cmp_code(raidz_test_opts_t *opts, const raidz_map_t *rm, const int parity)
{ {
int i, ret = 0; int r, i, ret = 0;
VERIFY(parity >= 1 && parity <= 3); VERIFY(parity >= 1 && parity <= 3);
for (i = 0; i < parity; i++) { for (r = 0; r < rm->rm_nrows; r++) {
if (abd_cmp(CODE_COL(rm, i), CODE_COL(opts->rm_golden, i)) raidz_row_t * const rr = rm->rm_row[r];
!= 0) { raidz_row_t * const rrg = opts->rm_golden->rm_row[r];
ret++; for (i = 0; i < parity; i++) {
LOG_OPT(D_DEBUG, opts, if (CODE_COL_SIZE(rrg, i) == 0) {
"\nParity block [%d] different!\n", i); 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);
}
} }
} }
return (ret); return (ret);
@ -206,16 +238,26 @@ cmp_code(raidz_test_opts_t *opts, const raidz_map_t *rm, const int parity)
static int static int
cmp_data(raidz_test_opts_t *opts, raidz_map_t *rm) cmp_data(raidz_test_opts_t *opts, raidz_map_t *rm)
{ {
int i, ret = 0; int r, i, dcols, ret = 0;
int dcols = opts->rm_golden->rm_cols - raidz_parity(opts->rm_golden);
for (i = 0; i < dcols; i++) { for (r = 0; r < rm->rm_nrows; r++) {
if (abd_cmp(DATA_COL(opts->rm_golden, i), DATA_COL(rm, i)) raidz_row_t *rr = rm->rm_row[r];
!= 0) { raidz_row_t *rrg = opts->rm_golden->rm_row[r];
ret++; 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;
}
LOG_OPT(D_DEBUG, opts, if (abd_cmp(DATA_COL(rrg, i),
"\nData block [%d] different!\n", i); DATA_COL(rr, i)) != 0) {
ret++;
LOG_OPT(D_DEBUG, opts,
"\nData block [%d] different!\n", i);
}
} }
} }
return (ret); return (ret);
@ -236,12 +278,13 @@ init_rand(void *data, size_t size, void *private)
static void static void
corrupt_colums(raidz_map_t *rm, const int *tgts, const int cnt) corrupt_colums(raidz_map_t *rm, const int *tgts, const int cnt)
{ {
int i; for (int r = 0; r < rm->rm_nrows; r++) {
raidz_col_t *col; raidz_row_t *rr = rm->rm_row[r];
for (int i = 0; i < cnt; i++) {
for (i = 0; i < cnt; i++) { raidz_col_t *col = &rr->rr_col[tgts[i]];
col = &rm->rm_col[tgts[i]]; abd_iterate_func(col->rc_abd, 0, col->rc_size,
abd_iterate_func(col->rc_abd, 0, col->rc_size, init_rand, NULL); init_rand, NULL);
}
} }
} }
@ -288,10 +331,22 @@ init_raidz_golden_map(raidz_test_opts_t *opts, const int parity)
VERIFY0(vdev_raidz_impl_set("original")); VERIFY0(vdev_raidz_impl_set("original"));
opts->rm_golden = vdev_raidz_map_alloc(opts->zio_golden, if (opts->rto_expand) {
opts->rto_ashift, total_ncols, parity); opts->rm_golden =
rm_test = vdev_raidz_map_alloc(zio_test, vdev_raidz_map_alloc_expanded(opts->zio_golden->io_abd,
opts->rto_ashift, total_ncols, parity); 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);
}
VERIFY(opts->zio_golden); VERIFY(opts->zio_golden);
VERIFY(opts->rm_golden); VERIFY(opts->rm_golden);
@ -312,6 +367,187 @@ init_raidz_golden_map(raidz_test_opts_t *opts, const int parity)
return (err); 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 * static raidz_map_t *
init_raidz_map(raidz_test_opts_t *opts, zio_t **zio, const int parity) init_raidz_map(raidz_test_opts_t *opts, zio_t **zio, const int parity)
{ {
@ -330,8 +566,15 @@ init_raidz_map(raidz_test_opts_t *opts, zio_t **zio, const int parity)
(*zio)->io_abd = raidz_alloc(alloc_dsize); (*zio)->io_abd = raidz_alloc(alloc_dsize);
init_zio_abd(*zio); init_zio_abd(*zio);
rm = vdev_raidz_map_alloc(*zio, opts->rto_ashift, if (opts->rto_expand) {
total_ncols, parity); 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);
}
VERIFY(rm); VERIFY(rm);
/* Make sure code columns are destroyed */ /* Make sure code columns are destroyed */
@ -420,7 +663,7 @@ run_rec_check_impl(raidz_test_opts_t *opts, raidz_map_t *rm, const int fn)
if (fn < RAIDZ_REC_PQ) { if (fn < RAIDZ_REC_PQ) {
/* can reconstruct 1 failed data disk */ /* can reconstruct 1 failed data disk */
for (x0 = 0; x0 < opts->rto_dcols; x0++) { for (x0 = 0; x0 < opts->rto_dcols; x0++) {
if (x0 >= rm->rm_cols - raidz_parity(rm)) if (x0 >= rm->rm_row[0]->rr_cols - raidz_parity(rm))
continue; continue;
/* Check if should stop */ /* Check if should stop */
@ -445,10 +688,11 @@ run_rec_check_impl(raidz_test_opts_t *opts, raidz_map_t *rm, const int fn)
} else if (fn < RAIDZ_REC_PQR) { } else if (fn < RAIDZ_REC_PQR) {
/* can reconstruct 2 failed data disk */ /* can reconstruct 2 failed data disk */
for (x0 = 0; x0 < opts->rto_dcols; x0++) { for (x0 = 0; x0 < opts->rto_dcols; x0++) {
if (x0 >= rm->rm_cols - raidz_parity(rm)) if (x0 >= rm->rm_row[0]->rr_cols - raidz_parity(rm))
continue; continue;
for (x1 = x0 + 1; x1 < opts->rto_dcols; x1++) { for (x1 = x0 + 1; x1 < opts->rto_dcols; x1++) {
if (x1 >= rm->rm_cols - raidz_parity(rm)) if (x1 >= rm->rm_row[0]->rr_cols -
raidz_parity(rm))
continue; continue;
/* Check if should stop */ /* Check if should stop */
@ -475,14 +719,15 @@ run_rec_check_impl(raidz_test_opts_t *opts, raidz_map_t *rm, const int fn)
} else { } else {
/* can reconstruct 3 failed data disk */ /* can reconstruct 3 failed data disk */
for (x0 = 0; x0 < opts->rto_dcols; x0++) { for (x0 = 0; x0 < opts->rto_dcols; x0++) {
if (x0 >= rm->rm_cols - raidz_parity(rm)) if (x0 >= rm->rm_row[0]->rr_cols - raidz_parity(rm))
continue; continue;
for (x1 = x0 + 1; x1 < opts->rto_dcols; x1++) { for (x1 = x0 + 1; x1 < opts->rto_dcols; x1++) {
if (x1 >= rm->rm_cols - raidz_parity(rm)) if (x1 >= rm->rm_row[0]->rr_cols -
raidz_parity(rm))
continue; continue;
for (x2 = x1 + 1; x2 < opts->rto_dcols; x2++) { for (x2 = x1 + 1; x2 < opts->rto_dcols; x2++) {
if (x2 >= if (x2 >= rm->rm_row[0]->rr_cols -
rm->rm_cols - raidz_parity(rm)) raidz_parity(rm))
continue; continue;
/* Check if should stop */ /* Check if should stop */
@ -700,6 +945,8 @@ run_sweep(void)
opts->rto_dcols = dcols_v[d]; opts->rto_dcols = dcols_v[d];
opts->rto_offset = (1 << ashift_v[a]) * rand(); opts->rto_offset = (1 << ashift_v[a]) * rand();
opts->rto_dsize = size_v[s]; 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 */ opts->rto_v = 0; /* be quiet */
VERIFY3P(thread_create(NULL, 0, sweep_thread, (void *) opts, VERIFY3P(thread_create(NULL, 0, sweep_thread, (void *) opts,
@ -732,6 +979,7 @@ exit:
return (sweep_state == SWEEP_ERROR ? SWEEP_ERROR : 0); return (sweep_state == SWEEP_ERROR ? SWEEP_ERROR : 0);
} }
int int
main(int argc, char **argv) main(int argc, char **argv)
{ {
@ -739,8 +987,8 @@ main(int argc, char **argv)
struct sigaction action; struct sigaction action;
int err = 0; int err = 0;
/* init gdb string early */ /* init gdb pid string early */
(void) sprintf(gdb, gdb_tmpl, getpid()); (void) sprintf(pid_s, "%d", getpid());
action.sa_handler = sig_handler; action.sa_handler = sig_handler;
sigemptyset(&action.sa_mask); sigemptyset(&action.sa_mask);
@ -757,7 +1005,7 @@ main(int argc, char **argv)
process_options(argc, argv); process_options(argc, argv);
kernel_init(FREAD); kernel_init(SPA_MODE_READ);
/* setup random data because rand() is not reentrant */ /* setup random data because rand() is not reentrant */
rand_data = (int *)umem_alloc(SPA_MAXBLOCKSIZE, UMEM_NOFAIL); rand_data = (int *)umem_alloc(SPA_MAXBLOCKSIZE, UMEM_NOFAIL);

View File

@ -38,18 +38,21 @@ static const char *raidz_impl_names[] = {
"avx512bw", "avx512bw",
"aarch64_neon", "aarch64_neon",
"aarch64_neonx2", "aarch64_neonx2",
"powerpc_altivec",
NULL NULL
}; };
typedef struct raidz_test_opts { typedef struct raidz_test_opts {
size_t rto_ashift; size_t rto_ashift;
size_t rto_offset; uint64_t rto_offset;
size_t rto_dcols; size_t rto_dcols;
size_t rto_dsize; size_t rto_dsize;
size_t rto_v; size_t rto_v;
size_t rto_sweep; size_t rto_sweep;
size_t rto_sweep_timeout; size_t rto_sweep_timeout;
size_t rto_benchmark; size_t rto_benchmark;
size_t rto_expand;
uint64_t rto_expand_offset;
size_t rto_sanity; size_t rto_sanity;
size_t rto_gdb; size_t rto_gdb;
@ -68,6 +71,8 @@ static const raidz_test_opts_t rto_opts_defaults = {
.rto_v = 0, .rto_v = 0,
.rto_sweep = 0, .rto_sweep = 0,
.rto_benchmark = 0, .rto_benchmark = 0,
.rto_expand = 0,
.rto_expand_offset = -1ULL,
.rto_sanity = 0, .rto_sanity = 0,
.rto_gdb = 0, .rto_gdb = 0,
.rto_should_stop = B_FALSE .rto_should_stop = B_FALSE
@ -112,4 +117,7 @@ void init_zio_abd(zio_t *zio);
void run_raidz_benchmark(void); 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 */ #endif /* RAIDZ_TEST_H */

View File

@ -1 +1,3 @@
include $(top_srcdir)/config/Shellcheck.am
dist_udev_SCRIPTS = vdev_id dist_udev_SCRIPTS = vdev_id

View File

@ -79,6 +79,34 @@
# channel 86:00.0 1 A # channel 86:00.0 1 A
# channel 86:00.0 0 B # channel 86:00.0 0 B
# #
# # Example vdev_id.conf - multipath / multijbod-daisychaining
# #
#
# multipath yes
# multijbod yes
#
# # PCI_ID HBA PORT CHANNEL NAME
# channel 85:00.0 1 A
# channel 85:00.0 0 B
# channel 86:00.0 1 A
# channel 86:00.0 0 B
# #
# # Example vdev_id.conf - multipath / mixed
# #
#
# multipath yes
# slot mix
#
# # PCI_ID HBA PORT CHANNEL NAME
# channel 85:00.0 3 A
# channel 85:00.0 2 B
# channel 86:00.0 3 A
# channel 86:00.0 2 B
# channel af:00.0 0 C
# channel af:00.0 1 C
# # # #
# # Example vdev_id.conf - alias # # Example vdev_id.conf - alias
# # # #
@ -92,9 +120,10 @@ PATH=/bin:/sbin:/usr/bin:/usr/sbin
CONFIG=/etc/zfs/vdev_id.conf CONFIG=/etc/zfs/vdev_id.conf
PHYS_PER_PORT= PHYS_PER_PORT=
DEV= DEV=
MULTIPATH=
TOPOLOGY= TOPOLOGY=
BAY= BAY=
ENCL_ID=""
UNIQ_ENCL_ID=""
usage() { usage() {
cat << EOF cat << EOF
@ -102,71 +131,153 @@ Usage: vdev_id [-h]
vdev_id <-d device> [-c config_file] [-p phys_per_port] vdev_id <-d device> [-c config_file] [-p phys_per_port]
[-g sas_direct|sas_switch|scsi] [-m] [-g sas_direct|sas_switch|scsi] [-m]
-c specify name of alernate config file [default=$CONFIG] -c specify name of an alternative config file [default=$CONFIG]
-d specify basename of device (i.e. sda) -d specify basename of device (i.e. sda)
-e Create enclose device symlinks only (/dev/by-enclosure) -e Create enclose device symlinks only (/dev/by-enclosure)
-g Storage network topology [default="$TOPOLOGY"] -g Storage network topology [default="$TOPOLOGY"]
-m Run in multipath mode -m Run in multipath mode
-j Run in multijbod mode
-p number of phy's per switch port [default=$PHYS_PER_PORT] -p number of phy's per switch port [default=$PHYS_PER_PORT]
-h show this summary -h show this summary
EOF EOF
exit 0 exit 1
# exit with error to avoid processing usage message by a udev rule
} }
map_slot() { map_slot() {
local LINUX_SLOT=$1 LINUX_SLOT=$1
local CHANNEL=$2 CHANNEL=$2
local MAPPED_SLOT=
MAPPED_SLOT=`awk "\\$1 == \"slot\" && \\$2 == ${LINUX_SLOT} && \ MAPPED_SLOT=$(awk -v linux_slot="$LINUX_SLOT" -v channel="$CHANNEL" \
\\$4 ~ /^${CHANNEL}$|^$/ { print \\$3; exit }" $CONFIG` '$1 == "slot" && $2 == linux_slot && \
($4 ~ "^"channel"$" || $4 ~ /^$/) { print $3; exit}' $CONFIG)
if [ -z "$MAPPED_SLOT" ] ; then if [ -z "$MAPPED_SLOT" ] ; then
MAPPED_SLOT=$LINUX_SLOT MAPPED_SLOT=$LINUX_SLOT
fi fi
printf "%d" ${MAPPED_SLOT} printf "%d" "${MAPPED_SLOT}"
} }
map_channel() { map_channel() {
local MAPPED_CHAN= MAPPED_CHAN=
local PCI_ID=$1 PCI_ID=$1
local PORT=$2 PORT=$2
case $TOPOLOGY in case $TOPOLOGY in
"sas_switch") "sas_switch")
MAPPED_CHAN=`awk "\\$1 == \"channel\" && \\$2 == ${PORT} \ MAPPED_CHAN=$(awk -v port="$PORT" \
{ print \\$3; exit }" $CONFIG` '$1 == "channel" && $2 == port \
{ print $3; exit }' $CONFIG)
;; ;;
"sas_direct"|"scsi") "sas_direct"|"scsi")
MAPPED_CHAN=`awk "\\$1 == \"channel\" && \ MAPPED_CHAN=$(awk -v pciID="$PCI_ID" -v port="$PORT" \
\\$2 == \"${PCI_ID}\" && \\$3 == ${PORT} \ '$1 == "channel" && $2 == pciID && $3 == port \
{ print \\$4; exit }" $CONFIG` {print $4}' $CONFIG)
;; ;;
esac esac
printf "%s" ${MAPPED_CHAN} printf "%s" "${MAPPED_CHAN}"
}
get_encl_id() {
set -- $(echo $1)
count=$#
i=1
while [ $i -le $count ] ; do
d=$(eval echo '$'{$i})
id=$(cat "/sys/class/enclosure/${d}/id")
ENCL_ID="${ENCL_ID} $id"
i=$((i + 1))
done
}
get_uniq_encl_id() {
for uuid in ${ENCL_ID}; do
found=0
for count in ${UNIQ_ENCL_ID}; do
if [ $count = $uuid ]; then
found=1
break
fi
done
if [ $found -eq 0 ]; then
UNIQ_ENCL_ID="${UNIQ_ENCL_ID} $uuid"
fi
done
}
# map_jbod explainer: The bsg driver knows the difference between a SAS
# expander and fanout expander. Use hostX instance along with top-level
# (whole enclosure) expander instances in /sys/class/enclosure and
# matching a field in an array of expanders, using the index of the
# matched array field as the enclosure instance, thereby making jbod IDs
# dynamic. Avoids reliance on high overhead userspace commands like
# multipath and lsscsi and instead uses existing sysfs data. $HOSTCHAN
# variable derived from devpath gymnastics in sas_handler() function.
map_jbod() {
DEVEXP=$(ls -l "/sys/block/$DEV/device/" | grep enclos | awk -F/ '{print $(NF-1) }')
DEV=$1
# Use "set --" to create index values (Arrays)
set -- $(ls -l /sys/class/enclosure | grep -v "^total" | awk '{print $9}')
# Get count of total elements
JBOD_COUNT=$#
JBOD_ITEM=$*
# Build JBODs (enclosure) id from sys/class/enclosure/<dev>/id
get_encl_id "$JBOD_ITEM"
# Different expander instances for each paths.
# Filter out and keep only unique id.
get_uniq_encl_id
# Identify final 'mapped jbod'
j=0
for count in ${UNIQ_ENCL_ID}; do
i=1
j=$((j + 1))
while [ $i -le $JBOD_COUNT ] ; do
d=$(eval echo '$'{$i})
id=$(cat "/sys/class/enclosure/${d}/id")
if [ "$d" = "$DEVEXP" ] && [ $id = $count ] ; then
MAPPED_JBOD=$j
break
fi
i=$((i + 1))
done
done
printf "%d" "${MAPPED_JBOD}"
} }
sas_handler() { sas_handler() {
if [ -z "$PHYS_PER_PORT" ] ; then if [ -z "$PHYS_PER_PORT" ] ; then
PHYS_PER_PORT=`awk "\\$1 == \"phys_per_port\" \ PHYS_PER_PORT=$(awk '$1 == "phys_per_port" \
{print \\$2; exit}" $CONFIG` {print $2; exit}' $CONFIG)
fi fi
PHYS_PER_PORT=${PHYS_PER_PORT:-4} PHYS_PER_PORT=${PHYS_PER_PORT:-4}
if ! echo $PHYS_PER_PORT | grep -q -E '^[0-9]+$' ; then
if ! echo "$PHYS_PER_PORT" | grep -q -E '^[0-9]+$' ; then
echo "Error: phys_per_port value $PHYS_PER_PORT is non-numeric" echo "Error: phys_per_port value $PHYS_PER_PORT is non-numeric"
exit 1 exit 1
fi fi
if [ -z "$MULTIPATH_MODE" ] ; then if [ -z "$MULTIPATH_MODE" ] ; then
MULTIPATH_MODE=`awk "\\$1 == \"multipath\" \ MULTIPATH_MODE=$(awk '$1 == "multipath" \
{print \\$2; exit}" $CONFIG` {print $2; exit}' $CONFIG)
fi
if [ -z "$MULTIJBOD_MODE" ] ; then
MULTIJBOD_MODE=$(awk '$1 == "multijbod" \
{print $2; exit}' $CONFIG)
fi fi
# Use first running component device if we're handling a dm-mpath device # Use first running component device if we're handling a dm-mpath device
if [ "$MULTIPATH_MODE" = "yes" ] ; then if [ "$MULTIPATH_MODE" = "yes" ] ; then
# If udev didn't tell us the UUID via DM_NAME, check /dev/mapper # If udev didn't tell us the UUID via DM_NAME, check /dev/mapper
if [ -z "$DM_NAME" ] ; then if [ -z "$DM_NAME" ] ; then
DM_NAME=`ls -l --full-time /dev/mapper | DM_NAME=$(ls -l --full-time /dev/mapper |
awk "/\/$DEV$/{print \\$9}"` grep "$DEV"$ | awk '{print $9}')
fi fi
# For raw disks udev exports DEVTYPE=partition when # For raw disks udev exports DEVTYPE=partition when
@ -176,28 +287,50 @@ sas_handler() {
# we have to append the -part suffix directly in the # we have to append the -part suffix directly in the
# helper. # helper.
if [ "$DEVTYPE" != "partition" ] ; then if [ "$DEVTYPE" != "partition" ] ; then
PART=`echo $DM_NAME | awk -Fp '/p/{print "-part"$2}'` # Match p[number], remove the 'p' and prepend "-part"
PART=$(echo "$DM_NAME" |
awk 'match($0,/p[0-9]+$/) {print "-part"substr($0,RSTART+1,RLENGTH-1)}')
fi fi
# Strip off partition information. # Strip off partition information.
DM_NAME=`echo $DM_NAME | sed 's/p[0-9][0-9]*$//'` DM_NAME=$(echo "$DM_NAME" | sed 's/p[0-9][0-9]*$//')
if [ -z "$DM_NAME" ] ; then if [ -z "$DM_NAME" ] ; then
return return
fi fi
# Get the raw scsi device name from multipath -ll. Strip off # Utilize DM device name to gather subordinate block devices
# leading pipe symbols to make field numbering consistent. # using sysfs to avoid userspace utilities
DEV=`multipath -ll $DM_NAME |
awk '/running/{gsub("^[|]"," "); print $3 ; exit}'` # If our DEVNAME is something like /dev/dm-177, then we may be
# able to get our DMDEV from it.
DMDEV=$(echo $DEVNAME | sed 's;/dev/;;g')
if [ ! -e /sys/block/$DMDEV/slaves/* ] ; then
# It's not there, try looking in /dev/mapper
DMDEV=$(ls -l --full-time /dev/mapper | grep $DM_NAME |
awk '{gsub("../", " "); print $NF}')
fi
# Use sysfs pointers in /sys/block/dm-X/slaves because using
# userspace tools creates lots of overhead and should be avoided
# whenever possible. Use awk to isolate lowest instance of
# sd device member in dm device group regardless of string
# length.
DEV=$(ls "/sys/block/$DMDEV/slaves" | awk '
{ len=sprintf ("%20s",length($0)); gsub(/ /,0,str); a[NR]=len "_" $0; }
END {
asort(a)
print substr(a[1],22)
}')
if [ -z "$DEV" ] ; then if [ -z "$DEV" ] ; then
return return
fi fi
fi fi
if echo $DEV | grep -q ^/devices/ ; then if echo "$DEV" | grep -q ^/devices/ ; then
sys_path=$DEV sys_path=$DEV
else else
sys_path=`udevadm info -q path -p /sys/block/$DEV 2>/dev/null` sys_path=$(udevadm info -q path -p "/sys/block/$DEV" 2>/dev/null)
fi fi
# Use positional parameters as an ad-hoc array # Use positional parameters as an ad-hoc array
@ -207,84 +340,104 @@ sas_handler() {
# Get path up to /sys/.../hostX # Get path up to /sys/.../hostX
i=1 i=1
while [ $i -le $num_dirs ] ; do
d=$(eval echo \${$i}) while [ $i -le "$num_dirs" ] ; do
d=$(eval echo '$'{$i})
scsi_host_dir="$scsi_host_dir/$d" scsi_host_dir="$scsi_host_dir/$d"
echo $d | grep -q -E '^host[0-9]+$' && break echo "$d" | grep -q -E '^host[0-9]+$' && break
i=$(($i + 1)) i=$((i + 1))
done done
if [ $i = $num_dirs ] ; then # Lets grab the SAS host channel number and save it for JBOD sorting later
HOSTCHAN=$(echo "$d" | awk -F/ '{ gsub("host","",$NF); print $NF}')
if [ $i = "$num_dirs" ] ; then
return return
fi fi
PCI_ID=$(eval echo \${$(($i -1))} | awk -F: '{print $2":"$3}') PCI_ID=$(eval echo '$'{$((i -1))} | awk -F: '{print $2":"$3}')
# In sas_switch mode, the directory four levels beneath # In sas_switch mode, the directory four levels beneath
# /sys/.../hostX contains symlinks to phy devices that reveal # /sys/.../hostX contains symlinks to phy devices that reveal
# the switch port number. In sas_direct mode, the phy links one # the switch port number. In sas_direct mode, the phy links one
# directory down reveal the HBA port. # directory down reveal the HBA port.
port_dir=$scsi_host_dir port_dir=$scsi_host_dir
case $TOPOLOGY in case $TOPOLOGY in
"sas_switch") j=$(($i + 4)) ;; "sas_switch") j=$((i + 4)) ;;
"sas_direct") j=$(($i + 1)) ;; "sas_direct") j=$((i + 1)) ;;
esac esac
i=$(($i + 1)) i=$((i + 1))
while [ $i -le $j ] ; do while [ $i -le $j ] ; do
port_dir="$port_dir/$(eval echo \${$i})" port_dir="$port_dir/$(eval echo '$'{$i})"
i=$(($i + 1)) i=$((i + 1))
done done
PHY=`ls -d $port_dir/phy* 2>/dev/null | head -1 | awk -F: '{print $NF}'` PHY=$(ls -vd "$port_dir"/phy* 2>/dev/null | head -1 | awk -F: '{print $NF}')
if [ -z "$PHY" ] ; then if [ -z "$PHY" ] ; then
PHY=0 PHY=0
fi fi
PORT=$(( $PHY / $PHYS_PER_PORT )) PORT=$((PHY / PHYS_PER_PORT))
# Look in /sys/.../sas_device/end_device-X for the bay_identifier # Look in /sys/.../sas_device/end_device-X for the bay_identifier
# attribute. # attribute.
end_device_dir=$port_dir end_device_dir=$port_dir
while [ $i -lt $num_dirs ] ; do
d=$(eval echo \${$i}) while [ $i -lt "$num_dirs" ] ; do
d=$(eval echo '$'{$i})
end_device_dir="$end_device_dir/$d" end_device_dir="$end_device_dir/$d"
if echo $d | grep -q '^end_device' ; then if echo "$d" | grep -q '^end_device' ; then
end_device_dir="$end_device_dir/sas_device/$d" end_device_dir="$end_device_dir/sas_device/$d"
break break
fi fi
i=$(($i + 1)) i=$((i + 1))
done done
# Add 'mix' slot type for environments where dm-multipath devices
# include end-devices connected via SAS expanders or direct connection
# to SAS HBA. A mixed connectivity environment such as pool devices
# contained in a SAS JBOD and spare drives or log devices directly
# connected in a server backplane without expanders in the I/O path.
SLOT= SLOT=
case $BAY in case $BAY in
"bay") "bay")
SLOT=`cat $end_device_dir/bay_identifier 2>/dev/null` SLOT=$(cat "$end_device_dir/bay_identifier" 2>/dev/null)
;;
"mix")
if [ $(cat "$end_device_dir/bay_identifier" 2>/dev/null) ] ; then
SLOT=$(cat "$end_device_dir/bay_identifier" 2>/dev/null)
else
SLOT=$(cat "$end_device_dir/phy_identifier" 2>/dev/null)
fi
;; ;;
"phy") "phy")
SLOT=`cat $end_device_dir/phy_identifier 2>/dev/null` SLOT=$(cat "$end_device_dir/phy_identifier" 2>/dev/null)
;; ;;
"port") "port")
d=$(eval echo \${$i}) d=$(eval echo '$'{$i})
SLOT=`echo $d | sed -e 's/^.*://'` SLOT=$(echo "$d" | sed -e 's/^.*://')
;; ;;
"id") "id")
i=$(($i + 1)) i=$((i + 1))
d=$(eval echo \${$i}) d=$(eval echo '$'{$i})
SLOT=`echo $d | sed -e 's/^.*://'` SLOT=$(echo "$d" | sed -e 's/^.*://')
;; ;;
"lun") "lun")
i=$(($i + 2)) i=$((i + 2))
d=$(eval echo \${$i}) d=$(eval echo '$'{$i})
SLOT=`echo $d | sed -e 's/^.*://'` SLOT=$(echo "$d" | sed -e 's/^.*://')
;; ;;
"ses") "ses")
# look for this SAS path in all SCSI Enclosure Services # look for this SAS path in all SCSI Enclosure Services
# (SES) enclosures # (SES) enclosures
sas_address=`cat $end_device_dir/sas_address 2>/dev/null` sas_address=$(cat "$end_device_dir/sas_address" 2>/dev/null)
enclosures=`lsscsi -g | \ enclosures=$(lsscsi -g | \
sed -n -e '/enclosu/s/^.* \([^ ][^ ]*\) *$/\1/p'` sed -n -e '/enclosu/s/^.* \([^ ][^ ]*\) *$/\1/p')
for enclosure in $enclosures; do for enclosure in $enclosures; do
set -- $(sg_ses -p aes $enclosure | \ set -- $(sg_ses -p aes "$enclosure" | \
awk "/device slot number:/{slot=\$12} \ awk "/device slot number:/{slot=\$12} \
/SAS address: $sas_address/\ /SAS address: $sas_address/\
{print slot}") {print slot}")
@ -299,42 +452,55 @@ sas_handler() {
return return
fi fi
CHAN=`map_channel $PCI_ID $PORT` if [ "$MULTIJBOD_MODE" = "yes" ] ; then
SLOT=`map_slot $SLOT $CHAN` CHAN=$(map_channel "$PCI_ID" "$PORT")
if [ -z "$CHAN" ] ; then SLOT=$(map_slot "$SLOT" "$CHAN")
return JBOD=$(map_jbod "$DEV")
if [ -z "$CHAN" ] ; then
return
fi
echo "${CHAN}"-"${JBOD}"-"${SLOT}${PART}"
else
CHAN=$(map_channel "$PCI_ID" "$PORT")
SLOT=$(map_slot "$SLOT" "$CHAN")
if [ -z "$CHAN" ] ; then
return
fi
echo "${CHAN}${SLOT}${PART}"
fi fi
echo ${CHAN}${SLOT}${PART}
} }
scsi_handler() { scsi_handler() {
if [ -z "$FIRST_BAY_NUMBER" ] ; then if [ -z "$FIRST_BAY_NUMBER" ] ; then
FIRST_BAY_NUMBER=`awk "\\$1 == \"first_bay_number\" \ FIRST_BAY_NUMBER=$(awk '$1 == "first_bay_number" \
{print \\$2; exit}" $CONFIG` {print $2; exit}' $CONFIG)
fi fi
FIRST_BAY_NUMBER=${FIRST_BAY_NUMBER:-0} FIRST_BAY_NUMBER=${FIRST_BAY_NUMBER:-0}
if [ -z "$PHYS_PER_PORT" ] ; then if [ -z "$PHYS_PER_PORT" ] ; then
PHYS_PER_PORT=`awk "\\$1 == \"phys_per_port\" \ PHYS_PER_PORT=$(awk '$1 == "phys_per_port" \
{print \\$2; exit}" $CONFIG` {print $2; exit}' $CONFIG)
fi fi
PHYS_PER_PORT=${PHYS_PER_PORT:-4} PHYS_PER_PORT=${PHYS_PER_PORT:-4}
if ! echo $PHYS_PER_PORT | grep -q -E '^[0-9]+$' ; then
if ! echo "$PHYS_PER_PORT" | grep -q -E '^[0-9]+$' ; then
echo "Error: phys_per_port value $PHYS_PER_PORT is non-numeric" echo "Error: phys_per_port value $PHYS_PER_PORT is non-numeric"
exit 1 exit 1
fi fi
if [ -z "$MULTIPATH_MODE" ] ; then if [ -z "$MULTIPATH_MODE" ] ; then
MULTIPATH_MODE=`awk "\\$1 == \"multipath\" \ MULTIPATH_MODE=$(awk '$1 == "multipath" \
{print \\$2; exit}" $CONFIG` {print $2; exit}' $CONFIG)
fi fi
# Use first running component device if we're handling a dm-mpath device # Use first running component device if we're handling a dm-mpath device
if [ "$MULTIPATH_MODE" = "yes" ] ; then if [ "$MULTIPATH_MODE" = "yes" ] ; then
# If udev didn't tell us the UUID via DM_NAME, check /dev/mapper # If udev didn't tell us the UUID via DM_NAME, check /dev/mapper
if [ -z "$DM_NAME" ] ; then if [ -z "$DM_NAME" ] ; then
DM_NAME=`ls -l --full-time /dev/mapper | DM_NAME=$(ls -l --full-time /dev/mapper |
awk "/\/$DEV$/{print \\$9}"` grep "$DEV"$ | awk '{print $9}')
fi fi
# For raw disks udev exports DEVTYPE=partition when # For raw disks udev exports DEVTYPE=partition when
@ -344,28 +510,30 @@ scsi_handler() {
# we have to append the -part suffix directly in the # we have to append the -part suffix directly in the
# helper. # helper.
if [ "$DEVTYPE" != "partition" ] ; then if [ "$DEVTYPE" != "partition" ] ; then
PART=`echo $DM_NAME | awk -Fp '/p/{print "-part"$2}'` # Match p[number], remove the 'p' and prepend "-part"
PART=$(echo "$DM_NAME" |
awk 'match($0,/p[0-9]+$/) {print "-part"substr($0,RSTART+1,RLENGTH-1)}')
fi fi
# Strip off partition information. # Strip off partition information.
DM_NAME=`echo $DM_NAME | sed 's/p[0-9][0-9]*$//'` DM_NAME=$(echo "$DM_NAME" | sed 's/p[0-9][0-9]*$//')
if [ -z "$DM_NAME" ] ; then if [ -z "$DM_NAME" ] ; then
return return
fi fi
# Get the raw scsi device name from multipath -ll. Strip off # Get the raw scsi device name from multipath -ll. Strip off
# leading pipe symbols to make field numbering consistent. # leading pipe symbols to make field numbering consistent.
DEV=`multipath -ll $DM_NAME | DEV=$(multipath -ll "$DM_NAME" |
awk '/running/{gsub("^[|]"," "); print $3 ; exit}'` awk '/running/{gsub("^[|]"," "); print $3 ; exit}')
if [ -z "$DEV" ] ; then if [ -z "$DEV" ] ; then
return return
fi fi
fi fi
if echo $DEV | grep -q ^/devices/ ; then if echo "$DEV" | grep -q ^/devices/ ; then
sys_path=$DEV sys_path=$DEV
else else
sys_path=`udevadm info -q path -p /sys/block/$DEV 2>/dev/null` sys_path=$(udevadm info -q path -p "/sys/block/$DEV" 2>/dev/null)
fi fi
# expect sys_path like this, for example: # expect sys_path like this, for example:
@ -378,44 +546,47 @@ scsi_handler() {
# Get path up to /sys/.../hostX # Get path up to /sys/.../hostX
i=1 i=1
while [ $i -le $num_dirs ] ; do
d=$(eval echo \${$i}) while [ $i -le "$num_dirs" ] ; do
d=$(eval echo '$'{$i})
scsi_host_dir="$scsi_host_dir/$d" scsi_host_dir="$scsi_host_dir/$d"
echo $d | grep -q -E '^host[0-9]+$' && break
i=$(($i + 1)) echo "$d" | grep -q -E '^host[0-9]+$' && break
i=$((i + 1))
done done
if [ $i = $num_dirs ] ; then if [ $i = "$num_dirs" ] ; then
return return
fi fi
PCI_ID=$(eval echo \${$(($i -1))} | awk -F: '{print $2":"$3}') PCI_ID=$(eval echo '$'{$((i -1))} | awk -F: '{print $2":"$3}')
# In scsi mode, the directory two levels beneath # In scsi mode, the directory two levels beneath
# /sys/.../hostX reveals the port and slot. # /sys/.../hostX reveals the port and slot.
port_dir=$scsi_host_dir port_dir=$scsi_host_dir
j=$(($i + 2)) j=$((i + 2))
i=$(($i + 1)) i=$((i + 1))
while [ $i -le $j ] ; do while [ $i -le $j ] ; do
port_dir="$port_dir/$(eval echo \${$i})" port_dir="$port_dir/$(eval echo '$'{$i})"
i=$(($i + 1)) i=$((i + 1))
done done
set -- $(echo $port_dir | sed -e 's/^.*:\([^:]*\):\([^:]*\)$/\1 \2/') set -- $(echo "$port_dir" | sed -e 's/^.*:\([^:]*\):\([^:]*\)$/\1 \2/')
PORT=$1 PORT=$1
SLOT=$(($2 + $FIRST_BAY_NUMBER)) SLOT=$(($2 + FIRST_BAY_NUMBER))
if [ -z "$SLOT" ] ; then if [ -z "$SLOT" ] ; then
return return
fi fi
CHAN=`map_channel $PCI_ID $PORT` CHAN=$(map_channel "$PCI_ID" "$PORT")
SLOT=`map_slot $SLOT $CHAN` SLOT=$(map_slot "$SLOT" "$CHAN")
if [ -z "$CHAN" ] ; then if [ -z "$CHAN" ] ; then
return return
fi fi
echo ${CHAN}${SLOT}${PART} echo "${CHAN}${SLOT}${PART}"
} }
# Figure out the name for the enclosure symlink # Figure out the name for the enclosure symlink
@ -426,7 +597,7 @@ enclosure_handler () {
# Get the enclosure ID ("0:0:0:0") # Get the enclosure ID ("0:0:0:0")
ENC=$(basename $(readlink -m "/sys/$DEVPATH/../..")) ENC=$(basename $(readlink -m "/sys/$DEVPATH/../.."))
if [ ! -d /sys/class/enclosure/$ENC ] ; then if [ ! -d "/sys/class/enclosure/$ENC" ] ; then
# Not an enclosure, bail out # Not an enclosure, bail out
return return
fi fi
@ -434,14 +605,14 @@ enclosure_handler () {
# Get the long sysfs device path to our enclosure. Looks like: # 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 # /devices/pci0000:00/0000:00:03.0/0000:05:00.0/host0/port-0:0/ ... /enclosure/0:0:0:0
ENC_DEVICE=$(readlink /sys/class/enclosure/$ENC) ENC_DEVICE=$(readlink "/sys/class/enclosure/$ENC")
# Grab the full path to the hosts port dir: # Grab the full path to the hosts port dir:
# /devices/pci0000:00/0000:00:03.0/0000:05:00.0/host0/port-0:0 # /devices/pci0000:00/0000:00:03.0/0000:05:00.0/host0/port-0:0
PORT_DIR=$(echo $ENC_DEVICE | grep -Eo '.+host[0-9]+/port-[0-9]+:[0-9]+') PORT_DIR=$(echo "$ENC_DEVICE" | grep -Eo '.+host[0-9]+/port-[0-9]+:[0-9]+')
# Get the port number # Get the port number
PORT_ID=$(echo $PORT_DIR | grep -Eo "[0-9]+$") PORT_ID=$(echo "$PORT_DIR" | grep -Eo "[0-9]+$")
# The PCI directory is two directories up from the port directory # The PCI directory is two directories up from the port directory
# /sys/devices/pci0000:00/0000:00:03.0/0000:05:00.0 # /sys/devices/pci0000:00/0000:00:03.0/0000:05:00.0
@ -452,7 +623,7 @@ enclosure_handler () {
# Name our device according to vdev_id.conf (like "L0" or "U1"). # Name our device according to vdev_id.conf (like "L0" or "U1").
NAME=$(awk "/channel/{if (\$1 == \"channel\" && \$2 == \"$PCI_ID\" && \ NAME=$(awk "/channel/{if (\$1 == \"channel\" && \$2 == \"$PCI_ID\" && \
\$3 == \"$PORT_ID\") {print \$4int(count[\$4])}; count[\$4]++}" $CONFIG) \$3 == \"$PORT_ID\") {print \$4\$3}}" $CONFIG)
echo "${NAME}" echo "${NAME}"
} }
@ -487,10 +658,12 @@ alias_handler () {
# digits as partitions, causing alias creation to fail. This # digits as partitions, causing alias creation to fail. This
# ambiguity seems unavoidable, so devices using this facility # ambiguity seems unavoidable, so devices using this facility
# must not use such names. # must not use such names.
local DM_PART= DM_PART=
if echo $DM_NAME | grep -q -E 'p[0-9][0-9]*$' ; then if echo "$DM_NAME" | grep -q -E 'p[0-9][0-9]*$' ; then
if [ "$DEVTYPE" != "partition" ] ; then if [ "$DEVTYPE" != "partition" ] ; then
DM_PART=`echo $DM_NAME | awk -Fp '/p/{print "-part"$2}'` # Match p[number], remove the 'p' and prepend "-part"
DM_PART=$(echo "$DM_NAME" |
awk 'match($0,/p[0-9]+$/) {print "-part"substr($0,RSTART+1,RLENGTH-1)}')
fi fi
fi fi
@ -498,21 +671,25 @@ alias_handler () {
for link in $DEVLINKS ; do for link in $DEVLINKS ; do
# Remove partition information to match key of top-level device. # Remove partition information to match key of top-level device.
if [ -n "$DM_PART" ] ; then if [ -n "$DM_PART" ] ; then
link=`echo $link | sed 's/p[0-9][0-9]*$//'` link=$(echo "$link" | sed 's/p[0-9][0-9]*$//')
fi fi
# Check both the fully qualified and the base name of link. # Check both the fully qualified and the base name of link.
for l in $link `basename $link` ; do for l in $link $(basename "$link") ; do
alias=`awk "\\$1 == \"alias\" && \\$3 == \"${l}\" \ if [ ! -z "$l" ]; then
{ print \\$2; exit }" $CONFIG` alias=$(awk -v var="$l" '($1 == "alias") && \
if [ -n "$alias" ] ; then ($3 == var) \
echo ${alias}${DM_PART} { print $2; exit }' $CONFIG)
return if [ -n "$alias" ] ; then
echo "${alias}${DM_PART}"
return
fi
fi fi
done done
done done
} }
while getopts 'c:d:eg:mp:h' OPTION; do # main
while getopts 'c:d:eg:jmp:h' OPTION; do
case ${OPTION} in case ${OPTION} in
c) c)
CONFIG=${OPTARG} CONFIG=${OPTARG}
@ -525,7 +702,9 @@ while getopts 'c:d:eg:mp:h' OPTION; do
# create the enclosure device symlinks only. We also need # create the enclosure device symlinks only. We also need
# "enclosure_symlinks yes" set in vdev_id.config to actually create the # "enclosure_symlinks yes" set in vdev_id.config to actually create the
# symlink. # symlink.
ENCLOSURE_MODE=$(awk '{if ($1 == "enclosure_symlinks") print $2}' $CONFIG) ENCLOSURE_MODE=$(awk '{if ($1 == "enclosure_symlinks") \
print $2}' "$CONFIG")
if [ "$ENCLOSURE_MODE" != "yes" ] ; then if [ "$ENCLOSURE_MODE" != "yes" ] ; then
exit 0 exit 0
fi fi
@ -536,6 +715,9 @@ while getopts 'c:d:eg:mp:h' OPTION; do
p) p)
PHYS_PER_PORT=${OPTARG} PHYS_PER_PORT=${OPTARG}
;; ;;
j)
MULTIJBOD_MODE=yes
;;
m) m)
MULTIPATH_MODE=yes MULTIPATH_MODE=yes
;; ;;
@ -545,34 +727,35 @@ while getopts 'c:d:eg:mp:h' OPTION; do
esac esac
done done
if [ ! -r $CONFIG ] ; then if [ ! -r "$CONFIG" ] ; then
exit 0 echo "Error: Config file \"$CONFIG\" not found"
exit 1
fi fi
if [ -z "$DEV" -a -z "$ENCLOSURE_MODE" ] ; then if [ -z "$DEV" ] && [ -z "$ENCLOSURE_MODE" ] ; then
echo "Error: missing required option -d" echo "Error: missing required option -d"
exit 1 exit 1
fi fi
if [ -z "$TOPOLOGY" ] ; then if [ -z "$TOPOLOGY" ] ; then
TOPOLOGY=`awk "\\$1 == \"topology\" {print \\$2; exit}" $CONFIG` TOPOLOGY=$(awk '($1 == "topology") {print $2; exit}' "$CONFIG")
fi fi
if [ -z "$BAY" ] ; then if [ -z "$BAY" ] ; then
BAY=`awk "\\$1 == \"slot\" {print \\$2; exit}" $CONFIG` BAY=$(awk '($1 == "slot") {print $2; exit}' "$CONFIG")
fi fi
TOPOLOGY=${TOPOLOGY:-sas_direct} TOPOLOGY=${TOPOLOGY:-sas_direct}
# Should we create /dev/by-enclosure symlinks? # Should we create /dev/by-enclosure symlinks?
if [ "$ENCLOSURE_MODE" = "yes" -a "$TOPOLOGY" = "sas_direct" ] ; then if [ "$ENCLOSURE_MODE" = "yes" ] && [ "$TOPOLOGY" = "sas_direct" ] ; then
ID_ENCLOSURE=$(enclosure_handler) ID_ENCLOSURE=$(enclosure_handler)
if [ -z "$ID_ENCLOSURE" ] ; then if [ -z "$ID_ENCLOSURE" ] ; then
exit 0 exit 0
fi fi
# Just create the symlinks to the enclosure devices and then exit. # Just create the symlinks to the enclosure devices and then exit.
ENCLOSURE_PREFIX=$(awk '/enclosure_symlinks_prefix/{print $2}' $CONFIG) ENCLOSURE_PREFIX=$(awk '/enclosure_symlinks_prefix/{print $2}' "$CONFIG")
if [ -z "$ENCLOSURE_PREFIX" ] ; then if [ -z "$ENCLOSURE_PREFIX" ] ; then
ENCLOSURE_PREFIX="enc" ENCLOSURE_PREFIX="enc"
fi fi
@ -582,16 +765,16 @@ if [ "$ENCLOSURE_MODE" = "yes" -a "$TOPOLOGY" = "sas_direct" ] ; then
fi fi
# First check if an alias was defined for this device. # First check if an alias was defined for this device.
ID_VDEV=`alias_handler` ID_VDEV=$(alias_handler)
if [ -z "$ID_VDEV" ] ; then if [ -z "$ID_VDEV" ] ; then
BAY=${BAY:-bay} BAY=${BAY:-bay}
case $TOPOLOGY in case $TOPOLOGY in
sas_direct|sas_switch) sas_direct|sas_switch)
ID_VDEV=`sas_handler` ID_VDEV=$(sas_handler)
;; ;;
scsi) scsi)
ID_VDEV=`scsi_handler` ID_VDEV=$(scsi_handler)
;; ;;
*) *)
echo "Error: unknown topology $TOPOLOGY" echo "Error: unknown topology $TOPOLOGY"

View File

@ -1,11 +1,7 @@
include $(top_srcdir)/config/Rules.am include $(top_srcdir)/config/Rules.am
# Unconditionally enable debugging for zdb # Unconditionally enable debugging for zdb
AM_CPPFLAGS += -DDEBUG -UNDEBUG AM_CPPFLAGS += -DDEBUG -UNDEBUG -DZFS_DEBUG
DEFAULT_INCLUDES += \
-I$(top_srcdir)/include \
-I$(top_srcdir)/lib/libspl/include
sbin_PROGRAMS = zdb sbin_PROGRAMS = zdb
@ -15,5 +11,8 @@ zdb_SOURCES = \
zdb.h zdb.h
zdb_LDADD = \ zdb_LDADD = \
$(top_builddir)/lib/libnvpair/libnvpair.la \ $(abs_top_builddir)/lib/libzpool/libzpool.la \
$(top_builddir)/lib/libzpool/libzpool.la $(abs_top_builddir)/lib/libzfs_core/libzfs_core.la \
$(abs_top_builddir)/lib/libnvpair/libnvpair.la
include $(top_srcdir)/config/CppCheck.am

File diff suppressed because it is too large Load Diff

View File

@ -62,9 +62,9 @@ print_log_bp(const blkptr_t *bp, const char *prefix)
/* ARGSUSED */ /* ARGSUSED */
static void static void
zil_prt_rec_create(zilog_t *zilog, int txtype, void *arg) zil_prt_rec_create(zilog_t *zilog, int txtype, const void *arg)
{ {
lr_create_t *lr = arg; const lr_create_t *lr = arg;
time_t crtime = lr->lr_crtime[0]; time_t crtime = lr->lr_crtime[0];
char *name, *link; char *name, *link;
lr_attr_t *lrattr; lr_attr_t *lrattr;
@ -98,9 +98,9 @@ zil_prt_rec_create(zilog_t *zilog, int txtype, void *arg)
/* ARGSUSED */ /* ARGSUSED */
static void static void
zil_prt_rec_remove(zilog_t *zilog, int txtype, void *arg) zil_prt_rec_remove(zilog_t *zilog, int txtype, const void *arg)
{ {
lr_remove_t *lr = arg; const lr_remove_t *lr = arg;
(void) printf("%sdoid %llu, name %s\n", tab_prefix, (void) printf("%sdoid %llu, name %s\n", tab_prefix,
(u_longlong_t)lr->lr_doid, (char *)(lr + 1)); (u_longlong_t)lr->lr_doid, (char *)(lr + 1));
@ -108,9 +108,9 @@ zil_prt_rec_remove(zilog_t *zilog, int txtype, void *arg)
/* ARGSUSED */ /* ARGSUSED */
static void static void
zil_prt_rec_link(zilog_t *zilog, int txtype, void *arg) zil_prt_rec_link(zilog_t *zilog, int txtype, const void *arg)
{ {
lr_link_t *lr = arg; 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", tab_prefix,
(u_longlong_t)lr->lr_doid, (u_longlong_t)lr->lr_link_obj, (u_longlong_t)lr->lr_doid, (u_longlong_t)lr->lr_link_obj,
@ -119,9 +119,9 @@ zil_prt_rec_link(zilog_t *zilog, int txtype, void *arg)
/* ARGSUSED */ /* ARGSUSED */
static void static void
zil_prt_rec_rename(zilog_t *zilog, int txtype, void *arg) zil_prt_rec_rename(zilog_t *zilog, int txtype, const void *arg)
{ {
lr_rename_t *lr = arg; const lr_rename_t *lr = arg;
char *snm = (char *)(lr + 1); char *snm = (char *)(lr + 1);
char *tnm = snm + strlen(snm) + 1; char *tnm = snm + strlen(snm) + 1;
@ -148,11 +148,11 @@ zil_prt_rec_write_cb(void *data, size_t len, void *unused)
/* ARGSUSED */ /* ARGSUSED */
static void static void
zil_prt_rec_write(zilog_t *zilog, int txtype, void *arg) zil_prt_rec_write(zilog_t *zilog, int txtype, const void *arg)
{ {
lr_write_t *lr = arg; const lr_write_t *lr = arg;
abd_t *data; abd_t *data;
blkptr_t *bp = &lr->lr_blkptr; const blkptr_t *bp = &lr->lr_blkptr;
zbookmark_phys_t zb; zbookmark_phys_t zb;
int verbose = MAX(dump_opt['d'], dump_opt['i']); int verbose = MAX(dump_opt['d'], dump_opt['i']);
int error; int error;
@ -211,9 +211,9 @@ out:
/* ARGSUSED */ /* ARGSUSED */
static void static void
zil_prt_rec_truncate(zilog_t *zilog, int txtype, void *arg) zil_prt_rec_truncate(zilog_t *zilog, int txtype, const void *arg)
{ {
lr_truncate_t *lr = arg; 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", tab_prefix,
(u_longlong_t)lr->lr_foid, (longlong_t)lr->lr_offset, (u_longlong_t)lr->lr_foid, (longlong_t)lr->lr_offset,
@ -222,9 +222,9 @@ zil_prt_rec_truncate(zilog_t *zilog, int txtype, void *arg)
/* ARGSUSED */ /* ARGSUSED */
static void static void
zil_prt_rec_setattr(zilog_t *zilog, int txtype, void *arg) zil_prt_rec_setattr(zilog_t *zilog, int txtype, const void *arg)
{ {
lr_setattr_t *lr = arg; const lr_setattr_t *lr = arg;
time_t atime = (time_t)lr->lr_atime[0]; time_t atime = (time_t)lr->lr_atime[0];
time_t mtime = (time_t)lr->lr_mtime[0]; time_t mtime = (time_t)lr->lr_mtime[0];
@ -268,15 +268,15 @@ zil_prt_rec_setattr(zilog_t *zilog, int txtype, void *arg)
/* ARGSUSED */ /* ARGSUSED */
static void static void
zil_prt_rec_acl(zilog_t *zilog, int txtype, void *arg) zil_prt_rec_acl(zilog_t *zilog, int txtype, const void *arg)
{ {
lr_acl_t *lr = arg; const lr_acl_t *lr = arg;
(void) printf("%sfoid %llu, aclcnt %llu\n", tab_prefix, (void) printf("%sfoid %llu, aclcnt %llu\n", tab_prefix,
(u_longlong_t)lr->lr_foid, (u_longlong_t)lr->lr_aclcnt); (u_longlong_t)lr->lr_foid, (u_longlong_t)lr->lr_aclcnt);
} }
typedef void (*zil_prt_rec_func_t)(zilog_t *, int, void *); typedef void (*zil_prt_rec_func_t)(zilog_t *, int, const void *);
typedef struct zil_rec_info { typedef struct zil_rec_info {
zil_prt_rec_func_t zri_print; zil_prt_rec_func_t zri_print;
const char *zri_name; const char *zri_name;
@ -309,7 +309,7 @@ static zil_rec_info_t zil_rec_info[TX_MAX_TYPE] = {
/* ARGSUSED */ /* ARGSUSED */
static int static int
print_log_record(zilog_t *zilog, lr_t *lr, void *arg, uint64_t claim_txg) print_log_record(zilog_t *zilog, const lr_t *lr, void *arg, uint64_t claim_txg)
{ {
int txtype; int txtype;
int verbose = MAX(dump_opt['d'], dump_opt['i']); int verbose = MAX(dump_opt['d'], dump_opt['i']);
@ -343,7 +343,8 @@ print_log_record(zilog_t *zilog, lr_t *lr, void *arg, uint64_t claim_txg)
/* ARGSUSED */ /* ARGSUSED */
static int static int
print_log_block(zilog_t *zilog, blkptr_t *bp, void *arg, uint64_t claim_txg) print_log_block(zilog_t *zilog, const blkptr_t *bp, void *arg,
uint64_t claim_txg)
{ {
char blkbuf[BP_SPRINTF_LEN + 10]; char blkbuf[BP_SPRINTF_LEN + 10];
int verbose = MAX(dump_opt['d'], dump_opt['i']); int verbose = MAX(dump_opt['d'], dump_opt['i']);

View File

@ -1,11 +1,10 @@
include $(top_srcdir)/config/Rules.am include $(top_srcdir)/config/Rules.am
include $(top_srcdir)/config/Shellcheck.am
DEFAULT_INCLUDES += \ AM_CFLAGS += $(LIBUDEV_CFLAGS) $(LIBUUID_CFLAGS)
-I$(top_srcdir)/include \
-I$(top_srcdir)/lib/libspl/include
EXTRA_DIST = zed.d/README \ SUBDIRS = zed.d
zed.d/history_event-zfs-list-cacher.sh.in SHELLCHECKDIRS = $(SUBDIRS)
sbin_PROGRAMS = zed sbin_PROGRAMS = zed
@ -41,61 +40,14 @@ FMA_SRC = \
zed_SOURCES = $(ZED_SRC) $(FMA_SRC) zed_SOURCES = $(ZED_SRC) $(FMA_SRC)
zed_LDADD = \ zed_LDADD = \
$(top_builddir)/lib/libnvpair/libnvpair.la \ $(abs_top_builddir)/lib/libzfs/libzfs.la \
$(top_builddir)/lib/libuutil/libuutil.la \ $(abs_top_builddir)/lib/libzfs_core/libzfs_core.la \
$(top_builddir)/lib/libzfs/libzfs.la $(abs_top_builddir)/lib/libnvpair/libnvpair.la \
$(abs_top_builddir)/lib/libuutil/libuutil.la
zed_LDADD += -lrt zed_LDADD += -lrt $(LIBATOMIC_LIBS) $(LIBUDEV_LIBS) $(LIBUUID_LIBS)
zed_LDFLAGS = -pthread zed_LDFLAGS = -pthread
zedconfdir = $(sysconfdir)/zfs/zed.d EXTRA_DIST = agents/README.md
dist_zedconf_DATA = \ include $(top_srcdir)/config/CppCheck.am
zed.d/zed-functions.sh \
zed.d/zed.rc
zedexecdir = $(zfsexecdir)/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
nodist_zedexec_SCRIPTS = zed.d/history_event-zfs-list-cacher.sh
$(nodist_zedexec_SCRIPTS): %: %.in
-$(SED) -e 's,@bindir\@,$(bindir),g' \
-e 's,@runstatedir\@,$(runstatedir),g' \
-e 's,@sbindir\@,$(sbindir),g' \
-e 's,@sysconfdir\@,$(sysconfdir),g' \
$< >'$@'
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"

View File

@ -25,7 +25,7 @@
*/ */
/* /*
* This file imlements the minimal FMD module API required to support the * This file implements the minimal FMD module API required to support the
* fault logic modules in ZED. This support includes module registration, * fault logic modules in ZED. This support includes module registration,
* memory allocation, module property accessors, basic case management, * memory allocation, module property accessors, basic case management,
* one-shot timers and SERD engines. * one-shot timers and SERD engines.

View File

@ -281,7 +281,7 @@ fmd_serd_eng_empty(fmd_serd_eng_t *sgp)
void void
fmd_serd_eng_reset(fmd_serd_eng_t *sgp) fmd_serd_eng_reset(fmd_serd_eng_t *sgp)
{ {
serd_log_msg(" SERD Engine: reseting %s", sgp->sg_name); serd_log_msg(" SERD Engine: resetting %s", sgp->sg_name);
while (sgp->sg_count != 0) while (sgp->sg_count != 0)
fmd_serd_eng_discard(sgp, list_head(&sgp->sg_list)); fmd_serd_eng_discard(sgp, list_head(&sgp->sg_list));

View File

@ -13,6 +13,7 @@
/* /*
* Copyright (c) 2016, Intel Corporation. * Copyright (c) 2016, Intel Corporation.
* Copyright (c) 2018, loli10K <ezomori.nozomu@gmail.com> * Copyright (c) 2018, loli10K <ezomori.nozomu@gmail.com>
* Copyright (c) 2021 Hewlett Packard Enterprise Development LP
*/ */
#include <libnvpair.h> #include <libnvpair.h>
@ -116,7 +117,8 @@ zfs_agent_iter_vdev(zpool_handle_t *zhp, nvlist_t *nvl, void *arg)
/* /*
* On a devid match, grab the vdev guid and expansion time, if any. * On a devid match, grab the vdev guid and expansion time, if any.
*/ */
if ((nvlist_lookup_string(nvl, ZPOOL_CONFIG_DEVID, &path) == 0) && if (gsp->gs_devid != NULL &&
(nvlist_lookup_string(nvl, ZPOOL_CONFIG_DEVID, &path) == 0) &&
(strcmp(gsp->gs_devid, path) == 0)) { (strcmp(gsp->gs_devid, path) == 0)) {
(void) nvlist_lookup_uint64(nvl, ZPOOL_CONFIG_GUID, (void) nvlist_lookup_uint64(nvl, ZPOOL_CONFIG_GUID,
&gsp->gs_vdev_guid); &gsp->gs_vdev_guid);
@ -176,10 +178,12 @@ zfs_agent_post_event(const char *class, const char *subclass, nvlist_t *nvl)
} }
/* /*
* On ZFS on Linux, we don't get the expected FM_RESOURCE_REMOVED * On Linux, we don't get the expected FM_RESOURCE_REMOVED ereport
* ereport from vdev_disk layer after a hot unplug. Fortunately we * from the vdev_disk layer after a hot unplug. Fortunately we do
* get a EC_DEV_REMOVE from our disk monitor and it is a suitable * get an 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. * 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) && if ((strcmp(class, EC_DEV_REMOVE) == 0) &&
(strcmp(subclass, ESC_DISK) == 0) && (strcmp(subclass, ESC_DISK) == 0) &&
@ -208,12 +212,18 @@ zfs_agent_post_event(const char *class, const char *subclass, nvlist_t *nvl)
* For multipath, spare and l2arc devices ZFS_EV_VDEV_GUID or * For multipath, spare and l2arc devices ZFS_EV_VDEV_GUID or
* ZFS_EV_POOL_GUID may be missing so find them. * ZFS_EV_POOL_GUID may be missing so find them.
*/ */
(void) nvlist_lookup_string(nvl, DEV_IDENTIFIER, if (pool_guid == 0 || vdev_guid == 0) {
&search.gs_devid); if ((nvlist_lookup_string(nvl, DEV_IDENTIFIER,
(void) zpool_iter(g_zfs_hdl, zfs_agent_iter_pool, &search); &search.gs_devid) == 0) &&
pool_guid = search.gs_pool_guid; (zpool_iter(g_zfs_hdl, zfs_agent_iter_pool, &search)
vdev_guid = search.gs_vdev_guid; == 1)) {
devtype = search.gs_vdev_type; 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;
}
}
/* /*
* We want to avoid reporting "remove" events coming from * We want to avoid reporting "remove" events coming from
@ -382,6 +392,7 @@ zfs_agent_init(libzfs_handle_t *zfs_hdl)
list_destroy(&agent_events); list_destroy(&agent_events);
zed_log_die("Failed to initialize agents"); zed_log_die("Failed to initialize agents");
} }
pthread_setname_np(g_agents_tid, "agents");
} }
void void

View File

@ -63,13 +63,10 @@
* If the device could not be replaced, then the second online attempt will * If the device could not be replaced, then the second online attempt will
* trigger the FMA fault that we skipped earlier. * trigger the FMA fault that we skipped earlier.
* *
* ZFS on Linux porting notes: * On Linux udev provides a disk insert for both the disk and the partition.
* Linux udev provides a disk insert for both the disk and the partition
*
*/ */
#include <ctype.h> #include <ctype.h>
#include <devid.h>
#include <fcntl.h> #include <fcntl.h>
#include <libnvpair.h> #include <libnvpair.h>
#include <libzfs.h> #include <libzfs.h>
@ -157,7 +154,7 @@ zfs_unavail_pool(zpool_handle_t *zhp, void *data)
* 1. physical match with no fs, no partition * 1. physical match with no fs, no partition
* tag it top, partition disk * tag it top, partition disk
* *
* 2. physical match again, see partion and tag * 2. physical match again, see partition and tag
* *
*/ */
@ -192,8 +189,8 @@ zfs_process_add(zpool_handle_t *zhp, nvlist_t *vdev, boolean_t labeled)
char rawpath[PATH_MAX], fullpath[PATH_MAX]; char rawpath[PATH_MAX], fullpath[PATH_MAX];
char devpath[PATH_MAX]; char devpath[PATH_MAX];
int ret; int ret;
int is_dm = 0; boolean_t is_dm = B_FALSE;
int is_sd = 0; boolean_t is_sd = B_FALSE;
uint_t c; uint_t c;
vdev_stat_t *vs; vdev_stat_t *vs;
@ -221,8 +218,8 @@ zfs_process_add(zpool_handle_t *zhp, nvlist_t *vdev, boolean_t labeled)
is_dm = zfs_dev_is_dm(path); is_dm = zfs_dev_is_dm(path);
zed_log_msg(LOG_INFO, "zfs_process_add: pool '%s' vdev '%s', phys '%s'" zed_log_msg(LOG_INFO, "zfs_process_add: pool '%s' vdev '%s', phys '%s'"
" wholedisk %d, dm %d (%llu)", zpool_get_name(zhp), path, " wholedisk %d, %s dm (guid %llu)", zpool_get_name(zhp), path,
physpath ? physpath : "NULL", wholedisk, is_dm, physpath ? physpath : "NULL", wholedisk, is_dm ? "is" : "not",
(long long unsigned int)guid); (long long unsigned int)guid);
/* /*
@ -267,7 +264,7 @@ zfs_process_add(zpool_handle_t *zhp, nvlist_t *vdev, boolean_t labeled)
* testing) * testing)
*/ */
if (physpath != NULL && strcmp("scsidebug", physpath) == 0) if (physpath != NULL && strcmp("scsidebug", physpath) == 0)
is_sd = 1; is_sd = B_TRUE;
/* /*
* If the pool doesn't have the autoreplace property set, then use * If the pool doesn't have the autoreplace property set, then use
@ -438,7 +435,15 @@ zfs_process_add(zpool_handle_t *zhp, nvlist_t *vdev, boolean_t labeled)
return; return;
} }
ret = zpool_vdev_attach(zhp, fullpath, path, nvroot, B_TRUE); /*
* Prefer sequential resilvering when supported (mirrors and dRAID),
* otherwise fallback to a traditional healing resilver.
*/
ret = zpool_vdev_attach(zhp, fullpath, path, nvroot, B_TRUE, B_TRUE);
if (ret != 0) {
ret = zpool_vdev_attach(zhp, fullpath, path, nvroot,
B_TRUE, B_FALSE);
}
zed_log_msg(LOG_INFO, " zpool_vdev_replace: %s with %s (%s)", zed_log_msg(LOG_INFO, " zpool_vdev_replace: %s with %s (%s)",
fullpath, path, (ret == 0) ? "no errors" : fullpath, path, (ret == 0) ? "no errors" :
@ -534,7 +539,7 @@ zfs_iter_vdev(zpool_handle_t *zhp, nvlist_t *nvl, void *data)
(dp->dd_func)(zhp, nvl, dp->dd_islabeled); (dp->dd_func)(zhp, nvl, dp->dd_islabeled);
} }
void static void
zfs_enable_ds(void *arg) zfs_enable_ds(void *arg)
{ {
unavailpool_t *pool = (unavailpool_t *)arg; unavailpool_t *pool = (unavailpool_t *)arg;
@ -635,6 +640,27 @@ devid_iter(const char *devid, zfs_process_func_t func, boolean_t is_slice)
return (data.dd_found); return (data.dd_found);
} }
/*
* Given a device guid, find any vdevs with a matching guid.
*/
static boolean_t
guid_iter(uint64_t pool_guid, uint64_t vdev_guid, const char *devid,
zfs_process_func_t func, boolean_t is_slice)
{
dev_data_t data = { 0 };
data.dd_func = func;
data.dd_found = B_FALSE;
data.dd_pool_guid = pool_guid;
data.dd_vdev_guid = vdev_guid;
data.dd_islabeled = is_slice;
data.dd_new_devid = devid;
(void) zpool_iter(g_zfshdl, zfs_iter_pool, &data);
return (data.dd_found);
}
/* /*
* Handle a EC_DEV_ADD.ESC_DISK event. * Handle a EC_DEV_ADD.ESC_DISK event.
* *
@ -658,15 +684,18 @@ static int
zfs_deliver_add(nvlist_t *nvl, boolean_t is_lofi) zfs_deliver_add(nvlist_t *nvl, boolean_t is_lofi)
{ {
char *devpath = NULL, *devid; char *devpath = NULL, *devid;
uint64_t pool_guid = 0, vdev_guid = 0;
boolean_t is_slice; boolean_t is_slice;
/* /*
* Expecting a devid string and an optional physical location * Expecting a devid string and an optional physical location and guid
*/ */
if (nvlist_lookup_string(nvl, DEV_IDENTIFIER, &devid) != 0) if (nvlist_lookup_string(nvl, DEV_IDENTIFIER, &devid) != 0)
return (-1); return (-1);
(void) nvlist_lookup_string(nvl, DEV_PHYS_PATH, &devpath); (void) nvlist_lookup_string(nvl, DEV_PHYS_PATH, &devpath);
(void) nvlist_lookup_uint64(nvl, ZFS_EV_POOL_GUID, &pool_guid);
(void) nvlist_lookup_uint64(nvl, ZFS_EV_VDEV_GUID, &vdev_guid);
is_slice = (nvlist_lookup_boolean(nvl, DEV_IS_PART) == 0); is_slice = (nvlist_lookup_boolean(nvl, DEV_IS_PART) == 0);
@ -674,15 +703,19 @@ zfs_deliver_add(nvlist_t *nvl, boolean_t is_lofi)
devid, devpath ? devpath : "NULL", is_slice); devid, devpath ? devpath : "NULL", is_slice);
/* /*
* Iterate over all vdevs looking for a match in the folllowing order: * Iterate over all vdevs looking for a match in the following order:
* 1. ZPOOL_CONFIG_DEVID (identifies the unique disk) * 1. ZPOOL_CONFIG_DEVID (identifies the unique disk)
* 2. ZPOOL_CONFIG_PHYS_PATH (identifies disk physical location). * 2. ZPOOL_CONFIG_PHYS_PATH (identifies disk physical location).
* * 3. ZPOOL_CONFIG_GUID (identifies unique vdev).
* For disks, we only want to pay attention to vdevs marked as whole
* disks or are a multipath device.
*/ */
if (!devid_iter(devid, zfs_process_add, is_slice) && devpath != NULL) if (devid_iter(devid, zfs_process_add, is_slice))
(void) devphys_iter(devpath, devid, zfs_process_add, is_slice); return (0);
if (devpath != NULL && devphys_iter(devpath, devid, zfs_process_add,
is_slice))
return (0);
if (vdev_guid != 0)
(void) guid_iter(pool_guid, vdev_guid, devid, zfs_process_add,
is_slice);
return (0); return (0);
} }
@ -892,7 +925,7 @@ zfs_enum_pools(void *arg)
* *
* sent messages from zevents or udev monitor * sent messages from zevents or udev monitor
* *
* For now, each agent has it's own libzfs instance * For now, each agent has its own libzfs instance
*/ */
int int
zfs_slm_init() zfs_slm_init()
@ -913,6 +946,7 @@ zfs_slm_init()
return (-1); return (-1);
} }
pthread_setname_np(g_zfs_tid, "enum-pools");
list_create(&g_device_list, sizeof (struct pendingdev), list_create(&g_device_list, sizeof (struct pendingdev),
offsetof(struct pendingdev, pd_node)); offsetof(struct pendingdev, pd_node));

View File

@ -38,8 +38,10 @@
#include <sys/fs/zfs.h> #include <sys/fs/zfs.h>
#include <sys/fm/protocol.h> #include <sys/fm/protocol.h>
#include <sys/fm/fs/zfs.h> #include <sys/fm/fs/zfs.h>
#include <libzutil.h>
#include <libzfs.h> #include <libzfs.h>
#include <string.h> #include <string.h>
#include <libgen.h>
#include "zfs_agents.h" #include "zfs_agents.h"
#include "fmd_api.h" #include "fmd_api.h"
@ -219,12 +221,18 @@ replace_with_spare(fmd_hdl_t *hdl, zpool_handle_t *zhp, nvlist_t *vdev)
* replace it. * replace it.
*/ */
for (s = 0; s < nspares; s++) { for (s = 0; s < nspares; s++) {
char *spare_name; boolean_t rebuild = B_FALSE;
char *spare_name, *type;
if (nvlist_lookup_string(spares[s], ZPOOL_CONFIG_PATH, if (nvlist_lookup_string(spares[s], ZPOOL_CONFIG_PATH,
&spare_name) != 0) &spare_name) != 0)
continue; 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 set, add the "ashift" pool property to the spare nvlist */
if (source != ZPROP_SRC_DEFAULT) if (source != ZPROP_SRC_DEFAULT)
(void) nvlist_add_uint64(spares[s], (void) nvlist_add_uint64(spares[s],
@ -234,10 +242,10 @@ replace_with_spare(fmd_hdl_t *hdl, zpool_handle_t *zhp, nvlist_t *vdev)
ZPOOL_CONFIG_CHILDREN, &spares[s], 1); ZPOOL_CONFIG_CHILDREN, &spares[s], 1);
fmd_hdl_debug(hdl, "zpool_vdev_replace '%s' with spare '%s'", fmd_hdl_debug(hdl, "zpool_vdev_replace '%s' with spare '%s'",
dev_name, basename(spare_name)); dev_name, zfs_basename(spare_name));
if (zpool_vdev_attach(zhp, dev_name, spare_name, if (zpool_vdev_attach(zhp, dev_name, spare_name,
replacement, B_TRUE) == 0) { replacement, B_TRUE, rebuild) == 0) {
free(dev_name); free(dev_name);
nvlist_free(replacement); nvlist_free(replacement);
return (B_TRUE); return (B_TRUE);
@ -319,12 +327,16 @@ zfs_retire_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl,
fmd_hdl_debug(hdl, "zfs_retire_recv: '%s'", class); fmd_hdl_debug(hdl, "zfs_retire_recv: '%s'", class);
nvlist_lookup_uint64(nvl, FM_EREPORT_PAYLOAD_ZFS_VDEV_STATE, &state);
/* /*
* If this is a resource notifying us of device removal then simply * If this is a resource notifying us of device removal then simply
* check for an available spare and continue unless the device is a * check for an available spare and continue unless the device is a
* l2arc vdev, in which case we just offline it. * l2arc vdev, in which case we just offline it.
*/ */
if (strcmp(class, "resource.fs.zfs.removed") == 0) { if (strcmp(class, "resource.fs.zfs.removed") == 0 ||
(strcmp(class, "resource.fs.zfs.statechange") == 0 &&
(state == VDEV_STATE_REMOVED || state == VDEV_STATE_FAULTED))) {
char *devtype; char *devtype;
char *devname; char *devname;
@ -347,9 +359,8 @@ zfs_retire_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl,
zpool_vdev_offline(zhp, devname, B_TRUE); zpool_vdev_offline(zhp, devname, B_TRUE);
} else if (!fmd_prop_get_int32(hdl, "spare_on_remove") || } else if (!fmd_prop_get_int32(hdl, "spare_on_remove") ||
replace_with_spare(hdl, zhp, vdev) == B_FALSE) { replace_with_spare(hdl, zhp, vdev) == B_FALSE) {
/* Could not handle with spare: offline the device */ /* Could not handle with spare */
fmd_hdl_debug(hdl, "zpool_vdev_offline '%s'", devname); fmd_hdl_debug(hdl, "no spare for '%s'", devname);
zpool_vdev_offline(zhp, devname, B_TRUE);
} }
free(devname); free(devname);
@ -361,12 +372,11 @@ zfs_retire_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl,
return; return;
/* /*
* Note: on zfsonlinux statechange events are more than just * Note: on Linux statechange events are more than just
* healthy ones so we need to confirm the actual state value. * healthy ones so we need to confirm the actual state value.
*/ */
if (strcmp(class, "resource.fs.zfs.statechange") == 0 && if (strcmp(class, "resource.fs.zfs.statechange") == 0 &&
nvlist_lookup_uint64(nvl, FM_EREPORT_PAYLOAD_ZFS_VDEV_STATE, state == VDEV_STATE_HEALTHY) {
&state) == 0 && state == VDEV_STATE_HEALTHY) {
zfs_vdev_repair(hdl, nvl); zfs_vdev_repair(hdl, nvl);
return; return;
} }
@ -497,6 +507,7 @@ zfs_retire_recv(fmd_hdl_t *hdl, fmd_event_t *ep, nvlist_t *nvl,
* Attempt to substitute a hot spare. * Attempt to substitute a hot spare.
*/ */
(void) replace_with_spare(hdl, zhp, vdev); (void) replace_with_spare(hdl, zhp, vdev);
zpool_close(zhp); zpool_close(zhp);
} }

View File

@ -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). * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC. * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
* Refer to the ZoL git commit log for authoritative copyright attribution. * Refer to the OpenZFS git commit log for authoritative copyright attribution.
* *
* The contents of this file are subject to the terms of the * The contents of this file are subject to the terms of the
* Common Development and Distribution License Version 1.0 (CDDL-1.0). * Common Development and Distribution License Version 1.0 (CDDL-1.0).
@ -60,8 +60,8 @@ _setup_sig_handlers(void)
zed_log_die("Failed to initialize sigset"); zed_log_die("Failed to initialize sigset");
sa.sa_flags = SA_RESTART; sa.sa_flags = SA_RESTART;
sa.sa_handler = SIG_IGN;
sa.sa_handler = SIG_IGN;
if (sigaction(SIGPIPE, &sa, NULL) < 0) if (sigaction(SIGPIPE, &sa, NULL) < 0)
zed_log_die("Failed to ignore SIGPIPE"); zed_log_die("Failed to ignore SIGPIPE");
@ -75,6 +75,10 @@ _setup_sig_handlers(void)
sa.sa_handler = _hup_handler; sa.sa_handler = _hup_handler;
if (sigaction(SIGHUP, &sa, NULL) < 0) if (sigaction(SIGHUP, &sa, NULL) < 0)
zed_log_die("Failed to register SIGHUP handler"); 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");
} }
/* /*
@ -212,22 +216,20 @@ _finish_daemonize(void)
int int
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
struct zed_conf *zcp; struct zed_conf zcp;
uint64_t saved_eid; uint64_t saved_eid;
int64_t saved_etime[2]; int64_t saved_etime[2];
zed_log_init(argv[0]); zed_log_init(argv[0]);
zed_log_stderr_open(LOG_NOTICE); zed_log_stderr_open(LOG_NOTICE);
zcp = zed_conf_create(); zed_conf_init(&zcp);
zed_conf_parse_opts(zcp, argc, argv); zed_conf_parse_opts(&zcp, argc, argv);
if (zcp->do_verbose) if (zcp.do_verbose)
zed_log_stderr_open(LOG_INFO); zed_log_stderr_open(LOG_INFO);
if (geteuid() != 0) if (geteuid() != 0)
zed_log_die("Must be run as root"); zed_log_die("Must be run as root");
zed_conf_parse_file(zcp);
zed_file_close_from(STDERR_FILENO + 1); zed_file_close_from(STDERR_FILENO + 1);
(void) umask(0); (void) umask(0);
@ -235,47 +237,72 @@ main(int argc, char *argv[])
if (chdir("/") < 0) if (chdir("/") < 0)
zed_log_die("Failed to change to root directory"); 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); exit(EXIT_FAILURE);
if (!zcp->do_foreground) { if (!zcp.do_foreground) {
_start_daemonize(); _start_daemonize();
zed_log_syslog_open(LOG_DAEMON); zed_log_syslog_open(LOG_DAEMON);
} }
_setup_sig_handlers(); _setup_sig_handlers();
if (zcp->do_memlock) if (zcp.do_memlock)
_lock_memory(); _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); exit(EXIT_FAILURE);
if (!zcp->do_foreground) if (!zcp.do_foreground)
_finish_daemonize(); _finish_daemonize();
zed_log_msg(LOG_NOTICE, zed_log_msg(LOG_NOTICE,
"ZFS Event Daemon %s-%s (PID %d)", "ZFS Event Daemon %s-%s (PID %d)",
ZFS_META_VERSION, ZFS_META_RELEASE, (int)getpid()); 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); 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); exit(EXIT_FAILURE);
zed_event_init(zcp); idle:
zed_event_seek(zcp, saved_eid, saved_etime); /*
* 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);
while (!_got_exit) { while (!_got_exit) {
int rv;
if (_got_hup) { if (_got_hup) {
_got_hup = 0; _got_hup = 0;
(void) zed_conf_scan_dir(zcp); (void) zed_conf_scan_dir(&zcp);
} }
zed_event_service(zcp); rv = zed_event_service(&zcp);
/* ENODEV: When kernel module is unloaded (osx) */
if (rv != 0)
break;
} }
zed_log_msg(LOG_NOTICE, "Exiting"); zed_log_msg(LOG_NOTICE, "Exiting");
zed_event_fini(zcp); zed_event_fini(&zcp);
zed_conf_destroy(zcp);
if (zcp.do_idle && !_got_exit)
goto idle;
out:
zed_conf_destroy(&zcp);
zed_log_fini(); zed_log_fini();
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }

57
cmd/zed/zed.d/Makefile.am Normal file
View File

@ -0,0 +1,57 @@
include $(top_srcdir)/config/Rules.am
include $(top_srcdir)/config/Substfiles.am
include $(top_srcdir)/config/Shellcheck.am
EXTRA_DIST += README
zedconfdir = $(sysconfdir)/zfs/zed.d
dist_zedconf_DATA = \
zed-functions.sh \
zed.rc
zedexecdir = $(zfsexecdir)/zed.d
dist_zedexec_SCRIPTS = \
all-debug.sh \
all-syslog.sh \
data-notify.sh \
generic-notify.sh \
resilver_finish-notify.sh \
scrub_finish-notify.sh \
statechange-led.sh \
statechange-notify.sh \
vdev_clear-led.sh \
vdev_attach-led.sh \
pool_import-led.sh \
resilver_finish-start-scrub.sh \
trim_finish-notify.sh
nodist_zedexec_SCRIPTS = history_event-zfs-list-cacher.sh
SUBSTFILES += $(nodist_zedexec_SCRIPTS)
zedconfdefaults = \
all-syslog.sh \
data-notify.sh \
history_event-zfs-list-cacher.sh \
resilver_finish-notify.sh \
scrub_finish-notify.sh \
statechange-led.sh \
statechange-notify.sh \
vdev_clear-led.sh \
vdev_attach-led.sh \
pool_import-led.sh \
resilver_finish-start-scrub.sh
install-data-hook:
$(MKDIR_P) "$(DESTDIR)$(zedconfdir)"
for f in $(zedconfdefaults); do \
test -f "$(DESTDIR)$(zedconfdir)/$${f}" -o \
-L "$(DESTDIR)$(zedconfdir)/$${f}" || \
ln -s "$(zedexecdir)/$${f}" "$(DESTDIR)$(zedconfdir)"; \
done
chmod 0600 "$(DESTDIR)$(zedconfdir)/zed.rc"
# False positive: 1>&"${ZED_FLOCK_FD}" looks suspiciously similar to a >&filename bash extension
CHECKBASHISMS_IGNORE = -e 'should be >word 2>&1' -e '&"$${ZED_FLOCK_FD}"'

View File

@ -12,15 +12,11 @@
zed_exit_if_ignoring_this_event zed_exit_if_ignoring_this_event
lockfile="$(basename -- "${ZED_DEBUG_LOG}").lock" zed_lock "${ZED_DEBUG_LOG}"
{
printenv | sort
echo
} 1>&"${ZED_FLOCK_FD}"
zed_unlock "${ZED_DEBUG_LOG}"
umask 077
zed_lock "${lockfile}"
exec >> "${ZED_DEBUG_LOG}"
printenv | sort
echo
exec >&-
zed_unlock "${lockfile}"
exit 0 exit 0

View File

@ -1,14 +1,51 @@
#!/bin/sh #!/bin/sh
#
# Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
# Copyright (c) 2020 by Delphix. All rights reserved.
#
# #
# Log the zevent via syslog. # Log the zevent via syslog.
#
[ -f "${ZED_ZEDLET_DIR}/zed.rc" ] && . "${ZED_ZEDLET_DIR}/zed.rc" [ -f "${ZED_ZEDLET_DIR}/zed.rc" ] && . "${ZED_ZEDLET_DIR}/zed.rc"
. "${ZED_ZEDLET_DIR}/zed-functions.sh" . "${ZED_ZEDLET_DIR}/zed-functions.sh"
zed_exit_if_ignoring_this_event zed_exit_if_ignoring_this_event
zed_log_msg "eid=${ZEVENT_EID}" "class=${ZEVENT_SUBCLASS}" \ # build a string of name=value pairs for this event
"${ZEVENT_POOL_GUID:+"pool_guid=${ZEVENT_POOL_GUID}"}" \ msg="eid=${ZEVENT_EID} class=${ZEVENT_SUBCLASS}"
"${ZEVENT_VDEV_PATH:+"vdev_path=${ZEVENT_VDEV_PATH}"}" \
"${ZEVENT_VDEV_STATE_STR:+"vdev_state=${ZEVENT_VDEV_STATE_STR}"}" 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=$(basename "${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}"
exit 0 exit 0

View File

@ -25,7 +25,7 @@ zed_rate_limit "${rate_limit_tag}" || exit 3
umask 077 umask 077
note_subject="ZFS ${ZEVENT_SUBCLASS} error for ${ZEVENT_POOL} on $(hostname)" note_subject="ZFS ${ZEVENT_SUBCLASS} error for ${ZEVENT_POOL} on $(hostname)"
note_pathname="${TMPDIR:="/tmp"}/$(basename -- "$0").${ZEVENT_EID}.$$" note_pathname="$(mktemp)"
{ {
echo "ZFS has detected a data error:" echo "ZFS has detected a data error:"
echo echo

View File

@ -31,7 +31,7 @@ umask 077
pool_str="${ZEVENT_POOL:+" for ${ZEVENT_POOL}"}" pool_str="${ZEVENT_POOL:+" for ${ZEVENT_POOL}"}"
host_str=" on $(hostname)" host_str=" on $(hostname)"
note_subject="ZFS ${ZEVENT_SUBCLASS} event${pool_str}${host_str}" note_subject="ZFS ${ZEVENT_SUBCLASS} event${pool_str}${host_str}"
note_pathname="${TMPDIR:="/tmp"}/$(basename -- "$0").${ZEVENT_EID}.$$" note_pathname="$(mktemp)"
{ {
echo "ZFS has posted the following event:" echo "ZFS has posted the following event:"
echo echo

View File

@ -3,9 +3,8 @@
# Track changes to enumerated pools for use in early-boot # Track changes to enumerated pools for use in early-boot
set -ef set -ef
FSLIST_DIR="@sysconfdir@/zfs/zfs-list.cache" FSLIST="@sysconfdir@/zfs/zfs-list.cache/${ZEVENT_POOL}"
FSLIST_TMP="@runstatedir@/zfs-list.cache.new" FSLIST_TMP="@runstatedir@/zfs-list.cache@${ZEVENT_POOL}"
FSLIST="${FSLIST_DIR}/${ZEVENT_POOL}"
# If the pool specific cache file is not writeable, abort # If the pool specific cache file is not writeable, abort
[ -w "${FSLIST}" ] || exit 0 [ -w "${FSLIST}" ] || exit 0
@ -13,21 +12,21 @@ FSLIST="${FSLIST_DIR}/${ZEVENT_POOL}"
[ -f "${ZED_ZEDLET_DIR}/zed.rc" ] && . "${ZED_ZEDLET_DIR}/zed.rc" [ -f "${ZED_ZEDLET_DIR}/zed.rc" ] && . "${ZED_ZEDLET_DIR}/zed.rc"
. "${ZED_ZEDLET_DIR}/zed-functions.sh" . "${ZED_ZEDLET_DIR}/zed-functions.sh"
zed_exit_if_ignoring_this_event [ "$ZEVENT_SUBCLASS" != "history_event" ] && exit 0
zed_check_cmd "${ZFS}" sort diff grep zed_check_cmd "${ZFS}" sort diff
# If we are acting on a snapshot, we have nothing to do # If we are acting on a snapshot, we have nothing to do
printf '%s' "${ZEVENT_HISTORY_DSNAME}" | grep '@' && exit 0 [ "${ZEVENT_HISTORY_DSNAME%@*}" = "${ZEVENT_HISTORY_DSNAME}" ] || exit 0
# We obtain a lock on zfs-list to avoid any simultaneous writes. # We lock the output file to avoid simultaneous writes.
# If we run into trouble, log and drop the lock # If we run into trouble, log and drop the lock
abort_alter() { abort_alter() {
zed_log_msg "Error updating zfs-list.cache!" zed_log_msg "Error updating zfs-list.cache for ${ZEVENT_POOL}!"
zed_unlock zfs-list zed_unlock "${FSLIST}"
} }
finished() { finished() {
zed_unlock zfs-list zed_unlock "${FSLIST}"
trap - EXIT trap - EXIT
exit 0 exit 0
} }
@ -37,7 +36,7 @@ case "${ZEVENT_HISTORY_INTERNAL_NAME}" in
;; ;;
export) export)
zed_lock zfs-list zed_lock "${FSLIST}"
trap abort_alter EXIT trap abort_alter EXIT
echo > "${FSLIST}" echo > "${FSLIST}"
finished finished
@ -46,8 +45,13 @@ case "${ZEVENT_HISTORY_INTERNAL_NAME}" in
set|inherit) set|inherit)
# Only act if one of the tracked properties is altered. # Only act if one of the tracked properties is altered.
case "${ZEVENT_HISTORY_INTERNAL_STR%%=*}" in case "${ZEVENT_HISTORY_INTERNAL_STR%%=*}" in
canmount|mountpoint|atime|relatime|devices|exec| \ canmount|mountpoint|atime|relatime|devices|exec|readonly| \
readonly|setuid|nbmand) ;; 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 ;; *) exit 0 ;;
esac esac
;; ;;
@ -58,11 +62,15 @@ case "${ZEVENT_HISTORY_INTERNAL_NAME}" in
;; ;;
esac esac
zed_lock zfs-list zed_lock "${FSLIST}"
trap abort_alter EXIT trap abort_alter EXIT
PROPS="name,mountpoint,canmount,atime,relatime,devices,exec,readonly" PROPS="name,mountpoint,canmount,atime,relatime,devices,exec\
PROPS="${PROPS},setuid,nbmand" ,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}" "${ZFS}" list -H -t filesystem -o $PROPS -r "${ZEVENT_POOL}" > "${FSLIST_TMP}"
@ -70,7 +78,7 @@ PROPS="${PROPS},setuid,nbmand"
sort "${FSLIST_TMP}" -o "${FSLIST_TMP}" sort "${FSLIST_TMP}" -o "${FSLIST_TMP}"
# Don't modify the file if it hasn't changed # Don't modify the file if it hasn't changed
diff -q "${FSLIST_TMP}" "${FSLIST}" || mv "${FSLIST_TMP}" "${FSLIST}" diff -q "${FSLIST_TMP}" "${FSLIST}" || cat "${FSLIST_TMP}" > "${FSLIST}"
rm -f "${FSLIST_TMP}" rm -f "${FSLIST_TMP}"
finished finished

View File

@ -5,10 +5,12 @@
# Exit codes: # Exit codes:
# 1: Internal error # 1: Internal error
# 2: Script wasn't enabled in zed.rc # 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" [ -f "${ZED_ZEDLET_DIR}/zed.rc" ] && . "${ZED_ZEDLET_DIR}/zed.rc"
. "${ZED_ZEDLET_DIR}/zed-functions.sh" . "${ZED_ZEDLET_DIR}/zed-functions.sh"
[ "${ZED_SCRUB_AFTER_RESILVER}" = "1" ] || exit 2 [ "${ZED_SCRUB_AFTER_RESILVER}" = "1" ] || exit 2
[ "${ZEVENT_RESILVER_TYPE}" != "sequential" ] || exit 3
[ -n "${ZEVENT_POOL}" ] || exit 1 [ -n "${ZEVENT_POOL}" ] || exit 1
[ -n "${ZEVENT_SUBCLASS}" ] || exit 1 [ -n "${ZEVENT_SUBCLASS}" ] || exit 1
zed_check_cmd "${ZPOOL}" || exit 1 zed_check_cmd "${ZPOOL}" || exit 1

View File

@ -41,7 +41,7 @@ fi
umask 077 umask 077
note_subject="ZFS ${ZEVENT_SUBCLASS} event for ${ZEVENT_POOL} on $(hostname)" note_subject="ZFS ${ZEVENT_SUBCLASS} event for ${ZEVENT_POOL} on $(hostname)"
note_pathname="${TMPDIR:="/tmp"}/$(basename -- "$0").${ZEVENT_EID}.$$" note_pathname="$(mktemp)"
{ {
echo "ZFS has finished a ${action}:" echo "ZFS has finished a ${action}:"
echo echo

View File

@ -1,26 +1,26 @@
#!/bin/sh #!/bin/sh
# #
# Turn off/on the VDEV's enclosure fault LEDs when the pool's state changes. # Turn off/on vdevs' enclosure fault LEDs when their pool's state changes.
# #
# Turn the VDEV's fault LED on if it becomes FAULTED, DEGRADED or UNAVAIL. # Turn a vdev's fault LED on if it becomes FAULTED, DEGRADED or UNAVAIL.
# Turn the LED off when it's back ONLINE again. # Turn its LED off when it's back ONLINE again.
# #
# This script run in two basic modes: # This script run in two basic modes:
# #
# 1. If $ZEVENT_VDEV_ENC_SYSFS_PATH and $ZEVENT_VDEV_STATE_STR are set, then # 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. # 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. # 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 # 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. # if you have no enclosure, or if your enclosure isn't supported.
# #
# Exit codes: # Exit codes:
# 0: enclosure led successfully set # 0: enclosure led successfully set
# 1: enclosure leds not not available # 1: enclosure leds not available
# 2: enclosure leds administratively disabled # 2: enclosure leds administratively disabled
# 3: The led sysfs path passed from ZFS does not exist # 3: The led sysfs path passed from ZFS does not exist
# 4: $ZPOOL not set # 4: $ZPOOL not set
@ -29,7 +29,8 @@
[ -f "${ZED_ZEDLET_DIR}/zed.rc" ] && . "${ZED_ZEDLET_DIR}/zed.rc" [ -f "${ZED_ZEDLET_DIR}/zed.rc" ] && . "${ZED_ZEDLET_DIR}/zed.rc"
. "${ZED_ZEDLET_DIR}/zed-functions.sh" . "${ZED_ZEDLET_DIR}/zed-functions.sh"
if [ ! -d /sys/class/enclosure ] ; then if [ ! -d /sys/class/enclosure ] && [ ! -d /sys/bus/pci/slots ] ; then
# No JBOD enclosure or NVMe slots
exit 1 exit 1
fi fi
@ -59,6 +60,10 @@ check_and_set_led()
file="$1" file="$1"
val="$2" val="$2"
if [ -z "$val" ]; then
return 0
fi
if [ ! -e "$file" ] ; then if [ ! -e "$file" ] ; then
return 3 return 3
fi fi
@ -66,11 +71,11 @@ check_and_set_led()
# If another process is accessing the LED when we attempt to update it, # 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 # the update will be lost so retry until the LED actually changes or we
# timeout. # timeout.
for _ in $(seq 1 5); do for _ in 1 2 3 4 5; do
# We want to check the current state first, since writing to the # We want to check the current state first, since writing to the
# 'fault' entry always always causes a SES command, even if the # 'fault' entry always causes a SES command, even if the
# current state is already what you want. # current state is already what you want.
current=$(cat "${file}") read -r current < "${file}"
# On some enclosures if you write 1 to fault, and read it back, # 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 # it will return 2. Treat all non-zero values as 1 for
@ -85,27 +90,84 @@ check_and_set_led()
else else
break break
fi fi
done 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
} }
state_to_val() state_to_val()
{ {
state="$1" state="$1"
if [ "$state" = "FAULTED" ] || [ "$state" = "DEGRADED" ] || \ case "$state" in
[ "$state" = "UNAVAIL" ] ; then FAULTED|DEGRADED|UNAVAIL)
echo 1 echo 1
elif [ "$state" = "ONLINE" ] ; then ;;
echo 0 ONLINE)
fi echo 0
;;
esac
} }
# process_pool ([pool])
# #
# Iterate through a pool (or pools) and set the VDEV's enclosure slot LEDs to # Given a nvme name like 'nvme0n1', pass back its slot directory
# the VDEV's state. # like "/sys/bus/pci/slots/0"
#
nvme_dev_to_slot()
{
dev="$1"
# Get the address "0000:01:00.0"
address=$(cat "/sys/class/block/$dev/device/address")
# For each /sys/bus/pci/slots subdir that is an actual number
# (rather than weird directories like "1-3/").
# shellcheck disable=SC2010
for i in $(ls /sys/bus/pci/slots/ | grep -E "^[0-9]+$") ; do
this_address=$(cat "/sys/bus/pci/slots/$i/address")
# The format of address is a little different between
# /sys/class/block/$dev/device/address and
# /sys/bus/pci/slots/
#
# address= "0000:01:00.0"
# this_address = "0000:01:00"
#
if echo "$address" | grep -Eq ^"$this_address" ; then
echo "/sys/bus/pci/slots/$i"
break
fi
done
}
# process_pool (pool)
#
# Iterate through a pool and set the vdevs' enclosure slot LEDs to
# those vdevs' state.
# #
# Arguments # Arguments
# pool: Optional pool name. If not specified, iterate though all pools. # pool: Pool name.
# #
# Return # Return
# 0 on success, 3 on missing sysfs path # 0 on success, 3 on missing sysfs path
@ -113,19 +175,27 @@ state_to_val()
process_pool() process_pool()
{ {
pool="$1" 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 rc=0
while read -r vdev state _ _ _ therest; do
# 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 # Read out current LED value and path
tmp=$(echo "$therest" | sed 's/^.*led_token=//g') # Get dev name (like 'sda')
vdev_enc_sysfs_path=$(echo "$tmp" | awk -F ',' '{print $2}') dev=$(basename "$(echo "$therest" | awk '{print $(NF-1)}')")
current_val=$(echo "$tmp" | awk -F ',' '{print $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}')
if [ "$current_val" != "0" ] ; then if [ "$current_val" != "0" ] ; then
current_val=1 current_val=1
@ -136,40 +206,33 @@ process_pool()
continue continue
fi fi
if [ ! -e "$vdev_enc_sysfs_path/fault" ] ; then led_path=$(path_to_led "$vdev_enc_sysfs_path")
#shellcheck disable=SC2030 if [ ! -e "$led_path" ] ; then
rc=1 rc=3
zed_log_msg "vdev $vdev '$file/fault' doesn't exist" zed_log_msg "vdev $vdev '$led_path' doesn't exist"
continue; continue
fi fi
val=$(state_to_val "$state") val=$(state_to_val "$state")
if [ "$current_val" = "$val" ] ; then if [ "$current_val" = "$val" ] ; then
# LED is already set correctly # LED is already set correctly
continue; continue
fi fi
if ! check_and_set_led "$vdev_enc_sysfs_path/fault" "$val"; then if ! check_and_set_led "$led_path" "$val"; then
rc=1 rc=3
fi fi
done 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 if [ -n "$ZEVENT_VDEV_ENC_SYSFS_PATH" ] && [ -n "$ZEVENT_VDEV_STATE_STR" ] ; then
# Got a statechange for an individual VDEV # Got a statechange for an individual vdev
val=$(state_to_val "$ZEVENT_VDEV_STATE_STR") val=$(state_to_val "$ZEVENT_VDEV_STATE_STR")
vdev=$(basename "$ZEVENT_VDEV_PATH") vdev=$(basename "$ZEVENT_VDEV_PATH")
check_and_set_led "$ZEVENT_VDEV_ENC_SYSFS_PATH/fault" "$val" ledpath=$(path_to_led "$ZEVENT_VDEV_ENC_SYSFS_PATH")
check_and_set_led "$ledpath" "$val"
else else
# Process the entire pool # Process the entire pool
poolname=$(zed_guid_to_pool "$ZEVENT_POOL_GUID") poolname=$(zed_guid_to_pool "$ZEVENT_POOL_GUID")

View File

@ -15,7 +15,7 @@
# Send notification in response to a fault induced statechange # Send notification in response to a fault induced statechange
# #
# ZEVENT_SUBCLASS: 'statechange' # ZEVENT_SUBCLASS: 'statechange'
# ZEVENT_VDEV_STATE_STR: 'DEGRADED', 'FAULTED' or 'REMOVED' # ZEVENT_VDEV_STATE_STR: 'DEGRADED', 'FAULTED', 'REMOVED', or 'UNAVAIL'
# #
# Exit codes: # Exit codes:
# 0: notification sent # 0: notification sent
@ -31,13 +31,14 @@
if [ "${ZEVENT_VDEV_STATE_STR}" != "FAULTED" ] \ if [ "${ZEVENT_VDEV_STATE_STR}" != "FAULTED" ] \
&& [ "${ZEVENT_VDEV_STATE_STR}" != "DEGRADED" ] \ && [ "${ZEVENT_VDEV_STATE_STR}" != "DEGRADED" ] \
&& [ "${ZEVENT_VDEV_STATE_STR}" != "REMOVED" ]; then && [ "${ZEVENT_VDEV_STATE_STR}" != "REMOVED" ] \
&& [ "${ZEVENT_VDEV_STATE_STR}" != "UNAVAIL" ]; then
exit 3 exit 3
fi fi
umask 077 umask 077
note_subject="ZFS device fault for pool ${ZEVENT_POOL_GUID} on $(hostname)" note_subject="ZFS device fault for pool ${ZEVENT_POOL_GUID} on $(hostname)"
note_pathname="${TMPDIR:="/tmp"}/$(basename -- "$0").${ZEVENT_EID}.$$" note_pathname="$(mktemp)"
{ {
if [ "${ZEVENT_VDEV_STATE_STR}" = "FAULTED" ] ; then if [ "${ZEVENT_VDEV_STATE_STR}" = "FAULTED" ] ; then
echo "The number of I/O errors associated with a ZFS device exceeded" echo "The number of I/O errors associated with a ZFS device exceeded"

View File

@ -0,0 +1,37 @@
#!/bin/sh
#
# Send notification in response to a TRIM_FINISH. The event
# will be received for each vdev in the pool which was trimmed.
#
# Exit codes:
# 0: notification sent
# 1: notification failed
# 2: notification not configured
# 9: internal error
[ -f "${ZED_ZEDLET_DIR}/zed.rc" ] && . "${ZED_ZEDLET_DIR}/zed.rc"
. "${ZED_ZEDLET_DIR}/zed-functions.sh"
[ -n "${ZEVENT_POOL}" ] || exit 9
[ -n "${ZEVENT_SUBCLASS}" ] || exit 9
zed_check_cmd "${ZPOOL}" || exit 9
umask 077
note_subject="ZFS ${ZEVENT_SUBCLASS} event for ${ZEVENT_POOL} on $(hostname)"
note_pathname="$(mktemp)"
{
echo "ZFS has finished a trim:"
echo
echo " eid: ${ZEVENT_EID}"
echo " class: ${ZEVENT_SUBCLASS}"
echo " host: $(hostname)"
echo " time: ${ZEVENT_TIME_STRING}"
"${ZPOOL}" status -t "${ZEVENT_POOL}"
} > "${note_pathname}"
zed_notify "${note_subject}" "${note_pathname}"; rv=$?
rm -f "${note_pathname}"
exit "${rv}"

View File

@ -126,10 +126,8 @@ zed_lock()
# Obtain a lock on the file bound to the given file descriptor. # Obtain a lock on the file bound to the given file descriptor.
# #
eval "exec ${fd}> '${lockfile}'" eval "exec ${fd}>> '${lockfile}'"
err="$(flock --exclusive "${fd}" 2>&1)" if ! err="$(flock --exclusive "${fd}" 2>&1)"; then
# shellcheck disable=SC2181
if [ $? -ne 0 ]; then
zed_log_err "failed to lock \"${lockfile}\": ${err}" zed_log_err "failed to lock \"${lockfile}\": ${err}"
fi fi
@ -165,9 +163,7 @@ zed_unlock()
fi fi
# Release the lock and close the file descriptor. # Release the lock and close the file descriptor.
err="$(flock --unlock "${fd}" 2>&1)" if ! err="$(flock --unlock "${fd}" 2>&1)"; then
# shellcheck disable=SC2181
if [ $? -ne 0 ]; then
zed_log_err "failed to unlock \"${lockfile}\": ${err}" zed_log_err "failed to unlock \"${lockfile}\": ${err}"
fi fi
eval "exec ${fd}>&-" eval "exec ${fd}>&-"
@ -202,6 +198,14 @@ zed_notify()
[ "${rv}" -eq 0 ] && num_success=$((num_success + 1)) [ "${rv}" -eq 0 ] && num_success=$((num_success + 1))
[ "${rv}" -eq 1 ] && num_failure=$((num_failure + 1)) [ "${rv}" -eq 1 ] && num_failure=$((num_failure + 1))
zed_notify_slack_webhook "${subject}" "${pathname}"; rv=$?
[ "${rv}" -eq 0 ] && num_success=$((num_success + 1))
[ "${rv}" -eq 1 ] && num_failure=$((num_failure + 1))
zed_notify_pushover "${subject}" "${pathname}"; rv=$?
[ "${rv}" -eq 0 ] && num_success=$((num_success + 1))
[ "${rv}" -eq 1 ] && num_failure=$((num_failure + 1))
[ "${num_success}" -gt 0 ] && return 0 [ "${num_success}" -gt 0 ] && return 0
[ "${num_failure}" -gt 0 ] && return 1 [ "${num_failure}" -gt 0 ] && return 1
return 2 return 2
@ -263,7 +267,7 @@ zed_notify_email()
-e "s/@SUBJECT@/${subject}/g")" -e "s/@SUBJECT@/${subject}/g")"
# shellcheck disable=SC2086 # shellcheck disable=SC2086
eval "${ZED_EMAIL_PROG}" ${ZED_EMAIL_OPTS} < "${pathname}" >/dev/null 2>&1 eval ${ZED_EMAIL_PROG} ${ZED_EMAIL_OPTS} < "${pathname}" >/dev/null 2>&1
rv=$? rv=$?
if [ "${rv}" -ne 0 ]; then if [ "${rv}" -ne 0 ]; then
zed_log_err "$(basename "${ZED_EMAIL_PROG}") exit=${rv}" zed_log_err "$(basename "${ZED_EMAIL_PROG}") exit=${rv}"
@ -359,6 +363,158 @@ zed_notify_pushbullet()
} }
# zed_notify_slack_webhook (subject, pathname)
#
# Notification via Slack Webhook <https://api.slack.com/incoming-webhooks>.
# The Webhook URL (ZED_SLACK_WEBHOOK_URL) identifies this client to the
# Slack channel.
#
# Requires awk, curl, and sed executables to be installed in the standard PATH.
#
# References
# https://api.slack.com/incoming-webhooks
#
# Arguments
# subject: notification subject
# pathname: pathname containing the notification message (OPTIONAL)
#
# Globals
# ZED_SLACK_WEBHOOK_URL
#
# Return
# 0: notification sent
# 1: notification failed
# 2: not configured
#
zed_notify_slack_webhook()
{
[ -n "${ZED_SLACK_WEBHOOK_URL}" ] || return 2
local subject="$1"
local pathname="${2:-"/dev/null"}"
local msg_body
local msg_tag
local msg_json
local msg_out
local msg_err
local url="${ZED_SLACK_WEBHOOK_URL}"
[ -n "${subject}" ] || return 1
if [ ! -r "${pathname}" ]; then
zed_log_err "slack webhook cannot read \"${pathname}\""
return 1
fi
zed_check_cmd "awk" "curl" "sed" || return 1
# Escape the following characters in the message body for JSON:
# newline, backslash, double quote, horizontal tab, vertical tab,
# and carriage return.
#
msg_body="$(awk '{ ORS="\\n" } { gsub(/\\/, "\\\\"); gsub(/"/, "\\\"");
gsub(/\t/, "\\t"); gsub(/\f/, "\\f"); gsub(/\r/, "\\r"); print }' \
"${pathname}")"
# Construct the JSON message for posting.
#
msg_json="$(printf '{"text": "*%s*\n%s"}' "${subject}" "${msg_body}" )"
# Send the POST request and check for errors.
#
msg_out="$(curl -X POST "${url}" \
--header "Content-Type: application/json" --data-binary "${msg_json}" \
2>/dev/null)"; rv=$?
if [ "${rv}" -ne 0 ]; then
zed_log_err "curl exit=${rv}"
return 1
fi
msg_err="$(echo "${msg_out}" \
| sed -n -e 's/.*"error" *:.*"message" *: *"\([^"]*\)".*/\1/p')"
if [ -n "${msg_err}" ]; then
zed_log_err "slack webhook \"${msg_err}"\"
return 1
fi
return 0
}
# zed_notify_pushover (subject, pathname)
#
# Send a notification via Pushover <https://pushover.net/>.
# The access token (ZED_PUSHOVER_TOKEN) identifies this client to the
# Pushover server. The user token (ZED_PUSHOVER_USER) defines the user or
# group to which the notification will be sent.
#
# Requires curl and sed executables to be installed in the standard PATH.
#
# References
# https://pushover.net/api
#
# Arguments
# subject: notification subject
# pathname: pathname containing the notification message (OPTIONAL)
#
# Globals
# ZED_PUSHOVER_TOKEN
# ZED_PUSHOVER_USER
#
# Return
# 0: notification sent
# 1: notification failed
# 2: not configured
#
zed_notify_pushover()
{
local subject="$1"
local pathname="${2:-"/dev/null"}"
local msg_body
local msg_out
local msg_err
local url="https://api.pushover.net/1/messages.json"
[ -n "${ZED_PUSHOVER_TOKEN}" ] && [ -n "${ZED_PUSHOVER_USER}" ] || return 2
if [ ! -r "${pathname}" ]; then
zed_log_err "pushover cannot read \"${pathname}\""
return 1
fi
zed_check_cmd "curl" "sed" || return 1
# Read the message body in.
#
msg_body="$(cat "${pathname}")"
if [ -z "${msg_body}" ]
then
msg_body=$subject
subject=""
fi
# Send the POST request and check for errors.
#
msg_out="$( \
curl \
--form-string "token=${ZED_PUSHOVER_TOKEN}" \
--form-string "user=${ZED_PUSHOVER_USER}" \
--form-string "message=${msg_body}" \
--form-string "title=${subject}" \
"${url}" \
2>/dev/null \
)"; rv=$?
if [ "${rv}" -ne 0 ]; then
zed_log_err "curl exit=${rv}"
return 1
fi
msg_err="$(echo "${msg_out}" \
| sed -n -e 's/.*"errors" *:.*\[\(.*\)\].*/\1/p')"
if [ -n "${msg_err}" ]; then
zed_log_err "pushover \"${msg_err}"\"
return 1
fi
return 0
}
# zed_rate_limit (tag, [interval]) # zed_rate_limit (tag, [interval])
# #
# Check whether an event of a given type [tag] has already occurred within the # Check whether an event of a given type [tag] has already occurred within the
@ -433,10 +589,8 @@ zed_guid_to_pool()
return return
fi fi
guid=$(printf "%llu" "$1") guid="$(printf "%u" "$1")"
if [ -n "$guid" ] ; then $ZPOOL get -H -ovalue,name guid | awk '$1 == '"$guid"' {print $2; exit}'
$ZPOOL get -H -ovalue,name guid | awk '$1=='"$guid"' {print $2}'
fi
} }
# zed_exit_if_ignoring_this_event # zed_exit_if_ignoring_this_event

View File

@ -74,6 +74,31 @@
# #
#ZED_PUSHBULLET_CHANNEL_TAG="" #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. # Default directory for zed state files.
# #
@ -81,8 +106,8 @@
## ##
# Turn on/off enclosure LEDs when drives get DEGRADED/FAULTED. This works for # Turn on/off enclosure LEDs when drives get DEGRADED/FAULTED. This works for
# device mapper and multipath devices as well. Your enclosure must be # device mapper and multipath devices as well. This works with JBOD enclosures
# supported by the Linux SES driver for this to work. # and NVMe PCI drives (assuming they're supported by Linux in sysfs).
# #
ZED_USE_ENCLOSURE_LEDS=1 ZED_USE_ENCLOSURE_LEDS=1
@ -110,5 +135,10 @@ ZED_USE_ENCLOSURE_LEDS=1
# Otherwise, if ZED_SYSLOG_SUBCLASS_EXCLUDE is set, the # Otherwise, if ZED_SYSLOG_SUBCLASS_EXCLUDE is set, the
# matching subclasses are excluded from logging. # matching subclasses are excluded from logging.
#ZED_SYSLOG_SUBCLASS_INCLUDE="checksum|scrub_*|vdev.*" #ZED_SYSLOG_SUBCLASS_INCLUDE="checksum|scrub_*|vdev.*"
#ZED_SYSLOG_SUBCLASS_EXCLUDE="statechange|config_*|history_event" ZED_SYSLOG_SUBCLASS_EXCLUDE="history_event"
##
# Use GUIDs instead of names when logging pool and vdevs
# Disabled by default, 1 to enable and 0 to disable.
#ZED_SYSLOG_DISPLAY_GUIDS=1

View File

@ -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). * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC. * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
* Refer to the ZoL git commit log for authoritative copyright attribution. * Refer to the OpenZFS git commit log for authoritative copyright attribution.
* *
* The contents of this file are subject to the terms of the * The contents of this file are subject to the terms of the
* Common Development and Distribution License Version 1.0 (CDDL-1.0). * Common Development and Distribution License Version 1.0 (CDDL-1.0).
@ -15,11 +15,6 @@
#ifndef ZED_H #ifndef ZED_H
#define 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. * Absolute path for the default zed pid file.
*/ */
@ -35,16 +30,6 @@
*/ */
#define ZED_ZEDLET_DIR SYSCONFDIR "/zfs/zed.d" #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. * String prefix for ZED variables passed via environment variables.
*/ */

View File

@ -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). * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC. * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
* Refer to the ZoL git commit log for authoritative copyright attribution. * Refer to the OpenZFS git commit log for authoritative copyright attribution.
* *
* The contents of this file are subject to the terms of the * The contents of this file are subject to the terms of the
* Common Development and Distribution License Version 1.0 (CDDL-1.0). * Common Development and Distribution License Version 1.0 (CDDL-1.0).
@ -22,6 +22,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <sys/types.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/uio.h> #include <sys/uio.h>
#include <unistd.h> #include <unistd.h>
@ -32,43 +33,26 @@
#include "zed_strings.h" #include "zed_strings.h"
/* /*
* Return a new configuration with default values. * Initialise the configuration with default values.
*/ */
struct zed_conf * void
zed_conf_create(void) zed_conf_init(struct zed_conf *zcp)
{ {
struct zed_conf *zcp; memset(zcp, 0, sizeof (*zcp));
zcp = calloc(1, sizeof (*zcp)); /* zcp->zfs_hdl opened in zed_event_init() */
if (!zcp) /* zcp->zedlets created in zed_conf_scan_dir() */
goto nomem;
zcp->syslog_facility = LOG_DAEMON; zcp->pid_fd = -1; /* opened in zed_conf_write_pid() */
zcp->min_events = ZED_MIN_EVENTS; zcp->state_fd = -1; /* opened in zed_conf_open_state() */
zcp->max_events = ZED_MAX_EVENTS; zcp->zevent_fd = -1; /* opened in zed_event_init() */
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() */
if (!(zcp->conf_file = strdup(ZED_CONF_FILE))) zcp->max_jobs = 16;
goto nomem;
if (!(zcp->pid_file = strdup(ZED_PID_FILE))) if (!(zcp->pid_file = strdup(ZED_PID_FILE)) ||
goto nomem; !(zcp->zedlet_dir = strdup(ZED_ZEDLET_DIR)) ||
!(zcp->state_file = strdup(ZED_STATE_FILE)))
if (!(zcp->zedlet_dir = strdup(ZED_ZEDLET_DIR))) zed_log_die("Failed to create conf: %s", strerror(errno));
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);
} }
/* /*
@ -79,9 +63,6 @@ nomem:
void void
zed_conf_destroy(struct zed_conf *zcp) zed_conf_destroy(struct zed_conf *zcp)
{ {
if (!zcp)
return;
if (zcp->state_fd >= 0) { if (zcp->state_fd >= 0) {
if (close(zcp->state_fd) < 0) if (close(zcp->state_fd) < 0)
zed_log_msg(LOG_WARNING, zed_log_msg(LOG_WARNING,
@ -102,10 +83,6 @@ zed_conf_destroy(struct zed_conf *zcp)
zcp->pid_file, strerror(errno)); zcp->pid_file, strerror(errno));
zcp->pid_fd = -1; zcp->pid_fd = -1;
} }
if (zcp->conf_file) {
free(zcp->conf_file);
zcp->conf_file = NULL;
}
if (zcp->pid_file) { if (zcp->pid_file) {
free(zcp->pid_file); free(zcp->pid_file);
zcp->pid_file = NULL; zcp->pid_file = NULL;
@ -122,7 +99,6 @@ zed_conf_destroy(struct zed_conf *zcp)
zed_strings_destroy(zcp->zedlets); zed_strings_destroy(zcp->zedlets);
zcp->zedlets = NULL; zcp->zedlets = NULL;
} }
free(zcp);
} }
/* /*
@ -132,44 +108,52 @@ zed_conf_destroy(struct zed_conf *zcp)
* otherwise, output to stderr and exit with a failure status. * otherwise, output to stderr and exit with a failure status.
*/ */
static void static void
_zed_conf_display_help(const char *prog, int got_err) _zed_conf_display_help(const char *prog, boolean_t got_err)
{ {
struct opt { const char *o, *d, *v; };
FILE *fp = got_err ? stderr : stdout; FILE *fp = got_err ? stderr : stdout;
int w1 = 4; /* width of leading whitespace */
int w2 = 8; /* width of L-justified option field */ 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" },
{},
};
fprintf(fp, "Usage: %s [OPTION]...\n", (prog ? prog : "zed")); fprintf(fp, "Usage: %s [OPTION]...\n", (prog ? prog : "zed"));
fprintf(fp, "\n"); fprintf(fp, "\n");
fprintf(fp, "%*c%*s %s\n", w1, 0x20, -w2, "-h", for (oo = iopts; oo->o; ++oo)
"Display help."); fprintf(fp, " %*s %s\n", -8, oo->o, oo->d);
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"); fprintf(fp, "\n");
fprintf(fp, "%*c%*s %s\n", w1, 0x20, -w2, "-v", for (oo = nopts; oo->o; ++oo)
"Be verbose."); fprintf(fp, " %*s %s\n", -8, oo->o, oo->d);
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"); fprintf(fp, "\n");
#if 0 for (oo = vopts; oo->o; ++oo)
fprintf(fp, "%*c%*s %s [%s]\n", w1, 0x20, -w2, "-c FILE", fprintf(fp, " %*s %s [%s]\n", -8, oo->o, oo->d, oo->v);
"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"); fprintf(fp, "\n");
exit(got_err ? EXIT_FAILURE : EXIT_SUCCESS); exit(got_err ? EXIT_FAILURE : EXIT_SUCCESS);
@ -181,20 +165,14 @@ _zed_conf_display_help(const char *prog, int got_err)
static void static void
_zed_conf_display_license(void) _zed_conf_display_license(void)
{ {
const char **pp; printf(
const char *text[] = { "The ZFS Event Daemon (ZED) is distributed under the terms of the\n"
"The ZFS Event Daemon (ZED) is distributed under the terms of the", " Common Development and Distribution License (CDDL-1.0)\n"
" Common Development and Distribution License (CDDL-1.0)", " <http://opensource.org/licenses/CDDL-1.0>.\n"
" <http://opensource.org/licenses/CDDL-1.0>.", "\n"
"",
"Developed at Lawrence Livermore National Laboratory" "Developed at Lawrence Livermore National Laboratory"
" (LLNL-CODE-403049).", " (LLNL-CODE-403049).\n"
"", "\n");
NULL
};
for (pp = text; *pp; pp++)
printf("%s\n", *pp);
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }
@ -229,16 +207,19 @@ _zed_conf_parse_path(char **resultp, const char *path)
if (path[0] == '/') { if (path[0] == '/') {
*resultp = strdup(path); *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 { } 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); *resultp = strdup(buf);
} }
if (!*resultp) if (!*resultp)
zed_log_die("Failed to copy path: %s", strerror(ENOMEM)); zed_log_die("Failed to copy path: %s", strerror(ENOMEM));
} }
@ -249,8 +230,9 @@ _zed_conf_parse_path(char **resultp, const char *path)
void void
zed_conf_parse_opts(struct zed_conf *zcp, int argc, char **argv) zed_conf_parse_opts(struct zed_conf *zcp, int argc, char **argv)
{ {
const char * const opts = ":hLVc:d:p:P:s:vfFMZ"; const char * const opts = ":hLVd:p:P:s:vfFMZIj:";
int opt; int opt;
unsigned long raw;
if (!zcp || !argv || !argv[0]) if (!zcp || !argv || !argv[0])
zed_log_die("Failed to parse options: Internal error"); zed_log_die("Failed to parse options: Internal error");
@ -260,7 +242,7 @@ zed_conf_parse_opts(struct zed_conf *zcp, int argc, char **argv)
while ((opt = getopt(argc, argv, opts)) != -1) { while ((opt = getopt(argc, argv, opts)) != -1) {
switch (opt) { switch (opt) {
case 'h': case 'h':
_zed_conf_display_help(argv[0], EXIT_SUCCESS); _zed_conf_display_help(argv[0], B_FALSE);
break; break;
case 'L': case 'L':
_zed_conf_display_license(); _zed_conf_display_license();
@ -268,12 +250,12 @@ zed_conf_parse_opts(struct zed_conf *zcp, int argc, char **argv)
case 'V': case 'V':
_zed_conf_display_version(); _zed_conf_display_version();
break; break;
case 'c':
_zed_conf_parse_path(&zcp->conf_file, optarg);
break;
case 'd': case 'd':
_zed_conf_parse_path(&zcp->zedlet_dir, optarg); _zed_conf_parse_path(&zcp->zedlet_dir, optarg);
break; break;
case 'I':
zcp->do_idle = 1;
break;
case 'p': case 'p':
_zed_conf_parse_path(&zcp->pid_file, optarg); _zed_conf_parse_path(&zcp->pid_file, optarg);
break; break;
@ -298,31 +280,30 @@ zed_conf_parse_opts(struct zed_conf *zcp, int argc, char **argv)
case 'Z': case 'Z':
zcp->do_zero = 1; zcp->do_zero = 1;
break; 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 '?': case '?':
default: default:
if (optopt == '?') if (optopt == '?')
_zed_conf_display_help(argv[0], EXIT_SUCCESS); _zed_conf_display_help(argv[0], B_FALSE);
fprintf(stderr, "%s: %s '-%c'\n\n", argv[0], fprintf(stderr, "%s: Invalid option '-%c'\n\n",
"Invalid option", optopt); argv[0], optopt);
_zed_conf_display_help(argv[0], EXIT_FAILURE); _zed_conf_display_help(argv[0], B_TRUE);
break; 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. * 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. * Files must be executable by user, but not writable by group or other.
@ -330,8 +311,6 @@ zed_conf_parse_file(struct zed_conf *zcp)
* *
* Return 0 on success with an updated set of zedlets, * Return 0 on success with an updated set of zedlets,
* or -1 on error with errno set. * or -1 on error with errno set.
*
* FIXME: Check if zedlet_dir and all parent dirs are secure.
*/ */
int int
zed_conf_scan_dir(struct zed_conf *zcp) zed_conf_scan_dir(struct zed_conf *zcp)
@ -447,8 +426,6 @@ zed_conf_scan_dir(struct zed_conf *zcp)
int int
zed_conf_write_pid(struct zed_conf *zcp) 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]; char buf[PATH_MAX];
int n; int n;
char *p; char *p;
@ -476,7 +453,7 @@ zed_conf_write_pid(struct zed_conf *zcp)
if (p) if (p)
*p = '\0'; *p = '\0';
if ((mkdirp(buf, dirmode) < 0) && (errno != EEXIST)) { if ((mkdirp(buf, 0755) < 0) && (errno != EEXIST)) {
zed_log_msg(LOG_ERR, "Failed to create directory \"%s\": %s", zed_log_msg(LOG_ERR, "Failed to create directory \"%s\": %s",
buf, strerror(errno)); buf, strerror(errno));
goto err; goto err;
@ -486,7 +463,7 @@ zed_conf_write_pid(struct zed_conf *zcp)
*/ */
mask = umask(0); mask = umask(0);
umask(mask | 022); umask(mask | 022);
zcp->pid_fd = open(zcp->pid_file, (O_RDWR | O_CREAT), filemode); zcp->pid_fd = open(zcp->pid_file, O_RDWR | O_CREAT | O_CLOEXEC, 0644);
umask(mask); umask(mask);
if (zcp->pid_fd < 0) { if (zcp->pid_fd < 0) {
zed_log_msg(LOG_ERR, "Failed to open PID file \"%s\": %s", zed_log_msg(LOG_ERR, "Failed to open PID file \"%s\": %s",
@ -523,7 +500,7 @@ zed_conf_write_pid(struct zed_conf *zcp)
errno = ERANGE; errno = ERANGE;
zed_log_msg(LOG_ERR, "Failed to write PID file \"%s\": %s", zed_log_msg(LOG_ERR, "Failed to write PID file \"%s\": %s",
zcp->pid_file, strerror(errno)); zcp->pid_file, strerror(errno));
} else if (zed_file_write_n(zcp->pid_fd, buf, n) != n) { } else if (write(zcp->pid_fd, buf, n) != n) {
zed_log_msg(LOG_ERR, "Failed to write PID file \"%s\": %s", zed_log_msg(LOG_ERR, "Failed to write PID file \"%s\": %s",
zcp->pid_file, strerror(errno)); zcp->pid_file, strerror(errno));
} else if (fdatasync(zcp->pid_fd) < 0) { } else if (fdatasync(zcp->pid_fd) < 0) {
@ -551,7 +528,6 @@ int
zed_conf_open_state(struct zed_conf *zcp) zed_conf_open_state(struct zed_conf *zcp)
{ {
char dirbuf[PATH_MAX]; char dirbuf[PATH_MAX];
mode_t dirmode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH;
int n; int n;
char *p; char *p;
int rv; int rv;
@ -573,7 +549,7 @@ zed_conf_open_state(struct zed_conf *zcp)
if (p) if (p)
*p = '\0'; *p = '\0';
if ((mkdirp(dirbuf, dirmode) < 0) && (errno != EEXIST)) { if ((mkdirp(dirbuf, 0755) < 0) && (errno != EEXIST)) {
zed_log_msg(LOG_WARNING, zed_log_msg(LOG_WARNING,
"Failed to create directory \"%s\": %s", "Failed to create directory \"%s\": %s",
dirbuf, strerror(errno)); dirbuf, strerror(errno));
@ -591,7 +567,7 @@ zed_conf_open_state(struct zed_conf *zcp)
(void) unlink(zcp->state_file); (void) unlink(zcp->state_file);
zcp->state_fd = open(zcp->state_file, zcp->state_fd = open(zcp->state_file,
(O_RDWR | O_CREAT), (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)); O_RDWR | O_CREAT | O_CLOEXEC, 0644);
if (zcp->state_fd < 0) { if (zcp->state_fd < 0) {
zed_log_msg(LOG_WARNING, "Failed to open state file \"%s\": %s", zed_log_msg(LOG_WARNING, "Failed to open state file \"%s\": %s",
zcp->state_file, strerror(errno)); zcp->state_file, strerror(errno));

View File

@ -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). * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC. * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
* Refer to the ZoL git commit log for authoritative copyright attribution. * Refer to the OpenZFS git commit log for authoritative copyright attribution.
* *
* The contents of this file are subject to the terms of the * The contents of this file are subject to the terms of the
* Common Development and Distribution License Version 1.0 (CDDL-1.0). * Common Development and Distribution License Version 1.0 (CDDL-1.0).
@ -20,42 +20,39 @@
#include "zed_strings.h" #include "zed_strings.h"
struct zed_conf { struct zed_conf {
unsigned do_force:1; /* true if force enabled */
unsigned do_foreground:1; /* true if run in foreground */
unsigned do_memlock:1; /* true if locking memory */
unsigned do_verbose:1; /* true if verbosity enabled */
unsigned do_zero:1; /* true if zeroing state */
int syslog_facility; /* syslog facility value */
int min_events; /* RESERVED FOR FUTURE USE */
int max_events; /* RESERVED FOR FUTURE USE */
char *conf_file; /* abs path to config file */
char *pid_file; /* abs path to pid file */ char *pid_file; /* abs path to pid file */
int pid_fd; /* fd to pid file for lock */
char *zedlet_dir; /* abs path to zedlet dir */ char *zedlet_dir; /* abs path to zedlet dir */
zed_strings_t *zedlets; /* names of enabled zedlets */
char *state_file; /* abs path to state file */ char *state_file; /* abs path to state file */
int state_fd; /* fd to state file */
libzfs_handle_t *zfs_hdl; /* handle to libzfs */ libzfs_handle_t *zfs_hdl; /* handle to libzfs */
int zevent_fd; /* fd for access to zevents */ zed_strings_t *zedlets; /* names of enabled zedlets */
char *path; /* custom $PATH for zedlets to use */ char *path; /* custom $PATH for zedlets to use */
int pid_fd; /* fd to pid file for lock */
int state_fd; /* fd to state file */
int zevent_fd; /* fd for access to zevents */
int16_t max_jobs; /* max zedlets to run at one time */
boolean_t do_force:1; /* true if force enabled */
boolean_t do_foreground:1; /* true if run in foreground */
boolean_t do_memlock:1; /* true if locking memory */
boolean_t do_verbose:1; /* true if verbosity enabled */
boolean_t do_zero:1; /* true if zeroing state */
boolean_t do_idle:1; /* true if idle enabled */
}; };
struct zed_conf *zed_conf_create(void); void zed_conf_init(struct zed_conf *zcp);
void zed_conf_destroy(struct zed_conf *zcp); void zed_conf_destroy(struct zed_conf *zcp);
void zed_conf_parse_opts(struct zed_conf *zcp, int argc, char **argv); void zed_conf_parse_opts(struct zed_conf *zcp, int argc, char **argv);
void zed_conf_parse_file(struct zed_conf *zcp);
int zed_conf_scan_dir(struct zed_conf *zcp); int zed_conf_scan_dir(struct zed_conf *zcp);
int zed_conf_write_pid(struct zed_conf *zcp); int zed_conf_write_pid(struct zed_conf *zcp);
int zed_conf_open_state(struct zed_conf *zcp); int zed_conf_open_state(struct zed_conf *zcp);
int zed_conf_read_state(struct zed_conf *zcp, uint64_t *eidp, int64_t etime[]); int zed_conf_read_state(struct zed_conf *zcp, uint64_t *eidp, int64_t etime[]);
int zed_conf_write_state(struct zed_conf *zcp, uint64_t eid, int64_t etime[]); int zed_conf_write_state(struct zed_conf *zcp, uint64_t eid, int64_t etime[]);
#endif /* !ZED_CONF_H */ #endif /* !ZED_CONF_H */

View File

@ -72,6 +72,8 @@ zed_udev_event(const char *class, const char *subclass, nvlist_t *nvl)
zed_log_msg(LOG_INFO, "\t%s: %s", DEV_PATH, strval); zed_log_msg(LOG_INFO, "\t%s: %s", DEV_PATH, strval);
if (nvlist_lookup_string(nvl, DEV_IDENTIFIER, &strval) == 0) if (nvlist_lookup_string(nvl, DEV_IDENTIFIER, &strval) == 0)
zed_log_msg(LOG_INFO, "\t%s: %s", DEV_IDENTIFIER, strval); zed_log_msg(LOG_INFO, "\t%s: %s", DEV_IDENTIFIER, strval);
if (nvlist_lookup_boolean(nvl, DEV_IS_PART) == B_TRUE)
zed_log_msg(LOG_INFO, "\t%s: B_TRUE", DEV_IS_PART);
if (nvlist_lookup_string(nvl, DEV_PHYS_PATH, &strval) == 0) if (nvlist_lookup_string(nvl, DEV_PHYS_PATH, &strval) == 0)
zed_log_msg(LOG_INFO, "\t%s: %s", DEV_PHYS_PATH, strval); zed_log_msg(LOG_INFO, "\t%s: %s", DEV_PHYS_PATH, strval);
if (nvlist_lookup_uint64(nvl, DEV_SIZE, &numval) == 0) if (nvlist_lookup_uint64(nvl, DEV_SIZE, &numval) == 0)
@ -379,6 +381,7 @@ zed_disk_event_init()
return (-1); return (-1);
} }
pthread_setname_np(g_mon_tid, "udev monitor");
zed_log_msg(LOG_INFO, "zed_disk_event_init"); zed_log_msg(LOG_INFO, "zed_disk_event_init");
return (0); return (0);

View File

@ -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). * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC. * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
* Refer to the ZoL git commit log for authoritative copyright attribution. * Refer to the OpenZFS git commit log for authoritative copyright attribution.
* *
* The contents of this file are subject to the terms of the * The contents of this file are subject to the terms of the
* Common Development and Distribution License Version 1.0 (CDDL-1.0). * Common Development and Distribution License Version 1.0 (CDDL-1.0).
@ -15,7 +15,7 @@
#include <ctype.h> #include <ctype.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <libzfs.h> /* FIXME: Replace with libzfs_core. */ #include <libzfs_core.h>
#include <paths.h> #include <paths.h>
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
@ -28,6 +28,7 @@
#include "zed.h" #include "zed.h"
#include "zed_conf.h" #include "zed_conf.h"
#include "zed_disk_event.h" #include "zed_disk_event.h"
#include "zed_event.h"
#include "zed_exec.h" #include "zed_exec.h"
#include "zed_file.h" #include "zed_file.h"
#include "zed_log.h" #include "zed_log.h"
@ -40,25 +41,36 @@
/* /*
* Open the libzfs interface. * Open the libzfs interface.
*/ */
void int
zed_event_init(struct zed_conf *zcp) zed_event_init(struct zed_conf *zcp)
{ {
if (!zcp) if (!zcp)
zed_log_die("Failed zed_event_init: %s", strerror(EINVAL)); zed_log_die("Failed zed_event_init: %s", strerror(EINVAL));
zcp->zfs_hdl = libzfs_init(); zcp->zfs_hdl = libzfs_init();
if (!zcp->zfs_hdl) if (!zcp->zfs_hdl) {
if (zcp->do_idle)
return (-1);
zed_log_die("Failed to initialize libzfs"); zed_log_die("Failed to initialize libzfs");
}
zcp->zevent_fd = open(ZFS_DEV, O_RDWR); zcp->zevent_fd = open(ZFS_DEV, O_RDWR | O_CLOEXEC);
if (zcp->zevent_fd < 0) if (zcp->zevent_fd < 0) {
if (zcp->do_idle)
return (-1);
zed_log_die("Failed to open \"%s\": %s", zed_log_die("Failed to open \"%s\": %s",
ZFS_DEV, strerror(errno)); ZFS_DEV, strerror(errno));
}
zfs_agent_init(zcp->zfs_hdl); zfs_agent_init(zcp->zfs_hdl);
if (zed_disk_event_init() != 0) if (zed_disk_event_init() != 0) {
if (zcp->do_idle)
return (-1);
zed_log_die("Failed to initialize disk events"); zed_log_die("Failed to initialize disk events");
}
return (0);
} }
/* /*
@ -84,6 +96,47 @@ zed_event_fini(struct zed_conf *zcp)
libzfs_fini(zcp->zfs_hdl); libzfs_fini(zcp->zfs_hdl);
zcp->zfs_hdl = NULL; zcp->zfs_hdl = NULL;
} }
zed_exec_fini();
}
static void
_bump_event_queue_length(void)
{
int zzlm = -1, wr;
char qlen_buf[12] = {0}; /* parameter is int => max "-2147483647\n" */
long int qlen;
zzlm = open("/sys/module/zfs/parameters/zfs_zevent_len_max", O_RDWR);
if (zzlm < 0)
goto done;
if (read(zzlm, qlen_buf, sizeof (qlen_buf)) < 0)
goto done;
qlen_buf[sizeof (qlen_buf) - 1] = '\0';
errno = 0;
qlen = strtol(qlen_buf, NULL, 10);
if (errno == ERANGE)
goto done;
if (qlen <= 0)
qlen = 512; /* default zfs_zevent_len_max value */
else
qlen *= 2;
if (qlen > INT_MAX)
qlen = INT_MAX;
wr = snprintf(qlen_buf, sizeof (qlen_buf), "%ld", qlen);
if (pwrite(zzlm, qlen_buf, wr, 0) < 0)
goto done;
zed_log_msg(LOG_WARNING, "Bumping queue length to %ld", qlen);
done:
if (zzlm > -1)
(void) close(zzlm);
} }
/* /*
@ -124,10 +177,7 @@ zed_event_seek(struct zed_conf *zcp, uint64_t saved_eid, int64_t saved_etime[])
if (n_dropped > 0) { if (n_dropped > 0) {
zed_log_msg(LOG_WARNING, "Missed %d events", n_dropped); zed_log_msg(LOG_WARNING, "Missed %d events", n_dropped);
/* _bump_event_queue_length();
* FIXME: Increase max size of event nvlist in
* /sys/module/zfs/parameters/zfs_zevent_len_max ?
*/
} }
if (nvlist_lookup_uint64(nvl, "eid", &eid) != 0) { if (nvlist_lookup_uint64(nvl, "eid", &eid) != 0) {
zed_log_msg(LOG_WARNING, "Failed to lookup zevent eid"); zed_log_msg(LOG_WARNING, "Failed to lookup zevent eid");
@ -199,7 +249,7 @@ _zed_event_value_is_hex(const char *name)
* *
* All environment variables in [zsp] should be added through this function. * All environment variables in [zsp] should be added through this function.
*/ */
static int static __attribute__((format(printf, 5, 6))) int
_zed_event_add_var(uint64_t eid, zed_strings_t *zsp, _zed_event_add_var(uint64_t eid, zed_strings_t *zsp,
const char *prefix, const char *name, const char *fmt, ...) const char *prefix, const char *name, const char *fmt, ...)
{ {
@ -574,8 +624,6 @@ _zed_event_add_string_array(uint64_t eid, zed_strings_t *zsp,
* Convert the nvpair [nvp] to a string which is added to the environment * Convert the nvpair [nvp] to a string which is added to the environment
* of the child process. * of the child process.
* Return 0 on success, -1 on error. * Return 0 on success, -1 on error.
*
* FIXME: Refactor with cmd/zpool/zpool_main.c:zpool_do_events_nvprint()?
*/ */
static void static void
_zed_event_add_nvpair(uint64_t eid, zed_strings_t *zsp, nvpair_t *nvp) _zed_event_add_nvpair(uint64_t eid, zed_strings_t *zsp, nvpair_t *nvp)
@ -674,23 +722,11 @@ _zed_event_add_nvpair(uint64_t eid, zed_strings_t *zsp, nvpair_t *nvp)
_zed_event_add_var(eid, zsp, prefix, name, _zed_event_add_var(eid, zsp, prefix, name,
"%llu", (u_longlong_t)i64); "%llu", (u_longlong_t)i64);
break; break;
case DATA_TYPE_NVLIST:
_zed_event_add_var(eid, zsp, prefix, name,
"%s", "_NOT_IMPLEMENTED_"); /* FIXME */
break;
case DATA_TYPE_STRING: case DATA_TYPE_STRING:
(void) nvpair_value_string(nvp, &str); (void) nvpair_value_string(nvp, &str);
_zed_event_add_var(eid, zsp, prefix, name, _zed_event_add_var(eid, zsp, prefix, name,
"%s", (str ? str : "<NULL>")); "%s", (str ? str : "<NULL>"));
break; break;
case DATA_TYPE_BOOLEAN_ARRAY:
_zed_event_add_var(eid, zsp, prefix, name,
"%s", "_NOT_IMPLEMENTED_"); /* FIXME */
break;
case DATA_TYPE_BYTE_ARRAY:
_zed_event_add_var(eid, zsp, prefix, name,
"%s", "_NOT_IMPLEMENTED_"); /* FIXME */
break;
case DATA_TYPE_INT8_ARRAY: case DATA_TYPE_INT8_ARRAY:
_zed_event_add_int8_array(eid, zsp, prefix, nvp); _zed_event_add_int8_array(eid, zsp, prefix, nvp);
break; break;
@ -718,9 +754,11 @@ _zed_event_add_nvpair(uint64_t eid, zed_strings_t *zsp, nvpair_t *nvp)
case DATA_TYPE_STRING_ARRAY: case DATA_TYPE_STRING_ARRAY:
_zed_event_add_string_array(eid, zsp, prefix, nvp); _zed_event_add_string_array(eid, zsp, prefix, nvp);
break; break;
case DATA_TYPE_NVLIST:
case DATA_TYPE_BOOLEAN_ARRAY:
case DATA_TYPE_BYTE_ARRAY:
case DATA_TYPE_NVLIST_ARRAY: case DATA_TYPE_NVLIST_ARRAY:
_zed_event_add_var(eid, zsp, prefix, name, _zed_event_add_var(eid, zsp, prefix, name, "_NOT_IMPLEMENTED_");
"%s", "_NOT_IMPLEMENTED_"); /* FIXME */
break; break;
default: default:
errno = EINVAL; errno = EINVAL;
@ -872,7 +910,7 @@ _zed_event_add_time_strings(uint64_t eid, zed_strings_t *zsp, int64_t etime[])
/* /*
* Service the next zevent, blocking until one is available. * Service the next zevent, blocking until one is available.
*/ */
void int
zed_event_service(struct zed_conf *zcp) zed_event_service(struct zed_conf *zcp)
{ {
nvlist_t *nvl; nvlist_t *nvl;
@ -890,20 +928,17 @@ zed_event_service(struct zed_conf *zcp)
errno = EINVAL; errno = EINVAL;
zed_log_msg(LOG_ERR, "Failed to service zevent: %s", zed_log_msg(LOG_ERR, "Failed to service zevent: %s",
strerror(errno)); strerror(errno));
return; return (EINVAL);
} }
rv = zpool_events_next(zcp->zfs_hdl, &nvl, &n_dropped, ZEVENT_NONE, rv = zpool_events_next(zcp->zfs_hdl, &nvl, &n_dropped, ZEVENT_NONE,
zcp->zevent_fd); zcp->zevent_fd);
if ((rv != 0) || !nvl) if ((rv != 0) || !nvl)
return; return (errno);
if (n_dropped > 0) { if (n_dropped > 0) {
zed_log_msg(LOG_WARNING, "Missed %d events", n_dropped); zed_log_msg(LOG_WARNING, "Missed %d events", n_dropped);
/* _bump_event_queue_length();
* FIXME: Increase max size of event nvlist in
* /sys/module/zfs/parameters/zfs_zevent_len_max ?
*/
} }
if (nvlist_lookup_uint64(nvl, "eid", &eid) != 0) { if (nvlist_lookup_uint64(nvl, "eid", &eid) != 0) {
zed_log_msg(LOG_WARNING, "Failed to lookup zevent eid"); zed_log_msg(LOG_WARNING, "Failed to lookup zevent eid");
@ -941,12 +976,12 @@ zed_event_service(struct zed_conf *zcp)
_zed_event_add_time_strings(eid, zsp, etime); _zed_event_add_time_strings(eid, zsp, etime);
zed_exec_process(eid, class, subclass, zed_exec_process(eid, class, subclass, zcp, zsp);
zcp->zedlet_dir, zcp->zedlets, zsp, zcp->zevent_fd);
zed_conf_write_state(zcp, eid, etime); zed_conf_write_state(zcp, eid, etime);
zed_strings_destroy(zsp); zed_strings_destroy(zsp);
} }
nvlist_free(nvl); nvlist_free(nvl);
return (0);
} }

View File

@ -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). * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC. * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
* Refer to the ZoL git commit log for authoritative copyright attribution. * Refer to the OpenZFS git commit log for authoritative copyright attribution.
* *
* The contents of this file are subject to the terms of the * The contents of this file are subject to the terms of the
* Common Development and Distribution License Version 1.0 (CDDL-1.0). * Common Development and Distribution License Version 1.0 (CDDL-1.0).
@ -17,13 +17,13 @@
#include <stdint.h> #include <stdint.h>
void zed_event_init(struct zed_conf *zcp); int zed_event_init(struct zed_conf *zcp);
void zed_event_fini(struct zed_conf *zcp); void zed_event_fini(struct zed_conf *zcp);
int zed_event_seek(struct zed_conf *zcp, uint64_t saved_eid, int zed_event_seek(struct zed_conf *zcp, uint64_t saved_eid,
int64_t saved_etime[]); int64_t saved_etime[]);
void zed_event_service(struct zed_conf *zcp); int zed_event_service(struct zed_conf *zcp);
#endif /* !ZED_EVENT_H */ #endif /* !ZED_EVENT_H */

View File

@ -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). * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC. * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
* Refer to the ZoL git commit log for authoritative copyright attribution. * Refer to the OpenZFS git commit log for authoritative copyright attribution.
* *
* The contents of this file are subject to the terms of the * The contents of this file are subject to the terms of the
* Common Development and Distribution License Version 1.0 (CDDL-1.0). * Common Development and Distribution License Version 1.0 (CDDL-1.0).
@ -18,16 +18,55 @@
#include <fcntl.h> #include <fcntl.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <stddef.h>
#include <sys/avl.h>
#include <sys/resource.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
#include "zed_file.h" #include <pthread.h>
#include <signal.h>
#include "zed_exec.h"
#include "zed_log.h" #include "zed_log.h"
#include "zed_strings.h" #include "zed_strings.h"
#define ZEVENT_FILENO 3 #define ZEVENT_FILENO 3
struct launched_process_node {
avl_node_t node;
pid_t pid;
uint64_t eid;
char *name;
};
static int
_launched_process_node_compare(const void *x1, const void *x2)
{
pid_t p1;
pid_t p2;
assert(x1 != NULL);
assert(x2 != NULL);
p1 = ((const struct launched_process_node *) x1)->pid;
p2 = ((const struct launched_process_node *) x2)->pid;
if (p1 < p2)
return (-1);
else if (p1 == p2)
return (0);
else
return (1);
}
static pthread_t _reap_children_tid = (pthread_t)-1;
static volatile boolean_t _reap_children_stop;
static avl_tree_t _launched_processes;
static pthread_mutex_t _launched_processes_lock = PTHREAD_MUTEX_INITIALIZER;
static int16_t _launched_processes_limit;
/* /*
* Create an environment string array for passing to execve() using the * Create an environment string array for passing to execve() using the
* NAME=VALUE strings in container [zsp]. * NAME=VALUE strings in container [zsp].
@ -78,20 +117,26 @@ _zed_exec_create_env(zed_strings_t *zsp)
*/ */
static void static void
_zed_exec_fork_child(uint64_t eid, const char *dir, const char *prog, _zed_exec_fork_child(uint64_t eid, const char *dir, const char *prog,
char *env[], int zfd) char *env[], int zfd, boolean_t in_foreground)
{ {
char path[PATH_MAX]; char path[PATH_MAX];
int n; int n;
pid_t pid; pid_t pid;
int fd; int fd;
pid_t wpid; struct launched_process_node *node;
int status; sigset_t mask;
struct timespec launch_timeout =
{ .tv_sec = 0, .tv_nsec = 200 * 1000 * 1000, };
assert(dir != NULL); assert(dir != NULL);
assert(prog != NULL); assert(prog != NULL);
assert(env != NULL); assert(env != NULL);
assert(zfd >= 0); assert(zfd >= 0);
while (__atomic_load_n(&_launched_processes_limit,
__ATOMIC_SEQ_CST) <= 0)
(void) nanosleep(&launch_timeout, NULL);
n = snprintf(path, sizeof (path), "%s/%s", dir, prog); n = snprintf(path, sizeof (path), "%s/%s", dir, prog);
if ((n < 0) || (n >= sizeof (path))) { if ((n < 0) || (n >= sizeof (path))) {
zed_log_msg(LOG_WARNING, zed_log_msg(LOG_WARNING,
@ -99,100 +144,179 @@ _zed_exec_fork_child(uint64_t eid, const char *dir, const char *prog,
prog, eid, strerror(ENAMETOOLONG)); prog, eid, strerror(ENAMETOOLONG));
return; return;
} }
(void) pthread_mutex_lock(&_launched_processes_lock);
pid = fork(); pid = fork();
if (pid < 0) { if (pid < 0) {
(void) pthread_mutex_unlock(&_launched_processes_lock);
zed_log_msg(LOG_WARNING, zed_log_msg(LOG_WARNING,
"Failed to fork \"%s\" for eid=%llu: %s", "Failed to fork \"%s\" for eid=%llu: %s",
prog, eid, strerror(errno)); prog, eid, strerror(errno));
return; return;
} else if (pid == 0) { } else if (pid == 0) {
(void) sigemptyset(&mask);
(void) sigprocmask(SIG_SETMASK, &mask, NULL);
(void) umask(022); (void) umask(022);
if ((fd = open("/dev/null", O_RDWR)) != -1) { if (in_foreground && /* we're already devnulled if daemonised */
(fd = open("/dev/null", O_RDWR | O_CLOEXEC)) != -1) {
(void) dup2(fd, STDIN_FILENO); (void) dup2(fd, STDIN_FILENO);
(void) dup2(fd, STDOUT_FILENO); (void) dup2(fd, STDOUT_FILENO);
(void) dup2(fd, STDERR_FILENO); (void) dup2(fd, STDERR_FILENO);
} }
(void) dup2(zfd, ZEVENT_FILENO); (void) dup2(zfd, ZEVENT_FILENO);
zed_file_close_from(ZEVENT_FILENO + 1);
execle(path, prog, NULL, env); execle(path, prog, NULL, env);
_exit(127); _exit(127);
} }
/* parent process */ /* parent process */
node = calloc(1, sizeof (*node));
if (node) {
node->pid = pid;
node->eid = eid;
node->name = strdup(prog);
avl_add(&_launched_processes, node);
}
(void) pthread_mutex_unlock(&_launched_processes_lock);
__atomic_sub_fetch(&_launched_processes_limit, 1, __ATOMIC_SEQ_CST);
zed_log_msg(LOG_INFO, "Invoking \"%s\" eid=%llu pid=%d", zed_log_msg(LOG_INFO, "Invoking \"%s\" eid=%llu pid=%d",
prog, eid, pid); prog, eid, pid);
}
/* FIXME: Timeout rogue child processes with sigalarm? */ static void
_nop(int sig)
{}
/* static void *
* Wait for child process using WNOHANG to limit _reap_children(void *arg)
* the time spent waiting to 10 seconds (10,000ms). {
*/ struct launched_process_node node, *pnode;
for (n = 0; n < 1000; n++) { pid_t pid;
wpid = waitpid(pid, &status, WNOHANG); int status;
if (wpid == (pid_t)-1) { struct rusage usage;
if (errno == EINTR) struct sigaction sa = {};
continue;
zed_log_msg(LOG_WARNING,
"Failed to wait for \"%s\" eid=%llu pid=%d",
prog, eid, pid);
break;
} else if (wpid == 0) {
struct timespec t;
/* child still running */ (void) sigfillset(&sa.sa_mask);
t.tv_sec = 0; (void) sigdelset(&sa.sa_mask, SIGCHLD);
t.tv_nsec = 10000000; /* 10ms */ (void) pthread_sigmask(SIG_SETMASK, &sa.sa_mask, NULL);
(void) nanosleep(&t, NULL);
continue;
}
if (WIFEXITED(status)) { (void) sigemptyset(&sa.sa_mask);
zed_log_msg(LOG_INFO, sa.sa_handler = _nop;
"Finished \"%s\" eid=%llu pid=%d exit=%d", sa.sa_flags = SA_NOCLDSTOP;
prog, eid, pid, WEXITSTATUS(status)); (void) sigaction(SIGCHLD, &sa, NULL);
} else if (WIFSIGNALED(status)) {
zed_log_msg(LOG_INFO, for (_reap_children_stop = B_FALSE; !_reap_children_stop; ) {
"Finished \"%s\" eid=%llu pid=%d sig=%d/%s", (void) pthread_mutex_lock(&_launched_processes_lock);
prog, eid, pid, WTERMSIG(status), pid = wait4(0, &status, WNOHANG, &usage);
strsignal(WTERMSIG(status)));
if (pid == 0 || pid == (pid_t)-1) {
(void) pthread_mutex_unlock(&_launched_processes_lock);
if (pid == 0 || errno == ECHILD)
pause();
else if (errno != EINTR)
zed_log_msg(LOG_WARNING,
"Failed to wait for children: %s",
strerror(errno));
} else { } else {
zed_log_msg(LOG_INFO, memset(&node, 0, sizeof (node));
"Finished \"%s\" eid=%llu pid=%d status=0x%X", node.pid = pid;
prog, eid, (unsigned int) status); pnode = avl_find(&_launched_processes, &node, NULL);
if (pnode) {
memcpy(&node, pnode, sizeof (node));
avl_remove(&_launched_processes, pnode);
free(pnode);
}
(void) pthread_mutex_unlock(&_launched_processes_lock);
__atomic_add_fetch(&_launched_processes_limit, 1,
__ATOMIC_SEQ_CST);
usage.ru_utime.tv_sec += usage.ru_stime.tv_sec;
usage.ru_utime.tv_usec += usage.ru_stime.tv_usec;
usage.ru_utime.tv_sec +=
usage.ru_utime.tv_usec / (1000 * 1000);
usage.ru_utime.tv_usec %= 1000 * 1000;
if (WIFEXITED(status)) {
zed_log_msg(LOG_INFO,
"Finished \"%s\" eid=%llu pid=%d "
"time=%llu.%06us exit=%d",
node.name, node.eid, pid,
(unsigned long long) usage.ru_utime.tv_sec,
(unsigned int) usage.ru_utime.tv_usec,
WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
zed_log_msg(LOG_INFO,
"Finished \"%s\" eid=%llu pid=%d "
"time=%llu.%06us sig=%d/%s",
node.name, node.eid, pid,
(unsigned long long) usage.ru_utime.tv_sec,
(unsigned int) usage.ru_utime.tv_usec,
WTERMSIG(status),
strsignal(WTERMSIG(status)));
} else {
zed_log_msg(LOG_INFO,
"Finished \"%s\" eid=%llu pid=%d "
"time=%llu.%06us status=0x%X",
node.name, node.eid,
(unsigned long long) usage.ru_utime.tv_sec,
(unsigned int) usage.ru_utime.tv_usec,
(unsigned int) status);
}
free(node.name);
} }
break;
} }
/* return (NULL);
* kill child process after 10 seconds }
*/
if (wpid == 0) { void
zed_log_msg(LOG_WARNING, "Killing hung \"%s\" pid=%d", zed_exec_fini(void)
prog, pid); {
(void) kill(pid, SIGKILL); struct launched_process_node *node;
void *ck = NULL;
if (_reap_children_tid == (pthread_t)-1)
return;
_reap_children_stop = B_TRUE;
(void) pthread_kill(_reap_children_tid, SIGCHLD);
(void) pthread_join(_reap_children_tid, NULL);
while ((node = avl_destroy_nodes(&_launched_processes, &ck)) != NULL) {
free(node->name);
free(node);
} }
avl_destroy(&_launched_processes);
(void) pthread_mutex_destroy(&_launched_processes_lock);
(void) pthread_mutex_init(&_launched_processes_lock, NULL);
_reap_children_tid = (pthread_t)-1;
} }
/* /*
* Process the event [eid] by synchronously invoking all zedlets with a * Process the event [eid] by synchronously invoking all zedlets with a
* matching class prefix. * matching class prefix.
* *
* Each executable in [zedlets] from the directory [dir] is matched against * Each executable in [zcp->zedlets] from the directory [zcp->zedlet_dir]
* the event's [class], [subclass], and the "all" class (which matches * is matched against the event's [class], [subclass], and the "all" class
* all events). Every zedlet with a matching class prefix is invoked. * (which matches all events).
* Every zedlet with a matching class prefix is invoked.
* The NAME=VALUE strings in [envs] will be passed to the zedlet as * The NAME=VALUE strings in [envs] will be passed to the zedlet as
* environment variables. * environment variables.
* *
* The file descriptor [zfd] is the zevent_fd used to track the * The file descriptor [zcp->zevent_fd] is the zevent_fd used to track the
* current cursor location within the zevent nvlist. * current cursor location within the zevent nvlist.
* *
* Return 0 on success, -1 on error. * Return 0 on success, -1 on error.
*/ */
int int
zed_exec_process(uint64_t eid, const char *class, const char *subclass, zed_exec_process(uint64_t eid, const char *class, const char *subclass,
const char *dir, zed_strings_t *zedlets, zed_strings_t *envs, int zfd) struct zed_conf *zcp, zed_strings_t *envs)
{ {
const char *class_strings[4]; const char *class_strings[4];
const char *allclass = "all"; const char *allclass = "all";
@ -201,9 +325,22 @@ zed_exec_process(uint64_t eid, const char *class, const char *subclass,
char **e; char **e;
int n; int n;
if (!dir || !zedlets || !envs || zfd < 0) if (!zcp->zedlet_dir || !zcp->zedlets || !envs || zcp->zevent_fd < 0)
return (-1); return (-1);
if (_reap_children_tid == (pthread_t)-1) {
_launched_processes_limit = zcp->max_jobs;
if (pthread_create(&_reap_children_tid, NULL,
_reap_children, NULL) != 0)
return (-1);
pthread_setname_np(_reap_children_tid, "reap ZEDLETs");
avl_create(&_launched_processes, _launched_process_node_compare,
sizeof (struct launched_process_node),
offsetof(struct launched_process_node, node));
}
csp = class_strings; csp = class_strings;
if (class) if (class)
@ -219,11 +356,13 @@ zed_exec_process(uint64_t eid, const char *class, const char *subclass,
e = _zed_exec_create_env(envs); e = _zed_exec_create_env(envs);
for (z = zed_strings_first(zedlets); z; z = zed_strings_next(zedlets)) { for (z = zed_strings_first(zcp->zedlets); z;
z = zed_strings_next(zcp->zedlets)) {
for (csp = class_strings; *csp; csp++) { for (csp = class_strings; *csp; csp++) {
n = strlen(*csp); n = strlen(*csp);
if ((strncmp(z, *csp, n) == 0) && !isalpha(z[n])) if ((strncmp(z, *csp, n) == 0) && !isalpha(z[n]))
_zed_exec_fork_child(eid, dir, z, e, zfd); _zed_exec_fork_child(eid, zcp->zedlet_dir,
z, e, zcp->zevent_fd, zcp->do_foreground);
} }
} }
free(e); free(e);

View File

@ -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). * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC. * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
* Refer to the ZoL git commit log for authoritative copyright attribution. * Refer to the OpenZFS git commit log for authoritative copyright attribution.
* *
* The contents of this file are subject to the terms of the * The contents of this file are subject to the terms of the
* Common Development and Distribution License Version 1.0 (CDDL-1.0). * Common Development and Distribution License Version 1.0 (CDDL-1.0).
@ -16,9 +16,12 @@
#define ZED_EXEC_H #define ZED_EXEC_H
#include <stdint.h> #include <stdint.h>
#include "zed_strings.h"
#include "zed_conf.h"
void zed_exec_fini(void);
int zed_exec_process(uint64_t eid, const char *class, const char *subclass, int zed_exec_process(uint64_t eid, const char *class, const char *subclass,
const char *dir, zed_strings_t *zedlets, zed_strings_t *envs, struct zed_conf *zcp, zed_strings_t *envs);
int zevent_fd);
#endif /* !ZED_EXEC_H */ #endif /* !ZED_EXEC_H */

View File

@ -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). * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC. * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
* Refer to the ZoL git commit log for authoritative copyright attribution. * Refer to the OpenZFS git commit log for authoritative copyright attribution.
* *
* The contents of this file are subject to the terms of the * The contents of this file are subject to the terms of the
* Common Development and Distribution License Version 1.0 (CDDL-1.0). * Common Development and Distribution License Version 1.0 (CDDL-1.0).
@ -12,72 +12,17 @@
* You may not use this file except in compliance with the license. * You may not use this file except in compliance with the license.
*/ */
#include <dirent.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h> #include <fcntl.h>
#include <limits.h> #include <limits.h>
#include <string.h> #include <string.h>
#include <sys/resource.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#include "zed_file.h"
#include "zed_log.h" #include "zed_log.h"
/*
* Read up to [n] bytes from [fd] into [buf].
* Return the number of bytes read, 0 on EOF, or -1 on error.
*/
ssize_t
zed_file_read_n(int fd, void *buf, size_t n)
{
unsigned char *p;
size_t n_left;
ssize_t n_read;
p = buf;
n_left = n;
while (n_left > 0) {
if ((n_read = read(fd, p, n_left)) < 0) {
if (errno == EINTR)
continue;
else
return (-1);
} else if (n_read == 0) {
break;
}
n_left -= n_read;
p += n_read;
}
return (n - n_left);
}
/*
* Write [n] bytes from [buf] out to [fd].
* Return the number of bytes written, or -1 on error.
*/
ssize_t
zed_file_write_n(int fd, void *buf, size_t n)
{
const unsigned char *p;
size_t n_left;
ssize_t n_written;
p = buf;
n_left = n;
while (n_left > 0) {
if ((n_written = write(fd, p, n_left)) < 0) {
if (errno == EINTR)
continue;
else
return (-1);
}
n_left -= n_written;
p += n_written;
}
return (n);
}
/* /*
* Set an exclusive advisory lock on the open file descriptor [fd]. * Set an exclusive advisory lock on the open file descriptor [fd].
* Return 0 on success, 1 if a conflicting lock is held by another process, * Return 0 on success, 1 if a conflicting lock is held by another process,
@ -159,6 +104,13 @@ zed_file_is_locked(int fd)
return (lock.l_pid); return (lock.l_pid);
} }
#if __APPLE__
#define PROC_SELF_FD "/dev/fd"
#else /* Linux-compatible layout */
#define PROC_SELF_FD "/proc/self/fd"
#endif
/* /*
* Close all open file descriptors greater than or equal to [lowfd]. * Close all open file descriptors greater than or equal to [lowfd].
* Any errors encountered while closing file descriptors are ignored. * Any errors encountered while closing file descriptors are ignored.
@ -166,51 +118,24 @@ zed_file_is_locked(int fd)
void void
zed_file_close_from(int lowfd) zed_file_close_from(int lowfd)
{ {
const int maxfd_def = 256; int errno_bak = errno;
int errno_bak; int maxfd = 0;
struct rlimit rl;
int maxfd;
int fd; int fd;
DIR *fddir;
struct dirent *fdent;
errno_bak = errno; if ((fddir = opendir(PROC_SELF_FD)) != NULL) {
while ((fdent = readdir(fddir)) != NULL) {
if (getrlimit(RLIMIT_NOFILE, &rl) < 0) { fd = atoi(fdent->d_name);
maxfd = maxfd_def; if (fd > maxfd && fd != dirfd(fddir))
} else if (rl.rlim_max == RLIM_INFINITY) { maxfd = fd;
maxfd = maxfd_def; }
(void) closedir(fddir);
} else { } else {
maxfd = rl.rlim_max; maxfd = sysconf(_SC_OPEN_MAX);
} }
for (fd = lowfd; fd < maxfd; fd++) for (fd = lowfd; fd < maxfd; fd++)
(void) close(fd); (void) close(fd);
errno = errno_bak; errno = errno_bak;
} }
/*
* Set the CLOEXEC flag on file descriptor [fd] so it will be automatically
* closed upon successful execution of one of the exec functions.
* Return 0 on success, or -1 on error.
*
* FIXME: No longer needed?
*/
int
zed_file_close_on_exec(int fd)
{
int flags;
if (fd < 0) {
errno = EBADF;
return (-1);
}
flags = fcntl(fd, F_GETFD);
if (flags == -1)
return (-1);
flags |= FD_CLOEXEC;
if (fcntl(fd, F_SETFD, flags) == -1)
return (-1);
return (0);
}

View File

@ -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). * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC. * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
* Refer to the ZoL git commit log for authoritative copyright attribution. * Refer to the OpenZFS git commit log for authoritative copyright attribution.
* *
* The contents of this file are subject to the terms of the * The contents of this file are subject to the terms of the
* Common Development and Distribution License Version 1.0 (CDDL-1.0). * Common Development and Distribution License Version 1.0 (CDDL-1.0).
@ -18,10 +18,6 @@
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
ssize_t zed_file_read_n(int fd, void *buf, size_t n);
ssize_t zed_file_write_n(int fd, void *buf, size_t n);
int zed_file_lock(int fd); int zed_file_lock(int fd);
int zed_file_unlock(int fd); int zed_file_unlock(int fd);
@ -30,6 +26,4 @@ pid_t zed_file_is_locked(int fd);
void zed_file_close_from(int fd); void zed_file_close_from(int fd);
int zed_file_close_on_exec(int fd);
#endif /* !ZED_FILE_H */ #endif /* !ZED_FILE_H */

View File

@ -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). * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC. * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
* Refer to the ZoL git commit log for authoritative copyright attribution. * Refer to the OpenZFS git commit log for authoritative copyright attribution.
* *
* The contents of this file are subject to the terms of the * The contents of this file are subject to the terms of the
* Common Development and Distribution License Version 1.0 (CDDL-1.0). * Common Development and Distribution License Version 1.0 (CDDL-1.0).

View File

@ -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). * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC. * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
* Refer to the ZoL git commit log for authoritative copyright attribution. * Refer to the OpenZFS git commit log for authoritative copyright attribution.
* *
* The contents of this file are subject to the terms of the * The contents of this file are subject to the terms of the
* Common Development and Distribution License Version 1.0 (CDDL-1.0). * Common Development and Distribution License Version 1.0 (CDDL-1.0).

View File

@ -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). * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC. * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
* Refer to the ZoL git commit log for authoritative copyright attribution. * Refer to the OpenZFS git commit log for authoritative copyright attribution.
* *
* The contents of this file are subject to the terms of the * The contents of this file are subject to the terms of the
* Common Development and Distribution License Version 1.0 (CDDL-1.0). * Common Development and Distribution License Version 1.0 (CDDL-1.0).
@ -108,7 +108,7 @@ _zed_strings_node_destroy(zed_strings_node_t *np)
* If [key] is specified, it will be used to index the node; otherwise, * If [key] is specified, it will be used to index the node; otherwise,
* the string [val] will be used. * the string [val] will be used.
*/ */
zed_strings_node_t * static zed_strings_node_t *
_zed_strings_node_create(const char *key, const char *val) _zed_strings_node_create(const char *key, const char *val)
{ {
zed_strings_node_t *np; zed_strings_node_t *np;

View File

@ -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). * Developed at Lawrence Livermore National Laboratory (LLNL-CODE-403049).
* Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC. * Copyright (C) 2013-2014 Lawrence Livermore National Security, LLC.
* Refer to the ZoL git commit log for authoritative copyright attribution. * Refer to the OpenZFS git commit log for authoritative copyright attribution.
* *
* The contents of this file are subject to the terms of the * The contents of this file are subject to the terms of the
* Common Development and Distribution License Version 1.0 (CDDL-1.0). * Common Development and Distribution License Version 1.0 (CDDL-1.0).

View File

@ -1,9 +1,5 @@
include $(top_srcdir)/config/Rules.am include $(top_srcdir)/config/Rules.am
DEFAULT_INCLUDES += \
-I$(top_srcdir)/include \
-I$(top_srcdir)/lib/libspl/include
sbin_PROGRAMS = zfs sbin_PROGRAMS = zfs
zfs_SOURCES = \ zfs_SOURCES = \
@ -15,7 +11,15 @@ zfs_SOURCES = \
zfs_projectutil.h zfs_projectutil.h
zfs_LDADD = \ zfs_LDADD = \
$(top_builddir)/lib/libnvpair/libnvpair.la \ $(abs_top_builddir)/lib/libzfs/libzfs.la \
$(top_builddir)/lib/libuutil/libuutil.la \ $(abs_top_builddir)/lib/libzfs_core/libzfs_core.la \
$(top_builddir)/lib/libzfs/libzfs.la \ $(abs_top_builddir)/lib/libnvpair/libnvpair.la \
$(top_builddir)/lib/libzfs_core/libzfs_core.la $(abs_top_builddir)/lib/libuutil/libuutil.la
zfs_LDADD += $(LTLIBINTL)
if BUILD_FREEBSD
zfs_LDADD += -lgeom -ljail
endif
include $(top_srcdir)/config/CppCheck.am

File diff suppressed because it is too large Load Diff

View File

@ -33,7 +33,7 @@ extern "C" {
void * safe_malloc(size_t size); void * safe_malloc(size_t size);
void nomem(void); void nomem(void);
libzfs_handle_t *g_zfs; extern libzfs_handle_t *g_zfs;
#ifdef __cplusplus #ifdef __cplusplus
} }

1
cmd/zfs_ids_to_path/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
zfs_ids_to_path

View File

@ -0,0 +1,11 @@
include $(top_srcdir)/config/Rules.am
sbin_PROGRAMS = zfs_ids_to_path
zfs_ids_to_path_SOURCES = \
zfs_ids_to_path.c
zfs_ids_to_path_LDADD = \
$(abs_top_builddir)/lib/libzfs/libzfs.la
include $(top_srcdir)/config/CppCheck.am

View File

@ -0,0 +1,96 @@
/*
* 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) 2019 by Delphix. All rights reserved.
*/
#include <libintl.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdint.h>
#include <libzfs.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
libzfs_handle_t *g_zfs;
static void
usage(int err)
{
fprintf(stderr, "Usage: zfs_ids_to_path [-v] <pool> <objset id> "
"<object id>\n");
exit(err);
}
int
main(int argc, char **argv)
{
boolean_t verbose = B_FALSE;
int c;
while ((c = getopt(argc, argv, "v")) != -1) {
switch (c) {
case 'v':
verbose = B_TRUE;
break;
}
}
argc -= optind;
argv += optind;
if (argc != 3) {
(void) fprintf(stderr, "Incorrect number of arguments: %d\n",
argc);
usage(1);
}
uint64_t objset, object;
if (sscanf(argv[1], "%llu", (u_longlong_t *)&objset) != 1) {
(void) fprintf(stderr, "Invalid objset id: %s\n", argv[1]);
usage(2);
}
if (sscanf(argv[2], "%llu", (u_longlong_t *)&object) != 1) {
(void) fprintf(stderr, "Invalid object id: %s\n", argv[2]);
usage(3);
}
if ((g_zfs = libzfs_init()) == NULL) {
(void) fprintf(stderr, "%s\n", libzfs_error_init(errno));
return (4);
}
zpool_handle_t *pool = zpool_open(g_zfs, argv[0]);
if (pool == NULL) {
fprintf(stderr, "Could not open pool %s\n", argv[0]);
libzfs_fini(g_zfs);
return (5);
}
char pathname[PATH_MAX * 2];
if (verbose) {
zpool_obj_to_path_ds(pool, objset, object, pathname,
sizeof (pathname));
} else {
zpool_obj_to_path(pool, objset, object, pathname,
sizeof (pathname));
}
printf("%s\n", pathname);
zpool_close(pool);
libzfs_fini(g_zfs);
return (0);
}

1
cmd/zgenhostid/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/zgenhostid

View File

@ -1 +1,7 @@
dist_bin_SCRIPTS = zgenhostid include $(top_srcdir)/config/Rules.am
sbin_PROGRAMS = zgenhostid
zgenhostid_SOURCES = zgenhostid.c
include $(top_srcdir)/config/CppCheck.am

View File

@ -1,61 +0,0 @@
#!/bin/bash
# Emulate genhostid(1) available on RHEL/CENTOS, for use on distros
# which do not provide that utility.
#
# Usage:
# zgenhostid
# zgenhostid <value>
#
# If /etc/hostid already exists and is size > 0, the script exits immediately
# and changes nothing. Unlike genhostid, this generates an error message.
#
# The first form generates a random hostid and stores it in /etc/hostid.
# The second form checks that the provided value is between 0x1 and 0xFFFFFFFF
# and if so, stores it in /etc/hostid. This form is not supported by
# genhostid(1).
hostid_file=/etc/hostid
function usage {
echo "$0 [value]"
echo "If $hostid_file is not present, store a hostid in it." >&2
echo "The optional value must be an 8-digit hex number between" >&2
echo "1 and 2^32-1. If no value is provided, a random one will" >&2
echo "be generated. The value must be unique among your systems." >&2
}
# hostid(1) ignores contents of /etc/hostid if size < 4 bytes. It would
# be better if this checked size >= 4 bytes but it the method must be
# widely portable.
if [ -s $hostid_file ]; then
echo "$hostid_file already exists. No change made." >&2
exit 1
fi
if [ -n "$1" ]; then
host_id=$1
else
# $RANDOM goes from 0..32k-1
number=$((((RANDOM % 4) * 32768 + RANDOM) * 32768 + RANDOM))
host_id=$(printf "%08x" $number)
fi
if egrep -o '^0{8}$' <<< $host_id >/dev/null 2>&1; then
usage
exit 2
fi
if ! egrep -o '^[a-fA-F0-9]{8}$' <<< $host_id >/dev/null 2>&1; then
usage
exit 3
fi
a=${host_id:6:2}
b=${host_id:4:2}
c=${host_id:2:2}
d=${host_id:0:2}
echo -ne \\x$a\\x$b\\x$c\\x$d > $hostid_file
exit 0

141
cmd/zgenhostid/zgenhostid.c Normal file
View File

@ -0,0 +1,141 @@
/*
* 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) 2020, Georgy Yakovlev. All rights reserved.
*/
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <inttypes.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
static __attribute__((noreturn)) void
usage(void)
{
(void) fprintf(stderr,
"usage: zgenhostid [-fh] [-o path] [value]\n\n"
" -f\t\t force hostid file write\n"
" -h\t\t print this usage and exit\n"
" -o <filename>\t write hostid to this file\n\n"
"If hostid file is not present, store a hostid in it.\n"
"The optional value should be an 8-digit hex number between"
" 1 and 2^32-1.\n"
"If the value is 0 or no value is provided, a random one"
" will be generated.\n"
"The value must be unique among your systems.\n");
exit(EXIT_FAILURE);
}
int
main(int argc, char **argv)
{
/* default file path, can be optionally set by user */
const char *path = "/etc/hostid";
/* holds converted user input or lrand48() generated value */
unsigned long input_i = 0;
int opt;
int force_fwrite = 0;
while ((opt = getopt_long(argc, argv, "fo:h?", 0, 0)) != -1) {
switch (opt) {
case 'f':
force_fwrite = 1;
break;
case 'o':
path = optarg;
break;
case 'h':
case '?':
usage();
}
}
char *in_s = argv[optind];
if (in_s != NULL) {
/* increment pointer by 2 if string is 0x prefixed */
if (strncasecmp("0x", in_s, 2) == 0) {
in_s += 2;
}
/* need to be exactly 8 characters */
const char *hex = "0123456789abcdefABCDEF";
if (strlen(in_s) != 8 || strspn(in_s, hex) != 8) {
fprintf(stderr, "%s\n", strerror(ERANGE));
usage();
}
input_i = strtoul(in_s, NULL, 16);
if (errno != 0) {
perror("strtoul");
exit(EXIT_FAILURE);
}
if (input_i > UINT32_MAX) {
fprintf(stderr, "%s\n", strerror(ERANGE));
usage();
}
}
struct stat fstat;
if (force_fwrite == 0 && stat(path, &fstat) == 0 &&
S_ISREG(fstat.st_mode)) {
fprintf(stderr, "%s: %s\n", path, strerror(EEXIST));
exit(EXIT_FAILURE);
}
/*
* generate if not provided by user
* also handle unlikely zero return from lrand48()
*/
while (input_i == 0) {
srand48(getpid() ^ time(NULL));
input_i = lrand48();
}
FILE *fp = fopen(path, "wb");
if (!fp) {
perror("fopen");
exit(EXIT_FAILURE);
}
/*
* we need just 4 bytes in native endianness
* not using sethostid() because it may be missing or just a stub
*/
uint32_t hostid = input_i;
int written = fwrite(&hostid, 1, 4, fp);
if (written != 4) {
perror("fwrite");
exit(EXIT_FAILURE);
}
fclose(fp);
exit(EXIT_SUCCESS);
}

View File

@ -1,8 +1,7 @@
include $(top_srcdir)/config/Rules.am include $(top_srcdir)/config/Rules.am
DEFAULT_INCLUDES += \ # Unconditionally enable debugging for zhack
-I$(top_srcdir)/include \ AM_CPPFLAGS += -DDEBUG -UNDEBUG -DZFS_DEBUG
-I$(top_srcdir)/lib/libspl/include
sbin_PROGRAMS = zhack sbin_PROGRAMS = zhack
@ -10,5 +9,8 @@ zhack_SOURCES = \
zhack.c zhack.c
zhack_LDADD = \ zhack_LDADD = \
$(top_builddir)/lib/libnvpair/libnvpair.la \ $(abs_top_builddir)/lib/libzpool/libzpool.la \
$(top_builddir)/lib/libzpool/libzpool.la $(abs_top_builddir)/lib/libzfs_core/libzfs_core.la \
$(abs_top_builddir)/lib/libnvpair/libnvpair.la
include $(top_srcdir)/config/CppCheck.am

View File

@ -48,16 +48,15 @@
#include <sys/zio_compress.h> #include <sys/zio_compress.h>
#include <sys/zfeature.h> #include <sys/zfeature.h>
#include <sys/dmu_tx.h> #include <sys/dmu_tx.h>
#include <zfeature_common.h>
#include <libzutil.h> #include <libzutil.h>
extern boolean_t zfeature_checks_disable;
const char cmdname[] = "zhack"; const char cmdname[] = "zhack";
static importargs_t g_importargs; static importargs_t g_importargs;
static char *g_pool; static char *g_pool;
static boolean_t g_readonly; static boolean_t g_readonly;
static void static __attribute__((noreturn)) void
usage(void) usage(void)
{ {
(void) fprintf(stderr, (void) fprintf(stderr,
@ -82,7 +81,7 @@ usage(void)
} }
static void static __attribute__((noreturn)) __attribute__((format(printf, 3, 4))) void
fatal(spa_t *spa, void *tag, const char *fmt, ...) fatal(spa_t *spa, void *tag, const char *fmt, ...)
{ {
va_list ap; va_list ap;
@ -103,8 +102,8 @@ fatal(spa_t *spa, void *tag, const char *fmt, ...)
/* ARGSUSED */ /* ARGSUSED */
static int static int
space_delta_cb(dmu_object_type_t bonustype, void *data, space_delta_cb(dmu_object_type_t bonustype, const void *data,
uint64_t *userp, uint64_t *groupp, uint64_t *projectp) zfs_file_info_t *zoi)
{ {
/* /*
* Is it a valid type of object to track? * Is it a valid type of object to track?
@ -113,7 +112,6 @@ space_delta_cb(dmu_object_type_t bonustype, void *data,
return (ENOENT); return (ENOENT);
(void) fprintf(stderr, "modifying object that needs user accounting"); (void) fprintf(stderr, "modifying object that needs user accounting");
abort(); abort();
/* NOTREACHED */
} }
/* /*
@ -126,7 +124,8 @@ zhack_import(char *target, boolean_t readonly)
nvlist_t *props; nvlist_t *props;
int error; int error;
kernel_init(readonly ? FREAD : (FREAD | FWRITE)); kernel_init(readonly ? SPA_MODE_READ :
(SPA_MODE_READ | SPA_MODE_WRITE));
dmu_objset_register_type(DMU_OST_ZFS, space_delta_cb); dmu_objset_register_type(DMU_OST_ZFS, space_delta_cb);
@ -149,6 +148,7 @@ zhack_import(char *target, boolean_t readonly)
zfeature_checks_disable = B_TRUE; zfeature_checks_disable = B_TRUE;
error = spa_import(target, config, props, error = spa_import(target, config, props,
(readonly ? ZFS_IMPORT_SKIP_MMP : ZFS_IMPORT_NORMAL)); (readonly ? ZFS_IMPORT_SKIP_MMP : ZFS_IMPORT_NORMAL));
fnvlist_free(config);
zfeature_checks_disable = B_FALSE; zfeature_checks_disable = B_FALSE;
if (error == EEXIST) if (error == EEXIST)
error = 0; error = 0;
@ -317,7 +317,8 @@ zhack_do_feature_enable(int argc, char **argv)
mos = spa->spa_meta_objset; mos = spa->spa_meta_objset;
if (zfeature_is_supported(feature.fi_guid)) if (zfeature_is_supported(feature.fi_guid))
fatal(spa, FTAG, "'%s' is a real feature, will not enable"); fatal(spa, FTAG, "'%s' is a real feature, will not enable",
feature.fi_guid);
if (0 == zap_contains(mos, spa->spa_feat_desc_obj, feature.fi_guid)) if (0 == zap_contains(mos, spa->spa_feat_desc_obj, feature.fi_guid))
fatal(spa, FTAG, "feature already enabled: %s", fatal(spa, FTAG, "feature already enabled: %s",
feature.fi_guid); feature.fi_guid);
@ -411,7 +412,8 @@ zhack_do_feature_ref(int argc, char **argv)
if (zfeature_is_supported(feature.fi_guid)) { if (zfeature_is_supported(feature.fi_guid)) {
fatal(spa, FTAG, fatal(spa, FTAG,
"'%s' is a real feature, will not change refcount"); "'%s' is a real feature, will not change refcount",
feature.fi_guid);
} }
if (0 == zap_contains(mos, spa->spa_feat_for_read_obj, if (0 == zap_contains(mos, spa->spa_feat_for_read_obj,

View File

@ -1,9 +1,5 @@
include $(top_srcdir)/config/Rules.am include $(top_srcdir)/config/Rules.am
DEFAULT_INCLUDES += \
-I$(top_srcdir)/include \
-I$(top_srcdir)/lib/libspl/include
sbin_PROGRAMS = zinject sbin_PROGRAMS = zinject
zinject_SOURCES = \ zinject_SOURCES = \
@ -12,5 +8,8 @@ zinject_SOURCES = \
zinject.h zinject.h
zinject_LDADD = \ zinject_LDADD = \
$(top_builddir)/lib/libnvpair/libnvpair.la \ $(abs_top_builddir)/lib/libzfs/libzfs.la \
$(top_builddir)/lib/libzfs/libzfs.la $(abs_top_builddir)/lib/libzfs_core/libzfs_core.la \
$(abs_top_builddir)/lib/libnvpair/libnvpair.la
include $(top_srcdir)/config/CppCheck.am

Some files were not shown because too many files have changed in this diff Show More