tmpfs_mem.c revision 1.3 1 /* $NetBSD: tmpfs_mem.c,v 1.3 2011/05/19 03:21:23 rmind Exp $ */
2
3 /*
4 * Copyright (c) 2010 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 /*
30 * tmpfs memory allocation routines.
31 * Implements memory usage accounting and limiting.
32 */
33
34 #include <sys/cdefs.h>
35 __KERNEL_RCSID(0, "$NetBSD: tmpfs_mem.c,v 1.3 2011/05/19 03:21:23 rmind Exp $");
36
37 #include <sys/param.h>
38 #include <sys/atomic.h>
39 #include <sys/kmem.h>
40 #include <sys/namei.h>
41 #include <sys/pool.h>
42
43 #include <fs/tmpfs/tmpfs.h>
44
45 extern struct pool tmpfs_dirent_pool;
46 extern struct pool tmpfs_node_pool;
47
48 void
49 tmpfs_mntmem_init(struct tmpfs_mount *mp, uint64_t memlimit)
50 {
51
52 mutex_init(&mp->tm_acc_lock, MUTEX_DEFAULT, IPL_NONE);
53 mp->tm_mem_limit = memlimit;
54 mp->tm_bytes_used = 0;
55 }
56
57 void
58 tmpfs_mntmem_destroy(struct tmpfs_mount *mp)
59 {
60
61 KASSERT(mp->tm_bytes_used == 0);
62 mutex_destroy(&mp->tm_acc_lock);
63 }
64
65 /*
66 * tmpfs_mem_info: return the number of available memory pages.
67 *
68 * => If 'total' is true, then return _total_ amount of pages.
69 * => If false, then return the amount of _free_ memory pages.
70 *
71 * Remember to remove TMPFS_PAGES_RESERVED from the returned value to avoid
72 * excessive memory usage.
73 */
74 size_t
75 tmpfs_mem_info(bool total)
76 {
77 size_t size = 0;
78
79 /* XXX: unlocked */
80 size += uvmexp.swpgavail;
81 if (!total) {
82 size -= uvmexp.swpgonly;
83 }
84 size += uvmexp.free;
85 size += uvmexp.filepages;
86 if (size > uvmexp.wired) {
87 size -= uvmexp.wired;
88 } else {
89 size = 0;
90 }
91 return size;
92 }
93
94 uint64_t
95 tmpfs_bytes_max(struct tmpfs_mount *mp)
96 {
97 size_t freepages = tmpfs_mem_info(false);
98 uint64_t avail_mem;
99
100 if (freepages < TMPFS_PAGES_RESERVED) {
101 freepages = 0;
102 } else {
103 freepages -= TMPFS_PAGES_RESERVED;
104 }
105 avail_mem = round_page(mp->tm_bytes_used) + (freepages << PAGE_SHIFT);
106 return MIN(mp->tm_mem_limit, avail_mem);
107 }
108
109 size_t
110 tmpfs_pages_avail(struct tmpfs_mount *mp)
111 {
112
113 return (tmpfs_bytes_max(mp) - mp->tm_bytes_used) >> PAGE_SHIFT;
114 }
115
116 bool
117 tmpfs_mem_incr(struct tmpfs_mount *mp, size_t sz)
118 {
119 uint64_t lim;
120
121 mutex_enter(&mp->tm_acc_lock);
122 lim = tmpfs_bytes_max(mp);
123 if (mp->tm_bytes_used + sz >= lim) {
124 mutex_exit(&mp->tm_acc_lock);
125 return false;
126 }
127 mp->tm_bytes_used += sz;
128 mutex_exit(&mp->tm_acc_lock);
129 return true;
130 }
131
132 void
133 tmpfs_mem_decr(struct tmpfs_mount *mp, size_t sz)
134 {
135
136 mutex_enter(&mp->tm_acc_lock);
137 KASSERT(mp->tm_bytes_used >= sz);
138 mp->tm_bytes_used -= sz;
139 mutex_exit(&mp->tm_acc_lock);
140 }
141
142 struct tmpfs_dirent *
143 tmpfs_dirent_get(struct tmpfs_mount *mp)
144 {
145
146 if (!tmpfs_mem_incr(mp, sizeof(struct tmpfs_dirent))) {
147 return NULL;
148 }
149 return pool_get(&tmpfs_dirent_pool, PR_WAITOK);
150 }
151
152 void
153 tmpfs_dirent_put(struct tmpfs_mount *mp, struct tmpfs_dirent *de)
154 {
155
156 tmpfs_mem_decr(mp, sizeof(struct tmpfs_dirent));
157 pool_put(&tmpfs_dirent_pool, de);
158 }
159
160 struct tmpfs_node *
161 tmpfs_node_get(struct tmpfs_mount *mp)
162 {
163
164 if (!tmpfs_mem_incr(mp, sizeof(struct tmpfs_node))) {
165 return NULL;
166 }
167 return pool_get(&tmpfs_node_pool, PR_WAITOK);
168 }
169
170 void
171 tmpfs_node_put(struct tmpfs_mount *mp, struct tmpfs_node *tn)
172 {
173
174 tmpfs_mem_decr(mp, sizeof(struct tmpfs_node));
175 pool_put(&tmpfs_node_pool, tn);
176 }
177
178 /*
179 * Quantum size to round-up the tmpfs names in order to reduce re-allocations.
180 */
181
182 #define TMPFS_NAME_QUANTUM (32)
183
184 char *
185 tmpfs_strname_alloc(struct tmpfs_mount *mp, size_t len)
186 {
187 const size_t sz = roundup2(len, TMPFS_NAME_QUANTUM);
188
189 KASSERT(sz > 0 && sz <= 1024);
190 if (!tmpfs_mem_incr(mp, sz)) {
191 return NULL;
192 }
193 return kmem_alloc(sz, KM_SLEEP);
194 }
195
196 void
197 tmpfs_strname_free(struct tmpfs_mount *mp, char *str, size_t len)
198 {
199 const size_t sz = roundup2(len, TMPFS_NAME_QUANTUM);
200
201 KASSERT(sz > 0 && sz <= 1024);
202 tmpfs_mem_decr(mp, sz);
203 kmem_free(str, sz);
204 }
205
206 bool
207 tmpfs_strname_neqlen(struct componentname *fcnp, struct componentname *tcnp)
208 {
209 const size_t fln = roundup2(fcnp->cn_namelen, TMPFS_NAME_QUANTUM);
210 const size_t tln = roundup2(tcnp->cn_namelen, TMPFS_NAME_QUANTUM);
211
212 return (fln != tln) || memcmp(fcnp->cn_nameptr, tcnp->cn_nameptr, fln);
213 }
214