437 changes: 248 additions & 189 deletions qapi/ui.json

Large diffs are not rendered by default.

84 changes: 30 additions & 54 deletions qapi/virtio.json
Expand Up @@ -16,7 +16,6 @@
# @name: Name of the VirtIODevice
#
# Since: 7.2
#
##
{ 'struct': 'VirtioInfo',
'data': { 'path': 'str',
Expand All @@ -28,6 +27,7 @@
# Returns a list of all realized VirtIODevices
#
# Features:
#
# @unstable: This command is meant for debugging.
#
# Returns: List of gathered VirtIODevices
Expand Down Expand Up @@ -60,17 +60,15 @@
# }
# ]
# }
#
##

{ 'command': 'x-query-virtio',
'returns': [ 'VirtioInfo' ],
'features': [ 'unstable' ] }

##
# @VhostStatus:
#
# Information about a vhost device. This information will only be
# Information about a vhost device. This information will only be
# displayed if the vhost device is active.
#
# @n-mem-sections: vhost_dev n_mem_sections
Expand Down Expand Up @@ -98,9 +96,7 @@
# @log-size: vhost_dev log_size
#
# Since: 7.2
#
##

{ 'struct': 'VhostStatus',
'data': { 'n-mem-sections': 'int',
'n-tmp-sections': 'int',
Expand All @@ -119,8 +115,8 @@
# @VirtioStatus:
#
# Full status of the virtio device with most VirtIODevice members.
# Also includes the full status of the corresponding vhost device
# if the vhost device is active.
# Also includes the full status of the corresponding vhost device if
# the vhost device is active.
#
# @name: VirtIODevice name
#
Expand All @@ -136,8 +132,8 @@
#
# @device-endian: VirtIODevice device_endian
#
# @num-vqs: VirtIODevice virtqueue count. This is the number of active
# virtqueues being used by the VirtIODevice.
# @num-vqs: VirtIODevice virtqueue count. This is the number of
# active virtqueues being used by the VirtIODevice.
#
# @status: VirtIODevice configuration status (VirtioDeviceStatus)
#
Expand All @@ -163,14 +159,12 @@
#
# @use-guest-notifier-mask: VirtIODevice use_guest_notifier_mask flag
#
# @vhost-dev: Corresponding vhost device info for a given VirtIODevice.
# Present if the given VirtIODevice has an active vhost
# device.
# @vhost-dev: Corresponding vhost device info for a given
# VirtIODevice. Present if the given VirtIODevice has an active
# vhost device.
#
# Since: 7.2
#
##

{ 'struct': 'VirtioStatus',
'data': { 'name': 'str',
'device-id': 'uint16',
Expand Down Expand Up @@ -202,6 +196,7 @@
# @path: Canonical QOM path of the VirtIODevice
#
# Features:
#
# @unstable: This command is meant for debugging.
#
# Returns: VirtioStatus of the virtio device
Expand Down Expand Up @@ -433,9 +428,7 @@
# "use-started": true
# }
# }
#
##

{ 'command': 'x-query-virtio-status',
'data': { 'path': 'str' },
'returns': 'VirtioStatus',
Expand All @@ -448,13 +441,13 @@
# device
#
# @statuses: List of decoded configuration statuses of the virtio
# device
# device
#
# @unknown-statuses: Virtio device statuses bitmap that have not been decoded
# @unknown-statuses: Virtio device statuses bitmap that have not been
# decoded
#
# Since: 7.2
##

{ 'struct': 'VirtioDeviceStatus',
'data': { 'statuses': [ 'str' ],
'*unknown-statuses': 'uint8' } }
Expand All @@ -466,35 +459,33 @@
# Vhost User device
#
# @protocols: List of decoded vhost user protocol features of a vhost
# user device
# user device
#
# @unknown-protocols: Vhost user device protocol features bitmap that
# have not been decoded
# have not been decoded
#
# Since: 7.2
##

{ 'struct': 'VhostDeviceProtocols',
'data': { 'protocols': [ 'str' ],
'*unknown-protocols': 'uint64' } }

##
# @VirtioDeviceFeatures:
#
# The common fields that apply to most Virtio devices. Some devices
# The common fields that apply to most Virtio devices. Some devices
# may not have their own device-specific features (e.g. virtio-rng).
#
# @transports: List of transport features of the virtio device
#
# @dev-features: List of device-specific features (if the device has
# unique features)
# unique features)
#
# @unknown-dev-features: Virtio device features bitmap that have not
# been decoded
# been decoded
#
# Since: 7.2
##

{ 'struct': 'VirtioDeviceFeatures',
'data': { 'transports': [ 'str' ],
'*dev-features': [ 'str' ],
Expand Down Expand Up @@ -525,7 +516,7 @@
# @vring-used: VirtQueue vring.used (device area)
#
# @last-avail-idx: VirtQueue last_avail_idx or return of vhost_dev
# vhost_get_vring_base (if vhost active)
# vhost_get_vring_base (if vhost active)
#
# @shadow-avail-idx: VirtQueue shadow_avail_idx
#
Expand All @@ -536,9 +527,7 @@
# @signalled-used-valid: VirtQueue signalled_used_valid flag
#
# Since: 7.2
#
##

{ 'struct': 'VirtQueueStatus',
'data': { 'name': 'str',
'queue-index': 'uint16',
Expand All @@ -565,16 +554,17 @@
# @queue: VirtQueue index to examine
#
# Features:
#
# @unstable: This command is meant for debugging.
#
# Returns: VirtQueueStatus of the VirtQueue
#
# Notes: last_avail_idx will not be displayed in the case where
# the selected VirtIODevice has a running vhost device and
# the VirtIODevice VirtQueue index (queue) does not exist for
# the corresponding vhost device vhost_virtqueue. Also,
# shadow_avail_idx will not be displayed in the case where
# the selected VirtIODevice has a running vhost device.
# Notes: last_avail_idx will not be displayed in the case where the
# selected VirtIODevice has a running vhost device and the
# VirtIODevice VirtQueue index (queue) does not exist for the
# corresponding vhost device vhost_virtqueue. Also,
# shadow_avail_idx will not be displayed in the case where the
# selected VirtIODevice has a running vhost device.
#
# Since: 7.2
#
Expand Down Expand Up @@ -626,9 +616,7 @@
# "vring-num": 128
# }
# }
#
##

{ 'command': 'x-query-virtio-queue-status',
'data': { 'path': 'str', 'queue': 'uint16' },
'returns': 'VirtQueueStatus',
Expand Down Expand Up @@ -667,9 +655,7 @@
# @used-size: vhost_virtqueue used_size
#
# Since: 7.2
#
##

{ 'struct': 'VirtVhostQueueStatus',
'data': { 'name': 'str',
'kick': 'int',
Expand All @@ -695,6 +681,7 @@
# @queue: vhost_virtqueue index to examine
#
# Features:
#
# @unstable: This command is meant for debugging.
#
# Returns: VirtVhostQueueStatus of the vhost_virtqueue
Expand Down Expand Up @@ -748,9 +735,7 @@
# "kick": 0
# }
# }
#
##

{ 'command': 'x-query-virtio-vhost-queue-status',
'data': { 'path': 'str', 'queue': 'uint16' },
'returns': 'VirtVhostQueueStatus',
Expand All @@ -768,9 +753,7 @@
# @flags: List of descriptor flags
#
# Since: 7.2
#
##

{ 'struct': 'VirtioRingDesc',
'data': { 'addr': 'uint64',
'len': 'uint32',
Expand All @@ -788,9 +771,7 @@
# @ring: VRingAvail ring[] entry at provided index
#
# Since: 7.2
#
##

{ 'struct': 'VirtioRingAvail',
'data': { 'flags': 'uint16',
'idx': 'uint16',
Expand All @@ -806,9 +787,7 @@
# @idx: VRingUsed index
#
# Since: 7.2
#
##

{ 'struct': 'VirtioRingUsed',
'data': { 'flags': 'uint16',
'idx': 'uint16' } }
Expand All @@ -830,9 +809,7 @@
# @used: VRingUsed info
#
# Since: 7.2
#
##

{ 'struct': 'VirtioQueueElement',
'data': { 'name': 'str',
'index': 'uint32',
Expand All @@ -849,10 +826,11 @@
#
# @queue: VirtQueue index to examine
#
# @index: Index of the element in the queue
# (default: head of the queue)
# @index: Index of the element in the queue (default: head of the
# queue)
#
# Features:
#
# @unstable: This command is meant for debugging.
#
# Returns: VirtioQueueElement information
Expand Down Expand Up @@ -945,9 +923,7 @@
# }
# }
# }
#
##

{ 'command': 'x-query-virtio-queue-element',
'data': { 'path': 'str', 'queue': 'uint16', '*index': 'uint16' },
'returns': 'VirtioQueueElement',
Expand Down
42 changes: 20 additions & 22 deletions qapi/yank.json
Expand Up @@ -9,7 +9,7 @@
##
# @YankInstanceType:
#
# An enumeration of yank instance types. See @YankInstance for more
# An enumeration of yank instance types. See @YankInstance for more
# information.
#
# Since: 6.0
Expand All @@ -20,8 +20,8 @@
##
# @YankInstanceBlockNode:
#
# Specifies which block graph node to yank. See @YankInstance for more
# information.
# Specifies which block graph node to yank. See @YankInstance for
# more information.
#
# @node-name: the name of the block graph node
#
Expand All @@ -33,8 +33,8 @@
##
# @YankInstanceChardev:
#
# Specifies which character device to yank. See @YankInstance for more
# information.
# Specifies which character device to yank. See @YankInstance for
# more information.
#
# @id: the chardev's ID
#
Expand All @@ -46,21 +46,18 @@
##
# @YankInstance:
#
# A yank instance can be yanked with the @yank qmp command to recover from a
# hanging QEMU.
# A yank instance can be yanked with the @yank qmp command to recover
# from a hanging QEMU.
#
# Currently implemented yank instances:
#
# - nbd block device:
# Yanking it will shut down the connection to the nbd server without
# attempting to reconnect.
# - socket chardev:
# Yanking it will shut down the connected socket.
# - migration:
# Yanking it will shut down all migration connections. Unlike
# @migrate_cancel, it will not notify the migration process, so migration
# will go into @failed state, instead of @cancelled state. @yank should be
# used to recover from hangs.
# - nbd block device: Yanking it will shut down the connection to the
# nbd server without attempting to reconnect.
# - socket chardev: Yanking it will shut down the connected socket.
# - migration: Yanking it will shut down all migration connections.
# Unlike @migrate_cancel, it will not notify the migration process,
# so migration will go into @failed state, instead of @cancelled
# state. @yank should be used to recover from hangs.
#
# Since: 6.0
##
Expand All @@ -74,13 +71,14 @@
##
# @yank:
#
# Try to recover from hanging QEMU by yanking the specified instances. See
# @YankInstance for more information.
# Try to recover from hanging QEMU by yanking the specified instances.
# See @YankInstance for more information.
#
# Takes a list of @YankInstance as argument.
#
# Returns: - Nothing on success
# - @DeviceNotFound error, if any of the YankInstances doesn't exist
# Returns:
# - Nothing on success
# - @DeviceNotFound error, if any of the YankInstances doesn't exist
#
# Example:
#
Expand All @@ -101,7 +99,7 @@
##
# @query-yank:
#
# Query yank instances. See @YankInstance for more information.
# Query yank instances. See @YankInstance for more information.
#
# Returns: list of @YankInstance
#
Expand Down
682 changes: 389 additions & 293 deletions qga/qapi-schema.json

Large diffs are not rendered by default.

137 changes: 47 additions & 90 deletions scripts/qapi/parser.py
Expand Up @@ -346,7 +346,7 @@ def accept(self, skip_comment: bool = True) -> None:
elif not self.tok.isspace():
# Show up to next structural, whitespace or quote
# character
match = must_match('[^[\\]{}:,\\s\'"]+',
match = must_match('[^[\\]{}:,\\s\']+',
self.src[self.cursor-1:])
raise QAPIParseError(self, "stray '%s'" % match.group(0))

Expand Down Expand Up @@ -468,34 +468,39 @@ class QAPIDoc:
class Section:
# pylint: disable=too-few-public-methods
def __init__(self, parser: QAPISchemaParser,
name: Optional[str] = None, indent: int = 0):

name: Optional[str] = None):
# parser, for error messages about indentation
self._parser = parser
# optional section name (argument/member or section name)
self.name = name
# section text without section name
self.text = ''
# the expected indent level of the text of this section
self._indent = indent
# indentation to strip (None means indeterminate)
self._indent = None if self.name else 0

def append(self, line: str) -> None:
# Strip leading spaces corresponding to the expected indent level
# Blank lines are always OK.
line = line.rstrip()

if line:
indent = must_match(r'\s*', line).end()
if indent < self._indent:
if self._indent is None:
# indeterminate indentation
if self.text != '':
# non-blank, non-first line determines indentation
self._indent = indent
elif indent < self._indent:
raise QAPIParseError(
self._parser,
"unexpected de-indent (expected at least %d spaces)" %
self._indent)
line = line[self._indent:]

self.text += line.rstrip() + '\n'
self.text += line + '\n'

class ArgSection(Section):
def __init__(self, parser: QAPISchemaParser,
name: str, indent: int = 0):
super().__init__(parser, name, indent)
name: str):
super().__init__(parser, name)
self.member: Optional['QAPISchemaMember'] = None

def connect(self, member: 'QAPISchemaMember') -> None:
Expand Down Expand Up @@ -558,12 +563,12 @@ def end_comment(self) -> None:
self._switch_section(QAPIDoc.NullSection(self._parser))

@staticmethod
def _is_section_tag(name: str) -> bool:
return name in ('Returns:', 'Since:',
# those are often singular or plural
'Note:', 'Notes:',
'Example:', 'Examples:',
'TODO:')
def _match_at_name_colon(string: str) -> re.Match:
return re.match(r'@([^:]*): *', string)

@staticmethod
def _match_section_tag(string: str) -> re.Match:
return re.match(r'(Returns|Since|Notes?|Examples?|TODO): *', string)

def _append_body_line(self, line: str) -> None:
"""
Expand All @@ -579,7 +584,6 @@ def _append_body_line(self, line: str) -> None:
Else, append the line to the current section.
"""
name = line.split(' ', 1)[0]
# FIXME not nice: things like '# @foo:' and '# @foo: ' aren't
# recognized, and get silently treated as ordinary text
if not self.symbol and not self.body.text and line.startswith('@'):
Expand All @@ -593,12 +597,12 @@ def _append_body_line(self, line: str) -> None:
self._parser, "name required after '@'")
elif self.symbol:
# This is a definition documentation block
if name.startswith('@') and name.endswith(':'):
if self._match_at_name_colon(line):
self._append_line = self._append_args_line
self._append_args_line(line)
elif line == 'Features:':
self._append_line = self._append_features_line
elif self._is_section_tag(name):
elif self._match_section_tag(line):
self._append_line = self._append_various_line
self._append_various_line(line)
else:
Expand All @@ -619,25 +623,10 @@ def _append_args_line(self, line: str) -> None:
Else, append the line to the current section.
"""
name = line.split(' ', 1)[0]

if name.startswith('@') and name.endswith(':'):
# If line is "@arg: first line of description", find
# the index of 'f', which is the indent we expect for any
# following lines. We then remove the leading "@arg:"
# from line and replace it with spaces so that 'f' has the
# same index as it did in the original line and can be
# handled the same way we will handle following lines.
indent = must_match(r'@\S*:\s*', line).end()
line = line[indent:]
if not line:
# Line was just the "@arg:" header; following lines
# are not indented
indent = 0
else:
line = ' ' * indent + line
self._start_args_section(name[1:-1], indent)
elif self._is_section_tag(name):
if match := self._match_at_name_colon(line):
line = line[match.end():]
self._start_args_section(match.group(1))
elif self._match_section_tag(line):
self._append_line = self._append_various_line
self._append_various_line(line)
return
Expand All @@ -654,25 +643,10 @@ def _append_args_line(self, line: str) -> None:
self._append_freeform(line)

def _append_features_line(self, line: str) -> None:
name = line.split(' ', 1)[0]

if name.startswith('@') and name.endswith(':'):
# If line is "@arg: first line of description", find
# the index of 'f', which is the indent we expect for any
# following lines. We then remove the leading "@arg:"
# from line and replace it with spaces so that 'f' has the
# same index as it did in the original line and can be
# handled the same way we will handle following lines.
indent = must_match(r'@\S*:\s*', line).end()
line = line[indent:]
if not line:
# Line was just the "@arg:" header; following lines
# are not indented
indent = 0
else:
line = ' ' * indent + line
self._start_features_section(name[1:-1], indent)
elif self._is_section_tag(name):
if match := self._match_at_name_colon(line):
line = line[match.end():]
self._start_features_section(match.group(1))
elif self._match_section_tag(line):
self._append_line = self._append_various_line
self._append_various_line(line)
return
Expand All @@ -696,64 +670,47 @@ def _append_various_line(self, line: str) -> None:
Else, append the line to the current section.
"""
name = line.split(' ', 1)[0]

if name.startswith('@') and name.endswith(':'):
if match := self._match_at_name_colon(line):
raise QAPIParseError(self._parser,
"'%s' can't follow '%s' section"
% (name, self.sections[0].name))
if self._is_section_tag(name):
# If line is "Section: first line of description", find
# the index of 'f', which is the indent we expect for any
# following lines. We then remove the leading "Section:"
# from line and replace it with spaces so that 'f' has the
# same index as it did in the original line and can be
# handled the same way we will handle following lines.
indent = must_match(r'\S*:\s*', line).end()
line = line[indent:]
if not line:
# Line was just the "Section:" header; following lines
# are not indented
indent = 0
else:
line = ' ' * indent + line
self._start_section(name[:-1], indent)
"'@%s:' can't follow '%s' section"
% (match.group(1), self.sections[0].name))
if match := self._match_section_tag(line):
line = line[match.end():]
self._start_section(match.group(1))

self._append_freeform(line)

def _start_symbol_section(
self,
symbols_dict: Dict[str, 'QAPIDoc.ArgSection'],
name: str,
indent: int) -> None:
name: str) -> None:
# FIXME invalid names other than the empty string aren't flagged
if not name:
raise QAPIParseError(self._parser, "invalid parameter name")
if name in symbols_dict:
raise QAPIParseError(self._parser,
"'%s' parameter name duplicated" % name)
assert not self.sections
new_section = QAPIDoc.ArgSection(self._parser, name, indent)
new_section = QAPIDoc.ArgSection(self._parser, name)
self._switch_section(new_section)
symbols_dict[name] = new_section

def _start_args_section(self, name: str, indent: int) -> None:
self._start_symbol_section(self.args, name, indent)
def _start_args_section(self, name: str) -> None:
self._start_symbol_section(self.args, name)

def _start_features_section(self, name: str, indent: int) -> None:
self._start_symbol_section(self.features, name, indent)
def _start_features_section(self, name: str) -> None:
self._start_symbol_section(self.features, name)

def _start_section(self, name: Optional[str] = None,
indent: int = 0) -> None:
def _start_section(self, name: Optional[str] = None) -> None:
if name in ('Returns', 'Since') and self.has_section(name):
raise QAPIParseError(self._parser,
"duplicated '%s' section" % name)
new_section = QAPIDoc.Section(self._parser, name, indent)
new_section = QAPIDoc.Section(self._parser, name)
self._switch_section(new_section)
self.sections.append(new_section)

def _switch_section(self, new_section: 'QAPIDoc.Section') -> None:
text = self._section.text = self._section.text.strip()
text = self._section.text = self._section.text.strip('\n')

# Only the 'body' section is allowed to have an empty body.
# All other sections, including anonymous ones, must have text.
Expand Down
375 changes: 369 additions & 6 deletions target/i386/cpu.c

Large diffs are not rendered by default.

15 changes: 15 additions & 0 deletions target/i386/cpu.h
Expand Up @@ -600,6 +600,7 @@ typedef enum FeatureWord {
FEAT_8000_0001_ECX, /* CPUID[8000_0001].ECX */
FEAT_8000_0007_EDX, /* CPUID[8000_0007].EDX */
FEAT_8000_0008_EBX, /* CPUID[8000_0008].EBX */
FEAT_8000_0021_EAX, /* CPUID[8000_0021].EAX */
FEAT_C000_0001_EDX, /* CPUID[C000_0001].EDX */
FEAT_KVM, /* CPUID[4000_0001].EAX (KVM_CPUID_FEATURES) */
FEAT_KVM_HINTS, /* CPUID[4000_0001].EDX */
Expand Down Expand Up @@ -773,6 +774,7 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w,
#define CPUID_SVM_AVIC (1U << 13)
#define CPUID_SVM_V_VMSAVE_VMLOAD (1U << 15)
#define CPUID_SVM_VGIF (1U << 16)
#define CPUID_SVM_VNMI (1U << 25)
#define CPUID_SVM_SVME_ADDR_CHK (1U << 28)

/* Support RDFSBASE/RDGSBASE/WRFSBASE/WRGSBASE */
Expand Down Expand Up @@ -946,8 +948,21 @@ uint64_t x86_cpu_get_supported_feature_word(FeatureWord w,
#define CPUID_8000_0008_EBX_IBRS (1U << 14)
/* Single Thread Indirect Branch Predictors */
#define CPUID_8000_0008_EBX_STIBP (1U << 15)
/* STIBP mode has enhanced performance and may be left always on */
#define CPUID_8000_0008_EBX_STIBP_ALWAYS_ON (1U << 17)
/* Speculative Store Bypass Disable */
#define CPUID_8000_0008_EBX_AMD_SSBD (1U << 24)
/* Predictive Store Forwarding Disable */
#define CPUID_8000_0008_EBX_AMD_PSFD (1U << 28)

/* Processor ignores nested data breakpoints */
#define CPUID_8000_0021_EAX_No_NESTED_DATA_BP (1U << 0)
/* LFENCE is always serializing */
#define CPUID_8000_0021_EAX_LFENCE_ALWAYS_SERIALIZING (1U << 2)
/* Null Selector Clears Base */
#define CPUID_8000_0021_EAX_NULL_SEL_CLR_BASE (1U << 6)
/* Automatic IBRS */
#define CPUID_8000_0021_EAX_AUTO_IBRS (1U << 8)

#define CPUID_XSAVE_XSAVEOPT (1U << 0)
#define CPUID_XSAVE_XSAVEC (1U << 1)
Expand Down
2 changes: 1 addition & 1 deletion tests/qapi-schema/doc-bad-indent.err
@@ -1 +1 @@
doc-bad-indent.json:6:1: unexpected de-indent (expected at least 4 spaces)
doc-bad-indent.json:7:1: unexpected de-indent (expected at least 2 spaces)
3 changes: 2 additions & 1 deletion tests/qapi-schema/doc-bad-indent.json
Expand Up @@ -3,6 +3,7 @@
##
# @foo:
# @a: line one
# line two is wrongly indented
# line two
# line three is wrongly indented
##
{ 'command': 'foo', 'data': { 'a': 'int' } }
20 changes: 13 additions & 7 deletions tests/qapi-schema/doc-good.json
Expand Up @@ -54,7 +54,7 @@
##
# @Enum:
#
# @one: The _one_ {and only}
# @one: The _one_ {and only}, description on the same line
#
# Features:
# @enum-feat: Also _one_ {and only}
Expand All @@ -73,7 +73,8 @@
# @Base:
#
# @base1:
# the first member
# description starts on a new line,
# not indented
##
{ 'struct': 'Base', 'data': { 'base1': 'Enum' },
'if': { 'all': ['IFALL1', 'IFALL2'] } }
Expand All @@ -83,7 +84,9 @@
#
# A paragraph
#
# Another paragraph (but no @var: line)
# Another paragraph
#
# @var1 is undocumented
#
# Features:
# @variant1-feat: a feature
Expand Down Expand Up @@ -118,7 +121,8 @@
##
# @Alternate:
#
# @i: an integer
# @i: description starts on the same line
# remainder indented the same
# @b is undocumented
#
# Features:
Expand All @@ -136,10 +140,12 @@
##
# @cmd:
#
# @arg1: the first argument
# @arg1:
# description starts on a new line,
# indented
#
# @arg2: the second
# argument
# @arg2: description starts on the same line
# remainder indented differently
#
# Features:
# @cmd-feat1: a feature
Expand Down
19 changes: 12 additions & 7 deletions tests/qapi-schema/doc-good.out
Expand Up @@ -104,7 +104,7 @@ doc symbol=Enum
body=

arg=one
The _one_ {and only}
The _one_ {and only}, description on the same line
arg=two

feature=enum-feat
Expand All @@ -117,12 +117,15 @@ doc symbol=Base
body=

arg=base1
the first member
description starts on a new line,
not indented
doc symbol=Variant1
body=
A paragraph

Another paragraph (but no @var: line)
Another paragraph

@var1 is undocumented
arg=var1

feature=variant1-feat
Expand All @@ -141,7 +144,8 @@ doc symbol=Alternate
body=

arg=i
an integer
description starts on the same line
remainder indented the same
@b is undocumented
arg=b

Expand All @@ -154,10 +158,11 @@ doc symbol=cmd
body=

arg=arg1
the first argument
description starts on a new line,
indented
arg=arg2
the second
argument
description starts on the same line
remainder indented differently
arg=arg3

feature=cmd-feat1
Expand Down
30 changes: 18 additions & 12 deletions tests/unit/test-aio-multithread.c
Expand Up @@ -107,8 +107,7 @@ static void test_lifecycle(void)
/* aio_co_schedule test. */

static Coroutine *to_schedule[NUM_CONTEXTS];

static bool now_stopping;
static bool stop[NUM_CONTEXTS];

static int count_retry;
static int count_here;
Expand Down Expand Up @@ -136,20 +135,27 @@ static bool schedule_next(int n)

static void finish_cb(void *opaque)
{
stop[id] = true;
schedule_next(id);
}

static coroutine_fn void test_multi_co_schedule_entry(void *opaque)
{
g_assert(to_schedule[id] == NULL);

while (!qatomic_mb_read(&now_stopping)) {
/*
* The next iteration will set to_schedule[id] again, but once finish_cb
* is scheduled there is no guarantee that it will actually be woken up,
* so at that point it must not go to sleep.
*/
while (!stop[id]) {
int n;

n = g_test_rand_int_range(0, NUM_CONTEXTS);
schedule_next(n);

qatomic_mb_set(&to_schedule[id], qemu_coroutine_self());
/* finish_cb can run here. */
qemu_coroutine_yield();
g_assert(to_schedule[id] == NULL);
}
Expand All @@ -161,7 +167,6 @@ static void test_multi_co_schedule(int seconds)
int i;

count_here = count_other = count_retry = 0;
now_stopping = false;

create_aio_contexts();
for (i = 0; i < NUM_CONTEXTS; i++) {
Expand All @@ -171,10 +176,10 @@ static void test_multi_co_schedule(int seconds)

g_usleep(seconds * 1000000);

qatomic_mb_set(&now_stopping, true);
/* Guarantee that each AioContext is woken up from its last wait. */
for (i = 0; i < NUM_CONTEXTS; i++) {
ctx_run(i, finish_cb, NULL);
to_schedule[i] = NULL;
g_assert(to_schedule[i] == NULL);
}

join_aio_contexts();
Expand All @@ -199,10 +204,11 @@ static uint32_t atomic_counter;
static uint32_t running;
static uint32_t counter;
static CoMutex comutex;
static bool now_stopping;

static void coroutine_fn test_multi_co_mutex_entry(void *opaque)
{
while (!qatomic_mb_read(&now_stopping)) {
while (!qatomic_read(&now_stopping)) {
qemu_co_mutex_lock(&comutex);
counter++;
qemu_co_mutex_unlock(&comutex);
Expand Down Expand Up @@ -236,7 +242,7 @@ static void test_multi_co_mutex(int threads, int seconds)

g_usleep(seconds * 1000000);

qatomic_mb_set(&now_stopping, true);
qatomic_set(&now_stopping, true);
while (running > 0) {
g_usleep(100000);
}
Expand Down Expand Up @@ -327,7 +333,7 @@ static void mcs_mutex_unlock(void)

static void test_multi_fair_mutex_entry(void *opaque)
{
while (!qatomic_mb_read(&now_stopping)) {
while (!qatomic_read(&now_stopping)) {
mcs_mutex_lock();
counter++;
mcs_mutex_unlock();
Expand Down Expand Up @@ -355,7 +361,7 @@ static void test_multi_fair_mutex(int threads, int seconds)

g_usleep(seconds * 1000000);

qatomic_mb_set(&now_stopping, true);
qatomic_set(&now_stopping, true);
while (running > 0) {
g_usleep(100000);
}
Expand Down Expand Up @@ -383,7 +389,7 @@ static QemuMutex mutex;

static void test_multi_mutex_entry(void *opaque)
{
while (!qatomic_mb_read(&now_stopping)) {
while (!qatomic_read(&now_stopping)) {
qemu_mutex_lock(&mutex);
counter++;
qemu_mutex_unlock(&mutex);
Expand Down Expand Up @@ -411,7 +417,7 @@ static void test_multi_mutex(int threads, int seconds)

g_usleep(seconds * 1000000);

qatomic_mb_set(&now_stopping, true);
qatomic_set(&now_stopping, true);
while (running > 0) {
g_usleep(100000);
}
Expand Down
69 changes: 43 additions & 26 deletions util/rcu.c
Expand Up @@ -83,19 +83,17 @@ static void wait_for_readers(void)
*/
qemu_event_reset(&rcu_gp_event);

/* Instead of using qatomic_mb_set for index->waiting, and
* qatomic_mb_read for index->ctr, memory barriers are placed
* manually since writes to different threads are independent.
* qemu_event_reset has acquire semantics, so no memory barrier
* is needed here.
*/
QLIST_FOREACH(index, &registry, node) {
qatomic_set(&index->waiting, true);
}

/* Here, order the stores to index->waiting before the loads of
* index->ctr. Pairs with smp_mb_placeholder() in rcu_read_unlock(),
* ensuring that the loads of index->ctr are sequentially consistent.
*
* If this is the last iteration, this barrier also prevents
* frees from seeping upwards, and orders the two wait phases
* on architectures with 32-bit longs; see synchronize_rcu().
*/
smp_mb_global();

Expand All @@ -104,7 +102,7 @@ static void wait_for_readers(void)
QLIST_REMOVE(index, node);
QLIST_INSERT_HEAD(&qsreaders, index, node);

/* No need for mb_set here, worst of all we
/* No need for memory barriers here, worst of all we
* get some extra futex wakeups.
*/
qatomic_set(&index->waiting, false);
Expand Down Expand Up @@ -149,26 +147,26 @@ void synchronize_rcu(void)

/* Write RCU-protected pointers before reading p_rcu_reader->ctr.
* Pairs with smp_mb_placeholder() in rcu_read_lock().
*
* Also orders write to RCU-protected pointers before
* write to rcu_gp_ctr.
*/
smp_mb_global();

QEMU_LOCK_GUARD(&rcu_registry_lock);
if (!QLIST_EMPTY(&registry)) {
/* In either case, the qatomic_mb_set below blocks stores that free
* old RCU-protected pointers.
*/
if (sizeof(rcu_gp_ctr) < 8) {
/* For architectures with 32-bit longs, a two-subphases algorithm
* ensures we do not encounter overflow bugs.
*
* Switch parity: 0 -> 1, 1 -> 0.
*/
qatomic_mb_set(&rcu_gp_ctr, rcu_gp_ctr ^ RCU_GP_CTR);
qatomic_set(&rcu_gp_ctr, rcu_gp_ctr ^ RCU_GP_CTR);
wait_for_readers();
qatomic_mb_set(&rcu_gp_ctr, rcu_gp_ctr ^ RCU_GP_CTR);
qatomic_set(&rcu_gp_ctr, rcu_gp_ctr ^ RCU_GP_CTR);
} else {
/* Increment current grace period. */
qatomic_mb_set(&rcu_gp_ctr, rcu_gp_ctr + RCU_GP_CTR);
qatomic_set(&rcu_gp_ctr, rcu_gp_ctr + RCU_GP_CTR);
}

wait_for_readers();
Expand All @@ -191,35 +189,54 @@ static void enqueue(struct rcu_head *node)
struct rcu_head **old_tail;

node->next = NULL;

/*
* Make this node the tail of the list. The node will be
* used by further enqueue operations, but it will not
* be dequeued yet...
*/
old_tail = qatomic_xchg(&tail, &node->next);
qatomic_mb_set(old_tail, node);

/*
* ... until it is pointed to from another item in the list.
* In the meantime, try_dequeue() will find a NULL next pointer
* and loop.
*
* Synchronizes with qatomic_load_acquire() in try_dequeue().
*/
qatomic_store_release(old_tail, node);
}

static struct rcu_head *try_dequeue(void)
{
struct rcu_head *node, *next;

retry:
/* Test for an empty list, which we do not expect. Note that for
/* Head is only written by this thread, so no need for barriers. */
node = head;

/*
* If the head node has NULL in its next pointer, the value is
* wrong and we need to wait until its enqueuer finishes the update.
*/
next = qatomic_load_acquire(&node->next);
if (!next) {
return NULL;
}

/*
* Test for an empty list, which we do not expect. Note that for
* the consumer head and tail are always consistent. The head
* is consistent because only the consumer reads/writes it.
* The tail, because it is the first step in the enqueuing.
* It is only the next pointers that might be inconsistent.
*/
if (head == &dummy && qatomic_mb_read(&tail) == &dummy.next) {
if (head == &dummy && qatomic_read(&tail) == &dummy.next) {
abort();
}

/* If the head node has NULL in its next pointer, the value is
* wrong and we need to wait until its enqueuer finishes the update.
*/
node = head;
next = qatomic_mb_read(&head->next);
if (!next) {
return NULL;
}

/* Since we are the sole consumer, and we excluded the empty case
/*
* Since we are the sole consumer, and we excluded the empty case
* above, the queue will always have at least two nodes: the
* dummy node, and the one being removed. So we do not need to update
* the tail pointer.
Expand Down