QAPI patches patches for 2021-10-29

-----BEGIN PGP SIGNATURE-----
 
 iQJGBAABCAAwFiEENUvIs9frKmtoZ05fOHC0AOuRhlMFAmF8S3USHGFybWJydUBy
 ZWRoYXQuY29tAAoJEDhwtADrkYZTkfQP/34UaR77wfOvemMBCY7CX/AD6KsJTt2v
 /mVJ8kUC+aJdyDa1orhDxLxyvx0YxSUjDUoTVGrb/CO8eM8dL4Lz6jCi1OiBCaBq
 JJAtUduaLDUUfZnKwrvhW0cGZeJ02eqxIKhV/9BFcFSr0fAzZRnF1RwsNBO/AqCr
 082bimWttsBpnNqnbWFK8uZYHMskbPOoXxklIMDM48BJhb6EOdH1/wZpeGdTUMIN
 zBTOynZW52xrDdlngo3UBr+uyDX8mjnwMOmpSa5YgkQselxDK172xZm31VQUMtGz
 S2qNCxxKf4J/wJMLElo0z3nEUUEFrpZzqifO1gsDit4eWexL35BHCdlRbB07iJ/+
 V0wtF2M+KQGhoqLJTvlVlymRAeC3ItHHDgh9qs3nS7w9w13oGuB7xo8Po5PfKpc3
 vT2XBYy8LPHLQ7/82nQGQdXkd1OB/8fwJt0bcPc+UIGAhNrwpSmCoO0ugqU2OD+C
 NJs6R3y1bbJnQGG5f3M/QU/dF0zYVpHv6a+QWFwwV+rc2RAjeeMrSYH+08t1c5+u
 Qh2P8YvgRPQxPmg9Rbqfcxq7CwVs0iZINfjmLwofbqBQUZUAq3phHRtJ9bmGbXkC
 pC4B+d76kefSxqlVNwi95Zg+CCURT8QzamKVlkZ/GNP73+orV9yQvA0aR4J2arLV
 1QVm4jTqAqIo
 =HKnl
 -----END PGP SIGNATURE-----

Merge remote-tracking branch 'remotes/armbru/tags/pull-qapi-2021-10-29' into staging

QAPI patches patches for 2021-10-29

# gpg: Signature made Fri 29 Oct 2021 12:28:53 PM PDT
# gpg:                using RSA key 354BC8B3D7EB2A6B68674E5F3870B400EB918653
# gpg:                issuer "armbru@redhat.com"
# gpg: Good signature from "Markus Armbruster <armbru@redhat.com>" [full]
# gpg:                 aka "Markus Armbruster <armbru@pond.sub.org>" [full]

* remotes/armbru/tags/pull-qapi-2021-10-29:
  qapi: Extend -compat to set policy for unstable interfaces
  qapi: Factor out compat_policy_input_ok()
  qapi: Generalize enum member policy checking
  qapi: Generalize command policy checking
  qapi: Generalize struct member policy checking
  qapi: Tools for sets of special feature flags in generated code
  qapi: Eliminate QCO_NO_OPTIONS for a slight simplification
  qapi: Mark unstable QMP parts with feature 'unstable'
  qapi: New special feature flag "unstable"

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson 2021-10-29 19:42:36 -07:00
commit dd61b91c08
30 changed files with 355 additions and 166 deletions

View File

@ -713,6 +713,10 @@ member as deprecated. It is not supported elsewhere so far.
Interfaces so marked may be withdrawn in future releases in accordance
with QEMU's deprecation policy.
Feature "unstable" marks a command, event, enum value, or struct
member as unstable. It is not supported elsewhere so far. Interfaces
so marked may be withdrawn or changed incompatibly in future releases.
Naming rules and reserved names
-------------------------------
@ -746,9 +750,8 @@ Member name ``u`` and names starting with ``has-`` or ``has_`` are reserved
for the generator, which uses them for unions and for tracking
optional members.
Any name (command, event, type, member, or enum value) beginning with
``x-`` is marked experimental, and may be withdrawn or changed
incompatibly in a future release.
Names beginning with ``x-`` used to signify "experimental". This
convention has been replaced by special feature "unstable".
Pragmas ``command-name-exceptions`` and ``member-name-exceptions`` let
you violate naming rules. Use for new code is strongly discouraged. See

View File

