Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FreeBSD: Clean up zfsdev_close to match Linux #11720

Merged
merged 4 commits into from
Mar 13, 2021

Conversation

ghost
Copy link

@ghost ghost commented Mar 9, 2021

Motivation and Context

We received reports of panics in zfs_onexit_destroy() caused by it being called with a NULL zs_onexit pointer. Upon investigation I discovered oddities in zfsdev_close() that were not present in the equivalent function for Linux.

When opening /dev/zfs, FreeBSD invokes zfsdev_open(), which takes zfsdev_state_lock around a call to zfs_ctldev_init(). Here we allocate a unique minor number and find a free state object in the global state list or allocate a new one if all existing objects are in use. We assign the state to the per-open filedescriptor data along with the destructor zfsdev_close(). This ensures zfsdev_close() is called when the last reference to the descriptor is dropped.

In zfsdev_close(), we receive the pointer to our state and take zfsdev_state_lock. It should be safe to assume this points to a valid state, as we are guaranteed to be called exactly once for this descriptor data and nothing else should be reusing the same state. This is where the existing code went off into the weeds. We tried to find the state object in the global state list to verify the state we have actually exists, which it surely must for us to be here. If we did find our state in the list, it was further validated by looking for a sentinel value in zs_minor to check if it had already been destructed, which it surely hasn't (if the code is correct) because we haven't done that yet. Neither of these checks are performed on Linux.

Now we actually invalidate the state and, still under zfsdev_state_lock, begin to destroy it. Then the lock is released, and the rest of the state is nulled out. Because we are not protected by the lock at this point, another open may have claimed this state before we finished nulling it, leading to the observed panic when the destructor is eventually called for that state. On Linux, the state is fully destroyed and nulled under the protection of the lock.

Description

Eliminate the gratuitous checks.
Assert that we do not have an impossible value in zs_minor, for consistency with Linux.
Keep zfsdev_state_lock held until we have fully destroyed and nulled the state.
While looking at minor numbers I spotted an unused #define ZFS_MIN_MINOR (ZFSDEV_MAX_MINOR + 1), and pruned it.

How Has This Been Tested?

Many rounds of ZTS, then a fixed module was provided to the reporter who reported back that so far they have been able to push the system much harder without encountering the issue.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Performance enhancement (non-breaking change which improves efficiency)
  • Code cleanup (non-breaking change which makes code smaller or more readable)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Library ABI change (libzfs, libzfs_core, libnvpair, libuutil and libzfsbootenv)
  • Documentation (a change to man pages or other documentation)

Checklist:

Ryan Moeller added 4 commits March 9, 2021 09:49
Signed-off-by: Ryan Moeller <ryan@iXsystems.com>
Signed-off-by: Ryan Moeller <ryan@iXsystems.com>
Signed-off-by: Ryan Moeller <ryan@iXsystems.com>
Signed-off-by: Ryan Moeller <ryan@iXsystems.com>
@ghost ghost added the Status: Code Review Needed Ready for review and testing label Mar 9, 2021
@ghost ghost requested a review from amotin March 9, 2021 23:08
@ghost
Copy link
Author

ghost commented Mar 9, 2021

After this cleanup, I think it should be possible to avoid taking the lock at all by moving the state invalidation to the end of the destructor. Nothing else is supposed to access the fields of the state we need to destroy until zs_minor is set to -1, so doing that last should make the other operations safe without locking.

@amotin
Copy link
Member

amotin commented Mar 12, 2021

After this cleanup, I think it should be possible to avoid taking the lock at all by moving the state invalidation to the end of the destructor. Nothing else is supposed to access the fields of the state we need to destroy until zs_minor is set to -1, so doing that last should make the other operations safe without locking.

I think you are right. But there should be some fence around that zs_minor setting to -1, either wmb() or atomic_store_rel_int() to not hit the issue you are just fixing on some less ordered platforms.

@ghost ghost added Status: Accepted Ready to integrate (reviewed, tested) and removed Status: Code Review Needed Ready for review and testing labels Mar 12, 2021
@behlendorf behlendorf merged commit f845b2d into openzfs:master Mar 13, 2021
@ghost ghost deleted the zfsdev-minor branch March 13, 2021 05:59
jsai20 pushed a commit to jsai20/zfs that referenced this pull request Mar 30, 2021
Resolve some oddities in zfsdev_close() which could result in a
panic and were not present in the equivalent function for Linux.

- Remove unused definition ZFS_MIN_MINOR
- FreeBSD: Simplify zfsdev state destruction
- Assert zs_minor is valid in zfsdev_close
- Make locking around zfsdev state match Linux

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Alexander Motin <mav@FreeBSD.org>
Signed-off-by: Ryan Moeller <ryan@iXsystems.com>
Closes openzfs#11720
adamdmoss pushed a commit to adamdmoss/zfs that referenced this pull request Apr 10, 2021
Resolve some oddities in zfsdev_close() which could result in a
panic and were not present in the equivalent function for Linux.

