xref: /netbsd-src/sys/fs/tmpfs/tmpfs_mem.c (revision fc536c07106418ea0832a8773a8826c301b48d61)
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