Enhance SPLAT kmem:slab_overcommit test
After the emergency slab objects were merged I started observing timeout failures in the kmem:slab_overcommit test. These were due to the ineffecient way the slab_overcommit reclaim function was implemented. And due to the additional cost of potentially allocating ten of thousands of emergency objects and tracking them on a single list. This patch addresses the first concern by enhansing the test case to trace all of the allocations objects as a linked list. This allows for a cleaner version of the reclaim function to simply release SPLAT_KMEM_OBJ_RECLAIM objects. Since this touches some common code all the tests which share these data structions were also updated. After making these changes slab_overcommit is reliably passing. However, there is certainly additional cleanup which could be done here. Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
This commit is contained in:
parent
cd5ca4b2f8
commit
efcd0ca32d
|
@ -242,23 +242,22 @@ splat_kmem_test4(struct file *file, void *arg)
|
||||||
#define SPLAT_KMEM_TEST_MAGIC 0x004488CCUL
|
#define SPLAT_KMEM_TEST_MAGIC 0x004488CCUL
|
||||||
#define SPLAT_KMEM_CACHE_NAME "kmem_test"
|
#define SPLAT_KMEM_CACHE_NAME "kmem_test"
|
||||||
#define SPLAT_KMEM_OBJ_COUNT 1024
|
#define SPLAT_KMEM_OBJ_COUNT 1024
|
||||||
#define SPLAT_KMEM_OBJ_RECLAIM 20 /* percent */
|
#define SPLAT_KMEM_OBJ_RECLAIM 1000 /* objects */
|
||||||
#define SPLAT_KMEM_THREADS 32
|
#define SPLAT_KMEM_THREADS 32
|
||||||
|
|
||||||
#define KCP_FLAG_READY 0x01
|
#define KCP_FLAG_READY 0x01
|
||||||
|
|
||||||
typedef struct kmem_cache_data {
|
typedef struct kmem_cache_data {
|
||||||
unsigned long kcd_magic;
|
unsigned long kcd_magic;
|
||||||
|
struct list_head kcd_node;
|
||||||
int kcd_flag;
|
int kcd_flag;
|
||||||
char kcd_buf[0];
|
char kcd_buf[0];
|
||||||
} kmem_cache_data_t;
|
} kmem_cache_data_t;
|
||||||
|
|
||||||
typedef struct kmem_cache_thread {
|
typedef struct kmem_cache_thread {
|
||||||
kmem_cache_t *kct_cache;
|
|
||||||
spinlock_t kct_lock;
|
spinlock_t kct_lock;
|
||||||
int kct_id;
|
int kct_id;
|
||||||
int kct_kcd_count;
|
struct list_head kct_list;
|
||||||
kmem_cache_data_t *kct_kcd[0];
|
|
||||||
} kmem_cache_thread_t;
|
} kmem_cache_thread_t;
|
||||||
|
|
||||||
typedef struct kmem_cache_priv {
|
typedef struct kmem_cache_priv {
|
||||||
|
@ -276,18 +275,15 @@ typedef struct kmem_cache_priv {
|
||||||
int kcp_count;
|
int kcp_count;
|
||||||
int kcp_alloc;
|
int kcp_alloc;
|
||||||
int kcp_rc;
|
int kcp_rc;
|
||||||
int kcp_kcd_count;
|
|
||||||
kmem_cache_data_t *kcp_kcd[0];
|
|
||||||
} kmem_cache_priv_t;
|
} kmem_cache_priv_t;
|
||||||
|
|
||||||
static kmem_cache_priv_t *
|
static kmem_cache_priv_t *
|
||||||
splat_kmem_cache_test_kcp_alloc(struct file *file, char *name,
|
splat_kmem_cache_test_kcp_alloc(struct file *file, char *name,
|
||||||
int size, int align, int alloc, int count)
|
int size, int align, int alloc)
|
||||||
{
|
{
|
||||||
kmem_cache_priv_t *kcp;
|
kmem_cache_priv_t *kcp;
|
||||||
|
|
||||||
kcp = vmem_zalloc(sizeof(kmem_cache_priv_t) +
|
kcp = kmem_zalloc(sizeof(kmem_cache_priv_t), KM_SLEEP);
|
||||||
count * sizeof(kmem_cache_data_t *), KM_SLEEP);
|
|
||||||
if (!kcp)
|
if (!kcp)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
|
@ -304,7 +300,6 @@ splat_kmem_cache_test_kcp_alloc(struct file *file, char *name,
|
||||||
kcp->kcp_count = 0;
|
kcp->kcp_count = 0;
|
||||||
kcp->kcp_alloc = alloc;
|
kcp->kcp_alloc = alloc;
|
||||||
kcp->kcp_rc = 0;
|
kcp->kcp_rc = 0;
|
||||||
kcp->kcp_kcd_count = count;
|
|
||||||
|
|
||||||
return kcp;
|
return kcp;
|
||||||
}
|
}
|
||||||
|
@ -312,34 +307,83 @@ splat_kmem_cache_test_kcp_alloc(struct file *file, char *name,
|
||||||
static void
|
static void
|
||||||
splat_kmem_cache_test_kcp_free(kmem_cache_priv_t *kcp)
|
splat_kmem_cache_test_kcp_free(kmem_cache_priv_t *kcp)
|
||||||
{
|
{
|
||||||
vmem_free(kcp, sizeof(kmem_cache_priv_t) +
|
kmem_free(kcp, sizeof(kmem_cache_priv_t));
|
||||||
kcp->kcp_kcd_count * sizeof(kmem_cache_data_t *));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static kmem_cache_thread_t *
|
static kmem_cache_thread_t *
|
||||||
splat_kmem_cache_test_kct_alloc(int id, int count)
|
splat_kmem_cache_test_kct_alloc(kmem_cache_priv_t *kcp, int id)
|
||||||
{
|
{
|
||||||
kmem_cache_thread_t *kct;
|
kmem_cache_thread_t *kct;
|
||||||
|
|
||||||
ASSERTF(id < SPLAT_KMEM_THREADS, "id=%d\n", id);
|
ASSERTF(id < SPLAT_KMEM_THREADS, "id=%d\n", id);
|
||||||
kct = vmem_zalloc(sizeof(kmem_cache_thread_t) +
|
ASSERT(kcp->kcp_kct[id] == NULL);
|
||||||
count * sizeof(kmem_cache_data_t *), KM_SLEEP);
|
|
||||||
|
kct = kmem_zalloc(sizeof(kmem_cache_thread_t), KM_SLEEP);
|
||||||
if (!kct)
|
if (!kct)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
spin_lock_init(&kct->kct_lock);
|
spin_lock_init(&kct->kct_lock);
|
||||||
kct->kct_cache = NULL;
|
|
||||||
kct->kct_id = id;
|
kct->kct_id = id;
|
||||||
kct->kct_kcd_count = count;
|
INIT_LIST_HEAD(&kct->kct_list);
|
||||||
|
|
||||||
|
spin_lock(&kcp->kcp_lock);
|
||||||
|
kcp->kcp_kct[id] = kct;
|
||||||
|
spin_unlock(&kcp->kcp_lock);
|
||||||
|
|
||||||
return kct;
|
return kct;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
splat_kmem_cache_test_kct_free(kmem_cache_thread_t *kct)
|
splat_kmem_cache_test_kct_free(kmem_cache_priv_t *kcp,
|
||||||
|
kmem_cache_thread_t *kct)
|
||||||
{
|
{
|
||||||
vmem_free(kct, sizeof(kmem_cache_thread_t) +
|
spin_lock(&kcp->kcp_lock);
|
||||||
kct->kct_kcd_count * sizeof(kmem_cache_data_t *));
|
kcp->kcp_kct[kct->kct_id] = NULL;
|
||||||
|
spin_unlock(&kcp->kcp_lock);
|
||||||
|
|
||||||
|
kmem_free(kct, sizeof(kmem_cache_thread_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
splat_kmem_cache_test_kcd_free(kmem_cache_priv_t *kcp,
|
||||||
|
kmem_cache_thread_t *kct)
|
||||||
|
{
|
||||||
|
kmem_cache_data_t *kcd;
|
||||||
|
|
||||||
|
spin_lock(&kct->kct_lock);
|
||||||
|
while (!list_empty(&kct->kct_list)) {
|
||||||
|
kcd = list_entry(kct->kct_list.next,
|
||||||
|
kmem_cache_data_t, kcd_node);
|
||||||
|
list_del(&kcd->kcd_node);
|
||||||
|
spin_unlock(&kct->kct_lock);
|
||||||
|
|
||||||
|
kmem_cache_free(kcp->kcp_cache, kcd);
|
||||||
|
|
||||||
|
spin_lock(&kct->kct_lock);
|
||||||
|
}
|
||||||
|
spin_unlock(&kct->kct_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
splat_kmem_cache_test_kcd_alloc(kmem_cache_priv_t *kcp,
|
||||||
|
kmem_cache_thread_t *kct, int count)
|
||||||
|
{
|
||||||
|
kmem_cache_data_t *kcd;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
kcd = kmem_cache_alloc(kcp->kcp_cache, KM_SLEEP);
|
||||||
|
if (kcd == NULL) {
|
||||||
|
splat_kmem_cache_test_kcd_free(kcp, kct);
|
||||||
|
return -ENOMEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_lock(&kct->kct_lock);
|
||||||
|
list_add_tail(&kcd->kcd_node, &kct->kct_list);
|
||||||
|
spin_unlock(&kct->kct_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -372,6 +416,7 @@ splat_kmem_cache_test_constructor(void *ptr, void *priv, int flags)
|
||||||
|
|
||||||
if (kcd && kcp) {
|
if (kcd && kcp) {
|
||||||
kcd->kcd_magic = kcp->kcp_magic;
|
kcd->kcd_magic = kcp->kcp_magic;
|
||||||
|
INIT_LIST_HEAD(&kcd->kcd_node);
|
||||||
kcd->kcd_flag = 1;
|
kcd->kcd_flag = 1;
|
||||||
memset(kcd->kcd_buf, 0xaa, kcp->kcp_size - (sizeof *kcd));
|
memset(kcd->kcd_buf, 0xaa, kcp->kcp_size - (sizeof *kcd));
|
||||||
kcp->kcp_count++;
|
kcp->kcp_count++;
|
||||||
|
@ -406,51 +451,41 @@ splat_kmem_cache_test_reclaim(void *priv)
|
||||||
{
|
{
|
||||||
kmem_cache_priv_t *kcp = (kmem_cache_priv_t *)priv;
|
kmem_cache_priv_t *kcp = (kmem_cache_priv_t *)priv;
|
||||||
kmem_cache_thread_t *kct;
|
kmem_cache_thread_t *kct;
|
||||||
int i, j, count;
|
kmem_cache_data_t *kcd;
|
||||||
|
LIST_HEAD(reclaim);
|
||||||
|
int i, count;
|
||||||
|
|
||||||
ASSERT(kcp->kcp_magic == SPLAT_KMEM_TEST_MAGIC);
|
ASSERT(kcp->kcp_magic == SPLAT_KMEM_TEST_MAGIC);
|
||||||
count = kcp->kcp_kcd_count * SPLAT_KMEM_OBJ_RECLAIM / 100;
|
|
||||||
|
|
||||||
/* Objects directly attached to the kcp */
|
/* For each kct thread reclaim some objects */
|
||||||
spin_lock(&kcp->kcp_lock);
|
spin_lock(&kcp->kcp_lock);
|
||||||
for (i = 0; i < kcp->kcp_kcd_count; i++) {
|
for (i = 0; i < SPLAT_KMEM_THREADS; i++) {
|
||||||
if (kcp->kcp_kcd[i]) {
|
kct = kcp->kcp_kct[i];
|
||||||
kmem_cache_free(kcp->kcp_cache, kcp->kcp_kcd[i]);
|
if (!kct)
|
||||||
kcp->kcp_kcd[i] = NULL;
|
continue;
|
||||||
|
|
||||||
if ((--count) == 0)
|
spin_unlock(&kcp->kcp_lock);
|
||||||
break;
|
spin_lock(&kct->kct_lock);
|
||||||
|
|
||||||
|
count = SPLAT_KMEM_OBJ_RECLAIM;
|
||||||
|
while (count > 0 && !list_empty(&kct->kct_list)) {
|
||||||
|
kcd = list_entry(kct->kct_list.next,
|
||||||
|
kmem_cache_data_t, kcd_node);
|
||||||
|
list_del(&kcd->kcd_node);
|
||||||
|
list_add(&kcd->kcd_node, &reclaim);
|
||||||
|
count--;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
spin_unlock(&kct->kct_lock);
|
||||||
|
spin_lock(&kcp->kcp_lock);
|
||||||
}
|
}
|
||||||
spin_unlock(&kcp->kcp_lock);
|
spin_unlock(&kcp->kcp_lock);
|
||||||
|
|
||||||
/* No threads containing objects to consider */
|
/* Freed outside the spin lock */
|
||||||
if (kcp->kcp_kct_count == -1)
|
while (!list_empty(&reclaim)) {
|
||||||
return;
|
kcd = list_entry(reclaim.next, kmem_cache_data_t, kcd_node);
|
||||||
|
list_del(&kcd->kcd_node);
|
||||||
/* Objects attached to a kct thread */
|
kmem_cache_free(kcp->kcp_cache, kcd);
|
||||||
for (i = 0; i < kcp->kcp_kct_count; i++) {
|
|
||||||
spin_lock(&kcp->kcp_lock);
|
|
||||||
kct = kcp->kcp_kct[i];
|
|
||||||
if (!kct) {
|
|
||||||
spin_unlock(&kcp->kcp_lock);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_lock(&kct->kct_lock);
|
|
||||||
count = kct->kct_kcd_count * SPLAT_KMEM_OBJ_RECLAIM / 100;
|
|
||||||
|
|
||||||
for (j = 0; j < kct->kct_kcd_count; j++) {
|
|
||||||
if (kct->kct_kcd[j]) {
|
|
||||||
kmem_cache_free(kcp->kcp_cache,kct->kct_kcd[j]);
|
|
||||||
kct->kct_kcd[j] = NULL;
|
|
||||||
|
|
||||||
if ((--count) == 0)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
spin_unlock(&kct->kct_lock);
|
|
||||||
spin_unlock(&kcp->kcp_lock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
@ -485,8 +520,7 @@ splat_kmem_cache_test_thread(void *arg)
|
||||||
{
|
{
|
||||||
kmem_cache_priv_t *kcp = (kmem_cache_priv_t *)arg;
|
kmem_cache_priv_t *kcp = (kmem_cache_priv_t *)arg;
|
||||||
kmem_cache_thread_t *kct;
|
kmem_cache_thread_t *kct;
|
||||||
int rc = 0, id, i;
|
int rc = 0, id;
|
||||||
void *obj;
|
|
||||||
|
|
||||||
ASSERT(kcp->kcp_magic == SPLAT_KMEM_TEST_MAGIC);
|
ASSERT(kcp->kcp_magic == SPLAT_KMEM_TEST_MAGIC);
|
||||||
|
|
||||||
|
@ -499,16 +533,12 @@ splat_kmem_cache_test_thread(void *arg)
|
||||||
kcp->kcp_kct_count++;
|
kcp->kcp_kct_count++;
|
||||||
spin_unlock(&kcp->kcp_lock);
|
spin_unlock(&kcp->kcp_lock);
|
||||||
|
|
||||||
kct = splat_kmem_cache_test_kct_alloc(id, kcp->kcp_alloc);
|
kct = splat_kmem_cache_test_kct_alloc(kcp, id);
|
||||||
if (!kct) {
|
if (!kct) {
|
||||||
rc = -ENOMEM;
|
rc = -ENOMEM;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_lock(&kcp->kcp_lock);
|
|
||||||
kcp->kcp_kct[id] = kct;
|
|
||||||
spin_unlock(&kcp->kcp_lock);
|
|
||||||
|
|
||||||
/* Wait for all threads to have started and report they are ready */
|
/* Wait for all threads to have started and report they are ready */
|
||||||
if (kcp->kcp_kct_count == SPLAT_KMEM_THREADS)
|
if (kcp->kcp_kct_count == SPLAT_KMEM_THREADS)
|
||||||
wake_up(&kcp->kcp_ctl_waitq);
|
wake_up(&kcp->kcp_ctl_waitq);
|
||||||
|
@ -516,34 +546,14 @@ splat_kmem_cache_test_thread(void *arg)
|
||||||
wait_event(kcp->kcp_thr_waitq,
|
wait_event(kcp->kcp_thr_waitq,
|
||||||
splat_kmem_cache_test_flags(kcp, KCP_FLAG_READY));
|
splat_kmem_cache_test_flags(kcp, KCP_FLAG_READY));
|
||||||
|
|
||||||
/*
|
/* Create and destroy objects */
|
||||||
* Updates to kct->kct_kcd[] are performed under a spin_lock so
|
rc = splat_kmem_cache_test_kcd_alloc(kcp, kct, kcp->kcp_alloc);
|
||||||
* they may safely run concurrent with the reclaim function. If
|
splat_kmem_cache_test_kcd_free(kcp, kct);
|
||||||
* we are not in a low memory situation we have one lock per-
|
|
||||||
* thread so they are not expected to be contended.
|
|
||||||
*/
|
|
||||||
for (i = 0; i < kct->kct_kcd_count; i++) {
|
|
||||||
obj = kmem_cache_alloc(kcp->kcp_cache, KM_SLEEP);
|
|
||||||
spin_lock(&kct->kct_lock);
|
|
||||||
kct->kct_kcd[i] = obj;
|
|
||||||
spin_unlock(&kct->kct_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < kct->kct_kcd_count; i++) {
|
|
||||||
spin_lock(&kct->kct_lock);
|
|
||||||
if (kct->kct_kcd[i]) {
|
|
||||||
kmem_cache_free(kcp->kcp_cache, kct->kct_kcd[i]);
|
|
||||||
kct->kct_kcd[i] = NULL;
|
|
||||||
}
|
|
||||||
spin_unlock(&kct->kct_lock);
|
|
||||||
}
|
|
||||||
out:
|
out:
|
||||||
spin_lock(&kcp->kcp_lock);
|
if (kct)
|
||||||
if (kct) {
|
splat_kmem_cache_test_kct_free(kcp, kct);
|
||||||
splat_kmem_cache_test_kct_free(kct);
|
|
||||||
kcp->kcp_kct[id] = kct = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
spin_lock(&kcp->kcp_lock);
|
||||||
if (!kcp->kcp_rc)
|
if (!kcp->kcp_rc)
|
||||||
kcp->kcp_rc = rc;
|
kcp->kcp_rc = rc;
|
||||||
|
|
||||||
|
@ -560,16 +570,15 @@ splat_kmem_cache_test(struct file *file, void *arg, char *name,
|
||||||
int size, int align, int flags)
|
int size, int align, int flags)
|
||||||
{
|
{
|
||||||
kmem_cache_priv_t *kcp;
|
kmem_cache_priv_t *kcp;
|
||||||
kmem_cache_data_t *kcd;
|
kmem_cache_data_t *kcd = NULL;
|
||||||
int rc = 0, max;
|
int rc = 0, max;
|
||||||
|
|
||||||
kcp = splat_kmem_cache_test_kcp_alloc(file, name, size, align, 0, 1);
|
kcp = splat_kmem_cache_test_kcp_alloc(file, name, size, align, 0);
|
||||||
if (!kcp) {
|
if (!kcp) {
|
||||||
splat_vprint(file, name, "Unable to create '%s'\n", "kcp");
|
splat_vprint(file, name, "Unable to create '%s'\n", "kcp");
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
kcp->kcp_kcd[0] = NULL;
|
|
||||||
kcp->kcp_cache =
|
kcp->kcp_cache =
|
||||||
kmem_cache_create(SPLAT_KMEM_CACHE_NAME,
|
kmem_cache_create(SPLAT_KMEM_CACHE_NAME,
|
||||||
kcp->kcp_size, kcp->kcp_align,
|
kcp->kcp_size, kcp->kcp_align,
|
||||||
|
@ -592,11 +601,8 @@ splat_kmem_cache_test(struct file *file, void *arg, char *name,
|
||||||
rc = -EINVAL;
|
rc = -EINVAL;
|
||||||
goto out_free;
|
goto out_free;
|
||||||
}
|
}
|
||||||
spin_lock(&kcp->kcp_lock);
|
|
||||||
kcp->kcp_kcd[0] = kcd;
|
|
||||||
spin_unlock(&kcp->kcp_lock);
|
|
||||||
|
|
||||||
if (!kcp->kcp_kcd[0]->kcd_flag) {
|
if (!kcd->kcd_flag) {
|
||||||
splat_vprint(file, name,
|
splat_vprint(file, name,
|
||||||
"Failed to run contructor for '%s'\n",
|
"Failed to run contructor for '%s'\n",
|
||||||
SPLAT_KMEM_CACHE_NAME);
|
SPLAT_KMEM_CACHE_NAME);
|
||||||
|
@ -604,7 +610,7 @@ splat_kmem_cache_test(struct file *file, void *arg, char *name,
|
||||||
goto out_free;
|
goto out_free;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kcp->kcp_kcd[0]->kcd_magic != kcp->kcp_magic) {
|
if (kcd->kcd_magic != kcp->kcp_magic) {
|
||||||
splat_vprint(file, name,
|
splat_vprint(file, name,
|
||||||
"Failed to pass private data to constructor "
|
"Failed to pass private data to constructor "
|
||||||
"for '%s'\n", SPLAT_KMEM_CACHE_NAME);
|
"for '%s'\n", SPLAT_KMEM_CACHE_NAME);
|
||||||
|
@ -613,10 +619,7 @@ splat_kmem_cache_test(struct file *file, void *arg, char *name,
|
||||||
}
|
}
|
||||||
|
|
||||||
max = kcp->kcp_count;
|
max = kcp->kcp_count;
|
||||||
spin_lock(&kcp->kcp_lock);
|
kmem_cache_free(kcp->kcp_cache, kcd);
|
||||||
kmem_cache_free(kcp->kcp_cache, kcp->kcp_kcd[0]);
|
|
||||||
kcp->kcp_kcd[0] = NULL;
|
|
||||||
spin_unlock(&kcp->kcp_lock);
|
|
||||||
|
|
||||||
/* Destroy the entire cache which will force destructors to
|
/* Destroy the entire cache which will force destructors to
|
||||||
* run and we can verify one was called for every object */
|
* run and we can verify one was called for every object */
|
||||||
|
@ -636,12 +639,8 @@ splat_kmem_cache_test(struct file *file, void *arg, char *name,
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
out_free:
|
out_free:
|
||||||
if (kcp->kcp_kcd[0]) {
|
if (kcd)
|
||||||
spin_lock(&kcp->kcp_lock);
|
kmem_cache_free(kcp->kcp_cache, kcd);
|
||||||
kmem_cache_free(kcp->kcp_cache, kcp->kcp_kcd[0]);
|
|
||||||
kcp->kcp_kcd[0] = NULL;
|
|
||||||
spin_unlock(&kcp->kcp_lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (kcp->kcp_cache)
|
if (kcp->kcp_cache)
|
||||||
kmem_cache_destroy(kcp->kcp_cache);
|
kmem_cache_destroy(kcp->kcp_cache);
|
||||||
|
@ -661,7 +660,7 @@ splat_kmem_cache_thread_test(struct file *file, void *arg, char *name,
|
||||||
char cache_name[32];
|
char cache_name[32];
|
||||||
int i, rc = 0;
|
int i, rc = 0;
|
||||||
|
|
||||||
kcp = splat_kmem_cache_test_kcp_alloc(file, name, size, 0, alloc, 0);
|
kcp = splat_kmem_cache_test_kcp_alloc(file, name, size, 0, alloc);
|
||||||
if (!kcp) {
|
if (!kcp) {
|
||||||
splat_vprint(file, name, "Unable to create '%s'\n", "kcp");
|
splat_vprint(file, name, "Unable to create '%s'\n", "kcp");
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -755,7 +754,9 @@ splat_kmem_test5(struct file *file, void *arg)
|
||||||
return splat_kmem_cache_test(file, arg, name, 128, 0, KMC_VMEM);
|
return splat_kmem_cache_test(file, arg, name, 128, 0, KMC_VMEM);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Validate large object cache behavior for dynamic/kmem/vmem caches */
|
/*
|
||||||
|
* Validate large object cache behavior for dynamic/kmem/vmem caches
|
||||||
|
*/
|
||||||
static int
|
static int
|
||||||
splat_kmem_test6(struct file *file, void *arg)
|
splat_kmem_test6(struct file *file, void *arg)
|
||||||
{
|
{
|
||||||
|
@ -773,7 +774,9 @@ splat_kmem_test6(struct file *file, void *arg)
|
||||||
return splat_kmem_cache_test(file, arg, name, 1024*1024, 0, KMC_VMEM);
|
return splat_kmem_cache_test(file, arg, name, 1024*1024, 0, KMC_VMEM);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Validate object alignment cache behavior for caches */
|
/*
|
||||||
|
* Validate object alignment cache behavior for caches
|
||||||
|
*/
|
||||||
static int
|
static int
|
||||||
splat_kmem_test7(struct file *file, void *arg)
|
splat_kmem_test7(struct file *file, void *arg)
|
||||||
{
|
{
|
||||||
|
@ -789,19 +792,31 @@ splat_kmem_test7(struct file *file, void *arg)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Validate kmem_cache_reap() by requesting the slab cache free any objects
|
||||||
|
* it can. For a few reasons this may not immediately result in more free
|
||||||
|
* memory even if objects are freed. First off, due to fragmentation we
|
||||||
|
* may not be able to reclaim any slabs. Secondly, even if we do we fully
|
||||||
|
* clear some slabs we will not want to immediately reclaim all of them
|
||||||
|
* because we may contend with cache allocations and thrash. What we want
|
||||||
|
* to see is the slab size decrease more gradually as it becomes clear they
|
||||||
|
* will not be needed. This should be achievable in less than a minute.
|
||||||
|
* If it takes longer than this something has gone wrong.
|
||||||
|
*/
|
||||||
static int
|
static int
|
||||||
splat_kmem_test8(struct file *file, void *arg)
|
splat_kmem_test8(struct file *file, void *arg)
|
||||||
{
|
{
|
||||||
kmem_cache_priv_t *kcp;
|
kmem_cache_priv_t *kcp;
|
||||||
kmem_cache_data_t *kcd;
|
kmem_cache_thread_t *kct;
|
||||||
int i, rc = 0;
|
int i, rc = 0;
|
||||||
|
|
||||||
kcp = splat_kmem_cache_test_kcp_alloc(file, SPLAT_KMEM_TEST8_NAME,
|
kcp = splat_kmem_cache_test_kcp_alloc(file, SPLAT_KMEM_TEST8_NAME,
|
||||||
256, 0, 0, SPLAT_KMEM_OBJ_COUNT);
|
256, 0, 0);
|
||||||
if (!kcp) {
|
if (!kcp) {
|
||||||
splat_vprint(file, SPLAT_KMEM_TEST8_NAME,
|
splat_vprint(file, SPLAT_KMEM_TEST8_NAME,
|
||||||
"Unable to create '%s'\n", "kcp");
|
"Unable to create '%s'\n", "kcp");
|
||||||
return -ENOMEM;
|
rc = -ENOMEM;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
kcp->kcp_cache =
|
kcp->kcp_cache =
|
||||||
|
@ -811,34 +826,27 @@ splat_kmem_test8(struct file *file, void *arg)
|
||||||
splat_kmem_cache_test_reclaim,
|
splat_kmem_cache_test_reclaim,
|
||||||
kcp, NULL, 0);
|
kcp, NULL, 0);
|
||||||
if (!kcp->kcp_cache) {
|
if (!kcp->kcp_cache) {
|
||||||
splat_kmem_cache_test_kcp_free(kcp);
|
|
||||||
splat_vprint(file, SPLAT_KMEM_TEST8_NAME,
|
splat_vprint(file, SPLAT_KMEM_TEST8_NAME,
|
||||||
"Unable to create '%s'\n", SPLAT_KMEM_CACHE_NAME);
|
"Unable to create '%s'\n", SPLAT_KMEM_CACHE_NAME);
|
||||||
return -ENOMEM;
|
rc = -ENOMEM;
|
||||||
|
goto out_kcp;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < SPLAT_KMEM_OBJ_COUNT; i++) {
|
kct = splat_kmem_cache_test_kct_alloc(kcp, 0);
|
||||||
kcd = kmem_cache_alloc(kcp->kcp_cache, KM_SLEEP);
|
if (!kct) {
|
||||||
spin_lock(&kcp->kcp_lock);
|
splat_vprint(file, SPLAT_KMEM_TEST8_NAME,
|
||||||
kcp->kcp_kcd[i] = kcd;
|
"Unable to create '%s'\n", "kct");
|
||||||
spin_unlock(&kcp->kcp_lock);
|
rc = -ENOMEM;
|
||||||
if (!kcd) {
|
goto out_cache;
|
||||||
splat_vprint(file, SPLAT_KMEM_TEST8_NAME,
|
}
|
||||||
"Unable to allocate from '%s'\n",
|
|
||||||
SPLAT_KMEM_CACHE_NAME);
|
rc = splat_kmem_cache_test_kcd_alloc(kcp, kct, SPLAT_KMEM_OBJ_COUNT);
|
||||||
}
|
if (rc) {
|
||||||
|
splat_vprint(file, SPLAT_KMEM_TEST8_NAME, "Unable to "
|
||||||
|
"allocate from '%s'\n", SPLAT_KMEM_CACHE_NAME);
|
||||||
|
goto out_kct;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Request the slab cache free any objects it can. For a few reasons
|
|
||||||
* this may not immediately result in more free memory even if objects
|
|
||||||
* are freed. First off, due to fragmentation we may not be able to
|
|
||||||
* reclaim any slabs. Secondly, even if we do we fully clear some
|
|
||||||
* slabs we will not want to immedately reclaim all of them because
|
|
||||||
* we may contend with cache allocs and thrash. What we want to see
|
|
||||||
* is the slab size decrease more gradually as it becomes clear they
|
|
||||||
* will not be needed. This should be acheivable in less than minute
|
|
||||||
* if it takes longer than this something has gone wrong.
|
|
||||||
*/
|
|
||||||
for (i = 0; i < 60; i++) {
|
for (i = 0; i < 60; i++) {
|
||||||
kmem_cache_reap_now(kcp->kcp_cache);
|
kmem_cache_reap_now(kcp->kcp_cache);
|
||||||
splat_kmem_cache_test_debug(file, SPLAT_KMEM_TEST8_NAME, kcp);
|
splat_kmem_cache_test_debug(file, SPLAT_KMEM_TEST8_NAME, kcp);
|
||||||
|
@ -864,31 +872,39 @@ splat_kmem_test8(struct file *file, void *arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Cleanup our mess (for failure case of time expiring) */
|
/* Cleanup our mess (for failure case of time expiring) */
|
||||||
spin_lock(&kcp->kcp_lock);
|
splat_kmem_cache_test_kcd_free(kcp, kct);
|
||||||
for (i = 0; i < SPLAT_KMEM_OBJ_COUNT; i++)
|
out_kct:
|
||||||
if (kcp->kcp_kcd[i])
|
splat_kmem_cache_test_kct_free(kcp, kct);
|
||||||
kmem_cache_free(kcp->kcp_cache, kcp->kcp_kcd[i]);
|
out_cache:
|
||||||
spin_unlock(&kcp->kcp_lock);
|
|
||||||
|
|
||||||
kmem_cache_destroy(kcp->kcp_cache);
|
kmem_cache_destroy(kcp->kcp_cache);
|
||||||
|
out_kcp:
|
||||||
splat_kmem_cache_test_kcp_free(kcp);
|
splat_kmem_cache_test_kcp_free(kcp);
|
||||||
|
out:
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Test cache aging, we have allocated a large number of objects thus
|
||||||
|
* creating a large number of slabs and then free'd them all. However,
|
||||||
|
* since there should be little memory pressure at the moment those
|
||||||
|
* slabs have not been freed. What we want to see is the slab size
|
||||||
|
* decrease gradually as it becomes clear they will not be be needed.
|
||||||
|
* This should be achievable in less than minute. If it takes longer
|
||||||
|
* than this something has gone wrong.
|
||||||
|
*/
|
||||||
static int
|
static int
|
||||||
splat_kmem_test9(struct file *file, void *arg)
|
splat_kmem_test9(struct file *file, void *arg)
|
||||||
{
|
{
|
||||||
kmem_cache_priv_t *kcp;
|
kmem_cache_priv_t *kcp;
|
||||||
kmem_cache_data_t *kcd;
|
kmem_cache_thread_t *kct;
|
||||||
int i, rc = 0, count = SPLAT_KMEM_OBJ_COUNT * 128;
|
int i, rc = 0, count = SPLAT_KMEM_OBJ_COUNT * 128;
|
||||||
|
|
||||||
kcp = splat_kmem_cache_test_kcp_alloc(file, SPLAT_KMEM_TEST9_NAME,
|
kcp = splat_kmem_cache_test_kcp_alloc(file, SPLAT_KMEM_TEST9_NAME,
|
||||||
256, 0, 0, count);
|
256, 0, 0);
|
||||||
if (!kcp) {
|
if (!kcp) {
|
||||||
splat_vprint(file, SPLAT_KMEM_TEST9_NAME,
|
splat_vprint(file, SPLAT_KMEM_TEST9_NAME,
|
||||||
"Unable to create '%s'\n", "kcp");
|
"Unable to create '%s'\n", "kcp");
|
||||||
return -ENOMEM;
|
rc = -ENOMEM;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
kcp->kcp_cache =
|
kcp->kcp_cache =
|
||||||
|
@ -897,38 +913,29 @@ splat_kmem_test9(struct file *file, void *arg)
|
||||||
splat_kmem_cache_test_destructor,
|
splat_kmem_cache_test_destructor,
|
||||||
NULL, kcp, NULL, 0);
|
NULL, kcp, NULL, 0);
|
||||||
if (!kcp->kcp_cache) {
|
if (!kcp->kcp_cache) {
|
||||||
splat_kmem_cache_test_kcp_free(kcp);
|
|
||||||
splat_vprint(file, SPLAT_KMEM_TEST9_NAME,
|
splat_vprint(file, SPLAT_KMEM_TEST9_NAME,
|
||||||
"Unable to create '%s'\n", SPLAT_KMEM_CACHE_NAME);
|
"Unable to create '%s'\n", SPLAT_KMEM_CACHE_NAME);
|
||||||
return -ENOMEM;
|
rc = -ENOMEM;
|
||||||
|
goto out_kcp;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < count; i++) {
|
kct = splat_kmem_cache_test_kct_alloc(kcp, 0);
|
||||||
kcd = kmem_cache_alloc(kcp->kcp_cache, KM_SLEEP);
|
if (!kct) {
|
||||||
spin_lock(&kcp->kcp_lock);
|
splat_vprint(file, SPLAT_KMEM_TEST8_NAME,
|
||||||
kcp->kcp_kcd[i] = kcd;
|
"Unable to create '%s'\n", "kct");
|
||||||
spin_unlock(&kcp->kcp_lock);
|
rc = -ENOMEM;
|
||||||
if (!kcd) {
|
goto out_cache;
|
||||||
splat_vprint(file, SPLAT_KMEM_TEST9_NAME,
|
|
||||||
"Unable to allocate from '%s'\n",
|
|
||||||
SPLAT_KMEM_CACHE_NAME);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_lock(&kcp->kcp_lock);
|
rc = splat_kmem_cache_test_kcd_alloc(kcp, kct, count);
|
||||||
for (i = 0; i < count; i++)
|
if (rc) {
|
||||||
if (kcp->kcp_kcd[i])
|
splat_vprint(file, SPLAT_KMEM_TEST9_NAME, "Unable to "
|
||||||
kmem_cache_free(kcp->kcp_cache, kcp->kcp_kcd[i]);
|
"allocate from '%s'\n", SPLAT_KMEM_CACHE_NAME);
|
||||||
spin_unlock(&kcp->kcp_lock);
|
goto out_kct;
|
||||||
|
}
|
||||||
|
|
||||||
|
splat_kmem_cache_test_kcd_free(kcp, kct);
|
||||||
|
|
||||||
/* We have allocated a large number of objects thus creating a
|
|
||||||
* large number of slabs and then free'd them all. However since
|
|
||||||
* there should be little memory pressure at the moment those
|
|
||||||
* slabs have not been freed. What we want to see is the slab
|
|
||||||
* size decrease gradually as it becomes clear they will not be
|
|
||||||
* be needed. This should be acheivable in less than minute
|
|
||||||
* if it takes longer than this something has gone wrong.
|
|
||||||
*/
|
|
||||||
for (i = 0; i < 60; i++) {
|
for (i = 0; i < 60; i++) {
|
||||||
splat_kmem_cache_test_debug(file, SPLAT_KMEM_TEST9_NAME, kcp);
|
splat_kmem_cache_test_debug(file, SPLAT_KMEM_TEST9_NAME, kcp);
|
||||||
|
|
||||||
|
@ -952,9 +959,13 @@ splat_kmem_test9(struct file *file, void *arg)
|
||||||
rc = -ENOMEM;
|
rc = -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out_kct:
|
||||||
|
splat_kmem_cache_test_kct_free(kcp, kct);
|
||||||
|
out_cache:
|
||||||
kmem_cache_destroy(kcp->kcp_cache);
|
kmem_cache_destroy(kcp->kcp_cache);
|
||||||
|
out_kcp:
|
||||||
splat_kmem_cache_test_kcp_free(kcp);
|
splat_kmem_cache_test_kcp_free(kcp);
|
||||||
|
out:
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -971,7 +982,7 @@ splat_kmem_test10(struct file *file, void *arg)
|
||||||
{
|
{
|
||||||
uint64_t size, alloc, rc = 0;
|
uint64_t size, alloc, rc = 0;
|
||||||
|
|
||||||
for (size = 16; size <= 1024*1024; size *= 2) {
|
for (size = 32; size <= 1024*1024; size *= 2) {
|
||||||
|
|
||||||
splat_vprint(file, SPLAT_KMEM_TEST10_NAME, "%-22s %s", "name",
|
splat_vprint(file, SPLAT_KMEM_TEST10_NAME, "%-22s %s", "name",
|
||||||
"time (sec)\tslabs \tobjs \thash\n");
|
"time (sec)\tslabs \tobjs \thash\n");
|
||||||
|
@ -1013,7 +1024,7 @@ splat_kmem_test11(struct file *file, void *arg)
|
||||||
{
|
{
|
||||||
uint64_t size, alloc, rc;
|
uint64_t size, alloc, rc;
|
||||||
|
|
||||||
size = 256*1024;
|
size = 8 * 1024;
|
||||||
alloc = ((4 * physmem * PAGE_SIZE) / size) / SPLAT_KMEM_THREADS;
|
alloc = ((4 * physmem * PAGE_SIZE) / size) / SPLAT_KMEM_THREADS;
|
||||||
|
|
||||||
splat_vprint(file, SPLAT_KMEM_TEST11_NAME, "%-22s %s", "name",
|
splat_vprint(file, SPLAT_KMEM_TEST11_NAME, "%-22s %s", "name",
|
||||||
|
@ -1132,7 +1143,7 @@ static int
|
||||||
splat_kmem_test13(struct file *file, void *arg)
|
splat_kmem_test13(struct file *file, void *arg)
|
||||||
{
|
{
|
||||||
kmem_cache_priv_t *kcp;
|
kmem_cache_priv_t *kcp;
|
||||||
kmem_cache_data_t *kcd;
|
kmem_cache_thread_t *kct;
|
||||||
dummy_page_t *dp;
|
dummy_page_t *dp;
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
struct timespec start, delta = { 0, 0 };
|
struct timespec start, delta = { 0, 0 };
|
||||||
|
@ -1143,11 +1154,12 @@ splat_kmem_test13(struct file *file, void *arg)
|
||||||
count = ((physmem * PAGE_SIZE) / 4 / size);
|
count = ((physmem * PAGE_SIZE) / 4 / size);
|
||||||
|
|
||||||
kcp = splat_kmem_cache_test_kcp_alloc(file, SPLAT_KMEM_TEST13_NAME,
|
kcp = splat_kmem_cache_test_kcp_alloc(file, SPLAT_KMEM_TEST13_NAME,
|
||||||
size, 0, 0, count);
|
size, 0, 0);
|
||||||
if (!kcp) {
|
if (!kcp) {
|
||||||
splat_vprint(file, SPLAT_KMEM_TEST13_NAME,
|
splat_vprint(file, SPLAT_KMEM_TEST13_NAME,
|
||||||
"Unable to create '%s'\n", "kcp");
|
"Unable to create '%s'\n", "kcp");
|
||||||
return -ENOMEM;
|
rc = -ENOMEM;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
kcp->kcp_cache =
|
kcp->kcp_cache =
|
||||||
|
@ -1157,22 +1169,25 @@ splat_kmem_test13(struct file *file, void *arg)
|
||||||
splat_kmem_cache_test_reclaim,
|
splat_kmem_cache_test_reclaim,
|
||||||
kcp, NULL, 0);
|
kcp, NULL, 0);
|
||||||
if (!kcp->kcp_cache) {
|
if (!kcp->kcp_cache) {
|
||||||
splat_kmem_cache_test_kcp_free(kcp);
|
|
||||||
splat_vprint(file, SPLAT_KMEM_TEST13_NAME,
|
splat_vprint(file, SPLAT_KMEM_TEST13_NAME,
|
||||||
"Unable to create '%s'\n", SPLAT_KMEM_CACHE_NAME);
|
"Unable to create '%s'\n", SPLAT_KMEM_CACHE_NAME);
|
||||||
return -ENOMEM;
|
rc = -ENOMEM;
|
||||||
|
goto out_kcp;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < count; i++) {
|
kct = splat_kmem_cache_test_kct_alloc(kcp, 0);
|
||||||
kcd = kmem_cache_alloc(kcp->kcp_cache, KM_SLEEP);
|
if (!kct) {
|
||||||
spin_lock(&kcp->kcp_lock);
|
splat_vprint(file, SPLAT_KMEM_TEST13_NAME,
|
||||||
kcp->kcp_kcd[i] = kcd;
|
"Unable to create '%s'\n", "kct");
|
||||||
spin_unlock(&kcp->kcp_lock);
|
rc = -ENOMEM;
|
||||||
if (!kcd) {
|
goto out_cache;
|
||||||
splat_vprint(file, SPLAT_KMEM_TEST13_NAME,
|
}
|
||||||
"Unable to allocate from '%s'\n",
|
|
||||||
SPLAT_KMEM_CACHE_NAME);
|
rc = splat_kmem_cache_test_kcd_alloc(kcp, kct, count);
|
||||||
}
|
if (rc) {
|
||||||
|
splat_vprint(file, SPLAT_KMEM_TEST13_NAME, "Unable to "
|
||||||
|
"allocate from '%s'\n", SPLAT_KMEM_CACHE_NAME);
|
||||||
|
goto out_kct;
|
||||||
}
|
}
|
||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
|
@ -1180,6 +1195,7 @@ splat_kmem_test13(struct file *file, void *arg)
|
||||||
INIT_LIST_HEAD(&list);
|
INIT_LIST_HEAD(&list);
|
||||||
start = current_kernel_time();
|
start = current_kernel_time();
|
||||||
|
|
||||||
|
/* Apply memory pressure */
|
||||||
while (kcp->kcp_cache->skc_slab_total > (slabs >> 2)) {
|
while (kcp->kcp_cache->skc_slab_total > (slabs >> 2)) {
|
||||||
|
|
||||||
if ((i % 10000) == 0)
|
if ((i % 10000) == 0)
|
||||||
|
@ -1226,15 +1242,14 @@ splat_kmem_test13(struct file *file, void *arg)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Release remaining kmem cache objects */
|
/* Release remaining kmem cache objects */
|
||||||
spin_lock(&kcp->kcp_lock);
|
splat_kmem_cache_test_kcd_free(kcp, kct);
|
||||||
for (i = 0; i < count; i++)
|
out_kct:
|
||||||
if (kcp->kcp_kcd[i])
|
splat_kmem_cache_test_kct_free(kcp, kct);
|
||||||
kmem_cache_free(kcp->kcp_cache, kcp->kcp_kcd[i]);
|
out_cache:
|
||||||
spin_unlock(&kcp->kcp_lock);
|
|
||||||
|
|
||||||
kmem_cache_destroy(kcp->kcp_cache);
|
kmem_cache_destroy(kcp->kcp_cache);
|
||||||
|
out_kcp:
|
||||||
splat_kmem_cache_test_kcp_free(kcp);
|
splat_kmem_cache_test_kcp_free(kcp);
|
||||||
|
out:
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue