job: Move state transitions to Job
This moves BlockJob.status and the closely related functions (block_)job_state_transition() and (block_)job_apply_verb to Job. The two QAPI enums are renamed to JobStatus and JobVerb. Signed-off-by: Kevin Wolf <kwolf@redhat.com> Reviewed-by: Max Reitz <mreitz@redhat.com> Reviewed-by: John Snow <jsnow@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com>
This commit is contained in:
parent
e7c1d78bbd
commit
a50c2ab858
@ -6,8 +6,6 @@ bdrv_lock_medium(void *bs, bool locked) "bs %p locked %d"
|
|||||||
|
|
||||||
# blockjob.c
|
# blockjob.c
|
||||||
block_job_completed(void *job, int ret, int jret) "job %p ret %d corrected ret %d"
|
block_job_completed(void *job, int ret, int jret) "job %p ret %d corrected ret %d"
|
||||||
block_job_state_transition(void *job, int ret, const char *legal, const char *s0, const char *s1) "job %p (ret: %d) attempting %s transition (%s-->%s)"
|
|
||||||
block_job_apply_verb(void *job, const char *state, const char *verb, const char *legal) "job %p in state %s; applying verb %s (%s)"
|
|
||||||
|
|
||||||
# block/block-backend.c
|
# block/block-backend.c
|
||||||
blk_co_preadv(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags 0x%x"
|
blk_co_preadv(void *blk, void *bs, int64_t offset, unsigned int bytes, int flags) "blk %p bs %p offset %"PRId64" bytes %u flags 0x%x"
|
||||||
|
102
blockjob.c
102
blockjob.c
@ -41,61 +41,6 @@
|
|||||||
* block_job_enter. */
|
* block_job_enter. */
|
||||||
static QemuMutex block_job_mutex;
|
static QemuMutex block_job_mutex;
|
||||||
|
|
||||||
/* BlockJob State Transition Table */
|
|
||||||
bool BlockJobSTT[BLOCK_JOB_STATUS__MAX][BLOCK_JOB_STATUS__MAX] = {
|
|
||||||
/* U, C, R, P, Y, S, W, D, X, E, N */
|
|
||||||
/* U: */ [BLOCK_JOB_STATUS_UNDEFINED] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
||||||
/* C: */ [BLOCK_JOB_STATUS_CREATED] = {0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1},
|
|
||||||
/* R: */ [BLOCK_JOB_STATUS_RUNNING] = {0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0},
|
|
||||||
/* P: */ [BLOCK_JOB_STATUS_PAUSED] = {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
||||||
/* Y: */ [BLOCK_JOB_STATUS_READY] = {0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0},
|
|
||||||
/* S: */ [BLOCK_JOB_STATUS_STANDBY] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
|
|
||||||
/* W: */ [BLOCK_JOB_STATUS_WAITING] = {0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0},
|
|
||||||
/* D: */ [BLOCK_JOB_STATUS_PENDING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0},
|
|
||||||
/* X: */ [BLOCK_JOB_STATUS_ABORTING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0},
|
|
||||||
/* E: */ [BLOCK_JOB_STATUS_CONCLUDED] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
|
|
||||||
/* N: */ [BLOCK_JOB_STATUS_NULL] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
|
||||||
};
|
|
||||||
|
|
||||||
bool BlockJobVerbTable[BLOCK_JOB_VERB__MAX][BLOCK_JOB_STATUS__MAX] = {
|
|
||||||
/* U, C, R, P, Y, S, W, D, X, E, N */
|
|
||||||
[BLOCK_JOB_VERB_CANCEL] = {0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0},
|
|
||||||
[BLOCK_JOB_VERB_PAUSE] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
|
|
||||||
[BLOCK_JOB_VERB_RESUME] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
|
|
||||||
[BLOCK_JOB_VERB_SET_SPEED] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
|
|
||||||
[BLOCK_JOB_VERB_COMPLETE] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
|
|
||||||
[BLOCK_JOB_VERB_FINALIZE] = {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
|
|
||||||
[BLOCK_JOB_VERB_DISMISS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0},
|
|
||||||
};
|
|
||||||
|
|
||||||
static void block_job_state_transition(BlockJob *job, BlockJobStatus s1)
|
|
||||||
{
|
|
||||||
BlockJobStatus s0 = job->status;
|
|
||||||
assert(s1 >= 0 && s1 <= BLOCK_JOB_STATUS__MAX);
|
|
||||||
trace_block_job_state_transition(job, job->ret, BlockJobSTT[s0][s1] ?
|
|
||||||
"allowed" : "disallowed",
|
|
||||||
BlockJobStatus_str(s0),
|
|
||||||
BlockJobStatus_str(s1));
|
|
||||||
assert(BlockJobSTT[s0][s1]);
|
|
||||||
job->status = s1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int block_job_apply_verb(BlockJob *job, BlockJobVerb bv, Error **errp)
|
|
||||||
{
|
|
||||||
assert(bv >= 0 && bv <= BLOCK_JOB_VERB__MAX);
|
|
||||||
trace_block_job_apply_verb(job, BlockJobStatus_str(job->status),
|
|
||||||
BlockJobVerb_str(bv),
|
|
||||||
BlockJobVerbTable[bv][job->status] ?
|
|
||||||
"allowed" : "prohibited");
|
|
||||||
if (BlockJobVerbTable[bv][job->status]) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
error_setg(errp, "Job '%s' in state '%s' cannot accept command verb '%s'",
|
|
||||||
job->job.id, BlockJobStatus_str(job->status),
|
|
||||||
BlockJobVerb_str(bv));
|
|
||||||
return -EPERM;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void block_job_lock(void)
|
static void block_job_lock(void)
|
||||||
{
|
{
|
||||||
qemu_mutex_lock(&block_job_mutex);
|
qemu_mutex_lock(&block_job_mutex);
|
||||||
@ -257,7 +202,7 @@ static void block_job_detach_aio_context(void *opaque);
|
|||||||
void block_job_unref(BlockJob *job)
|
void block_job_unref(BlockJob *job)
|
||||||
{
|
{
|
||||||
if (--job->refcnt == 0) {
|
if (--job->refcnt == 0) {
|
||||||
assert(job->status == BLOCK_JOB_STATUS_NULL);
|
assert(job->job.status == JOB_STATUS_NULL);
|
||||||
assert(!job->txn);
|
assert(!job->txn);
|
||||||
BlockDriverState *bs = blk_bs(job->blk);
|
BlockDriverState *bs = blk_bs(job->blk);
|
||||||
bs->job = NULL;
|
bs->job = NULL;
|
||||||
@ -409,7 +354,7 @@ void block_job_start(BlockJob *job)
|
|||||||
job->pause_count--;
|
job->pause_count--;
|
||||||
job->busy = true;
|
job->busy = true;
|
||||||
job->paused = false;
|
job->paused = false;
|
||||||
block_job_state_transition(job, BLOCK_JOB_STATUS_RUNNING);
|
job_state_transition(&job->job, JOB_STATUS_RUNNING);
|
||||||
bdrv_coroutine_enter(blk_bs(job->blk), job->co);
|
bdrv_coroutine_enter(blk_bs(job->blk), job->co);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -421,7 +366,7 @@ static void block_job_decommission(BlockJob *job)
|
|||||||
job->paused = false;
|
job->paused = false;
|
||||||
job->deferred_to_main_loop = true;
|
job->deferred_to_main_loop = true;
|
||||||
block_job_txn_del_job(job);
|
block_job_txn_del_job(job);
|
||||||
block_job_state_transition(job, BLOCK_JOB_STATUS_NULL);
|
job_state_transition(&job->job, JOB_STATUS_NULL);
|
||||||
block_job_unref(job);
|
block_job_unref(job);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -432,7 +377,7 @@ static void block_job_do_dismiss(BlockJob *job)
|
|||||||
|
|
||||||
static void block_job_conclude(BlockJob *job)
|
static void block_job_conclude(BlockJob *job)
|
||||||
{
|
{
|
||||||
block_job_state_transition(job, BLOCK_JOB_STATUS_CONCLUDED);
|
job_state_transition(&job->job, JOB_STATUS_CONCLUDED);
|
||||||
if (job->auto_dismiss || !block_job_started(job)) {
|
if (job->auto_dismiss || !block_job_started(job)) {
|
||||||
block_job_do_dismiss(job);
|
block_job_do_dismiss(job);
|
||||||
}
|
}
|
||||||
@ -444,7 +389,7 @@ static void block_job_update_rc(BlockJob *job)
|
|||||||
job->ret = -ECANCELED;
|
job->ret = -ECANCELED;
|
||||||
}
|
}
|
||||||
if (job->ret) {
|
if (job->ret) {
|
||||||
block_job_state_transition(job, BLOCK_JOB_STATUS_ABORTING);
|
job_state_transition(&job->job, JOB_STATUS_ABORTING);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -652,7 +597,7 @@ static void block_job_completed_txn_success(BlockJob *job)
|
|||||||
BlockJobTxn *txn = job->txn;
|
BlockJobTxn *txn = job->txn;
|
||||||
BlockJob *other_job;
|
BlockJob *other_job;
|
||||||
|
|
||||||
block_job_state_transition(job, BLOCK_JOB_STATUS_WAITING);
|
job_state_transition(&job->job, JOB_STATUS_WAITING);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Successful completion, see if there are other running jobs in this
|
* Successful completion, see if there are other running jobs in this
|
||||||
@ -677,7 +622,7 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp)
|
|||||||
{
|
{
|
||||||
int64_t old_speed = job->speed;
|
int64_t old_speed = job->speed;
|
||||||
|
|
||||||
if (block_job_apply_verb(job, BLOCK_JOB_VERB_SET_SPEED, errp)) {
|
if (job_apply_verb(&job->job, JOB_VERB_SET_SPEED, errp)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (speed < 0) {
|
if (speed < 0) {
|
||||||
@ -709,7 +654,7 @@ void block_job_complete(BlockJob *job, Error **errp)
|
|||||||
{
|
{
|
||||||
/* Should not be reachable via external interface for internal jobs */
|
/* Should not be reachable via external interface for internal jobs */
|
||||||
assert(job->job.id);
|
assert(job->job.id);
|
||||||
if (block_job_apply_verb(job, BLOCK_JOB_VERB_COMPLETE, errp)) {
|
if (job_apply_verb(&job->job, JOB_VERB_COMPLETE, errp)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (job->pause_count || job->cancelled || !job->driver->complete) {
|
if (job->pause_count || job->cancelled || !job->driver->complete) {
|
||||||
@ -724,7 +669,7 @@ void block_job_complete(BlockJob *job, Error **errp)
|
|||||||
void block_job_finalize(BlockJob *job, Error **errp)
|
void block_job_finalize(BlockJob *job, Error **errp)
|
||||||
{
|
{
|
||||||
assert(job && job->job.id);
|
assert(job && job->job.id);
|
||||||
if (block_job_apply_verb(job, BLOCK_JOB_VERB_FINALIZE, errp)) {
|
if (job_apply_verb(&job->job, JOB_VERB_FINALIZE, errp)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
block_job_do_finalize(job);
|
block_job_do_finalize(job);
|
||||||
@ -735,7 +680,7 @@ void block_job_dismiss(BlockJob **jobptr, Error **errp)
|
|||||||
BlockJob *job = *jobptr;
|
BlockJob *job = *jobptr;
|
||||||
/* similarly to _complete, this is QMP-interface only. */
|
/* similarly to _complete, this is QMP-interface only. */
|
||||||
assert(job->job.id);
|
assert(job->job.id);
|
||||||
if (block_job_apply_verb(job, BLOCK_JOB_VERB_DISMISS, errp)) {
|
if (job_apply_verb(&job->job, JOB_VERB_DISMISS, errp)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -745,7 +690,7 @@ void block_job_dismiss(BlockJob **jobptr, Error **errp)
|
|||||||
|
|
||||||
void block_job_user_pause(BlockJob *job, Error **errp)
|
void block_job_user_pause(BlockJob *job, Error **errp)
|
||||||
{
|
{
|
||||||
if (block_job_apply_verb(job, BLOCK_JOB_VERB_PAUSE, errp)) {
|
if (job_apply_verb(&job->job, JOB_VERB_PAUSE, errp)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (job->user_paused) {
|
if (job->user_paused) {
|
||||||
@ -768,7 +713,7 @@ void block_job_user_resume(BlockJob *job, Error **errp)
|
|||||||
error_setg(errp, "Can't resume a job that was not paused");
|
error_setg(errp, "Can't resume a job that was not paused");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (block_job_apply_verb(job, BLOCK_JOB_VERB_RESUME, errp)) {
|
if (job_apply_verb(&job->job, JOB_VERB_RESUME, errp)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
block_job_iostatus_reset(job);
|
block_job_iostatus_reset(job);
|
||||||
@ -778,7 +723,7 @@ void block_job_user_resume(BlockJob *job, Error **errp)
|
|||||||
|
|
||||||
void block_job_cancel(BlockJob *job, bool force)
|
void block_job_cancel(BlockJob *job, bool force)
|
||||||
{
|
{
|
||||||
if (job->status == BLOCK_JOB_STATUS_CONCLUDED) {
|
if (job->job.status == JOB_STATUS_CONCLUDED) {
|
||||||
block_job_do_dismiss(job);
|
block_job_do_dismiss(job);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -794,7 +739,7 @@ void block_job_cancel(BlockJob *job, bool force)
|
|||||||
|
|
||||||
void block_job_user_cancel(BlockJob *job, bool force, Error **errp)
|
void block_job_user_cancel(BlockJob *job, bool force, Error **errp)
|
||||||
{
|
{
|
||||||
if (block_job_apply_verb(job, BLOCK_JOB_VERB_CANCEL, errp)) {
|
if (job_apply_verb(&job->job, JOB_VERB_CANCEL, errp)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
block_job_cancel(job, force);
|
block_job_cancel(job, force);
|
||||||
@ -859,7 +804,7 @@ BlockJobInfo *block_job_query(BlockJob *job, Error **errp)
|
|||||||
info->speed = job->speed;
|
info->speed = job->speed;
|
||||||
info->io_status = job->iostatus;
|
info->io_status = job->iostatus;
|
||||||
info->ready = job->ready;
|
info->ready = job->ready;
|
||||||
info->status = job->status;
|
info->status = job->job.status;
|
||||||
info->auto_finalize = job->auto_finalize;
|
info->auto_finalize = job->auto_finalize;
|
||||||
info->auto_dismiss = job->auto_dismiss;
|
info->auto_dismiss = job->auto_dismiss;
|
||||||
info->has_error = job->ret != 0;
|
info->has_error = job->ret != 0;
|
||||||
@ -907,7 +852,7 @@ static void block_job_event_completed(BlockJob *job, const char *msg)
|
|||||||
|
|
||||||
static int block_job_event_pending(BlockJob *job)
|
static int block_job_event_pending(BlockJob *job)
|
||||||
{
|
{
|
||||||
block_job_state_transition(job, BLOCK_JOB_STATUS_PENDING);
|
job_state_transition(&job->job, JOB_STATUS_PENDING);
|
||||||
if (!job->auto_finalize && !block_job_is_internal(job)) {
|
if (!job->auto_finalize && !block_job_is_internal(job)) {
|
||||||
qapi_event_send_block_job_pending(job_type(&job->job),
|
qapi_event_send_block_job_pending(job_type(&job->job),
|
||||||
job->job.id,
|
job->job.id,
|
||||||
@ -975,7 +920,6 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
|
|||||||
job->refcnt = 1;
|
job->refcnt = 1;
|
||||||
job->auto_finalize = !(flags & BLOCK_JOB_MANUAL_FINALIZE);
|
job->auto_finalize = !(flags & BLOCK_JOB_MANUAL_FINALIZE);
|
||||||
job->auto_dismiss = !(flags & BLOCK_JOB_MANUAL_DISMISS);
|
job->auto_dismiss = !(flags & BLOCK_JOB_MANUAL_DISMISS);
|
||||||
block_job_state_transition(job, BLOCK_JOB_STATUS_CREATED);
|
|
||||||
aio_timer_init(qemu_get_aio_context(), &job->sleep_timer,
|
aio_timer_init(qemu_get_aio_context(), &job->sleep_timer,
|
||||||
QEMU_CLOCK_REALTIME, SCALE_NS,
|
QEMU_CLOCK_REALTIME, SCALE_NS,
|
||||||
block_job_sleep_timer_cb, job);
|
block_job_sleep_timer_cb, job);
|
||||||
@ -1017,7 +961,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver,
|
|||||||
|
|
||||||
void block_job_early_fail(BlockJob *job)
|
void block_job_early_fail(BlockJob *job)
|
||||||
{
|
{
|
||||||
assert(job->status == BLOCK_JOB_STATUS_CREATED);
|
assert(job->job.status == JOB_STATUS_CREATED);
|
||||||
block_job_decommission(job);
|
block_job_decommission(job);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1077,14 +1021,14 @@ void coroutine_fn block_job_pause_point(BlockJob *job)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (block_job_should_pause(job) && !block_job_is_cancelled(job)) {
|
if (block_job_should_pause(job) && !block_job_is_cancelled(job)) {
|
||||||
BlockJobStatus status = job->status;
|
JobStatus status = job->job.status;
|
||||||
block_job_state_transition(job, status == BLOCK_JOB_STATUS_READY ? \
|
job_state_transition(&job->job, status == JOB_STATUS_READY
|
||||||
BLOCK_JOB_STATUS_STANDBY : \
|
? JOB_STATUS_STANDBY
|
||||||
BLOCK_JOB_STATUS_PAUSED);
|
: JOB_STATUS_PAUSED);
|
||||||
job->paused = true;
|
job->paused = true;
|
||||||
block_job_do_yield(job, -1);
|
block_job_do_yield(job, -1);
|
||||||
job->paused = false;
|
job->paused = false;
|
||||||
block_job_state_transition(job, status);
|
job_state_transition(&job->job, status);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (job->driver->resume) {
|
if (job->driver->resume) {
|
||||||
@ -1176,7 +1120,7 @@ void block_job_iostatus_reset(BlockJob *job)
|
|||||||
|
|
||||||
void block_job_event_ready(BlockJob *job)
|
void block_job_event_ready(BlockJob *job)
|
||||||
{
|
{
|
||||||
block_job_state_transition(job, BLOCK_JOB_STATUS_READY);
|
job_state_transition(&job->job, JOB_STATUS_READY);
|
||||||
job->ready = true;
|
job->ready = true;
|
||||||
|
|
||||||
if (block_job_is_internal(job)) {
|
if (block_job_is_internal(job)) {
|
||||||
|
@ -147,9 +147,6 @@ typedef struct BlockJob {
|
|||||||
*/
|
*/
|
||||||
QEMUTimer sleep_timer;
|
QEMUTimer sleep_timer;
|
||||||
|
|
||||||
/** Current state; See @BlockJobStatus for details. */
|
|
||||||
BlockJobStatus status;
|
|
||||||
|
|
||||||
/** True if this job should automatically finalize itself */
|
/** True if this job should automatically finalize itself */
|
||||||
bool auto_finalize;
|
bool auto_finalize;
|
||||||
|
|
||||||
|
@ -41,6 +41,9 @@ typedef struct Job {
|
|||||||
/** The type of this job. */
|
/** The type of this job. */
|
||||||
const JobDriver *driver;
|
const JobDriver *driver;
|
||||||
|
|
||||||
|
/** Current state; See @JobStatus for details. */
|
||||||
|
JobStatus status;
|
||||||
|
|
||||||
/** Element of the list of jobs */
|
/** Element of the list of jobs */
|
||||||
QLIST_ENTRY(Job) job_list;
|
QLIST_ENTRY(Job) job_list;
|
||||||
} Job;
|
} Job;
|
||||||
@ -90,4 +93,14 @@ Job *job_next(Job *job);
|
|||||||
*/
|
*/
|
||||||
Job *job_get(const char *id);
|
Job *job_get(const char *id);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether the verb @verb can be applied to @job in its current state.
|
||||||
|
* Returns 0 if the verb can be applied; otherwise errp is set and -EPERM
|
||||||
|
* returned.
|
||||||
|
*/
|
||||||
|
int job_apply_verb(Job *job, JobVerb verb, Error **errp);
|
||||||
|
|
||||||
|
/* TODO To be removed from the public interface */
|
||||||
|
void job_state_transition(Job *job, JobStatus s1);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
56
job.c
56
job.c
@ -28,9 +28,63 @@
|
|||||||
#include "qapi/error.h"
|
#include "qapi/error.h"
|
||||||
#include "qemu/job.h"
|
#include "qemu/job.h"
|
||||||
#include "qemu/id.h"
|
#include "qemu/id.h"
|
||||||
|
#include "trace-root.h"
|
||||||
|
|
||||||
static QLIST_HEAD(, Job) jobs = QLIST_HEAD_INITIALIZER(jobs);
|
static QLIST_HEAD(, Job) jobs = QLIST_HEAD_INITIALIZER(jobs);
|
||||||
|
|
||||||
|
/* Job State Transition Table */
|
||||||
|
bool JobSTT[JOB_STATUS__MAX][JOB_STATUS__MAX] = {
|
||||||
|
/* U, C, R, P, Y, S, W, D, X, E, N */
|
||||||
|
/* U: */ [JOB_STATUS_UNDEFINED] = {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
/* C: */ [JOB_STATUS_CREATED] = {0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1},
|
||||||
|
/* R: */ [JOB_STATUS_RUNNING] = {0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0},
|
||||||
|
/* P: */ [JOB_STATUS_PAUSED] = {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
/* Y: */ [JOB_STATUS_READY] = {0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0},
|
||||||
|
/* S: */ [JOB_STATUS_STANDBY] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
|
||||||
|
/* W: */ [JOB_STATUS_WAITING] = {0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0},
|
||||||
|
/* D: */ [JOB_STATUS_PENDING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0},
|
||||||
|
/* X: */ [JOB_STATUS_ABORTING] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0},
|
||||||
|
/* E: */ [JOB_STATUS_CONCLUDED] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1},
|
||||||
|
/* N: */ [JOB_STATUS_NULL] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
|
||||||
|
};
|
||||||
|
|
||||||
|
bool JobVerbTable[JOB_VERB__MAX][JOB_STATUS__MAX] = {
|
||||||
|
/* U, C, R, P, Y, S, W, D, X, E, N */
|
||||||
|
[JOB_VERB_CANCEL] = {0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0},
|
||||||
|
[JOB_VERB_PAUSE] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
|
||||||
|
[JOB_VERB_RESUME] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
|
||||||
|
[JOB_VERB_SET_SPEED] = {0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0},
|
||||||
|
[JOB_VERB_COMPLETE] = {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0},
|
||||||
|
[JOB_VERB_FINALIZE] = {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0},
|
||||||
|
[JOB_VERB_DISMISS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0},
|
||||||
|
};
|
||||||
|
|
||||||
|
/* TODO Make static once the whole state machine is in job.c */
|
||||||
|
void job_state_transition(Job *job, JobStatus s1)
|
||||||
|
{
|
||||||
|
JobStatus s0 = job->status;
|
||||||
|
assert(s1 >= 0 && s1 <= JOB_STATUS__MAX);
|
||||||
|
trace_job_state_transition(job, /* TODO re-enable: job->ret */ 0,
|
||||||
|
JobSTT[s0][s1] ? "allowed" : "disallowed",
|
||||||
|
JobStatus_str(s0), JobStatus_str(s1));
|
||||||
|
assert(JobSTT[s0][s1]);
|
||||||
|
job->status = s1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int job_apply_verb(Job *job, JobVerb verb, Error **errp)
|
||||||
|
{
|
||||||
|
JobStatus s0 = job->status;
|
||||||
|
assert(verb >= 0 && verb <= JOB_VERB__MAX);
|
||||||
|
trace_job_apply_verb(job, JobStatus_str(s0), JobVerb_str(verb),
|
||||||
|
JobVerbTable[verb][s0] ? "allowed" : "prohibited");
|
||||||
|
if (JobVerbTable[verb][s0]) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
error_setg(errp, "Job '%s' in state '%s' cannot accept command verb '%s'",
|
||||||
|
job->id, JobStatus_str(s0), JobVerb_str(verb));
|
||||||
|
return -EPERM;
|
||||||
|
}
|
||||||
|
|
||||||
JobType job_type(const Job *job)
|
JobType job_type(const Job *job)
|
||||||
{
|
{
|
||||||
return job->driver->job_type;
|
return job->driver->job_type;
|
||||||
@ -81,6 +135,8 @@ void *job_create(const char *job_id, const JobDriver *driver, Error **errp)
|
|||||||
job->driver = driver;
|
job->driver = driver;
|
||||||
job->id = g_strdup(job_id);
|
job->id = g_strdup(job_id);
|
||||||
|
|
||||||
|
job_state_transition(job, JOB_STATUS_CREATED);
|
||||||
|
|
||||||
QLIST_INSERT_HEAD(&jobs, job, job_list);
|
QLIST_INSERT_HEAD(&jobs, job, job_list);
|
||||||
|
|
||||||
return job;
|
return job;
|
||||||
|
@ -1068,9 +1068,9 @@
|
|||||||
'data': ['commit', 'stream', 'mirror', 'backup'] }
|
'data': ['commit', 'stream', 'mirror', 'backup'] }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @BlockJobVerb:
|
# @JobVerb:
|
||||||
#
|
#
|
||||||
# Represents command verbs that can be applied to a blockjob.
|
# Represents command verbs that can be applied to a job.
|
||||||
#
|
#
|
||||||
# @cancel: see @block-job-cancel
|
# @cancel: see @block-job-cancel
|
||||||
#
|
#
|
||||||
@ -1088,14 +1088,14 @@
|
|||||||
#
|
#
|
||||||
# Since: 2.12
|
# Since: 2.12
|
||||||
##
|
##
|
||||||
{ 'enum': 'BlockJobVerb',
|
{ 'enum': 'JobVerb',
|
||||||
'data': ['cancel', 'pause', 'resume', 'set-speed', 'complete', 'dismiss',
|
'data': ['cancel', 'pause', 'resume', 'set-speed', 'complete', 'dismiss',
|
||||||
'finalize' ] }
|
'finalize' ] }
|
||||||
|
|
||||||
##
|
##
|
||||||
# @BlockJobStatus:
|
# @JobStatus:
|
||||||
#
|
#
|
||||||
# Indicates the present state of a given blockjob in its lifetime.
|
# Indicates the present state of a given job in its lifetime.
|
||||||
#
|
#
|
||||||
# @undefined: Erroneous, default state. Should not ever be visible.
|
# @undefined: Erroneous, default state. Should not ever be visible.
|
||||||
#
|
#
|
||||||
@ -1134,7 +1134,7 @@
|
|||||||
#
|
#
|
||||||
# Since: 2.12
|
# Since: 2.12
|
||||||
##
|
##
|
||||||
{ 'enum': 'BlockJobStatus',
|
{ 'enum': 'JobStatus',
|
||||||
'data': ['undefined', 'created', 'running', 'paused', 'ready', 'standby',
|
'data': ['undefined', 'created', 'running', 'paused', 'ready', 'standby',
|
||||||
'waiting', 'pending', 'aborting', 'concluded', 'null' ] }
|
'waiting', 'pending', 'aborting', 'concluded', 'null' ] }
|
||||||
|
|
||||||
@ -1184,7 +1184,7 @@
|
|||||||
'data': {'type': 'str', 'device': 'str', 'len': 'int',
|
'data': {'type': 'str', 'device': 'str', 'len': 'int',
|
||||||
'offset': 'int', 'busy': 'bool', 'paused': 'bool', 'speed': 'int',
|
'offset': 'int', 'busy': 'bool', 'paused': 'bool', 'speed': 'int',
|
||||||
'io-status': 'BlockDeviceIoStatus', 'ready': 'bool',
|
'io-status': 'BlockDeviceIoStatus', 'ready': 'bool',
|
||||||
'status': 'BlockJobStatus',
|
'status': 'JobStatus',
|
||||||
'auto-finalize': 'bool', 'auto-dismiss': 'bool',
|
'auto-finalize': 'bool', 'auto-dismiss': 'bool',
|
||||||
'*error': 'str' } }
|
'*error': 'str' } }
|
||||||
|
|
||||||
@ -2416,7 +2416,7 @@
|
|||||||
# QEMU 2.12+ job lifetime management semantics.
|
# QEMU 2.12+ job lifetime management semantics.
|
||||||
#
|
#
|
||||||
# This command will refuse to operate on any job that has not yet reached
|
# This command will refuse to operate on any job that has not yet reached
|
||||||
# its terminal state, BLOCK_JOB_STATUS_CONCLUDED. For jobs that make use of
|
# its terminal state, JOB_STATUS_CONCLUDED. For jobs that make use of the
|
||||||
# BLOCK_JOB_READY event, block-job-cancel or block-job-complete will still need
|
# BLOCK_JOB_READY event, block-job-cancel or block-job-complete will still need
|
||||||
# to be used as appropriate.
|
# to be used as appropriate.
|
||||||
#
|
#
|
||||||
|
@ -211,7 +211,7 @@ static CancelJob *create_common(BlockJob **pjob)
|
|||||||
job = mk_job(blk, "Steve", &test_cancel_driver, true,
|
job = mk_job(blk, "Steve", &test_cancel_driver, true,
|
||||||
BLOCK_JOB_MANUAL_FINALIZE | BLOCK_JOB_MANUAL_DISMISS);
|
BLOCK_JOB_MANUAL_FINALIZE | BLOCK_JOB_MANUAL_DISMISS);
|
||||||
block_job_ref(job);
|
block_job_ref(job);
|
||||||
assert(job->status == BLOCK_JOB_STATUS_CREATED);
|
assert(job->job.status == JOB_STATUS_CREATED);
|
||||||
s = container_of(job, CancelJob, common);
|
s = container_of(job, CancelJob, common);
|
||||||
s->blk = blk;
|
s->blk = blk;
|
||||||
|
|
||||||
@ -223,15 +223,14 @@ static void cancel_common(CancelJob *s)
|
|||||||
{
|
{
|
||||||
BlockJob *job = &s->common;
|
BlockJob *job = &s->common;
|
||||||
BlockBackend *blk = s->blk;
|
BlockBackend *blk = s->blk;
|
||||||
BlockJobStatus sts = job->status;
|
JobStatus sts = job->job.status;
|
||||||
|
|
||||||
block_job_cancel_sync(job);
|
block_job_cancel_sync(job);
|
||||||
if ((sts != BLOCK_JOB_STATUS_CREATED) &&
|
if (sts != JOB_STATUS_CREATED && sts != JOB_STATUS_CONCLUDED) {
|
||||||
(sts != BLOCK_JOB_STATUS_CONCLUDED)) {
|
|
||||||
BlockJob *dummy = job;
|
BlockJob *dummy = job;
|
||||||
block_job_dismiss(&dummy, &error_abort);
|
block_job_dismiss(&dummy, &error_abort);
|
||||||
}
|
}
|
||||||
assert(job->status == BLOCK_JOB_STATUS_NULL);
|
assert(job->job.status == JOB_STATUS_NULL);
|
||||||
block_job_unref(job);
|
block_job_unref(job);
|
||||||
destroy_blk(blk);
|
destroy_blk(blk);
|
||||||
}
|
}
|
||||||
@ -253,7 +252,7 @@ static void test_cancel_running(void)
|
|||||||
s = create_common(&job);
|
s = create_common(&job);
|
||||||
|
|
||||||
block_job_start(job);
|
block_job_start(job);
|
||||||
assert(job->status == BLOCK_JOB_STATUS_RUNNING);
|
assert(job->job.status == JOB_STATUS_RUNNING);
|
||||||
|
|
||||||
cancel_common(s);
|
cancel_common(s);
|
||||||
}
|
}
|
||||||
@ -266,11 +265,11 @@ static void test_cancel_paused(void)
|
|||||||
s = create_common(&job);
|
s = create_common(&job);
|
||||||
|
|
||||||
block_job_start(job);
|
block_job_start(job);
|
||||||
assert(job->status == BLOCK_JOB_STATUS_RUNNING);
|
assert(job->job.status == JOB_STATUS_RUNNING);
|
||||||
|
|
||||||
block_job_user_pause(job, &error_abort);
|
block_job_user_pause(job, &error_abort);
|
||||||
block_job_enter(job);
|
block_job_enter(job);
|
||||||
assert(job->status == BLOCK_JOB_STATUS_PAUSED);
|
assert(job->job.status == JOB_STATUS_PAUSED);
|
||||||
|
|
||||||
cancel_common(s);
|
cancel_common(s);
|
||||||
}
|
}
|
||||||
@ -283,11 +282,11 @@ static void test_cancel_ready(void)
|
|||||||
s = create_common(&job);
|
s = create_common(&job);
|
||||||
|
|
||||||
block_job_start(job);
|
block_job_start(job);
|
||||||
assert(job->status == BLOCK_JOB_STATUS_RUNNING);
|
assert(job->job.status == JOB_STATUS_RUNNING);
|
||||||
|
|
||||||
s->should_converge = true;
|
s->should_converge = true;
|
||||||
block_job_enter(job);
|
block_job_enter(job);
|
||||||
assert(job->status == BLOCK_JOB_STATUS_READY);
|
assert(job->job.status == JOB_STATUS_READY);
|
||||||
|
|
||||||
cancel_common(s);
|
cancel_common(s);
|
||||||
}
|
}
|
||||||
@ -300,15 +299,15 @@ static void test_cancel_standby(void)
|
|||||||
s = create_common(&job);
|
s = create_common(&job);
|
||||||
|
|
||||||
block_job_start(job);
|
block_job_start(job);
|
||||||
assert(job->status == BLOCK_JOB_STATUS_RUNNING);
|
assert(job->job.status == JOB_STATUS_RUNNING);
|
||||||
|
|
||||||
s->should_converge = true;
|
s->should_converge = true;
|
||||||
block_job_enter(job);
|
block_job_enter(job);
|
||||||
assert(job->status == BLOCK_JOB_STATUS_READY);
|
assert(job->job.status == JOB_STATUS_READY);
|
||||||
|
|
||||||
block_job_user_pause(job, &error_abort);
|
block_job_user_pause(job, &error_abort);
|
||||||
block_job_enter(job);
|
block_job_enter(job);
|
||||||
assert(job->status == BLOCK_JOB_STATUS_STANDBY);
|
assert(job->job.status == JOB_STATUS_STANDBY);
|
||||||
|
|
||||||
cancel_common(s);
|
cancel_common(s);
|
||||||
}
|
}
|
||||||
@ -321,18 +320,18 @@ static void test_cancel_pending(void)
|
|||||||
s = create_common(&job);
|
s = create_common(&job);
|
||||||
|
|
||||||
block_job_start(job);
|
block_job_start(job);
|
||||||
assert(job->status == BLOCK_JOB_STATUS_RUNNING);
|
assert(job->job.status == JOB_STATUS_RUNNING);
|
||||||
|
|
||||||
s->should_converge = true;
|
s->should_converge = true;
|
||||||
block_job_enter(job);
|
block_job_enter(job);
|
||||||
assert(job->status == BLOCK_JOB_STATUS_READY);
|
assert(job->job.status == JOB_STATUS_READY);
|
||||||
|
|
||||||
block_job_complete(job, &error_abort);
|
block_job_complete(job, &error_abort);
|
||||||
block_job_enter(job);
|
block_job_enter(job);
|
||||||
while (!s->completed) {
|
while (!s->completed) {
|
||||||
aio_poll(qemu_get_aio_context(), true);
|
aio_poll(qemu_get_aio_context(), true);
|
||||||
}
|
}
|
||||||
assert(job->status == BLOCK_JOB_STATUS_PENDING);
|
assert(job->job.status == JOB_STATUS_PENDING);
|
||||||
|
|
||||||
cancel_common(s);
|
cancel_common(s);
|
||||||
}
|
}
|
||||||
@ -345,21 +344,21 @@ static void test_cancel_concluded(void)
|
|||||||
s = create_common(&job);
|
s = create_common(&job);
|
||||||
|
|
||||||
block_job_start(job);
|
block_job_start(job);
|
||||||
assert(job->status == BLOCK_JOB_STATUS_RUNNING);
|
assert(job->job.status == JOB_STATUS_RUNNING);
|
||||||
|
|
||||||
s->should_converge = true;
|
s->should_converge = true;
|
||||||
block_job_enter(job);
|
block_job_enter(job);
|
||||||
assert(job->status == BLOCK_JOB_STATUS_READY);
|
assert(job->job.status == JOB_STATUS_READY);
|
||||||
|
|
||||||
block_job_complete(job, &error_abort);
|
block_job_complete(job, &error_abort);
|
||||||
block_job_enter(job);
|
block_job_enter(job);
|
||||||
while (!s->completed) {
|
while (!s->completed) {
|
||||||
aio_poll(qemu_get_aio_context(), true);
|
aio_poll(qemu_get_aio_context(), true);
|
||||||
}
|
}
|
||||||
assert(job->status == BLOCK_JOB_STATUS_PENDING);
|
assert(job->job.status == JOB_STATUS_PENDING);
|
||||||
|
|
||||||
block_job_finalize(job, &error_abort);
|
block_job_finalize(job, &error_abort);
|
||||||
assert(job->status == BLOCK_JOB_STATUS_CONCLUDED);
|
assert(job->job.status == JOB_STATUS_CONCLUDED);
|
||||||
|
|
||||||
cancel_common(s);
|
cancel_common(s);
|
||||||
}
|
}
|
||||||
|
@ -104,6 +104,10 @@ gdbstub_err_invalid_rle(void) "got invalid RLE sequence"
|
|||||||
gdbstub_err_checksum_invalid(uint8_t ch) "got invalid command checksum digit: 0x%02x"
|
gdbstub_err_checksum_invalid(uint8_t ch) "got invalid command checksum digit: 0x%02x"
|
||||||
gdbstub_err_checksum_incorrect(uint8_t expected, uint8_t got) "got command packet with incorrect checksum, expected=0x%02x, received=0x%02x"
|
gdbstub_err_checksum_incorrect(uint8_t expected, uint8_t got) "got command packet with incorrect checksum, expected=0x%02x, received=0x%02x"
|
||||||
|
|
||||||
|
# job.c
|
||||||
|
job_state_transition(void *job, int ret, const char *legal, const char *s0, const char *s1) "job %p (ret: %d) attempting %s transition (%s-->%s)"
|
||||||
|
job_apply_verb(void *job, const char *state, const char *verb, const char *legal) "job %p in state %s; applying verb %s (%s)"
|
||||||
|
|
||||||
### Guest events, keep at bottom
|
### Guest events, keep at bottom
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user