![Michal Hocko](/assets/img/avatar_default.png)
Patch series "kvmalloc", v5. There are many open coded kmalloc with vmalloc fallback instances in the tree. Most of them are not careful enough or simply do not care about the underlying semantic of the kmalloc/page allocator which means that a) some vmalloc fallbacks are basically unreachable because the kmalloc part will keep retrying until it succeeds b) the page allocator can invoke a really disruptive steps like the OOM killer to move forward which doesn't sound appropriate when we consider that the vmalloc fallback is available. As it can be seen implementing kvmalloc requires quite an intimate knowledge if the page allocator and the memory reclaim internals which strongly suggests that a helper should be implemented in the memory subsystem proper. Most callers, I could find, have been converted to use the helper instead. This is patch 6. There are some more relying on __GFP_REPEAT in the networking stack which I have converted as well and Eric Dumazet was not opposed [2] to convert them as well. [1] http://lkml.kernel.org/r/20170130094940.13546-1-mhocko@kernel.org [2] http://lkml.kernel.org/r/1485273626.16328.301.camel@edumazet-glaptop3.roam.corp.google.com This patch (of 9): Using kmalloc with the vmalloc fallback for larger allocations is a common pattern in the kernel code. Yet we do not have any common helper for that and so users have invented their own helpers. Some of them are really creative when doing so. Let's just add kv[mz]alloc and make sure it is implemented properly. This implementation makes sure to not make a large memory pressure for > PAGE_SZE requests (__GFP_NORETRY) and also to not warn about allocation failures. This also rules out the OOM killer as the vmalloc is a more approapriate fallback than a disruptive user visible action. This patch also changes some existing users and removes helpers which are specific for them. In some cases this is not possible (e.g. ext4_kvmalloc, libcfs_kvzalloc) because those seems to be broken and require GFP_NO{FS,IO} context which is not vmalloc compatible in general (note that the page table allocation is GFP_KERNEL). Those need to be fixed separately. While we are at it, document that __vmalloc{_node} about unsupported gfp mask because there seems to be a lot of confusion out there. kvmalloc_node will warn about GFP_KERNEL incompatible (which are not superset) flags to catch new abusers. Existing ones would have to die slowly. [sfr@canb.auug.org.au: f2fs fixup] Link: http://lkml.kernel.org/r/20170320163735.332e64b7@canb.auug.org.au Link: http://lkml.kernel.org/r/20170306103032.2540-2-mhocko@kernel.org Signed-off-by: Michal Hocko <mhocko@suse.com> Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au> Reviewed-by: Andreas Dilger <adilger@dilger.ca> [ext4 part] Acked-by: Vlastimil Babka <vbabka@suse.cz> Cc: John Hubbard <jhubbard@nvidia.com> Cc: David Miller <davem@davemloft.net> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
174 lines
4.3 KiB
C
174 lines
4.3 KiB
C
/*
|
|
* AppArmor security module
|
|
*
|
|
* This file contains basic common functions used in AppArmor
|
|
*
|
|
* Copyright (C) 1998-2008 Novell/SUSE
|
|
* Copyright 2009-2010 Canonical Ltd.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation, version 2 of the
|
|
* License.
|
|
*/
|
|
|
|
#include <linux/ctype.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/string.h>
|
|
#include <linux/vmalloc.h>
|
|
|
|
#include "include/audit.h"
|
|
#include "include/apparmor.h"
|
|
#include "include/lib.h"
|
|
#include "include/policy.h"
|
|
|
|
/**
|
|
* aa_split_fqname - split a fqname into a profile and namespace name
|
|
* @fqname: a full qualified name in namespace profile format (NOT NULL)
|
|
* @ns_name: pointer to portion of the string containing the ns name (NOT NULL)
|
|
*
|
|
* Returns: profile name or NULL if one is not specified
|
|
*
|
|
* Split a namespace name from a profile name (see policy.c for naming
|
|
* description). If a portion of the name is missing it returns NULL for
|
|
* that portion.
|
|
*
|
|
* NOTE: may modify the @fqname string. The pointers returned point
|
|
* into the @fqname string.
|
|
*/
|
|
char *aa_split_fqname(char *fqname, char **ns_name)
|
|
{
|
|
char *name = strim(fqname);
|
|
|
|
*ns_name = NULL;
|
|
if (name[0] == ':') {
|
|
char *split = strchr(&name[1], ':');
|
|
*ns_name = skip_spaces(&name[1]);
|
|
if (split) {
|
|
/* overwrite ':' with \0 */
|
|
*split++ = 0;
|
|
if (strncmp(split, "//", 2) == 0)
|
|
split += 2;
|
|
name = skip_spaces(split);
|
|
} else
|
|
/* a ns name without a following profile is allowed */
|
|
name = NULL;
|
|
}
|
|
if (name && *name == 0)
|
|
name = NULL;
|
|
|
|
return name;
|
|
}
|
|
|
|
/**
|
|
* skipn_spaces - Removes leading whitespace from @str.
|
|
* @str: The string to be stripped.
|
|
*
|
|
* Returns a pointer to the first non-whitespace character in @str.
|
|
* if all whitespace will return NULL
|
|
*/
|
|
|
|
static const char *skipn_spaces(const char *str, size_t n)
|
|
{
|
|
for (; n && isspace(*str); --n)
|
|
++str;
|
|
if (n)
|
|
return (char *)str;
|
|
return NULL;
|
|
}
|
|
|
|
const char *aa_splitn_fqname(const char *fqname, size_t n, const char **ns_name,
|
|
size_t *ns_len)
|
|
{
|
|
const char *end = fqname + n;
|
|
const char *name = skipn_spaces(fqname, n);
|
|
|
|
if (!name)
|
|
return NULL;
|
|
*ns_name = NULL;
|
|
*ns_len = 0;
|
|
if (name[0] == ':') {
|
|
char *split = strnchr(&name[1], end - &name[1], ':');
|
|
*ns_name = skipn_spaces(&name[1], end - &name[1]);
|
|
if (!*ns_name)
|
|
return NULL;
|
|
if (split) {
|
|
*ns_len = split - *ns_name;
|
|
if (*ns_len == 0)
|
|
*ns_name = NULL;
|
|
split++;
|
|
if (end - split > 1 && strncmp(split, "//", 2) == 0)
|
|
split += 2;
|
|
name = skipn_spaces(split, end - split);
|
|
} else {
|
|
/* a ns name without a following profile is allowed */
|
|
name = NULL;
|
|
*ns_len = end - *ns_name;
|
|
}
|
|
}
|
|
if (name && *name == 0)
|
|
name = NULL;
|
|
|
|
return name;
|
|
}
|
|
|
|
/**
|
|
* aa_info_message - log a none profile related status message
|
|
* @str: message to log
|
|
*/
|
|
void aa_info_message(const char *str)
|
|
{
|
|
if (audit_enabled) {
|
|
DEFINE_AUDIT_DATA(sa, LSM_AUDIT_DATA_NONE, NULL);
|
|
|
|
aad(&sa)->info = str;
|
|
aa_audit_msg(AUDIT_APPARMOR_STATUS, &sa, NULL);
|
|
}
|
|
printk(KERN_INFO "AppArmor: %s\n", str);
|
|
}
|
|
|
|
/**
|
|
* aa_policy_init - initialize a policy structure
|
|
* @policy: policy to initialize (NOT NULL)
|
|
* @prefix: prefix name if any is required. (MAYBE NULL)
|
|
* @name: name of the policy, init will make a copy of it (NOT NULL)
|
|
*
|
|
* Note: this fn creates a copy of strings passed in
|
|
*
|
|
* Returns: true if policy init successful
|
|
*/
|
|
bool aa_policy_init(struct aa_policy *policy, const char *prefix,
|
|
const char *name, gfp_t gfp)
|
|
{
|
|
/* freed by policy_free */
|
|
if (prefix) {
|
|
policy->hname = kmalloc(strlen(prefix) + strlen(name) + 3,
|
|
gfp);
|
|
if (policy->hname)
|
|
sprintf((char *)policy->hname, "%s//%s", prefix, name);
|
|
} else
|
|
policy->hname = kstrdup(name, gfp);
|
|
if (!policy->hname)
|
|
return false;
|
|
/* base.name is a substring of fqname */
|
|
policy->name = basename(policy->hname);
|
|
INIT_LIST_HEAD(&policy->list);
|
|
INIT_LIST_HEAD(&policy->profiles);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* aa_policy_destroy - free the elements referenced by @policy
|
|
* @policy: policy that is to have its elements freed (NOT NULL)
|
|
*/
|
|
void aa_policy_destroy(struct aa_policy *policy)
|
|
{
|
|
AA_BUG(on_list_rcu(&policy->profiles));
|
|
AA_BUG(on_list_rcu(&policy->list));
|
|
|
|
/* don't free name as its a subset of hname */
|
|
kzfree(policy->hname);
|
|
}
|