Merge tag 'pm-6.19-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm
Pull power management fixes from Rafael Wysocki:
"These fix an error path memory leak in the energy model management
code, fix a kerneldoc comment in it, and fix and revamp the energy
model YNL specification added recently along with the new energy model
management netlink interface (that received feedback after being
added):
- Fix a memory leak in em_create_pd() error path (Malaya Kumar Rout)
- Fix stale description of the cost field in struct em_perf_state to
reflect the current code (Yaxiong Tian)
- Fix and revamp the energy model YNL specification added recently
along with the energy model netlink interface (Changwoo Min)"
* tag 'pm-6.19-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm:
PM: EM: Add dump to get-perf-domains in the EM YNL spec
PM: EM: Change cpus' type from string to u64 array in the EM YNL spec
PM: EM: Rename em.yaml to dev-energymodel.yaml
PM: EM: Fix yamllint warnings in the EM YNL spec
PM: EM: Fix memory leak in em_create_pd() error path
PM: EM: Fix incorrect description of the cost field in struct em_perf_state
This commit is contained in:
175
Documentation/netlink/specs/dev-energymodel.yaml
Normal file
175
Documentation/netlink/specs/dev-energymodel.yaml
Normal file
@@ -0,0 +1,175 @@
|
||||
# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
|
||||
#
|
||||
# Copyright (c) 2025 Valve Corporation.
|
||||
#
|
||||
---
|
||||
name: dev-energymodel
|
||||
|
||||
doc: |
|
||||
Energy model netlink interface to notify its changes.
|
||||
|
||||
protocol: genetlink
|
||||
|
||||
uapi-header: linux/dev_energymodel.h
|
||||
|
||||
definitions:
|
||||
-
|
||||
type: flags
|
||||
name: perf-state-flags
|
||||
entries:
|
||||
-
|
||||
name: perf-state-inefficient
|
||||
doc: >-
|
||||
The performance state is inefficient. There is in this perf-domain,
|
||||
another performance state with a higher frequency but a lower or
|
||||
equal power cost.
|
||||
-
|
||||
type: flags
|
||||
name: perf-domain-flags
|
||||
entries:
|
||||
-
|
||||
name: perf-domain-microwatts
|
||||
doc: >-
|
||||
The power values are in micro-Watts or some other scale.
|
||||
-
|
||||
name: perf-domain-skip-inefficiencies
|
||||
doc: >-
|
||||
Skip inefficient states when estimating energy consumption.
|
||||
-
|
||||
name: perf-domain-artificial
|
||||
doc: >-
|
||||
The power values are artificial and might be created by platform
|
||||
missing real power information.
|
||||
|
||||
attribute-sets:
|
||||
-
|
||||
name: perf-domain
|
||||
doc: >-
|
||||
Information on a single performance domains.
|
||||
attributes:
|
||||
-
|
||||
name: pad
|
||||
type: pad
|
||||
-
|
||||
name: perf-domain-id
|
||||
type: u32
|
||||
doc: >-
|
||||
A unique ID number for each performance domain.
|
||||
-
|
||||
name: flags
|
||||
type: u64
|
||||
doc: >-
|
||||
Bitmask of performance domain flags.
|
||||
enum: perf-domain-flags
|
||||
-
|
||||
name: cpus
|
||||
type: u64
|
||||
multi-attr: true
|
||||
doc: >-
|
||||
CPUs that belong to this performance domain.
|
||||
-
|
||||
name: perf-table
|
||||
doc: >-
|
||||
Performance states table.
|
||||
attributes:
|
||||
-
|
||||
name: perf-domain-id
|
||||
type: u32
|
||||
doc: >-
|
||||
A unique ID number for each performance domain.
|
||||
-
|
||||
name: perf-state
|
||||
type: nest
|
||||
nested-attributes: perf-state
|
||||
multi-attr: true
|
||||
-
|
||||
name: perf-state
|
||||
doc: >-
|
||||
Performance state of a performance domain.
|
||||
attributes:
|
||||
-
|
||||
name: pad
|
||||
type: pad
|
||||
-
|
||||
name: performance
|
||||
type: u64
|
||||
doc: >-
|
||||
CPU performance (capacity) at a given frequency.
|
||||
-
|
||||
name: frequency
|
||||
type: u64
|
||||
doc: >-
|
||||
The frequency in KHz, for consistency with CPUFreq.
|
||||
-
|
||||
name: power
|
||||
type: u64
|
||||
doc: >-
|
||||
The power consumed at this level (by 1 CPU or by a registered
|
||||
device). It can be a total power: static and dynamic.
|
||||
-
|
||||
name: cost
|
||||
type: u64
|
||||
doc: >-
|
||||
The cost coefficient associated with this level, used during energy
|
||||
calculation. Equal to: power * max_frequency / frequency.
|
||||
-
|
||||
name: flags
|
||||
type: u64
|
||||
doc: >-
|
||||
Bitmask of performance state flags.
|
||||
enum: perf-state-flags
|
||||
|
||||
operations:
|
||||
list:
|
||||
-
|
||||
name: get-perf-domains
|
||||
attribute-set: perf-domain
|
||||
doc: Get the list of information for all performance domains.
|
||||
do:
|
||||
request:
|
||||
attributes:
|
||||
- perf-domain-id
|
||||
reply:
|
||||
attributes: &perf-domain-attrs
|
||||
- pad
|
||||
- perf-domain-id
|
||||
- flags
|
||||
- cpus
|
||||
dump:
|
||||
reply:
|
||||
attributes: *perf-domain-attrs
|
||||
-
|
||||
name: get-perf-table
|
||||
attribute-set: perf-table
|
||||
doc: Get the energy model table of a performance domain.
|
||||
do:
|
||||
request:
|
||||
attributes:
|
||||
- perf-domain-id
|
||||
reply:
|
||||
attributes:
|
||||
- perf-domain-id
|
||||
- perf-state
|
||||
-
|
||||
name: perf-domain-created
|
||||
doc: A performance domain is created.
|
||||
notify: get-perf-table
|
||||
mcgrp: event
|
||||
-
|
||||
name: perf-domain-updated
|
||||
doc: A performance domain is updated.
|
||||
notify: get-perf-table
|
||||
mcgrp: event
|
||||
-
|
||||
name: perf-domain-deleted
|
||||
doc: A performance domain is deleted.
|
||||
attribute-set: perf-table
|
||||
event:
|
||||
attributes:
|
||||
- perf-domain-id
|
||||
mcgrp: event
|
||||
|
||||
mcast-groups:
|
||||
list:
|
||||
-
|
||||
name: event
|
||||
@@ -1,113 +0,0 @@
|
||||
# SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
|
||||
|
||||
name: em
|
||||
|
||||
doc: |
|
||||
Energy model netlink interface to notify its changes.
|
||||
|
||||
protocol: genetlink
|
||||
|
||||
uapi-header: linux/energy_model.h
|
||||
|
||||
attribute-sets:
|
||||
-
|
||||
name: pds
|
||||
attributes:
|
||||
-
|
||||
name: pd
|
||||
type: nest
|
||||
nested-attributes: pd
|
||||
multi-attr: true
|
||||
-
|
||||
name: pd
|
||||
attributes:
|
||||
-
|
||||
name: pad
|
||||
type: pad
|
||||
-
|
||||
name: pd-id
|
||||
type: u32
|
||||
-
|
||||
name: flags
|
||||
type: u64
|
||||
-
|
||||
name: cpus
|
||||
type: string
|
||||
-
|
||||
name: pd-table
|
||||
attributes:
|
||||
-
|
||||
name: pd-id
|
||||
type: u32
|
||||
-
|
||||
name: ps
|
||||
type: nest
|
||||
nested-attributes: ps
|
||||
multi-attr: true
|
||||
-
|
||||
name: ps
|
||||
attributes:
|
||||
-
|
||||
name: pad
|
||||
type: pad
|
||||
-
|
||||
name: performance
|
||||
type: u64
|
||||
-
|
||||
name: frequency
|
||||
type: u64
|
||||
-
|
||||
name: power
|
||||
type: u64
|
||||
-
|
||||
name: cost
|
||||
type: u64
|
||||
-
|
||||
name: flags
|
||||
type: u64
|
||||
|
||||
operations:
|
||||
list:
|
||||
-
|
||||
name: get-pds
|
||||
attribute-set: pds
|
||||
doc: Get the list of information for all performance domains.
|
||||
do:
|
||||
reply:
|
||||
attributes:
|
||||
- pd
|
||||
-
|
||||
name: get-pd-table
|
||||
attribute-set: pd-table
|
||||
doc: Get the energy model table of a performance domain.
|
||||
do:
|
||||
request:
|
||||
attributes:
|
||||
- pd-id
|
||||
reply:
|
||||
attributes:
|
||||
- pd-id
|
||||
- ps
|
||||
-
|
||||
name: pd-created
|
||||
doc: A performance domain is created.
|
||||
notify: get-pd-table
|
||||
mcgrp: event
|
||||
-
|
||||
name: pd-updated
|
||||
doc: A performance domain is updated.
|
||||
notify: get-pd-table
|
||||
mcgrp: event
|
||||
-
|
||||
name: pd-deleted
|
||||
doc: A performance domain is deleted.
|
||||
attribute-set: pd-table
|
||||
event:
|
||||
attributes:
|
||||
- pd-id
|
||||
mcgrp: event
|
||||
|
||||
mcast-groups:
|
||||
list:
|
||||
-
|
||||
name: event
|
||||
@@ -9304,12 +9304,12 @@ M: Lukasz Luba <lukasz.luba@arm.com>
|
||||
M: "Rafael J. Wysocki" <rafael@kernel.org>
|
||||
L: linux-pm@vger.kernel.org
|
||||
S: Maintained
|
||||
F: kernel/power/energy_model.c
|
||||
F: include/linux/energy_model.h
|
||||
F: Documentation/netlink/specs/dev-energymodel.yaml
|
||||
F: Documentation/power/energy-model.rst
|
||||
F: Documentation/netlink/specs/em.yaml
|
||||
F: include/uapi/linux/energy_model.h
|
||||
F: include/linux/energy_model.h
|
||||
F: include/uapi/linux/dev_energymodel.h
|
||||
F: kernel/power/em_netlink*.*
|
||||
F: kernel/power/energy_model.c
|
||||
|
||||
EPAPR HYPERVISOR BYTE CHANNEL DEVICE DRIVER
|
||||
M: Laurentiu Tudor <laurentiu.tudor@nxp.com>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
* @power: The power consumed at this level (by 1 CPU or by a registered
|
||||
* device). It can be a total power: static and dynamic.
|
||||
* @cost: The cost coefficient associated with this level, used during
|
||||
* energy calculation. Equal to: power * max_frequency / frequency
|
||||
* energy calculation. Equal to: 10 * power * max_frequency / frequency
|
||||
* @flags: see "em_perf_state flags" description below.
|
||||
*/
|
||||
struct em_perf_state {
|
||||
|
||||
82
include/uapi/linux/dev_energymodel.h
Normal file
82
include/uapi/linux/dev_energymodel.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/* Do not edit directly, auto-generated from: */
|
||||
/* Documentation/netlink/specs/dev-energymodel.yaml */
|
||||
/* YNL-GEN uapi header */
|
||||
/* To regenerate run: tools/net/ynl/ynl-regen.sh */
|
||||
|
||||
#ifndef _UAPI_LINUX_DEV_ENERGYMODEL_H
|
||||
#define _UAPI_LINUX_DEV_ENERGYMODEL_H
|
||||
|
||||
#define DEV_ENERGYMODEL_FAMILY_NAME "dev-energymodel"
|
||||
#define DEV_ENERGYMODEL_FAMILY_VERSION 1
|
||||
|
||||
/**
|
||||
* enum dev_energymodel_perf_state_flags
|
||||
* @DEV_ENERGYMODEL_PERF_STATE_FLAGS_PERF_STATE_INEFFICIENT: The performance
|
||||
* state is inefficient. There is in this perf-domain, another performance
|
||||
* state with a higher frequency but a lower or equal power cost.
|
||||
*/
|
||||
enum dev_energymodel_perf_state_flags {
|
||||
DEV_ENERGYMODEL_PERF_STATE_FLAGS_PERF_STATE_INEFFICIENT = 1,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum dev_energymodel_perf_domain_flags
|
||||
* @DEV_ENERGYMODEL_PERF_DOMAIN_FLAGS_PERF_DOMAIN_MICROWATTS: The power values
|
||||
* are in micro-Watts or some other scale.
|
||||
* @DEV_ENERGYMODEL_PERF_DOMAIN_FLAGS_PERF_DOMAIN_SKIP_INEFFICIENCIES: Skip
|
||||
* inefficient states when estimating energy consumption.
|
||||
* @DEV_ENERGYMODEL_PERF_DOMAIN_FLAGS_PERF_DOMAIN_ARTIFICIAL: The power values
|
||||
* are artificial and might be created by platform missing real power
|
||||
* information.
|
||||
*/
|
||||
enum dev_energymodel_perf_domain_flags {
|
||||
DEV_ENERGYMODEL_PERF_DOMAIN_FLAGS_PERF_DOMAIN_MICROWATTS = 1,
|
||||
DEV_ENERGYMODEL_PERF_DOMAIN_FLAGS_PERF_DOMAIN_SKIP_INEFFICIENCIES = 2,
|
||||
DEV_ENERGYMODEL_PERF_DOMAIN_FLAGS_PERF_DOMAIN_ARTIFICIAL = 4,
|
||||
};
|
||||
|
||||
enum {
|
||||
DEV_ENERGYMODEL_A_PERF_DOMAIN_PAD = 1,
|
||||
DEV_ENERGYMODEL_A_PERF_DOMAIN_PERF_DOMAIN_ID,
|
||||
DEV_ENERGYMODEL_A_PERF_DOMAIN_FLAGS,
|
||||
DEV_ENERGYMODEL_A_PERF_DOMAIN_CPUS,
|
||||
|
||||
__DEV_ENERGYMODEL_A_PERF_DOMAIN_MAX,
|
||||
DEV_ENERGYMODEL_A_PERF_DOMAIN_MAX = (__DEV_ENERGYMODEL_A_PERF_DOMAIN_MAX - 1)
|
||||
};
|
||||
|
||||
enum {
|
||||
DEV_ENERGYMODEL_A_PERF_TABLE_PERF_DOMAIN_ID = 1,
|
||||
DEV_ENERGYMODEL_A_PERF_TABLE_PERF_STATE,
|
||||
|
||||
__DEV_ENERGYMODEL_A_PERF_TABLE_MAX,
|
||||
DEV_ENERGYMODEL_A_PERF_TABLE_MAX = (__DEV_ENERGYMODEL_A_PERF_TABLE_MAX - 1)
|
||||
};
|
||||
|
||||
enum {
|
||||
DEV_ENERGYMODEL_A_PERF_STATE_PAD = 1,
|
||||
DEV_ENERGYMODEL_A_PERF_STATE_PERFORMANCE,
|
||||
DEV_ENERGYMODEL_A_PERF_STATE_FREQUENCY,
|
||||
DEV_ENERGYMODEL_A_PERF_STATE_POWER,
|
||||
DEV_ENERGYMODEL_A_PERF_STATE_COST,
|
||||
DEV_ENERGYMODEL_A_PERF_STATE_FLAGS,
|
||||
|
||||
__DEV_ENERGYMODEL_A_PERF_STATE_MAX,
|
||||
DEV_ENERGYMODEL_A_PERF_STATE_MAX = (__DEV_ENERGYMODEL_A_PERF_STATE_MAX - 1)
|
||||
};
|
||||
|
||||
enum {
|
||||
DEV_ENERGYMODEL_CMD_GET_PERF_DOMAINS = 1,
|
||||
DEV_ENERGYMODEL_CMD_GET_PERF_TABLE,
|
||||
DEV_ENERGYMODEL_CMD_PERF_DOMAIN_CREATED,
|
||||
DEV_ENERGYMODEL_CMD_PERF_DOMAIN_UPDATED,
|
||||
DEV_ENERGYMODEL_CMD_PERF_DOMAIN_DELETED,
|
||||
|
||||
__DEV_ENERGYMODEL_CMD_MAX,
|
||||
DEV_ENERGYMODEL_CMD_MAX = (__DEV_ENERGYMODEL_CMD_MAX - 1)
|
||||
};
|
||||
|
||||
#define DEV_ENERGYMODEL_MCGRP_EVENT "event"
|
||||
|
||||
#endif /* _UAPI_LINUX_DEV_ENERGYMODEL_H */
|
||||
@@ -1,63 +0,0 @@
|
||||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/* Do not edit directly, auto-generated from: */
|
||||
/* Documentation/netlink/specs/em.yaml */
|
||||
/* YNL-GEN uapi header */
|
||||
/* To regenerate run: tools/net/ynl/ynl-regen.sh */
|
||||
|
||||
#ifndef _UAPI_LINUX_ENERGY_MODEL_H
|
||||
#define _UAPI_LINUX_ENERGY_MODEL_H
|
||||
|
||||
#define EM_FAMILY_NAME "em"
|
||||
#define EM_FAMILY_VERSION 1
|
||||
|
||||
enum {
|
||||
EM_A_PDS_PD = 1,
|
||||
|
||||
__EM_A_PDS_MAX,
|
||||
EM_A_PDS_MAX = (__EM_A_PDS_MAX - 1)
|
||||
};
|
||||
|
||||
enum {
|
||||
EM_A_PD_PAD = 1,
|
||||
EM_A_PD_PD_ID,
|
||||
EM_A_PD_FLAGS,
|
||||
EM_A_PD_CPUS,
|
||||
|
||||
__EM_A_PD_MAX,
|
||||
EM_A_PD_MAX = (__EM_A_PD_MAX - 1)
|
||||
};
|
||||
|
||||
enum {
|
||||
EM_A_PD_TABLE_PD_ID = 1,
|
||||
EM_A_PD_TABLE_PS,
|
||||
|
||||
__EM_A_PD_TABLE_MAX,
|
||||
EM_A_PD_TABLE_MAX = (__EM_A_PD_TABLE_MAX - 1)
|
||||
};
|
||||
|
||||
enum {
|
||||
EM_A_PS_PAD = 1,
|
||||
EM_A_PS_PERFORMANCE,
|
||||
EM_A_PS_FREQUENCY,
|
||||
EM_A_PS_POWER,
|
||||
EM_A_PS_COST,
|
||||
EM_A_PS_FLAGS,
|
||||
|
||||
__EM_A_PS_MAX,
|
||||
EM_A_PS_MAX = (__EM_A_PS_MAX - 1)
|
||||
};
|
||||
|
||||
enum {
|
||||
EM_CMD_GET_PDS = 1,
|
||||
EM_CMD_GET_PD_TABLE,
|
||||
EM_CMD_PD_CREATED,
|
||||
EM_CMD_PD_UPDATED,
|
||||
EM_CMD_PD_DELETED,
|
||||
|
||||
__EM_CMD_MAX,
|
||||
EM_CMD_MAX = (__EM_CMD_MAX - 1)
|
||||
};
|
||||
|
||||
#define EM_MCGRP_EVENT "event"
|
||||
|
||||
#endif /* _UAPI_LINUX_ENERGY_MODEL_H */
|
||||
@@ -12,27 +12,35 @@
|
||||
#include <linux/energy_model.h>
|
||||
#include <net/sock.h>
|
||||
#include <net/genetlink.h>
|
||||
#include <uapi/linux/energy_model.h>
|
||||
#include <uapi/linux/dev_energymodel.h>
|
||||
|
||||
#include "em_netlink.h"
|
||||
#include "em_netlink_autogen.h"
|
||||
|
||||
#define EM_A_PD_CPUS_LEN 256
|
||||
|
||||
/*************************** Command encoding ********************************/
|
||||
struct dump_ctx {
|
||||
int idx;
|
||||
int start;
|
||||
struct sk_buff *skb;
|
||||
struct netlink_callback *cb;
|
||||
};
|
||||
|
||||
static int __em_nl_get_pd_size(struct em_perf_domain *pd, void *data)
|
||||
{
|
||||
char cpus_buf[EM_A_PD_CPUS_LEN];
|
||||
int nr_cpus, msg_sz, cpus_sz;
|
||||
int *tot_msg_sz = data;
|
||||
int msg_sz, cpus_sz;
|
||||
|
||||
cpus_sz = snprintf(cpus_buf, sizeof(cpus_buf), "%*pb",
|
||||
cpumask_pr_args(to_cpumask(pd->cpus)));
|
||||
nr_cpus = cpumask_weight(to_cpumask(pd->cpus));
|
||||
cpus_sz = nla_total_size_64bit(sizeof(u64)) * nr_cpus;
|
||||
|
||||
msg_sz = nla_total_size(0) + /* EM_A_PDS_PD */
|
||||
nla_total_size(sizeof(u32)) + /* EM_A_PD_PD_ID */
|
||||
nla_total_size_64bit(sizeof(u64)) + /* EM_A_PD_FLAGS */
|
||||
nla_total_size(cpus_sz); /* EM_A_PD_CPUS */
|
||||
msg_sz = nla_total_size(0) +
|
||||
/* DEV_ENERGYMODEL_A_PERF_DOMAINS_PERF_DOMAIN */
|
||||
nla_total_size(sizeof(u32)) +
|
||||
/* DEV_ENERGYMODEL_A_PERF_DOMAIN_PERF_DOMAIN_ID */
|
||||
nla_total_size_64bit(sizeof(u64)) +
|
||||
/* DEV_ENERGYMODEL_A_PERF_DOMAIN_FLAGS */
|
||||
nla_total_size(cpus_sz);
|
||||
/* DEV_ENERGYMODEL_A_PERF_DOMAIN_CPUS */
|
||||
|
||||
*tot_msg_sz += nlmsg_total_size(genlmsg_msg_size(msg_sz));
|
||||
return 0;
|
||||
@@ -40,56 +48,80 @@ static int __em_nl_get_pd_size(struct em_perf_domain *pd, void *data)
|
||||
|
||||
static int __em_nl_get_pd(struct em_perf_domain *pd, void *data)
|
||||
{
|
||||
char cpus_buf[EM_A_PD_CPUS_LEN];
|
||||
struct sk_buff *msg = data;
|
||||
struct nlattr *entry;
|
||||
struct cpumask *cpumask;
|
||||
int cpu;
|
||||
|
||||
entry = nla_nest_start(msg, EM_A_PDS_PD);
|
||||
if (!entry)
|
||||
if (nla_put_u32(msg, DEV_ENERGYMODEL_A_PERF_DOMAIN_PERF_DOMAIN_ID,
|
||||
pd->id))
|
||||
goto out_cancel_nest;
|
||||
|
||||
if (nla_put_u32(msg, EM_A_PD_PD_ID, pd->id))
|
||||
if (nla_put_u64_64bit(msg, DEV_ENERGYMODEL_A_PERF_DOMAIN_FLAGS,
|
||||
pd->flags, DEV_ENERGYMODEL_A_PERF_DOMAIN_PAD))
|
||||
goto out_cancel_nest;
|
||||
|
||||
if (nla_put_u64_64bit(msg, EM_A_PD_FLAGS, pd->flags, EM_A_PD_PAD))
|
||||
goto out_cancel_nest;
|
||||
|
||||
snprintf(cpus_buf, sizeof(cpus_buf), "%*pb",
|
||||
cpumask_pr_args(to_cpumask(pd->cpus)));
|
||||
if (nla_put_string(msg, EM_A_PD_CPUS, cpus_buf))
|
||||
goto out_cancel_nest;
|
||||
|
||||
nla_nest_end(msg, entry);
|
||||
cpumask = to_cpumask(pd->cpus);
|
||||
for_each_cpu(cpu, cpumask) {
|
||||
if (nla_put_u64_64bit(msg, DEV_ENERGYMODEL_A_PERF_DOMAIN_CPUS,
|
||||
cpu, DEV_ENERGYMODEL_A_PERF_DOMAIN_PAD))
|
||||
goto out_cancel_nest;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
out_cancel_nest:
|
||||
nla_nest_cancel(msg, entry);
|
||||
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
int em_nl_get_pds_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
static int __em_nl_get_pd_for_dump(struct em_perf_domain *pd, void *data)
|
||||
{
|
||||
const struct genl_info *info;
|
||||
struct dump_ctx *ctx = data;
|
||||
void *hdr;
|
||||
int ret;
|
||||
|
||||
if (ctx->idx++ < ctx->start)
|
||||
return 0;
|
||||
|
||||
info = genl_info_dump(ctx->cb);
|
||||
hdr = genlmsg_iput(ctx->skb, info);
|
||||
if (!hdr) {
|
||||
genlmsg_cancel(ctx->skb, hdr);
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
ret = __em_nl_get_pd(pd, ctx->skb);
|
||||
genlmsg_end(ctx->skb, hdr);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int dev_energymodel_nl_get_perf_domains_doit(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
int id, ret = -EMSGSIZE, msg_sz = 0;
|
||||
int cmd = info->genlhdr->cmd;
|
||||
struct em_perf_domain *pd;
|
||||
struct sk_buff *msg;
|
||||
void *hdr;
|
||||
int cmd = info->genlhdr->cmd;
|
||||
int ret = -EMSGSIZE, msg_sz = 0;
|
||||
|
||||
for_each_em_perf_domain(__em_nl_get_pd_size, &msg_sz);
|
||||
if (!info->attrs[DEV_ENERGYMODEL_A_PERF_DOMAIN_PERF_DOMAIN_ID])
|
||||
return -EINVAL;
|
||||
|
||||
id = nla_get_u32(info->attrs[DEV_ENERGYMODEL_A_PERF_DOMAIN_PERF_DOMAIN_ID]);
|
||||
pd = em_perf_domain_get_by_id(id);
|
||||
|
||||
__em_nl_get_pd_size(pd, &msg_sz);
|
||||
msg = genlmsg_new(msg_sz, GFP_KERNEL);
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
hdr = genlmsg_put_reply(msg, info, &em_nl_family, 0, cmd);
|
||||
hdr = genlmsg_put_reply(msg, info, &dev_energymodel_nl_family, 0, cmd);
|
||||
if (!hdr)
|
||||
goto out_free_msg;
|
||||
|
||||
ret = for_each_em_perf_domain(__em_nl_get_pd, msg);
|
||||
ret = __em_nl_get_pd(pd, msg);
|
||||
if (ret)
|
||||
goto out_cancel_msg;
|
||||
|
||||
genlmsg_end(msg, hdr);
|
||||
|
||||
return genlmsg_reply(msg, info);
|
||||
@@ -98,19 +130,31 @@ out_cancel_msg:
|
||||
genlmsg_cancel(msg, hdr);
|
||||
out_free_msg:
|
||||
nlmsg_free(msg);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int dev_energymodel_nl_get_perf_domains_dumpit(struct sk_buff *skb,
|
||||
struct netlink_callback *cb)
|
||||
{
|
||||
struct dump_ctx ctx = {
|
||||
.idx = 0,
|
||||
.start = cb->args[0],
|
||||
.skb = skb,
|
||||
.cb = cb,
|
||||
};
|
||||
|
||||
return for_each_em_perf_domain(__em_nl_get_pd_for_dump, &ctx);
|
||||
}
|
||||
|
||||
static struct em_perf_domain *__em_nl_get_pd_table_id(struct nlattr **attrs)
|
||||
{
|
||||
struct em_perf_domain *pd;
|
||||
int id;
|
||||
|
||||
if (!attrs[EM_A_PD_TABLE_PD_ID])
|
||||
if (!attrs[DEV_ENERGYMODEL_A_PERF_TABLE_PERF_DOMAIN_ID])
|
||||
return NULL;
|
||||
|
||||
id = nla_get_u32(attrs[EM_A_PD_TABLE_PD_ID]);
|
||||
id = nla_get_u32(attrs[DEV_ENERGYMODEL_A_PERF_TABLE_PERF_DOMAIN_ID]);
|
||||
pd = em_perf_domain_get_by_id(id);
|
||||
return pd;
|
||||
}
|
||||
@@ -119,25 +163,34 @@ static int __em_nl_get_pd_table_size(const struct em_perf_domain *pd)
|
||||
{
|
||||
int id_sz, ps_sz;
|
||||
|
||||
id_sz = nla_total_size(sizeof(u32)); /* EM_A_PD_TABLE_PD_ID */
|
||||
ps_sz = nla_total_size(0) + /* EM_A_PD_TABLE_PS */
|
||||
nla_total_size_64bit(sizeof(u64)) + /* EM_A_PS_PERFORMANCE */
|
||||
nla_total_size_64bit(sizeof(u64)) + /* EM_A_PS_FREQUENCY */
|
||||
nla_total_size_64bit(sizeof(u64)) + /* EM_A_PS_POWER */
|
||||
nla_total_size_64bit(sizeof(u64)) + /* EM_A_PS_COST */
|
||||
nla_total_size_64bit(sizeof(u64)); /* EM_A_PS_FLAGS */
|
||||
id_sz = nla_total_size(sizeof(u32));
|
||||
/* DEV_ENERGYMODEL_A_PERF_TABLE_PERF_DOMAIN_ID */
|
||||
ps_sz = nla_total_size(0) +
|
||||
/* DEV_ENERGYMODEL_A_PERF_TABLE_PERF_STATE */
|
||||
nla_total_size_64bit(sizeof(u64)) +
|
||||
/* DEV_ENERGYMODEL_A_PERF_STATE_PERFORMANCE */
|
||||
nla_total_size_64bit(sizeof(u64)) +
|
||||
/* DEV_ENERGYMODEL_A_PERF_STATE_FREQUENCY */
|
||||
nla_total_size_64bit(sizeof(u64)) +
|
||||
/* DEV_ENERGYMODEL_A_PERF_STATE_POWER */
|
||||
nla_total_size_64bit(sizeof(u64)) +
|
||||
/* DEV_ENERGYMODEL_A_PERF_STATE_COST */
|
||||
nla_total_size_64bit(sizeof(u64));
|
||||
/* DEV_ENERGYMODEL_A_PERF_STATE_FLAGS */
|
||||
ps_sz *= pd->nr_perf_states;
|
||||
|
||||
return nlmsg_total_size(genlmsg_msg_size(id_sz + ps_sz));
|
||||
}
|
||||
|
||||
static int __em_nl_get_pd_table(struct sk_buff *msg, const struct em_perf_domain *pd)
|
||||
static
|
||||
int __em_nl_get_pd_table(struct sk_buff *msg, const struct em_perf_domain *pd)
|
||||
{
|
||||
struct em_perf_state *table, *ps;
|
||||
struct nlattr *entry;
|
||||
int i;
|
||||
|
||||
if (nla_put_u32(msg, EM_A_PD_TABLE_PD_ID, pd->id))
|
||||
if (nla_put_u32(msg, DEV_ENERGYMODEL_A_PERF_TABLE_PERF_DOMAIN_ID,
|
||||
pd->id))
|
||||
goto out_err;
|
||||
|
||||
rcu_read_lock();
|
||||
@@ -146,24 +199,35 @@ static int __em_nl_get_pd_table(struct sk_buff *msg, const struct em_perf_domain
|
||||
for (i = 0; i < pd->nr_perf_states; i++) {
|
||||
ps = &table[i];
|
||||
|
||||
entry = nla_nest_start(msg, EM_A_PD_TABLE_PS);
|
||||
entry = nla_nest_start(msg,
|
||||
DEV_ENERGYMODEL_A_PERF_TABLE_PERF_STATE);
|
||||
if (!entry)
|
||||
goto out_unlock_ps;
|
||||
|
||||
if (nla_put_u64_64bit(msg, EM_A_PS_PERFORMANCE,
|
||||
ps->performance, EM_A_PS_PAD))
|
||||
if (nla_put_u64_64bit(msg,
|
||||
DEV_ENERGYMODEL_A_PERF_STATE_PERFORMANCE,
|
||||
ps->performance,
|
||||
DEV_ENERGYMODEL_A_PERF_STATE_PAD))
|
||||
goto out_cancel_ps_nest;
|
||||
if (nla_put_u64_64bit(msg, EM_A_PS_FREQUENCY,
|
||||
ps->frequency, EM_A_PS_PAD))
|
||||
if (nla_put_u64_64bit(msg,
|
||||
DEV_ENERGYMODEL_A_PERF_STATE_FREQUENCY,
|
||||
ps->frequency,
|
||||
DEV_ENERGYMODEL_A_PERF_STATE_PAD))
|
||||
goto out_cancel_ps_nest;
|
||||
if (nla_put_u64_64bit(msg, EM_A_PS_POWER,
|
||||
ps->power, EM_A_PS_PAD))
|
||||
if (nla_put_u64_64bit(msg,
|
||||
DEV_ENERGYMODEL_A_PERF_STATE_POWER,
|
||||
ps->power,
|
||||
DEV_ENERGYMODEL_A_PERF_STATE_PAD))
|
||||
goto out_cancel_ps_nest;
|
||||
if (nla_put_u64_64bit(msg, EM_A_PS_COST,
|
||||
ps->cost, EM_A_PS_PAD))
|
||||
if (nla_put_u64_64bit(msg,
|
||||
DEV_ENERGYMODEL_A_PERF_STATE_COST,
|
||||
ps->cost,
|
||||
DEV_ENERGYMODEL_A_PERF_STATE_PAD))
|
||||
goto out_cancel_ps_nest;
|
||||
if (nla_put_u64_64bit(msg, EM_A_PS_FLAGS,
|
||||
ps->flags, EM_A_PS_PAD))
|
||||
if (nla_put_u64_64bit(msg,
|
||||
DEV_ENERGYMODEL_A_PERF_STATE_FLAGS,
|
||||
ps->flags,
|
||||
DEV_ENERGYMODEL_A_PERF_STATE_PAD))
|
||||
goto out_cancel_ps_nest;
|
||||
|
||||
nla_nest_end(msg, entry);
|
||||
@@ -179,7 +243,8 @@ out_err:
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
int em_nl_get_pd_table_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
int dev_energymodel_nl_get_perf_table_doit(struct sk_buff *skb,
|
||||
struct genl_info *info)
|
||||
{
|
||||
int cmd = info->genlhdr->cmd;
|
||||
int msg_sz, ret = -EMSGSIZE;
|
||||
@@ -197,7 +262,7 @@ int em_nl_get_pd_table_doit(struct sk_buff *skb, struct genl_info *info)
|
||||
if (!msg)
|
||||
return -ENOMEM;
|
||||
|
||||
hdr = genlmsg_put_reply(msg, info, &em_nl_family, 0, cmd);
|
||||
hdr = genlmsg_put_reply(msg, info, &dev_energymodel_nl_family, 0, cmd);
|
||||
if (!hdr)
|
||||
goto out_free_msg;
|
||||
|
||||
@@ -221,7 +286,7 @@ static void __em_notify_pd_table(const struct em_perf_domain *pd, int ntf_type)
|
||||
int msg_sz, ret = -EMSGSIZE;
|
||||
void *hdr;
|
||||
|
||||
if (!genl_has_listeners(&em_nl_family, &init_net, EM_NLGRP_EVENT))
|
||||
if (!genl_has_listeners(&dev_energymodel_nl_family, &init_net, DEV_ENERGYMODEL_NLGRP_EVENT))
|
||||
return;
|
||||
|
||||
msg_sz = __em_nl_get_pd_table_size(pd);
|
||||
@@ -230,7 +295,7 @@ static void __em_notify_pd_table(const struct em_perf_domain *pd, int ntf_type)
|
||||
if (!msg)
|
||||
return;
|
||||
|
||||
hdr = genlmsg_put(msg, 0, 0, &em_nl_family, 0, ntf_type);
|
||||
hdr = genlmsg_put(msg, 0, 0, &dev_energymodel_nl_family, 0, ntf_type);
|
||||
if (!hdr)
|
||||
goto out_free_msg;
|
||||
|
||||
@@ -240,28 +305,28 @@ static void __em_notify_pd_table(const struct em_perf_domain *pd, int ntf_type)
|
||||
|
||||
genlmsg_end(msg, hdr);
|
||||
|
||||
genlmsg_multicast(&em_nl_family, msg, 0, EM_NLGRP_EVENT, GFP_KERNEL);
|
||||
genlmsg_multicast(&dev_energymodel_nl_family, msg, 0,
|
||||
DEV_ENERGYMODEL_NLGRP_EVENT, GFP_KERNEL);
|
||||
|
||||
return;
|
||||
|
||||
out_free_msg:
|
||||
nlmsg_free(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
void em_notify_pd_created(const struct em_perf_domain *pd)
|
||||
{
|
||||
__em_notify_pd_table(pd, EM_CMD_PD_CREATED);
|
||||
__em_notify_pd_table(pd, DEV_ENERGYMODEL_CMD_PERF_DOMAIN_CREATED);
|
||||
}
|
||||
|
||||
void em_notify_pd_updated(const struct em_perf_domain *pd)
|
||||
{
|
||||
__em_notify_pd_table(pd, EM_CMD_PD_UPDATED);
|
||||
__em_notify_pd_table(pd, DEV_ENERGYMODEL_CMD_PERF_DOMAIN_UPDATED);
|
||||
}
|
||||
|
||||
static int __em_notify_pd_deleted_size(const struct em_perf_domain *pd)
|
||||
{
|
||||
int id_sz = nla_total_size(sizeof(u32)); /* EM_A_PD_TABLE_PD_ID */
|
||||
int id_sz = nla_total_size(sizeof(u32)); /* DEV_ENERGYMODEL_A_PERF_TABLE_PERF_DOMAIN_ID */
|
||||
|
||||
return nlmsg_total_size(genlmsg_msg_size(id_sz));
|
||||
}
|
||||
@@ -272,7 +337,8 @@ void em_notify_pd_deleted(const struct em_perf_domain *pd)
|
||||
void *hdr;
|
||||
int msg_sz;
|
||||
|
||||
if (!genl_has_listeners(&em_nl_family, &init_net, EM_NLGRP_EVENT))
|
||||
if (!genl_has_listeners(&dev_energymodel_nl_family, &init_net,
|
||||
DEV_ENERGYMODEL_NLGRP_EVENT))
|
||||
return;
|
||||
|
||||
msg_sz = __em_notify_pd_deleted_size(pd);
|
||||
@@ -281,28 +347,29 @@ void em_notify_pd_deleted(const struct em_perf_domain *pd)
|
||||
if (!msg)
|
||||
return;
|
||||
|
||||
hdr = genlmsg_put(msg, 0, 0, &em_nl_family, 0, EM_CMD_PD_DELETED);
|
||||
hdr = genlmsg_put(msg, 0, 0, &dev_energymodel_nl_family, 0,
|
||||
DEV_ENERGYMODEL_CMD_PERF_DOMAIN_DELETED);
|
||||
if (!hdr)
|
||||
goto out_free_msg;
|
||||
|
||||
if (nla_put_u32(msg, EM_A_PD_TABLE_PD_ID, pd->id)) {
|
||||
if (nla_put_u32(msg, DEV_ENERGYMODEL_A_PERF_TABLE_PERF_DOMAIN_ID,
|
||||
pd->id))
|
||||
goto out_free_msg;
|
||||
}
|
||||
|
||||
genlmsg_end(msg, hdr);
|
||||
|
||||
genlmsg_multicast(&em_nl_family, msg, 0, EM_NLGRP_EVENT, GFP_KERNEL);
|
||||
genlmsg_multicast(&dev_energymodel_nl_family, msg, 0,
|
||||
DEV_ENERGYMODEL_NLGRP_EVENT, GFP_KERNEL);
|
||||
|
||||
return;
|
||||
|
||||
out_free_msg:
|
||||
nlmsg_free(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
/**************************** Initialization *********************************/
|
||||
static int __init em_netlink_init(void)
|
||||
{
|
||||
return genl_register_family(&em_nl_family);
|
||||
return genl_register_family(&dev_energymodel_nl_family);
|
||||
}
|
||||
postcore_initcall(em_netlink_init);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
|
||||
/* Do not edit directly, auto-generated from: */
|
||||
/* Documentation/netlink/specs/em.yaml */
|
||||
/* Documentation/netlink/specs/dev-energymodel.yaml */
|
||||
/* YNL-GEN kernel source */
|
||||
/* To regenerate run: tools/net/ynl/ynl-regen.sh */
|
||||
|
||||
@@ -9,41 +9,53 @@
|
||||
|
||||
#include "em_netlink_autogen.h"
|
||||
|
||||
#include <uapi/linux/energy_model.h>
|
||||
#include <uapi/linux/dev_energymodel.h>
|
||||
|
||||
/* EM_CMD_GET_PD_TABLE - do */
|
||||
static const struct nla_policy em_get_pd_table_nl_policy[EM_A_PD_TABLE_PD_ID + 1] = {
|
||||
[EM_A_PD_TABLE_PD_ID] = { .type = NLA_U32, },
|
||||
/* DEV_ENERGYMODEL_CMD_GET_PERF_DOMAINS - do */
|
||||
static const struct nla_policy dev_energymodel_get_perf_domains_nl_policy[DEV_ENERGYMODEL_A_PERF_DOMAIN_PERF_DOMAIN_ID + 1] = {
|
||||
[DEV_ENERGYMODEL_A_PERF_DOMAIN_PERF_DOMAIN_ID] = { .type = NLA_U32, },
|
||||
};
|
||||
|
||||
/* Ops table for em */
|
||||
static const struct genl_split_ops em_nl_ops[] = {
|
||||
/* DEV_ENERGYMODEL_CMD_GET_PERF_TABLE - do */
|
||||
static const struct nla_policy dev_energymodel_get_perf_table_nl_policy[DEV_ENERGYMODEL_A_PERF_TABLE_PERF_DOMAIN_ID + 1] = {
|
||||
[DEV_ENERGYMODEL_A_PERF_TABLE_PERF_DOMAIN_ID] = { .type = NLA_U32, },
|
||||
};
|
||||
|
||||
/* Ops table for dev_energymodel */
|
||||
static const struct genl_split_ops dev_energymodel_nl_ops[] = {
|
||||
{
|
||||
.cmd = EM_CMD_GET_PDS,
|
||||
.doit = em_nl_get_pds_doit,
|
||||
.flags = GENL_CMD_CAP_DO,
|
||||
.cmd = DEV_ENERGYMODEL_CMD_GET_PERF_DOMAINS,
|
||||
.doit = dev_energymodel_nl_get_perf_domains_doit,
|
||||
.policy = dev_energymodel_get_perf_domains_nl_policy,
|
||||
.maxattr = DEV_ENERGYMODEL_A_PERF_DOMAIN_PERF_DOMAIN_ID,
|
||||
.flags = GENL_CMD_CAP_DO,
|
||||
},
|
||||
{
|
||||
.cmd = EM_CMD_GET_PD_TABLE,
|
||||
.doit = em_nl_get_pd_table_doit,
|
||||
.policy = em_get_pd_table_nl_policy,
|
||||
.maxattr = EM_A_PD_TABLE_PD_ID,
|
||||
.cmd = DEV_ENERGYMODEL_CMD_GET_PERF_DOMAINS,
|
||||
.dumpit = dev_energymodel_nl_get_perf_domains_dumpit,
|
||||
.flags = GENL_CMD_CAP_DUMP,
|
||||
},
|
||||
{
|
||||
.cmd = DEV_ENERGYMODEL_CMD_GET_PERF_TABLE,
|
||||
.doit = dev_energymodel_nl_get_perf_table_doit,
|
||||
.policy = dev_energymodel_get_perf_table_nl_policy,
|
||||
.maxattr = DEV_ENERGYMODEL_A_PERF_TABLE_PERF_DOMAIN_ID,
|
||||
.flags = GENL_CMD_CAP_DO,
|
||||
},
|
||||
};
|
||||
|
||||
static const struct genl_multicast_group em_nl_mcgrps[] = {
|
||||
[EM_NLGRP_EVENT] = { "event", },
|
||||
static const struct genl_multicast_group dev_energymodel_nl_mcgrps[] = {
|
||||
[DEV_ENERGYMODEL_NLGRP_EVENT] = { "event", },
|
||||
};
|
||||
|
||||
struct genl_family em_nl_family __ro_after_init = {
|
||||
.name = EM_FAMILY_NAME,
|
||||
.version = EM_FAMILY_VERSION,
|
||||
struct genl_family dev_energymodel_nl_family __ro_after_init = {
|
||||
.name = DEV_ENERGYMODEL_FAMILY_NAME,
|
||||
.version = DEV_ENERGYMODEL_FAMILY_VERSION,
|
||||
.netnsok = true,
|
||||
.parallel_ops = true,
|
||||
.module = THIS_MODULE,
|
||||
.split_ops = em_nl_ops,
|
||||
.n_split_ops = ARRAY_SIZE(em_nl_ops),
|
||||
.mcgrps = em_nl_mcgrps,
|
||||
.n_mcgrps = ARRAY_SIZE(em_nl_mcgrps),
|
||||
.split_ops = dev_energymodel_nl_ops,
|
||||
.n_split_ops = ARRAY_SIZE(dev_energymodel_nl_ops),
|
||||
.mcgrps = dev_energymodel_nl_mcgrps,
|
||||
.n_mcgrps = ARRAY_SIZE(dev_energymodel_nl_mcgrps),
|
||||
};
|
||||
|
||||
@@ -1,24 +1,28 @@
|
||||
/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */
|
||||
/* Do not edit directly, auto-generated from: */
|
||||
/* Documentation/netlink/specs/em.yaml */
|
||||
/* Documentation/netlink/specs/dev-energymodel.yaml */
|
||||
/* YNL-GEN kernel header */
|
||||
/* To regenerate run: tools/net/ynl/ynl-regen.sh */
|
||||
|
||||
#ifndef _LINUX_EM_GEN_H
|
||||
#define _LINUX_EM_GEN_H
|
||||
#ifndef _LINUX_DEV_ENERGYMODEL_GEN_H
|
||||
#define _LINUX_DEV_ENERGYMODEL_GEN_H
|
||||
|
||||
#include <net/netlink.h>
|
||||
#include <net/genetlink.h>
|
||||
|
||||
#include <uapi/linux/energy_model.h>
|
||||
#include <uapi/linux/dev_energymodel.h>
|
||||
|
||||
int em_nl_get_pds_doit(struct sk_buff *skb, struct genl_info *info);
|
||||
int em_nl_get_pd_table_doit(struct sk_buff *skb, struct genl_info *info);
|
||||
int dev_energymodel_nl_get_perf_domains_doit(struct sk_buff *skb,
|
||||
struct genl_info *info);
|
||||
int dev_energymodel_nl_get_perf_domains_dumpit(struct sk_buff *skb,
|
||||
struct netlink_callback *cb);
|
||||
int dev_energymodel_nl_get_perf_table_doit(struct sk_buff *skb,
|
||||
struct genl_info *info);
|
||||
|
||||
enum {
|
||||
EM_NLGRP_EVENT,
|
||||
DEV_ENERGYMODEL_NLGRP_EVENT,
|
||||
};
|
||||
|
||||
extern struct genl_family em_nl_family;
|
||||
extern struct genl_family dev_energymodel_nl_family;
|
||||
|
||||
#endif /* _LINUX_EM_GEN_H */
|
||||
#endif /* _LINUX_DEV_ENERGYMODEL_GEN_H */
|
||||
|
||||
@@ -449,8 +449,10 @@ static int em_create_pd(struct device *dev, int nr_states,
|
||||
INIT_LIST_HEAD(&pd->node);
|
||||
|
||||
id = ida_alloc(&em_pd_ida, GFP_KERNEL);
|
||||
if (id < 0)
|
||||
return -ENOMEM;
|
||||
if (id < 0) {
|
||||
kfree(pd);
|
||||
return id;
|
||||
}
|
||||
pd->id = id;
|
||||
|
||||
em_table = em_table_alloc(pd);
|
||||
|
||||
Reference in New Issue
Block a user