qemu-block
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-block] [PATCH v3 4/7] block: convert ThrottleGroup to object w


From: Stefan Hajnoczi
Subject: Re: [Qemu-block] [PATCH v3 4/7] block: convert ThrottleGroup to object with QOM
Date: Tue, 1 Aug 2017 16:47:03 +0100
User-agent: Mutt/1.8.3 (2017-05-23)

On Mon, Jul 31, 2017 at 12:54:40PM +0300, Manos Pitsidianakis wrote:
> ThrottleGroup is converted to an object. This will allow the future
> throttle block filter drive easy creation and configuration of throttle
> groups in QMP and cli.
> 
> A new QAPI struct, ThrottleLimits, is introduced to provide a shared
> struct for all throttle configuration needs in QMP.
> 
> ThrottleGroups can be created via CLI as
>     -object throttle-group,id=foo,x-iops-total=100,x-..
> where x-* are individual limit properties. Since we can't add non-scalar
> properties in -object this interface must be used instead. However,
> setting these properties must be disabled after initialization because
> certain combinations of limits are forbidden and thus configuration
> changes should be done in one transaction. The individual properties
> will go away when support for non-scalar values in CLI is implemented
> and thus are marked as experimental.
> 
> ThrottleGroup also has a `limits` property that uses the ThrottleLimits
> struct.  It can be used to create ThrottleGroups or set the
> configuration in existing groups as follows:
> 
> { "execute": "object-add",
>   "arguments": {
>     "qom-type": "throttle-group",
>     "id": "foo",
>     "props" : {
>       "limits": {
>           "iops-total": 100
>       }
>     }
>   }
> }
> { "execute" : "qom-set",
>     "arguments" : {
>         "path" : "foo",
>         "property" : "limits",
>         "value" : {
>             "iops-total" : 99
>         }
>     }
> }
> 
> This also means a group's configuration can be fetched with qom-get.
> 
> ThrottleGroups can be anonymous which means they can't get accessed by
> other users ie they will always be units instead of group (Because they
> have one ThrottleGroupMember).

blockdev.c automatically assigns -drive id= to the group name if
throttling.group= wasn't given.  So who will use anonymous single-drive
mode?

