mirror of
https://github.com/openharmony/kernel_linux_common_modules.git
synced 2026-08-26 17:16:37 -04:00
sched: add qos ctrl and authority ctrl for FFRT
ohos inclusion category: feature issue: #I76RYD CVE:NA Signed-off-by: Huzhaodong <huzhaodong@huawei.com>
This commit is contained in:
committed by
wanxiaoqing40281
parent
af27f4af85
commit
90dc7a386f
@@ -1,5 +1,6 @@
|
||||
(1) The directories below are licensed under GPL-2.0-or-later.
|
||||
./newip/
|
||||
./xpm/
|
||||
./qos_auth/
|
||||
|
||||
As for the specific use of the licenses, please refer to the relevant description in the documents.
|
||||
|
||||
@@ -59,8 +59,10 @@ Note:If the text contains special characters, please escape them according to th
|
||||
<policy name="newipPolicy" desc="New IP protocol stack">
|
||||
<policyitem type="compatibility" name="GPL" path="newip/.*" rule="may" group="defaultGroup" filefilter="defaultPolicyFilter"/>
|
||||
<policyitem type="compatibility" name="GPL" path="xpm/.*" rule="may" group="defaultGroup" filefilter="defaultPolicyFilter"/>
|
||||
<policyitem type="compatibility" name="GPL" path="qos_auth/.*" rule="may" group="defaultGroup" filefilter="defaultPolicyFilter"/>
|
||||
<policyitem type="license" name="GPL" path="newip/.*" rule="may" group="defaultGroup" filefilter="defaultPolicyFilter"/>
|
||||
<policyitem type="license" name="GPL" path="xpm/.*" rule="may" group="defaultGroup" filefilter="defaultPolicyFilter"/>
|
||||
<policyitem type="license" name="GPL" path="qos_auth/.*" rule="may" group="defaultGroup" filefilter="defaultPolicyFilter"/>
|
||||
</policy>
|
||||
</policylist>
|
||||
<filefilterlist>
|
||||
@@ -78,6 +80,7 @@ Note:If the text contains special characters, please escape them according to th
|
||||
<filefilter name="binaryFileTypePolicyFilter" desc="binary file filter">
|
||||
<filteritem type="filepath" name="newip/figures/.*"/>
|
||||
<filteritem type="filepath" name="xpm/figures/.*"/>
|
||||
<filteritem type="filepath" name="qos_auth/figures/.*"/>
|
||||
</filefilter>
|
||||
</filefilterlist>
|
||||
</oatconfig>
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
## 背景
|
||||
|
||||
为支持并发编程框架FFRT的底层调度能力而设计,允许app侧根据业务逻辑在线程间分配调度资源,保障关键任务时延达标,保障用户体验。
|
||||
|
||||
涉及到权限下放和调度资源分配,需要内核实现:
|
||||
|
||||
1. 高效的能感知状态的app权限管控机制(轻量化权限管控)
|
||||
2. 动态的调度资源分配机制(动态多级qos)
|
||||
|
||||
## auth_ctrl(Authority Control)模块
|
||||
|
||||
轻量化权限管控模块,uid粒度的权限管控,根据app前后台状态动态管控对内核feature接口的访问权限
|
||||
|
||||

|
||||
|
||||
## qos_ctrl(Qos Control)模块
|
||||
|
||||
动态多级qos模块,提供多种policy(前台/后台/system等),每个policy包含6个qos等级,可以从时间片、调度时延、供给3个维度去更改线程调度行为
|
||||
|
||||

