diff options
72 files changed, 2572 insertions, 850 deletions
@@ -280,6 +280,12 @@ static void aio_timerlist_notify(void *opaque) aio_notify(opaque); } +static void aio_rfifolock_cb(void *opaque) +{ + /* Kick owner thread in case they are blocked in aio_poll() */ + aio_notify(opaque); +} + AioContext *aio_context_new(Error **errp) { int ret; @@ -297,7 +303,7 @@ AioContext *aio_context_new(Error **errp) event_notifier_test_and_clear); ctx->thread_pool = NULL; qemu_mutex_init(&ctx->bh_lock); - rfifolock_init(&ctx->lock, NULL, NULL); + rfifolock_init(&ctx->lock, aio_rfifolock_cb, ctx); timerlistgroup_init(&ctx->tlg, aio_timerlist_notify, ctx); return ctx; diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c index 74ead97d87..ed7655de86 100644 --- a/audio/alsaaudio.c +++ b/audio/alsaaudio.c @@ -266,31 +266,19 @@ static int alsa_poll_helper (snd_pcm_t *handle, struct pollhlp *hlp, int mask) for (i = 0; i < count; ++i) { if (pfds[i].events & POLLIN) { - err = qemu_set_fd_handler (pfds[i].fd, alsa_poll_handler, - NULL, hlp); + qemu_set_fd_handler (pfds[i].fd, alsa_poll_handler, NULL, hlp); } if (pfds[i].events & POLLOUT) { if (conf.verbose) { dolog ("POLLOUT %d %d\n", i, pfds[i].fd); } - err = qemu_set_fd_handler (pfds[i].fd, NULL, - alsa_poll_handler, hlp); + qemu_set_fd_handler (pfds[i].fd, NULL, alsa_poll_handler, hlp); } if (conf.verbose) { dolog ("Set handler events=%#x index=%d fd=%d err=%d\n", pfds[i].events, i, pfds[i].fd, err); } - if (err) { - dolog ("Failed to set handler events=%#x index=%d fd=%d err=%d\n", - pfds[i].events, i, pfds[i].fd, err); - - while (i--) { - qemu_set_fd_handler (pfds[i].fd, NULL, NULL, NULL); - } - g_free (pfds); - return -1; - } } hlp->pfds = pfds; hlp->count = count; diff --git a/audio/ossaudio.c b/audio/ossaudio.c index 4db2ca65bf..b9c6b30ca1 100644 --- a/audio/ossaudio.c +++ b/audio/ossaudio.c @@ -138,18 +138,18 @@ static void oss_helper_poll_in (void *opaque) audio_run ("oss_poll_in"); } -static int oss_poll_out (HWVoiceOut *hw) +static void oss_poll_out (HWVoiceOut *hw) { OSSVoiceOut *oss = (OSSVoiceOut *) hw; - return qemu_set_fd_handler (oss->fd, NULL, oss_helper_poll_out, NULL); + qemu_set_fd_handler (oss->fd, NULL, oss_helper_poll_out, NULL); } -static int oss_poll_in (HWVoiceIn *hw) +static void oss_poll_in (HWVoiceIn *hw) { OSSVoiceIn *oss = (OSSVoiceIn *) hw; - return qemu_set_fd_handler (oss->fd, oss_helper_poll_in, NULL, NULL); + qemu_set_fd_handler (oss->fd, oss_helper_poll_in, NULL, NULL); } static int oss_write (SWVoiceOut *sw, void *buf, int len) @@ -634,7 +634,8 @@ static int oss_ctl_out (HWVoiceOut *hw, int cmd, ...) va_end (ap); ldebug ("enabling voice\n"); - if (poll_mode && oss_poll_out (hw)) { + if (poll_mode) { + oss_poll_out (hw); poll_mode = 0; } hw->poll_mode = poll_mode; @@ -828,7 +829,8 @@ static int oss_ctl_in (HWVoiceIn *hw, int cmd, ...) poll_mode = va_arg (ap, int); va_end (ap); - if (poll_mode && oss_poll_in (hw)) { + if (poll_mode) { + oss_poll_in (hw); poll_mode = 0; } hw->poll_mode = poll_mode; @@ -36,6 +36,7 @@ #include "qmp-commands.h" #include "qemu/timer.h" #include "qapi-event.h" +#include "block/throttle-groups.h" #ifdef CONFIG_BSD #include <sys/types.h> @@ -1951,12 +1952,18 @@ static void bdrv_move_feature_fields(BlockDriverState *bs_dest, bs_dest->enable_write_cache = bs_src->enable_write_cache; /* i/o throttled req */ - memcpy(&bs_dest->throttle_state, - &bs_src->throttle_state, - sizeof(ThrottleState)); + bs_dest->throttle_state = bs_src->throttle_state, + bs_dest->io_limits_enabled = bs_src->io_limits_enabled; + bs_dest->pending_reqs[0] = bs_src->pending_reqs[0]; + bs_dest->pending_reqs[1] = bs_src->pending_reqs[1]; bs_dest->throttled_reqs[0] = bs_src->throttled_reqs[0]; bs_dest->throttled_reqs[1] = bs_src->throttled_reqs[1]; - bs_dest->io_limits_enabled = bs_src->io_limits_enabled; + memcpy(&bs_dest->round_robin, + &bs_src->round_robin, + sizeof(bs_dest->round_robin)); + memcpy(&bs_dest->throttle_timers, + &bs_src->throttle_timers, + sizeof(ThrottleTimers)); /* r/w error */ bs_dest->on_read_error = bs_src->on_read_error; @@ -2014,12 +2021,21 @@ void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old) QTAILQ_REMOVE(&graph_bdrv_states, bs_old, node_list); } + /* If the BlockDriverState is part of a throttling group acquire + * its lock since we're going to mess with the protected fields. + * Otherwise there's no need to worry since no one else can touch + * them. */ + if (bs_old->throttle_state) { + throttle_group_lock(bs_old); + } + /* bs_new must be unattached and shouldn't have anything fancy enabled */ assert(!bs_new->blk); assert(QLIST_EMPTY(&bs_new->dirty_bitmaps)); assert(bs_new->job == NULL); assert(bs_new->io_limits_enabled == false); - assert(!throttle_have_timer(&bs_new->throttle_state)); + assert(bs_new->throttle_state == NULL); + assert(!throttle_timers_are_initialized(&bs_new->throttle_timers)); tmp = *bs_new; *bs_new = *bs_old; @@ -2036,7 +2052,13 @@ void bdrv_swap(BlockDriverState *bs_new, BlockDriverState *bs_old) /* Check a few fields that should remain attached to the device */ assert(bs_new->job == NULL); assert(bs_new->io_limits_enabled == false); - assert(!throttle_have_timer(&bs_new->throttle_state)); + assert(bs_new->throttle_state == NULL); + assert(!throttle_timers_are_initialized(&bs_new->throttle_timers)); + + /* Release the ThrottleGroup lock */ + if (bs_old->throttle_state) { + throttle_group_unlock(bs_old); + } /* insert the nodes back into the graph node list if needed */ if (bs_new->node_name[0] != '\0') { @@ -3848,7 +3870,7 @@ void bdrv_detach_aio_context(BlockDriverState *bs) } if (bs->io_limits_enabled) { - throttle_detach_aio_context(&bs->throttle_state); + throttle_timers_detach_aio_context(&bs->throttle_timers); } if (bs->drv->bdrv_detach_aio_context) { bs->drv->bdrv_detach_aio_context(bs); @@ -3884,7 +3906,7 @@ void bdrv_attach_aio_context(BlockDriverState *bs, bs->drv->bdrv_attach_aio_context(bs, new_context); } if (bs->io_limits_enabled) { - throttle_attach_aio_context(&bs->throttle_state, new_context); + throttle_timers_attach_aio_context(&bs->throttle_timers, new_context); } QLIST_FOREACH(ban, &bs->aio_notifiers, list) { diff --git a/block/Makefile.objs b/block/Makefile.objs index 0d8c2a4ab6..c34fd7cdc2 100644 --- a/block/Makefile.objs +++ b/block/Makefile.objs @@ -10,6 +10,7 @@ block-obj-$(CONFIG_WIN32) += raw-win32.o win32-aio.o block-obj-$(CONFIG_POSIX) += raw-posix.o block-obj-$(CONFIG_LINUX_AIO) += linux-aio.o block-obj-y += null.o mirror.o io.o +block-obj-y += throttle-groups.o block-obj-y += nbd.o nbd-client.o sheepdog.o block-obj-$(CONFIG_LIBISCSI) += iscsi.o diff --git a/block/io.c b/block/io.c index e394d92626..bb4f78784e 100644 --- a/block/io.c +++ b/block/io.c @@ -23,9 +23,9 @@ */ #include "trace.h" -#include "sysemu/qtest.h" #include "block/blockjob.h" #include "block/block_int.h" +#include "block/throttle-groups.h" #define NOT_DONE 0x7fffffff /* used while emulated sync operation in progress */ @@ -65,7 +65,7 @@ void bdrv_set_io_limits(BlockDriverState *bs, { int i; - throttle_config(&bs->throttle_state, cfg); + throttle_group_config(bs, cfg); for (i = 0; i < 2; i++) { qemu_co_enter_next(&bs->throttled_reqs[i]); @@ -95,72 +95,33 @@ static bool bdrv_start_throttled_reqs(BlockDriverState *bs) void bdrv_io_limits_disable(BlockDriverState *bs) { bs->io_limits_enabled = false; - bdrv_start_throttled_reqs(bs); - - throttle_destroy(&bs->throttle_state); -} - -static void bdrv_throttle_read_timer_cb(void *opaque) -{ - BlockDriverState *bs = opaque; - qemu_co_enter_next(&bs->throttled_reqs[0]); -} - -static void bdrv_throttle_write_timer_cb(void *opaque) -{ - BlockDriverState *bs = opaque; - qemu_co_enter_next(&bs->throttled_reqs[1]); + throttle_group_unregister_bs(bs); } /* should be called before bdrv_set_io_limits if a limit is set */ -void bdrv_io_limits_enable(BlockDriverState *bs) +void bdrv_io_limits_enable(BlockDriverState *bs, const char *group) { - int clock_type = QEMU_CLOCK_REALTIME; - - if (qtest_enabled()) { - /* For testing block IO throttling only */ - clock_type = QEMU_CLOCK_VIRTUAL; - } assert(!bs->io_limits_enabled); - throttle_init(&bs->throttle_state, - bdrv_get_aio_context(bs), - clock_type, - bdrv_throttle_read_timer_cb, - bdrv_throttle_write_timer_cb, - bs); + throttle_group_register_bs(bs, group); bs->io_limits_enabled = true; } -/* This function makes an IO wait if needed - * - * @nb_sectors: the number of sectors of the IO - * @is_write: is the IO a write - */ -static void bdrv_io_limits_intercept(BlockDriverState *bs, - unsigned int bytes, - bool is_write) +void bdrv_io_limits_update_group(BlockDriverState *bs, const char *group) { - /* does this io must wait */ - bool must_wait = throttle_schedule_timer(&bs->throttle_state, is_write); - - /* if must wait or any request of this type throttled queue the IO */ - if (must_wait || - !qemu_co_queue_empty(&bs->throttled_reqs[is_write])) { - qemu_co_queue_wait(&bs->throttled_reqs[is_write]); + /* this bs is not part of any group */ + if (!bs->throttle_state) { + return; } - /* the IO will be executed, do the accounting */ - throttle_account(&bs->throttle_state, is_write, bytes); - - - /* if the next request must wait -> do nothing */ - if (throttle_schedule_timer(&bs->throttle_state, is_write)) { + /* this bs is a part of the same group than the one we want */ + if (!g_strcmp0(throttle_group_get_name(bs), group)) { return; } - /* else queue next request for execution */ - qemu_co_queue_next(&bs->throttled_reqs[is_write]); + /* need to change the group this bs belong to */ + bdrv_io_limits_disable(bs); + bdrv_io_limits_enable(bs, group); } void bdrv_setup_io_funcs(BlockDriver *bdrv) @@ -967,7 +928,7 @@ static int coroutine_fn bdrv_co_do_preadv(BlockDriverState *bs, /* throttling disk I/O */ if (bs->io_limits_enabled) { - bdrv_io_limits_intercept(bs, bytes, false); + throttle_group_co_io_limits_intercept(bs, bytes, false); } /* Align read if necessary by padding qiov */ @@ -1297,7 +1258,7 @@ static int coroutine_fn bdrv_co_do_pwritev(BlockDriverState *bs, /* throttling disk I/O */ if (bs->io_limits_enabled) { - bdrv_io_limits_intercept(bs, bytes, true); + throttle_group_co_io_limits_intercept(bs, bytes, true); } /* diff --git a/block/qapi.c b/block/qapi.c index 18d2b95f54..a738148bce 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -24,6 +24,7 @@ #include "block/qapi.h" #include "block/block_int.h" +#include "block/throttle-groups.h" #include "block/write-threshold.h" #include "qmp-commands.h" #include "qapi-visit.h" @@ -65,7 +66,9 @@ BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs, Error **errp) if (bs->io_limits_enabled) { ThrottleConfig cfg; - throttle_get_config(&bs->throttle_state, &cfg); + + throttle_group_get_config(bs, &cfg); + info->bps = cfg.buckets[THROTTLE_BPS_TOTAL].avg; info->bps_rd = cfg.buckets[THROTTLE_BPS_READ].avg; info->bps_wr = cfg.buckets[THROTTLE_BPS_WRITE].avg; @@ -90,6 +93,9 @@ BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs, Error **errp) info->has_iops_size = cfg.op_size; info->iops_size = cfg.op_size; + + info->has_group = true; + info->group = g_strdup(throttle_group_get_name(bs)); } info->write_threshold = bdrv_write_threshold_get(bs); diff --git a/block/throttle-groups.c b/block/throttle-groups.c new file mode 100644 index 0000000000..efc462fbc5 --- /dev/null +++ b/block/throttle-groups.c @@ -0,0 +1,496 @@ +/* + * QEMU block throttling group infrastructure + * + * Copyright (C) Nodalink, EURL. 2014 + * Copyright (C) Igalia, S.L. 2015 + * + * Authors: + * Benoît Canet <benoit.canet@nodalink.com> + * Alberto Garcia <berto@igalia.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include "block/throttle-groups.h" +#include "qemu/queue.h" +#include "qemu/thread.h" +#include "sysemu/qtest.h" + +/* The ThrottleGroup structure (with its ThrottleState) is shared + * among different BlockDriverState and it's independent from + * AioContext, so in order to use it from different threads it needs + * its own locking. + * + * This locking is however handled internally in this file, so it's + * mostly transparent to outside users (but see the documentation in + * throttle_groups_lock()). + * + * The whole ThrottleGroup structure is private and invisible to + * outside users, that only use it through its ThrottleState. + * + * In addition to the ThrottleGroup structure, BlockDriverState has + * fields that need to be accessed by other members of the group and + * therefore also need to be protected by this lock. Once a BDS is + * registered in a group those fields can be accessed by other threads + * any time. + * + * Again, all this is handled internally and is mostly transparent to + * the outside. The 'throttle_timers' field however has an additional + * constraint because it may be temporarily invalid (see for example + * bdrv_set_aio_context()). Therefore in this file a thread will + * access some other BDS's timers only after verifying that that BDS + * has throttled requests in the queue. + */ +typedef struct ThrottleGroup { + char *name; /* This is constant during the lifetime of the group */ + + QemuMutex lock; /* This lock protects the following four fields */ + ThrottleState ts; + QLIST_HEAD(, BlockDriverState) head; + BlockDriverState *tokens[2]; + bool any_timer_armed[2]; + + /* These two are protected by the global throttle_groups_lock */ + unsigned refcount; + QTAILQ_ENTRY(ThrottleGroup) list; +} ThrottleGroup; + +static QemuMutex throttle_groups_lock; +static QTAILQ_HEAD(, ThrottleGroup) throttle_groups = + QTAILQ_HEAD_INITIALIZER(throttle_groups); + +/* Increments the reference count of a ThrottleGroup given its name. + * + * If no ThrottleGroup is found with the given name a new one is + * created. + * + * @name: the name of the ThrottleGroup + * @ret: the ThrottleGroup + */ +static ThrottleGroup *throttle_group_incref(const char *name) +{ + ThrottleGroup *tg = NULL; + ThrottleGroup *iter; + + 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; + } + } + + /* Create a new one if not found */ + if (!tg) { + tg = g_new0(ThrottleGroup, 1); + tg->name = g_strdup(name); + qemu_mutex_init(&tg->lock); + throttle_init(&tg->ts); + QLIST_INIT(&tg->head); + + QTAILQ_INSERT_TAIL(&throttle_groups, tg, list); + } + + tg->refcount++; + + qemu_mutex_unlock(&throttle_groups_lock); + + return tg; +} + +/* Decrease the reference count of a ThrottleGroup. + * + * When the reference count reaches zero the ThrottleGroup is + * destroyed. + * + * @tg: The ThrottleGroup to unref + */ +static void throttle_group_unref(ThrottleGroup *tg) +{ + 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); +} + +/* Get the name from a BlockDriverState's ThrottleGroup. The name (and + * the pointer) is guaranteed to remain constant during the lifetime + * of the group. + * + * @bs: a BlockDriverState that is member of a throttling group + * @ret: the name of the group. + */ +const char *throttle_group_get_name(BlockDriverState *bs) +{ + ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts); + return tg->name; +} + +/* Return the next BlockDriverState in the round-robin sequence, + * simulating a circular list. + * + * This assumes that tg->lock is held. + * + * @bs: the current BlockDriverState + * @ret: the next BlockDriverState in the sequence + */ +static 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; +} + +/* Return the next BlockDriverState in the round-robin sequence with + * pending I/O requests. + * + * This assumes that tg->lock is held. + * + * @bs: the current BlockDriverState + * @is_write: the type of operation (read/write) + * @ret: the next BlockDriverState with pending requests, or bs + * if there is none. + */ +static BlockDriverState *next_throttle_token(BlockDriverState *bs, + bool is_write) +{ + ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts); + BlockDriverState *token, *start; + + start = token = tg->tokens[is_write]; + + /* get next bs round in round robin style */ + token = throttle_group_next_bs(token); + while (token != start && !token->pending_reqs[is_write]) { + token = throttle_group_next_bs(token); + } + + /* If no IO are queued for scheduling on the next round robin token + * then decide the token is the current bs because chances are + * the current bs get the current request queued. + */ + if (token == start && !token->pending_reqs[is_write]) { + token = bs; + } + + return token; +} + +/* Check if the next I/O request for a BlockDriverState needs to be + * throttled or not. If there's no timer set in this group, set one + * and update the token accordingly. + * + * This assumes that tg->lock is held. + * + * @bs: the current BlockDriverState + * @is_write: the type of operation (read/write) + * @ret: whether the I/O request needs to be throttled or not + */ +static bool throttle_group_schedule_timer(BlockDriverState *bs, + bool is_write) +{ + ThrottleState *ts = bs->throttle_state; + ThrottleTimers *tt = &bs->throttle_timers; + ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); + bool must_wait; + + /* Check if any of the timers in this group is already armed */ + if (tg->any_timer_armed[is_write]) { + return true; + } + + must_wait = throttle_schedule_timer(ts, tt, is_write); + + /* If a timer just got armed, set bs as the current token */ + if (must_wait) { + tg->tokens[is_write] = bs; + tg->any_timer_armed[is_write] = true; + } + + return must_wait; +} + +/* Look for the next pending I/O request and schedule it. + * + * This assumes that tg->lock is held. + * + * @bs: the current BlockDriverState + * @is_write: the type of operation (read/write) + */ +static void schedule_next_request(BlockDriverState *bs, bool is_write) +{ + ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts); + bool must_wait; + BlockDriverState *token; + + /* Check if there's any pending request to schedule next */ + token = next_throttle_token(bs, is_write); + if (!token->pending_reqs[is_write]) { + return; + } + + /* Set a timer for the request if it needs to be throttled */ + must_wait = throttle_group_schedule_timer(token, is_write); + + /* If it doesn't have to wait, queue it for immediate execution */ + if (!must_wait) { + /* Give preference to requests from the current bs */ + if (qemu_in_coroutine() && + qemu_co_queue_next(&bs->throttled_reqs[is_write])) { + token = bs; + } else { + ThrottleTimers *tt = &token->throttle_timers; + int64_t now = qemu_clock_get_ns(tt->clock_type); + timer_mod(tt->timers[is_write], now + 1); + tg->any_timer_armed[is_write] = true; + } + tg->tokens[is_write] = token; + } +} + +/* Check if an I/O request needs to be throttled, wait and set a timer + * if necessary, and schedule the next request using a round robin + * algorithm. + * + * @bs: the current BlockDriverState + * @bytes: the number of bytes for this I/O + * @is_write: the type of operation (read/write) + */ +void coroutine_fn throttle_group_co_io_limits_intercept(BlockDriverState *bs, + unsigned int bytes, + bool is_write) +{ + bool must_wait; + BlockDriverState *token; + + ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts); + qemu_mutex_lock(&tg->lock); + + /* First we check if this I/O has to be throttled. */ + token = next_throttle_token(bs, is_write); + must_wait = throttle_group_schedule_timer(token, is_write); + + /* Wait if there's a timer set or queued requests of this type */ + if (must_wait || bs->pending_reqs[is_write]) { + bs->pending_reqs[is_write]++; + qemu_mutex_unlock(&tg->lock); + qemu_co_queue_wait(&bs->throttled_reqs[is_write]); + qemu_mutex_lock(&tg->lock); + bs->pending_reqs[is_write]--; + } + + /* The I/O will be executed, so do the accounting */ + throttle_account(bs->throttle_state, is_write, bytes); + + /* Schedule the next request */ + schedule_next_request(bs, is_write); + + qemu_mutex_unlock(&tg->lock); +} + +/* Update the throttle configuration for a particular group. Similar + * to throttle_config(), but guarantees atomicity within the + * throttling group. + * + * @bs: a BlockDriverState that is member of the group + * @cfg: the configuration to set + */ +void throttle_group_config(BlockDriverState *bs, ThrottleConfig *cfg) +{ + ThrottleTimers *tt = &bs->throttle_timers; + ThrottleState *ts = bs->throttle_state; + ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); + qemu_mutex_lock(&tg->lock); + throttle_config(ts, tt, cfg); + /* throttle_config() cancels the timers */ + tg->any_timer_armed[0] = tg->any_timer_armed[1] = false; + qemu_mutex_unlock(&tg->lock); +} + +/* Get the throttle configuration from a particular group. Similar to + * throttle_get_config(), but guarantees atomicity within the + * throttling group. + * + * @bs: a BlockDriverState that is member of the group + * @cfg: the configuration will be written here + */ +void throttle_group_get_config(BlockDriverState *bs, ThrottleConfig *cfg) +{ + ThrottleState *ts = bs->throttle_state; + ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); + qemu_mutex_lock(&tg->lock); + throttle_get_config(ts, cfg); + qemu_mutex_unlock(&tg->lock); +} + +/* ThrottleTimers callback. This wakes up a request that was waiting + * because it had been throttled. + * + * @bs: the BlockDriverState whose request had been throttled + * @is_write: the type of operation (read/write) + */ +static void timer_cb(BlockDriverState *bs, bool is_write) +{ + ThrottleState *ts = bs->throttle_state; + ThrottleGroup *tg = container_of(ts, ThrottleGroup, ts); + bool empty_queue; + + /* The timer has just been fired, so we can update the flag */ + qemu_mutex_lock(&tg->lock); + tg->any_timer_armed[is_write] = false; + qemu_mutex_unlock(&tg->lock); + + /* Run the request that was waiting for this timer */ + empty_queue = !qemu_co_enter_next(&bs->throttled_reqs[is_write]); + + /* If the request queue was empty then we have to take care of + * scheduling the next one */ + if (empty_queue) { + qemu_mutex_lock(&tg->lock); + schedule_next_request(bs, is_write); + qemu_mutex_unlock(&tg->lock); + } +} + +static void read_timer_cb(void *opaque) +{ + timer_cb(opaque, false); +} + +static void write_timer_cb(void *opaque) +{ + timer_cb(opaque, true); +} + +/* Register a BlockDriverState in the throttling group, also + * initializing its timers and updating its throttle_state pointer to + * point to it. If a throttling group with that name does not exist + * yet, it will be created. + * + * @bs: the BlockDriverState to insert + * @groupname: the name of the group + */ +void throttle_group_register_bs(BlockDriverState *bs, const char *groupname) +{ + int i; + ThrottleGroup *tg = throttle_group_incref(groupname); + int clock_type = QEMU_CLOCK_REALTIME; + + if (qtest_enabled()) { + /* For testing block IO throttling only */ + clock_type = QEMU_CLOCK_VIRTUAL; + } + + bs->throttle_state = &tg->ts; + + qemu_mutex_lock(&tg->lock); + /* If the ThrottleGroup is new set this BlockDriverState as the token */ + for (i = 0; i < 2; i++) { + if (!tg->tokens[i]) { + tg->tokens[i] = bs; + } + } + + QLIST_INSERT_HEAD(&tg->head, bs, round_robin); + + throttle_timers_init(&bs->throttle_timers, + bdrv_get_aio_context(bs), + clock_type, + read_timer_cb, + write_timer_cb, + bs); + + qemu_mutex_unlock(&tg->lock); +} + +/* Unregister a BlockDriverState from its group, removing it from the + * list, destroying the timers and setting the throttle_state pointer + * to NULL. + * + * The group will be destroyed if it's empty after this operation. + * + * @bs: the BlockDriverState to remove + */ +void throttle_group_unregister_bs(BlockDriverState *bs) +{ + ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts); + int i; + + qemu_mutex_lock(&tg->lock); + for (i = 0; i < 2; i++) { + if (tg->tokens[i] == bs) { + BlockDriverState *token = throttle_group_next_bs(bs); + /* Take care of the case where this is the last bs in the group */ + if (token == bs) { + token = NULL; + } + tg->tokens[i] = token; + } + } + + /* remove the current bs from the list */ + QLIST_REMOVE(bs, round_robin); + throttle_timers_destroy(&bs->throttle_timers); + qemu_mutex_unlock(&tg->lock); + + throttle_group_unref(tg); + bs->throttle_state = NULL; +} + +/* Acquire the lock of this throttling group. + * + * You won't normally need to use this. None of the functions from the + * ThrottleGroup API require you to acquire the lock since all of them + * deal with it internally. + * + * This should only be used in exceptional cases when you want to + * access the protected fields of a BlockDriverState directly + * (e.g. bdrv_swap()). + * + * @bs: a BlockDriverState that is member of the group + */ +void throttle_group_lock(BlockDriverState *bs) +{ + ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts); + qemu_mutex_lock(&tg->lock); +} + +/* Release the lock of this throttling group. + * + * See the comments in throttle_group_lock(). + */ +void throttle_group_unlock(BlockDriverState *bs) +{ + ThrottleGroup *tg = container_of(bs->throttle_state, ThrottleGroup, ts); + qemu_mutex_unlock(&tg->lock); +} + +static void throttle_groups_init(void) +{ + qemu_mutex_init(&throttle_groups_lock); +} + +block_init(throttle_groups_init); diff --git a/blockdev-nbd.c b/blockdev-nbd.c index 85cda4cfa7..0d9df47ce4 100644 --- a/blockdev-nbd.c +++ b/blockdev-nbd.c @@ -43,7 +43,7 @@ void qmp_nbd_server_start(SocketAddress *addr, Error **errp) server_fd = socket_listen(addr, errp); if (server_fd != -1) { - qemu_set_fd_handler2(server_fd, NULL, nbd_accept, NULL, NULL); + qemu_set_fd_handler(server_fd, nbd_accept, NULL, NULL); } } @@ -129,7 +129,7 @@ void qmp_nbd_server_stop(Error **errp) } if (server_fd != -1) { - qemu_set_fd_handler2(server_fd, NULL, NULL, NULL, NULL); + qemu_set_fd_handler(server_fd, NULL, NULL, NULL); close(server_fd); server_fd = -1; } diff --git a/blockdev.c b/blockdev.c index fffc9b5a06..3aa1ae6698 100644 --- a/blockdev.c +++ b/blockdev.c @@ -34,6 +34,7 @@ #include "sysemu/blockdev.h" #include "hw/block/block.h" #include "block/blockjob.h" +#include "block/throttle-groups.h" #include "monitor/monitor.h" #include "qemu/option.h" #include "qemu/config-file.h" @@ -357,6 +358,7 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, const char *id; bool has_driver_specific_opts; BlockdevDetectZeroesOptions detect_zeroes; + const char *throttling_group; /* Check common options by copying from bs_opts to opts, all other options * stay in bs_opts for processing by bdrv_open(). */ @@ -459,6 +461,8 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, cfg.op_size = qemu_opt_get_number(opts, "throttling.iops-size", 0); + throttling_group = qemu_opt_get(opts, "throttling.group"); + if (!check_throttle_config(&cfg, &error)) { error_propagate(errp, error); goto early_err; @@ -547,7 +551,10 @@ static BlockBackend *blockdev_init(const char *file, QDict *bs_opts, /* disk I/O throttling */ if (throttle_enabled(&cfg)) { - bdrv_io_limits_enable(bs); + if (!throttling_group) { + throttling_group = blk_name(blk); + } + bdrv_io_limits_enable(bs, throttling_group); bdrv_set_io_limits(bs, &cfg); } @@ -711,6 +718,8 @@ DriveInfo *drive_new(QemuOpts *all_opts, BlockInterfaceType block_default_type) { "iops_size", "throttling.iops-size" }, + { "group", "throttling.group" }, + { "readonly", "read-only" }, }; @@ -1951,7 +1960,9 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd, bool has_iops_wr_max, int64_t iops_wr_max, bool has_iops_size, - int64_t iops_size, Error **errp) + int64_t iops_size, + bool has_group, + const char *group, Error **errp) { ThrottleConfig cfg; BlockDriverState *bs; @@ -2004,14 +2015,19 @@ void qmp_block_set_io_throttle(const char *device, int64_t bps, int64_t bps_rd, aio_context = bdrv_get_aio_context(bs); aio_context_acquire(aio_context); - if (!bs->io_limits_enabled && throttle_enabled(&cfg)) { - bdrv_io_limits_enable(bs); - } else if (bs->io_limits_enabled && !throttle_enabled(&cfg)) { - bdrv_io_limits_disable(bs); - } - - if (bs->io_limits_enabled) { + if (throttle_enabled(&cfg)) { + /* Enable I/O limits if they're not enabled yet, otherwise + * just update the throttling group. */ + if (!bs->io_limits_enabled) { + bdrv_io_limits_enable(bs, has_group ? group : device); + } else if (has_group) { + bdrv_io_limits_update_group(bs, group); + } + /* Set the new throttling configuration */ bdrv_set_io_limits(bs, &cfg); + } else if (bs->io_limits_enabled) { + /* If all throttling settings are set to 0, disable I/O limits */ + bdrv_io_limits_disable(bs); } aio_context_release(aio_context); @@ -3189,6 +3205,10 @@ QemuOptsList qemu_common_drive_opts = { .type = QEMU_OPT_NUMBER, .help = "when limiting by iops max size of an I/O in bytes", },{ + .name = "throttling.group", + .type = QEMU_OPT_STRING, + .help = "name of the block throttling group", + },{ .name = "copy-on-read", .type = QEMU_OPT_BOOL, .help = "copy read data from backing file into image file", diff --git a/docs/specs/rocker.txt b/docs/specs/rocker.txt index 1e7e1e1859..0af5c61585 100644 --- a/docs/specs/rocker.txt +++ b/docs/specs/rocker.txt @@ -420,6 +420,7 @@ Other properties for front-panel ports are available via DMA CMD descriptors: LEARNING 1 MAC address learning on port 1 = enabled 0 = disabled + PHYS_NAME <var> Physical port name (string) Set PORT_SETTINGS descriptor: diff --git a/hmp-commands.hx b/hmp-commands.hx index 3d7dfccf7c..d3b7932ff6 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1799,5 +1799,29 @@ show available trace events and their state ETEXI STEXI +@item rocker @var{name} +@findex rocker +Show Rocker(s) +ETEXI + +STEXI +@item rocker_ports @var{name} +@findex rocker_ports +Show Rocker ports +ETEXI + +STEXI +@item rocker_of_dpa_flows @var{name} [@var{tbl_id}] +@findex rocker_of_dpa_flows +Show Rocker OF-DPA flow tables +ETEXI + +STEXI +@item rocker_of_dpa_groups @var{name} [@var{type}] +@findex rocker_of_dpa_groups +Show Rocker OF-DPA groups +ETEXI + +STEXI @end table ETEXI @@ -15,6 +15,7 @@ #include "hmp.h" #include "net/net.h" +#include "net/eth.h" #include "sysemu/char.h" #include "sysemu/block-backend.h" #include "qemu/option.h" @@ -399,7 +400,8 @@ static void print_block_info(Monitor *mon, BlockInfo *info, " iops_max=%" PRId64 " iops_rd_max=%" PRId64 " iops_wr_max=%" PRId64 - " iops_size=%" PRId64 "\n", + " iops_size=%" PRId64 + " group=%s\n", inserted->bps, inserted->bps_rd, inserted->bps_wr, @@ -412,7 +414,8 @@ static void print_block_info(Monitor *mon, BlockInfo *info, inserted->iops_max, inserted->iops_rd_max, inserted->iops_wr_max, - inserted->iops_size); + inserted->iops_size, + inserted->group); } if (verbose) { @@ -1356,7 +1359,9 @@ void hmp_block_set_io_throttle(Monitor *mon, const QDict *qdict) false, 0, false, /* No default I/O size */ - 0, &err); + 0, + false, + NULL, &err); hmp_handle_error(mon, &err); } @@ -1999,3 +2004,305 @@ void hmp_qom_set(Monitor *mon, const QDict *qdict) } hmp_handle_error(mon, &err); } + +void hmp_rocker(Monitor *mon, const QDict *qdict) +{ + const char *name = qdict_get_str(qdict, "name"); + RockerSwitch *rocker; + Error *errp = NULL; + + rocker = qmp_query_rocker(name, &errp); + if (errp != NULL) { + hmp_handle_error(mon, &errp); + return; + } + + monitor_printf(mon, "name: %s\n", rocker->name); + monitor_printf(mon, "id: 0x%" PRIx64 "\n", rocker->id); + monitor_printf(mon, "ports: %d\n", rocker->ports); + + qapi_free_RockerSwitch(rocker); +} + +void hmp_rocker_ports(Monitor *mon, const QDict *qdict) +{ + RockerPortList *list, *port; + const char *name = qdict_get_str(qdict, "name"); + Error *errp = NULL; + + list = qmp_query_rocker_ports(name, &errp); + if (errp != NULL) { + hmp_handle_error(mon, &errp); + return; + } + + monitor_printf(mon, " ena/ speed/ auto\n"); + monitor_printf(mon, " port link duplex neg?\n"); + + for (port = list; port; port = port->next) { + monitor_printf(mon, "%10s %-4s %-3s %2s %-3s\n", + port->value->name, + port->value->enabled ? port->value->link_up ? + "up" : "down" : "!ena", + port->value->speed == 10000 ? "10G" : "??", + port->value->duplex ? "FD" : "HD", + port->value->autoneg ? "Yes" : "No"); + } + + qapi_free_RockerPortList(list); +} + +void hmp_rocker_of_dpa_flows(Monitor *mon, const QDict *qdict) +{ + RockerOfDpaFlowList *list, *info; + const char *name = qdict_get_str(qdict, "name"); + uint32_t tbl_id = qdict_get_try_int(qdict, "tbl_id", -1); + Error *errp = NULL; + + list = qmp_query_rocker_of_dpa_flows(name, tbl_id != -1, tbl_id, &errp); + if (errp != NULL) { + hmp_handle_error(mon, &errp); + return; + } + + monitor_printf(mon, "prio tbl hits key(mask) --> actions\n"); + + for (info = list; info; info = info->next) { + RockerOfDpaFlow *flow = info->value; + RockerOfDpaFlowKey *key = flow->key; + RockerOfDpaFlowMask *mask = flow->mask; + RockerOfDpaFlowAction *action = flow->action; + + if (flow->hits) { + monitor_printf(mon, "%-4d %-3d %-4" PRIu64, + key->priority, key->tbl_id, flow->hits); + } else { + monitor_printf(mon, "%-4d %-3d ", + key->priority, key->tbl_id); + } + + if (key->has_in_pport) { + monitor_printf(mon, " pport %d", key->in_pport); + if (mask->has_in_pport) { + monitor_printf(mon, "(0x%x)", mask->in_pport); + } + } + + if (key->has_vlan_id) { + monitor_printf(mon, " vlan %d", + key->vlan_id & VLAN_VID_MASK); + if (mask->has_vlan_id) { + monitor_printf(mon, "(0x%x)", mask->vlan_id); + } + } + + if (key->has_tunnel_id) { + monitor_printf(mon, " tunnel %d", key->tunnel_id); + if (mask->has_tunnel_id) { + monitor_printf(mon, "(0x%x)", mask->tunnel_id); + } + } + + if (key->has_eth_type) { + switch (key->eth_type) { + case 0x0806: + monitor_printf(mon, " ARP"); + break; + case 0x0800: + monitor_printf(mon, " IP"); + break; + case 0x86dd: + monitor_printf(mon, " IPv6"); + break; + case 0x8809: + monitor_printf(mon, " LACP"); + break; + case 0x88cc: + monitor_printf(mon, " LLDP"); + break; + default: + monitor_printf(mon, " eth type 0x%04x", key->eth_type); + break; + } + } + + if (key->has_eth_src) { + if ((strcmp(key->eth_src, "01:00:00:00:00:00") == 0) && + (mask->has_eth_src) && + (strcmp(mask->eth_src, "01:00:00:00:00:00") == 0)) { + monitor_printf(mon, " src <any mcast/bcast>"); + } else if ((strcmp(key->eth_src, "00:00:00:00:00:00") == 0) && + (mask->has_eth_src) && + (strcmp(mask->eth_src, "01:00:00:00:00:00") == 0)) { + monitor_printf(mon, " src <any ucast>"); + } else { + monitor_printf(mon, " src %s", key->eth_src); + if (mask->has_eth_src) { + monitor_printf(mon, "(%s)", mask->eth_src); + } + } + } + + if (key->has_eth_dst) { + if ((strcmp(key->eth_dst, "01:00:00:00:00:00") == 0) && + (mask->has_eth_dst) && + (strcmp(mask->eth_dst, "01:00:00:00:00:00") == 0)) { + monitor_printf(mon, " dst <any mcast/bcast>"); + } else if ((strcmp(key->eth_dst, "00:00:00:00:00:00") == 0) && + (mask->has_eth_dst) && + (strcmp(mask->eth_dst, "01:00:00:00:00:00") == 0)) { + monitor_printf(mon, " dst <any ucast>"); + } else { + monitor_printf(mon, " dst %s", key->eth_dst); + if (mask->has_eth_dst) { + monitor_printf(mon, "(%s)", mask->eth_dst); + } + } + } + + if (key->has_ip_proto) { + monitor_printf(mon, " proto %d", key->ip_proto); + if (mask->has_ip_proto) { + monitor_printf(mon, "(0x%x)", mask->ip_proto); + } + } + + if (key->has_ip_tos) { + monitor_printf(mon, " TOS %d", key->ip_tos); + if (mask->has_ip_tos) { + monitor_printf(mon, "(0x%x)", mask->ip_tos); + } + } + + if (key->has_ip_dst) { + monitor_printf(mon, " dst %s", key->ip_dst); + } + + if (action->has_goto_tbl || action->has_group_id || + action->has_new_vlan_id) { + monitor_printf(mon, " -->"); + } + + if (action->has_new_vlan_id) { + monitor_printf(mon, " apply new vlan %d", + ntohs(action->new_vlan_id)); + } + + if (action->has_group_id) { + monitor_printf(mon, " write group 0x%08x", action->group_id); + } + + if (action->has_goto_tbl) { + monitor_printf(mon, " goto tbl %d", action->goto_tbl); + } + + monitor_printf(mon, "\n"); + } + + qapi_free_RockerOfDpaFlowList(list); +} + +void hmp_rocker_of_dpa_groups(Monitor *mon, const QDict *qdict) +{ + RockerOfDpaGroupList *list, *g; + const char *name = qdict_get_str(qdict, "name"); + uint8_t type = qdict_get_try_int(qdict, "type", 9); + Error *errp = NULL; + bool set = false; + + list = qmp_query_rocker_of_dpa_groups(name, type != 9, type, &errp); + if (errp != NULL) { + hmp_handle_error(mon, &errp); + return; + } + + monitor_printf(mon, "id (decode) --> buckets\n"); + + for (g = list; g; g = g->next) { + RockerOfDpaGroup *group = g->value; + + monitor_printf(mon, "0x%08x", group->id); + + monitor_printf(mon, " (type %s", group->type == 0 ? "L2 interface" : + group->type == 1 ? "L2 rewrite" : + group->type == 2 ? "L3 unicast" : + group->type == 3 ? "L2 multicast" : + group->type == 4 ? "L2 flood" : + group->type == 5 ? "L3 interface" : + group->type == 6 ? "L3 multicast" : + group->type == 7 ? "L3 ECMP" : + group->type == 8 ? "L2 overlay" : + "unknown"); + + if (group->has_vlan_id) { + monitor_printf(mon, " vlan %d", group->vlan_id); + } + + if (group->has_pport) { + monitor_printf(mon, " pport %d", group->pport); + } + + if (group->has_index) { + monitor_printf(mon, " index %d", group->index); + } + + monitor_printf(mon, ") -->"); + + if (group->has_set_vlan_id && group->set_vlan_id) { + set = true; + monitor_printf(mon, " set vlan %d", + group->set_vlan_id & VLAN_VID_MASK); + } + + if (group->has_set_eth_src) { + if (!set) { + set = true; + monitor_printf(mon, " set"); + } + monitor_printf(mon, " src %s", group->set_eth_src); + } + + if (group->has_set_eth_dst) { + if (!set) { + set = true; + monitor_printf(mon, " set"); + } + monitor_printf(mon, " dst %s", group->set_eth_dst); + } + + set = false; + + if (group->has_ttl_check && group->ttl_check) { + monitor_printf(mon, " check TTL"); + } + + if (group->has_group_id && group->group_id) { + monitor_printf(mon, " group id 0x%08x", group->group_id); + } + + if (group->has_pop_vlan && group->pop_vlan) { + monitor_printf(mon, " pop vlan"); + } + + if (group->has_out_pport) { + monitor_printf(mon, " out pport %d", group->out_pport); + } + + if (group->has_group_ids) { + struct uint32List *id; + + monitor_printf(mon, " groups ["); + for (id = group->group_ids; id; id = id->next) { + monitor_printf(mon, "0x%08x", id->value); + if (id->next) { + monitor_printf(mon, ","); + } + } + monitor_printf(mon, "]"); + } + + monitor_printf(mon, "\n"); + } + + qapi_free_RockerOfDpaGroupList(list); +} @@ -124,5 +124,9 @@ void host_net_remove_completion(ReadLineState *rs, int nb_args, const char *str); void delvm_completion(ReadLineState *rs, int nb_args, const char *str); void loadvm_completion(ReadLineState *rs, int nb_args, const char *str); +void hmp_rocker(Monitor *mon, const QDict *qdict); +void hmp_rocker_ports(Monitor *mon, const QDict *qdict); +void hmp_rocker_of_dpa_flows(Monitor *mon, const QDict *qdict); +void hmp_rocker_of_dpa_groups(Monitor *mon, const QDict *qdict); #endif diff --git a/hw/net/Makefile.objs b/hw/net/Makefile.objs index 7b91c4e51d..98801739ef 100644 --- a/hw/net/Makefile.objs +++ b/hw/net/Makefile.objs @@ -39,3 +39,4 @@ obj-$(CONFIG_ETSEC) += fsl_etsec/etsec.o fsl_etsec/registers.o \ common-obj-$(CONFIG_ROCKER) += rocker/rocker.o rocker/rocker_fp.o \ rocker/rocker_desc.o rocker/rocker_world.o \ rocker/rocker_of_dpa.o +obj-$(call lnot,$(CONFIG_ROCKER)) += rocker/qmp-norocker.o diff --git a/hw/net/rocker/qmp-norocker.c b/hw/net/rocker/qmp-norocker.c new file mode 100644 index 0000000000..f253747361 --- /dev/null +++ b/hw/net/rocker/qmp-norocker.c @@ -0,0 +1,50 @@ +/* + * QMP Target options - Commands handled based on a target config + * versus a host config + * + * Copyright (c) 2015 David Ahern <dsahern@gmail.com> + * + * 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 of the License, or + * (at your option) any later version. + * + * 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. + */ + +#include "qemu-common.h" +#include "qmp-commands.h" +#include "qapi/qmp/qerror.h" + +RockerSwitch *qmp_query_rocker(const char *name, Error **errp) +{ + error_set(errp, QERR_FEATURE_DISABLED, "rocker"); + return NULL; +}; + +RockerPortList *qmp_query_rocker_ports(const char *name, Error **errp) +{ + error_set(errp, QERR_FEATURE_DISABLED, "rocker"); + return NULL; +}; + +RockerOfDpaFlowList *qmp_query_rocker_of_dpa_flows(const char *name, + bool has_tbl_id, + uint32_t tbl_id, + Error **errp) +{ + error_set(errp, QERR_FEATURE_DISABLED, "rocker"); + return NULL; +}; + +RockerOfDpaGroupList *qmp_query_rocker_of_dpa_groups(const char *name, + bool has_type, + uint8_t type, + Error **errp) +{ + error_set(errp, QERR_FEATURE_DISABLED, "rocker"); + return NULL; +}; diff --git a/hw/net/rocker/rocker.c b/hw/net/rocker/rocker.c index 55b6c46157..4d25842509 100644 --- a/hw/net/rocker/rocker.c +++ b/hw/net/rocker/rocker.c @@ -94,6 +94,51 @@ World *rocker_get_world(Rocker *r, enum rocker_world_type type) return NULL; } +RockerSwitch *qmp_query_rocker(const char *name, Error **errp) +{ + RockerSwitch *rocker = g_malloc0(sizeof(*rocker)); + Rocker *r; + + r = rocker_find(name); + if (!r) { + error_set(errp, ERROR_CLASS_GENERIC_ERROR, + "rocker %s not found", name); + return NULL; + } + + rocker->name = g_strdup(r->name); + rocker->id = r->switch_id; + rocker->ports = r->fp_ports; + + return rocker; +} + +RockerPortList *qmp_query_rocker_ports(const char *name, Error **errp) +{ + RockerPortList *list = NULL; + Rocker *r; + int i; + + r = rocker_find(name); + if (!r) { + error_set(errp, ERROR_CLASS_GENERIC_ERROR, + "rocker %s not found", name); + return NULL; + } + + for (i = r->fp_ports - 1; i >= 0; i--) { + RockerPortList *info = g_malloc0(sizeof(*info)); + info->value = g_malloc0(sizeof(*info->value)); + struct fp_port *port = r->fp_port[i]; + + fp_port_get_info(port, info); + info->next = list; + list = info; + } + + return list; +} + uint32_t rocker_fp_ports(Rocker *r) { return r->fp_ports; @@ -238,6 +283,7 @@ static int cmd_get_port_settings(Rocker *r, uint8_t duplex; uint8_t autoneg; uint8_t learning; + char *phys_name; MACAddr macaddr; enum rocker_world_type mode; size_t tlv_size; @@ -265,6 +311,7 @@ static int cmd_get_port_settings(Rocker *r, fp_port_get_macaddr(fp_port, &macaddr); mode = world_type(fp_port_get_world(fp_port)); learning = fp_port_get_learning(fp_port); + phys_name = fp_port_get_name(fp_port); tlv_size = rocker_tlv_total_size(0) + /* nest */ rocker_tlv_total_size(sizeof(uint32_t)) + /* pport */ @@ -273,7 +320,8 @@ static int cmd_get_port_settings(Rocker *r, rocker_tlv_total_size(sizeof(uint8_t)) + /* autoneg */ rocker_tlv_total_size(sizeof(macaddr.a)) + /* macaddr */ rocker_tlv_total_size(sizeof(uint8_t)) + /* mode */ - rocker_tlv_total_size(sizeof(uint8_t)); /* learning */ + rocker_tlv_total_size(sizeof(uint8_t)) + /* learning */ + rocker_tlv_total_size(strlen(phys_name)); if (tlv_size > desc_buf_size(info)) { return -ROCKER_EMSGSIZE; @@ -290,6 +338,8 @@ static int cmd_get_port_settings(Rocker *r, rocker_tlv_put_u8(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_MODE, mode); rocker_tlv_put_u8(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_LEARNING, learning); + rocker_tlv_put(buf, &pos, ROCKER_TLV_CMD_PORT_SETTINGS_PHYS_NAME, + strlen(phys_name), phys_name); rocker_tlv_nest_end(buf, &pos, nest); return desc_set_buf(info, tlv_size); @@ -1277,6 +1327,22 @@ static int pci_rocker_init(PCIDevice *dev) goto err_duplicate; } + /* Rocker name is passed in port name requests to OS with the intention + * that the name is used in interface names. Limit the length of the + * rocker name to avoid naming problems in the OS. Also, adding the + * port number as p# and unganged breakout b#, where # is at most 2 + * digits, so leave room for it too (-1 for string terminator, -3 for + * p# and -3 for b#) + */ +#define ROCKER_IFNAMSIZ 16 +#define MAX_ROCKER_NAME_LEN (ROCKER_IFNAMSIZ - 1 - 3 - 3) + if (strlen(r->name) > MAX_ROCKER_NAME_LEN) { + fprintf(stderr, + "rocker: name too long; please shorten to at most %d chars\n", + MAX_ROCKER_NAME_LEN); + return -EINVAL; + } + if (memcmp(&r->fp_start_macaddr, &zero, sizeof(zero)) == 0) { memcpy(&r->fp_start_macaddr, &dflt, sizeof(dflt)); r->fp_start_macaddr.a[4] += (sw_index++); diff --git a/hw/net/rocker/rocker_fp.c b/hw/net/rocker/rocker_fp.c index 2f1e3b348a..d8d934c396 100644 --- a/hw/net/rocker/rocker_fp.c +++ b/hw/net/rocker/rocker_fp.c @@ -41,11 +41,26 @@ struct fp_port { NICConf conf; }; +char *fp_port_get_name(FpPort *port) +{ + return port->name; +} + bool fp_port_get_link_up(FpPort *port) { return !qemu_get_queue(port->nic)->link_down; } +void fp_port_get_info(FpPort *port, RockerPortList *info) +{ + info->value->name = g_strdup(port->name); + info->value->enabled = port->enabled; + info->value->link_up = fp_port_get_link_up(port); + info->value->speed = port->speed; + info->value->duplex = port->duplex; + info->value->autoneg = port->autoneg; +} + void fp_port_get_macaddr(FpPort *port, MACAddr *macaddr) { memcpy(macaddr->a, port->conf.macaddr.a, sizeof(macaddr->a)); @@ -173,8 +188,19 @@ bool fp_port_enabled(FpPort *port) return port->enabled; } +static void fp_port_set_link(FpPort *port, bool up) +{ + NetClientState *nc = qemu_get_queue(port->nic); + + if (up == nc->link_down) { + nc->link_down = !up; + nc->info->link_status_changed(nc); + } +} + void fp_port_enable(FpPort *port) { + fp_port_set_link(port, true); port->enabled = true; DPRINTF("port %d enabled\n", port->index); } @@ -182,6 +208,7 @@ void fp_port_enable(FpPort *port) void fp_port_disable(FpPort *port) { port->enabled = false; + fp_port_set_link(port, false); DPRINTF("port %d disabled\n", port->index); } @@ -201,7 +228,7 @@ FpPort *fp_port_alloc(Rocker *r, char *sw_name, /* front-panel switch port names are 1-based */ - port->name = g_strdup_printf("%s.%d", sw_name, port->pport); + port->name = g_strdup_printf("%sp%d", sw_name, port->pport); memcpy(port->conf.macaddr.a, start_mac, sizeof(port->conf.macaddr.a)); port->conf.macaddr.a[5] += index; diff --git a/hw/net/rocker/rocker_fp.h b/hw/net/rocker/rocker_fp.h index a5f28f120d..ab80fd833c 100644 --- a/hw/net/rocker/rocker_fp.h +++ b/hw/net/rocker/rocker_fp.h @@ -26,7 +26,9 @@ typedef struct fp_port FpPort; int fp_port_eg(FpPort *port, const struct iovec *iov, int iovcnt); +char *fp_port_get_name(FpPort *port); bool fp_port_get_link_up(FpPort *port); +void fp_port_get_info(FpPort *port, RockerPortList *info); void fp_port_get_macaddr(FpPort *port, MACAddr *macaddr); void fp_port_set_macaddr(FpPort *port, MACAddr *macaddr); uint8_t fp_port_get_learning(FpPort *port); diff --git a/hw/net/rocker/rocker_hw.h b/hw/net/rocker/rocker_hw.h index c9c85a75bd..fe639badd4 100644 --- a/hw/net/rocker/rocker_hw.h +++ b/hw/net/rocker/rocker_hw.h @@ -179,6 +179,7 @@ enum { ROCKER_TLV_CMD_PORT_SETTINGS_MACADDR, /* binary */ ROCKER_TLV_CMD_PORT_SETTINGS_MODE, /* u8 */ ROCKER_TLV_CMD_PORT_SETTINGS_LEARNING, /* u8 */ + ROCKER_TLV_CMD_PORT_SETTINGS_PHYS_NAME, /* binary */ __ROCKER_TLV_CMD_PORT_SETTINGS_MAX, ROCKER_TLV_CMD_PORT_SETTINGS_MAX = __ROCKER_TLV_CMD_PORT_SETTINGS_MAX - 1, diff --git a/hw/net/rocker/rocker_of_dpa.c b/hw/net/rocker/rocker_of_dpa.c index 1bcb7af5ef..b25a17d6d7 100644 --- a/hw/net/rocker/rocker_of_dpa.c +++ b/hw/net/rocker/rocker_of_dpa.c @@ -2302,6 +2302,318 @@ static void of_dpa_uninit(World *world) g_hash_table_destroy(of_dpa->flow_tbl); } +struct of_dpa_flow_fill_context { + RockerOfDpaFlowList *list; + uint32_t tbl_id; +}; + +static void of_dpa_flow_fill(void *cookie, void *value, void *user_data) +{ + struct of_dpa_flow *flow = value; + struct of_dpa_flow_key *key = &flow->key; + struct of_dpa_flow_key *mask = &flow->mask; + struct of_dpa_flow_fill_context *flow_context = user_data; + RockerOfDpaFlowList *new; + RockerOfDpaFlow *nflow; + RockerOfDpaFlowKey *nkey; + RockerOfDpaFlowMask *nmask; + RockerOfDpaFlowAction *naction; + + if (flow_context->tbl_id != -1 && + flow_context->tbl_id != key->tbl_id) { + return; + } + + new = g_malloc0(sizeof(*new)); + nflow = new->value = g_malloc0(sizeof(*nflow)); + nkey = nflow->key = g_malloc0(sizeof(*nkey)); + nmask = nflow->mask = g_malloc0(sizeof(*nmask)); + naction = nflow->action = g_malloc0(sizeof(*naction)); + + nflow->cookie = flow->cookie; + nflow->hits = flow->stats.hits; + nkey->priority = flow->priority; + nkey->tbl_id = key->tbl_id; + + if (key->in_pport || mask->in_pport) { + nkey->has_in_pport = true; + nkey->in_pport = key->in_pport; + } + + if (nkey->has_in_pport && mask->in_pport != 0xffffffff) { + nmask->has_in_pport = true; + nmask->in_pport = mask->in_pport; + } + + if (key->eth.vlan_id || mask->eth.vlan_id) { + nkey->has_vlan_id = true; + nkey->vlan_id = ntohs(key->eth.vlan_id); + } + + if (nkey->has_vlan_id && mask->eth.vlan_id != 0xffff) { + nmask->has_vlan_id = true; + nmask->vlan_id = ntohs(mask->eth.vlan_id); + } + + if (key->tunnel_id || mask->tunnel_id) { + nkey->has_tunnel_id = true; + nkey->tunnel_id = key->tunnel_id; + } + + if (nkey->has_tunnel_id && mask->tunnel_id != 0xffffffff) { + nmask->has_tunnel_id = true; + nmask->tunnel_id = mask->tunnel_id; + } + + if (memcmp(key->eth.src.a, zero_mac.a, ETH_ALEN) || + memcmp(mask->eth.src.a, zero_mac.a, ETH_ALEN)) { + nkey->has_eth_src = true; + nkey->eth_src = qemu_mac_strdup_printf(key->eth.src.a); + } + + if (nkey->has_eth_src && memcmp(mask->eth.src.a, ff_mac.a, ETH_ALEN)) { + nmask->has_eth_src = true; + nmask->eth_src = qemu_mac_strdup_printf(mask->eth.src.a); + } + + if (memcmp(key->eth.dst.a, zero_mac.a, ETH_ALEN) || + memcmp(mask->eth.dst.a, zero_mac.a, ETH_ALEN)) { + nkey->has_eth_dst = true; + nkey->eth_dst = qemu_mac_strdup_printf(key->eth.dst.a); + } + + if (nkey->has_eth_dst && memcmp(mask->eth.dst.a, ff_mac.a, ETH_ALEN)) { + nmask->has_eth_dst = true; + nmask->eth_dst = qemu_mac_strdup_printf(mask->eth.dst.a); + } + + if (key->eth.type) { + + nkey->has_eth_type = true; + nkey->eth_type = ntohs(key->eth.type); + + switch (ntohs(key->eth.type)) { + case 0x0800: + case 0x86dd: + if (key->ip.proto || mask->ip.proto) { + nkey->has_ip_proto = true; + nkey->ip_proto = key->ip.proto; + } + if (nkey->has_ip_proto && mask->ip.proto != 0xff) { + nmask->has_ip_proto = true; + nmask->ip_proto = mask->ip.proto; + } + if (key->ip.tos || mask->ip.tos) { + nkey->has_ip_tos = true; + nkey->ip_tos = key->ip.tos; + } + if (nkey->has_ip_tos && mask->ip.tos != 0xff) { + nmask->has_ip_tos = true; + nmask->ip_tos = mask->ip.tos; + } + break; + } + + switch (ntohs(key->eth.type)) { + case 0x0800: + if (key->ipv4.addr.dst || mask->ipv4.addr.dst) { + char *dst = inet_ntoa(*(struct in_addr *)&key->ipv4.addr.dst); + int dst_len = of_dpa_mask2prefix(mask->ipv4.addr.dst); + nkey->has_ip_dst = true; + nkey->ip_dst = g_strdup_printf("%s/%d", dst, dst_len); + } + break; + } + } + + if (flow->action.goto_tbl) { + naction->has_goto_tbl = true; + naction->goto_tbl = flow->action.goto_tbl; + } + + if (flow->action.write.group_id) { + naction->has_group_id = true; + naction->group_id = flow->action.write.group_id; + } + + if (flow->action.apply.new_vlan_id) { + naction->has_new_vlan_id = true; + naction->new_vlan_id = flow->action.apply.new_vlan_id; + } + + new->next = flow_context->list; + flow_context->list = new; +} + +RockerOfDpaFlowList *qmp_query_rocker_of_dpa_flows(const char *name, + bool has_tbl_id, + uint32_t tbl_id, + Error **errp) +{ + struct rocker *r; + struct world *w; + struct of_dpa *of_dpa; + struct of_dpa_flow_fill_context fill_context = { + .list = NULL, + .tbl_id = tbl_id, + }; + + r = rocker_find(name); + if (!r) { + error_set(errp, ERROR_CLASS_GENERIC_ERROR, + "rocker %s not found", name); + return NULL; + } + + w = rocker_get_world(r, ROCKER_WORLD_TYPE_OF_DPA); + if (!w) { + error_set(errp, ERROR_CLASS_GENERIC_ERROR, + "rocker %s doesn't have OF-DPA world", name); + return NULL; + } + + of_dpa = world_private(w); + + g_hash_table_foreach(of_dpa->flow_tbl, of_dpa_flow_fill, &fill_context); + + return fill_context.list; +} + +struct of_dpa_group_fill_context { + RockerOfDpaGroupList *list; + uint8_t type; +}; + +static void of_dpa_group_fill(void *key, void *value, void *user_data) +{ + struct of_dpa_group *group = value; + struct of_dpa_group_fill_context *flow_context = user_data; + RockerOfDpaGroupList *new; + RockerOfDpaGroup *ngroup; + struct uint32List *id; + int i; + + if (flow_context->type != 9 && + flow_context->type != ROCKER_GROUP_TYPE_GET(group->id)) { + return; + } + + new = g_malloc0(sizeof(*new)); + ngroup = new->value = g_malloc0(sizeof(*ngroup)); + + ngroup->id = group->id; + + ngroup->type = ROCKER_GROUP_TYPE_GET(group->id); + + switch (ngroup->type) { + case ROCKER_OF_DPA_GROUP_TYPE_L2_INTERFACE: + ngroup->has_vlan_id = true; + ngroup->vlan_id = ROCKER_GROUP_VLAN_GET(group->id); + ngroup->has_pport = true; + ngroup->pport = ROCKER_GROUP_PORT_GET(group->id); + ngroup->has_out_pport = true; + ngroup->out_pport = group->l2_interface.out_pport; + ngroup->has_pop_vlan = true; + ngroup->pop_vlan = group->l2_interface.pop_vlan; + break; + case ROCKER_OF_DPA_GROUP_TYPE_L2_REWRITE: + ngroup->has_index = true; + ngroup->index = ROCKER_GROUP_INDEX_LONG_GET(group->id); + ngroup->has_group_id = true; + ngroup->group_id = group->l2_rewrite.group_id; + if (group->l2_rewrite.vlan_id) { + ngroup->has_set_vlan_id = true; + ngroup->set_vlan_id = ntohs(group->l2_rewrite.vlan_id); + } + break; + if (memcmp(group->l2_rewrite.src_mac.a, zero_mac.a, ETH_ALEN)) { + ngroup->has_set_eth_src = true; + ngroup->set_eth_src = + qemu_mac_strdup_printf(group->l2_rewrite.src_mac.a); + } + if (memcmp(group->l2_rewrite.dst_mac.a, zero_mac.a, ETH_ALEN)) { + ngroup->has_set_eth_dst = true; + ngroup->set_eth_dst = + qemu_mac_strdup_printf(group->l2_rewrite.dst_mac.a); + } + case ROCKER_OF_DPA_GROUP_TYPE_L2_FLOOD: + case ROCKER_OF_DPA_GROUP_TYPE_L2_MCAST: + ngroup->has_vlan_id = true; + ngroup->vlan_id = ROCKER_GROUP_VLAN_GET(group->id); + ngroup->has_index = true; + ngroup->index = ROCKER_GROUP_INDEX_GET(group->id); + for (i = 0; i < group->l2_flood.group_count; i++) { + ngroup->has_group_ids = true; + id = g_malloc0(sizeof(*id)); + id->value = group->l2_flood.group_ids[i]; + id->next = ngroup->group_ids; + ngroup->group_ids = id; + } + break; + case ROCKER_OF_DPA_GROUP_TYPE_L3_UCAST: + ngroup->has_index = true; + ngroup->index = ROCKER_GROUP_INDEX_LONG_GET(group->id); + ngroup->has_group_id = true; + ngroup->group_id = group->l3_unicast.group_id; + if (group->l3_unicast.vlan_id) { + ngroup->has_set_vlan_id = true; + ngroup->set_vlan_id = ntohs(group->l3_unicast.vlan_id); + } + if (memcmp(group->l3_unicast.src_mac.a, zero_mac.a, ETH_ALEN)) { + ngroup->has_set_eth_src = true; + ngroup->set_eth_src = + qemu_mac_strdup_printf(group->l3_unicast.src_mac.a); + } + if (memcmp(group->l3_unicast.dst_mac.a, zero_mac.a, ETH_ALEN)) { + ngroup->has_set_eth_dst = true; + ngroup->set_eth_dst = + qemu_mac_strdup_printf(group->l3_unicast.dst_mac.a); + } + if (group->l3_unicast.ttl_check) { + ngroup->has_ttl_check = true; + ngroup->ttl_check = group->l3_unicast.ttl_check; + } + break; + } + + new->next = flow_context->list; + flow_context->list = new; +} + +RockerOfDpaGroupList *qmp_query_rocker_of_dpa_groups(const char *name, + bool has_type, + uint8_t type, + Error **errp) +{ + struct rocker *r; + struct world *w; + struct of_dpa *of_dpa; + struct of_dpa_group_fill_context fill_context = { + .list = NULL, + .type = type, + }; + + r = rocker_find(name); + if (!r) { + error_set(errp, ERROR_CLASS_GENERIC_ERROR, + "rocker %s not found", name); + return NULL; + } + + w = rocker_get_world(r, ROCKER_WORLD_TYPE_OF_DPA); + if (!w) { + error_set(errp, ERROR_CLASS_GENERIC_ERROR, + "rocker %s doesn't have OF-DPA world", name); + return NULL; + } + + of_dpa = world_private(w); + + g_hash_table_foreach(of_dpa->group_tbl, of_dpa_group_fill, &fill_context); + + return fill_context.list; +} + static WorldOps of_dpa_ops = { .init = of_dpa_init, .uninit = of_dpa_uninit, diff --git a/hw/sh4/r2d.c b/hw/sh4/r2d.c index 4221060308..5e22ed79b2 100644 --- a/hw/sh4/r2d.c +++ b/hw/sh4/r2d.c @@ -127,7 +127,7 @@ static void r2d_fpga_irq_set(void *opaque, int n, int level) update_irl(fpga); } -static uint32_t r2d_fpga_read(void *opaque, hwaddr addr) +static uint64_t r2d_fpga_read(void *opaque, hwaddr addr, unsigned int size) { r2d_fpga_t *s = opaque; @@ -146,7 +146,7 @@ static uint32_t r2d_fpga_read(void *opaque, hwaddr addr) } static void -r2d_fpga_write(void *opaque, hwaddr addr, uint32_t value) +r2d_fpga_write(void *opaque, hwaddr addr, uint64_t value, unsigned int size) { r2d_fpga_t *s = opaque; @@ -170,10 +170,10 @@ r2d_fpga_write(void *opaque, hwaddr addr, uint32_t value) } static const MemoryRegionOps r2d_fpga_ops = { - .old_mmio = { - .read = { r2d_fpga_read, r2d_fpga_read, NULL, }, - .write = { r2d_fpga_write, r2d_fpga_write, NULL, }, - }, + .read = r2d_fpga_read, + .write = r2d_fpga_write, + .impl.min_access_size = 2, + .impl.max_access_size = 2, .endianness = DEVICE_NATIVE_ENDIAN, }; diff --git a/hw/xen/xen_backend.c b/hw/xen/xen_backend.c index b2cb22b99d..2510e2e4ff 100644 --- a/hw/xen/xen_backend.c +++ b/hw/xen/xen_backend.c @@ -714,9 +714,7 @@ int xen_be_init(void) return -1; } - if (qemu_set_fd_handler(xs_fileno(xenstore), xenstore_update, NULL, NULL) < 0) { - goto err; - } + qemu_set_fd_handler(xs_fileno(xenstore), xenstore_update, NULL, NULL); if (xen_xc == XC_HANDLER_INITIAL_VALUE) { /* Check if xen_init() have been called */ diff --git a/include/block/aio.h b/include/block/aio.h index d2bb423de1..b46103ece7 100644 --- a/include/block/aio.h +++ b/include/block/aio.h @@ -241,7 +241,7 @@ bool aio_dispatch(AioContext *ctx); bool aio_poll(AioContext *ctx, bool blocking); /* Register a file descriptor and associated callbacks. Behaves very similarly - * to qemu_set_fd_handler2. Unlike qemu_set_fd_handler2, these callbacks will + * to qemu_set_fd_handler. Unlike qemu_set_fd_handler, these callbacks will * be invoked when using aio_poll(). * * Code that invokes AIO completion functions should rely on this function diff --git a/include/block/block.h b/include/block/block.h index 45e23401f7..07bb724f7d 100644 --- a/include/block/block.h +++ b/include/block/block.h @@ -182,8 +182,9 @@ void bdrv_stats_print(Monitor *mon, const QObject *data); void bdrv_info_stats(Monitor *mon, QObject **ret_data); /* disk I/O throttling */ -void bdrv_io_limits_enable(BlockDriverState *bs); +void bdrv_io_limits_enable(BlockDriverState *bs, const char *group); void bdrv_io_limits_disable(BlockDriverState *bs); +void bdrv_io_limits_update_group(BlockDriverState *bs, const char *group); void bdrv_init(void); void bdrv_init_with_whitelist(void); diff --git a/include/block/block_int.h b/include/block/block_int.h index 2732ccdaae..888ec09e96 100644 --- a/include/block/block_int.h +++ b/include/block/block_int.h @@ -392,9 +392,14 @@ struct BlockDriverState { unsigned int serialising_in_flight; /* I/O throttling */ - ThrottleState throttle_state; CoQueue throttled_reqs[2]; bool io_limits_enabled; + /* The following fields are protected by the ThrottleGroup lock. + * See the ThrottleGroup documentation for details. */ + ThrottleState *throttle_state; + ThrottleTimers throttle_timers; + unsigned pending_reqs[2]; + QLIST_ENTRY(BlockDriverState) round_robin; /* I/O stats (display with "info blockstats"). */ BlockAcctStats stats; diff --git a/include/block/throttle-groups.h b/include/block/throttle-groups.h new file mode 100644 index 0000000000..fab113f6d1 --- /dev/null +++ b/include/block/throttle-groups.h @@ -0,0 +1,46 @@ +/* + * QEMU block throttling group infrastructure + * + * Copyright (C) Nodalink, EURL. 2014 + * Copyright (C) Igalia, S.L. 2015 + * + * Authors: + * Benoît Canet <benoit.canet@nodalink.com> + * Alberto Garcia <berto@igalia.com> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#ifndef THROTTLE_GROUPS_H +#define THROTTLE_GROUPS_H + +#include "qemu/throttle.h" +#include "block/block_int.h" + +const char *throttle_group_get_name(BlockDriverState *bs); + +void throttle_group_config(BlockDriverState *bs, ThrottleConfig *cfg); +void throttle_group_get_config(BlockDriverState *bs, ThrottleConfig *cfg); + +void throttle_group_register_bs(BlockDriverState *bs, const char *groupname); +void throttle_group_unregister_bs(BlockDriverState *bs); + +void coroutine_fn throttle_group_co_io_limits_intercept(BlockDriverState *bs, + unsigned int bytes, + bool is_write); + +void throttle_group_lock(BlockDriverState *bs); +void throttle_group_unlock(BlockDriverState *bs); + +#endif diff --git a/include/qemu/main-loop.h b/include/qemu/main-loop.h index 62c68c0f32..0f4a0fd4b2 100644 --- a/include/qemu/main-loop.h +++ b/include/qemu/main-loop.h @@ -96,8 +96,7 @@ AioContext *qemu_get_aio_context(void); * that the main loop waits for. * * Calling qemu_notify_event is rarely necessary, because main loop - * services (bottom halves and timers) call it themselves. One notable - * exception occurs when using qemu_set_fd_handler2 (see below). + * services (bottom halves and timers) call it themselves. */ void qemu_notify_event(void); @@ -172,52 +171,6 @@ typedef void IOReadHandler(void *opaque, const uint8_t *buf, int size); typedef int IOCanReadHandler(void *opaque); /** - * qemu_set_fd_handler2: Register a file descriptor with the main loop - * - * This function tells the main loop to wake up whenever one of the - * following conditions is true: - * - * 1) if @fd_write is not %NULL, when the file descriptor is writable; - * - * 2) if @fd_read is not %NULL, when the file descriptor is readable. - * - * @fd_read_poll can be used to disable the @fd_read callback temporarily. - * This is useful to avoid calling qemu_set_fd_handler2 every time the - * client becomes interested in reading (or dually, stops being interested). - * A typical example is when @fd is a listening socket and you want to bound - * the number of active clients. Remember to call qemu_notify_event whenever - * the condition may change from %false to %true. - * - * The callbacks that are set up by qemu_set_fd_handler2 are level-triggered. - * If @fd_read does not read from @fd, or @fd_write does not write to @fd - * until its buffers are full, they will be called again on the next - * iteration. - * - * @fd: The file descriptor to be observed. Under Windows it must be - * a #SOCKET. - * - * @fd_read_poll: A function that returns 1 if the @fd_read callback - * should be fired. If the function returns 0, the main loop will not - * end its iteration even if @fd becomes readable. - * - * @fd_read: A level-triggered callback that is fired if @fd is readable - * at the beginning of a main loop iteration, or if it becomes readable - * during one. - * - * @fd_write: A level-triggered callback that is fired when @fd is writable - * at the beginning of a main loop iteration, or if it becomes writable - * during one. - * - * @opaque: A pointer-sized value that is passed to @fd_read_poll, - * @fd_read and @fd_write. - */ -int qemu_set_fd_handler2(int fd, - IOCanReadHandler *fd_read_poll, - IOHandler *fd_read, - IOHandler *fd_write, - void *opaque); - -/** * qemu_set_fd_handler: Register a file descriptor with the main loop * * This function tells the main loop to wake up whenever one of the @@ -245,10 +198,10 @@ int qemu_set_fd_handler2(int fd, * * @opaque: A pointer-sized value that is passed to @fd_read and @fd_write. */ -int qemu_set_fd_handler(int fd, - IOHandler *fd_read, - IOHandler *fd_write, - void *opaque); +void qemu_set_fd_handler(int fd, + IOHandler *fd_read, + IOHandler *fd_write, + void *opaque); #ifdef CONFIG_POSIX /** diff --git a/include/qemu/throttle.h b/include/qemu/throttle.h index b890613a9c..5af76f0ff4 100644 --- a/include/qemu/throttle.h +++ b/include/qemu/throttle.h @@ -1,10 +1,12 @@ /* * QEMU throttling infrastructure * - * Copyright (C) Nodalink, SARL. 2013 + * Copyright (C) Nodalink, EURL. 2013-2014 + * Copyright (C) Igalia, S.L. 2015 * - * Author: - * Benoît Canet <benoit.canet@irqsave.net> + * Authors: + * Benoît Canet <benoit.canet@nodalink.com> + * Alberto Garcia <berto@igalia.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -65,14 +67,17 @@ typedef struct ThrottleConfig { typedef struct ThrottleState { ThrottleConfig cfg; /* configuration */ int64_t previous_leak; /* timestamp of the last leak done */ - QEMUTimer * timers[2]; /* timers used to do the throttling */ +} ThrottleState; + +typedef struct ThrottleTimers { + QEMUTimer *timers[2]; /* timers used to do the throttling */ QEMUClockType clock_type; /* the clock used */ /* Callbacks */ QEMUTimerCB *read_timer_cb; QEMUTimerCB *write_timer_cb; void *timer_opaque; -} ThrottleState; +} ThrottleTimers; /* operations on single leaky buckets */ void throttle_leak_bucket(LeakyBucket *bkt, int64_t delta); @@ -86,20 +91,23 @@ bool throttle_compute_timer(ThrottleState *ts, int64_t *next_timestamp); /* init/destroy cycle */ -void throttle_init(ThrottleState *ts, - AioContext *aio_context, - QEMUClockType clock_type, - void (read_timer)(void *), - void (write_timer)(void *), - void *timer_opaque); +void throttle_init(ThrottleState *ts); + +void throttle_timers_init(ThrottleTimers *tt, + AioContext *aio_context, + QEMUClockType clock_type, + QEMUTimerCB *read_timer_cb, + QEMUTimerCB *write_timer_cb, + void *timer_opaque); -void throttle_destroy(ThrottleState *ts); +void throttle_timers_destroy(ThrottleTimers *tt); -void throttle_detach_aio_context(ThrottleState *ts); +void throttle_timers_detach_aio_context(ThrottleTimers *tt); -void throttle_attach_aio_context(ThrottleState *ts, AioContext *new_context); +void throttle_timers_attach_aio_context(ThrottleTimers *tt, + AioContext *new_context); -bool throttle_have_timer(ThrottleState *ts); +bool throttle_timers_are_initialized(ThrottleTimers *tt); /* configuration */ bool throttle_enabled(ThrottleConfig *cfg); @@ -108,12 +116,16 @@ bool throttle_conflicting(ThrottleConfig *cfg); bool throttle_is_valid(ThrottleConfig *cfg); -void throttle_config(ThrottleState *ts, ThrottleConfig *cfg); +void throttle_config(ThrottleState *ts, + ThrottleTimers *tt, + ThrottleConfig *cfg); void throttle_get_config(ThrottleState *ts, ThrottleConfig *cfg); /* usage */ -bool throttle_schedule_timer(ThrottleState *ts, bool is_write); +bool throttle_schedule_timer(ThrottleState *ts, + ThrottleTimers *tt, + bool is_write); void throttle_account(ThrottleState *ts, bool is_write, uint64_t size); diff --git a/iohandler.c b/iohandler.c index cca614f087..826f713e9f 100644 --- a/iohandler.c +++ b/iohandler.c @@ -33,7 +33,6 @@ #endif typedef struct IOHandlerRecord { - IOCanReadHandler *fd_read_poll; IOHandler *fd_read; IOHandler *fd_write; void *opaque; @@ -46,11 +45,7 @@ typedef struct IOHandlerRecord { static QLIST_HEAD(, IOHandlerRecord) io_handlers = QLIST_HEAD_INITIALIZER(io_handlers); - -/* XXX: fd_read_poll should be suppressed, but an API change is - necessary in the character devices to suppress fd_can_read(). */ -int qemu_set_fd_handler2(int fd, - IOCanReadHandler *fd_read_poll, +void qemu_set_fd_handler(int fd, IOHandler *fd_read, IOHandler *fd_write, void *opaque) @@ -75,7 +70,6 @@ int qemu_set_fd_handler2(int fd, QLIST_INSERT_HEAD(&io_handlers, ioh, next); found: ioh->fd = fd; - ioh->fd_read_poll = fd_read_poll; ioh->fd_read = fd_read; ioh->fd_write = fd_write; ioh->opaque = opaque; @@ -83,15 +77,6 @@ int qemu_set_fd_handler2(int fd, ioh->deleted = 0; qemu_notify_event(); } - return 0; -} - -int qemu_set_fd_handler(int fd, - IOHandler *fd_read, - IOHandler *fd_write, - void *opaque) -{ - return qemu_set_fd_handler2(fd, NULL, fd_read, fd_write, opaque); } void qemu_iohandler_fill(GArray *pollfds) @@ -103,9 +88,7 @@ void qemu_iohandler_fill(GArray *pollfds) if (ioh->deleted) continue; - if (ioh->fd_read && - (!ioh->fd_read_poll || - ioh->fd_read_poll(ioh->opaque) != 0)) { + if (ioh->fd_read) { events |= G_IO_IN | G_IO_HUP | G_IO_ERR; } if (ioh->fd_write) { diff --git a/iothread.c b/iothread.c index 0416fc4268..878a594ef4 100644 --- a/iothread.c +++ b/iothread.c @@ -31,14 +31,21 @@ typedef ObjectClass IOThreadClass; static void *iothread_run(void *opaque) { IOThread *iothread = opaque; + bool blocking; qemu_mutex_lock(&iothread->init_done_lock); iothread->thread_id = qemu_get_thread_id(); qemu_cond_signal(&iothread->init_done_cond); qemu_mutex_unlock(&iothread->init_done_lock); - while (!atomic_read(&iothread->stopping)) { - aio_poll(iothread->ctx, true); + while (!iothread->stopping) { + aio_context_acquire(iothread->ctx); + blocking = true; + while (!iothread->stopping && aio_poll(iothread->ctx, blocking)) { + /* Progress was made, keep going */ + blocking = false; + } + aio_context_release(iothread->ctx); } return NULL; } diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 0ba97062b7..b71e866973 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -1075,6 +1075,35 @@ static inline void elf_core_copy_regs(target_elf_gregset_t *regs, #define USE_ELF_CORE_DUMP #define ELF_EXEC_PAGESIZE 4096 +enum { + SH_CPU_HAS_FPU = 0x0001, /* Hardware FPU support */ + SH_CPU_HAS_P2_FLUSH_BUG = 0x0002, /* Need to flush the cache in P2 area */ + SH_CPU_HAS_MMU_PAGE_ASSOC = 0x0004, /* SH3: TLB way selection bit support */ + SH_CPU_HAS_DSP = 0x0008, /* SH-DSP: DSP support */ + SH_CPU_HAS_PERF_COUNTER = 0x0010, /* Hardware performance counters */ + SH_CPU_HAS_PTEA = 0x0020, /* PTEA register */ + SH_CPU_HAS_LLSC = 0x0040, /* movli.l/movco.l */ + SH_CPU_HAS_L2_CACHE = 0x0080, /* Secondary cache / URAM */ + SH_CPU_HAS_OP32 = 0x0100, /* 32-bit instruction support */ + SH_CPU_HAS_PTEAEX = 0x0200, /* PTE ASID Extension support */ +}; + +#define ELF_HWCAP get_elf_hwcap() + +static uint32_t get_elf_hwcap(void) +{ + SuperHCPU *cpu = SUPERH_CPU(thread_cpu); + uint32_t hwcap = 0; + + hwcap |= SH_CPU_HAS_FPU; + + if (cpu->env.features & SH_FEATURE_SH4A) { + hwcap |= SH_CPU_HAS_LLSC; + } + + return hwcap; +} + #endif #ifdef TARGET_CRIS diff --git a/linux-user/main.c b/linux-user/main.c index 6989b82455..a0d3e58bd4 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -3925,6 +3925,8 @@ int main(int argc, char **argv, char **envp) # else cpu_model = "750"; # endif +#elif defined TARGET_SH4 + cpu_model = TYPE_SH7785_CPU; #else cpu_model = "any"; #endif diff --git a/main-loop.c b/main-loop.c index 981bcb5f8e..82875a4dfd 100644 --- a/main-loop.c +++ b/main-loop.c @@ -100,8 +100,7 @@ static int qemu_signal_init(void) fcntl_setfl(sigfd, O_NONBLOCK); - qemu_set_fd_handler2(sigfd, NULL, sigfd_handler, NULL, - (void *)(intptr_t)sigfd); + qemu_set_fd_handler(sigfd, sigfd_handler, NULL, (void *)(intptr_t)sigfd); return 0; } diff --git a/migration/exec.c b/migration/exec.c index 479024752f..8406d2bbde 100644 --- a/migration/exec.c +++ b/migration/exec.c @@ -49,7 +49,7 @@ static void exec_accept_incoming_migration(void *opaque) { QEMUFile *f = opaque; - qemu_set_fd_handler2(qemu_get_fd(f), NULL, NULL, NULL, NULL); + qemu_set_fd_handler(qemu_get_fd(f), NULL, NULL, NULL); process_incoming_migration(f); } @@ -64,6 +64,6 @@ void exec_start_incoming_migration(const char *command, Error **errp) return; } - qemu_set_fd_handler2(qemu_get_fd(f), NULL, - exec_accept_incoming_migration, NULL, f); + qemu_set_fd_handler(qemu_get_fd(f), exec_accept_incoming_migration, NULL, + f); } diff --git a/migration/fd.c b/migration/fd.c index 129da9910b..3e4bed0e06 100644 --- a/migration/fd.c +++ b/migration/fd.c @@ -62,7 +62,7 @@ static void fd_accept_incoming_migration(void *opaque) { QEMUFile *f = opaque; - qemu_set_fd_handler2(qemu_get_fd(f), NULL, NULL, NULL, NULL); + qemu_set_fd_handler(qemu_get_fd(f), NULL, NULL, NULL); process_incoming_migration(f); } @@ -84,5 +84,5 @@ void fd_start_incoming_migration(const char *infd, Error **errp) return; } - qemu_set_fd_handler2(fd, NULL, fd_accept_incoming_migration, NULL, f); + qemu_set_fd_handler(fd, fd_accept_incoming_migration, NULL, f); } diff --git a/migration/rdma.c b/migration/rdma.c index 48b3e64b34..cf5de7e2ae 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -2840,7 +2840,7 @@ static int qemu_rdma_accept(RDMAContext *rdma) } } - qemu_set_fd_handler2(rdma->channel->fd, NULL, NULL, NULL, NULL); + qemu_set_fd_handler(rdma->channel->fd, NULL, NULL, NULL); ret = rdma_accept(rdma->cm_id, &conn_param); if (ret) { @@ -3337,9 +3337,8 @@ void rdma_start_incoming_migration(const char *host_port, Error **errp) trace_rdma_start_incoming_migration_after_rdma_listen(); - qemu_set_fd_handler2(rdma->channel->fd, NULL, - rdma_accept_incoming_migration, NULL, - (void *)(intptr_t) rdma); + qemu_set_fd_handler(rdma->channel->fd, rdma_accept_incoming_migration, + NULL, (void *)(intptr_t)rdma); return; err: error_propagate(errp, local_err); diff --git a/migration/tcp.c b/migration/tcp.c index 91c9cf381e..ae891728ef 100644 --- a/migration/tcp.c +++ b/migration/tcp.c @@ -65,7 +65,7 @@ static void tcp_accept_incoming_migration(void *opaque) c = qemu_accept(s, (struct sockaddr *)&addr, &addrlen); err = socket_error(); } while (c < 0 && err == EINTR); - qemu_set_fd_handler2(s, NULL, NULL, NULL, NULL); + qemu_set_fd_handler(s, NULL, NULL, NULL); closesocket(s); DPRINTF("accepted migration\n"); @@ -98,6 +98,6 @@ void tcp_start_incoming_migration(const char *host_port, Error **errp) return; } - qemu_set_fd_handler2(s, NULL, tcp_accept_incoming_migration, NULL, - (void *)(intptr_t)s); + qemu_set_fd_handler(s, tcp_accept_incoming_migration, NULL, + (void *)(intptr_t)s); } diff --git a/migration/unix.c b/migration/unix.c index 1cdadfbc83..b591813eb9 100644 --- a/migration/unix.c +++ b/migration/unix.c @@ -65,7 +65,7 @@ static void unix_accept_incoming_migration(void *opaque) c = qemu_accept(s, (struct sockaddr *)&addr, &addrlen); err = errno; } while (c < 0 && err == EINTR); - qemu_set_fd_handler2(s, NULL, NULL, NULL, NULL); + qemu_set_fd_handler(s, NULL, NULL, NULL); close(s); DPRINTF("accepted migration\n"); @@ -98,6 +98,6 @@ void unix_start_incoming_migration(const char *path, Error **errp) return; } - qemu_set_fd_handler2(s, NULL, unix_accept_incoming_migration, NULL, - (void *)(intptr_t)s); + qemu_set_fd_handler(s, unix_accept_incoming_migration, NULL, + (void *)(intptr_t)s); } @@ -2863,6 +2863,34 @@ static mon_cmd_t info_cmds[] = { .mhandler.cmd = hmp_info_memory_devices, }, { + .name = "rocker", + .args_type = "name:s", + .params = "name", + .help = "Show rocker switch", + .mhandler.cmd = hmp_rocker, + }, + { + .name = "rocker-ports", + .args_type = "name:s", + .params = "name", + .help = "Show rocker ports", + .mhandler.cmd = hmp_rocker_ports, + }, + { + .name = "rocker-of-dpa-flows", + .args_type = "name:s,tbl_id:i?", + .params = "name [tbl_id]", + .help = "Show rocker OF-DPA flow tables", + .mhandler.cmd = hmp_rocker_of_dpa_flows, + }, + { + .name = "rocker-of-dpa-groups", + .args_type = "name:s,type:i?", + .params = "name [type]", + .help = "Show rocker OF-DPA groups", + .mhandler.cmd = hmp_rocker_of_dpa_groups, + }, + { .name = NULL, }, }; diff --git a/net/l2tpv3.c b/net/l2tpv3.c index ed395dc126..356dae2b72 100644 --- a/net/l2tpv3.c +++ b/net/l2tpv3.c @@ -133,17 +133,15 @@ typedef struct NetL2TPV3State { } NetL2TPV3State; -static int l2tpv3_can_send(void *opaque); static void net_l2tpv3_send(void *opaque); static void l2tpv3_writable(void *opaque); static void l2tpv3_update_fd_handler(NetL2TPV3State *s) { - qemu_set_fd_handler2(s->fd, - s->read_poll ? l2tpv3_can_send : NULL, - s->read_poll ? net_l2tpv3_send : NULL, - s->write_poll ? l2tpv3_writable : NULL, - s); + qemu_set_fd_handler(s->fd, + s->read_poll ? net_l2tpv3_send : NULL, + s->write_poll ? l2tpv3_writable : NULL, + s); } static void l2tpv3_read_poll(NetL2TPV3State *s, bool enable) @@ -169,13 +167,6 @@ static void l2tpv3_writable(void *opaque) qemu_flush_queued_packets(&s->nc); } -static int l2tpv3_can_send(void *opaque) -{ - NetL2TPV3State *s = opaque; - - return qemu_can_send_packet(&s->nc); -} - static void l2tpv3_send_completed(NetClientState *nc, ssize_t len) { NetL2TPV3State *s = DO_UPCAST(NetL2TPV3State, nc, nc); diff --git a/net/netmap.c b/net/netmap.c index 69300eb1ae..508b82947d 100644 --- a/net/netmap.c +++ b/net/netmap.c @@ -132,26 +132,16 @@ error: return -1; } -/* Tell the event-loop if the netmap backend can send packets - to the frontend. */ -static int netmap_can_send(void *opaque) -{ - NetmapState *s = opaque; - - return qemu_can_send_packet(&s->nc); -} - static void netmap_send(void *opaque); static void netmap_writable(void *opaque); /* Set the event-loop handlers for the netmap backend. */ static void netmap_update_fd_handler(NetmapState *s) { - qemu_set_fd_handler2(s->me.fd, - s->read_poll ? netmap_can_send : NULL, - s->read_poll ? netmap_send : NULL, - s->write_poll ? netmap_writable : NULL, - s); + qemu_set_fd_handler(s->me.fd, + s->read_poll ? netmap_send : NULL, + s->write_poll ? netmap_writable : NULL, + s); } /* Update the read handler. */ @@ -317,7 +307,7 @@ static void netmap_send(void *opaque) /* Keep sending while there are available packets into the netmap RX ring and the forwarding path towards the peer is open. */ - while (!nm_ring_empty(ring) && qemu_can_send_packet(&s->nc)) { + while (!nm_ring_empty(ring)) { uint32_t i; uint32_t idx; bool morefrag; diff --git a/net/socket.c b/net/socket.c index 5a19aa1881..c752696cbb 100644 --- a/net/socket.c +++ b/net/socket.c @@ -51,21 +51,12 @@ typedef struct NetSocketState { static void net_socket_accept(void *opaque); static void net_socket_writable(void *opaque); -/* Only read packets from socket when peer can receive them */ -static int net_socket_can_send(void *opaque) -{ - NetSocketState *s = opaque; - - return qemu_can_send_packet(&s->nc); -} - static void net_socket_update_fd_handler(NetSocketState *s) { - qemu_set_fd_handler2(s->fd, - s->read_poll ? net_socket_can_send : NULL, - s->read_poll ? s->send_fn : NULL, - s->write_poll ? net_socket_writable : NULL, - s); + qemu_set_fd_handler(s->fd, + s->read_poll ? s->send_fn : NULL, + s->write_poll ? net_socket_writable : NULL, + s); } static void net_socket_read_poll(NetSocketState *s, bool enable) @@ -142,6 +133,15 @@ static ssize_t net_socket_receive_dgram(NetClientState *nc, const uint8_t *buf, return ret; } +static void net_socket_send_completed(NetClientState *nc, ssize_t len) +{ + NetSocketState *s = DO_UPCAST(NetSocketState, nc, nc); + + if (!s->read_poll) { + net_socket_read_poll(s, true); + } +} + static void net_socket_send(void *opaque) { NetSocketState *s = opaque; @@ -211,9 +211,13 @@ static void net_socket_send(void *opaque) buf += l; size -= l; if (s->index >= s->packet_len) { - qemu_send_packet(&s->nc, s->buf, s->packet_len); s->index = 0; s->state = 0; + if (qemu_send_packet_async(&s->nc, s->buf, size, + net_socket_send_completed) == 0) { + net_socket_read_poll(s, false); + break; + } } break; } @@ -234,7 +238,10 @@ static void net_socket_send_dgram(void *opaque) net_socket_write_poll(s, false); return; } - qemu_send_packet(&s->nc, s->buf, size); + if (qemu_send_packet_async(&s->nc, s->buf, size, + net_socket_send_completed) == 0) { + net_socket_read_poll(s, false); + } } static int net_socket_mcast_create(struct sockaddr_in *mcastaddr, struct in_addr *localaddr) @@ -62,17 +62,15 @@ typedef struct TAPState { static void launch_script(const char *setup_script, const char *ifname, int fd, Error **errp); -static int tap_can_send(void *opaque); static void tap_send(void *opaque); static void tap_writable(void *opaque); static void tap_update_fd_handler(TAPState *s) { - qemu_set_fd_handler2(s->fd, - s->read_poll && s->enabled ? tap_can_send : NULL, - s->read_poll && s->enabled ? tap_send : NULL, - s->write_poll && s->enabled ? tap_writable : NULL, - s); + qemu_set_fd_handler(s->fd, + s->read_poll && s->enabled ? tap_send : NULL, + s->write_poll && s->enabled ? tap_writable : NULL, + s); } static void tap_read_poll(TAPState *s, bool enable) @@ -166,13 +164,6 @@ static ssize_t tap_receive(NetClientState *nc, const uint8_t *buf, size_t size) return tap_write_packet(s, iov, 1); } -static int tap_can_send(void *opaque) -{ - TAPState *s = opaque; - - return qemu_can_send_packet(&s->nc); -} - #ifndef __sun__ ssize_t tap_read_packet(int tapfd, uint8_t *buf, int maxlen) { @@ -192,7 +183,7 @@ static void tap_send(void *opaque) int size; int packets = 0; - while (qemu_can_send_packet(&s->nc)) { + while (true) { uint8_t *buf = s->buf; size = tap_read_packet(s->fd, s->buf, sizeof(s->buf)); diff --git a/qapi-schema.json b/qapi-schema.json index 6e17a5c36c..bcc604b813 100644 --- a/qapi-schema.json +++ b/qapi-schema.json @@ -3788,3 +3788,6 @@ # Since: 2.1 ## { 'command': 'rtc-reset-reinjection' } + +# Rocker ethernet network switch +{ 'include': 'qapi/rocker.json' } diff --git a/qapi/block-core.json b/qapi/block-core.json index 8411d4f83a..afa9d3d1f3 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -259,6 +259,8 @@ # # @iops_size: #optional an I/O size in bytes (Since 1.7) # +# @group: #optional throttle group name (Since 2.4) +# # @cache: the cache mode used for the block device (since: 2.3) # # @write_threshold: configured write threshold for the device. @@ -278,7 +280,7 @@ '*bps_max': 'int', '*bps_rd_max': 'int', '*bps_wr_max': 'int', '*iops_max': 'int', '*iops_rd_max': 'int', '*iops_wr_max': 'int', - '*iops_size': 'int', 'cache': 'BlockdevCacheInfo', + '*iops_size': 'int', '*group': 'str', 'cache': 'BlockdevCacheInfo', 'write_threshold': 'int' } } ## @@ -1062,6 +1064,27 @@ # # Change I/O throttle limits for a block drive. # +# Since QEMU 2.4, each device with I/O limits is member of a throttle +# group. +# +# If two or more devices are members of the same group, the limits +# will apply to the combined I/O of the whole group in a round-robin +# fashion. Therefore, setting new I/O limits to a device will affect +# the whole group. +# +# The name of the group can be specified using the 'group' parameter. +# If the parameter is unset, it is assumed to be the current group of +# that device. If it's not in any group yet, the name of the device +# will be used as the name for its group. +# +# The 'group' parameter can also be used to move a device to a +# different group. In this case the limits specified in the parameters +# will be applied to the new group only. +# +# I/O limits can be disabled by setting all of them to 0. In this case +# the device will be removed from its group and the rest of its +# members will no be affected. The 'group' parameter is ignored. +# # @device: The name of the device # # @bps: total throughput limit in bytes per second @@ -1090,6 +1113,8 @@ # # @iops_size: #optional an I/O size in bytes (Since 1.7) # +# @group: #optional throttle group name (Since 2.4) +# # Returns: Nothing on success # If @device is not a valid block device, DeviceNotFound # @@ -1101,7 +1126,7 @@ '*bps_max': 'int', '*bps_rd_max': 'int', '*bps_wr_max': 'int', '*iops_max': 'int', '*iops_rd_max': 'int', '*iops_wr_max': 'int', - '*iops_size': 'int' } } + '*iops_size': 'int', '*group': 'str' } } ## # @block-stream: diff --git a/qapi/rocker.json b/qapi/rocker.json new file mode 100644 index 0000000000..2fe7fdfa66 --- /dev/null +++ b/qapi/rocker.json @@ -0,0 +1,286 @@ +## +# @Rocker: +# +# Rocker switch information. +# +# @name: switch name +# +# @id: switch ID +# +# @ports: number of front-panel ports +# +# Since: 2.4 +## +{ 'struct': 'RockerSwitch', + 'data': { 'name': 'str', 'id': 'uint64', 'ports': 'uint32' } } + +## +# @query-rocker: +# +# Return rocker switch information. +# +# Returns: @Rocker information +# +# Since: 2.4 +## +{ 'command': 'query-rocker', + 'data': { 'name': 'str' }, + 'returns': 'RockerSwitch' } + +## +# @RockerPortDuplex: +# +# An eumeration of port duplex states. +# +# @half: half duplex +# +# @full: full duplex +# +# Since: 2.4 +## +{ 'enum': 'RockerPortDuplex', 'data': [ 'half', 'full' ] } + +## +# @RockerPortAutoneg: +# +# An eumeration of port autoneg states. +# +# @off: autoneg is off +# +# @on: autoneg is on +# +# Since: 2.4 +## +{ 'enum': 'RockerPortAutoneg', 'data': [ 'off', 'on' ] } + +## +# @RockerPort: +# +# Rocker switch port information. +# +# @name: port name +# +# @enabled: port is enabled for I/O +# +# @link-up: physical link is UP on port +# +# @speed: port link speed in Mbps +# +# @duplex: port link duplex +# +# @autoneg: port link autoneg +# +# Since: 2.4 +## +{ 'struct': 'RockerPort', + 'data': { 'name': 'str', 'enabled': 'bool', 'link-up': 'bool', + 'speed': 'uint32', 'duplex': 'RockerPortDuplex', + 'autoneg': 'RockerPortAutoneg' } } + +## +# @query-rocker-ports: +# +# Return rocker switch information. +# +# Returns: @Rocker information +# +# Since: 2.4 +## +{ 'command': 'query-rocker-ports', + 'data': { 'name': 'str' }, + 'returns': ['RockerPort'] } + +## +# @RockerOfDpaFlowKey: +# +# Rocker switch OF-DPA flow key +# +# @priority: key priority, 0 being lowest priority +# +# @tbl-id: flow table ID +# +# @in-pport: #optional physical input port +# +# @tunnel-id: #optional tunnel ID +# +# @vlan-id: #optional VLAN ID +# +# @eth-type: #optional Ethernet header type +# +# @eth-src: #optional Ethernet header source MAC address +# +# @eth-dst: #optional Ethernet header destination MAC address +# +# @ip-proto: #optional IP Header protocol field +# +# @ip-tos: #optional IP header TOS field +# +# @ip-dst: #optional IP header destination address +# +# Note: fields are marked #optional to indicate that they may or may not +# appear in the flow key depending if they're relevant to the flow key. +# +# Since: 2.4 +## +{ 'struct': 'RockerOfDpaFlowKey', + 'data' : { 'priority': 'uint32', 'tbl-id': 'uint32', '*in-pport': 'uint32', + '*tunnel-id': 'uint32', '*vlan-id': 'uint16', + '*eth-type': 'uint16', '*eth-src': 'str', '*eth-dst': 'str', + '*ip-proto': 'uint8', '*ip-tos': 'uint8', '*ip-dst': 'str' } } + +## +# @RockerOfDpaFlowMask: +# +# Rocker switch OF-DPA flow mask +# +# @in-pport: #optional physical input port +# +# @tunnel-id: #optional tunnel ID +# +# @vlan-id: #optional VLAN ID +# +# @eth-src: #optional Ethernet header source MAC address +# +# @eth-dst: #optional Ethernet header destination MAC address +# +# @ip-proto: #optional IP Header protocol field +# +# @ip-tos: #optional IP header TOS field +# +# Note: fields are marked #optional to indicate that they may or may not +# appear in the flow mask depending if they're relevant to the flow mask. +# +# Since: 2.4 +## +{ 'struct': 'RockerOfDpaFlowMask', + 'data' : { '*in-pport': 'uint32', '*tunnel-id': 'uint32', + '*vlan-id': 'uint16', '*eth-src': 'str', '*eth-dst': 'str', + '*ip-proto': 'uint8', '*ip-tos': 'uint8' } } + +## +# @RockerOfDpaFlowAction: +# +# Rocker switch OF-DPA flow action +# +# @goto-tbl: #optional next table ID +# +# @group-id: #optional group ID +# +# @tunnel-lport: #optional tunnel logical port ID +# +# @vlan-id: #optional VLAN ID +# +# @new-vlan-id: #optional new VLAN ID +# +# @out-pport: #optional physical output port +# +# Note: fields are marked #optional to indicate that they may or may not +# appear in the flow action depending if they're relevant to the flow action. +# +# Since: 2.4 +## +{ 'struct': 'RockerOfDpaFlowAction', + 'data' : { '*goto-tbl': 'uint32', '*group-id': 'uint32', + '*tunnel-lport': 'uint32', '*vlan-id': 'uint16', + '*new-vlan-id': 'uint16', '*out-pport': 'uint32' } } + +## +# @RockerOfDpaFlow: +# +# Rocker switch OF-DPA flow +# +# @cookie: flow unique cookie ID +# +# @hits: count of matches (hits) on flow +# +# @key: flow key +# +# @mask: flow mask +# +# @action: flow action +# +# Since: 2.4 +## +{ 'struct': 'RockerOfDpaFlow', + 'data': { 'cookie': 'uint64', 'hits': 'uint64', 'key': 'RockerOfDpaFlowKey', + 'mask': 'RockerOfDpaFlowMask', 'action': 'RockerOfDpaFlowAction' } } + +## +# @query-rocker-of-dpa-flows: +# +# Return rocker OF-DPA flow information. +# +# @name: switch name +# +# @tbl-id: #optional flow table ID. If tbl-id is not specified, returns +# flow information for all tables. +# +# Returns: @Rocker OF-DPA flow information +# +# Since: 2.4 +## +{ 'command': 'query-rocker-of-dpa-flows', + 'data': { 'name': 'str', '*tbl-id': 'uint32' }, + 'returns': ['RockerOfDpaFlow'] } + +## +# @RockerOfDpaGroup: +# +# Rocker switch OF-DPA group +# +# @id: group unique ID +# +# @type: group type +# +# @vlan-id: #optional VLAN ID +# +# @pport: #optional physical port number +# +# @index: #optional group index, unique with group type +# +# @out-pport: #optional output physical port number +# +# @group-id: #optional next group ID +# +# @set-vlan-id: #optional VLAN ID to set +# +# @pop-vlan: #optional pop VLAN headr from packet +# +# @group-ids: #optional list of next group IDs +# +# @set-eth-src: #optional set source MAC address in Ethernet header +# +# @set-eth-dst: #optional set destination MAC address in Ethernet header +# +# @ttl-check: #optional perform TTL check +# +# Note: fields are marked #optional to indicate that they may or may not +# appear in the group depending if they're relevant to the group type. +# +# Since: 2.4 +## +{ 'struct': 'RockerOfDpaGroup', + 'data': { 'id': 'uint32', 'type': 'uint8', '*vlan-id': 'uint16', + '*pport': 'uint32', '*index': 'uint32', '*out-pport': 'uint32', + '*group-id': 'uint32', '*set-vlan-id': 'uint16', + '*pop-vlan': 'uint8', '*group-ids': ['uint32'], + '*set-eth-src': 'str', '*set-eth-dst': 'str', + '*ttl-check': 'uint8' } } + +## +# @query-rocker-of-dpa-groups: +# +# Return rocker OF-DPA group information. +# +# @name: switch name +# +# @type: #optional group type. If type is not specified, returns +# group information for all group types. +# +# Returns: @Rocker OF-DPA group information +# +# Since: 2.4 +## +{ 'command': 'query-rocker-of-dpa-groups', + 'data': { 'name': 'str', '*type': 'uint8' }, + 'returns': ['RockerOfDpaGroup'] } diff --git a/qemu-options.hx b/qemu-options.hx index 1d281f6818..289ddcb944 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -468,6 +468,7 @@ DEF("drive", HAS_ARG, QEMU_OPTION_drive, " [[,bps_max=bm]|[[,bps_rd_max=rm][,bps_wr_max=wm]]]\n" " [[,iops_max=im]|[[,iops_rd_max=irm][,iops_wr_max=iwm]]]\n" " [[,iops_size=is]]\n" + " [[,group=g]]\n" " use 'file' as a drive image\n", QEMU_ARCH_ALL) STEXI @item -drive @var{option}[,@var{option}[,@var{option}[,...]]] diff --git a/qmp-commands.hx b/qmp-commands.hx index 867a21fab6..1db6524e97 100644 --- a/qmp-commands.hx +++ b/qmp-commands.hx @@ -1853,7 +1853,7 @@ EQMP { .name = "block_set_io_throttle", - .args_type = "device:B,bps:l,bps_rd:l,bps_wr:l,iops:l,iops_rd:l,iops_wr:l,bps_max:l?,bps_rd_max:l?,bps_wr_max:l?,iops_max:l?,iops_rd_max:l?,iops_wr_max:l?,iops_size:l?", + .args_type = "device:B,bps:l,bps_rd:l,bps_wr:l,iops:l,iops_rd:l,iops_wr:l,bps_max:l?,bps_rd_max:l?,bps_wr_max:l?,iops_max:l?,iops_rd_max:l?,iops_wr_max:l?,iops_size:l?,group:s?", .mhandler.cmd_new = qmp_marshal_input_block_set_io_throttle, }, @@ -1879,6 +1879,7 @@ Arguments: - "iops_rd_max": read I/O operations max (json-int) - "iops_wr_max": write I/O operations max (json-int) - "iops_size": I/O size in bytes when limiting (json-int) +- "group": throttle group name (json-string) Example: @@ -4165,3 +4166,106 @@ Example: <- { "return": {} } EQMP + + { + .name = "query-rocker", + .args_type = "name:s", + .mhandler.cmd_new = qmp_marshal_input_query_rocker, + }, + +SQMP +Show rocker switch +------------------ + +Arguments: + +- "name": switch name + +Example: + +-> { "execute": "query-rocker", "arguments": { "name": "sw1" } } +<- { "return": {"name": "sw1", "ports": 2, "id": 1327446905938}} + +EQMP + + { + .name = "query-rocker-ports", + .args_type = "name:s", + .mhandler.cmd_new = qmp_marshal_input_query_rocker_ports, + }, + +SQMP +Show rocker switch ports +------------------------ + +Arguments: + +- "name": switch name + +Example: + +-> { "execute": "query-rocker-ports", "arguments": { "name": "sw1" } } +<- { "return": [ {"duplex": "full", "enabled": true, "name": "sw1.1", + "autoneg": "off", "link-up": true, "speed": 10000}, + {"duplex": "full", "enabled": true, "name": "sw1.2", + "autoneg": "off", "link-up": true, "speed": 10000} + ]} + +EQMP + + { + .name = "query-rocker-of-dpa-flows", + .args_type = "name:s,tbl-id:i?", + .mhandler.cmd_new = qmp_marshal_input_query_rocker_of_dpa_flows, + }, + +SQMP +Show rocker switch OF-DPA flow tables +------------------------------------- + +Arguments: + +- "name": switch name +- "tbl-id": (optional) flow table ID + +Example: + +-> { "execute": "query-rocker-of-dpa-flows", "arguments": { "name": "sw1" } } +<- { "return": [ {"key": {"in-pport": 0, "priority": 1, "tbl-id": 0}, + "hits": 138, + "cookie": 0, + "action": {"goto-tbl": 10}, + "mask": {"in-pport": 4294901760} + }, + {...more...}, + ]} + +EQMP + + { + .name = "query-rocker-of-dpa-groups", + .args_type = "name:s,type:i?", + .mhandler.cmd_new = qmp_marshal_input_query_rocker_of_dpa_groups, + }, + +SQMP +Show rocker OF-DPA group tables +------------------------------- + +Arguments: + +- "name": switch name +- "type": (optional) group type + +Example: + +-> { "execute": "query-rocker-of-dpa-groups", "arguments": { "name": "sw1" } } +<- { "return": [ {"type": 0, "out-pport": 2, "pport": 2, "vlan-id": 3841, + "pop-vlan": 1, "id": 251723778}, + {"type": 0, "out-pport": 0, "pport": 0, "vlan-id": 3841, + "pop-vlan": 1, "id": 251723776}, + {"type": 0, "out-pport": 1, "pport": 1, "vlan-id": 3840, + "pop-vlan": 1, "id": 251658241}, + {"type": 0, "out-pport": 0, "pport": 0, "vlan-id": 3840, + "pop-vlan": 1, "id": 251658240} + ]} diff --git a/stubs/set-fd-handler.c b/stubs/set-fd-handler.c index fc874d33fe..a8481bc3c1 100644 --- a/stubs/set-fd-handler.c +++ b/stubs/set-fd-handler.c @@ -1,8 +1,7 @@ #include "qemu-common.h" #include "qemu/main-loop.h" -int qemu_set_fd_handler2(int fd, - IOCanReadHandler *fd_read_poll, +void qemu_set_fd_handler(int fd, IOHandler *fd_read, IOHandler *fd_write, void *opaque) diff --git a/target-sh4/cpu.c b/target-sh4/cpu.c index d187a2bdba..cccb14fe7b 100644 --- a/target-sh4/cpu.c +++ b/target-sh4/cpu.c @@ -61,7 +61,8 @@ static void superh_cpu_reset(CPUState *s) env->fpscr = FPSCR_PR; /* value for userspace according to the kernel */ set_float_rounding_mode(float_round_nearest_even, &env->fp_status); /* ?! */ #else - env->sr = SR_MD | SR_RB | SR_BL | SR_I3 | SR_I2 | SR_I1 | SR_I0; + env->sr = (1u << SR_MD) | (1u << SR_RB) | (1u << SR_BL) | + (1u << SR_I3) | (1u << SR_I2) | (1u << SR_I1) | (1u << SR_I0); env->fpscr = FPSCR_DN | FPSCR_RM_ZERO; /* CPU reset value according to SH4 manual */ set_float_rounding_mode(float_round_to_zero, &env->fp_status); set_flush_to_zero(1, &env->fp_status); diff --git a/target-sh4/cpu.h b/target-sh4/cpu.h index c8dea6c020..4a027a6c1c 100644 --- a/target-sh4/cpu.h +++ b/target-sh4/cpu.h @@ -47,18 +47,18 @@ #define TARGET_PHYS_ADDR_SPACE_BITS 32 #define TARGET_VIRT_ADDR_SPACE_BITS 32 -#define SR_MD (1 << 30) -#define SR_RB (1 << 29) -#define SR_BL (1 << 28) -#define SR_FD (1 << 15) -#define SR_M (1 << 9) -#define SR_Q (1 << 8) -#define SR_I3 (1 << 7) -#define SR_I2 (1 << 6) -#define SR_I1 (1 << 5) -#define SR_I0 (1 << 4) -#define SR_S (1 << 1) -#define SR_T (1 << 0) +#define SR_MD 30 +#define SR_RB 29 +#define SR_BL 28 +#define SR_FD 15 +#define SR_M 9 +#define SR_Q 8 +#define SR_I3 7 +#define SR_I2 6 +#define SR_I1 5 +#define SR_I0 4 +#define SR_S 1 +#define SR_T 0 #define FPSCR_MASK (0x003fffff) #define FPSCR_FR (1 << 21) @@ -138,7 +138,10 @@ typedef struct CPUSH4State { uint32_t flags; /* general execution flags */ uint32_t gregs[24]; /* general registers */ float32 fregs[32]; /* floating point registers */ - uint32_t sr; /* status register */ + uint32_t sr; /* status register (with T split out) */ + uint32_t sr_m; /* M bit of status register */ + uint32_t sr_q; /* Q bit of status register */ + uint32_t sr_t; /* T bit of status register */ uint32_t ssr; /* saved status register */ uint32_t spc; /* saved program counter */ uint32_t gbr; /* global base register */ @@ -234,7 +237,7 @@ void cpu_load_tlb(CPUSH4State * env); #define MMU_USER_IDX 1 static inline int cpu_mmu_index (CPUSH4State *env) { - return (env->sr & SR_MD) == 0 ? 1 : 0; + return (env->sr & (1u << SR_MD)) == 0 ? 1 : 0; } #include "exec/cpu-all.h" @@ -331,6 +334,21 @@ static inline int cpu_ptel_pr (uint32_t ptel) #define TB_FLAG_PENDING_MOVCA (1 << 4) +static inline target_ulong cpu_read_sr(CPUSH4State *env) +{ + return env->sr | (env->sr_m << SR_M) | + (env->sr_q << SR_Q) | + (env->sr_t << SR_T); +} + +static inline void cpu_write_sr(CPUSH4State *env, target_ulong sr) +{ + env->sr_m = (sr >> SR_M) & 1; + env->sr_q = (sr >> SR_Q) & 1; + env->sr_t = (sr >> SR_T) & 1; + env->sr = sr & ~((1u << SR_M) | (1u << SR_Q) | (1u << SR_T)); +} + static inline void cpu_get_tb_cpu_state(CPUSH4State *env, target_ulong *pc, target_ulong *cs_base, int *flags) { @@ -339,8 +357,8 @@ static inline void cpu_get_tb_cpu_state(CPUSH4State *env, target_ulong *pc, *flags = (env->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL | DELAY_SLOT_TRUE | DELAY_SLOT_CLEARME)) /* Bits 0- 3 */ | (env->fpscr & (FPSCR_FR | FPSCR_SZ | FPSCR_PR)) /* Bits 19-21 */ - | (env->sr & (SR_MD | SR_RB)) /* Bits 29-30 */ - | (env->sr & SR_FD) /* Bit 15 */ + | (env->sr & ((1u << SR_MD) | (1u << SR_RB))) /* Bits 29-30 */ + | (env->sr & (1u << SR_FD)) /* Bit 15 */ | (env->movcal_backup ? TB_FLAG_PENDING_MOVCA : 0); /* Bit 4 */ } diff --git a/target-sh4/gdbstub.c b/target-sh4/gdbstub.c index df4fa2af76..a365a27aad 100644 --- a/target-sh4/gdbstub.c +++ b/target-sh4/gdbstub.c @@ -31,7 +31,7 @@ int superh_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) switch (n) { case 0 ... 7: - if ((env->sr & (SR_MD | SR_RB)) == (SR_MD | SR_RB)) { + if ((env->sr & (1u << SR_MD)) && (env->sr & (1u << SR_RB))) { return gdb_get_regl(mem_buf, env->gregs[n + 16]); } else { return gdb_get_regl(mem_buf, env->gregs[n]); @@ -51,7 +51,7 @@ int superh_cpu_gdb_read_register(CPUState *cs, uint8_t *mem_buf, int n) case 21: return gdb_get_regl(mem_buf, env->macl); case 22: - return gdb_get_regl(mem_buf, env->sr); + return gdb_get_regl(mem_buf, cpu_read_sr(env)); case 23: return gdb_get_regl(mem_buf, env->fpul); case 24: @@ -83,7 +83,7 @@ int superh_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) switch (n) { case 0 ... 7: - if ((env->sr & (SR_MD | SR_RB)) == (SR_MD | SR_RB)) { + if ((env->sr & (1u << SR_MD)) && (env->sr & (1u << SR_RB))) { env->gregs[n + 16] = ldl_p(mem_buf); } else { env->gregs[n] = ldl_p(mem_buf); @@ -111,7 +111,7 @@ int superh_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) env->macl = ldl_p(mem_buf); break; case 22: - env->sr = ldl_p(mem_buf); + cpu_write_sr(env, ldl_p(mem_buf)); break; case 23: env->fpul = ldl_p(mem_buf); diff --git a/target-sh4/helper.c b/target-sh4/helper.c index 58113601ec..a533f08ea3 100644 --- a/target-sh4/helper.c +++ b/target-sh4/helper.c @@ -93,7 +93,7 @@ void superh_cpu_do_interrupt(CPUState *cs) do_exp = cs->exception_index != -1; do_irq = do_irq && (cs->exception_index == -1); - if (env->sr & SR_BL) { + if (env->sr & (1u << SR_BL)) { if (do_exp && cs->exception_index != 0x1e0) { cs->exception_index = 0x000; /* masked exception -> reset */ } @@ -162,10 +162,10 @@ void superh_cpu_do_interrupt(CPUState *cs) log_cpu_state(cs, 0); } - env->ssr = env->sr; + env->ssr = cpu_read_sr(env); env->spc = env->pc; env->sgr = env->gregs[15]; - env->sr |= SR_BL | SR_MD | SR_RB; + env->sr |= (1u << SR_BL) | (1u << SR_MD) | (1u << SR_RB); if (env->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL)) { /* Branch instruction should be executed again before delay slot. */ @@ -182,7 +182,7 @@ void superh_cpu_do_interrupt(CPUState *cs) case 0x000: case 0x020: case 0x140: - env->sr &= ~SR_FD; + env->sr &= ~(1u << SR_FD); env->sr |= 0xf << 4; /* IMASK */ env->pc = 0xa0000000; break; @@ -355,23 +355,24 @@ static int get_mmu_address(CPUSH4State * env, target_ulong * physical, int use_asid, n; tlb_t *matching = NULL; - use_asid = (env->mmucr & MMUCR_SV) == 0 || (env->sr & SR_MD) == 0; + use_asid = !(env->mmucr & MMUCR_SV) || !(env->sr & (1u << SR_MD)); if (rw == 2) { n = find_itlb_entry(env, address, use_asid); if (n >= 0) { matching = &env->itlb[n]; - if (!(env->sr & SR_MD) && !(matching->pr & 2)) + if (!(env->sr & (1u << SR_MD)) && !(matching->pr & 2)) { n = MMU_ITLB_VIOLATION; - else + } else { *prot = PAGE_EXEC; + } } else { n = find_utlb_entry(env, address, use_asid); if (n >= 0) { n = copy_utlb_entry_itlb(env, n); matching = &env->itlb[n]; - if (!(env->sr & SR_MD) && !(matching->pr & 2)) { - n = MMU_ITLB_VIOLATION; + if (!(env->sr & (1u << SR_MD)) && !(matching->pr & 2)) { + n = MMU_ITLB_VIOLATION; } else { *prot = PAGE_READ | PAGE_EXEC; if ((matching->pr & 1) && matching->d) { @@ -388,7 +389,7 @@ static int get_mmu_address(CPUSH4State * env, target_ulong * physical, n = find_utlb_entry(env, address, use_asid); if (n >= 0) { matching = &env->utlb[n]; - if (!(env->sr & SR_MD) && !(matching->pr & 2)) { + if (!(env->sr & (1u << SR_MD)) && !(matching->pr & 2)) { n = (rw == 1) ? MMU_DTLB_VIOLATION_WRITE : MMU_DTLB_VIOLATION_READ; } else if ((rw == 1) && !(matching->pr & 1)) { @@ -421,7 +422,7 @@ static int get_physical_address(CPUSH4State * env, target_ulong * physical, /* P1, P2 and P4 areas do not use translation */ if ((address >= 0x80000000 && address < 0xc0000000) || address >= 0xe0000000) { - if (!(env->sr & SR_MD) + if (!(env->sr & (1u << SR_MD)) && (address < 0xe0000000 || address >= 0xe4000000)) { /* Unauthorized access in user mode (only store queues are available) */ fprintf(stderr, "Unauthorized access\n"); @@ -690,7 +691,7 @@ void cpu_sh4_write_mmaped_utlb_addr(CPUSH4State *s, hwaddr addr, uint8_t d = (uint8_t)((mem_value & 0x00000200) >> 9); uint8_t v = (uint8_t)((mem_value & 0x00000100) >> 8); uint8_t asid = (uint8_t)(mem_value & 0x000000ff); - int use_asid = (s->mmucr & MMUCR_SV) == 0 || (s->sr & SR_MD) == 0; + int use_asid = !(s->mmucr & MMUCR_SV) || !(s->sr & (1u << SR_MD)); if (associate) { int i; @@ -821,10 +822,10 @@ void cpu_sh4_write_mmaped_utlb_data(CPUSH4State *s, hwaddr addr, int cpu_sh4_is_cached(CPUSH4State * env, target_ulong addr) { int n; - int use_asid = (env->mmucr & MMUCR_SV) == 0 || (env->sr & SR_MD) == 0; + int use_asid = !(env->mmucr & MMUCR_SV) || !(env->sr & (1u << SR_MD)); /* check area */ - if (env->sr & SR_MD) { + if (env->sr & (1u << SR_MD)) { /* For previledged mode, P2 and P4 area is not cachable. */ if ((0xA0000000 <= addr && addr < 0xC0000000) || 0xE0000000 <= addr) return 0; diff --git a/target-sh4/helper.h b/target-sh4/helper.h index 3b5c436ab4..c9bc407042 100644 --- a/target-sh4/helper.h +++ b/target-sh4/helper.h @@ -11,7 +11,6 @@ DEF_HELPER_3(movcal, void, env, i32, i32) DEF_HELPER_1(discard_movcal_backup, void, env) DEF_HELPER_2(ocbi, void, env, i32) -DEF_HELPER_3(div1, i32, env, i32, i32) DEF_HELPER_3(macl, void, env, i32, i32) DEF_HELPER_3(macw, void, env, i32, i32) diff --git a/target-sh4/op_helper.c b/target-sh4/op_helper.c index 74a5c4ea77..cbc11aeccd 100644 --- a/target-sh4/op_helper.c +++ b/target-sh4/op_helper.c @@ -156,124 +156,6 @@ void helper_ocbi(CPUSH4State *env, uint32_t address) } } -#define T (env->sr & SR_T) -#define Q (env->sr & SR_Q ? 1 : 0) -#define M (env->sr & SR_M ? 1 : 0) -#define SETT env->sr |= SR_T -#define CLRT env->sr &= ~SR_T -#define SETQ env->sr |= SR_Q -#define CLRQ env->sr &= ~SR_Q -#define SETM env->sr |= SR_M -#define CLRM env->sr &= ~SR_M - -uint32_t helper_div1(CPUSH4State *env, uint32_t arg0, uint32_t arg1) -{ - uint32_t tmp0, tmp2; - uint8_t old_q, tmp1 = 0xff; - - //printf("div1 arg0=0x%08x arg1=0x%08x M=%d Q=%d T=%d\n", arg0, arg1, M, Q, T); - old_q = Q; - if ((0x80000000 & arg1) != 0) - SETQ; - else - CLRQ; - tmp2 = arg0; - arg1 <<= 1; - arg1 |= T; - switch (old_q) { - case 0: - switch (M) { - case 0: - tmp0 = arg1; - arg1 -= tmp2; - tmp1 = arg1 > tmp0; - switch (Q) { - case 0: - if (tmp1) - SETQ; - else - CLRQ; - break; - case 1: - if (tmp1 == 0) - SETQ; - else - CLRQ; - break; - } - break; - case 1: - tmp0 = arg1; - arg1 += tmp2; - tmp1 = arg1 < tmp0; - switch (Q) { - case 0: - if (tmp1 == 0) - SETQ; - else - CLRQ; - break; - case 1: - if (tmp1) - SETQ; - else - CLRQ; - break; - } - break; - } - break; - case 1: - switch (M) { - case 0: - tmp0 = arg1; - arg1 += tmp2; - tmp1 = arg1 < tmp0; - switch (Q) { - case 0: - if (tmp1) - SETQ; - else - CLRQ; - break; - case 1: - if (tmp1 == 0) - SETQ; - else - CLRQ; - break; - } - break; - case 1: - tmp0 = arg1; - arg1 -= tmp2; - tmp1 = arg1 > tmp0; - switch (Q) { - case 0: - if (tmp1 == 0) - SETQ; - else - CLRQ; - break; - case 1: - if (tmp1) - SETQ; - else - CLRQ; - break; - } - break; - } - break; - } - if (Q == M) - SETT; - else - CLRT; - //printf("Output: arg1=0x%08x M=%d Q=%d T=%d\n", arg1, M, Q, T); - return arg1; -} - void helper_macl(CPUSH4State *env, uint32_t arg0, uint32_t arg1) { int64_t res; @@ -282,7 +164,7 @@ void helper_macl(CPUSH4State *env, uint32_t arg0, uint32_t arg1) res += (int64_t) (int32_t) arg0 *(int64_t) (int32_t) arg1; env->mach = (res >> 32) & 0xffffffff; env->macl = res & 0xffffffff; - if (env->sr & SR_S) { + if (env->sr & (1u << SR_S)) { if (res < 0) env->mach |= 0xffff0000; else @@ -298,7 +180,7 @@ void helper_macw(CPUSH4State *env, uint32_t arg0, uint32_t arg1) res += (int64_t) (int16_t) arg0 *(int64_t) (int16_t) arg1; env->mach = (res >> 32) & 0xffffffff; env->macl = res & 0xffffffff; - if (env->sr & SR_S) { + if (env->sr & (1u << SR_S)) { if (res < -0x80000000) { env->mach = 1; env->macl = 0x80000000; @@ -309,16 +191,6 @@ void helper_macw(CPUSH4State *env, uint32_t arg0, uint32_t arg1) } } -static inline void set_t(CPUSH4State *env) -{ - env->sr |= SR_T; -} - -static inline void clr_t(CPUSH4State *env) -{ - env->sr &= ~SR_T; -} - void helper_ld_fpscr(CPUSH4State *env, uint32_t val) { env->fpscr = val & FPSCR_MASK; @@ -403,10 +275,8 @@ void helper_fcmp_eq_FT(CPUSH4State *env, float32 t0, float32 t1) relation = float32_compare(t0, t1, &env->fp_status); if (unlikely(relation == float_relation_unordered)) { update_fpscr(env, GETPC()); - } else if (relation == float_relation_equal) { - set_t(env); } else { - clr_t(env); + env->sr_t = (relation == float_relation_equal); } } @@ -418,10 +288,8 @@ void helper_fcmp_eq_DT(CPUSH4State *env, float64 t0, float64 t1) relation = float64_compare(t0, t1, &env->fp_status); if (unlikely(relation == float_relation_unordered)) { update_fpscr(env, GETPC()); - } else if (relation == float_relation_equal) { - set_t(env); } else { - clr_t(env); + env->sr_t = (relation == float_relation_equal); } } @@ -433,10 +301,8 @@ void helper_fcmp_gt_FT(CPUSH4State *env, float32 t0, float32 t1) relation = float32_compare(t0, t1, &env->fp_status); if (unlikely(relation == float_relation_unordered)) { update_fpscr(env, GETPC()); - } else if (relation == float_relation_greater) { - set_t(env); } else { - clr_t(env); + env->sr_t = (relation == float_relation_greater); } } @@ -448,10 +314,8 @@ void helper_fcmp_gt_DT(CPUSH4State *env, float64 t0, float64 t1) relation = float64_compare(t0, t1, &env->fp_status); if (unlikely(relation == float_relation_unordered)) { update_fpscr(env, GETPC()); - } else if (relation == float_relation_greater) { - set_t(env); } else { - clr_t(env); + env->sr_t = (relation == float_relation_greater); } } diff --git a/target-sh4/translate.c b/target-sh4/translate.c index 41aa928321..28259f9e14 100644 --- a/target-sh4/translate.c +++ b/target-sh4/translate.c @@ -18,7 +18,6 @@ */ #define DEBUG_DISAS -//#define SH4_SINGLE_STEP #include "cpu.h" #include "disas/disas.h" @@ -47,7 +46,7 @@ typedef struct DisasContext { #if defined(CONFIG_USER_ONLY) #define IS_USER(ctx) 1 #else -#define IS_USER(ctx) (!(ctx->flags & SR_MD)) +#define IS_USER(ctx) (!(ctx->flags & (1u << SR_MD))) #endif enum { @@ -62,7 +61,8 @@ enum { /* global register indexes */ static TCGv_ptr cpu_env; static TCGv cpu_gregs[24]; -static TCGv cpu_pc, cpu_sr, cpu_ssr, cpu_spc, cpu_gbr; +static TCGv cpu_sr, cpu_sr_m, cpu_sr_q, cpu_sr_t; +static TCGv cpu_pc, cpu_ssr, cpu_spc, cpu_gbr; static TCGv cpu_vbr, cpu_sgr, cpu_dbr, cpu_mach, cpu_macl; static TCGv cpu_pr, cpu_fpscr, cpu_fpul, cpu_ldst; static TCGv cpu_fregs[32]; @@ -110,6 +110,12 @@ void sh4_translate_init(void) offsetof(CPUSH4State, pc), "PC"); cpu_sr = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUSH4State, sr), "SR"); + cpu_sr_m = tcg_global_mem_new_i32(TCG_AREG0, + offsetof(CPUSH4State, sr_m), "SR_M"); + cpu_sr_q = tcg_global_mem_new_i32(TCG_AREG0, + offsetof(CPUSH4State, sr_q), "SR_Q"); + cpu_sr_t = tcg_global_mem_new_i32(TCG_AREG0, + offsetof(CPUSH4State, sr_t), "SR_T"); cpu_ssr = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUSH4State, ssr), "SSR"); cpu_spc = tcg_global_mem_new_i32(TCG_AREG0, @@ -156,7 +162,7 @@ void superh_cpu_dump_state(CPUState *cs, FILE *f, CPUSH4State *env = &cpu->env; int i; cpu_fprintf(f, "pc=0x%08x sr=0x%08x pr=0x%08x fpscr=0x%08x\n", - env->pc, env->sr, env->pr, env->fpscr); + env->pc, cpu_read_sr(env), env->pr, env->fpscr); cpu_fprintf(f, "spc=0x%08x ssr=0x%08x gbr=0x%08x vbr=0x%08x\n", env->spc, env->ssr, env->gbr, env->vbr); cpu_fprintf(f, "sgr=0x%08x dbr=0x%08x delayed_pc=0x%08x fpul=0x%08x\n", @@ -175,6 +181,30 @@ void superh_cpu_dump_state(CPUState *cs, FILE *f, } } +static void gen_read_sr(TCGv dst) +{ + TCGv t0 = tcg_temp_new(); + tcg_gen_shli_i32(t0, cpu_sr_q, SR_Q); + tcg_gen_or_i32(dst, dst, t0); + tcg_gen_shli_i32(t0, cpu_sr_m, SR_M); + tcg_gen_or_i32(dst, dst, t0); + tcg_gen_shli_i32(t0, cpu_sr_t, SR_T); + tcg_gen_or_i32(dst, cpu_sr, t0); + tcg_temp_free_i32(t0); +} + +static void gen_write_sr(TCGv src) +{ + tcg_gen_andi_i32(cpu_sr, src, + ~((1u << SR_Q) | (1u << SR_M) | (1u << SR_T))); + tcg_gen_shri_i32(cpu_sr_q, src, SR_Q); + tcg_gen_andi_i32(cpu_sr_q, cpu_sr_q, 1); + tcg_gen_shri_i32(cpu_sr_m, src, SR_M); + tcg_gen_andi_i32(cpu_sr_m, cpu_sr_m, 1); + tcg_gen_shri_i32(cpu_sr_t, src, SR_T); + tcg_gen_andi_i32(cpu_sr_t, cpu_sr_t, 1); +} + static void gen_goto_tb(DisasContext * ctx, int n, target_ulong dest) { TranslationBlock *tb; @@ -210,12 +240,9 @@ static void gen_jump(DisasContext * ctx) static inline void gen_branch_slot(uint32_t delayed_pc, int t) { - TCGv sr; TCGLabel *label = gen_new_label(); tcg_gen_movi_i32(cpu_delayed_pc, delayed_pc); - sr = tcg_temp_new(); - tcg_gen_andi_i32(sr, cpu_sr, SR_T); - tcg_gen_brcondi_i32(t ? TCG_COND_EQ:TCG_COND_NE, sr, 0, label); + tcg_gen_brcondi_i32(t ? TCG_COND_EQ : TCG_COND_NE, cpu_sr_t, 0, label); tcg_gen_ori_i32(cpu_flags, cpu_flags, DELAY_SLOT_TRUE); gen_set_label(label); } @@ -224,13 +251,8 @@ static inline void gen_branch_slot(uint32_t delayed_pc, int t) static void gen_conditional_jump(DisasContext * ctx, target_ulong ift, target_ulong ifnott) { - TCGLabel *l1; - TCGv sr; - - l1 = gen_new_label(); - sr = tcg_temp_new(); - tcg_gen_andi_i32(sr, cpu_sr, SR_T); - tcg_gen_brcondi_i32(TCG_COND_NE, sr, 0, l1); + TCGLabel *l1 = gen_new_label(); + tcg_gen_brcondi_i32(TCG_COND_NE, cpu_sr_t, 0, l1); gen_goto_tb(ctx, 0, ifnott); gen_set_label(l1); gen_goto_tb(ctx, 1, ift); @@ -252,54 +274,12 @@ static void gen_delayed_conditional_jump(DisasContext * ctx) gen_jump(ctx); } -static inline void gen_cmp(int cond, TCGv t0, TCGv t1) -{ - TCGv t; - - t = tcg_temp_new(); - tcg_gen_setcond_i32(cond, t, t1, t0); - tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T); - tcg_gen_or_i32(cpu_sr, cpu_sr, t); - - tcg_temp_free(t); -} - -static inline void gen_cmp_imm(int cond, TCGv t0, int32_t imm) -{ - TCGv t; - - t = tcg_temp_new(); - tcg_gen_setcondi_i32(cond, t, t0, imm); - tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T); - tcg_gen_or_i32(cpu_sr, cpu_sr, t); - - tcg_temp_free(t); -} - static inline void gen_store_flags(uint32_t flags) { tcg_gen_andi_i32(cpu_flags, cpu_flags, DELAY_SLOT_TRUE); tcg_gen_ori_i32(cpu_flags, cpu_flags, flags); } -static inline void gen_copy_bit_i32(TCGv t0, int p0, TCGv t1, int p1) -{ - TCGv tmp = tcg_temp_new(); - - p0 &= 0x1f; - p1 &= 0x1f; - - tcg_gen_andi_i32(tmp, t1, (1 << p1)); - tcg_gen_andi_i32(t0, t0, ~(1 << p0)); - if (p0 < p1) - tcg_gen_shri_i32(tmp, tmp, p1 - p0); - else if (p0 > p1) - tcg_gen_shli_i32(tmp, tmp, p0 - p1); - tcg_gen_or_i32(t0, t0, tmp); - - tcg_temp_free(tmp); -} - static inline void gen_load_fpr64(TCGv_i64 t, int reg) { tcg_gen_concat_i32_i64(t, cpu_fregs[reg + 1], cpu_fregs[reg]); @@ -326,10 +306,12 @@ static inline void gen_store_fpr64 (TCGv_i64 t, int reg) #define B11_8 ((ctx->opcode >> 8) & 0xf) #define B15_12 ((ctx->opcode >> 12) & 0xf) -#define REG(x) ((x) < 8 && (ctx->flags & (SR_MD | SR_RB)) == (SR_MD | SR_RB) \ +#define REG(x) ((x) < 8 && (ctx->flags & (1u << SR_MD))\ + && (ctx->flags & (1u << SR_RB))\ ? (cpu_gregs[x + 16]) : (cpu_gregs[x])) -#define ALTREG(x) ((x) < 8 && (ctx->flags & (SR_MD | SR_RB)) != (SR_MD | SR_RB)\ +#define ALTREG(x) ((x) < 8 && (!(ctx->flags & (1u << SR_MD))\ + || !(ctx->flags & (1u << SR_RB)))\ ? (cpu_gregs[x + 16]) : (cpu_gregs[x])) #define FREG(x) (ctx->flags & FPSCR_FR ? (x) ^ 0x10 : (x)) @@ -359,7 +341,7 @@ static inline void gen_store_fpr64 (TCGv_i64 t, int reg) } #define CHECK_FPU_ENABLED \ - if (ctx->flags & SR_FD) { \ + if (ctx->flags & (1u << SR_FD)) { \ tcg_gen_movi_i32(cpu_pc, ctx->pc); \ if (ctx->flags & (DELAY_SLOT | DELAY_SLOT_CONDITIONAL)) { \ gen_helper_raise_slot_fpu_disable(cpu_env); \ @@ -409,7 +391,9 @@ static void _decode_opc(DisasContext * ctx) switch (ctx->opcode) { case 0x0019: /* div0u */ - tcg_gen_andi_i32(cpu_sr, cpu_sr, ~(SR_M | SR_Q | SR_T)); + tcg_gen_movi_i32(cpu_sr_m, 0); + tcg_gen_movi_i32(cpu_sr_q, 0); + tcg_gen_movi_i32(cpu_sr_t, 0); return; case 0x000b: /* rts */ CHECK_NOT_DELAY_SLOT @@ -422,10 +406,10 @@ static void _decode_opc(DisasContext * ctx) tcg_gen_movi_i32(cpu_macl, 0); return; case 0x0048: /* clrs */ - tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_S); + tcg_gen_andi_i32(cpu_sr, cpu_sr, ~(1u << SR_S)); return; case 0x0008: /* clrt */ - tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T); + tcg_gen_movi_i32(cpu_sr_t, 0); return; case 0x0038: /* ldtlb */ CHECK_PRIVILEGED @@ -434,16 +418,16 @@ static void _decode_opc(DisasContext * ctx) case 0x002b: /* rte */ CHECK_PRIVILEGED CHECK_NOT_DELAY_SLOT - tcg_gen_mov_i32(cpu_sr, cpu_ssr); + gen_write_sr(cpu_ssr); tcg_gen_mov_i32(cpu_delayed_pc, cpu_spc); ctx->flags |= DELAY_SLOT; ctx->delayed_pc = (uint32_t) - 1; return; case 0x0058: /* sets */ - tcg_gen_ori_i32(cpu_sr, cpu_sr, SR_S); + tcg_gen_ori_i32(cpu_sr, cpu_sr, (1u << SR_S)); return; case 0x0018: /* sett */ - tcg_gen_ori_i32(cpu_sr, cpu_sr, SR_T); + tcg_gen_movi_i32(cpu_sr_t, 1); return; case 0xfbfd: /* frchg */ tcg_gen_xori_i32(cpu_fpscr, cpu_fpscr, FPSCR_FR); @@ -659,22 +643,14 @@ static void _decode_opc(DisasContext * ctx) return; case 0x300e: /* addc Rm,Rn */ { - TCGv t0, t1, t2; - t0 = tcg_temp_new(); - tcg_gen_andi_i32(t0, cpu_sr, SR_T); + TCGv t0, t1; + t0 = tcg_const_tl(0); t1 = tcg_temp_new(); - tcg_gen_add_i32(t1, REG(B7_4), REG(B11_8)); - tcg_gen_add_i32(t0, t0, t1); - t2 = tcg_temp_new(); - tcg_gen_setcond_i32(TCG_COND_GTU, t2, REG(B11_8), t1); - tcg_gen_setcond_i32(TCG_COND_GTU, t1, t1, t0); - tcg_gen_or_i32(t1, t1, t2); - tcg_temp_free(t2); - tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T); - tcg_gen_or_i32(cpu_sr, cpu_sr, t1); - tcg_temp_free(t1); - tcg_gen_mov_i32(REG(B11_8), t0); + tcg_gen_add2_i32(t1, cpu_sr_t, cpu_sr_t, t0, REG(B7_4), t0); + tcg_gen_add2_i32(REG(B11_8), cpu_sr_t, + REG(B11_8), t0, t1, cpu_sr_t); tcg_temp_free(t0); + tcg_temp_free(t1); } return; case 0x300f: /* addv Rm,Rn */ @@ -686,11 +662,9 @@ static void _decode_opc(DisasContext * ctx) tcg_gen_xor_i32(t1, t0, REG(B11_8)); t2 = tcg_temp_new(); tcg_gen_xor_i32(t2, REG(B7_4), REG(B11_8)); - tcg_gen_andc_i32(t1, t1, t2); + tcg_gen_andc_i32(cpu_sr_t, t1, t2); tcg_temp_free(t2); - tcg_gen_shri_i32(t1, t1, 31); - tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T); - tcg_gen_or_i32(cpu_sr, cpu_sr, t1); + tcg_gen_shri_i32(cpu_sr_t, cpu_sr_t, 31); tcg_temp_free(t1); tcg_gen_mov_i32(REG(B7_4), t0); tcg_temp_free(t0); @@ -700,54 +674,79 @@ static void _decode_opc(DisasContext * ctx) tcg_gen_and_i32(REG(B11_8), REG(B11_8), REG(B7_4)); return; case 0x3000: /* cmp/eq Rm,Rn */ - gen_cmp(TCG_COND_EQ, REG(B7_4), REG(B11_8)); + tcg_gen_setcond_i32(TCG_COND_EQ, cpu_sr_t, REG(B11_8), REG(B7_4)); return; case 0x3003: /* cmp/ge Rm,Rn */ - gen_cmp(TCG_COND_GE, REG(B7_4), REG(B11_8)); + tcg_gen_setcond_i32(TCG_COND_GE, cpu_sr_t, REG(B11_8), REG(B7_4)); return; case 0x3007: /* cmp/gt Rm,Rn */ - gen_cmp(TCG_COND_GT, REG(B7_4), REG(B11_8)); + tcg_gen_setcond_i32(TCG_COND_GT, cpu_sr_t, REG(B11_8), REG(B7_4)); return; case 0x3006: /* cmp/hi Rm,Rn */ - gen_cmp(TCG_COND_GTU, REG(B7_4), REG(B11_8)); + tcg_gen_setcond_i32(TCG_COND_GTU, cpu_sr_t, REG(B11_8), REG(B7_4)); return; case 0x3002: /* cmp/hs Rm,Rn */ - gen_cmp(TCG_COND_GEU, REG(B7_4), REG(B11_8)); + tcg_gen_setcond_i32(TCG_COND_GEU, cpu_sr_t, REG(B11_8), REG(B7_4)); return; case 0x200c: /* cmp/str Rm,Rn */ { TCGv cmp1 = tcg_temp_new(); TCGv cmp2 = tcg_temp_new(); - tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T); tcg_gen_xor_i32(cmp1, REG(B7_4), REG(B11_8)); tcg_gen_andi_i32(cmp2, cmp1, 0xff000000); - tcg_gen_setcondi_i32(TCG_COND_EQ, cmp2, cmp2, 0); - tcg_gen_or_i32(cpu_sr, cpu_sr, cmp2); + tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, cmp2, 0); tcg_gen_andi_i32(cmp2, cmp1, 0x00ff0000); tcg_gen_setcondi_i32(TCG_COND_EQ, cmp2, cmp2, 0); - tcg_gen_or_i32(cpu_sr, cpu_sr, cmp2); + tcg_gen_or_i32(cpu_sr_t, cpu_sr_t, cmp2); tcg_gen_andi_i32(cmp2, cmp1, 0x0000ff00); tcg_gen_setcondi_i32(TCG_COND_EQ, cmp2, cmp2, 0); - tcg_gen_or_i32(cpu_sr, cpu_sr, cmp2); + tcg_gen_or_i32(cpu_sr_t, cpu_sr_t, cmp2); tcg_gen_andi_i32(cmp2, cmp1, 0x000000ff); tcg_gen_setcondi_i32(TCG_COND_EQ, cmp2, cmp2, 0); - tcg_gen_or_i32(cpu_sr, cpu_sr, cmp2); + tcg_gen_or_i32(cpu_sr_t, cpu_sr_t, cmp2); tcg_temp_free(cmp2); tcg_temp_free(cmp1); } return; case 0x2007: /* div0s Rm,Rn */ - { - gen_copy_bit_i32(cpu_sr, 8, REG(B11_8), 31); /* SR_Q */ - gen_copy_bit_i32(cpu_sr, 9, REG(B7_4), 31); /* SR_M */ - TCGv val = tcg_temp_new(); - tcg_gen_xor_i32(val, REG(B7_4), REG(B11_8)); - gen_copy_bit_i32(cpu_sr, 0, val, 31); /* SR_T */ - tcg_temp_free(val); - } + tcg_gen_shri_i32(cpu_sr_q, REG(B11_8), 31); /* SR_Q */ + tcg_gen_shri_i32(cpu_sr_m, REG(B7_4), 31); /* SR_M */ + tcg_gen_xor_i32(cpu_sr_t, cpu_sr_q, cpu_sr_m); /* SR_T */ return; case 0x3004: /* div1 Rm,Rn */ - gen_helper_div1(REG(B11_8), cpu_env, REG(B7_4), REG(B11_8)); + { + TCGv t0 = tcg_temp_new(); + TCGv t1 = tcg_temp_new(); + TCGv t2 = tcg_temp_new(); + TCGv zero = tcg_const_i32(0); + + /* shift left arg1, saving the bit being pushed out and inserting + T on the right */ + tcg_gen_shri_i32(t0, REG(B11_8), 31); + tcg_gen_shli_i32(REG(B11_8), REG(B11_8), 1); + tcg_gen_or_i32(REG(B11_8), REG(B11_8), cpu_sr_t); + + /* Add or subtract arg0 from arg1 depending if Q == M. To avoid + using 64-bit temps, we compute arg0's high part from q ^ m, so + that it is 0x00000000 when adding the value or 0xffffffff when + subtracting it. */ + tcg_gen_xor_i32(t1, cpu_sr_q, cpu_sr_m); + tcg_gen_subi_i32(t1, t1, 1); + tcg_gen_neg_i32(t2, REG(B7_4)); + tcg_gen_movcond_i32(TCG_COND_EQ, t2, t1, zero, REG(B7_4), t2); + tcg_gen_add2_i32(REG(B11_8), t1, REG(B11_8), zero, t2, t1); + + /* compute T and Q depending on carry */ + tcg_gen_andi_i32(t1, t1, 1); + tcg_gen_xor_i32(t1, t1, t0); + tcg_gen_xori_i32(cpu_sr_t, t1, 1); + tcg_gen_xor_i32(cpu_sr_q, cpu_sr_m, t1); + + tcg_temp_free(zero); + tcg_temp_free(t2); + tcg_temp_free(t1); + tcg_temp_free(t0); + } return; case 0x300d: /* dmuls.l Rm,Rn */ tcg_gen_muls2_i32(cpu_macl, cpu_mach, REG(B7_4), REG(B11_8)); @@ -827,19 +826,13 @@ static void _decode_opc(DisasContext * ctx) return; case 0x600a: /* negc Rm,Rn */ { - TCGv t0, t1; - t0 = tcg_temp_new(); - tcg_gen_neg_i32(t0, REG(B7_4)); - t1 = tcg_temp_new(); - tcg_gen_andi_i32(t1, cpu_sr, SR_T); - tcg_gen_sub_i32(REG(B11_8), t0, t1); - tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T); - tcg_gen_setcondi_i32(TCG_COND_GTU, t1, t0, 0); - tcg_gen_or_i32(cpu_sr, cpu_sr, t1); - tcg_gen_setcond_i32(TCG_COND_GTU, t1, REG(B11_8), t0); - tcg_gen_or_i32(cpu_sr, cpu_sr, t1); + TCGv t0 = tcg_const_i32(0); + tcg_gen_add2_i32(REG(B11_8), cpu_sr_t, + REG(B7_4), t0, cpu_sr_t, t0); + tcg_gen_sub2_i32(REG(B11_8), cpu_sr_t, + t0, t0, REG(B11_8), cpu_sr_t); + tcg_gen_andi_i32(cpu_sr_t, cpu_sr_t, 1); tcg_temp_free(t0); - tcg_temp_free(t1); } return; case 0x6007: /* not Rm,Rn */ @@ -918,22 +911,15 @@ static void _decode_opc(DisasContext * ctx) return; case 0x300a: /* subc Rm,Rn */ { - TCGv t0, t1, t2; - t0 = tcg_temp_new(); - tcg_gen_andi_i32(t0, cpu_sr, SR_T); + TCGv t0, t1; + t0 = tcg_const_tl(0); t1 = tcg_temp_new(); - tcg_gen_sub_i32(t1, REG(B11_8), REG(B7_4)); - tcg_gen_sub_i32(t0, t1, t0); - t2 = tcg_temp_new(); - tcg_gen_setcond_i32(TCG_COND_LTU, t2, REG(B11_8), t1); - tcg_gen_setcond_i32(TCG_COND_LTU, t1, t1, t0); - tcg_gen_or_i32(t1, t1, t2); - tcg_temp_free(t2); - tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T); - tcg_gen_or_i32(cpu_sr, cpu_sr, t1); - tcg_temp_free(t1); - tcg_gen_mov_i32(REG(B11_8), t0); + tcg_gen_add2_i32(t1, cpu_sr_t, cpu_sr_t, t0, REG(B7_4), t0); + tcg_gen_sub2_i32(REG(B11_8), cpu_sr_t, + REG(B11_8), t0, t1, cpu_sr_t); + tcg_gen_andi_i32(cpu_sr_t, cpu_sr_t, 1); tcg_temp_free(t0); + tcg_temp_free(t1); } return; case 0x300b: /* subv Rm,Rn */ @@ -947,9 +933,7 @@ static void _decode_opc(DisasContext * ctx) tcg_gen_xor_i32(t2, REG(B11_8), REG(B7_4)); tcg_gen_and_i32(t1, t1, t2); tcg_temp_free(t2); - tcg_gen_shri_i32(t1, t1, 31); - tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T); - tcg_gen_or_i32(cpu_sr, cpu_sr, t1); + tcg_gen_shri_i32(cpu_sr_t, t1, 31); tcg_temp_free(t1); tcg_gen_mov_i32(REG(B11_8), t0); tcg_temp_free(t0); @@ -959,7 +943,7 @@ static void _decode_opc(DisasContext * ctx) { TCGv val = tcg_temp_new(); tcg_gen_and_i32(val, REG(B7_4), REG(B11_8)); - gen_cmp_imm(TCG_COND_EQ, val, 0); + tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, val, 0); tcg_temp_free(val); } return; @@ -1025,24 +1009,19 @@ static void _decode_opc(DisasContext * ctx) return; case 0xf00b: /* fmov {F,D,X}Rm,@-Rn - FPSCR: Nothing */ CHECK_FPU_ENABLED + TCGv addr = tcg_temp_new_i32(); + tcg_gen_subi_i32(addr, REG(B11_8), 4); if (ctx->flags & FPSCR_SZ) { - TCGv addr = tcg_temp_new_i32(); int fr = XREG(B7_4); - tcg_gen_subi_i32(addr, REG(B11_8), 4); tcg_gen_qemu_st_i32(cpu_fregs[fr+1], addr, ctx->memidx, MO_TEUL); tcg_gen_subi_i32(addr, addr, 4); tcg_gen_qemu_st_i32(cpu_fregs[fr], addr, ctx->memidx, MO_TEUL); - tcg_gen_mov_i32(REG(B11_8), addr); - tcg_temp_free(addr); } else { - TCGv addr; - addr = tcg_temp_new_i32(); - tcg_gen_subi_i32(addr, REG(B11_8), 4); tcg_gen_qemu_st_i32(cpu_fregs[FREG(B7_4)], addr, ctx->memidx, MO_TEUL); - tcg_gen_mov_i32(REG(B11_8), addr); - tcg_temp_free(addr); } + tcg_gen_mov_i32(REG(B11_8), addr); + tcg_temp_free(addr); return; case 0xf006: /* fmov @(R0,Rm),{F,D,X}Rm - FPSCR: Nothing */ CHECK_FPU_ENABLED @@ -1210,7 +1189,7 @@ static void _decode_opc(DisasContext * ctx) ctx->flags |= DELAY_SLOT_CONDITIONAL; return; case 0x8800: /* cmp/eq #imm,R0 */ - gen_cmp_imm(TCG_COND_EQ, REG(0), B7_0s); + tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, REG(0), B7_0s); return; case 0xc400: /* mov.b @(disp,GBR),R0 */ { @@ -1326,7 +1305,7 @@ static void _decode_opc(DisasContext * ctx) { TCGv val = tcg_temp_new(); tcg_gen_andi_i32(val, REG(0), B7_0); - gen_cmp_imm(TCG_COND_EQ, val, 0); + tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, val, 0); tcg_temp_free(val); } return; @@ -1336,7 +1315,7 @@ static void _decode_opc(DisasContext * ctx) tcg_gen_add_i32(val, REG(0), cpu_gbr); tcg_gen_qemu_ld_i32(val, val, ctx->memidx, MO_UB); tcg_gen_andi_i32(val, val, B7_0); - gen_cmp_imm(TCG_COND_EQ, val, 0); + tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, val, 0); tcg_temp_free(val); } return; @@ -1399,14 +1378,14 @@ static void _decode_opc(DisasContext * ctx) ctx->delayed_pc = (uint32_t) - 1; return; case 0x4015: /* cmp/pl Rn */ - gen_cmp_imm(TCG_COND_GT, REG(B11_8), 0); + tcg_gen_setcondi_i32(TCG_COND_GT, cpu_sr_t, REG(B11_8), 0); return; case 0x4011: /* cmp/pz Rn */ - gen_cmp_imm(TCG_COND_GE, REG(B11_8), 0); + tcg_gen_setcondi_i32(TCG_COND_GE, cpu_sr_t, REG(B11_8), 0); return; case 0x4010: /* dt Rn */ tcg_gen_subi_i32(REG(B11_8), REG(B11_8), 1); - gen_cmp_imm(TCG_COND_EQ, REG(B11_8), 0); + tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, REG(B11_8), 0); return; case 0x402b: /* jmp @Rn */ CHECK_NOT_DELAY_SLOT @@ -1423,15 +1402,21 @@ static void _decode_opc(DisasContext * ctx) return; case 0x400e: /* ldc Rm,SR */ CHECK_PRIVILEGED - tcg_gen_andi_i32(cpu_sr, REG(B11_8), 0x700083f3); - ctx->bstate = BS_STOP; + { + TCGv val = tcg_temp_new(); + tcg_gen_andi_i32(val, REG(B11_8), 0x700083f3); + gen_write_sr(val); + tcg_temp_free(val); + ctx->bstate = BS_STOP; + } return; case 0x4007: /* ldc.l @Rm+,SR */ CHECK_PRIVILEGED { TCGv val = tcg_temp_new(); tcg_gen_qemu_ld_i32(val, REG(B11_8), ctx->memidx, MO_TESL); - tcg_gen_andi_i32(cpu_sr, val, 0x700083f3); + tcg_gen_andi_i32(val, val, 0x700083f3); + gen_write_sr(val); tcg_temp_free(val); tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4); ctx->bstate = BS_STOP; @@ -1439,15 +1424,18 @@ static void _decode_opc(DisasContext * ctx) return; case 0x0002: /* stc SR,Rn */ CHECK_PRIVILEGED - tcg_gen_mov_i32(REG(B11_8), cpu_sr); + gen_read_sr(REG(B11_8)); return; case 0x4003: /* stc SR,@-Rn */ CHECK_PRIVILEGED { TCGv addr = tcg_temp_new(); + TCGv val = tcg_temp_new(); tcg_gen_subi_i32(addr, REG(B11_8), 4); - tcg_gen_qemu_st_i32(cpu_sr, addr, ctx->memidx, MO_TEUL); + gen_read_sr(val); + tcg_gen_qemu_st_i32(val, addr, ctx->memidx, MO_TEUL); tcg_gen_mov_i32(REG(B11_8), addr); + tcg_temp_free(val); tcg_temp_free(addr); } return; @@ -1545,7 +1533,7 @@ static void _decode_opc(DisasContext * ctx) tcg_gen_addi_i32(REG(B11_8), REG(B11_8), 4); return; case 0x0029: /* movt Rn */ - tcg_gen_andi_i32(REG(B11_8), cpu_sr, SR_T); + tcg_gen_mov_i32(REG(B11_8), cpu_sr_t); return; case 0x0073: /* MOVCO.L @@ -1555,8 +1543,7 @@ static void _decode_opc(DisasContext * ctx) */ if (ctx->features & SH_FEATURE_SH4A) { TCGLabel *label = gen_new_label(); - tcg_gen_andi_i32(cpu_sr, cpu_sr, ~SR_T); - tcg_gen_or_i32(cpu_sr, cpu_sr, cpu_ldst); + tcg_gen_mov_i32(cpu_sr_t, cpu_ldst); tcg_gen_brcondi_i32(TCG_COND_EQ, cpu_ldst, 0, label); tcg_gen_qemu_st_i32(REG(0), REG(B11_8), ctx->memidx, MO_TEUL); gen_set_label(label); @@ -1609,42 +1596,42 @@ static void _decode_opc(DisasContext * ctx) case 0x4024: /* rotcl Rn */ { TCGv tmp = tcg_temp_new(); - tcg_gen_mov_i32(tmp, cpu_sr); - gen_copy_bit_i32(cpu_sr, 0, REG(B11_8), 31); + tcg_gen_mov_i32(tmp, cpu_sr_t); + tcg_gen_shri_i32(cpu_sr_t, REG(B11_8), 31); tcg_gen_shli_i32(REG(B11_8), REG(B11_8), 1); - gen_copy_bit_i32(REG(B11_8), 0, tmp, 0); + tcg_gen_or_i32(REG(B11_8), REG(B11_8), tmp); tcg_temp_free(tmp); } return; case 0x4025: /* rotcr Rn */ { TCGv tmp = tcg_temp_new(); - tcg_gen_mov_i32(tmp, cpu_sr); - gen_copy_bit_i32(cpu_sr, 0, REG(B11_8), 0); + tcg_gen_shli_i32(tmp, cpu_sr_t, 31); + tcg_gen_andi_i32(cpu_sr_t, REG(B11_8), 1); tcg_gen_shri_i32(REG(B11_8), REG(B11_8), 1); - gen_copy_bit_i32(REG(B11_8), 31, tmp, 0); + tcg_gen_or_i32(REG(B11_8), REG(B11_8), tmp); tcg_temp_free(tmp); } return; case 0x4004: /* rotl Rn */ tcg_gen_rotli_i32(REG(B11_8), REG(B11_8), 1); - gen_copy_bit_i32(cpu_sr, 0, REG(B11_8), 0); + tcg_gen_andi_i32(cpu_sr_t, REG(B11_8), 0); return; case 0x4005: /* rotr Rn */ - gen_copy_bit_i32(cpu_sr, 0, REG(B11_8), 0); + tcg_gen_andi_i32(cpu_sr_t, REG(B11_8), 0); tcg_gen_rotri_i32(REG(B11_8), REG(B11_8), 1); return; case 0x4000: /* shll Rn */ case 0x4020: /* shal Rn */ - gen_copy_bit_i32(cpu_sr, 0, REG(B11_8), 31); + tcg_gen_shri_i32(cpu_sr_t, REG(B11_8), 31); tcg_gen_shli_i32(REG(B11_8), REG(B11_8), 1); return; case 0x4021: /* shar Rn */ - gen_copy_bit_i32(cpu_sr, 0, REG(B11_8), 0); + tcg_gen_andi_i32(cpu_sr_t, REG(B11_8), 1); tcg_gen_sari_i32(REG(B11_8), REG(B11_8), 1); return; case 0x4001: /* shlr Rn */ - gen_copy_bit_i32(cpu_sr, 0, REG(B11_8), 0); + tcg_gen_andi_i32(cpu_sr_t, REG(B11_8), 1); tcg_gen_shri_i32(REG(B11_8), REG(B11_8), 1); return; case 0x4008: /* shll2 Rn */ @@ -1672,7 +1659,7 @@ static void _decode_opc(DisasContext * ctx) tcg_gen_mov_i32(addr, REG(B11_8)); val = tcg_temp_local_new(); tcg_gen_qemu_ld_i32(val, addr, ctx->memidx, MO_UB); - gen_cmp_imm(TCG_COND_EQ, val, 0); + tcg_gen_setcondi_i32(TCG_COND_EQ, cpu_sr_t, val, 0); tcg_gen_ori_i32(val, val, 0x80); tcg_gen_qemu_st_i32(val, addr, ctx->memidx, MO_UB); tcg_temp_free(val); @@ -1874,7 +1861,7 @@ gen_intermediate_code_internal(SuperHCPU *cpu, TranslationBlock *tb, ctx.pc = pc_start; ctx.flags = (uint32_t)tb->flags; ctx.bstate = BS_NONE; - ctx.memidx = (ctx.flags & SR_MD) == 0 ? 1 : 0; + ctx.memidx = (ctx.flags & (1u << SR_MD)) == 0 ? 1 : 0; /* We don't know if the delayed pc came from a dynamic or static branch, so assume it is a dynamic branch. */ ctx.delayed_pc = -1; /* use delayed pc from env pointer */ diff --git a/tests/qemu-iotests/093 b/tests/qemu-iotests/093 index b9096a55d4..c0e9e2b0b5 100755 --- a/tests/qemu-iotests/093 +++ b/tests/qemu-iotests/093 @@ -3,6 +3,7 @@ # Tests for IO throttling # # Copyright (C) 2015 Red Hat, Inc. +# Copyright (C) 2015 Igalia, S.L. # # 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 @@ -22,6 +23,7 @@ import iotests class ThrottleTestCase(iotests.QMPTestCase): test_img = "null-aio://" + max_drives = 3 def blockstats(self, device): result = self.vm.qmp("query-blockstats") @@ -32,26 +34,31 @@ class ThrottleTestCase(iotests.QMPTestCase): raise Exception("Device not found for blockstats: %s" % device) def setUp(self): - self.vm = iotests.VM().add_drive(self.test_img) + self.vm = iotests.VM() + for i in range(0, self.max_drives): + self.vm.add_drive(self.test_img) self.vm.launch() def tearDown(self): self.vm.shutdown() - def do_test_throttle(self, seconds, params): + def do_test_throttle(self, ndrives, seconds, params): def check_limit(limit, num): # IO throttling algorithm is discrete, allow 10% error so the test # is more robust return limit == 0 or \ - (num < seconds * limit * 1.1 - and num > seconds * limit * 0.9) + (num < seconds * limit * 1.1 / ndrives + and num > seconds * limit * 0.9 / ndrives) nsec_per_sec = 1000000000 - params['device'] = 'drive0' + params['group'] = 'test' - result = self.vm.qmp("block_set_io_throttle", conv_keys=False, **params) - self.assert_qmp(result, 'return', {}) + # Set the I/O throttling parameters to all drives + for i in range(0, ndrives): + params['device'] = 'drive%d' % i + result = self.vm.qmp("block_set_io_throttle", conv_keys=False, **params) + self.assert_qmp(result, 'return', {}) # Set vm clock to a known value ns = seconds * nsec_per_sec @@ -66,32 +73,60 @@ class ThrottleTestCase(iotests.QMPTestCase): params['iops'] / 2, params['iops_rd']) rd_nr *= seconds * 2 + rd_nr /= ndrives wr_nr = max(params['bps'] / rq_size / 2, params['bps_wr'] / rq_size, params['iops'] / 2, params['iops_wr']) wr_nr *= seconds * 2 + wr_nr /= ndrives + + # Send I/O requests to all drives for i in range(rd_nr): - self.vm.hmp_qemu_io("drive0", "aio_read %d %d" % (i * rq_size, rq_size)) - for i in range(wr_nr): - self.vm.hmp_qemu_io("drive0", "aio_write %d %d" % (i * rq_size, rq_size)) + for drive in range(0, ndrives): + self.vm.hmp_qemu_io("drive%d" % drive, "aio_read %d %d" % + (i * rq_size, rq_size)) - start_rd_bytes, start_rd_iops, start_wr_bytes, start_wr_iops = self.blockstats('drive0') + for i in range(wr_nr): + for drive in range(0, ndrives): + self.vm.hmp_qemu_io("drive%d" % drive, "aio_write %d %d" % + (i * rq_size, rq_size)) + + # We'll store the I/O stats for each drive in these arrays + start_rd_bytes = [0] * ndrives + start_rd_iops = [0] * ndrives + start_wr_bytes = [0] * ndrives + start_wr_iops = [0] * ndrives + end_rd_bytes = [0] * ndrives + end_rd_iops = [0] * ndrives + end_wr_bytes = [0] * ndrives + end_wr_iops = [0] * ndrives + + # Read the stats before advancing the clock + for i in range(0, ndrives): + start_rd_bytes[i], start_rd_iops[i], start_wr_bytes[i], \ + start_wr_iops[i] = self.blockstats('drive%d' % i) self.vm.qtest("clock_step %d" % ns) - end_rd_bytes, end_rd_iops, end_wr_bytes, end_wr_iops = self.blockstats('drive0') - - rd_bytes = end_rd_bytes - start_rd_bytes - rd_iops = end_rd_iops - start_rd_iops - wr_bytes = end_wr_bytes - start_wr_bytes - wr_iops = end_wr_iops - start_wr_iops - self.assertTrue(check_limit(params['bps'], rd_bytes + wr_bytes)) - self.assertTrue(check_limit(params['bps_rd'], rd_bytes)) - self.assertTrue(check_limit(params['bps_wr'], wr_bytes)) - self.assertTrue(check_limit(params['iops'], rd_iops + wr_iops)) - self.assertTrue(check_limit(params['iops_rd'], rd_iops)) - self.assertTrue(check_limit(params['iops_wr'], wr_iops)) + # Read the stats after advancing the clock + for i in range(0, ndrives): + end_rd_bytes[i], end_rd_iops[i], end_wr_bytes[i], \ + end_wr_iops[i] = self.blockstats('drive%d' % i) + + # Check that the I/O is within the limits and evenly distributed + for i in range(0, ndrives): + rd_bytes = end_rd_bytes[i] - start_rd_bytes[i] + rd_iops = end_rd_iops[i] - start_rd_iops[i] + wr_bytes = end_wr_bytes[i] - start_wr_bytes[i] + wr_iops = end_wr_iops[i] - start_wr_iops[i] + + self.assertTrue(check_limit(params['bps'], rd_bytes + wr_bytes)) + self.assertTrue(check_limit(params['bps_rd'], rd_bytes)) + self.assertTrue(check_limit(params['bps_wr'], wr_bytes)) + self.assertTrue(check_limit(params['iops'], rd_iops + wr_iops)) + self.assertTrue(check_limit(params['iops_rd'], rd_iops)) + self.assertTrue(check_limit(params['iops_wr'], wr_iops)) def test_all(self): params = {"bps": 4096, @@ -101,11 +136,13 @@ class ThrottleTestCase(iotests.QMPTestCase): "iops_rd": 10, "iops_wr": 10, } - # Pick each out of all possible params and test - for tk in params: - limits = dict([(k, 0) for k in params]) - limits[tk] = params[tk] - self.do_test_throttle(5, limits) + # Repeat the test with different numbers of drives + for ndrives in range(1, self.max_drives + 1): + # Pick each out of all possible params and test + for tk in params: + limits = dict([(k, 0) for k in params]) + limits[tk] = params[tk] * ndrives + self.do_test_throttle(ndrives, 5, limits) class ThrottleTestCoroutine(ThrottleTestCase): test_img = "null-co://" diff --git a/tests/rocker/bridge b/tests/rocker/bridge index 7a03f9a227..46abc6f4f6 100755 --- a/tests/rocker/bridge +++ b/tests/rocker/bridge @@ -9,8 +9,8 @@ while ! simp ssh tut h2 --cmd "ping -c 1 localhost >/dev/null"; do sleep 1; done # configure a 2-port bridge simp ssh tut sw1 --cmd "sudo /sbin/ip link add name br0 type bridge" -simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev swp1 master br0" -simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev swp2 master br0" +simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev sw1p1 master br0" +simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev sw1p2 master br0" # turn off vlan default_pvid on br0 @@ -18,28 +18,23 @@ simp ssh tut sw1 --cmd "echo 0 | sudo dd of=/sys/class/net/br0/bridge/default_pv # turn off learning and flooding in SW -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp1 learning off" -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp2 learning off" +simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p1 learning off" +simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p2 learning off" -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp1 flood off" -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp2 flood off" - -# turn on learning in HW - -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp1 learning on self" -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp2 learning on self" +simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p1 flood off" +simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p2 flood off" # bring up bridge and ports simp ssh tut sw1 --cmd "sudo ifconfig br0 up" -simp ssh tut sw1 --cmd "sudo ifconfig swp1 up" -simp ssh tut sw1 --cmd "sudo ifconfig swp2 up" +simp ssh tut sw1 --cmd "sudo ifconfig sw1p1 up" +simp ssh tut sw1 --cmd "sudo ifconfig sw1p2 up" simp ssh tut sw1 --cmd "sudo ifconfig br0 11.0.0.3/24" # config IP on hosts -simp ssh tut h1 --cmd "sudo ifconfig swp1 11.0.0.1/24" -simp ssh tut h2 --cmd "sudo ifconfig swp1 11.0.0.2/24" +simp ssh tut h1 --cmd "sudo ifconfig sw1p1 11.0.0.1/24" +simp ssh tut h2 --cmd "sudo ifconfig sw1p1 11.0.0.2/24" # test... diff --git a/tests/rocker/bridge-stp b/tests/rocker/bridge-stp index 4a111a17d3..008568ad8a 100755 --- a/tests/rocker/bridge-stp +++ b/tests/rocker/bridge-stp @@ -10,8 +10,8 @@ while ! simp ssh tut h2 --cmd "ping -c 1 localhost >/dev/null"; do sleep 1; done simp ssh tut sw1 --cmd "sudo /sbin/ip link add name br0 type bridge" simp ssh tut sw1 --cmd "sudo brctl stp br0 on" -simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev swp1 master br0" -simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev swp2 master br0" +simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev sw1p1 master br0" +simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev sw1p2 master br0" # turn off vlan default_pvid on br0 @@ -19,27 +19,22 @@ simp ssh tut sw1 --cmd "echo 0 | sudo dd of=/sys/class/net/br0/bridge/default_pv # turn off learning and flooding in SW -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp1 learning off" -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp2 learning off" +simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p1 learning off" +simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p2 learning off" -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp1 flood off" -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp2 flood off" - -# turn on learning in HW - -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp1 learning on self" -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp2 learning on self" +simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p1 flood off" +simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p2 flood off" # config IP on hosts -simp ssh tut h1 --cmd "sudo ifconfig swp1 11.0.0.1/24" -simp ssh tut h2 --cmd "sudo ifconfig swp1 11.0.0.2/24" +simp ssh tut h1 --cmd "sudo ifconfig sw1p1 11.0.0.1/24" +simp ssh tut h2 --cmd "sudo ifconfig sw1p1 11.0.0.2/24" # bring up bridge and ports simp ssh tut sw1 --cmd "sudo ifconfig br0 up" -simp ssh tut sw1 --cmd "sudo ifconfig swp1 up" -simp ssh tut sw1 --cmd "sudo ifconfig swp2 up" +simp ssh tut sw1 --cmd "sudo ifconfig sw1p1 up" +simp ssh tut sw1 --cmd "sudo ifconfig sw1p2 up" # test... diff --git a/tests/rocker/bridge-vlan b/tests/rocker/bridge-vlan index 9fa3431f66..ef9e5f53bb 100755 --- a/tests/rocker/bridge-vlan +++ b/tests/rocker/bridge-vlan @@ -9,8 +9,8 @@ while ! simp ssh tut h2 --cmd "ping -c 1 localhost >/dev/null"; do sleep 1; done # configure a 2-port bridge simp ssh tut sw1 --cmd "sudo /sbin/ip link add name br0 type bridge" -simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev swp1 master br0" -simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev swp2 master br0" +simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev sw1p1 master br0" +simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev sw1p2 master br0" # turn off vlan default_pvid on br0 # turn on vlan filtering on br0 @@ -20,37 +20,32 @@ simp ssh tut sw1 --cmd "echo 1 | sudo dd of=/sys/class/net/br0/bridge/vlan_filte # add both ports to VLAN 57 -simp ssh tut sw1 --cmd "sudo /sbin/bridge vlan add vid 57 dev swp1 master" -simp ssh tut sw1 --cmd "sudo /sbin/bridge vlan add vid 57 dev swp2 master" +simp ssh tut sw1 --cmd "sudo /sbin/bridge vlan add vid 57 dev sw1p1 master self" +simp ssh tut sw1 --cmd "sudo /sbin/bridge vlan add vid 57 dev sw1p2 master self" # turn off learning and flooding in SW -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp1 learning off" -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp2 learning off" +simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p1 learning off" +simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p2 learning off" -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp1 flood off" -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp2 flood off" - -# turn on learning in HW - -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp1 learning on self" -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp2 learning on self" +simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p1 flood off" +simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p2 flood off" # bring up bridge and ports simp ssh tut sw1 --cmd "sudo ifconfig br0 up" -simp ssh tut sw1 --cmd "sudo ifconfig swp1 up" -simp ssh tut sw1 --cmd "sudo ifconfig swp2 up" +simp ssh tut sw1 --cmd "sudo ifconfig sw1p1 up" +simp ssh tut sw1 --cmd "sudo ifconfig sw1p2 up" # config IP on host VLANs -simp ssh tut h1 --cmd "sudo vconfig add swp1 57 >/dev/null 2>&1" -simp ssh tut h1 --cmd "sudo ifconfig swp1 up" -simp ssh tut h1 --cmd "sudo ifconfig swp1.57 11.0.0.1/24" +simp ssh tut h1 --cmd "sudo vconfig add sw1p1 57 >/dev/null 2>&1" +simp ssh tut h1 --cmd "sudo ifconfig sw1p1 up" +simp ssh tut h1 --cmd "sudo ifconfig sw1p1.57 11.0.0.1/24" -simp ssh tut h2 --cmd "sudo vconfig add swp1 57 >/dev/null 2>&1" -simp ssh tut h2 --cmd "sudo ifconfig swp1 up" -simp ssh tut h2 --cmd "sudo ifconfig swp1.57 11.0.0.2/24" +simp ssh tut h2 --cmd "sudo vconfig add sw1p1 57 >/dev/null 2>&1" +simp ssh tut h2 --cmd "sudo ifconfig sw1p1 up" +simp ssh tut h2 --cmd "sudo ifconfig sw1p1.57 11.0.0.2/24" # test... diff --git a/tests/rocker/bridge-vlan-stp b/tests/rocker/bridge-vlan-stp index 77ab67efe2..c660312bc6 100755 --- a/tests/rocker/bridge-vlan-stp +++ b/tests/rocker/bridge-vlan-stp @@ -10,8 +10,8 @@ while ! simp ssh tut h2 --cmd "ping -c 1 localhost >/dev/null"; do sleep 1; done simp ssh tut sw1 --cmd "sudo /sbin/ip link add name br0 type bridge" simp ssh tut sw1 --cmd "sudo brctl stp br0 on" -simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev swp1 master br0" -simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev swp2 master br0" +simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev sw1p1 master br0" +simp ssh tut sw1 --cmd "sudo /sbin/ip link set dev sw1p2 master br0" # turn off vlan default_pvid on br0 # turn on vlan filtering on br0 @@ -21,37 +21,32 @@ simp ssh tut sw1 --cmd "echo 1 | sudo dd of=/sys/class/net/br0/bridge/vlan_filte # add both ports to VLAN 57 -simp ssh tut sw1 --cmd "sudo /sbin/bridge vlan add vid 57 dev swp1 master" -simp ssh tut sw1 --cmd "sudo /sbin/bridge vlan add vid 57 dev swp2 master" +simp ssh tut sw1 --cmd "sudo /sbin/bridge vlan add vid 57 dev sw1p1 master self" +simp ssh tut sw1 --cmd "sudo /sbin/bridge vlan add vid 57 dev sw1p2 master self" # turn off learning and flooding in SW -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp1 learning off" -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp2 learning off" +simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p1 learning off" +simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p2 learning off" -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp1 flood off" -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp2 flood off" - -# turn on learning in HW - -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp1 learning on self" -simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev swp2 learning on self" +simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p1 flood off" +simp ssh tut sw1 --cmd "sudo /sbin/bridge link set dev sw1p2 flood off" # config IP on host VLANs -simp ssh tut h1 --cmd "sudo vconfig add swp1 57 >/dev/null 2>&1" -simp ssh tut h1 --cmd "sudo ifconfig swp1 up" -simp ssh tut h1 --cmd "sudo ifconfig swp1.57 11.0.0.1/24" +simp ssh tut h1 --cmd "sudo vconfig add sw1p1 57 >/dev/null 2>&1" +simp ssh tut h1 --cmd "sudo ifconfig sw1p1 up" +simp ssh tut h1 --cmd "sudo ifconfig sw1p1.57 11.0.0.1/24" -simp ssh tut h2 --cmd "sudo vconfig add swp1 57 >/dev/null 2>&1" -simp ssh tut h2 --cmd "sudo ifconfig swp1 up" -simp ssh tut h2 --cmd "sudo ifconfig swp1.57 11.0.0.2/24" +simp ssh tut h2 --cmd "sudo vconfig add sw1p1 57 >/dev/null 2>&1" +simp ssh tut h2 --cmd "sudo ifconfig sw1p1 up" +simp ssh tut h2 --cmd "sudo ifconfig sw1p1.57 11.0.0.2/24" # bring up bridge and ports simp ssh tut sw1 --cmd "sudo ifconfig br0 up" -simp ssh tut sw1 --cmd "sudo ifconfig swp1 up" -simp ssh tut sw1 --cmd "sudo ifconfig swp2 up" +simp ssh tut sw1 --cmd "sudo ifconfig sw1p1 up" +simp ssh tut sw1 --cmd "sudo ifconfig sw1p2 up" # test... diff --git a/tests/rocker/port b/tests/rocker/port index 3437f7d7fe..5f2c248046 100755 --- a/tests/rocker/port +++ b/tests/rocker/port @@ -7,13 +7,13 @@ while ! simp ssh tut h2 --cmd "ping -c 1 localhost >/dev/null"; do sleep 1; done # bring up DUT ports -simp ssh tut sw1 --cmd "sudo ifconfig swp1 11.0.0.1/24" -simp ssh tut sw1 --cmd "sudo ifconfig swp2 12.0.0.1/24" +simp ssh tut sw1 --cmd "sudo ifconfig sw1p1 11.0.0.1/24" +simp ssh tut sw1 --cmd "sudo ifconfig sw1p2 12.0.0.1/24" # config IP on hosts -simp ssh tut h1 --cmd "sudo ifconfig swp1 11.0.0.2/24" -simp ssh tut h2 --cmd "sudo ifconfig swp1 12.0.0.2/24" +simp ssh tut h1 --cmd "sudo ifconfig sw1p1 11.0.0.2/24" +simp ssh tut h2 --cmd "sudo ifconfig sw1p1 12.0.0.2/24" # test... diff --git a/tests/test-aio.c b/tests/test-aio.c index 4b0cb45d31..a7cb5c9915 100644 --- a/tests/test-aio.c +++ b/tests/test-aio.c @@ -107,7 +107,6 @@ static void test_notify(void) typedef struct { QemuMutex start_lock; - EventNotifier notifier; bool thread_acquired; } AcquireTestData; @@ -119,8 +118,6 @@ static void *test_acquire_thread(void *opaque) qemu_mutex_lock(&data->start_lock); qemu_mutex_unlock(&data->start_lock); - g_usleep(500000); - event_notifier_set(&data->notifier); aio_context_acquire(ctx); aio_context_release(ctx); @@ -129,19 +126,20 @@ static void *test_acquire_thread(void *opaque) return NULL; } -static void dummy_notifier_read(EventNotifier *n) +static void dummy_notifier_read(EventNotifier *unused) { - event_notifier_test_and_clear(n); + g_assert(false); /* should never be invoked */ } static void test_acquire(void) { QemuThread thread; + EventNotifier notifier; AcquireTestData data; /* Dummy event notifier ensures aio_poll() will block */ - event_notifier_init(&data.notifier, false); - aio_set_event_notifier(ctx, &data.notifier, dummy_notifier_read); + event_notifier_init(¬ifier, false); + aio_set_event_notifier(ctx, ¬ifier, dummy_notifier_read); g_assert(!aio_poll(ctx, false)); /* consume aio_notify() */ qemu_mutex_init(&data.start_lock); @@ -155,13 +153,12 @@ static void test_acquire(void) /* Block in aio_poll(), let other thread kick us and acquire context */ aio_context_acquire(ctx); qemu_mutex_unlock(&data.start_lock); /* let the thread run */ - g_assert(aio_poll(ctx, true)); - g_assert(!data.thread_acquired); + g_assert(!aio_poll(ctx, true)); aio_context_release(ctx); qemu_thread_join(&thread); - aio_set_event_notifier(ctx, &data.notifier, NULL); - event_notifier_cleanup(&data.notifier); + aio_set_event_notifier(ctx, ¬ifier, NULL); + event_notifier_cleanup(¬ifier); g_assert(data.thread_acquired); } diff --git a/tests/test-throttle.c b/tests/test-throttle.c index d8ba415e43..016844546a 100644 --- a/tests/test-throttle.c +++ b/tests/test-throttle.c @@ -1,10 +1,12 @@ /* * Throttle infrastructure tests * - * Copyright Nodalink, SARL. 2013 + * Copyright Nodalink, EURL. 2013-2014 + * Copyright Igalia, S.L. 2015 * * Authors: - * Benoît Canet <benoit.canet@irqsave.net> + * Benoît Canet <benoit.canet@nodalink.com> + * Alberto Garcia <berto@igalia.com> * * This work is licensed under the terms of the GNU LGPL, version 2 or later. * See the COPYING.LIB file in the top-level directory. @@ -15,11 +17,13 @@ #include "block/aio.h" #include "qemu/throttle.h" #include "qemu/error-report.h" +#include "block/throttle-groups.h" static AioContext *ctx; static LeakyBucket bkt; static ThrottleConfig cfg; static ThrottleState ts; +static ThrottleTimers tt; /* useful function */ static bool double_cmp(double x, double y) @@ -103,17 +107,19 @@ static void test_init(void) { int i; - /* fill the structure with crap */ + /* fill the structures with crap */ memset(&ts, 1, sizeof(ts)); + memset(&tt, 1, sizeof(tt)); - /* init the structure */ - throttle_init(&ts, ctx, QEMU_CLOCK_VIRTUAL, - read_timer_cb, write_timer_cb, &ts); + /* init structures */ + throttle_init(&ts); + throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL, + read_timer_cb, write_timer_cb, &ts); /* check initialized fields */ - g_assert(ts.clock_type == QEMU_CLOCK_VIRTUAL); - g_assert(ts.timers[0]); - g_assert(ts.timers[1]); + g_assert(tt.clock_type == QEMU_CLOCK_VIRTUAL); + g_assert(tt.timers[0]); + g_assert(tt.timers[1]); /* check other fields where cleared */ g_assert(!ts.previous_leak); @@ -124,17 +130,18 @@ static void test_init(void) g_assert(!ts.cfg.buckets[i].level); } - throttle_destroy(&ts); + throttle_timers_destroy(&tt); } static void test_destroy(void) { int i; - throttle_init(&ts, ctx, QEMU_CLOCK_VIRTUAL, - read_timer_cb, write_timer_cb, &ts); - throttle_destroy(&ts); + throttle_init(&ts); + throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL, + read_timer_cb, write_timer_cb, &ts); + throttle_timers_destroy(&tt); for (i = 0; i < 2; i++) { - g_assert(!ts.timers[i]); + g_assert(!tt.timers[i]); } } @@ -170,11 +177,12 @@ static void test_config_functions(void) orig_cfg.op_size = 1; - throttle_init(&ts, ctx, QEMU_CLOCK_VIRTUAL, - read_timer_cb, write_timer_cb, &ts); + throttle_init(&ts); + throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL, + read_timer_cb, write_timer_cb, &ts); /* structure reset by throttle_init previous_leak should be null */ g_assert(!ts.previous_leak); - throttle_config(&ts, &orig_cfg); + throttle_config(&ts, &tt, &orig_cfg); /* has previous leak been initialized by throttle_config ? */ g_assert(ts.previous_leak); @@ -182,7 +190,7 @@ static void test_config_functions(void) /* get back the fixed configuration */ throttle_get_config(&ts, &final_cfg); - throttle_destroy(&ts); + throttle_timers_destroy(&tt); g_assert(final_cfg.buckets[THROTTLE_BPS_TOTAL].avg == 153); g_assert(final_cfg.buckets[THROTTLE_BPS_READ].avg == 56); @@ -323,43 +331,47 @@ static void test_is_valid(void) static void test_have_timer(void) { - /* zero the structure */ + /* zero structures */ memset(&ts, 0, sizeof(ts)); + memset(&tt, 0, sizeof(tt)); /* no timer set should return false */ - g_assert(!throttle_have_timer(&ts)); + g_assert(!throttle_timers_are_initialized(&tt)); - /* init the structure */ - throttle_init(&ts, ctx, QEMU_CLOCK_VIRTUAL, - read_timer_cb, write_timer_cb, &ts); + /* init structures */ + throttle_init(&ts); + throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL, + read_timer_cb, write_timer_cb, &ts); /* timer set by init should return true */ - g_assert(throttle_have_timer(&ts)); + g_assert(throttle_timers_are_initialized(&tt)); - throttle_destroy(&ts); + throttle_timers_destroy(&tt); } static void test_detach_attach(void) { - /* zero the structure */ + /* zero structures */ memset(&ts, 0, sizeof(ts)); + memset(&tt, 0, sizeof(tt)); /* init the structure */ - throttle_init(&ts, ctx, QEMU_CLOCK_VIRTUAL, - read_timer_cb, write_timer_cb, &ts); + throttle_init(&ts); + throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL, + read_timer_cb, write_timer_cb, &ts); /* timer set by init should return true */ - g_assert(throttle_have_timer(&ts)); + g_assert(throttle_timers_are_initialized(&tt)); /* timer should no longer exist after detaching */ - throttle_detach_aio_context(&ts); - g_assert(!throttle_have_timer(&ts)); + throttle_timers_detach_aio_context(&tt); + g_assert(!throttle_timers_are_initialized(&tt)); /* timer should exist again after attaching */ - throttle_attach_aio_context(&ts, ctx); - g_assert(throttle_have_timer(&ts)); + throttle_timers_attach_aio_context(&tt, ctx); + g_assert(throttle_timers_are_initialized(&tt)); - throttle_destroy(&ts); + throttle_timers_destroy(&tt); } static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */ @@ -387,9 +399,10 @@ static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */ cfg.op_size = op_size; - throttle_init(&ts, ctx, QEMU_CLOCK_VIRTUAL, - read_timer_cb, write_timer_cb, &ts); - throttle_config(&ts, &cfg); + throttle_init(&ts); + throttle_timers_init(&tt, ctx, QEMU_CLOCK_VIRTUAL, + read_timer_cb, write_timer_cb, &ts); + throttle_config(&ts, &tt, &cfg); /* account a read */ throttle_account(&ts, false, size); @@ -414,7 +427,7 @@ static bool do_test_accounting(bool is_ops, /* are we testing bps or ops */ return false; } - throttle_destroy(&ts); + throttle_timers_destroy(&tt); return true; } @@ -490,23 +503,80 @@ static void test_accounting(void) (64.0 / 13))); } +static void test_groups(void) +{ + ThrottleConfig cfg1, cfg2; + BlockDriverState *bdrv1, *bdrv2, *bdrv3; + + bdrv1 = bdrv_new(); + bdrv2 = bdrv_new(); + bdrv3 = bdrv_new(); + + g_assert(bdrv1->throttle_state == NULL); + g_assert(bdrv2->throttle_state == NULL); + g_assert(bdrv3->throttle_state == NULL); + + throttle_group_register_bs(bdrv1, "bar"); + throttle_group_register_bs(bdrv2, "foo"); + throttle_group_register_bs(bdrv3, "bar"); + + g_assert(bdrv1->throttle_state != NULL); + g_assert(bdrv2->throttle_state != NULL); + g_assert(bdrv3->throttle_state != NULL); + + g_assert(!strcmp(throttle_group_get_name(bdrv1), "bar")); + g_assert(!strcmp(throttle_group_get_name(bdrv2), "foo")); + g_assert(bdrv1->throttle_state == bdrv3->throttle_state); + + /* Setting the config of a group member affects the whole group */ + memset(&cfg1, 0, sizeof(cfg1)); + cfg1.buckets[THROTTLE_BPS_READ].avg = 500000; + cfg1.buckets[THROTTLE_BPS_WRITE].avg = 285000; + cfg1.buckets[THROTTLE_OPS_READ].avg = 20000; + cfg1.buckets[THROTTLE_OPS_WRITE].avg = 12000; + throttle_group_config(bdrv1, &cfg1); + + throttle_group_get_config(bdrv1, &cfg1); + throttle_group_get_config(bdrv3, &cfg2); + g_assert(!memcmp(&cfg1, &cfg2, sizeof(cfg1))); + + cfg2.buckets[THROTTLE_BPS_READ].avg = 4547; + cfg2.buckets[THROTTLE_BPS_WRITE].avg = 1349; + cfg2.buckets[THROTTLE_OPS_READ].avg = 123; + cfg2.buckets[THROTTLE_OPS_WRITE].avg = 86; + throttle_group_config(bdrv3, &cfg1); + + throttle_group_get_config(bdrv1, &cfg1); + throttle_group_get_config(bdrv3, &cfg2); + g_assert(!memcmp(&cfg1, &cfg2, sizeof(cfg1))); + + throttle_group_unregister_bs(bdrv1); + throttle_group_unregister_bs(bdrv2); + throttle_group_unregister_bs(bdrv3); + + g_assert(bdrv1->throttle_state == NULL); + g_assert(bdrv2->throttle_state == NULL); + g_assert(bdrv3->throttle_state == NULL); +} + int main(int argc, char **argv) { - GSource *src; Error *local_error = NULL; - init_clocks(); + qemu_init_main_loop(&local_error); + ctx = qemu_get_aio_context(); - ctx = aio_context_new(&local_error); if (!ctx) { error_report("Failed to create AIO Context: '%s'", - error_get_pretty(local_error)); - error_free(local_error); + local_error ? error_get_pretty(local_error) : + "Failed to initialize the QEMU main loop"); + if (local_error) { + error_free(local_error); + } exit(1); } - src = aio_get_g_source(ctx); - g_source_attach(src, NULL); - g_source_unref(src); + + bdrv_init(); do {} while (g_main_context_iteration(NULL, false)); @@ -523,6 +593,7 @@ int main(int argc, char **argv) g_test_add_func("/throttle/config/is_valid", test_is_valid); g_test_add_func("/throttle/config_functions", test_config_functions); g_test_add_func("/throttle/accounting", test_accounting); + g_test_add_func("/throttle/groups", test_groups); return g_test_run(); } diff --git a/ui/vnc-auth-sasl.c b/ui/vnc-auth-sasl.c index 2ddd2591f9..62a5fc4bf1 100644 --- a/ui/vnc-auth-sasl.c +++ b/ui/vnc-auth-sasl.c @@ -86,7 +86,7 @@ long vnc_client_write_sasl(VncState *vs) * SASL encoded output */ if (vs->output.offset == 0) { - qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); + qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs); } return ret; diff --git a/ui/vnc-auth-vencrypt.c b/ui/vnc-auth-vencrypt.c index 03ea48a69c..8fc965b4ad 100644 --- a/ui/vnc-auth-vencrypt.c +++ b/ui/vnc-auth-vencrypt.c @@ -94,7 +94,7 @@ static int vnc_start_vencrypt_handshake(VncState *vs) } VNC_DEBUG("Handshake done, switching to TLS data mode\n"); - qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, vnc_client_write, vs); + qemu_set_fd_handler(vs->csock, vnc_client_read, vnc_client_write, vs); start_auth_vencrypt_subauth(vs); diff --git a/ui/vnc-ws.c b/ui/vnc-ws.c index 38a1b8b646..8c18268054 100644 --- a/ui/vnc-ws.c +++ b/ui/vnc-ws.c @@ -56,7 +56,7 @@ static int vncws_start_tls_handshake(VncState *vs) } VNC_DEBUG("Handshake done, switching to TLS data mode\n"); - qemu_set_fd_handler2(vs->csock, NULL, vncws_handshake_read, NULL, vs); + qemu_set_fd_handler(vs->csock, vncws_handshake_read, NULL, vs); return 0; } @@ -98,7 +98,7 @@ void vncws_handshake_read(void *opaque) handshake_end = (uint8_t *)g_strstr_len((char *)vs->ws_input.buffer, vs->ws_input.offset, WS_HANDSHAKE_END); if (handshake_end) { - qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); + qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs); vncws_process_handshake(vs, vs->ws_input.buffer, vs->ws_input.offset); buffer_advance(&vs->ws_input, handshake_end - vs->ws_input.buffer + strlen(WS_HANDSHAKE_END)); @@ -176,7 +176,7 @@ long vnc_client_write_ws(VncState *vs) buffer_advance(&vs->ws_output, ret); if (vs->ws_output.offset == 0) { - qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); + qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs); } return ret; @@ -1213,7 +1213,7 @@ static void vnc_disconnect_start(VncState *vs) if (vs->csock == -1) return; vnc_set_share_mode(vs, VNC_SHARE_MODE_DISCONNECTED); - qemu_set_fd_handler2(vs->csock, NULL, NULL, NULL, NULL); + qemu_set_fd_handler(vs->csock, NULL, NULL, NULL); closesocket(vs->csock); vs->csock = -1; } @@ -1387,7 +1387,7 @@ static long vnc_client_write_plain(VncState *vs) buffer_advance(&vs->output, ret); if (vs->output.offset == 0) { - qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); + qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs); } return ret; @@ -1434,7 +1434,7 @@ void vnc_client_write(void *opaque) ) { vnc_client_write_locked(opaque); } else if (vs->csock != -1) { - qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); + qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs); } vnc_unlock_output(vs); } @@ -1581,7 +1581,7 @@ void vnc_write(VncState *vs, const void *data, size_t len) buffer_reserve(&vs->output, len); if (vs->csock != -1 && buffer_empty(&vs->output)) { - qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, vnc_client_write, vs); + qemu_set_fd_handler(vs->csock, vnc_client_read, vnc_client_write, vs); } buffer_append(&vs->output, data, len); @@ -3022,18 +3022,16 @@ static void vnc_connect(VncDisplay *vd, int csock, vs->websocket = 1; #ifdef CONFIG_VNC_TLS if (vd->ws_tls) { - qemu_set_fd_handler2(vs->csock, NULL, vncws_tls_handshake_io, - NULL, vs); + qemu_set_fd_handler(vs->csock, vncws_tls_handshake_io, NULL, vs); } else #endif /* CONFIG_VNC_TLS */ { - qemu_set_fd_handler2(vs->csock, NULL, vncws_handshake_read, - NULL, vs); + qemu_set_fd_handler(vs->csock, vncws_handshake_read, NULL, vs); } } else #endif /* CONFIG_VNC_WS */ { - qemu_set_fd_handler2(vs->csock, NULL, vnc_client_read, NULL, vs); + qemu_set_fd_handler(vs->csock, vnc_client_read, NULL, vs); } vnc_client_cache_addr(vs); @@ -3182,14 +3180,14 @@ static void vnc_display_close(VncDisplay *vs) vs->enabled = false; vs->is_unix = false; if (vs->lsock != -1) { - qemu_set_fd_handler2(vs->lsock, NULL, NULL, NULL, NULL); + qemu_set_fd_handler(vs->lsock, NULL, NULL, NULL); close(vs->lsock); vs->lsock = -1; } #ifdef CONFIG_VNC_WS vs->ws_enabled = false; if (vs->lwebsock != -1) { - qemu_set_fd_handler2(vs->lwebsock, NULL, NULL, NULL, NULL); + qemu_set_fd_handler(vs->lwebsock, NULL, NULL, NULL); close(vs->lwebsock); vs->lwebsock = -1; } @@ -3707,12 +3705,11 @@ void vnc_display_open(const char *id, Error **errp) #endif /* CONFIG_VNC_WS */ } vs->enabled = true; - qemu_set_fd_handler2(vs->lsock, NULL, - vnc_listen_regular_read, NULL, vs); + qemu_set_fd_handler(vs->lsock, vnc_listen_regular_read, NULL, vs); #ifdef CONFIG_VNC_WS if (vs->ws_enabled) { - qemu_set_fd_handler2(vs->lwebsock, NULL, - vnc_listen_websocket_read, NULL, vs); + qemu_set_fd_handler(vs->lwebsock, vnc_listen_websocket_read, + NULL, vs); } #endif /* CONFIG_VNC_WS */ } diff --git a/util/event_notifier-posix.c b/util/event_notifier-posix.c index 8442c6e63c..ed4ca2b01e 100644 --- a/util/event_notifier-posix.c +++ b/util/event_notifier-posix.c @@ -85,7 +85,8 @@ int event_notifier_get_fd(EventNotifier *e) int event_notifier_set_handler(EventNotifier *e, EventNotifierHandler *handler) { - return qemu_set_fd_handler(e->rfd, (IOHandler *)handler, NULL, e); + qemu_set_fd_handler(e->rfd, (IOHandler *)handler, NULL, e); + return 0; } int event_notifier_set(EventNotifier *e) diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index f9ad34e40c..4026314435 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -244,7 +244,7 @@ static void wait_for_connect(void *opaque) bool in_progress; Error *err = NULL; - qemu_set_fd_handler2(s->fd, NULL, NULL, NULL, NULL); + qemu_set_fd_handler(s->fd, NULL, NULL, NULL); do { rc = qemu_getsockopt(s->fd, SOL_SOCKET, SO_ERROR, &val, &valsize); @@ -316,8 +316,7 @@ static int inet_connect_addr(struct addrinfo *addr, bool *in_progress, if (connect_state != NULL && QEMU_SOCKET_RC_INPROGRESS(rc)) { connect_state->fd = sock; - qemu_set_fd_handler2(sock, NULL, NULL, wait_for_connect, - connect_state); + qemu_set_fd_handler(sock, NULL, wait_for_connect, connect_state); *in_progress = true; } else if (rc < 0) { error_setg_errno(errp, errno, "Failed to connect socket"); @@ -796,8 +795,7 @@ int unix_connect_opts(QemuOpts *opts, Error **errp, if (connect_state != NULL && QEMU_SOCKET_RC_INPROGRESS(rc)) { connect_state->fd = sock; - qemu_set_fd_handler2(sock, NULL, NULL, wait_for_connect, - connect_state); + qemu_set_fd_handler(sock, NULL, wait_for_connect, connect_state); return sock; } else if (rc >= 0) { /* non blocking socket immediate success, call callback */ diff --git a/util/throttle.c b/util/throttle.c index f976ac7de5..706c13111e 100644 --- a/util/throttle.c +++ b/util/throttle.c @@ -1,10 +1,12 @@ /* * QEMU throttling infrastructure * - * Copyright (C) Nodalink, SARL. 2013 + * Copyright (C) Nodalink, EURL. 2013-2014 + * Copyright (C) Igalia, S.L. 2015 * - * Author: - * Benoît Canet <benoit.canet@irqsave.net> + * Authors: + * Benoît Canet <benoit.canet@nodalink.com> + * Alberto Garcia <berto@igalia.com> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -159,29 +161,36 @@ bool throttle_compute_timer(ThrottleState *ts, } /* Add timers to event loop */ -void throttle_attach_aio_context(ThrottleState *ts, AioContext *new_context) +void throttle_timers_attach_aio_context(ThrottleTimers *tt, + AioContext *new_context) { - ts->timers[0] = aio_timer_new(new_context, ts->clock_type, SCALE_NS, - ts->read_timer_cb, ts->timer_opaque); - ts->timers[1] = aio_timer_new(new_context, ts->clock_type, SCALE_NS, - ts->write_timer_cb, ts->timer_opaque); + tt->timers[0] = aio_timer_new(new_context, tt->clock_type, SCALE_NS, + tt->read_timer_cb, tt->timer_opaque); + tt->timers[1] = aio_timer_new(new_context, tt->clock_type, SCALE_NS, + tt->write_timer_cb, tt->timer_opaque); } /* To be called first on the ThrottleState */ -void throttle_init(ThrottleState *ts, - AioContext *aio_context, - QEMUClockType clock_type, - QEMUTimerCB *read_timer_cb, - QEMUTimerCB *write_timer_cb, - void *timer_opaque) +void throttle_init(ThrottleState *ts) { memset(ts, 0, sizeof(ThrottleState)); +} + +/* To be called first on the ThrottleTimers */ +void throttle_timers_init(ThrottleTimers *tt, + AioContext *aio_context, + QEMUClockType clock_type, + QEMUTimerCB *read_timer_cb, + QEMUTimerCB *write_timer_cb, + void *timer_opaque) +{ + memset(tt, 0, sizeof(ThrottleTimers)); - ts->clock_type = clock_type; - ts->read_timer_cb = read_timer_cb; - ts->write_timer_cb = write_timer_cb; - ts->timer_opaque = timer_opaque; - throttle_attach_aio_context(ts, aio_context); + tt->clock_type = clock_type; + tt->read_timer_cb = read_timer_cb; + tt->write_timer_cb = write_timer_cb; + tt->timer_opaque = timer_opaque; + throttle_timers_attach_aio_context(tt, aio_context); } /* destroy a timer */ @@ -195,25 +204,25 @@ static void throttle_timer_destroy(QEMUTimer **timer) } /* Remove timers from event loop */ -void throttle_detach_aio_context(ThrottleState *ts) +void throttle_timers_detach_aio_context(ThrottleTimers *tt) { int i; for (i = 0; i < 2; i++) { - throttle_timer_destroy(&ts->timers[i]); + throttle_timer_destroy(&tt->timers[i]); } } -/* To be called last on the ThrottleState */ -void throttle_destroy(ThrottleState *ts) +/* To be called last on the ThrottleTimers */ +void throttle_timers_destroy(ThrottleTimers *tt) { - throttle_detach_aio_context(ts); + throttle_timers_detach_aio_context(tt); } /* is any throttling timer configured */ -bool throttle_have_timer(ThrottleState *ts) +bool throttle_timers_are_initialized(ThrottleTimers *tt) { - if (ts->timers[0]) { + if (tt->timers[0]) { return true; } @@ -324,9 +333,12 @@ static void throttle_cancel_timer(QEMUTimer *timer) /* Used to configure the throttle * * @ts: the throttle state we are working on + * @tt: the throttle timers we use in this aio context * @cfg: the config to set */ -void throttle_config(ThrottleState *ts, ThrottleConfig *cfg) +void throttle_config(ThrottleState *ts, + ThrottleTimers *tt, + ThrottleConfig *cfg) { int i; @@ -336,10 +348,10 @@ void throttle_config(ThrottleState *ts, ThrottleConfig *cfg) throttle_fix_bucket(&ts->cfg.buckets[i]); } - ts->previous_leak = qemu_clock_get_ns(ts->clock_type); + ts->previous_leak = qemu_clock_get_ns(tt->clock_type); for (i = 0; i < 2; i++) { - throttle_cancel_timer(ts->timers[i]); + throttle_cancel_timer(tt->timers[i]); } } @@ -358,12 +370,15 @@ void throttle_get_config(ThrottleState *ts, ThrottleConfig *cfg) * * NOTE: this function is not unit tested due to it's usage of timer_mod * + * @tt: the timers structure * @is_write: the type of operation (read/write) * @ret: true if the timer has been scheduled else false */ -bool throttle_schedule_timer(ThrottleState *ts, bool is_write) +bool throttle_schedule_timer(ThrottleState *ts, + ThrottleTimers *tt, + bool is_write) { - int64_t now = qemu_clock_get_ns(ts->clock_type); + int64_t now = qemu_clock_get_ns(tt->clock_type); int64_t next_timestamp; bool must_wait; @@ -378,12 +393,12 @@ bool throttle_schedule_timer(ThrottleState *ts, bool is_write) } /* request throttled and timer pending -> do nothing */ - if (timer_pending(ts->timers[is_write])) { + if (timer_pending(tt->timers[is_write])) { return true; } /* request throttled and timer not pending -> arm timer */ - timer_mod(ts->timers[is_write], next_timestamp); + timer_mod(tt->timers[is_write], next_timestamp); return true; } |