tape_core.c

来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,236 行 · 第 1/3 页

C
1,236
字号
		case TS_INIT:		case TS_NOT_OPER:			spin_unlock_irq(get_ccwdev_lock(device->cdev));			break;		case TS_UNUSED:			tape_state_set(device, TS_INIT);			spin_unlock_irq(get_ccwdev_lock(device->cdev));			tape_cleanup_device(device);			break;		default:			DBF_EVENT(3, "(%08x): Set offline failed "				"- drive in use.\n",				device->cdev_id);			PRINT_WARN("(%s): Set offline failed "				"- drive in use.\n",				device->cdev->dev.bus_id);			spin_unlock_irq(get_ccwdev_lock(device->cdev));			return -EBUSY;	}	DBF_LH(3, "(%08x): Drive set offline.\n", device->cdev_id);	return 0;}/* * Allocate memory for a new device structure. */static struct tape_device *tape_alloc_device(void){	struct tape_device *device;	device = (struct tape_device *)		kmalloc(sizeof(struct tape_device), GFP_KERNEL);	if (device == NULL) {		DBF_EXCEPTION(2, "ti:no mem\n");		PRINT_INFO ("can't allocate memory for "			    "tape info structure\n");		return ERR_PTR(-ENOMEM);	}	memset(device, 0, sizeof(struct tape_device));	device->modeset_byte = (char *) kmalloc(1, GFP_KERNEL | GFP_DMA);	if (device->modeset_byte == NULL) {		DBF_EXCEPTION(2, "ti:no mem\n");		PRINT_INFO("can't allocate memory for modeset byte\n");		kfree(device);		return ERR_PTR(-ENOMEM);	}	INIT_LIST_HEAD(&device->req_queue);	INIT_LIST_HEAD(&device->node);	init_waitqueue_head(&device->state_change_wq);	device->tape_state = TS_INIT;	device->medium_state = MS_UNKNOWN;	*device->modeset_byte = 0;	device->first_minor = -1;	atomic_set(&device->ref_count, 1);	return device;}/* * Get a reference to an existing device structure. This will automatically * increment the reference count. */struct tape_device *tape_get_device_reference(struct tape_device *device){	DBF_EVENT(4, "tape_get_device_reference(%p) = %i\n", device,		atomic_inc_return(&device->ref_count));	return device;}/* * Decrease the reference counter of a devices structure. If the * reference counter reaches zero free the device structure. * The function returns a NULL pointer to be used by the caller * for clearing reference pointers. */struct tape_device *tape_put_device(struct tape_device *device){	int remain;	remain = atomic_dec_return(&device->ref_count);	if (remain > 0) {		DBF_EVENT(4, "tape_put_device(%p) -> %i\n", device, remain);	} else {		if (remain < 0) {			DBF_EVENT(4, "put device without reference\n");			PRINT_ERR("put device without reference\n");		} else {			DBF_EVENT(4, "tape_free_device(%p)\n", device);			kfree(device->modeset_byte);			kfree(device);		}	}	return NULL;			}/* * Find tape device by a device index. */struct tape_device *tape_get_device(int devindex){	struct tape_device *device, *tmp;	device = ERR_PTR(-ENODEV);	read_lock(&tape_device_lock);	list_for_each_entry(tmp, &tape_device_list, node) {		if (tmp->first_minor / TAPE_MINORS_PER_DEV == devindex) {			device = tape_get_device_reference(tmp);			break;		}	}	read_unlock(&tape_device_lock);	return device;}/* * Driverfs tape probe function. */inttape_generic_probe(struct ccw_device *cdev){	struct tape_device *device;	device = tape_alloc_device();	if (IS_ERR(device))		return -ENODEV;	PRINT_INFO("tape device %s found\n", cdev->dev.bus_id);	cdev->dev.driver_data = device;	device->cdev = cdev;	device->cdev_id = busid_to_int(cdev->dev.bus_id);	cdev->handler = __tape_do_irq;	ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP);	sysfs_create_group(&cdev->dev.kobj, &tape_attr_group);	return 0;}static inline void__tape_discard_requests(struct tape_device *device){	struct tape_request *	request;	struct list_head *	l, *n;	list_for_each_safe(l, n, &device->req_queue) {		request = list_entry(l, struct tape_request, list);		if (request->status == TAPE_REQUEST_IN_IO)			request->status = TAPE_REQUEST_DONE;		list_del(&request->list);		/* Decrease ref_count for removed request. */		request->device = tape_put_device(device);		request->rc = -EIO;		if (request->callback != NULL)			request->callback(request, request->callback_data);	}}/* * Driverfs tape remove function. * * This function is called whenever the common I/O layer detects the device * gone. This can happen at any time and we cannot refuse. */voidtape_generic_remove(struct ccw_device *cdev){	struct tape_device *	device;	device = cdev->dev.driver_data;	if (!device) {		PRINT_ERR("No device pointer in tape_generic_remove!\n");		return;	}	DBF_LH(3, "(%08x): tape_generic_remove(%p)\n", device->cdev_id, cdev);	spin_lock_irq(get_ccwdev_lock(device->cdev));	switch (device->tape_state) {		case TS_INIT:			tape_state_set(device, TS_NOT_OPER);		case TS_NOT_OPER:			/*			 * Nothing to do.			 */			spin_unlock_irq(get_ccwdev_lock(device->cdev));			break;		case TS_UNUSED:			/*			 * Need only to release the device.			 */			tape_state_set(device, TS_NOT_OPER);			spin_unlock_irq(get_ccwdev_lock(device->cdev));			tape_cleanup_device(device);			break;		default:			/*			 * There may be requests on the queue. We will not get			 * an interrupt for a request that was running. So we			 * just post them all as I/O errors.			 */			DBF_EVENT(3, "(%08x): Drive in use vanished!\n",				device->cdev_id);			PRINT_WARN("(%s): Drive in use vanished - "				"expect trouble!\n",				device->cdev->dev.bus_id);			PRINT_WARN("State was %i\n", device->tape_state);			tape_state_set(device, TS_NOT_OPER);			__tape_discard_requests(device);			spin_unlock_irq(get_ccwdev_lock(device->cdev));			tape_cleanup_device(device);	}	if (cdev->dev.driver_data != NULL) {		sysfs_remove_group(&cdev->dev.kobj, &tape_attr_group);		cdev->dev.driver_data = tape_put_device(cdev->dev.driver_data);	}}/* * Allocate a new tape ccw request */struct tape_request *tape_alloc_request(int cplength, int datasize){	struct tape_request *request;	if (datasize > PAGE_SIZE || (cplength*sizeof(struct ccw1)) > PAGE_SIZE)		BUG();	DBF_LH(6, "tape_alloc_request(%d, %d)\n", cplength, datasize);	request = (struct tape_request *) kmalloc(sizeof(struct tape_request),						  GFP_KERNEL);	if (request == NULL) {		DBF_EXCEPTION(1, "cqra nomem\n");		return ERR_PTR(-ENOMEM);	}	memset(request, 0, sizeof(struct tape_request));	/* allocate channel program */	if (cplength > 0) {		request->cpaddr = kmalloc(cplength*sizeof(struct ccw1),					  GFP_ATOMIC | GFP_DMA);		if (request->cpaddr == NULL) {			DBF_EXCEPTION(1, "cqra nomem\n");			kfree(request);			return ERR_PTR(-ENOMEM);		}		memset(request->cpaddr, 0, cplength*sizeof(struct ccw1));	}	/* alloc small kernel buffer */	if (datasize > 0) {		request->cpdata = kmalloc(datasize, GFP_KERNEL | GFP_DMA);		if (request->cpdata == NULL) {			DBF_EXCEPTION(1, "cqra nomem\n");			if (request->cpaddr != NULL)				kfree(request->cpaddr);			kfree(request);			return ERR_PTR(-ENOMEM);		}		memset(request->cpdata, 0, datasize);	}	DBF_LH(6, "New request %p(%p/%p)\n", request, request->cpaddr,		request->cpdata);	return request;}/* * Free tape ccw request */voidtape_free_request (struct tape_request * request){	DBF_LH(6, "Free request %p\n", request);	if (request->device != NULL) {		request->device = tape_put_device(request->device);	}	if (request->cpdata != NULL)		kfree(request->cpdata);	if (request->cpaddr != NULL)		kfree(request->cpaddr);	kfree(request);}static inline void__tape_do_io_list(struct tape_device *device){	struct list_head *l, *n;	struct tape_request *request;	int rc;	DBF_LH(6, "__tape_do_io_list(%p)\n", device);	/*	 * Try to start each request on request queue until one is	 * started successful.	 */	list_for_each_safe(l, n, &device->req_queue) {		request = list_entry(l, struct tape_request, list);#ifdef CONFIG_S390_TAPE_BLOCK		if (request->op == TO_BLOCK)			device->discipline->check_locate(device, request);#endif		rc = ccw_device_start(device->cdev, request->cpaddr,				      (unsigned long) request, 0x00,				      request->options);		if (rc == 0) {			request->status = TAPE_REQUEST_IN_IO;			break;		}		/* Start failed. Remove request and indicate failure. */		DBF_EVENT(1, "tape: DOIO failed with er = %i\n", rc);		/* Set ending status and do callback. */		request->rc = rc;		request->status = TAPE_REQUEST_DONE;		__tape_remove_request(device, request);	}}static void__tape_remove_request(struct tape_device *device, struct tape_request *request){	/* Remove from request queue. */	list_del(&request->list);	/* Do callback. */	if (request->callback != NULL)		request->callback(request, request->callback_data);	/* Start next request. */	if (!list_empty(&device->req_queue))		__tape_do_io_list(device);}/* * Write sense data to console/dbf */voidtape_dump_sense(struct tape_device* device, struct tape_request *request,		struct irb *irb){	unsigned int *sptr;	PRINT_INFO("-------------------------------------------------\n");	PRINT_INFO("DSTAT : %02x  CSTAT: %02x	CPA: %04x\n",		   irb->scsw.dstat, irb->scsw.cstat, irb->scsw.cpa);	PRINT_INFO("DEVICE: %s\n", device->cdev->dev.bus_id);	if (request != NULL)		PRINT_INFO("OP	  : %s\n", tape_op_verbose[request->op]);	sptr = (unsigned int *) irb->ecw;	PRINT_INFO("Sense data: %08X %08X %08X %08X \n",		   sptr[0], sptr[1], sptr[2], sptr[3]);	PRINT_INFO("Sense data: %08X %08X %08X %08X \n",		   sptr[4], sptr[5], sptr[6], sptr[7]);	PRINT_INFO("--------------------------------------------------\n");}/* * Write sense data to dbf */voidtape_dump_sense_dbf(struct tape_device *device, struct tape_request *request,		    struct irb *irb){	unsigned int *sptr;	const char* op;	if (request != NULL)		op = tape_op_verbose[request->op];	else		op = "---";	DBF_EVENT(3, "DSTAT : %02x   CSTAT: %02x\n",		  irb->scsw.dstat,irb->scsw.cstat);	DBF_EVENT(3, "DEVICE: %08x OP\t: %s\n", device->cdev_id, op);	sptr = (unsigned int *) irb->ecw;	DBF_EVENT(3, "%08x %08x\n", sptr[0], sptr[1]);	DBF_EVENT(3, "%08x %08x\n", sptr[2], sptr[3]);	DBF_EVENT(3, "%08x %08x\n", sptr[4], sptr[5]);	DBF_EVENT(3, "%08x %08x\n", sptr[6], sptr[7]);}/* * I/O helper function. Adds the request to the request queue * and starts it if the tape is idle. Has to be called with * the device lock held. */static inline int__tape_do_io(struct tape_device *device, struct tape_request *request){	int rc;	switch (request->op) {		case TO_MSEN:		case TO_ASSIGN:		case TO_UNASSIGN:		case TO_READ_ATTMSG:			if (device->tape_state == TS_INIT)				break;			if (device->tape_state == TS_UNUSED)				break;		default:			if (device->tape_state == TS_BLKUSE)				break;			if (device->tape_state != TS_IN_USE)

⌨️ 快捷键说明

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