From patchwork Tue Aug 12 13:42:24 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Beno=C3=AEt_Canet?= X-Patchwork-Id: 379361 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 73867140087 for ; Tue, 12 Aug 2014 23:57:07 +1000 (EST) Received: from localhost ([::1]:42242 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1XHCZV-0003GX-KZ for incoming@patchwork.ozlabs.org; Tue, 12 Aug 2014 09:57:05 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:41521) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1XHCMH-0000br-QB for qemu-devel@nongnu.org; Tue, 12 Aug 2014 09:43:30 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1XHCMC-0001W4-Im for qemu-devel@nongnu.org; Tue, 12 Aug 2014 09:43:25 -0400 Received: from lputeaux-656-01-25-125.w80-12.abo.wanadoo.fr ([80.12.84.125]:41989 helo=paradis.irqsave.net) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1XHCMC-0001VR-4L for qemu-devel@nongnu.org; Tue, 12 Aug 2014 09:43:20 -0400 Received: from localhost.localdomain (laure.irqsave.net [192.168.77.2]) by paradis.irqsave.net (Postfix) with ESMTP id B52A0CB501; Tue, 12 Aug 2014 15:43:17 +0200 (CEST) From: =?UTF-8?q?Beno=C3=AEt=20Canet?= To: qemu-devel@nongnu.org Date: Tue, 12 Aug 2014 15:42:24 +0200 Message-Id: <1407850947-17625-3-git-send-email-benoit.canet@irqsave.net> X-Mailer: git-send-email 2.1.0.rc1 In-Reply-To: <1407850947-17625-1-git-send-email-benoit.canet@irqsave.net> References: <1407850947-17625-1-git-send-email-benoit.canet@irqsave.net> MIME-Version: 1.0 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x [generic] X-Received-From: 80.12.84.125 Cc: kwolf@redhat.com, =?UTF-8?q?Beno=C3=AEt=20Canet?= , Benoit Canet , stefanha@redhat.com Subject: [Qemu-devel] [RFC 2/5] throttle: Add throttle group infrastructure. X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org The throttle_group_incref increment the refcount of a throttle group given it's name and return the associated throttle state. The throttle_group_unref is the mirror function for cleaning up. Signed-off-by: Benoit Canet --- include/block/block_int.h | 1 + include/qemu/throttle-groups.h | 43 +++++++++ util/Makefile.objs | 1 + util/throttle-groups.c | 195 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 240 insertions(+) create mode 100644 include/qemu/throttle-groups.h create mode 100644 util/throttle-groups.c diff --git a/include/block/block_int.h b/include/block/block_int.h index 2a0c146..6066f63 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -339,6 +339,7 @@ struct BlockDriverState { ThrottleTimers throttle_timers; CoQueue throttled_reqs[2]; bool io_limits_enabled; + QLIST_ENTRY(BlockDriverState) round_robin; /* I/O stats (display with "info blockstats"). */ uint64_t nr_bytes[BDRV_MAX_IOTYPE]; diff --git a/include/qemu/throttle-groups.h b/include/qemu/throttle-groups.h new file mode 100644 index 0000000..12c56e6 --- /dev/null +++ b/include/qemu/throttle-groups.h @@ -0,0 +1,43 @@ +/* + * QEMU throttling group infrastructure + * + * Copyright (C) Nodalink, EURL. 2014 + * + * Author: + * Benoît Canet + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#ifndef THROTTLE_GROUPS_H +#define THROTTLE_GROUPS_H + +#include "qemu/throttle.h" +#include "block/block_int.h" + +ThrottleState *throttle_group_incref(const char *name); +bool throttle_group_unref(ThrottleState *ts); + +void throttle_group_register_bs(ThrottleState *ts, BlockDriverState *bs); +BlockDriverState *throttle_group_next_bs(BlockDriverState *bs); + +void throttle_group_set_token(ThrottleState *ts, + BlockDriverState *token, + bool is_write); +BlockDriverState *throttle_group_token(ThrottleState *ts, bool is_write); + +void throttle_group_lock(ThrottleState *ts); +void throttle_group_unlock(ThrottleState *ts); + +#endif diff --git a/util/Makefile.objs b/util/Makefile.objs index 6b3c83b..3c95397 100644 --- a/util/Makefile.objs +++ b/util/Makefile.objs @@ -12,6 +12,7 @@ util-obj-y += qemu-option.o qemu-progress.o util-obj-y += hexdump.o util-obj-y += crc32c.o util-obj-y += throttle.o +util-obj-y += throttle-groups.o util-obj-y += getauxval.o util-obj-y += readline.o util-obj-y += rfifolock.o diff --git a/util/throttle-groups.c b/util/throttle-groups.c new file mode 100644 index 0000000..153bacc --- /dev/null +++ b/util/throttle-groups.c @@ -0,0 +1,195 @@ +/* + * QEMU throttling group infrastructure + * + * Copyright (C) Nodalink, EURL. 2014 + * + * Author: + * Benoît Canet + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + */ + +#include "qemu/throttle-groups.h" +#include "qemu/queue.h" +#include "qemu/thread.h" + +typedef struct ThrottleGroup { + char name[32]; + ThrottleState ts; + uint64_t refcount; + QTAILQ_ENTRY(ThrottleGroup) list; + QLIST_HEAD(, BlockDriverState) head; + BlockDriverState *tokens[2]; /* current round-robin tokens */ + QemuMutex lock; /* Used to synchronize all elements belonging to a group */ +} ThrottleGroup; + +static QTAILQ_HEAD(, ThrottleGroup) throttle_groups = + QTAILQ_HEAD_INITIALIZER(throttle_groups); + +/* increments a ThrottleGroup reference count given it's name + * + * If no ThrottleGroup is found with the given name a new one is created. + * + * @name: the name of the ThrottleGroup + * @ret: the ThrottleGroup's ThrottleState address + */ +ThrottleState *throttle_group_incref(const char *name) +{ + ThrottleGroup *tg; + + /* return the correct ThrottleState if a group with this name exists */ + QTAILQ_FOREACH(tg, &throttle_groups, list) { + /* group not found -> continue */ + if (strcmp(name, tg->name)) { + continue; + } + /* group found -> increment it's refcount and return ThrottleState */ + tg->refcount++; + return &tg->ts; + } + + /* throttle group not found -> prepare new entry */ + tg = g_new0(ThrottleGroup, 1); + throttle_init(&tg->ts); + pstrcpy(tg->name, sizeof(tg->name), name); + tg->refcount = 1; + qemu_mutex_init(&tg->lock); + QLIST_INIT(&tg->head); + + /* insert new entry in the list */ + QTAILQ_INSERT_TAIL(&throttle_groups, tg, list); + + /* return newly allocated ThrottleState */ + return &tg->ts; +} + +/* decrement a ThrottleGroup given it's ThrottleState address + * + * When the refcount reach zero the ThrottleGroup is destroyed + * + * @ts: The ThrottleState address belonging to the ThrottleGroup to unref + * @ret: true on success else false + */ +bool throttle_group_unref(ThrottleState *ts) +{ + ThrottleGroup *tg; + bool found = false; + + /* Find the ThrottleGroup of the given ThrottleState */ + QTAILQ_FOREACH(tg, &throttle_groups, list) { + /* correct group found stop iterating */ + if (&tg->ts == ts) { + qemu_mutex_lock(&tg->lock); + found = true; + break; + } + } + + /* If the ThrottleState was not found something is seriously broken */ + if (!found) { + return false; + } + + tg->refcount--; + + /* If ThrottleGroup is used keep it. */ + if (tg->refcount) { + qemu_mutex_unlock(&tg->lock); + return true; + } + + /* Else destroy it */ + QTAILQ_REMOVE(&throttle_groups, tg, list); + qemu_mutex_unlock(&tg->lock); + qemu_mutex_destroy(&tg->lock); + g_free(tg); + return true; +} + +/* Register a BlockDriverState in the doubly linked list + * + * @ts: the ThrottleState the code is working on + * @bs: the BlockDriverState to insert + */ +void throttle_group_register_bs(ThrottleState *ts, BlockDriverState *bs) +{ + ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); + QLIST_INSERT_HEAD(&tg->head, bs, round_robin); +} + +/* Return the next BlockDriverState in the round-robin sequence + * + * This takes care of simulating a circular list + * + * @bs: the input current BlockDriverState + * @ret: the next BlockDriverState in the sequence + */ +BlockDriverState *throttle_group_next_bs(BlockDriverState *bs) +{ + ThrottleState *ts = &bs->throttle_state; + ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); + BlockDriverState *next = QLIST_NEXT(bs, round_robin); + + if (!next) { + return QLIST_FIRST(&tg->head); + } + + return next; +} + +/* Used to set a token BlockDriverState into a ThrottleState's ThrottleGroup + * + * @ts: the ThrottleState the code is working on + * @token: the token BlockDriverState to set + * @is_write: true if we are talking about write operations else false + */ +void throttle_group_set_token(ThrottleState *ts, + BlockDriverState *token, + bool is_write) +{ + ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); + tg->tokens[is_write] = token; +} + +/* Used to get the token BlockDriverState of a ThrottleState's ThrottleGroup + * + * @ts: the ThrottleState the code is working on + * @ret: the token BlockDriverState that is retrieved + * @is_write: true if we are talking about write operations else false + */ +BlockDriverState *throttle_group_token(ThrottleState *ts, bool is_write) +{ + ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); + return tg->tokens[is_write]; +} + +/* Used to lock a ThrottleState's ThrottleGroup + * + * @ts: the ThrottleState the code is working on + */ +void throttle_group_lock(ThrottleState *ts) +{ + ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); + qemu_mutex_lock(&tg->lock); +} + +/* Used to unlock a ThrottleState's ThrottleGroup + * + * @ts: the ThrottleState the code is working on + */ +void throttle_group_unlock(ThrottleState *ts) +{ + ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); + qemu_mutex_unlock(&tg->lock); +}