svn commit: r234849 - stable/9/sys/fs/tmpfs
Gleb Kurtsou
gleb at FreeBSD.org
Mon Apr 30 17:56:50 UTC 2012
Author: gleb
Date: Mon Apr 30 17:56:49 2012
New Revision: 234849
URL: http://svn.freebsd.org/changeset/base/234849
Log:
MFC r233998-r234000 and r234325:
r233998:
Add reserved memory limit sysctl to tmpfs. Cleanup availble and used
memory functions. Check if free pages available before allocating new
node.
r233999 (partial):
Add vfs_getopt_size. Support human readable file system options in tmpfs.
Increase maximum tmpfs file system size to 4GB*PAGE_SIZE on 32 bit archs.
NOTE: To preserve KBI add tmpfs_getopt_size function instead of global
vfs_getopt_size.
r234000:
tmpfs supports only INT_MAX nodes due to limitations of unit number
allocator. Replace UINT32_MAX checks with INT_MAX. Keeping more than 2^31
nodes in memory is not likely to become possible in foreseeable feature
and would require new unit number allocator.
r234325:
Provide better description for vfs.tmpfs.memory_reserved sysctl.
Modified:
stable/9/sys/fs/tmpfs/tmpfs.h
stable/9/sys/fs/tmpfs/tmpfs_subr.c
stable/9/sys/fs/tmpfs/tmpfs_vfsops.c
Directory Properties:
stable/9/sys/ (props changed)
stable/9/sys/fs/ (props changed)
Modified: stable/9/sys/fs/tmpfs/tmpfs.h
==============================================================================
--- stable/9/sys/fs/tmpfs/tmpfs.h Mon Apr 30 17:53:02 2012 (r234848)
+++ stable/9/sys/fs/tmpfs/tmpfs.h Mon Apr 30 17:56:49 2012 (r234849)
@@ -337,11 +337,10 @@ struct tmpfs_mount {
* system, set during mount time. This variable must never be
* used directly as it may be bigger than the current amount of
* free memory; in the extreme case, it will hold the SIZE_MAX
- * value. Instead, use the TMPFS_PAGES_MAX macro. */
+ * value. */
size_t tm_pages_max;
- /* Number of pages in use by the file system. Cannot be bigger
- * than the value returned by TMPFS_PAGES_MAX in any case. */
+ /* Number of pages in use by the file system. */
size_t tm_pages_used;
/* Pointer to the node representing the root directory of this
@@ -486,57 +485,15 @@ int tmpfs_truncate(struct vnode *, off_t
* Memory management stuff.
*/
-/* Amount of memory pages to reserve for the system (e.g., to not use by
- * tmpfs).
- * XXX: Should this be tunable through sysctl, for instance? */
-#define TMPFS_PAGES_RESERVED (4 * 1024 * 1024 / PAGE_SIZE)
-
/*
- * Returns information about the number of available memory pages,
- * including physical and virtual ones.
- *
- * Remember to remove TMPFS_PAGES_RESERVED from the returned value to avoid
- * excessive memory usage.
- *
+ * Amount of memory pages to reserve for the system (e.g., to not use by
+ * tmpfs).
*/
-static __inline size_t
-tmpfs_mem_info(void)
-{
-
- return (swap_pager_avail + cnt.v_free_count + cnt.v_cache_count);
-}
-
-/* Returns the maximum size allowed for a tmpfs file system. This macro
- * must be used instead of directly retrieving the value from tm_pages_max.
- * The reason is that the size of a tmpfs file system is dynamic: it lets
- * the user store files as long as there is enough free memory (including
- * physical memory and swap space). Therefore, the amount of memory to be
- * used is either the limit imposed by the user during mount time or the
- * amount of available memory, whichever is lower. To avoid consuming all
- * the memory for a given mount point, the system will always reserve a
- * minimum of TMPFS_PAGES_RESERVED pages, which is also taken into account
- * by this macro (see above). */
-static __inline size_t
-TMPFS_PAGES_MAX(struct tmpfs_mount *tmp)
-{
- size_t freepages;
+#define TMPFS_PAGES_MINRESERVED (4 * 1024 * 1024 / PAGE_SIZE)
- freepages = tmpfs_mem_info();
- freepages -= freepages < TMPFS_PAGES_RESERVED ?
- freepages : TMPFS_PAGES_RESERVED;
-
- return MIN(tmp->tm_pages_max, freepages + tmp->tm_pages_used);
-}
+size_t tmpfs_mem_avail(void);
-/* Returns the available space for the given file system. */
-#define TMPFS_META_PAGES(tmp) (howmany((tmp)->tm_nodes_inuse * (sizeof(struct tmpfs_node) \
- + sizeof(struct tmpfs_dirent)), PAGE_SIZE))
-#define TMPFS_FILE_PAGES(tmp) ((tmp)->tm_pages_used)
-
-#define TMPFS_PAGES_AVAIL(tmp) (TMPFS_PAGES_MAX(tmp) > \
- TMPFS_META_PAGES(tmp)+TMPFS_FILE_PAGES(tmp)? \
- TMPFS_PAGES_MAX(tmp) - TMPFS_META_PAGES(tmp) \
- - TMPFS_FILE_PAGES(tmp):0)
+size_t tmpfs_pages_used(struct tmpfs_mount *tmp);
#endif
Modified: stable/9/sys/fs/tmpfs/tmpfs_subr.c
==============================================================================
--- stable/9/sys/fs/tmpfs/tmpfs_subr.c Mon Apr 30 17:53:02 2012 (r234848)
+++ stable/9/sys/fs/tmpfs/tmpfs_subr.c Mon Apr 30 17:56:49 2012 (r234849)
@@ -58,6 +58,70 @@ __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",
+ "Amount of available memory and swap below which tmpfs growth stops");
+
+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);
+}
+
/* --------------------------------------------------------------------- */
/*
@@ -98,6 +162,8 @@ tmpfs_alloc_node(struct tmpfs_mount *tmp
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);
@@ -916,7 +982,7 @@ tmpfs_reg_resize(struct vnode *vp, off_t
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);
TMPFS_LOCK(tmp);
Modified: stable/9/sys/fs/tmpfs/tmpfs_vfsops.c
==============================================================================
--- stable/9/sys/fs/tmpfs/tmpfs_vfsops.c Mon Apr 30 17:53:02 2012 (r234848)
+++ stable/9/sys/fs/tmpfs/tmpfs_vfsops.c Mon Apr 30 17:56:49 2012 (r234849)
@@ -85,6 +85,49 @@ static const char *tmpfs_opts[] = {
/* --------------------------------------------------------------------- */
static int
+tmpfs_getopt_size(struct vfsoptlist *opts, const char *name, off_t *value)
+{
+ char *opt_value, *vtp;
+ quad_t iv;
+ int error, opt_len;
+
+ error = vfs_getopt(opts, name, (void **)&opt_value, &opt_len);
+ if (error != 0)
+ return (error);
+ if (opt_len == 0 || opt_value == NULL)
+ return (EINVAL);
+ if (opt_value[0] == '\0' || opt_value[opt_len - 1] != '\0')
+ return (EINVAL);
+ iv = strtoq(opt_value, &vtp, 0);
+ if (vtp == opt_value || (vtp[0] != '\0' && vtp[1] != '\0'))
+ return (EINVAL);
+ if (iv < 0)
+ return (EINVAL);
+ switch (vtp[0]) {
+ case 't':
+ case 'T':
+ iv *= 1024;
+ case 'g':
+ case 'G':
+ iv *= 1024;
+ case 'm':
+ case 'M':
+ iv *= 1024;
+ case 'k':
+ case 'K':
+ iv *= 1024;
+ case '\0':
+ break;
+ default:
+ return (EINVAL);
+ }
+ *value = iv;
+
+ return (0);
+}
+
+
+static int
tmpfs_node_ctor(void *mem, int size, void *arg, int flags)
{
struct tmpfs_node *node = (struct tmpfs_node *)mem;
@@ -130,14 +173,14 @@ tmpfs_node_fini(void *mem, int size)
static int
tmpfs_mount(struct mount *mp)
{
+ const size_t nodes_per_page = howmany(PAGE_SIZE,
+ sizeof(struct tmpfs_dirent) + sizeof(struct tmpfs_node));
struct tmpfs_mount *tmp;
struct tmpfs_node *root;
- size_t pages;
- uint32_t nodes;
int error;
/* Size counters. */
- u_int nodes_max;
- u_quad_t size_max, maxfilesize;
+ u_quad_t pages;
+ off_t nodes_max, size_max, maxfilesize;
/* Root node attributes. */
uid_t root_uid;
@@ -171,46 +214,47 @@ tmpfs_mount(struct mount *mp)
if (mp->mnt_cred->cr_ruid != 0 ||
vfs_scanopt(mp->mnt_optnew, "mode", "%ho", &root_mode) != 1)
root_mode = va.va_mode;
- if (vfs_scanopt(mp->mnt_optnew, "inodes", "%u", &nodes_max) != 1)
+ if (tmpfs_getopt_size(mp->mnt_optnew, "inodes", &nodes_max) != 0)
nodes_max = 0;
- if (vfs_scanopt(mp->mnt_optnew, "size", "%qu", &size_max) != 1)
+ if (tmpfs_getopt_size(mp->mnt_optnew, "size", &size_max) != 0)
size_max = 0;
- if (vfs_scanopt(mp->mnt_optnew, "maxfilesize", "%qu",
- &maxfilesize) != 1)
+ if (tmpfs_getopt_size(mp->mnt_optnew, "maxfilesize", &maxfilesize) != 0)
maxfilesize = 0;
/* Do not allow mounts if we do not have enough memory to preserve
* the minimum reserved pages. */
- if (tmpfs_mem_info() < TMPFS_PAGES_RESERVED)
+ if (tmpfs_mem_avail() < TMPFS_PAGES_MINRESERVED)
return ENOSPC;
/* Get the maximum number of memory pages this file system is
* allowed to use, based on the maximum size the user passed in
* the mount structure. A value of zero is treated as if the
* maximum available space was requested. */
- if (size_max < PAGE_SIZE || size_max > SIZE_MAX - PAGE_SIZE)
+ if (size_max < PAGE_SIZE || size_max > OFF_MAX - PAGE_SIZE ||
+ (SIZE_MAX < OFF_MAX && size_max / PAGE_SIZE >= SIZE_MAX))
pages = SIZE_MAX;
else
pages = howmany(size_max, PAGE_SIZE);
MPASS(pages > 0);
if (nodes_max <= 3) {
- if (pages > UINT32_MAX - 3)
- nodes = UINT32_MAX;
+ if (pages < INT_MAX / nodes_per_page)
+ nodes_max = pages * nodes_per_page;
else
- nodes = pages + 3;
- } else
- nodes = nodes_max;
- MPASS(nodes >= 3);
+ nodes_max = INT_MAX;
+ }
+ if (nodes_max > INT_MAX)
+ nodes_max = INT_MAX;
+ MPASS(nodes_max >= 3);
/* Allocate the tmpfs mount structure and fill it. */
tmp = (struct tmpfs_mount *)malloc(sizeof(struct tmpfs_mount),
M_TMPFSMNT, M_WAITOK | M_ZERO);
mtx_init(&tmp->allnode_lock, "tmpfs allnode lock", NULL, MTX_DEF);
- tmp->tm_nodes_max = nodes;
+ tmp->tm_nodes_max = nodes_max;
tmp->tm_nodes_inuse = 0;
- tmp->tm_maxfilesize = maxfilesize > 0 ? maxfilesize : UINT64_MAX;
+ tmp->tm_maxfilesize = maxfilesize > 0 ? maxfilesize : OFF_MAX;
LIST_INIT(&tmp->tm_nodes_used);
tmp->tm_pages_max = pages;
@@ -380,22 +424,30 @@ tmpfs_fhtovp(struct mount *mp, struct fi
static int
tmpfs_statfs(struct mount *mp, struct statfs *sbp)
{
- fsfilcnt_t freenodes;
struct tmpfs_mount *tmp;
+ size_t used;
tmp = VFS_TO_TMPFS(mp);
sbp->f_iosize = PAGE_SIZE;
sbp->f_bsize = PAGE_SIZE;
- sbp->f_blocks = TMPFS_PAGES_MAX(tmp);
- sbp->f_bavail = sbp->f_bfree = TMPFS_PAGES_AVAIL(tmp);
-
- freenodes = MIN(tmp->tm_nodes_max - tmp->tm_nodes_inuse,
- TMPFS_PAGES_AVAIL(tmp) * PAGE_SIZE / sizeof(struct tmpfs_node));
-
- sbp->f_files = freenodes + tmp->tm_nodes_inuse;
- sbp->f_ffree = freenodes;
+ used = tmpfs_pages_used(tmp);
+ if (tmp->tm_pages_max != SIZE_MAX)
+ sbp->f_blocks = tmp->tm_pages_max;
+ else
+ sbp->f_blocks = used + tmpfs_mem_avail();
+ if (sbp->f_blocks <= used)
+ sbp->f_bavail = 0;
+ else
+ sbp->f_bavail = sbp->f_blocks - used;
+ sbp->f_bfree = sbp->f_bavail;
+ used = tmp->tm_nodes_inuse;
+ sbp->f_files = tmp->tm_nodes_max;
+ if (sbp->f_files <= used)
+ sbp->f_ffree = 0;
+ else
+ sbp->f_ffree = sbp->f_files - used;
/* sbp->f_owner = tmp->tn_uid; */
return 0;
More information about the svn-src-stable-9
mailing list