1 /*
2  * 2007+ Copyright (c) Evgeniy Polyakov <zbr@ioremap.net>
3  * All rights reserved.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  */
15 
16 #include <linux/module.h>
17 #include <linux/slab.h>
18 #include <linux/mempool.h>
19 
20 #include "netfs.h"
21 
22 static struct kmem_cache *pohmelfs_mcache_cache;
23 static mempool_t *pohmelfs_mcache_pool;
24 
pohmelfs_mcache_cmp(u64 gen,u64 new)25 static inline int pohmelfs_mcache_cmp(u64 gen, u64 new)
26 {
27 	if (gen < new)
28 		return 1;
29 	if (gen > new)
30 		return -1;
31 	return 0;
32 }
33 
pohmelfs_mcache_search(struct pohmelfs_sb * psb,u64 gen)34 struct pohmelfs_mcache *pohmelfs_mcache_search(struct pohmelfs_sb *psb, u64 gen)
35 {
36 	struct rb_root *root = &psb->mcache_root;
37 	struct rb_node *n = root->rb_node;
38 	struct pohmelfs_mcache *tmp, *ret = NULL;
39 	int cmp;
40 
41 	while (n) {
42 		tmp = rb_entry(n, struct pohmelfs_mcache, mcache_entry);
43 
44 		cmp = pohmelfs_mcache_cmp(tmp->gen, gen);
45 		if (cmp < 0)
46 			n = n->rb_left;
47 		else if (cmp > 0)
48 			n = n->rb_right;
49 		else {
50 			ret = tmp;
51 			pohmelfs_mcache_get(ret);
52 			break;
53 		}
54 	}
55 
56 	return ret;
57 }
58 
pohmelfs_mcache_insert(struct pohmelfs_sb * psb,struct pohmelfs_mcache * m)59 static int pohmelfs_mcache_insert(struct pohmelfs_sb *psb, struct pohmelfs_mcache *m)
60 {
61 	struct rb_root *root = &psb->mcache_root;
62 	struct rb_node **n = &root->rb_node, *parent = NULL;
63 	struct pohmelfs_mcache *ret = NULL, *tmp;
64 	int cmp;
65 
66 	while (*n) {
67 		parent = *n;
68 
69 		tmp = rb_entry(parent, struct pohmelfs_mcache, mcache_entry);
70 
71 		cmp = pohmelfs_mcache_cmp(tmp->gen, m->gen);
72 		if (cmp < 0)
73 			n = &parent->rb_left;
74 		else if (cmp > 0)
75 			n = &parent->rb_right;
76 		else {
77 			ret = tmp;
78 			break;
79 		}
80 	}
81 
82 	if (ret)
83 		return -EEXIST;
84 
85 	rb_link_node(&m->mcache_entry, parent, n);
86 	rb_insert_color(&m->mcache_entry, root);
87 
88 	return 0;
89 }
90 
pohmelfs_mcache_remove(struct pohmelfs_sb * psb,struct pohmelfs_mcache * m)91 static int pohmelfs_mcache_remove(struct pohmelfs_sb *psb, struct pohmelfs_mcache *m)
92 {
93 	if (m && m->mcache_entry.rb_parent_color) {
94 		rb_erase(&m->mcache_entry, &psb->mcache_root);
95 		m->mcache_entry.rb_parent_color = 0;
96 		return 1;
97 	}
98 	return 0;
99 }
100 
pohmelfs_mcache_remove_locked(struct pohmelfs_sb * psb,struct pohmelfs_mcache * m)101 void pohmelfs_mcache_remove_locked(struct pohmelfs_sb *psb, struct pohmelfs_mcache *m)
102 {
103 	mutex_lock(&psb->mcache_lock);
104 	pohmelfs_mcache_remove(psb, m);
105 	mutex_unlock(&psb->mcache_lock);
106 }
107 
pohmelfs_mcache_alloc(struct pohmelfs_sb * psb,u64 start,unsigned int size,void * data)108 struct pohmelfs_mcache *pohmelfs_mcache_alloc(struct pohmelfs_sb *psb, u64 start,
109 		unsigned int size, void *data)
110 {
111 	struct pohmelfs_mcache *m;
112 	int err = -ENOMEM;
113 
114 	m = mempool_alloc(pohmelfs_mcache_pool, GFP_KERNEL);
115 	if (!m)
116 		goto err_out_exit;
117 
118 	init_completion(&m->complete);
119 	m->err = 0;
120 	atomic_set(&m->refcnt, 1);
121 	m->data = data;
122 	m->start = start;
123 	m->size = size;
124 	m->gen = atomic_long_inc_return(&psb->mcache_gen);
125 
126 	mutex_lock(&psb->mcache_lock);
127 	err = pohmelfs_mcache_insert(psb, m);
128 	mutex_unlock(&psb->mcache_lock);
129 	if (err)
130 		goto err_out_free;
131 
132 	return m;
133 
134 err_out_free:
135 	mempool_free(m, pohmelfs_mcache_pool);
136 err_out_exit:
137 	return ERR_PTR(err);
138 }
139 
pohmelfs_mcache_free(struct pohmelfs_sb * psb,struct pohmelfs_mcache * m)140 void pohmelfs_mcache_free(struct pohmelfs_sb *psb, struct pohmelfs_mcache *m)
141 {
142 	pohmelfs_mcache_remove_locked(psb, m);
143 
144 	mempool_free(m, pohmelfs_mcache_pool);
145 }
146 
pohmelfs_mcache_init(void)147 int __init pohmelfs_mcache_init(void)
148 {
149 	pohmelfs_mcache_cache = kmem_cache_create("pohmelfs_mcache_cache",
150 				sizeof(struct pohmelfs_mcache),
151 				0, (SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD), NULL);
152 	if (!pohmelfs_mcache_cache)
153 		goto err_out_exit;
154 
155 	pohmelfs_mcache_pool = mempool_create_slab_pool(256, pohmelfs_mcache_cache);
156 	if (!pohmelfs_mcache_pool)
157 		goto err_out_free;
158 
159 	return 0;
160 
161 err_out_free:
162 	kmem_cache_destroy(pohmelfs_mcache_cache);
163 err_out_exit:
164 	return -ENOMEM;
165 }
166 
pohmelfs_mcache_exit(void)167 void pohmelfs_mcache_exit(void)
168 {
169 	mempool_destroy(pohmelfs_mcache_pool);
170 	kmem_cache_destroy(pohmelfs_mcache_cache);
171 }
172