ceph: map snapid to anonymous bdev ID
ceph_getattr() return zero dev ID for head inodes and set dev ID to snapid directly for snaphost inodes. This is not good because userspace utilities may consider device ID of 0 as invalid, snapid may conflict with other device's ID. This patch introduces "snapids to anonymous bdev IDs" map. we create a new mapping when we see a snapid for the first time. we trim unused mapping after it is ilde for 5 minutes. Link: http://tracker.ceph.com/issues/22353 Signed-off-by: "Yan, Zheng" <zyan@redhat.com> Acked-by: Jeff Layton <jlayton@redhat.com> Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
This commit is contained in:
156
fs/ceph/snap.c
156
fs/ceph/snap.c
@@ -3,12 +3,13 @@
|
||||
|
||||
#include <linux/sort.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "super.h"
|
||||
#include "mds_client.h"
|
||||
|
||||
#include <linux/ceph/decode.h>
|
||||
|
||||
/* unused map expires after 5 minutes */
|
||||
#define CEPH_SNAPID_MAP_TIMEOUT (5 * 60 * HZ)
|
||||
|
||||
/*
|
||||
* Snapshots in ceph are driven in large part by cooperation from the
|
||||
* client. In contrast to local file systems or file servers that
|
||||
@@ -989,3 +990,154 @@ out:
|
||||
up_write(&mdsc->snap_rwsem);
|
||||
return;
|
||||
}
|
||||
|
||||
struct ceph_snapid_map* ceph_get_snapid_map(struct ceph_mds_client *mdsc,
|
||||
u64 snap)
|
||||
{
|
||||
struct ceph_snapid_map *sm, *exist;
|
||||
struct rb_node **p, *parent;
|
||||
int ret;
|
||||
|
||||
exist = NULL;
|
||||
spin_lock(&mdsc->snapid_map_lock);
|
||||
p = &mdsc->snapid_map_tree.rb_node;
|
||||
while (*p) {
|
||||
exist = rb_entry(*p, struct ceph_snapid_map, node);
|
||||
if (snap > exist->snap) {
|
||||
p = &(*p)->rb_left;
|
||||
} else if (snap < exist->snap) {
|
||||
p = &(*p)->rb_right;
|
||||
} else {
|
||||
if (atomic_inc_return(&exist->ref) == 1)
|
||||
list_del_init(&exist->lru);
|
||||
break;
|
||||
}
|
||||
exist = NULL;
|
||||
}
|
||||
spin_unlock(&mdsc->snapid_map_lock);
|
||||
if (exist) {
|
||||
dout("found snapid map %llx -> %x\n", exist->snap, exist->dev);
|
||||
return exist;
|
||||
}
|
||||
|
||||
sm = kmalloc(sizeof(*sm), GFP_NOFS);
|
||||
if (!sm)
|
||||
return NULL;
|
||||
|
||||
ret = get_anon_bdev(&sm->dev);
|
||||
if (ret < 0) {
|
||||
kfree(sm);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&sm->lru);
|
||||
atomic_set(&sm->ref, 1);
|
||||
sm->snap = snap;
|
||||
|
||||
exist = NULL;
|
||||
parent = NULL;
|
||||
p = &mdsc->snapid_map_tree.rb_node;
|
||||
spin_lock(&mdsc->snapid_map_lock);
|
||||
while (*p) {
|
||||
parent = *p;
|
||||
exist = rb_entry(*p, struct ceph_snapid_map, node);
|
||||
if (snap > exist->snap)
|
||||
p = &(*p)->rb_left;
|
||||
else if (snap < exist->snap)
|
||||
p = &(*p)->rb_right;
|
||||
else
|
||||
break;
|
||||
exist = NULL;
|
||||
}
|
||||
if (exist) {
|
||||
if (atomic_inc_return(&exist->ref) == 1)
|
||||
list_del_init(&exist->lru);
|
||||
} else {
|
||||
rb_link_node(&sm->node, parent, p);
|
||||
rb_insert_color(&sm->node, &mdsc->snapid_map_tree);
|
||||
}
|
||||
spin_unlock(&mdsc->snapid_map_lock);
|
||||
if (exist) {
|
||||
free_anon_bdev(sm->dev);
|
||||
kfree(sm);
|
||||
dout("found snapid map %llx -> %x\n", exist->snap, exist->dev);
|
||||
return exist;
|
||||
}
|
||||
|
||||
dout("create snapid map %llx -> %x\n", sm->snap, sm->dev);
|
||||
return sm;
|
||||
}
|
||||
|
||||
void ceph_put_snapid_map(struct ceph_mds_client* mdsc,
|
||||
struct ceph_snapid_map *sm)
|
||||
{
|
||||
if (!sm)
|
||||
return;
|
||||
if (atomic_dec_and_lock(&sm->ref, &mdsc->snapid_map_lock)) {
|
||||
if (!RB_EMPTY_NODE(&sm->node)) {
|
||||
sm->last_used = jiffies;
|
||||
list_add_tail(&sm->lru, &mdsc->snapid_map_lru);
|
||||
spin_unlock(&mdsc->snapid_map_lock);
|
||||
} else {
|
||||
/* already cleaned up by
|
||||
* ceph_cleanup_snapid_map() */
|
||||
spin_unlock(&mdsc->snapid_map_lock);
|
||||
kfree(sm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ceph_trim_snapid_map(struct ceph_mds_client *mdsc)
|
||||
{
|
||||
struct ceph_snapid_map *sm;
|
||||
unsigned long now;
|
||||
LIST_HEAD(to_free);
|
||||
|
||||
spin_lock(&mdsc->snapid_map_lock);
|
||||
now = jiffies;
|
||||
|
||||
while (!list_empty(&mdsc->snapid_map_lru)) {
|
||||
sm = list_first_entry(&mdsc->snapid_map_lru,
|
||||
struct ceph_snapid_map, lru);
|
||||
if (time_after(sm->last_used + CEPH_SNAPID_MAP_TIMEOUT, now))
|
||||
break;
|
||||
|
||||
rb_erase(&sm->node, &mdsc->snapid_map_tree);
|
||||
list_move(&sm->lru, &to_free);
|
||||
}
|
||||
spin_unlock(&mdsc->snapid_map_lock);
|
||||
|
||||
while (!list_empty(&to_free)) {
|
||||
sm = list_first_entry(&to_free, struct ceph_snapid_map, lru);
|
||||
list_del(&sm->lru);
|
||||
dout("trim snapid map %llx -> %x\n", sm->snap, sm->dev);
|
||||
free_anon_bdev(sm->dev);
|
||||
kfree(sm);
|
||||
}
|
||||
}
|
||||
|
||||
void ceph_cleanup_snapid_map(struct ceph_mds_client *mdsc)
|
||||
{
|
||||
struct ceph_snapid_map *sm;
|
||||
struct rb_node *p;
|
||||
LIST_HEAD(to_free);
|
||||
|
||||
spin_lock(&mdsc->snapid_map_lock);
|
||||
while ((p = rb_first(&mdsc->snapid_map_tree))) {
|
||||
sm = rb_entry(p, struct ceph_snapid_map, node);
|
||||
rb_erase(p, &mdsc->snapid_map_tree);
|
||||
RB_CLEAR_NODE(p);
|
||||
list_move(&sm->lru, &to_free);
|
||||
}
|
||||
spin_unlock(&mdsc->snapid_map_lock);
|
||||
|
||||
while (!list_empty(&to_free)) {
|
||||
sm = list_first_entry(&to_free, struct ceph_snapid_map, lru);
|
||||
list_del(&sm->lru);
|
||||
free_anon_bdev(sm->dev);
|
||||
if (WARN_ON_ONCE(atomic_read(&sm->ref))) {
|
||||
pr_err("snapid map %llx -> %x still in use\n",
|
||||
sm->snap, sm->dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user