dasd.c

来自「linux 内核源代码」· C语言 代码 · 共 2,303 行 · 第 1/4 页

C
2,303
字号
		}		/* Process finished ERP request. */		if (cqr->refers) {			__dasd_process_erp(device, cqr);			goto restart;		}		/* Rechain finished requests to final queue */		cqr->endclk = get_clock();		list_move_tail(&cqr->list, final_queue);	}}static voiddasd_end_request_cb(struct dasd_ccw_req * cqr, void *data){	struct request *req;	struct dasd_device *device;	int status;	req = (struct request *) data;	device = cqr->device;	dasd_profile_end(device, cqr, req);	status = cqr->device->discipline->free_cp(cqr,req);	spin_lock_irq(&device->request_queue_lock);	dasd_end_request(req, status);	spin_unlock_irq(&device->request_queue_lock);}/* * Fetch requests from the block device queue. */static void__dasd_process_blk_queue(struct dasd_device * device){	struct request_queue *queue;	struct request *req;	struct dasd_ccw_req *cqr;	int nr_queued;	queue = device->request_queue;	/* No queue ? Then there is nothing to do. */	if (queue == NULL)		return;	/*	 * We requeue request from the block device queue to the ccw	 * queue only in two states. In state DASD_STATE_READY the	 * partition detection is done and we need to requeue requests	 * for that. State DASD_STATE_ONLINE is normal block device	 * operation.	 */	if (device->state != DASD_STATE_READY &&	    device->state != DASD_STATE_ONLINE)		return;	nr_queued = 0;	/* Now we try to fetch requests from the request queue */	list_for_each_entry(cqr, &device->ccw_queue, list)		if (cqr->status == DASD_CQR_QUEUED)			nr_queued++;	while (!blk_queue_plugged(queue) &&	       elv_next_request(queue) &&		nr_queued < DASD_CHANQ_MAX_SIZE) {		req = elv_next_request(queue);		if (device->features & DASD_FEATURE_READONLY &&		    rq_data_dir(req) == WRITE) {			DBF_DEV_EVENT(DBF_ERR, device,				      "Rejecting write request %p",				      req);			blkdev_dequeue_request(req);			dasd_end_request(req, 0);			continue;		}		if (device->stopped & DASD_STOPPED_DC_EIO) {			blkdev_dequeue_request(req);			dasd_end_request(req, 0);			continue;		}		cqr = device->discipline->build_cp(device, req);		if (IS_ERR(cqr)) {			if (PTR_ERR(cqr) == -ENOMEM)				break;	/* terminate request queue loop */			if (PTR_ERR(cqr) == -EAGAIN) {				/*				 * The current request cannot be build right				 * now, we have to try later. If this request				 * is the head-of-queue we stop the device				 * for 1/2 second.				 */				if (!list_empty(&device->ccw_queue))					break;				device->stopped |= DASD_STOPPED_PENDING;				dasd_set_timer(device, HZ/2);				break;			}			DBF_DEV_EVENT(DBF_ERR, device,				      "CCW creation failed (rc=%ld) "				      "on request %p",				      PTR_ERR(cqr), req);			blkdev_dequeue_request(req);			dasd_end_request(req, 0);			continue;		}		cqr->callback = dasd_end_request_cb;		cqr->callback_data = (void *) req;		cqr->status = DASD_CQR_QUEUED;		blkdev_dequeue_request(req);		list_add_tail(&cqr->list, &device->ccw_queue);		dasd_profile_start(device, cqr, req);		nr_queued++;	}}/* * Take a look at the first request on the ccw queue and check * if it reached its expire time. If so, terminate the IO. */static void__dasd_check_expire(struct dasd_device * device){	struct dasd_ccw_req *cqr;	if (list_empty(&device->ccw_queue))		return;	cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, list);	if ((cqr->status == DASD_CQR_IN_IO && cqr->expires != 0) &&	    (time_after_eq(jiffies, cqr->expires + cqr->starttime))) {		if (device->discipline->term_IO(cqr) != 0) {			/* Hmpf, try again in 5 sec */			dasd_set_timer(device, 5*HZ);			DEV_MESSAGE(KERN_ERR, device,				    "internal error - timeout (%is) expired "				    "for cqr %p, termination failed, "				    "retrying in 5s",				    (cqr->expires/HZ), cqr);		} else {			DEV_MESSAGE(KERN_ERR, device,				    "internal error - timeout (%is) expired "				    "for cqr %p (%i retries left)",				    (cqr->expires/HZ), cqr, cqr->retries);		}	}}/* * Take a look at the first request on the ccw queue and check * if it needs to be started. */static void__dasd_start_head(struct dasd_device * device){	struct dasd_ccw_req *cqr;	int rc;	if (list_empty(&device->ccw_queue))		return;	cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, list);	if (cqr->status != DASD_CQR_QUEUED)		return;	/* Non-temporary stop condition will trigger fail fast */	if (device->stopped & ~DASD_STOPPED_PENDING &&	    test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) &&	    (!dasd_eer_enabled(device))) {		cqr->status = DASD_CQR_FAILED;		dasd_schedule_bh(device);		return;	}	/* Don't try to start requests if device is stopped */	if (device->stopped)		return;	rc = device->discipline->start_IO(cqr);	if (rc == 0)		dasd_set_timer(device, cqr->expires);	else if (rc == -EACCES) {		dasd_schedule_bh(device);	} else		/* Hmpf, try again in 1/2 sec */		dasd_set_timer(device, 50);}static inline int_wait_for_clear(struct dasd_ccw_req *cqr){	return (cqr->status == DASD_CQR_QUEUED);}/* * Remove all requests from the ccw queue (all = '1') or only block device * requests in case all = '0'. * Take care of the erp-chain (chained via cqr->refers) and remove either * the whole erp-chain or none of the erp-requests. * If a request is currently running, term_IO is called and the request * is re-queued. Prior to removing the terminated request we need to wait * for the clear-interrupt. * In case termination is not possible we stop processing and just finishing * the already moved requests. */static intdasd_flush_ccw_queue(struct dasd_device * device, int all){	struct dasd_ccw_req *cqr, *orig, *n;	int rc, i;	struct list_head flush_queue;	INIT_LIST_HEAD(&flush_queue);	spin_lock_irq(get_ccwdev_lock(device->cdev));	rc = 0;restart:	list_for_each_entry_safe(cqr, n, &device->ccw_queue, list) {		/* get original request of erp request-chain */		for (orig = cqr; orig->refers != NULL; orig = orig->refers);		/* Flush all request or only block device requests? */		if (all == 0 && cqr->callback != dasd_end_request_cb &&		    orig->callback != dasd_end_request_cb) {			continue;		}		/* Check status and move request to flush_queue */		switch (cqr->status) {		case DASD_CQR_IN_IO:			rc = device->discipline->term_IO(cqr);			if (rc) {				/* unable to terminate requeust */				DEV_MESSAGE(KERN_ERR, device,					    "dasd flush ccw_queue is unable "					    " to terminate request %p",					    cqr);				/* stop flush processing */				goto finished;			}			break;		case DASD_CQR_QUEUED:		case DASD_CQR_ERROR:			/* set request to FAILED */			cqr->stopclk = get_clock();			cqr->status = DASD_CQR_FAILED;			break;		default: /* do not touch the others */			break;		}		/* Rechain request (including erp chain) */		for (i = 0; cqr != NULL; cqr = cqr->refers, i++) {			cqr->endclk = get_clock();			list_move_tail(&cqr->list, &flush_queue);		}		if (i > 1)			/* moved more than one request - need to restart */			goto restart;	}finished:	spin_unlock_irq(get_ccwdev_lock(device->cdev));	/* Now call the callback function of flushed requests */restart_cb:	list_for_each_entry_safe(cqr, n, &flush_queue, list) {		if (cqr->status == DASD_CQR_CLEAR) {			/* wait for clear interrupt! */			wait_event(dasd_flush_wq, _wait_for_clear(cqr));			cqr->status = DASD_CQR_FAILED;		}		/* Process finished ERP request. */		if (cqr->refers) {			__dasd_process_erp(device, cqr);			/* restart list_for_xx loop since dasd_process_erp			 * might remove multiple elements */			goto restart_cb;		}		/* call the callback function */		cqr->endclk = get_clock();		if (cqr->callback != NULL)			(cqr->callback)(cqr, cqr->callback_data);	}	return rc;}/* * Acquire the device lock and process queues for the device. */static voiddasd_tasklet(struct dasd_device * device){	struct list_head final_queue;	struct list_head *l, *n;	struct dasd_ccw_req *cqr;	atomic_set (&device->tasklet_scheduled, 0);	INIT_LIST_HEAD(&final_queue);	spin_lock_irq(get_ccwdev_lock(device->cdev));	/* Check expire time of first request on the ccw queue. */	__dasd_check_expire(device);	/* Finish off requests on ccw queue */	__dasd_process_ccw_queue(device, &final_queue);	spin_unlock_irq(get_ccwdev_lock(device->cdev));	/* Now call the callback function of requests with final status */	list_for_each_safe(l, n, &final_queue) {		cqr = list_entry(l, struct dasd_ccw_req, list);		list_del_init(&cqr->list);		if (cqr->callback != NULL)			(cqr->callback)(cqr, cqr->callback_data);	}	spin_lock_irq(&device->request_queue_lock);	spin_lock(get_ccwdev_lock(device->cdev));	/* Get new request from the block device request queue */	__dasd_process_blk_queue(device);	/* Now check if the head of the ccw queue needs to be started. */	__dasd_start_head(device);	spin_unlock(get_ccwdev_lock(device->cdev));	spin_unlock_irq(&device->request_queue_lock);	dasd_put_device(device);}/* * Schedules a call to dasd_tasklet over the device tasklet. */voiddasd_schedule_bh(struct dasd_device * device){	/* Protect against rescheduling. */	if (atomic_cmpxchg (&device->tasklet_scheduled, 0, 1) != 0)		return;	dasd_get_device(device);	tasklet_hi_schedule(&device->tasklet);}/* * Queue a request to the head of the ccw_queue. Start the I/O if * possible. */voiddasd_add_request_head(struct dasd_ccw_req *req){	struct dasd_device *device;	unsigned long flags;	device = req->device;	spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);	req->status = DASD_CQR_QUEUED;	req->device = device;	list_add(&req->list, &device->ccw_queue);	/* let the bh start the request to keep them in order */	dasd_schedule_bh(device);	spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);}/* * Queue a request to the tail of the ccw_queue. Start the I/O if * possible. */voiddasd_add_request_tail(struct dasd_ccw_req *req){	struct dasd_device *device;	unsigned long flags;	device = req->device;	spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);	req->status = DASD_CQR_QUEUED;	req->device = device;	list_add_tail(&req->list, &device->ccw_queue);	/* let the bh start the request to keep them in order */	dasd_schedule_bh(device);	spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);}/* * Wakeup callback. */static voiddasd_wakeup_cb(struct dasd_ccw_req *cqr, void *data){	wake_up((wait_queue_head_t *) data);}static inline int_wait_for_wakeup(struct dasd_ccw_req *cqr){	struct dasd_device *device;	int rc;	device = cqr->device;	spin_lock_irq(get_ccwdev_lock(device->cdev));	rc = ((cqr->status == DASD_CQR_DONE ||	       cqr->status == DASD_CQR_FAILED) &&	      list_empty(&cqr->list));	spin_unlock_irq(get_ccwdev_lock(device->cdev));	return rc;}/* * Attempts to start a special ccw queue and waits for its completion. */intdasd_sleep_on(struct dasd_ccw_req * cqr){	wait_queue_head_t wait_q;	struct dasd_device *device;	int rc;	device = cqr->device;	spin_lock_irq(get_ccwdev_lock(device->cdev));	init_waitqueue_head (&wait_q);	cqr->callback = dasd_wakeup_cb;	cqr->callback_data = (void *) &wait_q;	cqr->status = DASD_CQR_QUEUED;	list_add_tail(&cqr->list, &device->ccw_queue);	/* let the bh start the request to keep them in order */	dasd_schedule_bh(device);	spin_unlock_irq(get_ccwdev_lock(device->cdev));	wait_event(wait_q, _wait_for_wakeup(cqr));	/* Request status is either done or failed. */	rc = (cqr->status == DASD_CQR_FAILED) ? -EIO : 0;	return rc;}/* * Attempts to start a special ccw queue and wait interruptible * for its completion. */intdasd_sleep_on_interruptible(struct dasd_ccw_req * cqr){	wait_queue_head_t wait_q;	struct dasd_device *device;	int rc, finished;	device = cqr->device;	spin_lock_irq(get_ccwdev_lock(device->cdev));	init_waitqueue_head (&wait_q);	cqr->callback = dasd_wakeup_cb;	cqr->callback_data = (void *) &wait_q;	cqr->status = DASD_CQR_QUEUED;	list_add_tail(&cqr->list, &device->ccw_queue);	/* let the bh start the request to keep them in order */	dasd_schedule_bh(device);	spin_unlock_irq(get_ccwdev_lock(device->cdev));	finished = 0;	while (!finished) {		rc = wait_event_interruptible(wait_q, _wait_for_wakeup(cqr));		if (rc != -ERESTARTSYS) {			/* Request is final (done or failed) */			rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO;			break;		}		spin_lock_irq(get_ccwdev_lock(device->cdev));		switch (cqr->status) {		case DASD_CQR_IN_IO:                        /* terminate runnig cqr */			if (device->discipline->term_IO) {				cqr->retries = -1;				device->discipline->term_IO(cqr);				/* wait (non-interruptible) for final status				 * because signal ist still pending */				spin_unlock_irq(get_ccwdev_lock(device->cdev));				wait_event(wait_q, _wait_for_wakeup(cqr));				spin_lock_irq(get_ccwdev_lock(device->cdev));				rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO;				finished = 1;			}			break;		case DASD_CQR_QUEUED:			/* request  */			list_del_init(&cqr->list);			rc = -EIO;			finished = 1;			break;		default:			/* cqr with 'non-interruptable' status - just wait */			break;		}		spin_unlock_irq(get_ccwdev_lock(device->cdev));	}	return rc;}/* * Whoa nelly now it gets really hairy. For some functions (e.g. steal lock * for eckd devices) the currently running request has to be terminated * and be put back to status queued, before the special request is added * to the head of the queue. Then the special request is waited on normally. */static inline int_dasd_term_running_cqr(struct dasd_device *device){	struct dasd_ccw_req *cqr;	if (list_empty(&device->ccw_queue))		return 0;	cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, list);	return device->discipline->term_IO(cqr);}intdasd_sleep_on_immediatly(struct dasd_ccw_req * cqr){	wait_queue_head_t wait_q;	struct dasd_device *device;	int rc;	device = cqr->device;	spin_lock_irq(get_ccwdev_lock(device->cdev));	rc = _dasd_term_running_cqr(device);	if (rc) {		spin_unlock_irq(get_ccwdev_lock(device->cdev));		return rc;	}	init_waitqueue_head (&wait_q);	cqr->callback = dasd_wakeup_cb;	cqr->callback_data = (void *) &wait_q;	cqr->status = DASD_CQR_QUEUED;	list_add(&cqr->list, &device->ccw_queue);	/* let the bh start the request to keep them in order */	dasd_schedule_bh(device);	spin_unlock_irq(get_ccwdev_lock(device->cdev));	wait_event(wait_q, _wait_for_wakeup(cqr));	/* Request status is either done or failed. */	rc = (cqr->status == DASD_CQR_FAILED) ? -EIO : 0;	return rc;}/* * Cancels a request that was started with dasd_sleep_on_req. * This is useful to timeout requests. The request will be * terminated if it is currently in i/o. * Returns 1 if the request has been terminated. */intdasd_cancel_req(struct dasd_ccw_req *cqr){	struct dasd_device *device = cqr->device;	unsigned long flags;	int rc;	rc = 0;	spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);	switch (cqr->status) {	case DASD_CQR_QUEUED:		/* request was not started - just set to failed */		cqr->status = DASD_CQR_FAILED;		break;	case DASD_CQR_IN_IO:		/* request in IO - terminate IO and release again */		if (device->discipline->term_IO(cqr) != 0)			/* what to do if unable to terminate ??????			   e.g. not _IN_IO */			cqr->status = DASD_CQR_FAILED;		cqr->stopclk = get_clock();		rc = 1;		break;	case DASD_CQR_DONE:	case DASD_CQR_FAILED:		/* already finished - do nothing */		break;	default:		DEV_MESSAGE(KERN_ALERT, device,			    "invalid status %02x in request",			    cqr->status);		BUG();

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?