Skip to content

Commit 2fe0758

Browse files
jtlaytongregkh
authored andcommitted
ceph: take snap_empty_lock atomically with snaprealm refcount change
commit 8434ffe upstream. There is a race in ceph_put_snap_realm. The change to the nref and the spinlock acquisition are not done atomically, so you could decrement nref, and before you take the spinlock, the nref is incremented again. At that point, you end up putting it on the empty list when it shouldn't be there. Eventually __cleanup_empty_realms runs and frees it when it's still in-use. Fix this by protecting the 1->0 transition with atomic_dec_and_lock, and just drop the spinlock if we can get the rwsem. Because these objects can also undergo a 0->1 refcount transition, we must protect that change as well with the spinlock. Increment locklessly unless the value is at 0, in which case we take the spinlock, increment and then take it off the empty list if it did the 0->1 transition. With these changes, I'm removing the dout() messages from these functions, as well as in __put_snap_realm. They've always been racy, and it's better to not print values that may be misleading. Cc: stable@vger.kernel.org URL: https://tracker.ceph.com/issues/46419 Reported-by: Mark Nelson <mnelson@redhat.com> Signed-off-by: Jeff Layton <jlayton@kernel.org> Reviewed-by: Luis Henriques <lhenriques@suse.de> Signed-off-by: Ilya Dryomov <idryomov@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent a23aced commit 2fe0758

1 file changed

Lines changed: 17 additions & 17 deletions

File tree

fs/ceph/snap.c

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -67,19 +67,19 @@ void ceph_get_snap_realm(struct ceph_mds_client *mdsc,
6767
{
6868
lockdep_assert_held(&mdsc->snap_rwsem);
6969

70-
dout("get_realm %p %d -> %d\n", realm,
71-
atomic_read(&realm->nref), atomic_read(&realm->nref)+1);
7270
/*
73-
* since we _only_ increment realm refs or empty the empty
74-
* list with snap_rwsem held, adjusting the empty list here is
75-
* safe. we do need to protect against concurrent empty list
76-
* additions, however.
71+
* The 0->1 and 1->0 transitions must take the snap_empty_lock
72+
* atomically with the refcount change. Go ahead and bump the
73+
* nref here, unless it's 0, in which case we take the spinlock
74+
* and then do the increment and remove it from the list.
7775
*/
78-
if (atomic_inc_return(&realm->nref) == 1) {
79-
spin_lock(&mdsc->snap_empty_lock);
76+
if (atomic_inc_not_zero(&realm->nref))
77+
return;
78+
79+
spin_lock(&mdsc->snap_empty_lock);
80+
if (atomic_inc_return(&realm->nref) == 1)
8081
list_del_init(&realm->empty_item);
81-
spin_unlock(&mdsc->snap_empty_lock);
82-
}
82+
spin_unlock(&mdsc->snap_empty_lock);
8383
}
8484

8585
static void __insert_snap_realm(struct rb_root *root,
@@ -208,28 +208,28 @@ static void __put_snap_realm(struct ceph_mds_client *mdsc,
208208
{
209209
lockdep_assert_held_write(&mdsc->snap_rwsem);
210210

211-
dout("__put_snap_realm %llx %p %d -> %d\n", realm->ino, realm,
212-
atomic_read(&realm->nref), atomic_read(&realm->nref)-1);
211+
/*
212+
* We do not require the snap_empty_lock here, as any caller that
213+
* increments the value must hold the snap_rwsem.
214+
*/
213215
if (atomic_dec_and_test(&realm->nref))
214216
__destroy_snap_realm(mdsc, realm);
215217
}
216218

217219
/*
218-
* caller needn't hold any locks
220+
* See comments in ceph_get_snap_realm. Caller needn't hold any locks.
219221
*/
220222
void ceph_put_snap_realm(struct ceph_mds_client *mdsc,
221223
struct ceph_snap_realm *realm)
222224
{
223-
dout("put_snap_realm %llx %p %d -> %d\n", realm->ino, realm,
224-
atomic_read(&realm->nref), atomic_read(&realm->nref)-1);
225-
if (!atomic_dec_and_test(&realm->nref))
225+
if (!atomic_dec_and_lock(&realm->nref, &mdsc->snap_empty_lock))
226226
return;
227227

228228
if (down_write_trylock(&mdsc->snap_rwsem)) {
229+
spin_unlock(&mdsc->snap_empty_lock);
229230
__destroy_snap_realm(mdsc, realm);
230231
up_write(&mdsc->snap_rwsem);
231232
} else {
232-
spin_lock(&mdsc->snap_empty_lock);
233233
list_add(&realm->empty_item, &mdsc->snap_empty);
234234
spin_unlock(&mdsc->snap_empty_lock);
235235
}

0 commit comments

Comments
 (0)