--- /dev/null
+#ifndef _FS_CEPH_STRING_TABLE_H
+#define _FS_CEPH_STRING_TABLE_H
+
+#include <linux/types.h>
+#include <linux/kref.h>
+#include <linux/rbtree.h>
+#include <linux/rcupdate.h>
+
+struct ceph_string {
+       struct kref kref;
+       union {
+               struct rb_node node;
+               struct rcu_head rcu;
+       };
+       size_t len;
+       char str[];
+};
+
+extern void ceph_release_string(struct kref *ref);
+extern struct ceph_string *ceph_find_or_create_string(const char *str,
+                                                     size_t len);
+extern bool ceph_strings_empty(void);
+
+static inline struct ceph_string *ceph_get_string(struct ceph_string *str)
+{
+       kref_get(&str->kref);
+       return str;
+}
+
+static inline void ceph_put_string(struct ceph_string *str)
+{
+       if (!str)
+               return;
+       kref_put(&str->kref, ceph_release_string);
+}
+
+static inline int ceph_compare_string(struct ceph_string *cs,
+                                     const char* str, size_t len)
+{
+       size_t cs_len = cs ? cs->len : 0;
+       if (cs_len != len)
+               return cs_len - len;
+       if (len == 0)
+               return 0;
+       return strncmp(cs->str, str, len);
+}
+
+#define ceph_try_get_string(x)                                 \
+({                                                             \
+       struct ceph_string *___str;                             \
+       rcu_read_lock();                                        \
+       for (;;) {                                              \
+               ___str = rcu_dereference(x);                    \
+               if (!___str ||                                  \
+                   kref_get_unless_zero(&___str->kref))        \
+                       break;                                  \
+       }                                                       \
+       rcu_read_unlock();                                      \
+       (___str);                                               \
+})
+
+#endif
 
--- /dev/null
+#include <linux/slab.h>
+#include <linux/gfp.h>
+#include <linux/string.h>
+#include <linux/spinlock.h>
+#include <linux/ceph/string_table.h>
+
+static DEFINE_SPINLOCK(string_tree_lock);
+static struct rb_root string_tree = RB_ROOT;
+
+struct ceph_string *ceph_find_or_create_string(const char* str, size_t len)
+{
+       struct ceph_string *cs, *exist;
+       struct rb_node **p, *parent;
+       int ret;
+
+       exist = NULL;
+       spin_lock(&string_tree_lock);
+       p = &string_tree.rb_node;
+       while (*p) {
+               exist = rb_entry(*p, struct ceph_string, node);
+               ret = ceph_compare_string(exist, str, len);
+               if (ret > 0)
+                       p = &(*p)->rb_left;
+               else if (ret < 0)
+                       p = &(*p)->rb_right;
+               else
+                       break;
+               exist = NULL;
+       }
+       if (exist && !kref_get_unless_zero(&exist->kref)) {
+               rb_erase(&exist->node, &string_tree);
+               RB_CLEAR_NODE(&exist->node);
+               exist = NULL;
+       }
+       spin_unlock(&string_tree_lock);
+       if (exist)
+               return exist;
+
+       cs = kmalloc(sizeof(*cs) + len + 1, GFP_NOFS);
+       if (!cs)
+               return NULL;
+
+       kref_init(&cs->kref);
+       cs->len = len;
+       memcpy(cs->str, str, len);
+       cs->str[len] = 0;
+
+retry:
+       exist = NULL;
+       parent = NULL;
+       p = &string_tree.rb_node;
+       spin_lock(&string_tree_lock);
+       while (*p) {
+               parent = *p;
+               exist = rb_entry(*p, struct ceph_string, node);
+               ret = ceph_compare_string(exist, str, len);
+               if (ret > 0)
+                       p = &(*p)->rb_left;
+               else if (ret < 0)
+                       p = &(*p)->rb_right;
+               else
+                       break;
+               exist = NULL;
+       }
+       ret = 0;
+       if (!exist) {
+               rb_link_node(&cs->node, parent, p);
+               rb_insert_color(&cs->node, &string_tree);
+       } else if (!kref_get_unless_zero(&exist->kref)) {
+               rb_erase(&exist->node, &string_tree);
+               RB_CLEAR_NODE(&exist->node);
+               ret = -EAGAIN;
+       }
+       spin_unlock(&string_tree_lock);
+       if (ret == -EAGAIN)
+               goto retry;
+
+       if (exist) {
+               kfree(cs);
+               cs = exist;
+       }
+
+       return cs;
+}
+EXPORT_SYMBOL(ceph_find_or_create_string);
+
+static void ceph_free_string(struct rcu_head *head)
+{
+       struct ceph_string *cs = container_of(head, struct ceph_string, rcu);
+       kfree(cs);
+}
+
+void ceph_release_string(struct kref *ref)
+{
+       struct ceph_string *cs = container_of(ref, struct ceph_string, kref);
+
+       spin_lock(&string_tree_lock);
+       if (!RB_EMPTY_NODE(&cs->node)) {
+               rb_erase(&cs->node, &string_tree);
+               RB_CLEAR_NODE(&cs->node);
+       }
+       spin_unlock(&string_tree_lock);
+
+       call_rcu(&cs->rcu, ceph_free_string);
+}
+EXPORT_SYMBOL(ceph_release_string);
+
+bool ceph_strings_empty(void)
+{
+       return RB_EMPTY_ROOT(&string_tree);
+}