⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dasd.c

📁 优龙2410linux2.6.8内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * File...........: linux/drivers/s390/block/dasd.c * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> *		    Horst Hummel <Horst.Hummel@de.ibm.com> *		    Carsten Otte <Cotte@de.ibm.com> *		    Martin Schwidefsky <schwidefsky@de.ibm.com> * Bugreports.to..: <Linux390@de.ibm.com> * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001 * * $Revision: 1.147 $ */#include <linux/config.h>#include <linux/kmod.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/ctype.h>#include <linux/major.h>#include <linux/slab.h>#include <linux/buffer_head.h>#include <asm/ccwdev.h>#include <asm/ebcdic.h>#include <asm/idals.h>#include <asm/todclk.h>/* This is ugly... */#define PRINTK_HEADER "dasd:"#include "dasd_int.h"/* * SECTION: Constant definitions to be used within this file */#define DASD_CHANQ_MAX_SIZE 4/* * SECTION: exported variables of dasd.c */debug_info_t *dasd_debug_area;struct dasd_discipline *dasd_diag_discipline_pointer;MODULE_AUTHOR("Holger Smolinski <Holger.Smolinski@de.ibm.com>");MODULE_DESCRIPTION("Linux on S/390 DASD device driver,"		   " Copyright 2000 IBM Corporation");MODULE_SUPPORTED_DEVICE("dasd");MODULE_PARM(dasd, "1-" __MODULE_STRING(256) "s");MODULE_LICENSE("GPL");/* * SECTION: prototypes for static functions of dasd.c */static int  dasd_alloc_queue(struct dasd_device * device);static void dasd_setup_queue(struct dasd_device * device);static void dasd_free_queue(struct dasd_device * device);static void dasd_flush_request_queue(struct dasd_device *);static void dasd_int_handler(struct ccw_device *, unsigned long, struct irb *);static void dasd_flush_ccw_queue(struct dasd_device *, int);static void dasd_tasklet(struct dasd_device *);static void do_kick_device(void *data);/* * SECTION: Operations on the device structure. */static wait_queue_head_t dasd_init_waitq;/* * Allocate memory for a new device structure. */struct dasd_device *dasd_alloc_device(void){	struct dasd_device *device;	device = kmalloc(sizeof (struct dasd_device), GFP_ATOMIC);	if (device == NULL)		return ERR_PTR(-ENOMEM);	memset(device, 0, sizeof (struct dasd_device));	/* open_count = 0 means device online but not in use */	atomic_set(&device->open_count, -1);	/* Get two pages for normal block device operations. */	device->ccw_mem = (void *) __get_free_pages(GFP_ATOMIC | GFP_DMA, 1);	if (device->ccw_mem == NULL) {		kfree(device);		return ERR_PTR(-ENOMEM);	}	/* Get one page for error recovery. */	device->erp_mem = (void *) get_zeroed_page(GFP_ATOMIC | GFP_DMA);	if (device->erp_mem == NULL) {		free_pages((unsigned long) device->ccw_mem, 1);		kfree(device);		return ERR_PTR(-ENOMEM);	}	dasd_init_chunklist(&device->ccw_chunks, device->ccw_mem, PAGE_SIZE*2);	dasd_init_chunklist(&device->erp_chunks, device->erp_mem, PAGE_SIZE);	spin_lock_init(&device->mem_lock);	spin_lock_init(&device->request_queue_lock);	atomic_set (&device->tasklet_scheduled, 0);	tasklet_init(&device->tasklet, 		     (void (*)(unsigned long)) dasd_tasklet,		     (unsigned long) device);	INIT_LIST_HEAD(&device->ccw_queue);	init_timer(&device->timer);	INIT_WORK(&device->kick_work, do_kick_device, device);	device->state = DASD_STATE_NEW;	device->target = DASD_STATE_NEW;	return device;}/* * Free memory of a device structure. */voiddasd_free_device(struct dasd_device *device){	if (device->private)		kfree(device->private);	free_page((unsigned long) device->erp_mem);	free_pages((unsigned long) device->ccw_mem, 1);	kfree(device);}/* * Make a new device known to the system. */static inline intdasd_state_new_to_known(struct dasd_device *device){	int rc;	/*	 * As long as the device is not in state DASD_STATE_NEW we want to 	 * keep the reference count > 0.	 */	dasd_get_device(device);	rc = dasd_alloc_queue(device);	if (rc) {		dasd_put_device(device);		return rc;	}	device->state = DASD_STATE_KNOWN;	return 0;}/* * Let the system forget about a device. */static inline voiddasd_state_known_to_new(struct dasd_device * device){	/* Forget the discipline information. */	device->discipline = NULL;	device->state = DASD_STATE_NEW;	dasd_free_queue(device);	/* Give up reference we took in dasd_state_new_to_known. */	dasd_put_device(device);}/* * Request the irq line for the device. */static inline intdasd_state_known_to_basic(struct dasd_device * device){	int rc;	/* Allocate and register gendisk structure. */	rc = dasd_gendisk_alloc(device);	if (rc)		return rc;	/* register 'device' debug area, used for all DBF_DEV_XXX calls */	device->debug_area = debug_register(device->cdev->dev.bus_id, 0, 2,					    8 * sizeof (long));	debug_register_view(device->debug_area, &debug_sprintf_view);	debug_set_level(device->debug_area, DBF_ERR);	DBF_DEV_EVENT(DBF_EMERG, device, "%s", "debug area created");	device->state = DASD_STATE_BASIC;	return 0;}/* * Release the irq line for the device. Terminate any running i/o. */static inline voiddasd_state_basic_to_known(struct dasd_device * device){	dasd_gendisk_free(device);	dasd_flush_ccw_queue(device, 1);	DBF_DEV_EVENT(DBF_EMERG, device, "%p debug area deleted", device);	if (device->debug_area != NULL) {		debug_unregister(device->debug_area);		device->debug_area = NULL;	}	device->state = DASD_STATE_KNOWN;}/* * Do the initial analysis. The do_analysis function may return * -EAGAIN in which case the device keeps the state DASD_STATE_BASIC * until the discipline decides to continue the startup sequence * by calling the function dasd_change_state. The eckd disciplines * uses this to start a ccw that detects the format. The completion * interrupt for this detection ccw uses the kernel event daemon to * trigger the call to dasd_change_state. All this is done in the * discipline code, see dasd_eckd.c. * After the analysis ccw is done (do_analysis returned 0 or error) * the block device is setup. Either a fake disk is added to allow * formatting or a proper device request queue is created. */static inline intdasd_state_basic_to_ready(struct dasd_device * device){	int rc;	rc = 0;	if (device->discipline->do_analysis != NULL)		rc = device->discipline->do_analysis(device);	if (rc)		return rc;	dasd_setup_queue(device);	device->state = DASD_STATE_READY;	if (dasd_scan_partitions(device) != 0)		device->state = DASD_STATE_BASIC;	return 0;}/* * Remove device from block device layer. Destroy dirty buffers. * Forget format information. Check if the target level is basic * and if it is create fake disk for formatting. */static inline voiddasd_state_ready_to_basic(struct dasd_device * device){	dasd_flush_ccw_queue(device, 0);	dasd_destroy_partitions(device);	dasd_flush_request_queue(device);	device->blocks = 0;	device->bp_block = 0;	device->s2b_shift = 0;	device->state = DASD_STATE_BASIC;}/* * Make the device online and schedule the bottom half to start * the requeueing of requests from the linux request queue to the * ccw queue. */static inline intdasd_state_ready_to_online(struct dasd_device * device){	device->state = DASD_STATE_ONLINE;	dasd_schedule_bh(device);	return 0;}/* * Stop the requeueing of requests again. */static inline voiddasd_state_online_to_ready(struct dasd_device * device){	device->state = DASD_STATE_READY;}/* * Device startup state changes. */static inline intdasd_increase_state(struct dasd_device *device){	int rc;	rc = 0;	if (device->state == DASD_STATE_NEW &&	    device->target >= DASD_STATE_KNOWN)		rc = dasd_state_new_to_known(device);	if (!rc &&	    device->state == DASD_STATE_KNOWN &&	    device->target >= DASD_STATE_BASIC)		rc = dasd_state_known_to_basic(device);	if (!rc &&	    device->state == DASD_STATE_BASIC &&	    device->target >= DASD_STATE_READY)		rc = dasd_state_basic_to_ready(device);	if (!rc &&	    device->state == DASD_STATE_READY &&	    device->target >= DASD_STATE_ONLINE)		rc = dasd_state_ready_to_online(device);	return rc;}/* * Device shutdown state changes. */static inline intdasd_decrease_state(struct dasd_device *device){	if (device->state == DASD_STATE_ONLINE &&	    device->target <= DASD_STATE_READY)		dasd_state_online_to_ready(device);		if (device->state == DASD_STATE_READY &&	    device->target <= DASD_STATE_BASIC)		dasd_state_ready_to_basic(device);		if (device->state == DASD_STATE_BASIC && 	    device->target <= DASD_STATE_KNOWN)		dasd_state_basic_to_known(device);		if (device->state == DASD_STATE_KNOWN &&	    device->target <= DASD_STATE_NEW)		dasd_state_known_to_new(device);	return 0;}/* * This is the main startup/shutdown routine. */static voiddasd_change_state(struct dasd_device *device){        int rc;	if (device->state == device->target)		/* Already where we want to go today... */		return;	if (device->state < device->target)		rc = dasd_increase_state(device);	else		rc = dasd_decrease_state(device);        if (rc && rc != -EAGAIN)                device->target = device->state;	if (device->state == device->target)		wake_up(&dasd_init_waitq);}/* * Kick starter for devices that did not complete the startup/shutdown * procedure or were sleeping because of a pending state. * dasd_kick_device will schedule a call do do_kick_device to the kernel * event daemon. */static voiddo_kick_device(void *data){	struct dasd_device *device;	device = (struct dasd_device *) data;	dasd_change_state(device);	dasd_schedule_bh(device);	dasd_put_device(device);}voiddasd_kick_device(struct dasd_device *device){	dasd_get_device(device);	/* queue call to dasd_kick_device to the kernel event daemon. */	schedule_work(&device->kick_work);}/* * Set the target state for a device and starts the state change. */voiddasd_set_target_state(struct dasd_device *device, int target){	/* If we are in probeonly mode stop at DASD_STATE_READY. */	if (dasd_probeonly && target > DASD_STATE_READY)		target = DASD_STATE_READY;	if (device->target != target) {                if (device->state == target)			wake_up(&dasd_init_waitq);		device->target = target;	}	if (device->state != device->target)		dasd_change_state(device);}/* * Enable devices with device numbers in [from..to]. */static inline int_wait_for_device(struct dasd_device *device){	return (device->state == device->target);}voiddasd_enable_device(struct dasd_device *device){	dasd_set_target_state(device, DASD_STATE_ONLINE);	if (device->state <= DASD_STATE_KNOWN)		/* No discipline for device found. */		dasd_set_target_state(device, DASD_STATE_NEW);	/* Now wait for the devices to come up. */	wait_event(dasd_init_waitq, _wait_for_device(device));}/* * SECTION: device operation (interrupt handler, start i/o, term i/o ...) */#ifdef CONFIG_DASD_PROFILEstruct dasd_profile_info_t dasd_global_profile;unsigned int dasd_profile_level = DASD_PROFILE_OFF;/* * Increments counter in global and local profiling structures. */#define dasd_profile_counter(value, counter, device) \{ \	int index; \	for (index = 0; index < 31 && value >> (2+index); index++); \	dasd_global_profile.counter[index]++; \	device->profile.counter[index]++; \}/* * Add profiling information for cqr before execution. */static inline voiddasd_profile_start(struct dasd_device *device, struct dasd_ccw_req * cqr,		   struct request *req){	struct list_head *l;	unsigned int counter;	if (dasd_profile_level != DASD_PROFILE_ON)		return;	/* count the length of the chanq for statistics */	counter = 0;	list_for_each(l, &device->ccw_queue)		if (++counter >= 31)			break;	dasd_global_profile.dasd_io_nr_req[counter]++;	device->profile.dasd_io_nr_req[counter]++;}/* * Add profiling information for cqr after execution. */static inline voiddasd_profile_end(struct dasd_device *device, struct dasd_ccw_req * cqr,		 struct request *req){	long strtime, irqtime, endtime, tottime;	/* in microseconds */	long tottimeps, sectors;	if (dasd_profile_level != DASD_PROFILE_ON)		return;	sectors = req->nr_sectors;	if (!cqr->buildclk || !cqr->startclk ||	    !cqr->stopclk || !cqr->endclk ||	    !sectors)		return;	strtime = ((cqr->startclk - cqr->buildclk) >> 12);	irqtime = ((cqr->stopclk - cqr->startclk) >> 12);	endtime = ((cqr->endclk - cqr->stopclk) >> 12);	tottime = ((cqr->endclk - cqr->buildclk) >> 12);	tottimeps = tottime / sectors;	if (!dasd_global_profile.dasd_io_reqs)		memset(&dasd_global_profile, 0,		       sizeof (struct dasd_profile_info_t));	dasd_global_profile.dasd_io_reqs++;	dasd_global_profile.dasd_io_sects += sectors;	if (!device->profile.dasd_io_reqs)		memset(&device->profile, 0,		       sizeof (struct dasd_profile_info_t));	device->profile.dasd_io_reqs++;	device->profile.dasd_io_sects += sectors;	dasd_profile_counter(sectors, dasd_io_secs, device);	dasd_profile_counter(tottime, dasd_io_times, device);	dasd_profile_counter(tottimeps, dasd_io_timps, device);	dasd_profile_counter(strtime, dasd_io_time1, device);	dasd_profile_counter(irqtime, dasd_io_time2, device);	dasd_profile_counter(irqtime / sectors, dasd_io_time2ps, device);	dasd_profile_counter(endtime, dasd_io_time3, device);}#else#define dasd_profile_start(device, cqr, req) do {} while (0)#define dasd_profile_end(device, cqr, req) do {} while (0)#endif				/* CONFIG_DASD_PROFILE *//* * Allocate memory for a channel program with 'cplength' channel * command words and 'datasize' additional space. There are two * variantes: 1) dasd_kmalloc_request uses kmalloc to get the needed * memory and 2) dasd_smalloc_request uses the static ccw memory * that gets allocated for each device. */struct dasd_ccw_req *dasd_kmalloc_request(char *magic, int cplength, int datasize,

⌨️ 快捷键说明

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