/src/lvm2/libdm/mm/pool.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. |
3 | | * Copyright (C) 2004-2011 Red Hat, Inc. All rights reserved. |
4 | | * |
5 | | * This file is part of the device-mapper userspace tools. |
6 | | * |
7 | | * This copyrighted material is made available to anyone wishing to use, |
8 | | * modify, copy, or redistribute it subject to the terms and conditions |
9 | | * of the GNU Lesser General Public License v.2.1. |
10 | | * |
11 | | * You should have received a copy of the GNU Lesser General Public License |
12 | | * along with this program; if not, write to the Free Software Foundation, |
13 | | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
14 | | */ |
15 | | |
16 | | #include "libdm/misc/dmlib.h" |
17 | | #include <sys/mman.h> |
18 | | #include <pthread.h> |
19 | | |
20 | | static DM_LIST_INIT(_dm_pools); |
21 | | static pthread_mutex_t _dm_pools_mutex = PTHREAD_MUTEX_INITIALIZER; |
22 | | void dm_pools_check_leaks(void); |
23 | | |
24 | | #ifdef DEBUG_ENFORCE_POOL_LOCKING |
25 | | #ifdef DEBUG_POOL |
26 | | #error Do not use DEBUG_POOL with DEBUG_ENFORCE_POOL_LOCKING |
27 | | #endif |
28 | | |
29 | | /* |
30 | | * Use mprotect system call to ensure all locked pages are not writable. |
31 | | * Generates segmentation fault with write access to the locked pool. |
32 | | * |
33 | | * - Implementation is using posix_memalign() to get page aligned |
34 | | * memory blocks (could be implemented also through malloc). |
35 | | * - Only pool-fast is properly handled for now. |
36 | | * - Checksum is slower compared to mprotect. |
37 | | */ |
38 | | static size_t _pagesize = 0; |
39 | | static size_t _pagesize_mask = 0; |
40 | | static pthread_once_t _pagesize_once = PTHREAD_ONCE_INIT; |
41 | | |
42 | | static void _init_pagesize(void) |
43 | | { |
44 | | _pagesize = getpagesize(); |
45 | | _pagesize_mask = _pagesize - 1; |
46 | | } |
47 | | |
48 | | static size_t _get_pagesize(void) |
49 | | { |
50 | | pthread_once(&_pagesize_once, _init_pagesize); |
51 | | return _pagesize; |
52 | | } |
53 | | |
54 | | static size_t _get_pagesize_mask(void) |
55 | | { |
56 | | pthread_once(&_pagesize_once, _init_pagesize); |
57 | | return _pagesize_mask; |
58 | | } |
59 | | |
60 | | #define ALIGN_ON_PAGE(size) (((size) + (_get_pagesize_mask())) & ~(_get_pagesize_mask())) |
61 | | #endif |
62 | | |
63 | | #ifdef DEBUG_POOL |
64 | | #include "pool-debug.c" |
65 | | #else |
66 | | #include "pool-fast.c" |
67 | | #endif |
68 | | |
69 | | char *dm_pool_strdup(struct dm_pool *p, const char *str) |
70 | 0 | { |
71 | 0 | size_t len = strlen(str) + 1; |
72 | 0 | char *ret = dm_pool_alloc(p, len); |
73 | |
|
74 | 0 | if (ret) |
75 | 0 | memcpy(ret, str, len); |
76 | |
|
77 | 0 | return ret; |
78 | 0 | } |
79 | | |
80 | | char *dm_pool_strndup(struct dm_pool *p, const char *str, size_t n) |
81 | 0 | { |
82 | 0 | size_t slen = strlen(str); |
83 | 0 | size_t len = (slen < n) ? slen : n; |
84 | 0 | char *ret = dm_pool_alloc(p, len + 1); |
85 | |
|
86 | 0 | if (ret) { |
87 | 0 | ret[len] = '\0'; |
88 | 0 | memcpy(ret, str, len); |
89 | 0 | } |
90 | |
|
91 | 0 | return ret; |
92 | 0 | } |
93 | | |
94 | | void *dm_pool_zalloc(struct dm_pool *p, size_t s) |
95 | 0 | { |
96 | 0 | void *ptr = dm_pool_alloc(p, s); |
97 | |
|
98 | 0 | if (ptr) |
99 | 0 | memset(ptr, 0, s); |
100 | |
|
101 | 0 | return ptr; |
102 | 0 | } |
103 | | |
104 | | void dm_pools_check_leaks(void) |
105 | 0 | { |
106 | 0 | struct dm_pool *p; |
107 | |
|
108 | 0 | pthread_mutex_lock(&_dm_pools_mutex); |
109 | 0 | if (dm_list_empty(&_dm_pools)) { |
110 | 0 | pthread_mutex_unlock(&_dm_pools_mutex); |
111 | 0 | return; |
112 | 0 | } |
113 | | |
114 | 0 | log_error("You have a memory leak (not released memory pool):"); |
115 | 0 | dm_list_iterate_items(p, &_dm_pools) { |
116 | | #ifdef DEBUG_POOL |
117 | | log_error(" [%p] %s (%u bytes)", |
118 | | p->orig_pool, |
119 | | p->name, p->stats.bytes); |
120 | | #else |
121 | 0 | log_error(" [%p] %s", (void *)p, p->name); |
122 | 0 | #endif |
123 | 0 | } |
124 | 0 | pthread_mutex_unlock(&_dm_pools_mutex); |
125 | 0 | log_error(INTERNAL_ERROR "Unreleased memory pool(s) found."); |
126 | 0 | } |
127 | | |
128 | | /** |
129 | | * Status of locked pool. |
130 | | * |
131 | | * \param p |
132 | | * Pool to be tested for lock status. |
133 | | * |
134 | | * \return |
135 | | * 1 when the pool is locked, 0 otherwise. |
136 | | */ |
137 | | int dm_pool_locked(struct dm_pool *p) |
138 | 0 | { |
139 | 0 | return p->locked; |
140 | 0 | } |
141 | | |
142 | | /** |
143 | | * Lock memory pool. |
144 | | * |
145 | | * \param p |
146 | | * Pool to be locked. |
147 | | * |
148 | | * \param crc |
149 | | * Bool specifies whether to store the pool crc/hash checksum. |
150 | | * |
151 | | * \return |
152 | | * 1 (success) when the pool was properly locked, 0 otherwise. |
153 | | */ |
154 | | int dm_pool_lock(struct dm_pool *p, int crc) |
155 | 0 | { |
156 | 0 | if (p->locked) { |
157 | 0 | log_error(INTERNAL_ERROR "Pool %s is already locked.", |
158 | 0 | p->name); |
159 | 0 | return 0; |
160 | 0 | } |
161 | | |
162 | 0 | if (crc) |
163 | 0 | p->crc = _pool_crc(p); /* Get crc for pool */ |
164 | |
|
165 | 0 | if (!_pool_protect(p, PROT_READ)) { |
166 | 0 | _pool_protect(p, PROT_READ | PROT_WRITE); |
167 | 0 | return_0; |
168 | 0 | } |
169 | | |
170 | 0 | p->locked = 1; |
171 | |
|
172 | 0 | log_debug_mem("Pool %s is locked.", p->name); |
173 | |
|
174 | 0 | return 1; |
175 | 0 | } |
176 | | |
177 | | /** |
178 | | * Unlock memory pool. |
179 | | * |
180 | | * \param p |
181 | | * Pool to be unlocked. |
182 | | * |
183 | | * \param crc |
184 | | * Bool enables compare of the pool crc/hash with the stored value |
185 | | * at pool lock. The pool is not properly unlocked if there is a mismatch. |
186 | | * |
187 | | * \return |
188 | | * 1 (success) when the pool was properly unlocked, 0 otherwise. |
189 | | */ |
190 | | int dm_pool_unlock(struct dm_pool *p, int crc) |
191 | 0 | { |
192 | 0 | if (!p->locked) { |
193 | 0 | log_error(INTERNAL_ERROR "Pool %s is already unlocked.", |
194 | 0 | p->name); |
195 | 0 | return 0; |
196 | 0 | } |
197 | | |
198 | 0 | p->locked = 0; |
199 | |
|
200 | 0 | if (!_pool_protect(p, PROT_READ | PROT_WRITE)) |
201 | 0 | return_0; |
202 | | |
203 | 0 | log_debug_mem("Pool %s is unlocked.", p->name); |
204 | |
|
205 | 0 | if (crc && (p->crc != _pool_crc(p))) { |
206 | 0 | log_error(INTERNAL_ERROR "Pool %s crc mismatch.", p->name); |
207 | 0 | return 0; |
208 | 0 | } |
209 | | |
210 | 0 | return 1; |
211 | 0 | } |