@ -13,10 +13,17 @@
#ifndef QAPI_COMPAT_POLICY_H
#define QAPI_COMPAT_POLICY_H
#include "qapi/error.h"
#include "qapi/qapi-types-compat.h"
extern CompatPolicy compat_policy;
bool compat_policy_input_ok(unsigned special_features,
const CompatPolicy *policy,
ErrorClass error_class,
const char *kind, const char *name,
Error **errp);
/*
* Create a QObject input visitor for @obj for use with QMP
*

View File

@ -21,12 +21,10 @@ typedef void (QmpCommandFunc)(QDict *, QObject **, Error **);
typedef enum QmpCommandOptions
{
QCO_NO_OPTIONS = 0x0,
QCO_NO_SUCCESS_RESP = (1U << 0),
QCO_ALLOW_OOB = (1U << 1),
QCO_ALLOW_PRECONFIG = (1U << 2),
QCO_COROUTINE = (1U << 3),
QCO_DEPRECATED = (1U << 4),
} QmpCommandOptions;
typedef struct QmpCommand
@ -35,6 +33,7 @@ typedef struct QmpCommand
/* Runs in coroutine context if QCO_COROUTINE is set */
QmpCommandFunc *fn;
QmpCommandOptions options;
unsigned special_features;
QTAILQ_ENTRY(QmpCommand) node;
bool enabled;
const char *disable_reason;
@ -43,7 +42,8 @@ typedef struct QmpCommand
typedef QTAILQ_HEAD(QmpCommandList, QmpCommand) QmpCommandList;
void qmp_register_command(QmpCommandList *cmds, const char *name,
QmpCommandFunc *fn, QmpCommandOptions options);
QmpCommandFunc *fn, QmpCommandOptions options,
unsigned special_features);
const QmpCommand *qmp_find_command(const QmpCommandList *cmds,
const char *name);
void qmp_disable_command(QmpCommandList *cmds, const char *name,

View File

@ -11,12 +11,14 @@
#ifndef QAPI_UTIL_H
#define QAPI_UTIL_H
/* QEnumLookup flags */
#define QAPI_ENUM_DEPRECATED 1
typedef enum {
QAPI_DEPRECATED,
QAPI_UNSTABLE,
} QapiSpecialFeature;
typedef struct QEnumLookup {
const char *const *array;
const unsigned char *const flags;
const unsigned char *const special_features;
const int size;
} QEnumLookup;

View File

@ -114,10 +114,12 @@ struct Visitor
void (*optional)(Visitor *v, const char *name, bool *present);
/* Optional */
bool (*deprecated_accept)(Visitor *v, const char *name, Error **errp);
bool (*policy_reject)(Visitor *v, const char *name,
unsigned special_features, Error **errp);
/* Optional */
bool (*deprecated)(Visitor *v, const char *name);
bool (*policy_skip)(Visitor *v, const char *name,
unsigned special_features);
/* Must be set */
VisitorType type;

View File

@ -461,22 +461,31 @@ void visit_end_alternate(Visitor *v, void **obj);
bool visit_optional(Visitor *v, const char *name, bool *present);
/*
* Should we reject deprecated member @name?
* Should we reject member @name due to policy?
*
* @special_features is the member's special features encoded as a
* bitset of QapiSpecialFeature.
*
* @name must not be NULL. This function is only useful between
* visit_start_struct() and visit_end_struct(), since only objects
* have deprecated members.
*/
bool visit_deprecated_accept(Visitor *v, const char *name, Error **errp);
bool visit_policy_reject(Visitor *v, const char *name,
unsigned special_features, Error **errp);
/*
* Should we visit deprecated member @name?
*
* Should we skip member @name due to policy?
*
* @special_features is the member's special features encoded as a
* bitset of QapiSpecialFeature.
*
* @name must not be NULL. This function is only useful between
* visit_start_struct() and visit_end_struct(), since only objects
* have deprecated members.
*/
bool visit_deprecated(Visitor *v, const char *name);
bool visit_policy_skip(Visitor *v, const char *name,
unsigned special_features);
/*
* Set policy for handling deprecated management interfaces.

View File

@ -230,12 +230,13 @@ static void monitor_init_qmp_commands(void)
qmp_init_marshal(&qmp_commands);
qmp_register_command(&qmp_commands, "device_add", qmp_device_add,
QCO_NO_OPTIONS);
qmp_register_command(&qmp_commands, "device_add",
qmp_device_add, 0, 0);
QTAILQ_INIT(&qmp_cap_negotiation_commands);
qmp_register_command(&qmp_cap_negotiation_commands, "qmp_capabilities",
qmp_marshal_qmp_capabilities, QCO_ALLOW_PRECONFIG);
qmp_marshal_qmp_capabilities,
QCO_ALLOW_PRECONFIG, 0);
}
/* Set the current CPU defined by the user. Callers must hold BQL. */

View File

@ -1438,6 +1438,9 @@
#
# @x-perf: Performance options. (Since 6.0)
#
# Features:
# @unstable: Member @x-perf is experimental.
#
# Note: @on-source-error and @on-target-error only affect background
# I/O. If an error occurs during a guest write request, the device's
# rerror/werror actions will be used.
@ -1452,7 +1455,9 @@
'*on-source-error': 'BlockdevOnError',
'*on-target-error': 'BlockdevOnError',
'*auto-finalize': 'bool', '*auto-dismiss': 'bool',
'*filter-node-name': 'str', '*x-perf': 'BackupPerf' } }
'*filter-node-name': 'str',
'*x-perf': { 'type': 'BackupPerf',
'features': [ 'unstable' ] } } }
##
# @DriveBackup:
@ -1916,9 +1921,13 @@
#
# Get the block graph.
#
# Features:
# @unstable: This command is meant for debugging.
#
# Since: 4.0
##
{ 'command': 'x-debug-query-block-graph', 'returns': 'XDbgBlockGraph' }
{ 'command': 'x-debug-query-block-graph', 'returns': 'XDbgBlockGraph',
'features': [ 'unstable' ] }
##
# @drive-mirror:
@ -2257,6 +2266,9 @@
#
# Get bitmap SHA256.
#
# Features:
# @unstable: This command is meant for debugging.
#
# Returns: - BlockDirtyBitmapSha256 on success
# - If @node is not a valid block device, DeviceNotFound
# - If @name is not found or if hashing has failed, GenericError with an
@ -2265,7 +2277,8 @@
# Since: 2.10
##
{ 'command': 'x-debug-block-dirty-bitmap-sha256',
'data': 'BlockDirtyBitmap', 'returns': 'BlockDirtyBitmapSha256' }
'data': 'BlockDirtyBitmap', 'returns': 'BlockDirtyBitmapSha256',
'features': [ 'unstable' ] }
##
# @blockdev-mirror:
@ -2495,27 +2508,57 @@
#
# Properties for throttle-group objects.
#
# The options starting with x- are aliases for the same key without x- in
# the @limits object. As indicated by the x- prefix, this is not a stable
# interface and may be removed or changed incompatibly in the future. Use
# @limits for a supported stable interface.
#
# @limits: limits to apply for this throttle group
#
# Features:
# @unstable: All members starting with x- are aliases for the same key
# without x- in the @limits object. This is not a stable
# interface and may be removed or changed incompatibly in
# the future. Use @limits for a supported stable
# interface.
#
# Since: 2.11
##
{ 'struct': 'ThrottleGroupProperties',
'data': { '*limits': 'ThrottleLimits',
'*x-iops-total' : 'int', '*x-iops-total-max' : 'int',
'*x-iops-total-max-length' : 'int', '*x-iops-read' : 'int',
'*x-iops-read-max' : 'int', '*x-iops-read-max-length' : 'int',
'*x-iops-write' : 'int', '*x-iops-write-max' : 'int',
'*x-iops-write-max-length' : 'int', '*x-bps-total' : 'int',
'*x-bps-total-max' : 'int', '*x-bps-total-max-length' : 'int',
'*x-bps-read' : 'int', '*x-bps-read-max' : 'int',
'*x-bps-read-max-length' : 'int', '*x-bps-write' : 'int',
'*x-bps-write-max' : 'int', '*x-bps-write-max-length' : 'int',
'*x-iops-size' : 'int' } }
'*x-iops-total': { 'type': 'int',
'features': [ 'unstable' ] },
'*x-iops-total-max': { 'type': 'int',
'features': [ 'unstable' ] },
'*x-iops-total-max-length': { 'type': 'int',
'features': [ 'unstable' ] },
'*x-iops-read': { 'type': 'int',
'features': [ 'unstable' ] },
'*x-iops-read-max': { 'type': 'int',
'features': [ 'unstable' ] },
'*x-iops-read-max-length': { 'type': 'int',
'features': [ 'unstable' ] },
'*x-iops-write': { 'type': 'int',
'features': [ 'unstable' ] },
'*x-iops-write-max': { 'type': 'int',
'features': [ 'unstable' ] },
'*x-iops-write-max-length': { 'type': 'int',
'features': [ 'unstable' ] },
'*x-bps-total': { 'type': 'int',
'features': [ 'unstable' ] },
'*x-bps-total-max': { 'type': 'int',
'features': [ 'unstable' ] },
'*x-bps-total-max-length': { 'type': 'int',
'features': [ 'unstable' ] },
'*x-bps-read': { 'type': 'int',
'features': [ 'unstable' ] },
'*x-bps-read-max': { 'type': 'int',
'features': [ 'unstable' ] },
'*x-bps-read-max-length': { 'type': 'int',
'features': [ 'unstable' ] },
'*x-bps-write': { 'type': 'int',
'features': [ 'unstable' ] },
'*x-bps-write-max': { 'type': 'int',
'features': [ 'unstable' ] },
'*x-bps-write-max-length': { 'type': 'int',
'features': [ 'unstable' ] },
'*x-iops-size': { 'type': 'int',
'features': [ 'unstable' ] } } }
##
# @block-stream:
@ -2916,6 +2959,7 @@
# read-only when the last writer is detached. This
# allows giving QEMU write permissions only on demand
# when an operation actually needs write access.
# @unstable: Member x-check-cache-dropped is meant for debugging.
#
# Since: 2.9
##
@ -2926,7 +2970,8 @@
'*aio': 'BlockdevAioOptions',
'*drop-cache': {'type': 'bool',
'if': 'CONFIG_LINUX'},
'*x-check-cache-dropped': 'bool' },
'*x-check-cache-dropped': { 'type': 'bool',
'features': [ 'unstable' ] } },
'features': [ { 'name': 'dynamic-auto-read-only',
'if': 'CONFIG_POSIX' } ] }
@ -4041,13 +4086,16 @@
# future requests before a successful reconnect will
# immediately fail. Default 0 (Since 4.2)
#
# Features:
# @unstable: Member @x-dirty-bitmap is experimental.
#
# Since: 2.9
##
{ 'struct': 'BlockdevOptionsNbd',
'data': { 'server': 'SocketAddress',
'*export': 'str',
'*tls-creds': 'str',
'*x-dirty-bitmap': 'str',
'*x-dirty-bitmap': { 'type': 'str', 'features': [ 'unstable' ] },
'*reconnect-delay': 'uint32' } }
##
@ -4865,13 +4913,17 @@
# and replacement of an active keyslot
# (possible loss of data if IO error happens)
#
# Features:
# @unstable: This command is experimental.
#
# Since: 5.1
##
{ 'command': 'x-blockdev-amend',
'data': { 'job-id': 'str',
'node-name': 'str',
'options': 'BlockdevAmendOptions',
'*force': 'bool' } }
'*force': 'bool' },
'features': [ 'unstable' ] }
##
# @BlockErrorAction:
@ -5242,16 +5294,18 @@
#
# @node: the name of the node that will be added.
#
# Note: this command is experimental, and its API is not stable. It
# does not support all kinds of operations, all kinds of children, nor
# all block drivers.
# Features:
# @unstable: This command is experimental, and its API is not stable. It
# does not support all kinds of operations, all kinds of
# children, nor all block drivers.
#
# FIXME Removing children from a quorum node means introducing gaps in the
# child indices. This cannot be represented in the 'children' list of
# BlockdevOptionsQuorum, as returned by .bdrv_refresh_filename().
# FIXME Removing children from a quorum node means introducing
# gaps in the child indices. This cannot be represented in the
# 'children' list of BlockdevOptionsQuorum, as returned by
# .bdrv_refresh_filename().
#
# Warning: The data in a new quorum child MUST be consistent with that of
# the rest of the array.
# Warning: The data in a new quorum child MUST be consistent
# with that of the rest of the array.
#
# Since: 2.7
#
@ -5280,7 +5334,8 @@
{ 'command': 'x-blockdev-change',
'data' : { 'parent': 'str',
'*child': 'str',
'*node': 'str' } }
'*node': 'str' },
'features': [ 'unstable' ] }
##
# @x-blockdev-set-iothread:
@ -5297,8 +5352,9 @@
# @force: true if the node and its children should be moved when a BlockBackend
# is already attached
#
# Note: this command is experimental and intended for test cases that need
# control over IOThreads only.
# Features:
# @unstable: This command is experimental and intended for test cases that
# need control over IOThreads only.
#
# Since: 2.12
#
@ -5320,7 +5376,8 @@
{ 'command': 'x-blockdev-set-iothread',
'data' : { 'node-name': 'str',
'iothread': 'StrOrNull',
'*force': 'bool' } }
'*force': 'bool' },
'features': [ 'unstable' ] }
##
# @QuorumOpType:

