A block driver can provide a callback to report driver-specific
statistics.
file-posix driver now reports discard statistics
Signed-off-by: Anton Nefedov <anton.nefedov@virtuozzo.com>
---
qapi/block-core.json | 39 +++++++++++++++++++++++++++++++++++++++
include/block/block.h | 1 +
include/block/block_int.h | 1 +
block.c | 9 +++++++++
block/file-posix.c | 17 +++++++++++++++++
block/qapi.c | 5 +++++
6 files changed, 72 insertions(+)
diff --git a/qapi/block-core.json b/qapi/block-core.json
index 01da84cb61..cd0344435e 100644
--- a/qapi/block-core.json
+++ b/qapi/block-core.json
@@ -877,6 +877,42 @@
'*x_wr_latency_histogram': 'BlockLatencyHistogramInfo',
'*x_flush_latency_histogram': 'BlockLatencyHistogramInfo' } }
+##
+# @BlockStatsSpecificFile:
+#
+# File driver statistics
+#
+# @discard-nb-ok: The number of succeeded discard operations performed by
+# the driver.
+#
+# @discard-nb-failed: The number of failed discard operations performed by
+# the driver.
+#
+# @discard-bytes-ok: The number of bytes discarded by the driver.
+#
+# Since 3.1
+##
+{ 'struct': 'BlockStatsSpecificFile',
+ 'data': {
+ 'discard-nb-ok': 'int',
+ 'discard-nb-failed': 'int',
+ 'discard-bytes-ok': 'int'
+ } }
+
+##
+# @BlockStatsSpecific:
+#
+# Block driver specific statistics
+#
+# Since: 3.1
+##
+{ 'union': 'BlockStatsSpecific',
+ 'base': { 'driver': 'BlockdevDriver' },
+ 'discriminator': 'driver',
+ 'data': {
+ 'file': 'BlockStatsSpecificFile'
+ } }
+
##
# @BlockStats:
#
@@ -892,6 +928,8 @@
#
# @stats: A @BlockDeviceStats for the device.
#
+# @driver-specific: Optional driver-specific stats. (Since 3.1)
+#
# @parent: This describes the file block device if it has one.
# Contains recursively the statistics of the underlying
# protocol (e.g. the host file for a qcow2 image). If there is
@@ -905,6 +943,7 @@
{ 'struct': 'BlockStats',
'data': {'*device': 'str', '*qdev': 'str', '*node-name': 'str',
'stats': 'BlockDeviceStats',
+ '*driver-specific': 'BlockStatsSpecific',
'*parent': 'BlockStats',
'*backing': 'BlockStats'} }
diff --git a/include/block/block.h b/include/block/block.h
index b189cf422e..07a3b31386 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -475,6 +475,7 @@ const char *bdrv_get_device_or_node_name(const BlockDriverState *bs);
int bdrv_get_flags(BlockDriverState *bs);
int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi);
ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs);
+BlockStatsSpecific *bdrv_get_specific_stats(BlockDriverState *bs);
void bdrv_round_to_clusters(BlockDriverState *bs,
int64_t offset, int64_t bytes,
int64_t *cluster_offset,
diff --git a/include/block/block_int.h b/include/block/block_int.h
index f605622216..236d4aceef 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -320,6 +320,7 @@ struct BlockDriver {
Error **errp);
int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi);
ImageInfoSpecific *(*bdrv_get_specific_info)(BlockDriverState *bs);
+ BlockStatsSpecific *(*bdrv_get_specific_stats)(BlockDriverState *bs);
int coroutine_fn (*bdrv_save_vmstate)(BlockDriverState *bs,
QEMUIOVector *qiov,
diff --git a/block.c b/block.c
index 95d8635aa1..1e5bba4ac6 100644
--- a/block.c
+++ b/block.c
@@ -4244,6 +4244,15 @@ ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs)
return NULL;
}
+BlockStatsSpecific *bdrv_get_specific_stats(BlockDriverState *bs)
+{
+ BlockDriver *drv = bs->drv;
+ if (!drv || !drv->bdrv_get_specific_stats) {
+ return NULL;
+ }
+ return drv->bdrv_get_specific_stats(bs);
+}
+
void bdrv_debug_event(BlockDriverState *bs, BlkdebugEvent event)
{
if (!bs || !bs->drv || !bs->drv->bdrv_debug_event) {
diff --git a/block/file-posix.c b/block/file-posix.c
index 1a7126046c..a0a06585ab 100644
--- a/block/file-posix.c
+++ b/block/file-posix.c
@@ -2630,6 +2630,21 @@ static int raw_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
return 0;
}
+static BlockStatsSpecific *raw_get_specific_stats(BlockDriverState *bs)
+{
+ BDRVRawState *s = bs->opaque;
+ BlockStatsSpecific *stats = g_new(BlockStatsSpecific, 1);
+
+ stats->driver = BLOCKDEV_DRIVER_FILE;
+ stats->u.file = (BlockStatsSpecificFile){
+ .discard_nb_ok = s->stats.discard_nb_ok,
+ .discard_nb_failed = s->stats.discard_nb_failed,
+ .discard_bytes_ok = s->stats.discard_bytes_ok,
+ };
+
+ return stats;
+}
+
static QemuOptsList raw_create_opts = {
.name = "raw-create-opts",
.head = QTAILQ_HEAD_INITIALIZER(raw_create_opts.head),
@@ -2741,6 +2756,7 @@ BlockDriver bdrv_file = {
.bdrv_get_info = raw_get_info,
.bdrv_get_allocated_file_size
= raw_get_allocated_file_size,
+ .bdrv_get_specific_stats = raw_get_specific_stats,
.bdrv_check_perm = raw_check_perm,
.bdrv_set_perm = raw_set_perm,
.bdrv_abort_perm_update = raw_abort_perm_update,
@@ -3226,6 +3242,7 @@ static BlockDriver bdrv_host_device = {
.bdrv_get_info = raw_get_info,
.bdrv_get_allocated_file_size
= raw_get_allocated_file_size,
+ .bdrv_get_specific_stats = raw_get_specific_stats,
.bdrv_check_perm = raw_check_perm,
.bdrv_set_perm = raw_set_perm,
.bdrv_abort_perm_update = raw_abort_perm_update,
diff --git a/block/qapi.c b/block/qapi.c
index df31f351d2..74f762ea6c 100644
--- a/block/qapi.c
+++ b/block/qapi.c
@@ -535,6 +535,11 @@ static BlockStats *bdrv_query_bds_stats(BlockDriverState *bs,
s->stats->wr_highest_offset = stat64_get(&bs->wr_highest_offset);
+ s->driver_specific = bdrv_get_specific_stats(bs);
+ if (s->driver_specific) {
+ s->has_driver_specific = true;
+ }
+
if (bs->file) {
s->has_parent = true;
s->parent = bdrv_query_bds_stats(bs->file->bs, blk_level);
--
2.17.1
31.10.2018 14:35, Anton Nefedov wrote:
> A block driver can provide a callback to report driver-specific
> statistics.
>
> file-posix driver now reports discard statistics
>
> Signed-off-by: Anton Nefedov <anton.nefedov@virtuozzo.com>
> ---
> qapi/block-core.json | 39 +++++++++++++++++++++++++++++++++++++++
> include/block/block.h | 1 +
> include/block/block_int.h | 1 +
> block.c | 9 +++++++++
> block/file-posix.c | 17 +++++++++++++++++
> block/qapi.c | 5 +++++
> 6 files changed, 72 insertions(+)
>
> diff --git a/qapi/block-core.json b/qapi/block-core.json
> index 01da84cb61..cd0344435e 100644
> --- a/qapi/block-core.json
> +++ b/qapi/block-core.json
> @@ -877,6 +877,42 @@
> '*x_wr_latency_histogram': 'BlockLatencyHistogramInfo',
> '*x_flush_latency_histogram': 'BlockLatencyHistogramInfo' } }
>
> +##
> +# @BlockStatsSpecificFile:
> +#
> +# File driver statistics
> +#
> +# @discard-nb-ok: The number of succeeded discard operations performed by
> +# the driver.
> +#
> +# @discard-nb-failed: The number of failed discard operations performed by
> +# the driver.
> +#
> +# @discard-bytes-ok: The number of bytes discarded by the driver.
> +#
> +# Since 3.1
colon after Since:
Since: 3.1
> +##
> +{ 'struct': 'BlockStatsSpecificFile',
> + 'data': {
> + 'discard-nb-ok': 'int',
> + 'discard-nb-failed': 'int',
> + 'discard-bytes-ok': 'int'
> + } }
the common stile here is no extra \n around braces, like:
{ 'struct': 'BlockStatsSpecificFile',
'data': { 'discard-nb-ok': 'int',
'discard-nb-failed': 'int',
'discard-bytes-ok': 'int' } }
> +
> +##
> +# @BlockStatsSpecific:
> +#
> +# Block driver specific statistics
> +#
> +# Since: 3.1
> +##
> +{ 'union': 'BlockStatsSpecific',
> + 'base': { 'driver': 'BlockdevDriver' },
> + 'discriminator': 'driver',
> + 'data': {
> + 'file': 'BlockStatsSpecificFile'
> + } }
and here.
> +
> ##
> # @BlockStats:
> #
> @@ -892,6 +928,8 @@
> #
> # @stats: A @BlockDeviceStats for the device.
> #
> +# @driver-specific: Optional driver-specific stats. (Since 3.1)
> +#
> # @parent: This describes the file block device if it has one.
> # Contains recursively the statistics of the underlying
> # protocol (e.g. the host file for a qcow2 image). If there is
> @@ -905,6 +943,7 @@
> { 'struct': 'BlockStats',
> 'data': {'*device': 'str', '*qdev': 'str', '*node-name': 'str',
> 'stats': 'BlockDeviceStats',
> + '*driver-specific': 'BlockStatsSpecific',
[offtopic]
hmm, do anyone know, why thunderbird when quoting patches adds "> " between all lines except ones starting with a space, and for lines, starting with space, it adds "> " (two extra spaces, not one), which leads to wrong indenting in quotes? And how to fix that?
[/offtopic]
> '*parent': 'BlockStats',
> '*backing': 'BlockStats'} }
>
> diff --git a/include/block/block.h b/include/block/block.h
> index b189cf422e..07a3b31386 100644
> --- a/include/block/block.h
> +++ b/include/block/block.h
> @@ -475,6 +475,7 @@ const char *bdrv_get_device_or_node_name(const BlockDriverState *bs);
> int bdrv_get_flags(BlockDriverState *bs);
> int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi);
> ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs);
> +BlockStatsSpecific *bdrv_get_specific_stats(BlockDriverState *bs);
> void bdrv_round_to_clusters(BlockDriverState *bs,
> int64_t offset, int64_t bytes,
> int64_t *cluster_offset,
> diff --git a/include/block/block_int.h b/include/block/block_int.h
> index f605622216..236d4aceef 100644
> --- a/include/block/block_int.h
> +++ b/include/block/block_int.h
> @@ -320,6 +320,7 @@ struct BlockDriver {
> Error **errp);
> int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi);
> ImageInfoSpecific *(*bdrv_get_specific_info)(BlockDriverState *bs);
> + BlockStatsSpecific *(*bdrv_get_specific_stats)(BlockDriverState *bs);
>
> int coroutine_fn (*bdrv_save_vmstate)(BlockDriverState *bs,
> QEMUIOVector *qiov,
> diff --git a/block.c b/block.c
> index 95d8635aa1..1e5bba4ac6 100644
> --- a/block.c
> +++ b/block.c
> @@ -4244,6 +4244,15 @@ ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs)
> return NULL;
> }
>
> +BlockStatsSpecific *bdrv_get_specific_stats(BlockDriverState *bs)
> +{
> + BlockDriver *drv = bs->drv;
> + if (!drv || !drv->bdrv_get_specific_stats) {
> + return NULL;
> + }
> + return drv->bdrv_get_specific_stats(bs);
> +}
> +
> void bdrv_debug_event(BlockDriverState *bs, BlkdebugEvent event)
> {
> if (!bs || !bs->drv || !bs->drv->bdrv_debug_event) {
> diff --git a/block/file-posix.c b/block/file-posix.c
> index 1a7126046c..a0a06585ab 100644
> --- a/block/file-posix.c
> +++ b/block/file-posix.c
> @@ -2630,6 +2630,21 @@ static int raw_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
> return 0;
> }
>
> +static BlockStatsSpecific *raw_get_specific_stats(BlockDriverState *bs)
> +{
> + BDRVRawState *s = bs->opaque;
> + BlockStatsSpecific *stats = g_new(BlockStatsSpecific, 1);
> +
> + stats->driver = BLOCKDEV_DRIVER_FILE;
it should be BLOCKDEV_DRIVER_HOST_DEVICE for bdrv_host_device driver I think.
> + stats->u.file = (BlockStatsSpecificFile){
and than, here we'll have "different" union field - host_device. (and for code-style, whitespace before brace)
> + .discard_nb_ok = s->stats.discard_nb_ok,
> + .discard_nb_failed = s->stats.discard_nb_failed,
> + .discard_bytes_ok = s->stats.discard_bytes_ok,
> + };
> +
> + return stats;
> +}
> +
> static QemuOptsList raw_create_opts = {
> .name = "raw-create-opts",
> .head = QTAILQ_HEAD_INITIALIZER(raw_create_opts.head),
> @@ -2741,6 +2756,7 @@ BlockDriver bdrv_file = {
> .bdrv_get_info = raw_get_info,
> .bdrv_get_allocated_file_size
> = raw_get_allocated_file_size,
> + .bdrv_get_specific_stats = raw_get_specific_stats,
> .bdrv_check_perm = raw_check_perm,
> .bdrv_set_perm = raw_set_perm,
> .bdrv_abort_perm_update = raw_abort_perm_update,
> @@ -3226,6 +3242,7 @@ static BlockDriver bdrv_host_device = {
> .bdrv_get_info = raw_get_info,
> .bdrv_get_allocated_file_size
> = raw_get_allocated_file_size,
> + .bdrv_get_specific_stats = raw_get_specific_stats,
> .bdrv_check_perm = raw_check_perm,
> .bdrv_set_perm = raw_set_perm,
> .bdrv_abort_perm_update = raw_abort_perm_update,
> diff --git a/block/qapi.c b/block/qapi.c
> index df31f351d2..74f762ea6c 100644
> --- a/block/qapi.c
> +++ b/block/qapi.c
> @@ -535,6 +535,11 @@ static BlockStats *bdrv_query_bds_stats(BlockDriverState *bs,
>
> s->stats->wr_highest_offset = stat64_get(&bs->wr_highest_offset);
>
> + s->driver_specific = bdrv_get_specific_stats(bs);
> + if (s->driver_specific) {
> + s->has_driver_specific = true;
> + }
> +
> if (bs->file) {
> s->has_parent = true;
> s->parent = bdrv_query_bds_stats(bs->file->bs, blk_level);
>
--
Best regards,
Vladimir
On 23/11/2018 10:21 PM, Vladimir Sementsov-Ogievskiy wrote:
> 31.10.2018 14:35, Anton Nefedov wrote:
>> A block driver can provide a callback to report driver-specific
>> statistics.
>>
>> file-posix driver now reports discard statistics
>>
>> Signed-off-by: Anton Nefedov <anton.nefedov@virtuozzo.com>
>> ---
>> qapi/block-core.json | 39 +++++++++++++++++++++++++++++++++++++++
>> include/block/block.h | 1 +
>> include/block/block_int.h | 1 +
>> block.c | 9 +++++++++
>> block/file-posix.c | 17 +++++++++++++++++
>> block/qapi.c | 5 +++++
>> 6 files changed, 72 insertions(+)
>>
>> diff --git a/qapi/block-core.json b/qapi/block-core.json
>> index 01da84cb61..cd0344435e 100644
>> --- a/qapi/block-core.json
>> +++ b/qapi/block-core.json
>> @@ -877,6 +877,42 @@
>> '*x_wr_latency_histogram': 'BlockLatencyHistogramInfo',
>> '*x_flush_latency_histogram': 'BlockLatencyHistogramInfo' } }
>>
>> +##
>> +# @BlockStatsSpecificFile:
>> +#
>> +# File driver statistics
>> +#
>> +# @discard-nb-ok: The number of succeeded discard operations performed by
>> +# the driver.
>> +#
>> +# @discard-nb-failed: The number of failed discard operations performed by
>> +# the driver.
>> +#
>> +# @discard-bytes-ok: The number of bytes discarded by the driver.
>> +#
>> +# Since 3.1
>
> colon after Since:
> Since: 3.1
>
done
>> +##
>> +{ 'struct': 'BlockStatsSpecificFile',
>> + 'data': {
>> + 'discard-nb-ok': 'int',
>> + 'discard-nb-failed': 'int',
>> + 'discard-bytes-ok': 'int'
>> + } }
>
> the common stile here is no extra \n around braces, like:
>
> { 'struct': 'BlockStatsSpecificFile',
> 'data': { 'discard-nb-ok': 'int',
> 'discard-nb-failed': 'int',
> 'discard-bytes-ok': 'int' } }
>
>
Ok this one is more frequent. Fixed.
>> +
>> +##
>> +# @BlockStatsSpecific:
>> +#
>> +# Block driver specific statistics
>> +#
>> +# Since: 3.1
>> +##
>> +{ 'union': 'BlockStatsSpecific',
>> + 'base': { 'driver': 'BlockdevDriver' },
>> + 'discriminator': 'driver',
>> + 'data': {
>> + 'file': 'BlockStatsSpecificFile'
>> + } }
>
> and here.
>
done
>> +
>> ##
>> # @BlockStats:
>> #
>> @@ -892,6 +928,8 @@
>> #
>> # @stats: A @BlockDeviceStats for the device.
>> #
>> +# @driver-specific: Optional driver-specific stats. (Since 3.1)
>> +#
>> # @parent: This describes the file block device if it has one.
>> # Contains recursively the statistics of the underlying
>> # protocol (e.g. the host file for a qcow2 image). If there is
>> @@ -905,6 +943,7 @@
>> { 'struct': 'BlockStats',
>> 'data': {'*device': 'str', '*qdev': 'str', '*node-name': 'str',
>> 'stats': 'BlockDeviceStats',
>> + '*driver-specific': 'BlockStatsSpecific',
>
> [offtopic]
> hmm, do anyone know, why thunderbird when quoting patches adds "> " between all lines except ones starting with a space, and for lines, starting with space, it adds "> " (two extra spaces, not one), which leads to wrong indenting in quotes? And how to fix that?
> [/offtopic]
>
>> '*parent': 'BlockStats',
>> '*backing': 'BlockStats'} }
>>
>> diff --git a/include/block/block.h b/include/block/block.h
>> index b189cf422e..07a3b31386 100644
>> --- a/include/block/block.h
>> +++ b/include/block/block.h
>> @@ -475,6 +475,7 @@ const char *bdrv_get_device_or_node_name(const BlockDriverState *bs);
>> int bdrv_get_flags(BlockDriverState *bs);
>> int bdrv_get_info(BlockDriverState *bs, BlockDriverInfo *bdi);
>> ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs);
>> +BlockStatsSpecific *bdrv_get_specific_stats(BlockDriverState *bs);
>> void bdrv_round_to_clusters(BlockDriverState *bs,
>> int64_t offset, int64_t bytes,
>> int64_t *cluster_offset,
>> diff --git a/include/block/block_int.h b/include/block/block_int.h
>> index f605622216..236d4aceef 100644
>> --- a/include/block/block_int.h
>> +++ b/include/block/block_int.h
>> @@ -320,6 +320,7 @@ struct BlockDriver {
>> Error **errp);
>> int (*bdrv_get_info)(BlockDriverState *bs, BlockDriverInfo *bdi);
>> ImageInfoSpecific *(*bdrv_get_specific_info)(BlockDriverState *bs);
>> + BlockStatsSpecific *(*bdrv_get_specific_stats)(BlockDriverState *bs);
>>
>> int coroutine_fn (*bdrv_save_vmstate)(BlockDriverState *bs,
>> QEMUIOVector *qiov,
>> diff --git a/block.c b/block.c
>> index 95d8635aa1..1e5bba4ac6 100644
>> --- a/block.c
>> +++ b/block.c
>> @@ -4244,6 +4244,15 @@ ImageInfoSpecific *bdrv_get_specific_info(BlockDriverState *bs)
>> return NULL;
>> }
>>
>> +BlockStatsSpecific *bdrv_get_specific_stats(BlockDriverState *bs)
>> +{
>> + BlockDriver *drv = bs->drv;
>> + if (!drv || !drv->bdrv_get_specific_stats) {
>> + return NULL;
>> + }
>> + return drv->bdrv_get_specific_stats(bs);
>> +}
>> +
>> void bdrv_debug_event(BlockDriverState *bs, BlkdebugEvent event)
>> {
>> if (!bs || !bs->drv || !bs->drv->bdrv_debug_event) {
>> diff --git a/block/file-posix.c b/block/file-posix.c
>> index 1a7126046c..a0a06585ab 100644
>> --- a/block/file-posix.c
>> +++ b/block/file-posix.c
>> @@ -2630,6 +2630,21 @@ static int raw_get_info(BlockDriverState *bs, BlockDriverInfo *bdi)
>> return 0;
>> }
>>
>> +static BlockStatsSpecific *raw_get_specific_stats(BlockDriverState *bs)
>> +{
>> + BDRVRawState *s = bs->opaque;
>> + BlockStatsSpecific *stats = g_new(BlockStatsSpecific, 1);
>> +
>> + stats->driver = BLOCKDEV_DRIVER_FILE;
>
> it should be BLOCKDEV_DRIVER_HOST_DEVICE for bdrv_host_device driver I think.
>
I guess so. Fixed
>> + stats->u.file = (BlockStatsSpecificFile){
>
> and than, here we'll have "different" union field - host_device. (and for code-style, whitespace before brace)
>
done
>> + .discard_nb_ok = s->stats.discard_nb_ok,
>> + .discard_nb_failed = s->stats.discard_nb_failed,
>> + .discard_bytes_ok = s->stats.discard_bytes_ok,
>> + };
>> +
>> + return stats;
>> +}
>> +
>> static QemuOptsList raw_create_opts = {
>> .name = "raw-create-opts",
>> .head = QTAILQ_HEAD_INITIALIZER(raw_create_opts.head),
>> @@ -2741,6 +2756,7 @@ BlockDriver bdrv_file = {
>> .bdrv_get_info = raw_get_info,
>> .bdrv_get_allocated_file_size
>> = raw_get_allocated_file_size,
>> + .bdrv_get_specific_stats = raw_get_specific_stats,
>> .bdrv_check_perm = raw_check_perm,
>> .bdrv_set_perm = raw_set_perm,
>> .bdrv_abort_perm_update = raw_abort_perm_update,
>> @@ -3226,6 +3242,7 @@ static BlockDriver bdrv_host_device = {
>> .bdrv_get_info = raw_get_info,
>> .bdrv_get_allocated_file_size
>> = raw_get_allocated_file_size,
>> + .bdrv_get_specific_stats = raw_get_specific_stats,
>> .bdrv_check_perm = raw_check_perm,
>> .bdrv_set_perm = raw_set_perm,
>> .bdrv_abort_perm_update = raw_abort_perm_update,
>> diff --git a/block/qapi.c b/block/qapi.c
>> index df31f351d2..74f762ea6c 100644
>> --- a/block/qapi.c
>> +++ b/block/qapi.c
>> @@ -535,6 +535,11 @@ static BlockStats *bdrv_query_bds_stats(BlockDriverState *bs,
>>
>> s->stats->wr_highest_offset = stat64_get(&bs->wr_highest_offset);
>>
>> + s->driver_specific = bdrv_get_specific_stats(bs);
>> + if (s->driver_specific) {
>> + s->has_driver_specific = true;
>> + }
>> +
>> if (bs->file) {
>> s->has_parent = true;
>> s->parent = bdrv_query_bds_stats(bs->file->bs, blk_level);
>>
>
>
© 2016 - 2025 Red Hat, Inc.