[SCSI] fix wrong context bugs in SCSI
There's a bug in releasing scsi_device where the release function actually frees the block queue. However, the block queue release calls flush_work(), which requires process context (the scsi_device structure may release from irq context). Update the release function to invoke via the execute_in_process_context() API. Also clean up the scsi_target structure releasing via this API. Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
This commit is contained in:
		
							parent
							
								
									faead26d7a
								
							
						
					
					
						commit
						65110b2168
					
				| @ -387,19 +387,12 @@ static struct scsi_target *scsi_alloc_target(struct device *parent, | ||||
| 	return found_target; | ||||
| } | ||||
| 
 | ||||
| struct work_queue_wrapper { | ||||
| 	struct work_struct	work; | ||||
| 	struct scsi_target	*starget; | ||||
| }; | ||||
| 
 | ||||
| static void scsi_target_reap_work(void *data) { | ||||
| 	struct work_queue_wrapper *wqw = (struct work_queue_wrapper *)data; | ||||
| 	struct scsi_target *starget = wqw->starget; | ||||
| static void scsi_target_reap_usercontext(void *data) | ||||
| { | ||||
| 	struct scsi_target *starget = data; | ||||
| 	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); | ||||
| 	unsigned long flags; | ||||
| 
 | ||||
| 	kfree(wqw); | ||||
| 
 | ||||
| 	spin_lock_irqsave(shost->host_lock, flags); | ||||
| 
 | ||||
| 	if (--starget->reap_ref == 0 && list_empty(&starget->devices)) { | ||||
| @ -428,18 +421,7 @@ static void scsi_target_reap_work(void *data) { | ||||
|  */ | ||||
| void scsi_target_reap(struct scsi_target *starget) | ||||
| { | ||||
| 	struct work_queue_wrapper *wqw =  | ||||
| 		kzalloc(sizeof(struct work_queue_wrapper), GFP_ATOMIC); | ||||
| 
 | ||||
| 	if (!wqw) { | ||||
| 		starget_printk(KERN_ERR, starget, | ||||
| 			       "Failed to allocate memory in scsi_reap_target()\n"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	INIT_WORK(&wqw->work, scsi_target_reap_work, wqw); | ||||
| 	wqw->starget = starget; | ||||
| 	schedule_work(&wqw->work); | ||||
| 	scsi_execute_in_process_context(scsi_target_reap_usercontext, starget); | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  | ||||
| @ -217,8 +217,9 @@ static void scsi_device_cls_release(struct class_device *class_dev) | ||||
| 	put_device(&sdev->sdev_gendev); | ||||
| } | ||||
| 
 | ||||
| static void scsi_device_dev_release(struct device *dev) | ||||
| static void scsi_device_dev_release_usercontext(void *data) | ||||
| { | ||||
| 	struct device *dev = data; | ||||
| 	struct scsi_device *sdev; | ||||
| 	struct device *parent; | ||||
| 	struct scsi_target *starget; | ||||
| @ -237,6 +238,7 @@ static void scsi_device_dev_release(struct device *dev) | ||||
| 
 | ||||
| 	if (sdev->request_queue) { | ||||
| 		sdev->request_queue->queuedata = NULL; | ||||
| 		/* user context needed to free queue */ | ||||
| 		scsi_free_queue(sdev->request_queue); | ||||
| 		/* temporary expedient, try to catch use of queue lock
 | ||||
| 		 * after free of sdev */ | ||||
| @ -252,6 +254,11 @@ static void scsi_device_dev_release(struct device *dev) | ||||
| 		put_device(parent); | ||||
| } | ||||
| 
 | ||||
| static void scsi_device_dev_release(struct device *dev) | ||||
| { | ||||
| 	scsi_execute_in_process_context(scsi_device_dev_release_usercontext,	dev); | ||||
| } | ||||
| 
 | ||||
| static struct class sdev_class = { | ||||
| 	.name		= "scsi_device", | ||||
| 	.release	= scsi_device_cls_release, | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user