summaryrefslogtreecommitdiffstats
path: root/sys/fs/tmpfs/tmpfs_subr.c
diff options
context:
space:
mode:
authorgleb <gleb@FreeBSD.org>2012-04-07 15:23:51 +0000
committergleb <gleb@FreeBSD.org>2012-04-07 15:23:51 +0000
commitc4ba84f86e0e1f38a9b7138ac26876c1b293d20f (patch)
treea1ae0c42f1a9be9480e7e83902f2cecbf1671e5d /sys/fs/tmpfs/tmpfs_subr.c
parentccd4f1d859903108662a41c03b085705ba7d8219 (diff)
downloadFreeBSD-src-c4ba84f86e0e1f38a9b7138ac26876c1b293d20f.zip
FreeBSD-src-c4ba84f86e0e1f38a9b7138ac26876c1b293d20f.tar.gz
Add reserved memory limit sysctl to tmpfs.
Cleanup availble and used memory functions. Check if free pages available before allocating new node. Discussed with: delphij
Diffstat (limited to 'sys/fs/tmpfs/tmpfs_subr.c')
-rw-r--r--sys/fs/tmpfs/tmpfs_subr.c67
1 files changed, 66 insertions, 1 deletions
diff --git a/sys/fs/tmpfs/tmpfs_subr.c b/sys/fs/tmpfs/tmpfs_subr.c
index fe596aa..cd0e243 100644
--- a/sys/fs/tmpfs/tmpfs_subr.c
+++ b/sys/fs/tmpfs/tmpfs_subr.c
@@ -59,6 +59,69 @@ __FBSDID("$FreeBSD$");
SYSCTL_NODE(_vfs, OID_AUTO, tmpfs, CTLFLAG_RW, 0, "tmpfs file system");
+static long tmpfs_pages_reserved = TMPFS_PAGES_MINRESERVED;
+
+static int
+sysctl_mem_reserved(SYSCTL_HANDLER_ARGS)
+{
+ int error;
+ long pages, bytes;
+
+ pages = *(long *)arg1;
+ bytes = pages * PAGE_SIZE;
+
+ error = sysctl_handle_long(oidp, &bytes, 0, req);
+ if (error || !req->newptr)
+ return (error);
+
+ pages = bytes / PAGE_SIZE;
+ if (pages < TMPFS_PAGES_MINRESERVED)
+ return (EINVAL);
+
+ *(long *)arg1 = pages;
+ return (0);
+}
+
+SYSCTL_PROC(_vfs_tmpfs, OID_AUTO, memory_reserved, CTLTYPE_LONG|CTLFLAG_RW,
+ &tmpfs_pages_reserved, 0, sysctl_mem_reserved, "L", "reserved memory");
+
+size_t
+tmpfs_mem_avail(void)
+{
+ vm_ooffset_t avail;
+
+ avail = swap_pager_avail + cnt.v_free_count + cnt.v_cache_count -
+ tmpfs_pages_reserved;
+ if (__predict_false(avail < 0))
+ avail = 0;
+ return (avail);
+}
+
+size_t
+tmpfs_pages_used(struct tmpfs_mount *tmp)
+{
+ const size_t node_size = sizeof(struct tmpfs_node) +
+ sizeof(struct tmpfs_dirent);
+ size_t meta_pages;
+
+ meta_pages = howmany((uintmax_t)tmp->tm_nodes_inuse * node_size,
+ PAGE_SIZE);
+ return (meta_pages + tmp->tm_pages_used);
+}
+
+static size_t
+tmpfs_pages_check_avail(struct tmpfs_mount *tmp, size_t req_pages)
+{
+ if (tmpfs_mem_avail() < req_pages)
+ return (0);
+
+ if (tmp->tm_pages_max != SIZE_MAX &&
+ tmp->tm_pages_max < req_pages + tmpfs_pages_used(tmp))
+ return (0);
+
+ return (1);
+}
+
/* --------------------------------------------------------------------- */
/*
@@ -99,6 +162,8 @@ tmpfs_alloc_node(struct tmpfs_mount *tmp, enum vtype type,
if (tmp->tm_nodes_inuse >= tmp->tm_nodes_max)
return (ENOSPC);
+ if (tmpfs_pages_check_avail(tmp, 1) == 0)
+ return (ENOSPC);
nnode = (struct tmpfs_node *)uma_zalloc_arg(
tmp->tm_node_pool, tmp, M_WAITOK);
@@ -917,7 +982,7 @@ tmpfs_reg_resize(struct vnode *vp, off_t newsize, boolean_t ignerr)
MPASS(oldpages == uobj->size);
newpages = OFF_TO_IDX(newsize + PAGE_MASK);
if (newpages > oldpages &&
- newpages - oldpages > TMPFS_PAGES_AVAIL(tmp))
+ tmpfs_pages_check_avail(tmp, newpages - oldpages) == 0)
return (ENOSPC);
VM_OBJECT_LOCK(uobj);
OpenPOWER on IntegriCloud