blockjob: Allow nested pause
This patch changes block_job_pause to increase the pause counter and block_job_resume to decrease it. The counter will allow calling block_job_pause/block_job_resume unconditionally on a job when we need to suspend the IO temporarily. From now on, each block_job_resume must be paired with a block_job_pause to keep the counter balanced. The user pause from QMP or HMP will only trigger block_job_pause once until it's resumed, this is achieved by adding a user_paused flag in BlockJob. One occurrence of block_job_resume in mirror_complete is replaced with block_job_enter which does what is necessary. In block_job_cancel, the cancel flag is good enough to instruct coroutines to quit loop, so use block_job_enter to replace the unpaired block_job_resume. Upon block job IO error, user is notified about the entering to the pause state, so this pause belongs to user pause, set the flag accordingly and expect a matching QMP resume. [Extended doc comments as suggested by Paolo Bonzini <pbonzini@redhat.com>. --Stefan] Signed-off-by: Fam Zheng <famz@redhat.com> Reviewed-by: Stefan Hajnoczi <stefanha@redhat.com> Reviewed-by: Paolo Bonzini <pbonzini@redhat.com> Reviewed-by: Alberto Garcia <berto@igalia.com> Message-id: 1428069921-2957-2-git-send-email-famz@redhat.com Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com> Signed-off-by: Kevin Wolf <kwolf@redhat.com>
This commit is contained in:
		
							parent
							
								
									199667a8c8
								
							
						
					
					
						commit
						751ebd76e6
					
				| @ -634,7 +634,7 @@ static void mirror_complete(BlockJob *job, Error **errp) | ||||
|     } | ||||
| 
 | ||||
|     s->should_complete = true; | ||||
|     block_job_resume(job); | ||||
|     block_job_enter(&s->common); | ||||
| } | ||||
| 
 | ||||
