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
This commit is contained in:
parent
26c9eb4a77
commit
da7aa2778e
@ -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)
|
||||
{
|
||||
#define TMPFS_PAGES_MINRESERVED (4 * 1024 * 1024 / PAGE_SIZE)
|
||||
|
||||
return (swap_pager_avail + cnt.v_free_count + cnt.v_cache_count);
|
||||
}
|
||||
size_t tmpfs_mem_avail(void);
|
||||
|
||||
/* 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;
|
||||
|
||||
freepages = tmpfs_mem_info();
|
||||
freepages -= freepages < TMPFS_PAGES_RESERVED ?
|
||||
freepages : TMPFS_PAGES_RESERVED;
|
||||
|
||||
return MIN(tmp->tm_pages_max, freepages + tmp->tm_pages_used);
|
||||
}
|
||||
|
||||
/* 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
|
||||
|
||||
|
@ -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);
|
||||
|
@ -183,7 +183,7 @@ tmpfs_mount(struct mount *mp)
|
||||
|
||||
/* 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
|
||||
@ -382,22 +382,30 @@ tmpfs_fhtovp(struct mount *mp, struct fid *fhp, int flags,
|
||||
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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user