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 22static struct kmem_cache *pohmelfs_mcache_cache; 23static mempool_t *pohmelfs_mcache_pool; 24 25static 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 34struct 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 59static 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 91static 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 101void 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 108struct 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 134err_out_free: 135 mempool_free(m, pohmelfs_mcache_pool); 136err_out_exit: 137 return ERR_PTR(err); 138} 139 140void 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 147int __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 161err_out_free: 162 kmem_cache_destroy(pohmelfs_mcache_cache); 163err_out_exit: 164 return -ENOMEM; 165} 166 167void pohmelfs_mcache_exit(void) 168{ 169 mempool_destroy(pohmelfs_mcache_pool); 170 kmem_cache_destroy(pohmelfs_mcache_cache); 171} 172