Print this page
@@ -18,11 +18,11 @@
*
* CDDL HEADER END
*/
/*
* Copyright (c) 1990, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright 2016 Joyent, Inc.
+ * Copyright 2015 Joyent, Inc.
*/
#include <sys/types.h>
#include <sys/errno.h>
#include <sys/param.h>
@@ -41,11 +41,10 @@
#include <sys/policy.h>
#include <sys/fs/tmp.h>
#include <sys/fs/tmpnode.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
-#include <vm/anon.h>
#define KILOBYTE 1024
#define MEGABYTE (1024 * KILOBYTE)
#define GIGABYTE (1024 * MEGABYTE)
@@ -53,84 +52,10 @@
#define VALIDMODEBITS 07777
extern pgcnt_t swapfs_minfree;
-void *
-tmp_kmem_zalloc(struct tmount *tm, size_t size, int flag)
-{
- void *buf;
- zone_t *zone;
- size_t pages;
-
- mutex_enter(&tm->tm_contents);
- zone = tm->tm_vfsp->vfs_zone;
- if (tm->tm_anonmem + size > tm->tm_anonmax ||
- tm->tm_anonmem + size < tm->tm_anonmem ||
- size + ptob(tmpfs_minfree) <= size ||
- !anon_checkspace(size + ptob(tmpfs_minfree), zone)) {
- mutex_exit(&tm->tm_contents);
- return (NULL);
- }
-
- /*
- * Only make anonymous memory reservations when a page boundary is
- * crossed. This is necessary since the anon_resv functions rounds up
- * to PAGESIZE internally.
- */
- pages = btopr(tm->tm_allocmem + size);
- pages -= btopr(tm->tm_allocmem);
- if (pages > 0 && anon_try_resv_zone(ptob(pages), zone) == 0) {
- mutex_exit(&tm->tm_contents);
- return (NULL);
- }
-
- tm->tm_allocmem += size;
- tm->tm_anonmem += size;
- mutex_exit(&tm->tm_contents);
-
- buf = kmem_zalloc(size, flag);
- if (buf == NULL) {
- mutex_enter(&tm->tm_contents);
- ASSERT(tm->tm_anonmem > tm->tm_anonmem - size);
- tm->tm_anonmem -= size;
- if (pages > 0) {
- /*
- * Re-chasing the zone pointer is necessary since a
- * forced umount could have been performed while the
- * tm_contents lock was dropped during allocation.
- */
- anon_unresv_zone(ptob(pages), tm->tm_vfsp->vfs_zone);
- }
- mutex_exit(&tm->tm_contents);
- }
-
- return (buf);
-}
-
-void
-tmp_kmem_free(struct tmount *tm, void *buf, size_t size)
-{
- size_t pages;
-
- kmem_free(buf, size);
- mutex_enter(&tm->tm_contents);
- ASSERT(tm->tm_anonmem > tm->tm_anonmem - size);
- tm->tm_anonmem -= size;
- pages = btopr(tm->tm_allocmem);
- tm->tm_allocmem -= size;
- pages -= btopr(tm->tm_allocmem);
- /*
- * Like the tmp_kmem_zalloc case, only unreserve anonymous memory when
- * a page boundary has been crossed.
- */
- if (pages > 0) {
- anon_unresv_zone(size, tm->tm_vfsp->vfs_zone);
- }
- mutex_exit(&tm->tm_contents);
-}
-
int
tmp_taccess(void *vtp, int mode, struct cred *cred)
{
struct tmpnode *tp = vtp;
int shift = 0;
@@ -172,12 +97,46 @@
return (0);
}
/*
- * Convert a string containing a number (number of bytes) to a size_t,
- * containing the corresponding number of bytes. On 32-bit kernels, the
+ * Allocate zeroed memory if tmpfs_maxkmem has not been exceeded
+ * or the 'musthave' flag is set. 'musthave' allocations should
+ * always be subordinate to normal allocations so that tmpfs_maxkmem
+ * can't be exceeded by more than a few KB. Example: when creating
+ * a new directory, the tmpnode is a normal allocation; if that
+ * succeeds, the dirents for "." and ".." are 'musthave' allocations.
+ */
+void *
+tmp_memalloc(size_t size, int musthave)
+{
+ static time_t last_warning;
+ time_t now;
+
+ if (atomic_add_long_nv(&tmp_kmemspace, size) < tmpfs_maxkmem ||
+ musthave)
+ return (kmem_zalloc(size, KM_SLEEP));
+
+ atomic_add_long(&tmp_kmemspace, -size);
+ now = gethrestime_sec();
+ if (last_warning != now) {
+ last_warning = now;
+ cmn_err(CE_WARN, "tmp_memalloc: tmpfs over memory limit");
+ }
+ return (NULL);
+}
+
+void
+tmp_memfree(void *cp, size_t size)
+{
+ kmem_free(cp, size);
+ atomic_add_long(&tmp_kmemspace, -size);
+}
+
+/*
+ * Convert a string containing a number (number of bytes) to a pgcnt_t,
+ * containing the corresponding number of pages. On 32-bit kernels, the
* maximum value encoded in 'str' is PAGESIZE * ULONG_MAX, while the value
* returned in 'maxpg' is at most ULONG_MAX.
*
* The number may be followed by a magnitude suffix: "k" or "K" for kilobytes;
* "m" or "M" for megabytes; "g" or "G" for gigabytes. This interface allows
@@ -191,16 +150,18 @@
*
* Parse and overflow errors are detected and a non-zero number returned on
* error.
*/
int
-tmp_convnum(char *str, size_t *maxbytes)
+tmp_convnum(char *str, pgcnt_t *maxpg)
{
u_longlong_t num = 0;
- u_longlong_t max_bytes = (uint64_t)SIZE_MAX;
- size_t pages;
-
+#ifdef _LP64
+ u_longlong_t max_bytes = ULONG_MAX;
+#else
+ u_longlong_t max_bytes = PAGESIZE * (uint64_t)ULONG_MAX;
+#endif
char *c;
const struct convchar {
char *cc_char;
uint64_t cc_factor;
} convchars[] = {
@@ -286,27 +247,18 @@
valid_char:
continue;
}
done:
-
/*
- * We've been given a size in bytes; however, we want to make sure that
- * we have at least one page worth no matter what. Therefore we use
- * btopr to round up. However, this may cause an overflow only if 'num'
- * is between (max_bytes - PAGESIZE) and (max_bytes). In this case the
- * resulting number is zero, which is what we check for below. Note, we
- * require at least one page, so if pages is zero, well, it wasn't going
- * to work anyways.
+ * Since btopr() rounds up to page granularity, this round-up can
+ * cause an overflow only if 'num' is between (max_bytes - PAGESIZE)
+ * and (max_bytes). In this case the resulting number is zero, which
+ * is what we check for below.
*/
- pages = btopr(num);
- if (pages == 0) {
+ if ((*maxpg = (pgcnt_t)btopr(num)) == 0 && num != 0)
return (EINVAL);
- }
-
- *maxbytes = ptob(pages);
-
return (0);
}
/*
* Parse an octal mode string for use as the permissions set for the root