> Signed-off-by: Manos Pitsidianakis <address@hidden>
> ---
> Notes:
>     Note: I tested Markus Armbruster's patch in <address@hidden>
>     on master and I can use this syntax successfuly:
>     -object '{ "qom-type" : "throttle-group", "id" : "foo", "props" : { 
> "limits" \
>     : { "iops-total" : 1000 } } }'
>     If this gets merged using -object will be a little more verbose but at 
> least we
>     won't have seperate properties, which is a good thing, so the x-* should 
> be
>     dropped.
> 
>  block/throttle-groups.c         | 402 
> ++++++++++++++++++++++++++++++++++++----
>  include/block/throttle-groups.h |   3 +
>  include/qemu/throttle-options.h |  59 ++++--
>  include/qemu/throttle.h         |   3 +
>  qapi/block-core.json            |  48 +++++
>  tests/test-throttle.c           |   1 +
>  util/throttle.c                 | 151 +++++++++++++++
>  7 files changed, 617 insertions(+), 50 deletions(-)
> 
> diff --git a/block/throttle-groups.c b/block/throttle-groups.c
> index f711a3dc62..b9c5036e44 100644
> --- a/block/throttle-groups.c
> +++ b/block/throttle-groups.c
> @@ -25,9 +25,17 @@
>  #include "qemu/osdep.h"
>  #include "sysemu/block-backend.h"
>  #include "block/throttle-groups.h"
> +#include "qemu/throttle-options.h"
>  #include "qemu/queue.h"
>  #include "qemu/thread.h"
>  #include "sysemu/qtest.h"
> +#include "qapi/error.h"
> +#include "qapi-visit.h"
> +#include "qom/object.h"
> +#include "qom/object_interfaces.h"
> +
> +static void throttle_group_obj_init(Object *obj);
> +static void throttle_group_obj_complete(UserCreatable *obj, Error **errp);
>  
>  /* The ThrottleGroup structure (with its ThrottleState) is shared
>   * among different ThrottleGroupMembers and it's independent from
> @@ -54,6 +62,10 @@
>   * that ThrottleGroupMember has throttled requests in the queue.
>   */
>  typedef struct ThrottleGroup {
> +    Object parent_obj;
> +
> +    /* refuse individual property change if initialization is complete */
> +    bool is_initialized;
>      char *name; /* This is constant during the lifetime of the group */
>  
>      QemuMutex lock; /* This lock protects the following four fields */
> @@ -63,8 +75,7 @@ typedef struct ThrottleGroup {
>      bool any_timer_armed[2];
>      QEMUClockType clock_type;
>  
> -    /* These two are protected by the global throttle_groups_lock */
> -    unsigned refcount;
> +    /* This is protected by the global throttle_groups_lock */
>      QTAILQ_ENTRY(ThrottleGroup) list;
>  } ThrottleGroup;
>  
> @@ -77,7 +88,8 @@ static QTAILQ_HEAD(, ThrottleGroup) throttle_groups =
>   * If no ThrottleGroup is found with the given name a new one is
>   * created.
>   *
> - * @name: the name of the ThrottleGroup
> + * @name: the name of the ThrottleGroup, NULL means a new anonymous group 
> will
> + *        be created.
>   * @ret:  the ThrottleState member of the ThrottleGroup
>   */
>  ThrottleState *throttle_group_incref(const char *name)
> @@ -87,32 +99,30 @@ ThrottleState *throttle_group_incref(const char *name)
>  
>      qemu_mutex_lock(&throttle_groups_lock);
>  
> -    /* Look for an existing group with that name */
> -    QTAILQ_FOREACH(iter, &throttle_groups, list) {
> -        if (!strcmp(name, iter->name)) {
> -            tg = iter;
> -            break;
> +    if (name) {
> +        /* Look for an existing group with that name */
> +        QTAILQ_FOREACH(iter, &throttle_groups, list) {
> +            if (!g_strcmp0(name, iter->name)) {
> +                tg = iter;
> +                break;
> +            }
>          }
>      }
>  
>      /* Create a new one if not found */
>      if (!tg) {
> -        tg = g_new0(ThrottleGroup, 1);
> +        /* new ThrottleGroup obj will have a refcnt = 1 */
> +        tg = THROTTLE_GROUP(object_new(TYPE_THROTTLE_GROUP));
>          tg->name = g_strdup(name);
> -        tg->clock_type = QEMU_CLOCK_REALTIME;
> -
> -        if (qtest_enabled()) {
> -            /* For testing block IO throttling only */
> -            tg->clock_type = QEMU_CLOCK_VIRTUAL;
> -        }
> -        qemu_mutex_init(&tg->lock);
> -        throttle_init(&tg->ts);
> -        QLIST_INIT(&tg->head);
> -
> -        QTAILQ_INSERT_TAIL(&throttle_groups, tg, list);
> +        throttle_group_obj_complete((UserCreatable *)tg, &error_abort);
>      }
>  
> -    tg->refcount++;
> +    qemu_mutex_lock(&tg->lock);
> +    if (!QLIST_EMPTY(&tg->head)) {
> +        /* only ref if the group is not empty */
> +        object_ref(OBJECT(tg));
> +    }
> +    qemu_mutex_unlock(&tg->lock);

How do the refcounts work in various cases (legacy -drive
throttling.group and -object throttle-group with 0, 1, or multiple
drives)?

It's not obvious to me that this code works in all cases.

>  
>      qemu_mutex_unlock(&throttle_groups_lock);
>  
> @@ -129,15 +139,7 @@ ThrottleState *throttle_group_incref(const char *name)
>  void throttle_group_unref(ThrottleState *ts)
>  {
>      ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts);
> -
> -    qemu_mutex_lock(&throttle_groups_lock);
> -    if (--tg->refcount == 0) {
> -        QTAILQ_REMOVE(&throttle_groups, tg, list);
> -        qemu_mutex_destroy(&tg->lock);
> -        g_free(tg->name);
> -        g_free(tg);
> -    }
> -    qemu_mutex_unlock(&throttle_groups_lock);
> +    object_unref(OBJECT(tg));
>  }
>  
>  /* Get the name from a ThrottleGroupMember's group. The name (and the 
> pointer)
> @@ -572,9 +574,347 @@ void 
> throttle_group_detach_aio_context(ThrottleGroupMember *tgm)
>      throttle_timers_detach_aio_context(tt);
>  }
>  
> +#undef THROTTLE_OPT_PREFIX
> +#define THROTTLE_OPT_PREFIX "x-"
> +#define DOUBLE 0
> +#define UINT64 1
> +#define UNSIGNED 2
> +
> +typedef struct {
> +    const char *name;
> +    BucketType type;
> +    int data_type;
> +    const ptrdiff_t offset; /* offset in LeakyBucket struct. */
> +} ThrottleParamInfo;
> +
> +static ThrottleParamInfo properties[] = {
> +{
> +    THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL,
> +    THROTTLE_OPS_TOTAL, DOUBLE, offsetof(LeakyBucket, avg),
> +},
> +{
> +    THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL_MAX,
> +    THROTTLE_OPS_TOTAL, DOUBLE, offsetof(LeakyBucket, max),
> +},
> +{
> +    THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL_MAX_LENGTH,
> +    THROTTLE_OPS_TOTAL, UNSIGNED, offsetof(LeakyBucket, burst_length),
> +},
> +{
> +    THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ,
> +    THROTTLE_OPS_READ, DOUBLE, offsetof(LeakyBucket, avg),
> +},
> +{
> +    THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ_MAX,
> +    THROTTLE_OPS_READ, DOUBLE, offsetof(LeakyBucket, max),
> +},
> +{
> +    THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ_MAX_LENGTH,
> +    THROTTLE_OPS_READ, UNSIGNED, offsetof(LeakyBucket, burst_length),
> +},
> +{
> +    THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE,
> +    THROTTLE_OPS_WRITE, DOUBLE, offsetof(LeakyBucket, avg),
> +},
> +{
> +    THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE_MAX,
> +    THROTTLE_OPS_WRITE, DOUBLE, offsetof(LeakyBucket, max),
> +},
> +{
> +    THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE_MAX_LENGTH,
> +    THROTTLE_OPS_WRITE, UNSIGNED, offsetof(LeakyBucket, burst_length),
> +},
> +{
> +    THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL,
> +    THROTTLE_BPS_TOTAL, DOUBLE, offsetof(LeakyBucket, avg),
> +},
> +{
> +    THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL_MAX,
> +    THROTTLE_BPS_TOTAL, DOUBLE, offsetof(LeakyBucket, max),
> +},
> +{
> +    THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL_MAX_LENGTH,
> +    THROTTLE_BPS_TOTAL, UNSIGNED, offsetof(LeakyBucket, burst_length),
> +},
> +{
> +    THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ,
> +    THROTTLE_BPS_READ, DOUBLE, offsetof(LeakyBucket, avg),
> +},
> +{
> +    THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ_MAX,
> +    THROTTLE_BPS_READ, DOUBLE, offsetof(LeakyBucket, max),
> +},
> +{
> +    THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ_MAX_LENGTH,
> +    THROTTLE_BPS_READ, UNSIGNED, offsetof(LeakyBucket, burst_length),
> +},
> +{
> +    THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE,
> +    THROTTLE_BPS_WRITE, DOUBLE, offsetof(LeakyBucket, avg),
> +},
> +{
> +    THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE_MAX,
> +    THROTTLE_BPS_WRITE, DOUBLE, offsetof(LeakyBucket, max),
> +},
> +{
> +    THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE_MAX_LENGTH,
> +    THROTTLE_BPS_WRITE, UNSIGNED, offsetof(LeakyBucket, burst_length),
> +},
> +{
> +    THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_SIZE,
> +    0, UINT64, offsetof(ThrottleConfig, op_size),
> +}
> +};
> +
> +static void throttle_group_obj_init(Object *obj)
> +{
> +    ThrottleGroup *tg = THROTTLE_GROUP(obj);
> +
> +    tg->clock_type = QEMU_CLOCK_REALTIME;
> +    if (qtest_enabled()) {
> +        /* For testing block IO throttling only */
> +        tg->clock_type = QEMU_CLOCK_VIRTUAL;
> +    }
> +    tg->is_initialized = false;
> +    QTAILQ_INSERT_TAIL(&throttle_groups, tg, list);

Is the lock taken when the object-add QMP command or -object
command-line argument are used?

In any case, please do not assume that throttle_groups_lock is held for
Object->init().  It should be possible to create new instances at any
time.

> +    qemu_mutex_init(&tg->lock);
> +    throttle_init(&tg->ts);
> +    QLIST_INIT(&tg->head);
> +}
> +
> +static void throttle_group_obj_complete(UserCreatable *obj, Error **errp)
> +{
> +    ThrottleGroup *tg = THROTTLE_GROUP(obj), *iter;
> +    ThrottleConfig *cfg = &tg->ts.cfg;
> +
> +    /* set group name to object id if it exists */
> +    if (!tg->name && tg->parent_obj.parent) {
> +        tg->name = object_get_canonical_path_component(OBJECT(obj));
> +    }
> +
> +    if (tg->name) {
> +        /* error if name is duplicate */
> +        QTAILQ_FOREACH(iter, &throttle_groups, list) {

Same locking issue: is throttle_groups_lock held here in all cases?  I
think it's not held in the object-add QMP and -object throttle-group
cases.

> +            if (!g_strcmp0(tg->name, iter->name) && tg != iter) {
> +                error_setg(errp, "A group with this name already exists");
> +                return;
> +            }
> +        }
> +    }
> +
> +    /* unfix buckets to check validity */
> +    throttle_get_config(&tg->ts, cfg);
> +    if (!throttle_is_valid(cfg, errp)) {
> +        return;
> +    }
> +    /* fix buckets again */
> +    throttle_config(&tg->ts, tg->clock_type, cfg);
> +
> +    tg->is_initialized = true;
> +}
> +
> +static void throttle_group_obj_finalize(Object *obj)
> +{
> +    ThrottleGroup *tg = THROTTLE_GROUP(obj);
> +    qemu_mutex_lock(&throttle_groups_lock);
> +    QTAILQ_REMOVE(&throttle_groups, tg, list);
> +    qemu_mutex_unlock(&throttle_groups_lock);
> +    qemu_mutex_destroy(&tg->lock);
> +    g_free(tg->name);
> +}
> +
> +static void throttle_group_set(Object *obj, Visitor *v, const char * name,
> +        void *opaque, Error **errp)
> +
> +{
> +    ThrottleGroup *tg = THROTTLE_GROUP(obj);
> +    ThrottleConfig cfg;
> +    ThrottleParamInfo *info = opaque;
> +    ThrottleLimits *arg = NULL;
> +    Error *local_err = NULL;
> +    int64_t value;
> +
> +    /* If we have finished initialization, don't accept individual property
> +     * changes through QOM. Throttle configuration limits must be set in one
> +     * transaction, as certain combinations are invalid.
> +     */
> +    if (tg->is_initialized) {
> +        error_setg(&local_err, "Property cannot be set after 
> initialization");
> +        goto fail;
> +    }
> +
> +    visit_type_int64(v, name, &value, &local_err);
> +    if (local_err) {
> +        goto fail;
> +    }
> +    if (value < 0) {
> +        error_setg(&local_err, "Property values cannot be negative");
> +        goto fail;
> +    }
> +
> +    cfg = tg->ts.cfg;
> +    switch (info->data_type) {
> +    case UINT64:
> +        {
> +            uint64_t *field = (void *)&cfg.buckets[info->type] + 
> info->offset;
> +            *field = value;
> +        }
> +        break;
> +    case DOUBLE:
> +        {
> +            double *field = (void *)&cfg.buckets[info->type] + info->offset;
> +            *field = value;
> +        }
> +        break;
> +    case UNSIGNED:
> +        {
> +            if (value > UINT_MAX) {
> +                error_setg(&local_err, "%s value must be in the"
> +                                       "range [0, %u]", info->name, 
> UINT_MAX);
> +                goto fail;
> +            }
> +            unsigned *field = (void *)&cfg.buckets[info->type] + 
> info->offset;
> +            *field = value;
> +        }
> +    }
> +
> +    tg->ts.cfg = cfg;
> +    goto ret;
> +
> +fail:
> +    error_propagate(errp, local_err);
> +ret:
> +    g_free(arg);
> +    return;

arg is unused, please drop it.

> +
> +}
> +
> +static void throttle_group_get(Object *obj, Visitor *v, const char *name,
> +        void *opaque, Error **errp)
> +{
> +    ThrottleGroup *tg = THROTTLE_GROUP(obj);
> +    ThrottleConfig cfg;
> +    ThrottleParamInfo *info = opaque;
> +    int64_t value;
> +
> +    cfg = tg->ts.cfg;
> +    switch (info->data_type) {
> +    case UINT64:
> +        {
> +            uint64_t *field = (void *)&cfg.buckets[info->type] + 
> info->offset;
> +            value = *field;
> +        }
> +        break;
> +    case DOUBLE:
> +        {
> +            double *field = (void *)&cfg.buckets[info->type] + info->offset;
> +            value = *field;
> +        }
> +        break;
> +    case UNSIGNED:
> +        {
> +            unsigned *field = (void *)&cfg.buckets[info->type] + 
> info->offset;
> +            value = *field;
> +        }
> +    }
> +
> +    visit_type_int64(v, name, &value, errp);
> +}
> +
> +static void throttle_group_set_limits(Object *obj, Visitor *v,
> +                                      const char *name, void *opaque,
> +                                      Error **errp)
> +
> +{
> +    ThrottleGroup *tg = THROTTLE_GROUP(obj);
> +    ThrottleConfig cfg;
> +    ThrottleLimits *arg = NULL;
> +    Error *local_err = NULL;
> +
> +    arg = g_new0(ThrottleLimits, 1);

Why does ThrottleLimits need to be allocated on the heap?

> +    visit_type_ThrottleLimits(v, name, &arg, &local_err);
> +    if (local_err) {
> +        goto fail;
> +    }
> +    qemu_mutex_lock(&tg->lock);
> +    throttle_get_config(&tg->ts, &cfg);
> +    throttle_limits_to_config(arg, &cfg, &local_err);
> +    if (local_err) {
> +        qemu_mutex_unlock(&tg->lock);
> +        goto fail;
> +    }
> +    throttle_config(&tg->ts, tg->clock_type, &cfg);
> +    qemu_mutex_unlock(&tg->lock);
> +
> +    goto ret;

error_propagate() is a nop if local_err is NULL so this goto is
unnecessary.

> +
> +fail:
> +    error_propagate(errp, local_err);
> +ret:
> +    g_free(arg);
> +    return;
> +}
> +
> +static void throttle_group_get_limits(Object *obj, Visitor *v,
> +                                      const char *name, void *opaque,
> +                                      Error **errp)
> +{
> +    ThrottleGroup *tg = THROTTLE_GROUP(obj);
> +    ThrottleConfig cfg;
> +    ThrottleLimits *arg = NULL;
> +
> +    arg = g_new0(ThrottleLimits, 1);
> +    qemu_mutex_lock(&tg->lock);
> +    throttle_get_config(&tg->ts, &cfg);
> +    qemu_mutex_unlock(&tg->lock);
> +    throttle_config_to_throttle_limits(&cfg, arg);
> +    visit_type_ThrottleLimits(v, name, &arg, errp);
> +    g_free(arg);
> +}
> +
> +static void throttle_group_obj_class_init(ObjectClass *klass, void 
> *class_data)
> +{
> +    size_t i = 0;
> +    UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass);
> +
> +    ucc->complete = throttle_group_obj_complete;
> +    /* individual properties */
> +    for (i = 0; i < sizeof(properties) / sizeof(ThrottleParamInfo); i++) {
> +        object_class_property_add(klass,
> +                properties[i].name,
> +                "int",
> +                throttle_group_get,
> +                throttle_group_set,
> +                NULL, &properties[i],
> +                &error_abort);
> +    }
> +
> +    /* ThrottleLimits */
> +    object_class_property_add(klass,
> +                              "limits", "ThrottleLimits",
> +                              throttle_group_get_limits,
> +                              throttle_group_set_limits,
> +                              NULL, NULL,
> +                              &error_abort);
> +}
> +
> +static const TypeInfo throttle_group_info = {
> +   .name = TYPE_THROTTLE_GROUP,
> +   .parent = TYPE_OBJECT,
> +   .class_init = throttle_group_obj_class_init,
> +   .instance_size = sizeof(ThrottleGroup),
> +   .instance_init = throttle_group_obj_init,
> +   .instance_finalize = throttle_group_obj_finalize,
> +   .interfaces = (InterfaceInfo[]) {
> +       { TYPE_USER_CREATABLE },
> +       { }
> +   },
> +};
> +
>  static void throttle_groups_init(void)
>  {
>      qemu_mutex_init(&throttle_groups_lock);
> +    type_register_static(&throttle_group_info);
>  }
>  
> -block_init(throttle_groups_init);
> +type_init(throttle_groups_init);
> diff --git a/include/block/throttle-groups.h b/include/block/throttle-groups.h
> index a0f27cac63..82f030523f 100644
> --- a/include/block/throttle-groups.h
> +++ b/include/block/throttle-groups.h
> @@ -53,6 +53,9 @@ typedef struct ThrottleGroupMember {
>  
>  } ThrottleGroupMember;
>  
> +#define TYPE_THROTTLE_GROUP "throttle-group"
> +#define THROTTLE_GROUP(obj) OBJECT_CHECK(ThrottleGroup, (obj), 
> TYPE_THROTTLE_GROUP)
> +
>  const char *throttle_group_get_name(ThrottleGroupMember *tgm);
>  
>  ThrottleState *throttle_group_incref(const char *name);
> diff --git a/include/qemu/throttle-options.h b/include/qemu/throttle-options.h
> index 3133d1ca40..182b7896e1 100644
> --- a/include/qemu/throttle-options.h
> +++ b/include/qemu/throttle-options.h
> @@ -10,81 +10,102 @@
>  #ifndef THROTTLE_OPTIONS_H
>  #define THROTTLE_OPTIONS_H
>  
> +#define QEMU_OPT_IOPS_TOTAL "iops-total"
> +#define QEMU_OPT_IOPS_TOTAL_MAX "iops-total-max"
> +#define QEMU_OPT_IOPS_TOTAL_MAX_LENGTH "iops-total-max-length"
> +#define QEMU_OPT_IOPS_READ "iops-read"
> +#define QEMU_OPT_IOPS_READ_MAX "iops-read-max"
> +#define QEMU_OPT_IOPS_READ_MAX_LENGTH "iops-read-max-length"
> +#define QEMU_OPT_IOPS_WRITE "iops-write"
> +#define QEMU_OPT_IOPS_WRITE_MAX "iops-write-max"
> +#define QEMU_OPT_IOPS_WRITE_MAX_LENGTH "iops-write-max-length"
> +#define QEMU_OPT_BPS_TOTAL "bps-total"
> +#define QEMU_OPT_BPS_TOTAL_MAX "bps-total-max"
> +#define QEMU_OPT_BPS_TOTAL_MAX_LENGTH "bps-total-max-length"
> +#define QEMU_OPT_BPS_READ "bps-read"
> +#define QEMU_OPT_BPS_READ_MAX "bps-read-max"
> +#define QEMU_OPT_BPS_READ_MAX_LENGTH "bps-read-max-length"
> +#define QEMU_OPT_BPS_WRITE "bps-write"
> +#define QEMU_OPT_BPS_WRITE_MAX "bps-write-max"
> +#define QEMU_OPT_BPS_WRITE_MAX_LENGTH "bps-write-max-length"
> +#define QEMU_OPT_IOPS_SIZE "iops-size"
> +
> +#define THROTTLE_OPT_PREFIX "throttling."
>  #define THROTTLE_OPTS \
>            { \
> -            .name = "throttling.iops-total",\
> +            .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL,\
>              .type = QEMU_OPT_NUMBER,\
>              .help = "limit total I/O operations per second",\
>          },{ \
> -            .name = "throttling.iops-read",\
> +            .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ,\
>              .type = QEMU_OPT_NUMBER,\
>              .help = "limit read operations per second",\
>          },{ \
> -            .name = "throttling.iops-write",\
> +            .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE,\
>              .type = QEMU_OPT_NUMBER,\
>              .help = "limit write operations per second",\
>          },{ \
> -            .name = "throttling.bps-total",\
> +            .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL,\
>              .type = QEMU_OPT_NUMBER,\
>              .help = "limit total bytes per second",\
>          },{ \
> -            .name = "throttling.bps-read",\
> +            .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ,\
>              .type = QEMU_OPT_NUMBER,\
>              .help = "limit read bytes per second",\
>          },{ \
> -            .name = "throttling.bps-write",\
> +            .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE,\
>              .type = QEMU_OPT_NUMBER,\
>              .help = "limit write bytes per second",\
>          },{ \
> -            .name = "throttling.iops-total-max",\
> +            .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL_MAX,\
>              .type = QEMU_OPT_NUMBER,\
>              .help = "I/O operations burst",\
>          },{ \
> -            .name = "throttling.iops-read-max",\
> +            .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ_MAX,\
>              .type = QEMU_OPT_NUMBER,\
>              .help = "I/O operations read burst",\
>          },{ \
> -            .name = "throttling.iops-write-max",\
> +            .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE_MAX,\
>              .type = QEMU_OPT_NUMBER,\
>              .help = "I/O operations write burst",\
>          },{ \
> -            .name = "throttling.bps-total-max",\
> +            .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL_MAX,\
>              .type = QEMU_OPT_NUMBER,\
>              .help = "total bytes burst",\
>          },{ \
> -            .name = "throttling.bps-read-max",\
> +            .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ_MAX,\
>              .type = QEMU_OPT_NUMBER,\
>              .help = "total bytes read burst",\
>          },{ \
> -            .name = "throttling.bps-write-max",\
> +            .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE_MAX,\
>              .type = QEMU_OPT_NUMBER,\
>              .help = "total bytes write burst",\
>          },{ \
> -            .name = "throttling.iops-total-max-length",\
> +            .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_TOTAL_MAX_LENGTH,\
>              .type = QEMU_OPT_NUMBER,\
>              .help = "length of the iops-total-max burst period, in seconds",\
>          },{ \
> -            .name = "throttling.iops-read-max-length",\
> +            .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_READ_MAX_LENGTH,\
>              .type = QEMU_OPT_NUMBER,\
>              .help = "length of the iops-read-max burst period, in seconds",\
>          },{ \
> -            .name = "throttling.iops-write-max-length",\
> +            .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_WRITE_MAX_LENGTH,\
>              .type = QEMU_OPT_NUMBER,\
>              .help = "length of the iops-write-max burst period, in seconds",\
>          },{ \
> -            .name = "throttling.bps-total-max-length",\
> +            .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_TOTAL_MAX_LENGTH,\
>              .type = QEMU_OPT_NUMBER,\
>              .help = "length of the bps-total-max burst period, in seconds",\
>          },{ \
> -            .name = "throttling.bps-read-max-length",\
> +            .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_READ_MAX_LENGTH,\
>              .type = QEMU_OPT_NUMBER,\
>              .help = "length of the bps-read-max burst period, in seconds",\
>          },{ \
> -            .name = "throttling.bps-write-max-length",\
> +            .name = THROTTLE_OPT_PREFIX QEMU_OPT_BPS_WRITE_MAX_LENGTH,\
>              .type = QEMU_OPT_NUMBER,\
>              .help = "length of the bps-write-max burst period, in seconds",\
>          },{ \
> -            .name = "throttling.iops-size",\
> +            .name = THROTTLE_OPT_PREFIX QEMU_OPT_IOPS_SIZE,\
>              .type = QEMU_OPT_NUMBER,\
>              .help = "when limiting by iops max size of an I/O in bytes",\
>          }
> diff --git a/include/qemu/throttle.h b/include/qemu/throttle.h
> index d056008c18..17e750b12d 100644
> --- a/include/qemu/throttle.h
> +++ b/include/qemu/throttle.h
> @@ -152,5 +152,8 @@ bool throttle_schedule_timer(ThrottleState *ts,
>                               bool is_write);
>  
>  void throttle_account(ThrottleState *ts, bool is_write, uint64_t size);
> +void throttle_limits_to_config(ThrottleLimits *arg, ThrottleConfig *cfg,
> +                               Error **errp);
> +void throttle_config_to_throttle_limits(ThrottleConfig *cfg, ThrottleLimits 
> *var);
>  
>  #endif
> diff --git a/qapi/block-core.json b/qapi/block-core.json
> index 833c602150..0bdc69aa5f 100644
> --- a/qapi/block-core.json
> +++ b/qapi/block-core.json
> @@ -1905,6 +1905,54 @@
>              '*iops_size': 'int', '*group': 'str' } }
>  
>  ##
> +# @ThrottleLimits:
> +#
> +# Limit parameters for throttling.
> +# Since some limit combinations are illegal, limits should always be set in 
> one
> +# transaction. All fields are optional. When setting limits, if a field is
> +# missing the current value is not changed.
> +#
> +# @iops-total:             limit total I/O operations per second
> +# @iops-total-max:         I/O operations burst
> +# @iops-total-max-length:  length of the iops-total-max burst period, in 
> seconds
> +#                          It must only be set if @iops-total-max is set as 
> well.
> +# @iops-read:              limit read operations per second
> +# @iops-read-max:          I/O operations read burst
> +# @iops-read-max-length:   length of the iops-read-max burst period, in 
> seconds
> +#                          It must only be set if @iops-read-max is set as 
> well.
> +# @iops-write:             limit write operations per second
> +# @iops-write-max:         I/O operations write burst
> +# @iops-write-max-length:  length of the iops-write-max burst period, in 
> seconds
> +#                          It must only be set if @iops-write-max is set as 
> well.
> +# @bps-total:              limit total bytes per second
> +# @bps-total-max:          total bytes burst
> +# @bps-total-max-length:   length of the bps-total-max burst period, in 
> seconds.
> +#                          It must only be set if @bps-total-max is set as 
> well.
> +# @bps-read:               limit read bytes per second
> +# @bps-read-max:           total bytes read burst
> +# @bps-read-max-length:    length of the bps-read-max burst period, in 
> seconds
> +#                          It must only be set if @bps-read-max is set as 
> well.
> +# @bps-write:              limit write bytes per second
> +# @bps-write-max:          total bytes write burst
> +# @bps-write-max-length:   length of the bps-write-max burst period, in 
> seconds
> +#                          It must only be set if @bps-write-max is set as 
> well.
> +# @iops-size:              when limiting by iops max size of an I/O in bytes
> +#
> +# Since: 2.11
> +##
> +{ 'struct': 'ThrottleLimits',
> +  'data': { '*iops-total' : 'int', '*iops-total-max' : 'int',
> +            '*iops-total-max-length' : 'int', '*iops-read' : 'int',
> +            '*iops-read-max' : 'int', '*iops-read-max-length' : 'int',
> +            '*iops-write' : 'int', '*iops-write-max' : 'int',
> +            '*iops-write-max-length' : 'int', '*bps-total' : 'int',
> +            '*bps-total-max' : 'int', '*bps-total-max-length' : 'int',
> +            '*bps-read' : 'int', '*bps-read-max' : 'int',
> +            '*bps-read-max-length' : 'int', '*bps-write' : 'int',
> +            '*bps-write-max' : 'int', '*bps-write-max-length' : 'int',
> +            '*iops-size' : 'int' } }
> +
> +##
>  # @block-stream:
>  #
>  # Copy data from a backing file into a block device.
> diff --git a/tests/test-throttle.c b/tests/test-throttle.c
> index 57cf5ba711..0ea9093eee 100644
> --- a/tests/test-throttle.c
> +++ b/tests/test-throttle.c
> @@ -662,6 +662,7 @@ int main(int argc, char **argv)
>      qemu_init_main_loop(&error_fatal);
>      ctx = qemu_get_aio_context();
>      bdrv_init();
> +    module_call_init(MODULE_INIT_QOM);
>  
>      do {} while (g_main_context_iteration(NULL, false));
>  
> diff --git a/util/throttle.c b/util/throttle.c
> index b2a52b8b34..99fd73157b 100644
> --- a/util/throttle.c
> +++ b/util/throttle.c
> @@ -502,3 +502,154 @@ void throttle_account(ThrottleState *ts, bool is_write, 
> uint64_t size)
>      }
>  }
>  
> +/* return a ThrottleConfig based on the options in a ThrottleLimits
> + *
> + * @arg:    the ThrottleLimits object to read from
> + * @cfg:    the ThrottleConfig to edit
> + * @errp:   error object
> + */
> +void throttle_limits_to_config(ThrottleLimits *arg, ThrottleConfig *cfg,
> +                               Error **errp)
> +{
> +    if (arg->has_bps_total) {
> +        cfg->buckets[THROTTLE_BPS_TOTAL].avg = arg->bps_total;
> +    }
> +    if (arg->has_bps_read) {
> +        cfg->buckets[THROTTLE_BPS_READ].avg  = arg->bps_read;
> +    }
> +    if (arg->has_bps_write) {
> +        cfg->buckets[THROTTLE_BPS_WRITE].avg = arg->bps_write;
> +    }
> +
> +    if (arg->has_iops_total) {
> +        cfg->buckets[THROTTLE_OPS_TOTAL].avg = arg->iops_total;
> +    }
> +    if (arg->has_iops_read) {
> +        cfg->buckets[THROTTLE_OPS_READ].avg  = arg->iops_read;
> +    }
> +    if (arg->has_iops_write) {
> +        cfg->buckets[THROTTLE_OPS_WRITE].avg = arg->iops_write;
> +    }
> +
> +    if (arg->has_bps_total_max) {
> +        cfg->buckets[THROTTLE_BPS_TOTAL].max = arg->bps_total_max;
> +    }
> +    if (arg->has_bps_read_max) {
> +        cfg->buckets[THROTTLE_BPS_READ].max = arg->bps_read_max;
> +    }
> +    if (arg->has_bps_write_max) {
> +        cfg->buckets[THROTTLE_BPS_WRITE].max = arg->bps_write_max;
> +    }
> +    if (arg->has_iops_total_max) {
> +        cfg->buckets[THROTTLE_OPS_TOTAL].max = arg->iops_total_max;
> +    }
> +    if (arg->has_iops_read_max) {
> +        cfg->buckets[THROTTLE_OPS_READ].max = arg->iops_read_max;
> +    }
> +    if (arg->has_iops_write_max) {
> +        cfg->buckets[THROTTLE_OPS_WRITE].max = arg->iops_write_max;
> +    }
> +
> +    if (arg->has_bps_total_max_length) {
> +        if (arg->bps_total_max_length > UINT_MAX) {
> +            error_setg(errp, "bps-total-max-length value must be in"
> +                    " the range [0, %u]", UINT_MAX);
> +            return;
> +        }
> +        cfg->buckets[THROTTLE_BPS_TOTAL].burst_length = 
> arg->bps_total_max_length;
> +    }
> +    if (arg->has_bps_read_max_length) {
> +        if (arg->bps_read_max_length > UINT_MAX) {
> +            error_setg(errp, "bps-read-max-length value must be in"
> +                    " the range [0, %u]", UINT_MAX);
> +            return;
> +        }
> +        cfg->buckets[THROTTLE_BPS_READ].burst_length = 
> arg->bps_read_max_length;
> +    }
> +    if (arg->has_bps_write_max_length) {
> +        if (arg->bps_write_max_length > UINT_MAX) {
> +            error_setg(errp, "bps-write-max-length value must be in"
> +                    " the range [0, %u]", UINT_MAX);
> +            return;
> +        }
> +        cfg->buckets[THROTTLE_BPS_WRITE].burst_length = 
> arg->bps_write_max_length;
> +    }
> +    if (arg->has_iops_total_max_length) {
> +        if (arg->iops_total_max_length > UINT_MAX) {
> +            error_setg(errp, "iops-total-max-length value must be in"
> +                    " the range [0, %u]", UINT_MAX);
> +            return;
> +        }
> +        cfg->buckets[THROTTLE_OPS_TOTAL].burst_length = 
> arg->iops_total_max_length;
> +    }
> +    if (arg->has_iops_read_max_length) {
> +        if (arg->iops_read_max_length > UINT_MAX) {
> +            error_setg(errp, "iops-read-max-length value must be in"
> +                    " the range [0, %u]", UINT_MAX);
> +            return;
> +        }
> +        cfg->buckets[THROTTLE_OPS_READ].burst_length = 
> arg->iops_read_max_length;
> +    }
> +    if (arg->has_iops_write_max_length) {
> +        if (arg->iops_write_max_length > UINT_MAX) {
> +            error_setg(errp, "iops-write-max-length value must be in"
> +                    " the range [0, %u]", UINT_MAX);
> +            return;
> +        }
> +        cfg->buckets[THROTTLE_OPS_WRITE].burst_length = 
> arg->iops_write_max_length;
> +    }
> +
> +    if (arg->has_iops_size) {
> +        cfg->op_size = arg->iops_size;
> +    }
> +
> +    throttle_is_valid(cfg, errp);
> +}
> +
> +/* write the options of a ThrottleConfig to a ThrottleLimits
> + *
> + * @cfg:    the ThrottleConfig to read from
> + * @var:    the ThrottleLimits to write to
> + */
> +void throttle_config_to_throttle_limits(ThrottleConfig *cfg, ThrottleLimits 
> *var)
> +{
> +    var->bps_total               = cfg->buckets[THROTTLE_BPS_TOTAL].avg;
> +    var->bps_read                = cfg->buckets[THROTTLE_BPS_READ].avg;
> +    var->bps_write               = cfg->buckets[THROTTLE_BPS_WRITE].avg;
> +    var->iops_total              = cfg->buckets[THROTTLE_OPS_TOTAL].avg;
> +    var->iops_read               = cfg->buckets[THROTTLE_OPS_READ].avg;
> +    var->iops_write              = cfg->buckets[THROTTLE_OPS_WRITE].avg;
> +    var->bps_total_max           = cfg->buckets[THROTTLE_BPS_TOTAL].max;
> +    var->bps_read_max            = cfg->buckets[THROTTLE_BPS_READ].max;
> +    var->bps_write_max           = cfg->buckets[THROTTLE_BPS_WRITE].max;
> +    var->iops_total_max          = cfg->buckets[THROTTLE_OPS_TOTAL].max;
> +    var->iops_read_max           = cfg->buckets[THROTTLE_OPS_READ].max;
> +    var->iops_write_max          = cfg->buckets[THROTTLE_OPS_WRITE].max;
> +    var->bps_total_max_length    = 
> cfg->buckets[THROTTLE_BPS_TOTAL].burst_length;
> +    var->bps_read_max_length     = 
> cfg->buckets[THROTTLE_BPS_READ].burst_length;
> +    var->bps_write_max_length    = 
> cfg->buckets[THROTTLE_BPS_WRITE].burst_length;
> +    var->iops_total_max_length   = 
> cfg->buckets[THROTTLE_OPS_TOTAL].burst_length;
> +    var->iops_read_max_length    = 
> cfg->buckets[THROTTLE_OPS_READ].burst_length;
> +    var->iops_write_max_length   = 
> cfg->buckets[THROTTLE_OPS_WRITE].burst_length;
> +    var->iops_size               = cfg->op_size;
> +
> +    var->has_bps_total = true;
> +    var->has_bps_read = true;
> +    var->has_bps_write = true;
> +    var->has_iops_total = true;
> +    var->has_iops_read = true;
> +    var->has_iops_write = true;
> +    var->has_bps_total_max = true;
> +    var->has_bps_read_max = true;
> +    var->has_bps_write_max = true;
> +    var->has_iops_total_max = true;
> +    var->has_iops_read_max = true;
> +    var->has_iops_write_max = true;
> +    var->has_bps_read_max_length = true;
> +    var->has_bps_total_max_length = true;
> +    var->has_bps_write_max_length = true;
> +    var->has_iops_total_max_length = true;
> +    var->has_iops_read_max_length = true;
> +    var->has_iops_write_max_length = true;
> +    var->has_iops_size = true;
> +}
> -- 
> 2.11.0
> 

Attachment: signature.asc
Description: PGP signature


reply via email to

[Prev in Thread] Current Thread [Next in Thread]