View File

@ -47,9 +47,15 @@
#
# @deprecated-input: how to handle deprecated input (default 'accept')
# @deprecated-output: how to handle deprecated output (default 'accept')
# @unstable-input: how to handle unstable input (default 'accept')
# (since 6.2)
# @unstable-output: how to handle unstable output (default 'accept')
# (since 6.2)
#
# Since: 6.0
##
{ 'struct': 'CompatPolicy',
'data': { '*deprecated-input': 'CompatPolicyInput',
'*deprecated-output': 'CompatPolicyOutput' } }
'*deprecated-output': 'CompatPolicyOutput',
'*unstable-input': 'CompatPolicyInput',
'*unstable-output': 'CompatPolicyOutput' } }

View File

@ -452,14 +452,20 @@
# procedure starts. The VM RAM is saved with running VM.
# (since 6.0)
#
# Features:
# @unstable: Members @x-colo and @x-ignore-shared are experimental.
#
# Since: 1.2
##
{ 'enum': 'MigrationCapability',
'data': ['xbzrle', 'rdma-pin-all', 'auto-converge', 'zero-blocks',
'compress', 'events', 'postcopy-ram', 'x-colo', 'release-ram',
'compress', 'events', 'postcopy-ram',
{ 'name': 'x-colo', 'features': [ 'unstable' ] },
'release-ram',
'block', 'return-path', 'pause-before-switchover', 'multifd',
'dirty-bitmaps', 'postcopy-blocktime', 'late-block-activate',
'x-ignore-shared', 'validate-uuid', 'background-snapshot'] }
{ 'name': 'x-ignore-shared', 'features': [ 'unstable' ] },
'validate-uuid', 'background-snapshot'] }
##
# @MigrationCapabilityStatus:
@ -743,6 +749,9 @@
# block device name if there is one, and to their node name
# otherwise. (Since 5.2)
#
# Features:
# @unstable: Member @x-checkpoint-delay is experimental.
#
# Since: 2.4
##
{ 'enum': 'MigrationParameter',
@ -753,7 +762,9 @@
'cpu-throttle-initial', 'cpu-throttle-increment',
'cpu-throttle-tailslow',
'tls-creds', 'tls-hostname', 'tls-authz', 'max-bandwidth',
'downtime-limit', 'x-checkpoint-delay', 'block-incremental',
'downtime-limit',
{ 'name': 'x-checkpoint-delay', 'features': [ 'unstable' ] },
'block-incremental',
'multifd-channels',
'xbzrle-cache-size', 'max-postcopy-bandwidth',
'max-cpu-throttle', 'multifd-compression',
@ -903,6 +914,9 @@
# block device name if there is one, and to their node name
# otherwise. (Since 5.2)
#
# Features:
# @unstable: Member @x-checkpoint-delay is experimental.
#
# Since: 2.4
##
# TODO either fuse back into MigrationParameters, or make
@ -925,7 +939,8 @@
'*tls-authz': 'StrOrNull',
'*max-bandwidth': 'size',
'*downtime-limit': 'uint64',
'*x-checkpoint-delay': 'uint32',
'*x-checkpoint-delay': { 'type': 'uint32',
'features': [ 'unstable' ] },
'*block-incremental': 'bool',
'*multifd-channels': 'uint8',
'*xbzrle-cache-size': 'size',
@ -1099,6 +1114,9 @@
# block device name if there is one, and to their node name
# otherwise. (Since 5.2)
#
# Features:
# @unstable: Member @x-checkpoint-delay is experimental.
#
# Since: 2.4
##
{ 'struct': 'MigrationParameters',
@ -1119,7 +1137,8 @@
'*tls-authz': 'str',
'*max-bandwidth': 'size',
'*downtime-limit': 'uint64',
'*x-checkpoint-delay': 'uint32',
'*x-checkpoint-delay': { 'type': 'uint32',
'features': [ 'unstable' ] },
'*block-incremental': 'bool',
'*multifd-channels': 'uint8',
'*xbzrle-cache-size': 'size',
@ -1351,6 +1370,9 @@
# If sent to the Secondary, the Secondary side will run failover work,
# then takes over server operation to become the service VM.
#
# Features:
# @unstable: This command is experimental.
#
# Since: 2.8
#
# Example:
@ -1359,7 +1381,8 @@
# <- { "return": {} }
#
##
{ 'command': 'x-colo-lost-heartbeat' }
{ 'command': 'x-colo-lost-heartbeat',
'features': [ 'unstable' ] }
##
# @migrate_cancel:

View File

@ -185,6 +185,9 @@
# available during the preconfig state (i.e. when the --preconfig command
# line option was in use).
#
# Features:
# @unstable: This command is experimental.
#
# Since 3.0
#
# Returns: nothing
@ -195,7 +198,8 @@
# <- { "return": {} }
#
##
{ 'command': 'x-exit-preconfig', 'allow-preconfig': true }
{ 'command': 'x-exit-preconfig', 'allow-preconfig': true,
'features': [ 'unstable' ] }
##
# @human-monitor-command:

View File

@ -246,25 +246,27 @@ static void forward_field_optional(Visitor *v, const char *name, bool *present)
visit_optional(ffv->target, name, present);
}
static bool forward_field_deprecated_accept(Visitor *v, const char *name,
Error **errp)
static bool forward_field_policy_reject(Visitor *v, const char *name,
unsigned special_features,
Error **errp)
{
ForwardFieldVisitor *ffv = to_ffv(v);
if (!forward_field_translate_name(ffv, &name, errp)) {
return false;
return true;
}
return visit_deprecated_accept(ffv->target, name, errp);
return visit_policy_reject(ffv->target, name, special_features, errp);
}
static bool forward_field_deprecated(Visitor *v, const char *name)
static bool forward_field_policy_skip(Visitor *v, const char *name,
unsigned special_features)
{
ForwardFieldVisitor *ffv = to_ffv(v);
if (!forward_field_translate_name(ffv, &name, NULL)) {
return false;
return true;
}
return visit_deprecated(ffv->target, name);
return visit_policy_skip(ffv->target, name, special_features);
}
static void forward_field_complete(Visitor *v, void *opaque)
@ -313,8 +315,8 @@ Visitor *visitor_forward_field(Visitor *target, const char *from, const char *to
v->visitor.type_any = forward_field_type_any;
v->visitor.type_null = forward_field_type_null;
v->visitor.optional = forward_field_optional;
v->visitor.deprecated_accept = forward_field_deprecated_accept;
v->visitor.deprecated = forward_field_deprecated;
v->visitor.policy_reject = forward_field_policy_reject;
v->visitor.policy_skip = forward_field_policy_skip;
v->visitor.complete = forward_field_complete;
v->visitor.free = forward_field_free;

View File

@ -11,10 +11,53 @@
*/
#include "qemu/osdep.h"
#include "qapi/compat-policy.h"
#include "qapi/error.h"
#include "qemu/ctype.h"
#include "qapi/qmp/qerror.h"
CompatPolicy compat_policy;
static bool compat_policy_input_ok1(const char *adjective,
CompatPolicyInput policy,
ErrorClass error_class,
const char *kind, const char *name,
Error **errp)
{
switch (policy) {
case COMPAT_POLICY_INPUT_ACCEPT:
return true;
case COMPAT_POLICY_INPUT_REJECT:
error_set(errp, error_class, "%s %s %s disabled by policy",
adjective, kind, name);
return false;
case COMPAT_POLICY_INPUT_CRASH:
default:
abort();
}
}
bool compat_policy_input_ok(unsigned special_features,
const CompatPolicy *policy,
ErrorClass error_class,
const char *kind, const char *name,
Error **errp)
{
if ((special_features & 1u << QAPI_DEPRECATED)
&& !compat_policy_input_ok1("Deprecated",
policy->deprecated_input,
error_class, kind, name, errp)) {
return false;
}
if ((special_features & (1u << QAPI_UNSTABLE))
&& !compat_policy_input_ok1("Unstable",
policy->unstable_input,
error_class, kind, name, errp)) {
return false;
}
return true;
}
const char *qapi_enum_lookup(const QEnumLookup *lookup, int val)
{
assert(val >= 0 && val < lookup->size);

View File

@ -13,6 +13,7 @@
*/
#include "qemu/osdep.h"
#include "qapi/compat-policy.h"
#include "qapi/error.h"
#include "qapi/qmp/qerror.h"
#include "qapi/visitor.h"
@ -139,22 +140,24 @@ bool visit_optional(Visitor *v, const char *name, bool *present)
return *present;
}
bool visit_deprecated_accept(Visitor *v, const char *name, Error **errp)
bool visit_policy_reject(Visitor *v, const char *name,
unsigned special_features, Error **errp)
{
trace_visit_deprecated_accept(v, name);
if (v->deprecated_accept) {
return v->deprecated_accept(v, name, errp);
trace_visit_policy_reject(v, name);
if (v->policy_reject) {
return v->policy_reject(v, name, special_features, errp);
}
return true;
return false;
}
bool visit_deprecated(Visitor *v, const char *name)
bool visit_policy_skip(Visitor *v, const char *name,
unsigned special_features)
{
trace_visit_deprecated(v, name);
if (v->deprecated) {
return v->deprecated(v, name);
trace_visit_policy_skip(v, name);
if (v->policy_skip) {
return v->policy_skip(v, name, special_features);
}
return true;
return false;
}
void visit_set_policy(Visitor *v, CompatPolicy *policy)
@ -406,18 +409,12 @@ static bool input_type_enum(Visitor *v, const char *name, int *obj,
return false;
}
if (lookup->flags && (lookup->flags[value] & QAPI_ENUM_DEPRECATED)) {
switch (v->compat_policy.deprecated_input) {
case COMPAT_POLICY_INPUT_ACCEPT:
break;
case COMPAT_POLICY_INPUT_REJECT:
error_setg(errp, "Deprecated value '%s' disabled by policy",
enum_str);
return false;
case COMPAT_POLICY_INPUT_CRASH:
default:
abort();
}
if (lookup->special_features
&& !compat_policy_input_ok(lookup->special_features[value],
&v->compat_policy,
ERROR_CLASS_GENERIC_ERROR,
"value", enum_str, errp)) {
return false;
}
*obj = value;

View File

@ -26,8 +26,6 @@
#include "qemu/coroutine.h"
#include "qemu/main-loop.h"
CompatPolicy compat_policy;
Visitor *qobject_input_visitor_new_qmp(QObject *obj)
{
Visitor *v = qobject_input_visitor_new(obj);
@ -176,19 +174,10 @@ QDict *qmp_dispatch(const QmpCommandList *cmds, QObject *request,
"The command %s has not been found", command);
goto out;
}
if (cmd->options & QCO_DEPRECATED) {
switch (compat_policy.deprecated_input) {
case COMPAT_POLICY_INPUT_ACCEPT:
break;
case COMPAT_POLICY_INPUT_REJECT:
error_set(&err, ERROR_CLASS_COMMAND_NOT_FOUND,
"Deprecated command %s disabled by policy",
command);
goto out;
case COMPAT_POLICY_INPUT_CRASH:
default:
abort();
}
if (!compat_policy_input_ok(cmd->special_features, &compat_policy,
ERROR_CLASS_COMMAND_NOT_FOUND,
"command", command, &err)) {
goto out;
}
if (!cmd->enabled) {
error_set(&err, ERROR_CLASS_COMMAND_NOT_FOUND,

View File

@ -16,7 +16,8 @@
#include "qapi/qmp/dispatch.h"
void qmp_register_command(QmpCommandList *cmds, const char *name,
QmpCommandFunc *fn, QmpCommandOptions options)
QmpCommandFunc *fn, QmpCommandOptions options,
unsigned special_features)
{
QmpCommand *cmd = g_malloc0(sizeof(*cmd));
@ -27,6 +28,7 @@ void qmp_register_command(QmpCommandList *cmds, const char *name,
cmd->fn = fn;
cmd->enabled = true;
cmd->options = options;
cmd->special_features = special_features;
QTAILQ_INSERT_TAIL(cmds, cmd, node);
}

View File

@ -14,6 +14,7 @@
#include "qemu/osdep.h"
#include <math.h>
#include "qapi/compat-policy.h"
#include "qapi/error.h"
#include "qapi/qobject-input-visitor.h"
#include "qapi/visitor-impl.h"
@ -662,20 +663,13 @@ static void qobject_input_optional(Visitor *v, const char *name, bool *present)
*present = true;
}
static bool qobject_input_deprecated_accept(Visitor *v, const char *name,
Error **errp)
static bool qobject_input_policy_reject(Visitor *v, const char *name,
unsigned special_features,
Error **errp)
{
switch (v->compat_policy.deprecated_input) {
case COMPAT_POLICY_INPUT_ACCEPT:
return true;
case COMPAT_POLICY_INPUT_REJECT:
error_setg(errp, "Deprecated parameter '%s' disabled by policy",
name);
return false;
case COMPAT_POLICY_INPUT_CRASH:
default:
abort();
}
return !compat_policy_input_ok(special_features, &v->compat_policy,
ERROR_CLASS_GENERIC_ERROR,
"parameter", name, errp);
}
static void qobject_input_free(Visitor *v)
@ -712,7 +706,7 @@ static QObjectInputVisitor *qobject_input_visitor_base_new(QObject *obj)
v->visitor.end_list = qobject_input_end_list;
v->visitor.start_alternate = qobject_input_start_alternate;
v->visitor.optional = qobject_input_optional;
v->visitor.deprecated_accept = qobject_input_deprecated_accept;
v->visitor.policy_reject = qobject_input_policy_reject;
v->visitor.free = qobject_input_free;
v->root = qobject_ref(obj);

View File

@ -13,6 +13,7 @@
*/
#include "qemu/osdep.h"
#include "qapi/compat-policy.h"
#include "qapi/qobject-output-visitor.h"
#include "qapi/visitor-impl.h"
#include "qemu/queue.h"
@ -208,9 +209,15 @@ static bool qobject_output_type_null(Visitor *v, const char *name,
return true;
}
static bool qobject_output_deprecated(Visitor *v, const char *name)
static bool qobject_output_policy_skip(Visitor *v, const char *name,
unsigned special_features)
{
return v->compat_policy.deprecated_output != COMPAT_POLICY_OUTPUT_HIDE;
CompatPolicy *pol = &v->compat_policy;
return ((special_features & 1u << QAPI_DEPRECATED)
&& pol->deprecated_output == COMPAT_POLICY_OUTPUT_HIDE)
|| ((special_features & 1u << QAPI_UNSTABLE)
&& pol->unstable_output == COMPAT_POLICY_OUTPUT_HIDE);
}
/* Finish building, and return the root object.
@ -262,7 +269,7 @@ Visitor *qobject_output_visitor_new(QObject **result)
v->visitor.type_number = qobject_output_type_number;
v->visitor.type_any = qobject_output_type_any;
v->visitor.type_null = qobject_output_type_null;
v->visitor.deprecated = qobject_output_deprecated;
v->visitor.policy_skip = qobject_output_policy_skip;
v->visitor.complete = qobject_output_complete;
v->visitor.free = qobject_output_free;

View File

@ -559,10 +559,8 @@
# for ramblock-id. Disable this for 4.0
# machine types or older to allow
# migration with newer QEMU versions.
# This option is considered stable
# despite the x- prefix. (default:
# false generally, but true for machine
# types <= 4.0)
# (default: false generally,
# but true for machine types <= 4.0)
#
# Note: prealloc=true and reserve=false cannot be set at the same time. With
# reserve=true, the behavior depends on the operating system: for example,
@ -785,6 +783,9 @@
##
# @ObjectType:
#
# Features:
# @unstable: Member @x-remote-object is experimental.
#
# Since: 6.0
##
{ 'enum': 'ObjectType',
@ -836,7 +837,7 @@
'tls-creds-psk',
'tls-creds-x509',
'tls-cipher-suites',
'x-remote-object'
{ 'name': 'x-remote-object', 'features': [ 'unstable' ] }
] }
##

View File

@ -17,8 +17,8 @@ visit_start_alternate(void *v, const char *name, void *obj, size_t size) "v=%p n
visit_end_alternate(void *v, void *obj) "v=%p obj=%p"
visit_optional(void *v, const char *name, bool *present) "v=%p name=%s present=%p"
visit_deprecated_accept(void *v, const char *name) "v=%p name=%s"
visit_deprecated(void *v, const char *name) "v=%p name=%s"
visit_policy_reject(void *v, const char *name) "v=%p name=%s"
visit_policy_skip(void *v, const char *name) "v=%p name=%s"
visit_type_enum(void *v, const char *name, int *obj) "v=%p name=%s obj=%p"
visit_type_int(void *v, const char *name, int64_t *obj) "v=%p name=%s obj=%p"

View File

@ -3641,7 +3641,9 @@ DEFHEADING(Debug/Expert options:)
DEF("compat", HAS_ARG, QEMU_OPTION_compat,
"-compat [deprecated-input=accept|reject|crash][,deprecated-output=accept|hide]\n"
" Policy for handling deprecated management interfaces\n",
" Policy for handling deprecated management interfaces\n"
"-compat [unstable-input=accept|reject|crash][,unstable-output=accept|hide]\n"
" Policy for handling unstable management interfaces\n",
QEMU_ARCH_ALL)
SRST
``-compat [deprecated-input=@var{input-policy}][,deprecated-output=@var{output-policy}]``
@ -3659,6 +3661,22 @@ SRST
Suppress deprecated command results and events
Limitation: covers only syntactic aspects of QMP.
``-compat [unstable-input=@var{input-policy}][,unstable-output=@var{output-policy}]``
Set policy for handling unstable management interfaces (experimental):
``unstable-input=accept`` (default)
Accept unstable commands and arguments
``unstable-input=reject``
Reject unstable commands and arguments
``unstable-input=crash``
Crash on unstable commands and arguments
``unstable-output=accept`` (default)
Emit unstable command results and events
``unstable-output=hide``
Suppress unstable command results and events
Limitation: covers only syntactic aspects of QMP.
ERST
DEF("fw_cfg", HAS_ARG, QEMU_OPTION_fwcfg,

View File

@ -26,6 +26,7 @@ from .gen import (
QAPISchemaModularCVisitor,
build_params,
ifcontext,
gen_special_features,
)
from .schema import (
QAPISchema,
@ -217,9 +218,6 @@ def gen_register_command(name: str,
coroutine: bool) -> str:
options = []
if 'deprecated' in [f.name for f in features]:
options += ['QCO_DEPRECATED']
if not success_response:
options += ['QCO_NO_SUCCESS_RESP']
if allow_oob:
@ -229,15 +227,13 @@ def gen_register_command(name: str,
if coroutine:
options += ['QCO_COROUTINE']
if not options:
options = ['QCO_NO_OPTIONS']
ret = mcgen('''
qmp_register_command(cmds, "%(name)s",
qmp_marshal_%(c_name)s, %(opts)s);
qmp_marshal_%(c_name)s, %(opts)s, %(feats)s);
''',
name=name, c_name=c_name(name),
opts=" | ".join(options))
opts=' | '.join(options) or 0,
feats=gen_special_features(features))
return ret

View File

@ -109,13 +109,15 @@ def gen_event_send(name: str,
if not boxed:
ret += gen_param_var(arg_type)
if 'deprecated' in [f.name for f in features]:
ret += mcgen('''
for f in features:
if f.is_special():
ret += mcgen('''
if (compat_policy.deprecated_output == COMPAT_POLICY_OUTPUT_HIDE) {
if (compat_policy.%(feat)s_output == COMPAT_POLICY_OUTPUT_HIDE) {
return;
}
''')
''',
feat=f.name)
ret += mcgen('''

View File

@ -18,6 +18,7 @@ from typing import (
Dict,
Iterator,
Optional,
Sequence,
Tuple,
)
@ -29,6 +30,7 @@ from .common import (
mcgen,
)
from .schema import (
QAPISchemaFeature,
QAPISchemaIfCond,
QAPISchemaModule,
QAPISchemaObjectType,
@ -37,6 +39,12 @@ from .schema import (
from .source import QAPISourceInfo
def gen_special_features(features: Sequence[QAPISchemaFeature]) -> str:
special_features = [f"1u << QAPI_{feat.name.upper()}"
for feat in features if feat.is_special()]
return ' | '.join(special_features) or '0'
class QAPIGen:
def __init__(self, fname: str):
self.fname = fname

View File

@ -254,9 +254,11 @@ class QAPISchemaType(QAPISchemaEntity):
def check(self, schema):
QAPISchemaEntity.check(self, schema)
if 'deprecated' in [f.name for f in self.features]:
raise QAPISemError(
self.info, "feature 'deprecated' is not supported for types")
for feat in self.features:
if feat.is_special():
raise QAPISemError(
self.info,
f"feature '{feat.name}' is not supported for types")
def describe(self):
assert self.meta
@ -725,6 +727,9 @@ class QAPISchemaEnumMember(QAPISchemaMember):
class QAPISchemaFeature(QAPISchemaMember):
role = 'feature'
def is_special(self):
return self.name in ('deprecated', 'unstable')
class QAPISchemaObjectTypeMember(QAPISchemaMember):
def __init__(self, name, info, typ, optional, ifcond=None, features=None):

View File

@ -16,7 +16,7 @@ This work is licensed under the terms of the GNU GPL, version 2.
from typing import List, Optional
from .common import c_enum_const, c_name, mcgen
from .gen import QAPISchemaModularCVisitor, ifcontext
from .gen import QAPISchemaModularCVisitor, gen_special_features, ifcontext
from .schema import (
QAPISchema,
QAPISchemaEnumMember,
@ -39,7 +39,7 @@ def gen_enum_lookup(name: str,
members: List[QAPISchemaEnumMember],
prefix: Optional[str] = None) -> str:
max_index = c_enum_const(name, '_MAX', prefix)
flags = ''
feats = ''
ret = mcgen('''
const QEnumLookup %(c_name)s_lookup = {
@ -54,19 +54,21 @@ const QEnumLookup %(c_name)s_lookup = {
''',
index=index, name=memb.name)
ret += memb.ifcond.gen_endif()
if 'deprecated' in (f.name for f in memb.features):
flags += mcgen('''
[%(index)s] = QAPI_ENUM_DEPRECATED,
''',
index=index)
if flags:
special_features = gen_special_features(memb.features)
if special_features != '0':
feats += mcgen('''
[%(index)s] = %(special_features)s,
''',
index=index, special_features=special_features)
if feats:
ret += mcgen('''
},
.flags = (const unsigned char[%(max_index)s]) {
.special_features = (const unsigned char[%(max_index)s]) {
''',
max_index=max_index)
ret += flags
ret += feats
ret += mcgen('''
},

View File

@ -21,7 +21,7 @@ from .common import (
indent,
mcgen,
)
from .gen import QAPISchemaModularCVisitor, ifcontext
from .gen import QAPISchemaModularCVisitor, gen_special_features, ifcontext
from .schema import (
QAPISchema,
QAPISchemaEnumMember,
@ -76,7 +76,6 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
c_type=base.c_name())
for memb in members:
deprecated = 'deprecated' in [f.name for f in memb.features]
ret += memb.ifcond.gen_if()
if memb.optional:
ret += mcgen('''
@ -84,14 +83,15 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
''',
name=memb.name, c_name=c_name(memb.name))
indent.increase()
if deprecated:
special_features = gen_special_features(memb.features)
if special_features != '0':
ret += mcgen('''
if (!visit_deprecated_accept(v, "%(name)s", errp)) {
if (visit_policy_reject(v, "%(name)s", %(special_features)s, errp)) {
return false;
}
if (visit_deprecated(v, "%(name)s")) {
if (!visit_policy_skip(v, "%(name)s", %(special_features)s)) {
''',
name=memb.name)
name=memb.name, special_features=special_features)
indent.increase()
ret += mcgen('''
if (!visit_type_%(c_type)s(v, "%(name)s", &obj->%(c_name)s, errp)) {
@ -100,7 +100,7 @@ bool visit_type_%(c_name)s_members(Visitor *v, %(c_name)s *obj, Error **errp)
''',
c_type=memb.type.c_name(), name=memb.name,
c_name=c_name(memb.name))
if deprecated:
if special_features != '0':
indent.decrease()
ret += mcgen('''
}

View File

@ -146,7 +146,8 @@ static void init_qmp_commands(void)
QTAILQ_INIT(&qmp_cap_negotiation_commands);
qmp_register_command(&qmp_cap_negotiation_commands, "qmp_capabilities",
qmp_marshal_qmp_capabilities, QCO_ALLOW_PRECONFIG);
qmp_marshal_qmp_capabilities,
QCO_ALLOW_PRECONFIG, 0);
}
static int getopt_set_loc(int argc, char **argv, const char *optstring,

View File

@ -273,7 +273,7 @@
'data': { 'foo': { 'type': 'int', 'features': [ 'deprecated' ] } },
'features': [ 'feature1' ] }
{ 'struct': 'FeatureStruct2',
'data': { 'foo': 'int' },
'data': { 'foo': { 'type': 'int', 'features': [ 'unstable' ] } },
'features': [ { 'name': 'feature1' } ] }
{ 'struct': 'FeatureStruct3',
'data': { 'foo': 'int' },
@ -331,7 +331,7 @@
{ 'command': 'test-command-features1',
'features': [ 'deprecated' ] }
{ 'command': 'test-command-features3',
'features': [ 'feature1', 'feature2' ] }
'features': [ 'unstable', 'feature1', 'feature2' ] }
{ 'command': 'test-command-cond-features1',
'features': [ { 'name': 'feature1', 'if': 'TEST_IF_FEATURE_1'} ] }
@ -348,3 +348,6 @@
{ 'event': 'TEST_EVENT_FEATURES1',
'features': [ 'deprecated' ] }
{ 'event': 'TEST_EVENT_FEATURES2',
'features': [ 'unstable' ] }

View File

@ -308,6 +308,7 @@ object FeatureStruct1
feature feature1
object FeatureStruct2
member foo: int optional=False
feature unstable
feature feature1
object FeatureStruct3
member foo: int optional=False
@ -373,6 +374,7 @@ command test-command-features1 None -> None
feature deprecated
command test-command-features3 None -> None
gen=True success_response=True boxed=False oob=False preconfig=False
feature unstable
feature feature1
feature feature2
command test-command-cond-features1 None -> None
@ -394,6 +396,9 @@ event TEST_EVENT_FEATURES0 FeatureStruct1
event TEST_EVENT_FEATURES1 None
boxed=False
feature deprecated
event TEST_EVENT_FEATURES2 None
boxed=False
feature unstable
module include/sub-module.json
include sub-sub-module.json
object SecondArrayRef