| static const BlockJobDriver mirror_job_driver = { | ||||
|  | ||||
| @ -2699,7 +2699,7 @@ void qmp_block_job_cancel(const char *device, | ||||
|         force = false; | ||||
|     } | ||||
| 
 | ||||
|     if (job->paused && !force) { | ||||
|     if (job->user_paused && !force) { | ||||
|         error_setg(errp, "The block job for device '%s' is currently paused", | ||||
|                    device); | ||||
|         goto out; | ||||
| @ -2716,10 +2716,11 @@ void qmp_block_job_pause(const char *device, Error **errp) | ||||
|     AioContext *aio_context; | ||||
|     BlockJob *job = find_block_job(device, &aio_context, errp); | ||||
| 
 | ||||
|     if (!job) { | ||||
|     if (!job || job->user_paused) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     job->user_paused = true; | ||||
|     trace_qmp_block_job_pause(job); | ||||
|     block_job_pause(job); | ||||
|     aio_context_release(aio_context); | ||||
| @ -2730,10 +2731,11 @@ void qmp_block_job_resume(const char *device, Error **errp) | ||||
|     AioContext *aio_context; | ||||
|     BlockJob *job = find_block_job(device, &aio_context, errp); | ||||
| 
 | ||||
|     if (!job) { | ||||
|     if (!job || !job->user_paused) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     job->user_paused = false; | ||||
|     trace_qmp_block_job_resume(job); | ||||
|     block_job_resume(job); | ||||
|     aio_context_release(aio_context); | ||||
|  | ||||
							
								
								
									
										23
									
								
								blockjob.c
									
									
									
									
									
								
							
							
						
						
									
										23
									
								
								blockjob.c
									
									
									
									
									
								
							| @ -107,7 +107,7 @@ void block_job_set_speed(BlockJob *job, int64_t speed, Error **errp) | ||||
| 
 | ||||
| void block_job_complete(BlockJob *job, Error **errp) | ||||
| { | ||||
|     if (job->paused || job->cancelled || !job->driver->complete) { | ||||
|     if (job->pause_count || job->cancelled || !job->driver->complete) { | ||||
|         error_set(errp, QERR_BLOCK_JOB_NOT_READY, | ||||
|                   bdrv_get_device_name(job->bs)); | ||||
|         return; | ||||
| @ -118,17 +118,26 @@ void block_job_complete(BlockJob *job, Error **errp) | ||||
| 
 | ||||
| void block_job_pause(BlockJob *job) | ||||
| { | ||||
|     job->paused = true; | ||||
|     job->pause_count++; | ||||
| } | ||||
| 
 | ||||
| bool block_job_is_paused(BlockJob *job) | ||||
| { | ||||
|     return job->paused; | ||||
|     return job->pause_count > 0; | ||||
| } | ||||
| 
 | ||||
| void block_job_resume(BlockJob *job) | ||||
| { | ||||
|     job->paused = false; | ||||
|     assert(job->pause_count > 0); | ||||
|     job->pause_count--; | ||||
|     if (job->pause_count) { | ||||
|         return; | ||||
|     } | ||||
|     block_job_enter(job); | ||||
| } | ||||
| 
 | ||||
| void block_job_enter(BlockJob *job) | ||||
| { | ||||
|     block_job_iostatus_reset(job); | ||||
|     if (job->co && !job->busy) { | ||||
|         qemu_coroutine_enter(job->co, NULL); | ||||
| @ -138,7 +147,7 @@ void block_job_resume(BlockJob *job) | ||||
| void block_job_cancel(BlockJob *job) | ||||
| { | ||||
|     job->cancelled = true; | ||||
|     block_job_resume(job); | ||||
|     block_job_enter(job); | ||||
| } | ||||
| 
 | ||||
| bool block_job_is_cancelled(BlockJob *job) | ||||
| @ -258,7 +267,7 @@ BlockJobInfo *block_job_query(BlockJob *job) | ||||
|     info->device    = g_strdup(bdrv_get_device_name(job->bs)); | ||||
|     info->len       = job->len; | ||||
|     info->busy      = job->busy; | ||||
|     info->paused    = job->paused; | ||||
|     info->paused    = job->pause_count > 0; | ||||
|     info->offset    = job->offset; | ||||
|     info->speed     = job->speed; | ||||
|     info->io_status = job->iostatus; | ||||
| @ -335,6 +344,8 @@ BlockErrorAction block_job_error_action(BlockJob *job, BlockDriverState *bs, | ||||
|                                     IO_OPERATION_TYPE_WRITE, | ||||
|                                     action, &error_abort); | ||||
|     if (action == BLOCK_ERROR_ACTION_STOP) { | ||||
|         /* make the pause user visible, which will be resumed from QMP. */ | ||||
|         job->user_paused = true; | ||||
|         block_job_pause(job); | ||||
|         block_job_iostatus_set_err(job, error); | ||||
|         if (bs != job->bs) { | ||||
|  | ||||
| @ -79,10 +79,16 @@ struct BlockJob { | ||||
|     bool cancelled; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Set to true if the job is either paused, or will pause itself | ||||
|      * as soon as possible (if busy == true). | ||||
|      * Counter for pause request. If non-zero, the block job is either paused, | ||||
|      * or if busy == true will pause itself as soon as possible. | ||||
|      */ | ||||
|     bool paused; | ||||
|     int pause_count; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Set to true if the job is paused by user.  Can be unpaused with the | ||||
|      * block-job-resume QMP command. | ||||
|      */ | ||||
|     bool user_paused; | ||||
| 
 | ||||
|     /**
 | ||||
|      * Set to false by the job while it is in a quiescent state, where | ||||
| @ -225,10 +231,18 @@ void block_job_pause(BlockJob *job); | ||||
|  * block_job_resume: | ||||
|  * @job: The job to be resumed. | ||||
|  * | ||||
|  * Resume the specified job. | ||||
|  * Resume the specified job.  Must be paired with a preceding block_job_pause. | ||||
|  */ | ||||
| void block_job_resume(BlockJob *job); | ||||
| 
 | ||||
| /**
 | ||||
|  * block_job_enter: | ||||
|  * @job: The job to enter. | ||||
|  * | ||||
|  * Continue the specified job by entering the coroutine. | ||||
|  */ | ||||
| void block_job_enter(BlockJob *job); | ||||
| 
 | ||||
| /**
 | ||||
|  * block_job_event_cancelled: | ||||
|  * @job: The job whose information is requested. | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Fam Zheng
						Fam Zheng