18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci#include <linux/slab.h>
38c2ecf20Sopenharmony_ci#include <linux/gfp.h>
48c2ecf20Sopenharmony_ci#include <linux/string.h>
58c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
68c2ecf20Sopenharmony_ci#include <linux/ceph/string_table.h>
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(string_tree_lock);
98c2ecf20Sopenharmony_cistatic struct rb_root string_tree = RB_ROOT;
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_cistruct ceph_string *ceph_find_or_create_string(const char* str, size_t len)
128c2ecf20Sopenharmony_ci{
138c2ecf20Sopenharmony_ci	struct ceph_string *cs, *exist;
148c2ecf20Sopenharmony_ci	struct rb_node **p, *parent;
158c2ecf20Sopenharmony_ci	int ret;
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci	exist = NULL;
188c2ecf20Sopenharmony_ci	spin_lock(&string_tree_lock);
198c2ecf20Sopenharmony_ci	p = &string_tree.rb_node;
208c2ecf20Sopenharmony_ci	while (*p) {
218c2ecf20Sopenharmony_ci		exist = rb_entry(*p, struct ceph_string, node);
228c2ecf20Sopenharmony_ci		ret = ceph_compare_string(exist, str, len);
238c2ecf20Sopenharmony_ci		if (ret > 0)
248c2ecf20Sopenharmony_ci			p = &(*p)->rb_left;
258c2ecf20Sopenharmony_ci		else if (ret < 0)
268c2ecf20Sopenharmony_ci			p = &(*p)->rb_right;
278c2ecf20Sopenharmony_ci		else
288c2ecf20Sopenharmony_ci			break;
298c2ecf20Sopenharmony_ci		exist = NULL;
308c2ecf20Sopenharmony_ci	}
318c2ecf20Sopenharmony_ci	if (exist && !kref_get_unless_zero(&exist->kref)) {
328c2ecf20Sopenharmony_ci		rb_erase(&exist->node, &string_tree);
338c2ecf20Sopenharmony_ci		RB_CLEAR_NODE(&exist->node);
348c2ecf20Sopenharmony_ci		exist = NULL;
358c2ecf20Sopenharmony_ci	}
368c2ecf20Sopenharmony_ci	spin_unlock(&string_tree_lock);
378c2ecf20Sopenharmony_ci	if (exist)
388c2ecf20Sopenharmony_ci		return exist;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	cs = kmalloc(sizeof(*cs) + len + 1, GFP_NOFS);
418c2ecf20Sopenharmony_ci	if (!cs)
428c2ecf20Sopenharmony_ci		return NULL;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	kref_init(&cs->kref);
458c2ecf20Sopenharmony_ci	cs->len = len;
468c2ecf20Sopenharmony_ci	memcpy(cs->str, str, len);
478c2ecf20Sopenharmony_ci	cs->str[len] = 0;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ciretry:
508c2ecf20Sopenharmony_ci	exist = NULL;
518c2ecf20Sopenharmony_ci	parent = NULL;
528c2ecf20Sopenharmony_ci	p = &string_tree.rb_node;
538c2ecf20Sopenharmony_ci	spin_lock(&string_tree_lock);
548c2ecf20Sopenharmony_ci	while (*p) {
558c2ecf20Sopenharmony_ci		parent = *p;
568c2ecf20Sopenharmony_ci		exist = rb_entry(*p, struct ceph_string, node);
578c2ecf20Sopenharmony_ci		ret = ceph_compare_string(exist, str, len);
588c2ecf20Sopenharmony_ci		if (ret > 0)
598c2ecf20Sopenharmony_ci			p = &(*p)->rb_left;
608c2ecf20Sopenharmony_ci		else if (ret < 0)
618c2ecf20Sopenharmony_ci			p = &(*p)->rb_right;
628c2ecf20Sopenharmony_ci		else
638c2ecf20Sopenharmony_ci			break;
648c2ecf20Sopenharmony_ci		exist = NULL;
658c2ecf20Sopenharmony_ci	}
668c2ecf20Sopenharmony_ci	ret = 0;
678c2ecf20Sopenharmony_ci	if (!exist) {
688c2ecf20Sopenharmony_ci		rb_link_node(&cs->node, parent, p);
698c2ecf20Sopenharmony_ci		rb_insert_color(&cs->node, &string_tree);
708c2ecf20Sopenharmony_ci	} else if (!kref_get_unless_zero(&exist->kref)) {
718c2ecf20Sopenharmony_ci		rb_erase(&exist->node, &string_tree);
728c2ecf20Sopenharmony_ci		RB_CLEAR_NODE(&exist->node);
738c2ecf20Sopenharmony_ci		ret = -EAGAIN;
748c2ecf20Sopenharmony_ci	}
758c2ecf20Sopenharmony_ci	spin_unlock(&string_tree_lock);
768c2ecf20Sopenharmony_ci	if (ret == -EAGAIN)
778c2ecf20Sopenharmony_ci		goto retry;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	if (exist) {
808c2ecf20Sopenharmony_ci		kfree(cs);
818c2ecf20Sopenharmony_ci		cs = exist;
828c2ecf20Sopenharmony_ci	}
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	return cs;
858c2ecf20Sopenharmony_ci}
868c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ceph_find_or_create_string);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_civoid ceph_release_string(struct kref *ref)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	struct ceph_string *cs = container_of(ref, struct ceph_string, kref);
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	spin_lock(&string_tree_lock);
938c2ecf20Sopenharmony_ci	if (!RB_EMPTY_NODE(&cs->node)) {
948c2ecf20Sopenharmony_ci		rb_erase(&cs->node, &string_tree);
958c2ecf20Sopenharmony_ci		RB_CLEAR_NODE(&cs->node);
968c2ecf20Sopenharmony_ci	}
978c2ecf20Sopenharmony_ci	spin_unlock(&string_tree_lock);
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	kfree_rcu(cs, rcu);
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ciEXPORT_SYMBOL(ceph_release_string);
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cibool ceph_strings_empty(void)
1048c2ecf20Sopenharmony_ci{
1058c2ecf20Sopenharmony_ci	return RB_EMPTY_ROOT(&string_tree);
1068c2ecf20Sopenharmony_ci}
107