- Remove unused definition ZFS_MIN_MINOR
- FreeBSD: Simplify zfsdev state destruction
- Assert zs_minor is valid in zfsdev_close
- Make locking around zfsdev state match Linux

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Alexander Motin <mav@FreeBSD.org>
Signed-off-by: Ryan Moeller <ryan@iXsystems.com>
Closes openzfs#11720
ghost pushed a commit to truenas/zfs that referenced this pull request May 6, 2021
Resolve some oddities in zfsdev_close() which could result in a
panic and were not present in the equivalent function for Linux.

- Remove unused definition ZFS_MIN_MINOR
- FreeBSD: Simplify zfsdev state destruction
- Assert zs_minor is valid in zfsdev_close
- Make locking around zfsdev state match Linux

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Alexander Motin <mav@FreeBSD.org>
Signed-off-by: Ryan Moeller <ryan@iXsystems.com>
Closes openzfs#11720
ghost pushed a commit to truenas/zfs that referenced this pull request May 7, 2021
Resolve some oddities in zfsdev_close() which could result in a
panic and were not present in the equivalent function for Linux.

- Remove unused definition ZFS_MIN_MINOR
- FreeBSD: Simplify zfsdev state destruction
- Assert zs_minor is valid in zfsdev_close
- Make locking around zfsdev state match Linux

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Alexander Motin <mav@FreeBSD.org>
Signed-off-by: Ryan Moeller <ryan@iXsystems.com>
Closes openzfs#11720
ghost pushed a commit to truenas/zfs that referenced this pull request May 10, 2021
Resolve some oddities in zfsdev_close() which could result in a
panic and were not present in the equivalent function for Linux.

- Remove unused definition ZFS_MIN_MINOR
- FreeBSD: Simplify zfsdev state destruction
- Assert zs_minor is valid in zfsdev_close
- Make locking around zfsdev state match Linux

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Alexander Motin <mav@FreeBSD.org>
Signed-off-by: Ryan Moeller <ryan@iXsystems.com>
Closes openzfs#11720
ghost pushed a commit to truenas/zfs that referenced this pull request May 10, 2021
Resolve some oddities in zfsdev_close() which could result in a
panic and were not present in the equivalent function for Linux.

- Remove unused definition ZFS_MIN_MINOR
- FreeBSD: Simplify zfsdev state destruction
- Assert zs_minor is valid in zfsdev_close
- Make locking around zfsdev state match Linux

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Alexander Motin <mav@FreeBSD.org>
Signed-off-by: Ryan Moeller <ryan@iXsystems.com>
Closes openzfs#11720
ghost pushed a commit to truenas/zfs that referenced this pull request May 10, 2021
Resolve some oddities in zfsdev_close() which could result in a
panic and were not present in the equivalent function for Linux.

- Remove unused definition ZFS_MIN_MINOR
- FreeBSD: Simplify zfsdev state destruction
- Assert zs_minor is valid in zfsdev_close
- Make locking around zfsdev state match Linux

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Alexander Motin <mav@FreeBSD.org>
Signed-off-by: Ryan Moeller <ryan@iXsystems.com>
Closes openzfs#11720
ghost pushed a commit to truenas/zfs that referenced this pull request May 13, 2021
Resolve some oddities in zfsdev_close() which could result in a
panic and were not present in the equivalent function for Linux.

- Remove unused definition ZFS_MIN_MINOR
- FreeBSD: Simplify zfsdev state destruction
- Assert zs_minor is valid in zfsdev_close
- Make locking around zfsdev state match Linux

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Alexander Motin <mav@FreeBSD.org>
Signed-off-by: Ryan Moeller <ryan@iXsystems.com>
Closes openzfs#11720
behlendorf pushed a commit that referenced this pull request May 20, 2021
Resolve some oddities in zfsdev_close() which could result in a
panic and were not present in the equivalent function for Linux.

- Remove unused definition ZFS_MIN_MINOR
- FreeBSD: Simplify zfsdev state destruction
- Assert zs_minor is valid in zfsdev_close
- Make locking around zfsdev state match Linux

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Alexander Motin <mav@FreeBSD.org>
Signed-off-by: Ryan Moeller <ryan@iXsystems.com>
Closes #11720
sempervictus pushed a commit to sempervictus/zfs that referenced this pull request May 31, 2021
Resolve some oddities in zfsdev_close() which could result in a
panic and were not present in the equivalent function for Linux.

- Remove unused definition ZFS_MIN_MINOR
- FreeBSD: Simplify zfsdev state destruction
- Assert zs_minor is valid in zfsdev_close
- Make locking around zfsdev state match Linux

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Alexander Motin <mav@FreeBSD.org>
Signed-off-by: Ryan Moeller <ryan@iXsystems.com>
Closes openzfs#11720
tonyhutter pushed a commit that referenced this pull request Jun 23, 2021
Resolve some oddities in zfsdev_close() which could result in a
panic and were not present in the equivalent function for Linux.

- Remove unused definition ZFS_MIN_MINOR
- FreeBSD: Simplify zfsdev state destruction
- Assert zs_minor is valid in zfsdev_close
- Make locking around zfsdev state match Linux

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Alexander Motin <mav@FreeBSD.org>
Signed-off-by: Ryan Moeller <ryan@iXsystems.com>
Closes #11720
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Status: Accepted Ready to integrate (reviewed, tested)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants