1*fc536c07Sriastradh /* $NetBSD: tmpfs_mem.c,v 1.14 2023/04/29 06:29:55 riastradh Exp $ */
2fc8b3b71Srmind
3fc8b3b71Srmind /*
44b8a875aSad * Copyright (c) 2010, 2011, 2020 The NetBSD Foundation, Inc.
5fc8b3b71Srmind * All rights reserved.
6fc8b3b71Srmind *
77384069aSrmind * This code is derived from software contributed to The NetBSD Foundation
87384069aSrmind * by Mindaugas Rasiukevicius.
97384069aSrmind *
10fc8b3b71Srmind * Redistribution and use in source and binary forms, with or without
11fc8b3b71Srmind * modification, are permitted provided that the following conditions
12fc8b3b71Srmind * are met:
13fc8b3b71Srmind * 1. Redistributions of source code must retain the above copyright
14fc8b3b71Srmind * notice, this list of conditions and the following disclaimer.
15fc8b3b71Srmind * 2. Redistributions in binary form must reproduce the above copyright
16fc8b3b71Srmind * notice, this list of conditions and the following disclaimer in the
17fc8b3b71Srmind * documentation and/or other materials provided with the distribution.
18fc8b3b71Srmind *
19fc8b3b71Srmind * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20fc8b3b71Srmind * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21fc8b3b71Srmind * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22fc8b3b71Srmind * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23fc8b3b71Srmind * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24fc8b3b71Srmind * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25fc8b3b71Srmind * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26fc8b3b71Srmind * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27fc8b3b71Srmind * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28fc8b3b71Srmind * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29fc8b3b71Srmind * POSSIBILITY OF SUCH DAMAGE.
30fc8b3b71Srmind */
31fc8b3b71Srmind
32fc8b3b71Srmind /*
33fc8b3b71Srmind * tmpfs memory allocation routines.
34fc8b3b71Srmind * Implements memory usage accounting and limiting.
35fc8b3b71Srmind */
36fc8b3b71Srmind
37fc8b3b71Srmind #include <sys/cdefs.h>
38*fc536c07Sriastradh __KERNEL_RCSID(0, "$NetBSD: tmpfs_mem.c,v 1.14 2023/04/29 06:29:55 riastradh Exp $");
39fc8b3b71Srmind
40fc8b3b71Srmind #include <sys/param.h>
41fc8b3b71Srmind #include <sys/atomic.h>
42fc8b3b71Srmind #include <sys/kmem.h>
43fc8b3b71Srmind #include <sys/namei.h>
44fc8b3b71Srmind #include <sys/pool.h>
45fc8b3b71Srmind
46fc8b3b71Srmind #include <fs/tmpfs/tmpfs.h>
47fc8b3b71Srmind
487d4d81a3Srmind extern struct pool tmpfs_dirent_pool;
497d4d81a3Srmind extern struct pool tmpfs_node_pool;
507d4d81a3Srmind
51fc8b3b71Srmind void
tmpfs_mntmem_init(struct tmpfs_mount * mp,uint64_t memlimit)52fc8b3b71Srmind tmpfs_mntmem_init(struct tmpfs_mount *mp, uint64_t memlimit)
53fc8b3b71Srmind {
54fc8b3b71Srmind
55fc8b3b71Srmind mutex_init(&mp->tm_acc_lock, MUTEX_DEFAULT, IPL_NONE);
56fc8b3b71Srmind mp->tm_mem_limit = memlimit;
57fc8b3b71Srmind mp->tm_bytes_used = 0;
58fc8b3b71Srmind }
59fc8b3b71Srmind
60fc8b3b71Srmind void
tmpfs_mntmem_destroy(struct tmpfs_mount * mp)61fc8b3b71Srmind tmpfs_mntmem_destroy(struct tmpfs_mount *mp)
62fc8b3b71Srmind {
63fc8b3b71Srmind
64fc8b3b71Srmind KASSERT(mp->tm_bytes_used == 0);
65fc8b3b71Srmind mutex_destroy(&mp->tm_acc_lock);
66fc8b3b71Srmind }
67fc8b3b71Srmind
680172bc97Schristos int
tmpfs_mntmem_set(struct tmpfs_mount * mp,uint64_t memlimit)690172bc97Schristos tmpfs_mntmem_set(struct tmpfs_mount *mp, uint64_t memlimit)
700172bc97Schristos {
710172bc97Schristos int error;
720172bc97Schristos
730172bc97Schristos mutex_enter(&mp->tm_acc_lock);
740172bc97Schristos if (round_page(mp->tm_bytes_used) >= memlimit)
750172bc97Schristos error = EBUSY;
760172bc97Schristos else {
770172bc97Schristos error = 0;
780172bc97Schristos mp->tm_mem_limit = memlimit;
790172bc97Schristos }
800172bc97Schristos mutex_exit(&mp->tm_acc_lock);
810172bc97Schristos return error;
820172bc97Schristos }
830172bc97Schristos
84fc8b3b71Srmind /*
85fc8b3b71Srmind * tmpfs_mem_info: return the number of available memory pages.
86fc8b3b71Srmind *
87fc8b3b71Srmind * => If 'total' is true, then return _total_ amount of pages.
88fc8b3b71Srmind * => If false, then return the amount of _free_ memory pages.
89fc8b3b71Srmind *
90c7dd06b6Smartin * Remember to remove uvmexp.freetarg from the returned value to avoid
91fc8b3b71Srmind * excessive memory usage.
92fc8b3b71Srmind */
93fc8b3b71Srmind size_t
tmpfs_mem_info(bool total)94fc8b3b71Srmind tmpfs_mem_info(bool total)
95fc8b3b71Srmind {
96fc8b3b71Srmind size_t size = 0;
97fc8b3b71Srmind
98fc8b3b71Srmind size += uvmexp.swpgavail;
99fc8b3b71Srmind if (!total) {
100fc8b3b71Srmind size -= uvmexp.swpgonly;
101fc8b3b71Srmind }
1024b8a875aSad size += uvm_availmem(true);
103fc8b3b71Srmind size += uvmexp.filepages;
104fc8b3b71Srmind if (size > uvmexp.wired) {
105fc8b3b71Srmind size -= uvmexp.wired;
106fc8b3b71Srmind } else {
107fc8b3b71Srmind size = 0;
108fc8b3b71Srmind }
109fc8b3b71Srmind return size;
110fc8b3b71Srmind }
111fc8b3b71Srmind
112fc8b3b71Srmind uint64_t
tmpfs_bytes_max(struct tmpfs_mount * mp)113fc8b3b71Srmind tmpfs_bytes_max(struct tmpfs_mount *mp)
114fc8b3b71Srmind {
115a98adcf8Spooka psize_t freepages = tmpfs_mem_info(false);
116a321adb5Sskrll int freetarg = uvmexp.freetarg; // XXX unlocked
117fc8b3b71Srmind uint64_t avail_mem;
118fc8b3b71Srmind
119a321adb5Sskrll if (freepages < freetarg) {
120fc8b3b71Srmind freepages = 0;
121fc8b3b71Srmind } else {
122a321adb5Sskrll freepages -= freetarg;
123fc8b3b71Srmind }
124a98adcf8Spooka avail_mem = round_page(mp->tm_bytes_used) + (freepages << PAGE_SHIFT);
12555946471Srmind return MIN(mp->tm_mem_limit, avail_mem);
126fc8b3b71Srmind }
127fc8b3b71Srmind
128fc8b3b71Srmind size_t
tmpfs_pages_avail(struct tmpfs_mount * mp)129fc8b3b71Srmind tmpfs_pages_avail(struct tmpfs_mount *mp)
130fc8b3b71Srmind {
131fc8b3b71Srmind
132fc8b3b71Srmind return (tmpfs_bytes_max(mp) - mp->tm_bytes_used) >> PAGE_SHIFT;
133fc8b3b71Srmind }
134fc8b3b71Srmind
135fc8b3b71Srmind bool
tmpfs_mem_incr(struct tmpfs_mount * mp,size_t sz)136fc8b3b71Srmind tmpfs_mem_incr(struct tmpfs_mount *mp, size_t sz)
137fc8b3b71Srmind {
13855946471Srmind uint64_t lim;
139fc8b3b71Srmind
140fc8b3b71Srmind mutex_enter(&mp->tm_acc_lock);
14155946471Srmind lim = tmpfs_bytes_max(mp);
142fc8b3b71Srmind if (mp->tm_bytes_used + sz >= lim) {
143fc8b3b71Srmind mutex_exit(&mp->tm_acc_lock);
144fc8b3b71Srmind return false;
145fc8b3b71Srmind }
146fc8b3b71Srmind mp->tm_bytes_used += sz;
147fc8b3b71Srmind mutex_exit(&mp->tm_acc_lock);
148fc8b3b71Srmind return true;
149fc8b3b71Srmind }
150fc8b3b71Srmind
151fc8b3b71Srmind void
tmpfs_mem_decr(struct tmpfs_mount * mp,size_t sz)152fc8b3b71Srmind tmpfs_mem_decr(struct tmpfs_mount *mp, size_t sz)
153fc8b3b71Srmind {
154fc8b3b71Srmind
155fc8b3b71Srmind mutex_enter(&mp->tm_acc_lock);
156fc8b3b71Srmind KASSERT(mp->tm_bytes_used >= sz);
157fc8b3b71Srmind mp->tm_bytes_used -= sz;
158fc8b3b71Srmind mutex_exit(&mp->tm_acc_lock);
159fc8b3b71Srmind }
160fc8b3b71Srmind
161fc8b3b71Srmind struct tmpfs_dirent *
tmpfs_dirent_get(struct tmpfs_mount * mp)162fc8b3b71Srmind tmpfs_dirent_get(struct tmpfs_mount *mp)
163fc8b3b71Srmind {
164fc8b3b71Srmind
165fc8b3b71Srmind if (!tmpfs_mem_incr(mp, sizeof(struct tmpfs_dirent))) {
166fc8b3b71Srmind return NULL;
167fc8b3b71Srmind }
1687d4d81a3Srmind return pool_get(&tmpfs_dirent_pool, PR_WAITOK);
169fc8b3b71Srmind }
170fc8b3b71Srmind
171fc8b3b71Srmind void
tmpfs_dirent_put(struct tmpfs_mount * mp,struct tmpfs_dirent * de)172fc8b3b71Srmind tmpfs_dirent_put(struct tmpfs_mount *mp, struct tmpfs_dirent *de)
173fc8b3b71Srmind {
174fc8b3b71Srmind
175fc8b3b71Srmind tmpfs_mem_decr(mp, sizeof(struct tmpfs_dirent));
1767d4d81a3Srmind pool_put(&tmpfs_dirent_pool, de);
177fc8b3b71Srmind }
178fc8b3b71Srmind
179fc8b3b71Srmind struct tmpfs_node *
tmpfs_node_get(struct tmpfs_mount * mp)180fc8b3b71Srmind tmpfs_node_get(struct tmpfs_mount *mp)
181fc8b3b71Srmind {
182fc8b3b71Srmind
1837384069aSrmind if (atomic_inc_uint_nv(&mp->tm_nodes_cnt) >= mp->tm_nodes_max) {
1847384069aSrmind atomic_dec_uint(&mp->tm_nodes_cnt);
1857384069aSrmind return NULL;
1867384069aSrmind }
187fc8b3b71Srmind if (!tmpfs_mem_incr(mp, sizeof(struct tmpfs_node))) {
188a321adb5Sskrll atomic_dec_uint(&mp->tm_nodes_cnt);
189fc8b3b71Srmind return NULL;
190fc8b3b71Srmind }
1917d4d81a3Srmind return pool_get(&tmpfs_node_pool, PR_WAITOK);
192fc8b3b71Srmind }
193fc8b3b71Srmind
194fc8b3b71Srmind void
tmpfs_node_put(struct tmpfs_mount * mp,struct tmpfs_node * tn)195fc8b3b71Srmind tmpfs_node_put(struct tmpfs_mount *mp, struct tmpfs_node *tn)
196fc8b3b71Srmind {
197fc8b3b71Srmind
1987384069aSrmind atomic_dec_uint(&mp->tm_nodes_cnt);
199fc8b3b71Srmind tmpfs_mem_decr(mp, sizeof(struct tmpfs_node));
2007d4d81a3Srmind pool_put(&tmpfs_node_pool, tn);
201fc8b3b71Srmind }
202fc8b3b71Srmind
203fc8b3b71Srmind /*
204fc8b3b71Srmind * Quantum size to round-up the tmpfs names in order to reduce re-allocations.
205fc8b3b71Srmind */
206fc8b3b71Srmind
207fc8b3b71Srmind #define TMPFS_NAME_QUANTUM (32)
208fc8b3b71Srmind
209fc8b3b71Srmind char *
tmpfs_strname_alloc(struct tmpfs_mount * mp,size_t len)210fc8b3b71Srmind tmpfs_strname_alloc(struct tmpfs_mount *mp, size_t len)
211fc8b3b71Srmind {
212fc8b3b71Srmind const size_t sz = roundup2(len, TMPFS_NAME_QUANTUM);
213fc8b3b71Srmind
214fc8b3b71Srmind KASSERT(sz > 0 && sz <= 1024);
215fc8b3b71Srmind if (!tmpfs_mem_incr(mp, sz)) {
216fc8b3b71Srmind return NULL;
217fc8b3b71Srmind }
218fc8b3b71Srmind return kmem_alloc(sz, KM_SLEEP);
219fc8b3b71Srmind }
220fc8b3b71Srmind
221fc8b3b71Srmind void
tmpfs_strname_free(struct tmpfs_mount * mp,char * str,size_t len)222fc8b3b71Srmind tmpfs_strname_free(struct tmpfs_mount *mp, char *str, size_t len)
223fc8b3b71Srmind {
224fc8b3b71Srmind const size_t sz = roundup2(len, TMPFS_NAME_QUANTUM);
225fc8b3b71Srmind
226fc8b3b71Srmind KASSERT(sz > 0 && sz <= 1024);
227fc8b3b71Srmind tmpfs_mem_decr(mp, sz);
228fc8b3b71Srmind kmem_free(str, sz);
229fc8b3b71Srmind }
230fc8b3b71Srmind
231fc8b3b71Srmind bool
tmpfs_strname_neqlen(struct componentname * fcnp,struct componentname * tcnp)232fc8b3b71Srmind tmpfs_strname_neqlen(struct componentname *fcnp, struct componentname *tcnp)
233fc8b3b71Srmind {
2347b3e6945Smaxv const size_t fln = fcnp->cn_namelen;
2357b3e6945Smaxv const size_t tln = tcnp->cn_namelen;
236fc8b3b71Srmind
237fc8b3b71Srmind return (fln != tln) || memcmp(fcnp->cn_nameptr, tcnp->cn_nameptr, fln);
238fc8b3b71Srmind }
239