|
||||
|
||||
## 目录
|
||||
|
||||
```
|
||||
├── README_zh.md
|
||||
├── apply_qos_auth.sh
|
||||
├── figures # ReadMe 内嵌图例
|
||||
├── auth_ctl
|
||||
│ ├── Kconfig
|
||||
│ ├── Makefile
|
||||
│ ├── auth_ctrl.c #权限管控主体代码
|
||||
│ ├── auth_ctrl.h
|
||||
│ ├── auth_qos_debug.c #debug节点主体
|
||||
│ ├── qos_ctrl.c #多级qos主体代码
|
||||
│ └── qos_ctrl.h
|
||||
└── include
|
||||
├── auth_ctrl.h
|
||||
├── qos_auth.h
|
||||
├── qos_ctrl.h
|
||||
└── rtg_auth.h
|
||||
```
|
||||
|
||||
## QOS_AUTH配置指导
|
||||
|
||||
1. 权限管控使能`CONFIG_AUTHORITY_CTRL=y`
|
||||
2. 多级qos使能`CONFIG_QOS_CTRL=y`
|
||||
3. RTG鉴权使能`CONFIG_RTG_AUTHORITY=y`
|
||||
4. qos鉴权使能`CONFIG_QOS_AUTHORITY=y`
|
||||
5. Debug节点使能`CONFIG_AUTH_QOS_DEBUG=y`
|
||||
6. Qos策略数量限制`CONFIG_QOS_POLICY_MAX_NR=5`,范围[5, 20]
|
||||
|
||||
qos成功设置的宏依赖:
|
||||
|
||||
```
|
||||
# 时延控制
|
||||
CONFIG_SCHED_LATENCY_NICE=y
|
||||
|
||||
# 供给
|
||||
CONFIG_UCLAMP_TASK=y
|
||||
CONFIG_UCLAMP_BUCKETS_COUNT=20
|
||||
CONFIG_UCLAMP_TASK_GROUP=y
|
||||
```
|
||||
|
||||
## 相关仓
|
||||
|
||||
|
||||
[kernel_linux_5.10](https://gitee.com/openharmony/kernel_linux_5.10)
|
||||
|
||||
[kernel_linux_config](https://gitee.com/openharmony/kernel_linux_config)
|
||||
|
||||
[device_board_hihope](https://gitee.com/openharmony/device_board_hihope)https://gitee.com/openharmony/device_board_hihope)
|
||||
@@ -0,0 +1,31 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
# Copyright (c) 2023 Huawei Device Co., Ltd.
|
||||
#
|
||||
|
||||
set -e
|
||||
|
||||
OHOS_SOURCE_ROOT=$1
|
||||
KERNEL_BUILD_ROOT=$2
|
||||
PRODUCT_NAME=$3
|
||||
KERNEL_VERSION=$4
|
||||
QOS_AUTH_SOURCE_ROOT=$OHOS_SOURCE_ROOT/kernel/linux/common_modules/qos_auth
|
||||
|
||||
function main()
|
||||
{
|
||||
pushd .
|
||||
|
||||
cd $KERNEL_BUILD_ROOT/include/linux/sched
|
||||
ln -sf $(realpath --relative-to=$KERNEL_BUILD_ROOT/include/linux/sched $QOS_AUTH_SOURCE_ROOT/include)/*.h ./
|
||||
|
||||
if [ ! -d "$KERNEL_BUILD_ROOT/drivers/auth_ctl" ]; then
|
||||
mkdir $KERNEL_BUILD_ROOT/drivers/auth_ctl
|
||||
fi
|
||||
|
||||
cd $KERNEL_BUILD_ROOT/drivers/auth_ctl
|
||||
ln -sf $(realpath --relative-to=$KERNEL_BUILD_ROOT/drivers/auth_ctl $QOS_AUTH_SOURCE_ROOT/auth_ctl)/* ./
|
||||
|
||||
popd
|
||||
}
|
||||
|
||||
main
|
||||
@@ -0,0 +1,55 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
config AUTHORITY_CTRL
|
||||
tristate "Authority Control for RTG & QOS"
|
||||
default n
|
||||
help
|
||||
Control thread's authority for specific kenrel feature such as RTG
|
||||
or QOS. Use uid as the authentication granularity. Status switching
|
||||
will change uid's authority, and would trigger additional actions
|
||||
registered by specific kernel feature.
|
||||
|
||||
config QOS_CTRL
|
||||
bool "Multiple Level Qos Control for thread"
|
||||
default n
|
||||
depends on AUTHORITY_CTRL
|
||||
help
|
||||
If set, thread can apply qos for less execution latency and get more
|
||||
cpu supply. Permission and absolute supply aggressiveness was controlled
|
||||
by AUTHORITY_CTRL.
|
||||
|
||||
config RTG_AUTHORITY
|
||||
bool "Authority Control for SCHED_RTG_FRAME"
|
||||
default n
|
||||
depends on AUTHORITY_CTRL
|
||||
depends on SCHED_RTG_FRAME
|
||||
help
|
||||
Authority control for SCHED_RTG_FRAME. If set, access to SCHED_RTG_FRAME's
|
||||
ioctl cmd will be restricted.
|
||||
|
||||
config QOS_AUTHORITY
|
||||
bool "Authority Control for QOS_CTRL"
|
||||
default n
|
||||
depends on AUTHORITY_CTRL
|
||||
depends on QOS_CTRL
|
||||
help
|
||||
Authority control for QOS_CTRL. If set, access to QOS_CTRL's ioctl cmd will
|
||||
be restricted.
|
||||
|
||||
config AUTH_QOS_DEBUG
|
||||
bool "Debug fs for qos_ctrl and auth_ctrl"
|
||||
default n
|
||||
depends on AUTHORITY_CTRL
|
||||
depends on RTG_AUTHORITY
|
||||
depends on QOS_AUTHORITY
|
||||
help
|
||||
If set, debug node will show auth and qos info
|
||||
|
||||
config QOS_POLICY_MAX_NR
|
||||
int "Number of supported qos policy"
|
||||
range 5 20
|
||||
default 5
|
||||
depends on QOS_CTRL
|
||||
help
|
||||
Qos policy number limit. Truly initialized qos policy could small then
|
||||
this value.
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-$(CONFIG_AUTHORITY_CTRL) += auth_qos_ctrl.o
|
||||
auth_qos_ctrl-$(CONFIG_AUTHORITY_CTRL) += auth_ctrl.o
|
||||
auth_qos_ctrl-$(CONFIG_QOS_CTRL) += qos_ctrl.o
|
||||
auth_qos_ctrl-$(CONFIG_AUTH_QOS_DEBUG) += auth_qos_debug.o
|
||||
@@ -0,0 +1,588 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* drivers/auth_ctl/auth_ctrl.c
|
||||
*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/cred.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/sched/auth_ctrl.h>
|
||||
#include <linux/sched/rtg_auth.h>
|
||||
#include <linux/sched/qos_ctrl.h>
|
||||
#include <linux/sched/qos_auth.h>
|
||||
|
||||
#include "auth_ctrl.h"
|
||||
#ifdef CONFIG_QOS_CTRL
|
||||
#include "qos_ctrl.h"
|
||||
#endif
|
||||
|
||||
typedef long (*auth_ctrl_func)(struct file *file, void __user *arg);
|
||||
|
||||
static long ctrl_auth_basic_operation(struct file *file, void __user *uarg);
|
||||
|
||||
static auth_ctrl_func g_func_array[AUTH_CTRL_MAX_NR] = {
|
||||
NULL, /* reserved */
|
||||
ctrl_auth_basic_operation,
|
||||
};
|
||||
|
||||
/*
|
||||
* uid-based authority idr table
|
||||
*/
|
||||
static struct idr *ua_idr;
|
||||
|
||||
struct idr *get_auth_ctrl_idr(void)
|
||||
{
|
||||
return ua_idr;
|
||||
}
|
||||
|
||||
static DEFINE_MUTEX(ua_idr_mutex);
|
||||
|
||||
struct mutex *get_auth_idr_mutex(void)
|
||||
{
|
||||
return &ua_idr_mutex;
|
||||
}
|
||||
|
||||
static struct auth_struct auth_super;
|
||||
|
||||
/*
|
||||
* change auth's status to SYSTEM and enable all feature access
|
||||
*/
|
||||
static void change_to_super(struct auth_struct *auth)
|
||||
{
|
||||
#ifdef CONFIG_RTG_AUTHORITY
|
||||
auth->rtg_auth_flag = AF_RTG_ALL;
|
||||
#endif
|
||||
#ifdef CONFIG_QOS_AUTHORITY
|
||||
auth->qos_auth_flag = AF_QOS_ALL;
|
||||
#endif
|
||||
auth->status = AUTH_STATUS_SYSTEM_SERVER;
|
||||
}
|
||||
|
||||
static void init_authority_record(struct auth_struct *auth)
|
||||
{
|
||||
#ifdef CONFIG_QOS_AUTHORITY
|
||||
int i;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_RTG_AUTHORITY
|
||||
auth->rtg_auth_flag = 0;
|
||||
#endif
|
||||
#ifdef CONFIG_QOS_AUTHORITY
|
||||
auth->qos_auth_flag = 0;
|
||||
#endif
|
||||
auth->status = AUTH_STATUS_DISABLED;
|
||||
mutex_init(&auth->mutex);
|
||||
refcount_set(&auth->usage, 1);
|
||||
#ifdef CONFIG_QOS_CTRL
|
||||
for (i = QOS_POLICY_MIN_LEVEL; i < NR_QOS; ++i) {
|
||||
INIT_LIST_HEAD(&auth->tasks[i]);
|
||||
auth->num[i] = 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void get_auth_struct(struct auth_struct *auth)
|
||||
{
|
||||
refcount_inc(&auth->usage);
|
||||
}
|
||||
|
||||
static void __put_auth_struct(struct auth_struct *auth)
|
||||
|
||||
{
|
||||
WARN_ON(auth->status != AUTH_STATUS_DEAD);
|
||||
WARN_ON(refcount_read(&auth->usage));
|
||||
|
||||
#ifdef CONFIG_QOS_CTRL
|
||||
/* refcount is zero here, no contend, no lock. */
|
||||
remove_qos_tasks(auth);
|
||||
#endif
|
||||
kfree(auth);
|
||||
}
|
||||
|
||||
void put_auth_struct(struct auth_struct *auth)
|
||||
{
|
||||
if (refcount_dec_and_test(&auth->usage))
|
||||
__put_auth_struct(auth);
|
||||
}
|
||||
|
||||
static int init_authority_control(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ua_idr = kzalloc(sizeof(*ua_idr), GFP_ATOMIC);
|
||||
if (ua_idr == NULL) {
|
||||
pr_err("[AUTH_CTRL] auth idr init failed, no memory!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
idr_init(ua_idr);
|
||||
|
||||
init_authority_record(&auth_super);
|
||||
change_to_super(&auth_super);
|
||||
|
||||
ret = idr_alloc(ua_idr, &auth_super, SUPER_UID, SUPER_UID + 1, GFP_ATOMIC);
|
||||
if (ret != SUPER_UID) {
|
||||
pr_err("[AUTH_CTRL] authority for super init failed! ret=%d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
idr_destroy(ua_idr);
|
||||
kfree(ua_idr);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int authority_remove_handler(int id, void *p, void *para)
|
||||
{
|
||||
struct auth_struct *auth = (struct auth_struct *)p;
|
||||
|
||||
mutex_lock(&auth->mutex);
|
||||
#ifdef CONFIG_QOS_CTRL
|
||||
qos_switch(auth, AUTH_STATUS_DISABLED);
|
||||
#endif
|
||||
auth->status = AUTH_STATUS_DEAD;
|
||||
mutex_unlock(&auth->mutex);
|
||||
put_auth_struct(auth);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void remove_authority_control(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ua_idr_mutex);
|
||||
ret = idr_for_each(ua_idr, authority_remove_handler, NULL);
|
||||
if (ret < 0)
|
||||
pr_err("[AUTH_CTRL] authority item remove failed\n");
|
||||
|
||||
idr_destroy(ua_idr);
|
||||
kfree(ua_idr);
|
||||
|
||||
mutex_unlock(&ua_idr_mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
* constrain user assigned auth_flag to kernel accepted auth_flag
|
||||
*/
|
||||
static int generic_auth_trim(unsigned int orig_flag, unsigned int constrain)
|
||||
{
|
||||
return orig_flag & constrain;
|
||||
}
|
||||
|
||||
static inline void set_auth_flag(struct auth_ctrl_data *data, struct auth_struct *auth_to_enable)
|
||||
{
|
||||
#ifdef CONFIG_RTG_AUTHORITY
|
||||
auth_to_enable->rtg_auth_flag = generic_auth_trim(data->rtg_ua_flag, AF_RTG_DELEGATED);
|
||||
#endif
|
||||
#ifdef CONFIG_QOS_AUTHORITY
|
||||
auth_to_enable->qos_auth_flag = generic_auth_trim(data->qos_ua_flag, AF_QOS_ALL);
|
||||
#endif
|
||||
}
|
||||
|
||||
static int auth_enable(struct auth_ctrl_data *data)
|
||||
{
|
||||
struct auth_struct *auth_to_enable;
|
||||
unsigned int uid = data->uid;
|
||||
int status = data->status;
|
||||
int ret;
|
||||
|
||||
mutex_lock(&ua_idr_mutex);
|
||||
auth_to_enable = idr_find(ua_idr, uid);
|
||||
/* auth exist, just resume the task's qos request */
|
||||
if (auth_to_enable) {
|
||||
get_auth_struct(auth_to_enable);
|
||||
mutex_unlock(&ua_idr_mutex);
|
||||
|
||||
mutex_lock(&auth_to_enable->mutex);
|
||||
if (auth_to_enable->status == AUTH_STATUS_DEAD) {
|
||||
mutex_unlock(&auth_to_enable->mutex);
|
||||
put_auth_struct(auth_to_enable);
|
||||
return -INVALID_AUTH;
|
||||
}
|
||||
|
||||
set_auth_flag(data, auth_to_enable);
|
||||
#ifdef CONFIG_QOS_CTRL
|
||||
qos_switch(auth_to_enable, status);
|
||||
#endif
|
||||
auth_to_enable->status = status;
|
||||
mutex_unlock(&auth_to_enable->mutex);
|
||||
ret = 0;
|
||||
put_auth_struct(auth_to_enable);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* auth not exist, build a new auth, then insert to idr */
|
||||
auth_to_enable = kzalloc(sizeof(*auth_to_enable), GFP_ATOMIC);
|
||||
if (!auth_to_enable) {
|
||||
mutex_unlock(&ua_idr_mutex);
|
||||
pr_err("[AUTH_CTRL] alloc auth data failed, no memory!\n");
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
init_authority_record(auth_to_enable);
|
||||
|
||||
/* no one could get the auth from idr now, no need to lock */
|
||||
set_auth_flag(data, auth_to_enable);
|
||||
auth_to_enable->status = status;
|
||||
|
||||
ret = idr_alloc(ua_idr, auth_to_enable, uid, uid + 1, GFP_ATOMIC);
|
||||
if (ret < 0) {
|
||||
pr_err("[AUTH_CTRL] add auth to idr failed, no memory!\n");
|
||||
kfree(auth_to_enable);
|
||||
}
|
||||
|
||||
mutex_unlock(&ua_idr_mutex);
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int auth_delete(struct auth_ctrl_data *data)
|
||||
{
|
||||
struct auth_struct *auth_to_delete;
|
||||
unsigned int uid = data->uid;
|
||||
|
||||
mutex_lock(&ua_idr_mutex);
|
||||
auth_to_delete = (struct auth_struct *)idr_remove(ua_idr, uid);
|
||||
if (!auth_to_delete) {
|
||||
mutex_unlock(&ua_idr_mutex);
|
||||
pr_err("[AUTH_CTRL] no auth data for this uid=%d, delete failed\n", uid);
|
||||
return -UID_NOT_FOUND;
|
||||
}
|
||||
mutex_unlock(&ua_idr_mutex);
|
||||
|
||||
mutex_lock(&auth_to_delete->mutex);
|
||||
#ifdef CONFIG_QOS_CTRL
|
||||
qos_switch(auth_to_delete, AUTH_STATUS_DISABLED);
|
||||
#endif
|
||||
auth_to_delete->status = AUTH_STATUS_DEAD;
|
||||
mutex_unlock(&auth_to_delete->mutex);
|
||||
|
||||
put_auth_struct(auth_to_delete);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int auth_get(struct auth_ctrl_data *data)
|
||||
{
|
||||
struct auth_struct *auth_to_get;
|
||||
unsigned int uid = data->uid;
|
||||
|
||||
mutex_lock(&ua_idr_mutex);
|
||||
auth_to_get = idr_find(ua_idr, uid);
|
||||
if (!auth_to_get) {
|
||||
mutex_unlock(&ua_idr_mutex);
|
||||
pr_err("[AUTH_CTRL] no auth data for this uid=%d to get\n", uid);
|
||||
return -UID_NOT_FOUND;
|
||||
}
|
||||
get_auth_struct(auth_to_get);
|
||||
mutex_unlock(&ua_idr_mutex);
|
||||
|
||||
mutex_lock(&auth_to_get->mutex);
|
||||
if (auth_to_get->status == AUTH_STATUS_DEAD) {
|
||||
mutex_unlock(&auth_to_get->mutex);
|
||||
put_auth_struct(auth_to_get);
|
||||
return -INVALID_AUTH;
|
||||
}
|
||||
#ifdef CONFIG_RTG_AUTHORITY
|
||||
data->rtg_ua_flag = auth_to_get->rtg_auth_flag;
|
||||
#endif
|
||||
#ifdef CONFIG_QOS_AUTHORITY
|
||||
data->qos_ua_flag = auth_to_get->qos_auth_flag;
|
||||
#endif
|
||||
data->status = auth_to_get->status;
|
||||
mutex_unlock(&auth_to_get->mutex);
|
||||
|
||||
put_auth_struct(auth_to_get);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int auth_switch(struct auth_ctrl_data *data)
|
||||
{
|
||||
struct auth_struct *auth;
|
||||
unsigned int uid = data->uid;
|
||||
unsigned int status = data->status;
|
||||
|
||||
if (status == 0 || status >= AUTH_STATUS_MAX_NR) {
|
||||
pr_err("[AUTH_CTRL] not valied status %d\n", status);
|
||||
return -ARG_INVALID;
|
||||
}
|
||||
|
||||
mutex_lock(&ua_idr_mutex);
|
||||
auth = idr_find(ua_idr, uid);
|
||||
if (!auth) {
|
||||
mutex_unlock(&ua_idr_mutex);
|
||||
pr_err("[AUTH_CTRL] no auth data for this uid to switch=%d\n", uid);
|
||||
return -UID_NOT_FOUND;
|
||||
}
|
||||
get_auth_struct(auth);
|
||||
mutex_unlock(&ua_idr_mutex);
|
||||
|
||||
mutex_lock(&auth->mutex);
|
||||
if (auth->status == AUTH_STATUS_DEAD) {
|
||||
mutex_unlock(&auth->mutex);
|
||||
put_auth_struct(auth);
|
||||
return -INVALID_AUTH;
|
||||
}
|
||||
|
||||
set_auth_flag(data, auth);
|
||||
#ifdef CONFIG_QOS_CTRL
|
||||
qos_switch(auth, status);
|
||||
#endif
|
||||
auth->status = status;
|
||||
mutex_unlock(&auth->mutex);
|
||||
|
||||
put_auth_struct(auth);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef int (*auth_manipulate_func)(struct auth_ctrl_data *data);
|
||||
|
||||
static auth_manipulate_func auth_func_array[AUTH_MAX_NR] = {
|
||||
/*
|
||||
* auth_enable: Start authority control for specific uid.
|
||||
* auth_delte: End authroity control, remove statistic datas.
|
||||
* auth_get: Get auth info, deprecated.
|
||||
* auth_switch: Change authority flag and status for specific uid.
|
||||
*/
|
||||
NULL,
|
||||
auth_enable,
|
||||
auth_delete,
|
||||
auth_get,
|
||||
auth_switch,
|
||||
};
|
||||
|
||||
static long do_auth_manipulate(struct auth_ctrl_data *data)
|
||||
{
|
||||
long ret = 0;
|
||||
unsigned int type = data->type;
|
||||
|
||||
if (type >= AUTH_MAX_NR) {
|
||||
pr_err("[AUTH_CTRL] BASIC_AUTH_CTRL_OPERATION type not valid\n");
|
||||
return -ARG_INVALID;
|
||||
}
|
||||
|
||||
if (auth_func_array[type])
|
||||
ret = (long)(*auth_func_array[type])(data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long ctrl_auth_basic_operation(struct file *file, void __user *uarg)
|
||||
{
|
||||
struct auth_ctrl_data auth_data;
|
||||
long ret;
|
||||
|
||||
if (copy_from_user(&auth_data, uarg, sizeof(struct auth_ctrl_data))) {
|
||||
pr_err("[AUTH_CTRL] BASIC_AUTH_CTRL_OPERATION copy data failed\n");
|
||||
return -ARG_INVALID;
|
||||
}
|
||||
|
||||
ret = do_auth_manipulate(&auth_data);
|
||||
if (ret < 0) {
|
||||
pr_err("[AUTH_CTRL] BASIC_AUTH_CTRL_OPERATION failed\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (copy_to_user(uarg, &auth_data, sizeof(struct auth_ctrl_data))) {
|
||||
pr_err("[AUTH_CTRL] BASIC_AUTH_CTRL_OPERATION send data failed\n");
|
||||
return -ARG_INVALID;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
long auth_ctrl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
void __user *uarg = (void __user *)arg;
|
||||
unsigned int func_cmd = _IOC_NR(cmd);
|
||||
|
||||
if (uarg == NULL) {
|
||||
pr_err("%s: invalid user uarg\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (_IOC_TYPE(cmd) != AUTH_CTRL_IPC_MAGIG) {
|
||||
pr_err("%s: authority ctrl magic fail, TYPE=%d\n",
|
||||
__func__, _IOC_TYPE(cmd));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (func_cmd >= AUTH_CTRL_MAX_NR) {
|
||||
pr_err("%s: authority ctrl cmd error, cmd:%d\n",
|
||||
__func__, _IOC_TYPE(cmd));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (g_func_array[func_cmd])
|
||||
return (*g_func_array[func_cmd])(file, uarg);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#define get_authority_flag(func_id) (1 << (func_id - 1))
|
||||
|
||||
static inline unsigned int get_true_uid(struct task_struct *p)
|
||||
{
|
||||
if (!p)
|
||||
return get_uid(current_user())->uid.val;
|
||||
|
||||
return task_uid(p).val;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return 1000 for both SYSTEM and ROOT
|
||||
* Return current's uid if p is NULL
|
||||
*/
|
||||
static inline unsigned int get_authority_uid(struct task_struct *p)
|
||||
{
|
||||
unsigned int uid = get_true_uid(p);
|
||||
|
||||
if (super_uid(uid))
|
||||
uid = SUPER_UID;
|
||||
|
||||
return uid;
|
||||
}
|
||||
|
||||
static unsigned int auth_flag(struct auth_struct *auth, unsigned int type)
|
||||
{
|
||||
switch (type) {
|
||||
#ifdef CONFIG_RTG_AUTHORITY
|
||||
case RTG_AUTH_FLAG:
|
||||
return auth->rtg_auth_flag;
|
||||
#endif
|
||||
#ifdef CONFIG_QOS_AUTHORITY
|
||||
case QOS_AUTH_FLAG:
|
||||
return auth->qos_auth_flag;
|
||||
#endif
|
||||
default:
|
||||
pr_err("[AUTH_CTRL] not valid auth type\n");
|
||||
return INVALIED_AUTH_FLAG;
|
||||
}
|
||||
}
|
||||
|
||||
bool check_authorized(unsigned int func_id, unsigned int type)
|
||||
{
|
||||
bool authorized = false;
|
||||
struct auth_struct *auth;
|
||||
unsigned int af = get_authority_flag(func_id);
|
||||
unsigned int uid = get_authority_uid(NULL);
|
||||
|
||||
mutex_lock(&ua_idr_mutex);
|
||||
if (!ua_idr) {
|
||||
mutex_unlock(&ua_idr_mutex);
|
||||
pr_err("[AUTH_CTRL] authority idr table missed, auth failed\n");
|
||||
return authorized;
|
||||
}
|
||||
|
||||
auth = (struct auth_struct *)idr_find(ua_idr, uid);
|
||||
if (!auth) {
|
||||
mutex_unlock(&ua_idr_mutex);
|
||||
pr_err("[AUTH_CTRL] no auth data for this uid=%d\n", uid);
|
||||
return authorized;
|
||||
}
|
||||
get_auth_struct(auth);
|
||||
mutex_unlock(&ua_idr_mutex);
|
||||
|
||||
mutex_lock(&auth->mutex);
|
||||
if (auth->status == AUTH_STATUS_DEAD) {
|
||||
mutex_unlock(&auth->mutex);
|
||||
pr_info("[AUTH_CTRL] not valied auth for uid %d\n", uid);
|
||||
put_auth_struct(auth);
|
||||
return authorized;
|
||||
}
|
||||
if (auth && (auth_flag(auth, type) & af))
|
||||
authorized = true;
|
||||
|
||||
mutex_unlock(&auth->mutex);
|
||||
|
||||
put_auth_struct(auth);
|
||||
|
||||
return authorized;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return authority info for given task
|
||||
* return current's auth if p is NULL
|
||||
* recount will inc if this call return the valid auth
|
||||
* make sure to call put_auth_struct before the calling end
|
||||
*/
|
||||
struct auth_struct *get_authority(struct task_struct *p)
|
||||
{
|
||||
unsigned int uid = get_authority_uid(p);
|
||||
struct auth_struct *auth;
|
||||
|
||||
mutex_lock(&ua_idr_mutex);
|
||||
auth = idr_find(ua_idr, uid);
|
||||
if (auth)
|
||||
get_auth_struct(auth);
|
||||
mutex_unlock(&ua_idr_mutex);
|
||||
|
||||
return auth;
|
||||
}
|
||||
|
||||
static const struct file_operations auth_ctrl_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.unlocked_ioctl = auth_ctrl_ioctl,
|
||||
.compat_ioctl = auth_ctrl_ioctl,
|
||||
};
|
||||
|
||||
static struct miscdevice auth_ctrl_device = {
|
||||
.minor = MISC_DYNAMIC_MINOR,
|
||||
.name = "auth_ctrl",
|
||||
.fops = &auth_ctrl_fops,
|
||||
};
|
||||
|
||||
static __init int auth_ctrl_init_module(void)
|
||||
{
|
||||
int err;
|
||||
|
||||
err = misc_register(&auth_ctrl_device);
|
||||
if (err < 0) {
|
||||
pr_err("auth_ctrl register failed\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
pr_info("auth_ctrl init success\n");
|
||||
|
||||
BUG_ON(init_authority_control());
|
||||
|
||||
#ifdef CONFIG_QOS_CTRL
|
||||
init_qos_ctrl();
|
||||
#endif
|
||||
|
||||
init_sched_auth_debug_procfs();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void auth_ctrl_exit_module(void)
|
||||
{
|
||||
remove_authority_control();
|
||||
misc_deregister(&auth_ctrl_device);
|
||||
}
|
||||
|
||||
/* module entry points */
|
||||
module_init(auth_ctrl_init_module);
|
||||
module_exit(auth_ctrl_exit_module);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* drivers/auth_ctl/auth_ctrl.h
|
||||
*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __AUTH_CTRL_H
|
||||
#define __AUTH_CTRL_H
|
||||
|
||||
#include <linux/ioctl.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/refcount.h>
|
||||
|
||||
#include <linux/sched/qos_ctrl.h>
|
||||
|
||||
struct auth_struct {
|
||||
struct mutex mutex;
|
||||
refcount_t usage;
|
||||
unsigned int status;
|
||||
#ifdef CONFIG_RTG_AUTHORITY
|
||||
unsigned int rtg_auth_flag;
|
||||
#endif
|
||||
#ifdef CONFIG_QOS_AUTHORITY
|
||||
unsigned int qos_auth_flag;
|
||||
#endif
|
||||
#ifdef CONFIG_QOS_CTRL
|
||||
unsigned int num[NR_QOS];
|
||||
struct list_head tasks[NR_QOS];
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
* for debug fs
|
||||
*/
|
||||
struct idr *get_auth_ctrl_idr(void);
|
||||
struct mutex *get_auth_idr_mutex(void);
|
||||
|
||||
#ifdef CONFIG_AUTH_QOS_DEBUG
|
||||
int __init init_sched_auth_debug_procfs(void);
|
||||
#else
|
||||
static inline int init_sched_auth_debug_procfs(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __AUTH_CTRL_H */
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* drivers/auth_ctl/auth_qos_debug.c
|
||||
*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
*
|
||||
*/
|
||||
#include <linux/cred.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/proc_fs.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/sched/auth_ctrl.h>
|
||||
#include <linux/sched/rtg_auth.h>
|
||||
#include <linux/sched/qos_ctrl.h>
|
||||
#include <linux/sched/qos_auth.h>
|
||||
|
||||
#include "auth_ctrl.h"
|
||||
#include "qos_ctrl.h"
|
||||
|
||||
#define seq_printf_auth(m, x...) \
|
||||
do { \
|
||||
if (m) \
|
||||
seq_printf(m, x); \
|
||||
else \
|
||||
printk(x); \
|
||||
} while (0)
|
||||
|
||||
static void print_auth_id(struct seq_file *file,
|
||||
const int uid)
|
||||
{
|
||||
seq_printf_auth(file, "AUTH_ID :%d\n", uid);
|
||||
}
|
||||
|
||||
static void print_auth_info(struct seq_file *file,
|
||||
const struct auth_struct *auth)
|
||||
{
|
||||
seq_printf_auth(file, "AUTH_STATUS :%d\n", auth->status);
|
||||
#ifdef CONFIG_RTG_AUTHORITY
|
||||
seq_printf_auth(file, "RTG_FLAG :%04x\n", auth->rtg_auth_flag);
|
||||
#endif
|
||||
#ifdef CONFIG_QOS_AUTHORITY
|
||||
seq_printf_auth(file, "QOS_FLAG :%04x\n", auth->qos_auth_flag);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void print_qos_count(struct seq_file *file,
|
||||
const struct auth_struct *auth)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = QOS_POLICY_MIN_LEVEL; i < NR_QOS; ++i)
|
||||
seq_printf_auth(file, "QOS level %d thread nr :%d\n", i, auth->num[i]);
|
||||
}
|
||||
|
||||
static void print_qos_thread(struct seq_file *file,
|
||||
const struct auth_struct *auth)
|
||||
{
|
||||
struct qos_task_struct *tmp, *next;
|
||||
struct task_struct *p;
|
||||
int i;
|
||||
|
||||
for (i = QOS_POLICY_MIN_LEVEL; i < NR_QOS; ++i) {
|
||||
seq_printf_auth(file, "QOS level %d threads:", i);
|
||||
list_for_each_entry_safe(tmp, next, &auth->tasks[i], qos_list) {
|
||||
p = container_of(tmp, struct task_struct, qts);
|
||||
seq_printf_auth(file, "%d ", p->pid);
|
||||
}
|
||||
seq_printf_auth(file, "\n");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static inline void print_auth_struct(struct seq_file *file, struct auth_struct *auth)
|
||||
{
|
||||
print_auth_info(file, auth);
|
||||
seq_printf_auth(file, "\n");
|
||||
print_qos_count(file, auth);
|
||||
seq_printf_auth(file, "\n");
|
||||
#ifdef CONFIG_QOS_CTRL
|
||||
print_qos_thread(file, auth);
|
||||
#endif
|
||||
seq_printf_auth(file, "---------------------------------------------------------\n");
|
||||
|
||||
}
|
||||
|
||||
int authority_printf_handler(int id, void *p, void *para)
|
||||
{
|
||||
struct auth_struct *auth = (struct auth_struct *)p;
|
||||
struct seq_file *file = (struct seq_file *)para;
|
||||
|
||||
/*
|
||||
* data consistency is not that important here
|
||||
*/
|
||||
seq_printf_auth(file, "\n\n");
|
||||
print_auth_id(file, id);
|
||||
seq_printf_auth(file, "\n");
|
||||
|
||||
/* no need to add refcount here, auth must alive in ua_idr_mutex */
|
||||
print_auth_struct(file, auth);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sched_auth_debug_show(struct seq_file *file, void *param)
|
||||
{
|
||||
struct idr *ua_idr = get_auth_ctrl_idr();
|
||||
struct mutex *ua_idr_mutex = get_auth_idr_mutex();
|
||||
/*
|
||||
* NOTICE:
|
||||
* if mutex in authority_printf_handler, sleep may occur
|
||||
* change ths spin_lock to mutex, or remove mutex in handler
|
||||
*/
|
||||
|
||||
mutex_lock(ua_idr_mutex);
|
||||
/* will never return 0 here, auth in ua_idr must alive */
|
||||
idr_for_each(ua_idr, authority_printf_handler, file);
|
||||
mutex_unlock(ua_idr_mutex);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sched_auth_debug_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
seq_release(inode, file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sched_auth_debug_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
return single_open(filp, sched_auth_debug_show, NULL);
|
||||
}
|
||||
|
||||
static const struct proc_ops sched_auth_debug_fops = {
|
||||
.proc_open = sched_auth_debug_open,
|
||||
.proc_read = seq_read,
|
||||
.proc_lseek = seq_lseek,
|
||||
.proc_release = sched_auth_debug_release,
|
||||
};
|
||||
|
||||
int __init init_sched_auth_debug_procfs(void)
|
||||
{
|
||||
struct proc_dir_entry *pe = NULL;
|
||||
|
||||
pe = proc_create("sched_auth_qos_debug",
|
||||
0400, NULL, &sched_auth_debug_fops);
|
||||
if (unlikely(!pe))
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,662 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* drivers/auth_ctl/auth_ctrl.c
|
||||
*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
*
|
||||
*/
|
||||
#include <linux/sched.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/stop_machine.h>
|
||||
#include <linux/sched/auth_ctrl.h>
|
||||
#include <linux/sched/rtg_auth.h>
|
||||
#include <linux/sched/qos_ctrl.h>
|
||||
#include <linux/sched/qos_auth.h>
|
||||
#include <uapi/linux/sched/types.h>
|
||||
|
||||
#include "auth_ctrl.h"
|
||||
#include "qos_ctrl.h"
|
||||
|
||||
typedef long (*qos_ctrl_func)(struct file *file, void __user *uarg);
|
||||
|
||||
static long ctrl_qos_operation(struct file *file, void __user *uarg);
|
||||
static long ctrl_qos_policy(struct file *file, void __user *uarg);
|
||||
|
||||
static qos_ctrl_func g_func_array[QOS_CTRL_MAX_NR] = {
|
||||
NULL, /* reserved */
|
||||
ctrl_qos_operation,
|
||||
ctrl_qos_policy,
|
||||
};
|
||||
|
||||
static struct qos_policy_map qos_policy_array[QOS_POLICY_MAX_NR];
|
||||
|
||||
void remove_qos_tasks(struct auth_struct *auth)
|
||||
{
|
||||
int i;
|
||||
struct qos_task_struct *tmp, *next;
|
||||
struct task_struct *p;
|
||||
|
||||
mutex_lock(&auth->mutex);
|
||||
for (i = QOS_POLICY_MIN_LEVEL; i < NR_QOS; ++i) {
|
||||
list_for_each_entry_safe(tmp, next, &auth->tasks[i], qos_list) {
|
||||
p = container_of(tmp, struct task_struct, qts);
|
||||
if (!list_empty(&tmp->qos_list)) {
|
||||
list_del_init(&tmp->qos_list);
|
||||
tmp->in_qos = NO_QOS;
|
||||
put_task_struct(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
mutex_unlock(&auth->mutex);
|
||||
}
|
||||
|
||||
static void init_sched_attr(struct sched_attr *attr)
|
||||
{
|
||||
memset(attr, 0, sizeof(struct sched_attr));
|
||||
}
|
||||
|
||||
static inline bool is_system(unsigned int uid)
|
||||
{
|
||||
return uid == SYSTEM_UID;
|
||||
}
|
||||
|
||||
/* This function must be called when p is valid. That means the p's refcount must exist */
|
||||
static int sched_set_task_qos_attr(struct task_struct *p, int level, int status)
|
||||
{
|
||||
struct qos_policy_item *item;
|
||||
struct qos_policy_map *policy_map;
|
||||
struct sched_attr attr;
|
||||
|
||||
read_lock(&qos_policy_array[status].lock);
|
||||
if (!qos_policy_array[status].initialized) {
|
||||
pr_err("[QOS_CTRL] dirty qos policy, pid=%d, uid=%d, status=%d\n",
|
||||
p->pid, p->cred->uid.val, status);
|
||||
read_unlock(&qos_policy_array[status].lock);
|
||||
return -DIRTY_QOS_POLICY;
|
||||
}
|
||||
|
||||
policy_map = &qos_policy_array[status];
|
||||
item = &policy_map->levels[level];
|
||||
|
||||
init_sched_attr(&attr);
|
||||
attr.size = sizeof(struct sched_attr);
|
||||
attr.sched_policy = SCHED_NORMAL;
|
||||
|
||||
if (policy_map->policy_flag & QOS_FLAG_NICE)
|
||||
attr.sched_nice = item->nice;
|
||||
|
||||
if (policy_map->policy_flag & QOS_FLAG_LATENCY_NICE) {
|
||||
attr.sched_flags |= SCHED_FLAG_LATENCY_NICE;
|
||||
attr.sched_latency_nice = item->latency_nice;
|
||||
}
|
||||
|
||||
if ((policy_map->policy_flag & QOS_FLAG_RT) && item->rt_sched_priority) {
|
||||
attr.sched_policy = SCHED_FIFO;
|
||||
attr.sched_flags |= SCHED_FLAG_RESET_ON_FORK;
|
||||
attr.sched_priority = item->rt_sched_priority;
|
||||
}
|
||||
|
||||
read_unlock(&qos_policy_array[status].lock);
|
||||
|
||||
if (unlikely(p->flags & PF_EXITING)) {
|
||||
pr_info("[QOS_CTRL] dying task, no need to set qos\n");
|
||||
return -THREAD_EXITING;
|
||||
}
|
||||
|
||||
return sched_setattr_nocheck(p, &attr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Switch qos mode when status changed.
|
||||
* Lock auth before calling this function
|
||||
*/
|
||||
void qos_switch(struct auth_struct *auth, int target_status)
|
||||
{
|
||||
int i;
|
||||
int ret;
|
||||
struct task_struct *task;
|
||||
struct qos_task_struct *qts;
|
||||
|
||||
if (!auth) {
|
||||
pr_err("[QOS_CTRL] auth no exist, qos switch failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
lockdep_assert_held(&auth->mutex);
|
||||
|
||||
if (auth->status == target_status) {
|
||||
pr_info("[QOS_CTRL] same status, no need to switch qos\n");
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = QOS_POLICY_MIN_LEVEL; i < NR_QOS; ++i) {
|
||||
list_for_each_entry(qts, &auth->tasks[i], qos_list) {
|
||||
task = container_of(qts, struct task_struct, qts);
|
||||
ret = sched_set_task_qos_attr(task, i, target_status);
|
||||
if (ret)
|
||||
pr_err("[QOS_CTRL] set qos attr failed, qos switch failed\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int qos_insert_task(struct task_struct *p, struct list_head *head, unsigned int level)
|
||||
{
|
||||
struct qos_task_struct *qts = &p->qts;
|
||||
|
||||
if (qts->in_qos > NO_QOS) {
|
||||
pr_err("[QOS_CTRL] qos apply still active, no duplicate add\n");
|
||||
return -PID_DUPLICATE;
|
||||
}
|
||||
|
||||
if (likely(list_empty(&qts->qos_list))) {
|
||||
get_task_struct(p);
|
||||
list_add(&qts->qos_list, head);
|
||||
qts->in_qos = level;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qos_remove_task(struct task_struct *p)
|
||||
{
|
||||
struct qos_task_struct *qts = (struct qos_task_struct *) &p->qts;
|
||||
|
||||
if (qts->in_qos == NO_QOS) {
|
||||
pr_err("[QOS_CTRL] task not in qos, no need to remove\n");
|
||||
return -PID_NOT_EXIST;
|
||||
}
|
||||
|
||||
if (likely(!list_empty(&qts->qos_list))) {
|
||||
list_del_init(&qts->qos_list);
|
||||
qts->in_qos = NO_QOS;
|
||||
put_task_struct(p);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool same_uid(struct task_struct *dude, struct task_struct *bro)
|
||||
{
|
||||
return uid_eq(task_uid(dude), task_uid(bro));
|
||||
}
|
||||
|
||||
static inline bool super_user(struct task_struct *p)
|
||||
{
|
||||
return super_uid(task_uid(p).val);
|
||||
}
|
||||
|
||||
/*
|
||||
* judge permission for changing other tasks' qos
|
||||
*/
|
||||
static bool can_change_qos(struct task_struct *p)
|
||||
{
|
||||
if (p != current && !same_uid(current, p) && !super_user(current)) {
|
||||
pr_err("[QOS_CTRL] %d apply for others not permit\n", p->pid);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int qos_apply(struct qos_ctrl_data *data)
|
||||
{
|
||||
unsigned int level = data->level;
|
||||
struct auth_struct *auth;
|
||||
struct task_struct *p;
|
||||
struct qos_task_struct *qts;
|
||||
int pid = data->pid;
|
||||
int ret;
|
||||
|
||||
if (level >= NR_QOS || level == NO_QOS) {
|
||||
pr_err("[QOS_CTRL] no this qos level, qos apply failed\n");
|
||||
ret = -ARG_INVALID;
|
||||
goto out;
|
||||
}
|
||||
|
||||
p = find_get_task_by_vpid((pid_t)pid);
|
||||
if (unlikely(!p)) {
|
||||
pr_err("[QOS_CTRL] no matching task for this pid, qos apply failed\n");
|
||||
ret = -ESRCH;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (unlikely(p->flags & PF_EXITING)) {
|
||||
pr_info("[QOS_CTRL] dying task, no need to set qos\n");
|
||||
ret = -THREAD_EXITING;
|
||||
goto out_put_task;
|
||||
}
|
||||
|
||||
if (!can_change_qos(p)) {
|
||||
pr_err("[QOS_CTRL] apply for others not permit\n");
|
||||
ret = -ARG_INVALID;
|
||||
goto out_put_task;
|
||||
}
|
||||
|
||||
auth = get_authority(p);
|
||||
if (!auth) {
|
||||
pr_err("[QOS_CTRL] no auth data for pid=%d(%s) this uid=%d, qos apply failed\n",
|
||||
p->pid, p->comm, p->cred->uid.val);
|
||||
put_task_struct(p);
|
||||
ret = -UID_NOT_FOUND;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mutex_lock(&auth->mutex);
|
||||
if (auth->status == AUTH_STATUS_DEAD) {
|
||||
pr_err("[QOS_CTRL] this auth data has been deleted\n");
|
||||
ret = -INVALID_AUTH;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
if (auth->num[level] >= QOS_NUM_MAX) {
|
||||
pr_err("[QOS_CTRL] qos num exceeds limit, cached only\n");
|
||||
ret = -QOS_THREAD_NUM_EXCEED_LIMIT;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
qts = (struct qos_task_struct *) &p->qts;
|
||||
|
||||
/* effective qos must in range [NO_QOS, NR_QOS) */
|
||||
if (qts->in_qos != NO_QOS) {
|
||||
if (qts->in_qos == level) {
|
||||
ret = 0;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
--auth->num[qts->in_qos];
|
||||
qos_remove_task(p);
|
||||
}
|
||||
|
||||
ret = qos_insert_task(p, &auth->tasks[level], level);
|
||||
if (ret < 0) {
|
||||
pr_err("[QOS_CTRL] insert task to qos list %d failed\n", level);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
++auth->num[level];
|
||||
|
||||
ret = sched_set_task_qos_attr(p, level, auth->status);
|
||||
if (ret) {
|
||||
pr_err("[QOS_CTRL] set qos_level %d for thread %d on status %d failed\n",
|
||||
level, p->pid, auth->status);
|
||||
--auth->num[level];
|
||||
qos_remove_task(p);
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&auth->mutex);
|
||||
put_auth_struct(auth);
|
||||
out_put_task:
|
||||
put_task_struct(p);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
int qos_leave(struct qos_ctrl_data *data)
|
||||
{
|
||||
unsigned int level;
|
||||
struct auth_struct *auth;
|
||||
struct task_struct *p;
|
||||
struct qos_task_struct *qts;
|
||||
int pid = data->pid;
|
||||
int ret;
|
||||
|
||||
p = find_get_task_by_vpid((pid_t)pid);
|
||||
if (!p) {
|
||||
pr_err("[QOS_CTRL] no matching task for this pid, qos apply failed\n");
|
||||
ret = -ESRCH;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (unlikely(p->flags & PF_EXITING)) {
|
||||
pr_info("[QOS_CTRL] dying task, no need to set qos\n");
|
||||
ret = -THREAD_EXITING;
|
||||
goto out_put_task;
|
||||
}
|
||||
|
||||
if (!can_change_qos(p)) {
|
||||
pr_err("[QOS_CTRL] apply for others not permit\n");
|
||||
ret = -ARG_INVALID;
|
||||
goto out_put_task;
|
||||
}
|
||||
|
||||
auth = get_authority(p);
|
||||
if (!auth) {
|
||||
pr_err("[QOS_CTRL] no auth data for pid=%d(%s) this uid=%d, qos stop failed\n",
|
||||
p->pid, p->comm, p->cred->uid.val);
|
||||
put_task_struct(p);
|
||||
ret = -UID_NOT_FOUND;
|
||||
goto out;
|
||||
}
|
||||
|
||||
mutex_lock(&auth->mutex);
|
||||
|
||||
qts = (struct qos_task_struct *) &p->qts;
|
||||
|
||||
level = qts->in_qos;
|
||||
|
||||
if (level == NO_QOS) {
|
||||
pr_err("[QOS_CTRL] task not in qos list, qos stop failed\n");
|
||||
ret = -ARG_INVALID;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
if (auth->status == AUTH_STATUS_DEAD) {
|
||||
pr_err("[QOS_CTRL] this auth data has been deleted\n");
|
||||
ret = -INVALID_AUTH;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
ret = qos_remove_task(p);
|
||||
if (ret < 0) {
|
||||
pr_err("[QOS_CTRL] remove task from qos list %d failed\n", level);
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
--auth->num[level];
|
||||
|
||||
/*
|
||||
* NO NEED to judge whether current status is AUTH_STATUS_DISABLE.
|
||||
* In the auth destoring context, the removing of thread's sched attr was protected by
|
||||
* auth->mutex, AUTH_STATUS_DISABLED will never appear here.
|
||||
*
|
||||
* The second param 3 means nothing, actually you can use any valid level here, cause the
|
||||
* policy matching AUTH_STATUS_DISABLED has default parameters for all qos level, which can
|
||||
* keep a powerful thread to behave like a ordinary thread.
|
||||
*/
|
||||
ret = sched_set_task_qos_attr(p, 3, AUTH_STATUS_DISABLED);
|
||||
if (ret)
|
||||
pr_err("[QOS_CTRL] set qos_level %d for thread %d on status %d to default failed\n",
|
||||
level, p->pid, auth->status);
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&auth->mutex);
|
||||
put_auth_struct(auth);
|
||||
out_put_task:
|
||||
put_task_struct(p);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void init_task_qos(struct task_struct *p)
|
||||
{
|
||||
struct qos_task_struct *qts = (struct qos_task_struct *) &p->qts;
|
||||
|
||||
INIT_LIST_HEAD(&qts->qos_list);
|
||||
qts->in_qos = NO_QOS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove statistic info in auth when task exit
|
||||
*/
|
||||
void sched_exit_qos_list(struct task_struct *p)
|
||||
{
|
||||
struct auth_struct *auth;
|
||||
struct qos_task_struct *qts = (struct qos_task_struct *) &p->qts;
|
||||
|
||||
/*
|
||||
* For common tasks(the vast majority):
|
||||
* skip get authority, fast return here.
|
||||
*
|
||||
* For qos tasks:
|
||||
* If contend with auth_delete() happens,
|
||||
* 1. function return here, auth_delete() will do the clean up
|
||||
* 2. function go on, either no auth return, either do clean up here
|
||||
* Both cases guarantee data synchronization
|
||||
*/
|
||||
if (likely(qts->in_qos == NO_QOS))
|
||||
return;
|
||||
|
||||
auth = get_authority(p);
|
||||
if (!auth)
|
||||
goto out;
|
||||
|
||||
mutex_lock(&auth->mutex);
|
||||
if (qts->in_qos == NO_QOS) {
|
||||
mutex_unlock(&auth->mutex);
|
||||
goto out_put_auth;
|
||||
}
|
||||
--auth->num[qts->in_qos];
|
||||
list_del_init(&qts->qos_list);
|
||||
qts->in_qos = NO_QOS;
|
||||
mutex_unlock(&auth->mutex);
|
||||
|
||||
out_put_auth:
|
||||
put_auth_struct(auth);
|
||||
out:
|
||||
return;
|
||||
}
|
||||
|
||||
typedef int (*qos_manipulate_func)(struct qos_ctrl_data *data);
|
||||
|
||||
static qos_manipulate_func qos_func_array[QOS_OPERATION_CMD_MAX_NR] = {
|
||||
NULL,
|
||||
qos_apply, //1
|
||||
qos_leave,
|
||||
};
|
||||
|
||||
static long do_qos_manipulate(struct qos_ctrl_data *data)
|
||||
{
|
||||
long ret = 0;
|
||||
unsigned int type = data->type;
|
||||
|
||||
if (type <= 0 || type >= QOS_OPERATION_CMD_MAX_NR) {
|
||||
pr_err("[QOS_CTRL] CMD_ID_QOS_MANIPULATE type not valid\n");
|
||||
return -ARG_INVALID;
|
||||
}
|
||||
|
||||
if (qos_func_array[type])
|
||||
ret = (long)(*qos_func_array[type])(data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long ctrl_qos_operation(struct file *file, void __user *uarg)
|
||||
{
|
||||
struct qos_ctrl_data qos_data;
|
||||
|
||||
if (copy_from_user(&qos_data, uarg, sizeof(struct qos_ctrl_data))) {
|
||||
pr_err("[QOS_CTRL] CMD_ID_QOS_APPLY copy data failed\n");
|
||||
return -ARG_INVALID;
|
||||
}
|
||||
|
||||
/* transfer user space qos level to kernel space qos level */
|
||||
qos_data.level -= QOS_LEVEL_INTERVAL;
|
||||
|
||||
return do_qos_manipulate(&qos_data);
|
||||
}
|
||||
|
||||
#define MAX_LATENCY_NICE 19
|
||||
#define MIN_LATENCY_NICE -20
|
||||
|
||||
static inline bool valid_nice(int nice)
|
||||
{
|
||||
return nice >= MIN_NICE && nice <= MAX_NICE;
|
||||
}
|
||||
|
||||
static inline bool valid_latency_nice(int latency_nice)
|
||||
{
|
||||
return latency_nice >= MIN_LATENCY_NICE && latency_nice <= MAX_LATENCY_NICE;
|
||||
}
|
||||
|
||||
static inline bool valid_uclamp(int uclamp_min, int uclamp_max)
|
||||
{
|
||||
if (uclamp_min > uclamp_max)
|
||||
return false;
|
||||
if (uclamp_max > SCHED_CAPACITY_SCALE)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool valid_rt(int sched_priority)
|
||||
{
|
||||
if (sched_priority > MAX_USER_RT_PRIO - 1 || sched_priority < 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool valid_qos_flag(unsigned int qos_flag)
|
||||
{
|
||||
if (qos_flag & ~QOS_FLAG_ALL)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool valid_qos_item(struct qos_policy_datas *datas)
|
||||
{
|
||||
int i;
|
||||
int type = datas->policy_type;
|
||||
struct qos_policy_data *data;
|
||||
|
||||
if (type <= 0 || type >= QOS_POLICY_MAX_NR) {
|
||||
pr_err("[QOS_CTRL] not valid qos policy type, policy change failed\n");
|
||||
goto out_failed;
|
||||
}
|
||||
|
||||
if (!valid_qos_flag(datas->policy_flag)) {
|
||||
pr_err("[QOS_CTRL] not valid qos flag, policy change failed\n");
|
||||
goto out_failed;
|
||||
}
|
||||
|
||||
/* check user space qos polcicy data, level 0 reserved */
|
||||
for (i = 0; i <= NR_QOS; ++i) {
|
||||
data = &datas->policys[i];
|
||||
|
||||
if (!valid_nice(data->nice)) {
|
||||
pr_err("[QOS_CTRL] invalid nice, policy change failed\n");
|
||||
goto out_failed;
|
||||
}
|
||||
|
||||
if (!valid_latency_nice(data->latency_nice)) {
|
||||
pr_err("[QOS_CTRL] invalid latency_nice, policy change failed\n");
|
||||
goto out_failed;
|
||||
}
|
||||
|
||||
if (!valid_uclamp(data->uclamp_min, data->uclamp_max)) {
|
||||
pr_err("[QOS_CTRL] invalid uclamp, policy change failed\n");
|
||||
goto out_failed;
|
||||
}
|
||||
|
||||
if (!valid_rt(data->rt_sched_priority)) {
|
||||
pr_err("[QOS_CTRL] invalid rt, policy change failed\n");
|
||||
goto out_failed;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
out_failed:
|
||||
pr_err("[QOS_CTRL] not valid qos policy params\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
static long do_qos_policy_change(struct qos_policy_datas *datas)
|
||||
{
|
||||
long ret = 0;
|
||||
int i;
|
||||
struct qos_policy_item *item;
|
||||
struct qos_policy_data *data;
|
||||
int type = datas->policy_type;
|
||||
|
||||
if (type >= QOS_POLICY_MAX_NR) {
|
||||
pr_err("[QOS_CTRL] not valid policy type\n");
|
||||
goto out_failed;
|
||||
}
|
||||
|
||||
if (!valid_qos_item(datas))
|
||||
goto out_failed;
|
||||
|
||||
write_lock(&qos_policy_array[type].lock);
|
||||
for (i = QOS_POLICY_MIN_LEVEL; i < NR_QOS; ++i) {
|
||||
item = &qos_policy_array[type].levels[i];
|
||||
|
||||
/* user space policy params */
|
||||
data = &datas->policys[i + QOS_LEVEL_INTERVAL];
|
||||
|
||||
item->nice = data->nice;
|
||||
item->latency_nice = data->latency_nice;
|
||||
item->uclamp_min = data->uclamp_min;
|
||||
item->uclamp_max = data->uclamp_max;
|
||||
/* only specific qos level could use SCHED_FIFO */
|
||||
item->rt_sched_priority = (i < MIN_RT_QOS_LEVEL) ? 0 :
|
||||
data->rt_sched_priority;
|
||||
}
|
||||
qos_policy_array[type].policy_flag = datas->policy_flag;
|
||||
qos_policy_array[type].initialized = true;
|
||||
write_unlock(&qos_policy_array[type].lock);
|
||||
|
||||
return ret;
|
||||
|
||||
out_failed:
|
||||
return -ARG_INVALID;
|
||||
}
|
||||
|
||||
static long ctrl_qos_policy(struct file *file, void __user *uarg)
|
||||
{
|
||||
struct qos_policy_datas policy_datas;
|
||||
|
||||
if (copy_from_user(&policy_datas, uarg, sizeof(struct qos_policy_datas))) {
|
||||
pr_err("[QOS_CTRL] CMD_ID_QOS_APPLY copy data failed\n");
|
||||
return -ARG_INVALID;
|
||||
}
|
||||
|
||||
return do_qos_policy_change(&policy_datas);
|
||||
}
|
||||
|
||||
long do_qos_ctrl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
void __user *uarg = (void __user *)arg;
|
||||
unsigned int func_cmd = _IOC_NR(cmd);
|
||||
|
||||
if (uarg == NULL) {
|
||||
pr_err("%s: invalid user uarg\n", __func__);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (_IOC_TYPE(cmd) != QOS_CTRL_IPC_MAGIG) {
|
||||
pr_err("%s: qos ctrl magic fail, TYPE=%d\n",
|
||||
__func__, _IOC_TYPE(cmd));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (func_cmd >= QOS_CTRL_MAX_NR) {
|
||||
pr_err("%s: qos ctrl cmd error, cmd:%d\n",
|
||||
__func__, _IOC_TYPE(cmd));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_QOS_AUTHORITY
|
||||
if (!check_authorized(func_cmd, QOS_AUTH_FLAG)) {
|
||||
pr_err("[QOS_CTRL] %s: uid not authorized\n", __func__);
|
||||
return -UID_NOT_AUTHORIZED;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (g_func_array[func_cmd])
|
||||
return (*g_func_array[func_cmd])(file, uarg);
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static void init_qos_policy_array(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* index 0 reserved */
|
||||
for (i = 1; i < QOS_POLICY_MAX_NR; ++i)
|
||||
rwlock_init(&qos_policy_array[i].lock);
|
||||
|
||||
pr_info("[QOS_CTRL] lock in qos policy initialized\n");
|
||||
}
|
||||
|
||||
int __init init_qos_ctrl(void)
|
||||
{
|
||||
init_qos_policy_array();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* drivers/auth_ctl/qos_ctrl.h
|
||||
*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __QOS_CTRL_H
|
||||
#define __QOS_CTRL_H
|
||||
|
||||
#include "../../kernel/sched/sched.h"
|
||||
|
||||
#include <linux/sched/qos_ctrl.h>
|
||||
|
||||
/* qos level interval between user space and kernel space */
|
||||
#define QOS_LEVEL_INTERVAL 1
|
||||
|
||||
/* min qos level used in kernel space, begin index for LOOP */
|
||||
#define QOS_POLICY_MIN_LEVEL (NO_QOS + QOS_LEVEL_INTERVAL)
|
||||
|
||||
struct qos_policy_item {
|
||||
int nice;
|
||||
int latency_nice;
|
||||
int uclamp_min;
|
||||
int uclamp_max;
|
||||
int rt_sched_priority;
|
||||
};
|
||||
|
||||
struct qos_policy_map {
|
||||
rwlock_t lock;
|
||||
bool initialized;
|
||||
unsigned int policy_flag;
|
||||
struct qos_policy_item levels[NR_QOS];
|
||||
};
|
||||
|
||||
int __init init_qos_ctrl(void);
|
||||
|
||||
#endif /* __OQS_CTRL_H */
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 110 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 229 KiB |
@@ -0,0 +1,116 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* include/linux/sched/auth_ctrl.h
|
||||
*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef _AUTH_CTRL_H
|
||||
#define _AUTH_CTRL_H
|
||||
|
||||
#include <linux/fs.h>
|
||||
|
||||
#define ROOT_UID 0
|
||||
#define SYSTEM_UID 1000
|
||||
|
||||
#define SUPER_UID SYSTEM_UID
|
||||
#define super_uid(uid) (uid == ROOT_UID || uid == SYSTEM_UID)
|
||||
|
||||
enum auth_ctrl_cmdid {
|
||||
BASIC_AUTH_CTRL = 1,
|
||||
AUTH_CTRL_MAX_NR
|
||||
};
|
||||
|
||||
#define AUTH_CTRL_IPC_MAGIG 0xCD
|
||||
|
||||
#define BASIC_AUTH_CTRL_OPERATION \
|
||||
_IOWR(AUTH_CTRL_IPC_MAGIG, BASIC_AUTH_CTRL, struct auth_ctrl_data)
|
||||
|
||||
enum auth_flag_type {
|
||||
#ifdef CONFIG_RTG_AUTHORITY
|
||||
RTG_AUTH_FLAG,
|
||||
#endif
|
||||
#ifdef CONFIG_QOS_AUTHORITY
|
||||
QOS_AUTH_FLAG,
|
||||
#endif
|
||||
};
|
||||
|
||||
#define INVALIED_AUTH_FLAG 0x00000000
|
||||
|
||||
struct auth_ctrl_data {
|
||||
unsigned int uid;
|
||||
|
||||
/*
|
||||
* type: operation type, see auth_manipulate_type, valid range [1, AUTH_MAX_NR)
|
||||
*
|
||||
* rtg_ua_flag: authority flag for RTG, see AF_RTG_ALL
|
||||
*
|
||||
* qos_ua_flag: authority flag for QOS, see AF_QOS_ALL
|
||||
*
|
||||
* status: current status for uid, use to match qos policy, see auth_status and
|
||||
* qos_policy_type, valid range [1, AUTH_STATUS_MAX_NR - 1)
|
||||
*
|
||||
*/
|
||||
unsigned int type;
|
||||
unsigned int rtg_ua_flag;
|
||||
unsigned int qos_ua_flag;
|
||||
unsigned int status;
|
||||
};
|
||||
|
||||
enum auth_err_no {
|
||||
ARG_INVALID = 1,
|
||||
THREAD_EXITING,
|
||||
DIRTY_QOS_POLICY,
|
||||
UID_NOT_AUTHORIZED,
|
||||
UID_NOT_FOUND,
|
||||
PID_DUPLICATE,
|
||||
PID_NOT_EXIST,
|
||||
INVALID_AUTH,
|
||||
QOS_THREAD_NUM_EXCEED_LIMIT,
|
||||
};
|
||||
|
||||
enum auth_manipulate_type {
|
||||
AUTH_ENABLE = 1,
|
||||
AUTH_DELETE,
|
||||
AUTH_GET,
|
||||
AUTH_SWITCH,
|
||||
AUTH_MAX_NR,
|
||||
};
|
||||
|
||||
#ifndef CONFIG_QOS_POLICY_MAX_NR
|
||||
#define QOS_STATUS_COUNT 5
|
||||
#else
|
||||
#define QOS_STATUS_COUNT CONFIG_QOS_POLICY_MAX_NR
|
||||
#endif
|
||||
|
||||
/* keep match with qos_policy_type */
|
||||
enum auth_status {
|
||||
/* reserved fo QOS_POLICY_DEFAULT, no qos supply in this status */
|
||||
AUTH_STATUS_DISABLED = 1,
|
||||
|
||||
/* reserved for ROOT and SYSTEM */
|
||||
AUTH_STATUS_SYSTEM_SERVER = 2,
|
||||
|
||||
/*
|
||||
* these space for user specific status
|
||||
* range (AUTH_STATUS_SYSTEM_SERVER, AUTH_STATUS_DEAD)
|
||||
*
|
||||
* initial the policy in matching index of qos_policy_array first before use
|
||||
* see ctrl_qos_policy
|
||||
*/
|
||||
|
||||
/* reserved for destorying auth_struct*/
|
||||
AUTH_STATUS_DEAD = QOS_STATUS_COUNT,
|
||||
|
||||
AUTH_STATUS_MAX_NR = QOS_STATUS_COUNT + 1,
|
||||
};
|
||||
|
||||
struct auth_struct;
|
||||
long auth_ctrl_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
|
||||
void get_auth_struct(struct auth_struct *auth);
|
||||
void put_auth_struct(struct auth_struct *auth);
|
||||
struct auth_struct *get_authority(struct task_struct *p);
|
||||
bool check_authorized(unsigned int func_id, unsigned int type);
|
||||
|
||||
#endif /* _AUTH_CTRL_H */
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* include/linux/sched/qos_auth.h
|
||||
*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef _QOS_AUTH_H
|
||||
#define _QOS_AUTH_H
|
||||
|
||||
#include <linux/sched.h>
|
||||
#include <linux/sched/auth_ctrl.h>
|
||||
|
||||
/*
|
||||
* QOS authority flags for SYSTEM or ROOT
|
||||
*
|
||||
* keep sync with qos_ctrl_cmdid
|
||||
* when add a new cmd to qos_ctrl_cmdid
|
||||
* keep new_flag = (old_flag << 1) + 1
|
||||
* up to now, next flag value is 0x0007
|
||||
*/
|
||||
#define AF_QOS_ALL 0x0003
|
||||
|
||||
/*
|
||||
* delegated authority for normal uid
|
||||
* trim access range for QOS
|
||||
*/
|
||||
#define AF_QOS_DELEGATED 0x0001
|
||||
|
||||
bool check_authorized(unsigned int func_id, unsigned int type);
|
||||
|
||||
#endif /* _QOS_AUTH_H */
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* include/linux/sched/qos_ctrl.h
|
||||
*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef _QOS_CTRL_H
|
||||
#define _QOS_CTRL_H
|
||||
|
||||
#include <linux/sched.h>
|
||||
#include <linux/fs.h>
|
||||
|
||||
enum qos_ctrl_cmdid {
|
||||
QOS_CTRL = 1,
|
||||
QOS_POLICY,
|
||||
QOS_CTRL_MAX_NR
|
||||
};
|
||||
|
||||
#define QOS_CTRL_IPC_MAGIG 0xCC
|
||||
|
||||
#define QOS_CTRL_BASIC_OPERATION \
|
||||
_IOWR(QOS_CTRL_IPC_MAGIG, QOS_CTRL, struct qos_ctrl_data)
|
||||
#define QOS_CTRL_POLICY_OPERATION \
|
||||
_IOWR(QOS_CTRL_IPC_MAGIG, QOS_POLICY, struct qos_policy_datas)
|
||||
|
||||
#define NO_QOS -1
|
||||
#define NR_QOS 6
|
||||
#define NR_RT_QOS 1
|
||||
#define MIN_RT_QOS_LEVEL (NR_QOS - NR_RT_QOS)
|
||||
|
||||
#define QOS_NUM_MAX 2000
|
||||
|
||||
enum qos_manipulate_type {
|
||||
QOS_APPLY = 1,
|
||||
QOS_LEAVE,
|
||||
QOS_OPERATION_CMD_MAX_NR,
|
||||
};
|
||||
|
||||
#ifndef CONFIG_QOS_POLICY_MAX_NR
|
||||
#define QOS_POLICYS_COUNT 5
|
||||
#else
|
||||
#define QOS_POLICYS_COUNT CONFIG_QOS_POLICY_MAX_NR
|
||||
#endif
|
||||
|
||||
/*
|
||||
* keep match with auth_status
|
||||
*
|
||||
* range (QOS_POLICY_SYSTEM, QOS_POLICY_MAX_NR) could defined by user
|
||||
* use ctrl_qos_policy
|
||||
*/
|
||||
enum qos_policy_type {
|
||||
QOS_POLICY_DEFAULT = 1, /* reserved for "NO QOS" */
|
||||
QOS_POLICY_SYSTEM = 2, /* reserved for ROOT and SYSTEM */
|
||||
QOS_POLICY_MAX_NR = QOS_POLICYS_COUNT,
|
||||
};
|
||||
|
||||
struct qos_ctrl_data {
|
||||
int pid;
|
||||
|
||||
/*
|
||||
* type: operation type, see qos_manipulate_type
|
||||
* level: valid from 1 to NR_QOS. Larger value, more aggressive supply
|
||||
*/
|
||||
unsigned int type;
|
||||
|
||||
/*
|
||||
* user space level, range from [1, NR_QOS]
|
||||
*
|
||||
* NOTICE!!!:
|
||||
* minus 1 before use in kernel, so the kernel range is [0, NR_QOS)
|
||||
*/
|
||||
unsigned int level;
|
||||
};
|
||||
|
||||
struct qos_policy_data {
|
||||
int nice;
|
||||
int latency_nice;
|
||||
int uclamp_min;
|
||||
int uclamp_max;
|
||||
int rt_sched_priority;
|
||||
};
|
||||
|
||||
#define QOS_FLAG_NICE 0x01
|
||||
#define QOS_FLAG_LATENCY_NICE 0x02
|
||||
#define QOS_FLAG_UCLAMP 0x04
|
||||
#define QOS_FLAG_RT 0x08
|
||||
|
||||
#define QOS_FLAG_ALL (QOS_FLAG_NICE | \
|
||||
QOS_FLAG_LATENCY_NICE | \
|
||||
QOS_FLAG_UCLAMP | \
|
||||
QOS_FLAG_RT)
|
||||
|
||||
struct qos_policy_datas {
|
||||
/*
|
||||
* policy_type: id for qos policy, valid from [1, QOS_POLICY_MAX_NR)
|
||||
* policy_flag: control valid sched attr for policy, QOS_FLAG_ALL for whole access
|
||||
* policys: sched params for specific level qos, minus 1 for matching struct in kerenl
|
||||
*/
|
||||
int policy_type;
|
||||
unsigned int policy_flag;
|
||||
struct qos_policy_data policys[NR_QOS + 1];
|
||||
};
|
||||
|
||||
struct auth_struct;
|
||||
|
||||
int qos_apply(struct qos_ctrl_data *data);
|
||||
int qos_leave(struct qos_ctrl_data *data);
|
||||
|
||||
void qos_switch(struct auth_struct *auth, int target_status);
|
||||
|
||||
void init_task_qos(struct task_struct *p);
|
||||
void sched_exit_qos_list(struct task_struct *p);
|
||||
void remove_qos_tasks(struct auth_struct *auth);
|
||||
|
||||
long do_qos_ctrl_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
|
||||
|
||||
#endif /* _QOS_CTRL_H */
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* include/linux/sched/rtg_auth.h
|
||||
*
|
||||
* Copyright (c) 2022 Huawei Device Co., Ltd.
|
||||
*/
|
||||
|
||||
#ifndef _RTG_AUTH_H
|
||||
#define _RTG_AUTH_H
|
||||
|
||||
#include <linux/sched.h>
|
||||
#include <linux/sched/auth_ctrl.h>
|
||||
|
||||
/*
|
||||
* RTG authority flags for SYSTEM or ROOT
|
||||
*
|
||||
* keep sync with rtg_sched_cmdid
|
||||
* when add a new cmd to rtg_sched_cmdid
|
||||
* keep new_flag = (old_flag << 1) + 1
|
||||
* up to now, next flag value is 0x3fff
|
||||
*/
|
||||
#define AF_RTG_ALL 0x1fff
|
||||
|
||||
/*
|
||||
* delegated authority for normal uid
|
||||
* trim access range for RTG
|
||||
*/
|
||||
#define AF_RTG_DELEGATED 0x1fff
|
||||
|
||||
bool check_authorized(unsigned int func_id, unsigned int type);
|
||||
|
||||
#endif /* _RTG_AUTH_H */
|
||||
|
||||
Reference in New Issue
Block a user