📄 dasd.c
字号:
/* * File...........: linux/drivers/s390/block/dasd.c * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com> * : Utz Bacher <utz.bacher@de.ibm.com> * Bugreports.to..: <Linux390@de.ibm.com> * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000 */#include <linux/config.h>#include <linux/init.h>#include <linux/stddef.h>#include <linux/kernel.h>#ifdef MODULE#include <linux/module.h>#endif /* MODULE */#include <linux/tqueue.h>#include <linux/timer.h>#include <linux/malloc.h>#include <linux/genhd.h>#include <linux/devfs_fs_kernel.h>#include <linux/hdreg.h>#include <linux/interrupt.h>#include <linux/ctype.h>#include <asm/io.h>#include <asm/semaphore.h>#include <asm/ebcdic.h>#include <asm/uaccess.h>#include <asm/irq.h>#include <linux/dasd.h>#include <linux/blk.h>#include "dasd_erp.h"#include "dasd_types.h"#include "dasd_ccwstuff.h"#define PRINTK_HEADER DASD_NAME":"#define CCW_READ_DEVICE_CHARACTERISTICS 0x64#define DASD_SSCH_RETRIES 2/* This macro is a little tricky, but makes the code more easy to read... */#define MATCH(info,ct,cm,dt,dm) ( \(( info -> sid_data.cu_type ct ) && ( info -> sid_data.cu_model cm )) && \(( info -> sid_data.dev_type dt ) && ( info -> sid_data.dev_model dm )) )/* Prototypes for the functions called from external */static int dasd_ioctl (struct inode *, struct file *, unsigned int, unsigned long);static int dasd_open (struct inode *, struct file *);static int dasd_release (struct inode *, struct file *);void dasd_debug (unsigned long tag);void dasd_profile_add (cqr_t *cqr);void dasd_proc_init (void);static int dasd_format( int, format_data_t * );static struct block_device_operations dasd_device_operations;spinlock_t dasd_lock; /* general purpose lock for the dasd driver *//* All asynchronous I/O should waint on this wait_queue */wait_queue_head_t dasd_waitq;static int dasd_autodetect = 1;static int dasd_devno[DASD_MAX_DEVICES] ={0,};static int dasd_count = 0;extern dasd_chanq_t *cq_head;static intdasd_get_hexdigit (char c){ if ((c >= '0') && (c <= '9')) return c - '0'; if ((c >= 'a') && (c <= 'f')) return c + 10 - 'a'; if ((c >= 'A') && (c <= 'F')) return c + 10 - 'A'; return -1;}/* sets the string pointer after the next comma */static voiddasd_scan_for_next_comma (char **strptr){ while (((**strptr) != ',') && ((**strptr)++)) (*strptr)++; /* set the position AFTER the comma */ if (**strptr == ',') (*strptr)++;}/*sets the string pointer after the next comma, if a parse error occured */static intdasd_get_next_int (char **strptr){ int j, i = -1; /* for cosmetic reasons first -1, then 0 */ if (isxdigit (**strptr)) { for (i = 0; isxdigit (**strptr);) { i <<= 4; j = dasd_get_hexdigit (**strptr); if (j == -1) { PRINT_ERR ("no integer: skipping range.\n"); dasd_scan_for_next_comma (strptr); i = -1; break; } i += j; (*strptr)++; if (i > 0xffff) { PRINT_ERR (" value too big, skipping range.\n"); dasd_scan_for_next_comma (strptr); i = -1; break; } } } return i;}static inline intdevindex_from_devno (int devno){ int i; for (i = 0; i < dasd_count; i++) { if (dasd_devno[i] == devno) return i; } if (dasd_autodetect) { if (dasd_count < DASD_MAX_DEVICES) { dasd_devno[dasd_count] = devno; return dasd_count++; } return -EOVERFLOW; } return -ENODEV;}/* returns 1, if dasd_no is in the specified ranges, otherwise 0 */static inline intdasd_is_accessible (int devno){ return (devindex_from_devno (devno) >= 0);}/* dasd_insert_range skips ranges, if the start or the end is -1 */static voiddasd_insert_range (int start, int end){ int curr; FUNCTION_ENTRY ("dasd_insert_range"); if (dasd_count >= DASD_MAX_DEVICES) { PRINT_ERR (" too many devices specified, ignoring some.\n"); FUNCTION_EXIT ("dasd_insert_range"); return; } if ((start == -1) || (end == -1)) { PRINT_ERR ("invalid format of parameter, skipping range\n"); FUNCTION_EXIT ("dasd_insert_range"); return; } if (end < start) { PRINT_ERR (" ignoring range from %x to %x - start value " \ "must be less than end value.\n", start, end); FUNCTION_EXIT ("dasd_insert_range"); return; }/* concurrent execution would be critical, but will not occur here */ for (curr = start; curr <= end; curr++) { if (dasd_is_accessible (curr)) { PRINT_WARN (" %x is already in list as device %d\n", curr, devindex_from_devno (curr)); } dasd_devno[dasd_count] = curr; dasd_count++; if (dasd_count >= DASD_MAX_DEVICES) { PRINT_ERR (" too many devices specified, ignoring some.\n"); break; } } PRINT_INFO (" added dasd range from %x to %x.\n", start, dasd_devno[dasd_count - 1]); FUNCTION_EXIT ("dasd_insert_range");}static int __initdasd_setup (char *str){ int devno, devno2; FUNCTION_ENTRY ("dasd_setup"); dasd_autodetect = 0; while (*str && *str != 1) { if (!isxdigit (*str)) { str++; /* to avoid looping on two commas */ PRINT_ERR (" kernel parameter in invalid format.\n"); continue; } devno = dasd_get_next_int (&str); /* range was skipped? -> scan for comma has been done */ if (devno == -1) continue; if (*str == ',') { str++; dasd_insert_range (devno, devno); continue; } if (*str == '-') { str++; devno2 = dasd_get_next_int (&str); if (devno2 == -1) { PRINT_ERR (" invalid character in " \ "kernel parameters."); } else { dasd_insert_range (devno, devno2); } dasd_scan_for_next_comma (&str); continue; } if (*str == 0) { dasd_insert_range (devno, devno); break; } PRINT_ERR (" unexpected character in kernel parameter, " \ "skipping range.\n"); } FUNCTION_EXIT ("dasd_setup"); return 1;}__setup("dasd=", dasd_setup);dasd_information_t *dasd_info[DASD_MAX_DEVICES] = {NULL,};static struct hd_struct dd_hdstruct[DASD_MAX_DEVICES << PARTN_BITS];static int dasd_blks[256] = {0,};static int dasd_secsize[256] = {0,};static int dasd_blksize[256] = {0,};static int dasd_maxsecs[256] = {0,};struct gendisk dd_gendisk ={ MAJOR_NR, /* Major number */ "dasd", /* Major name */ PARTN_BITS, /* Bits to shift to get real from partn */ 1 << PARTN_BITS, /* Number of partitions per real */ dd_hdstruct, /* hd struct */ dasd_blks, /* sizes in blocks */ DASD_MAX_DEVICES, /* number */ NULL, /* internal */ NULL /* next */};static atomic_t bh_scheduled = ATOMIC_INIT (0);static inline voidschedule_bh (void (*func) (void)){ static struct tq_struct dasd_tq = {0,}; /* Protect against rescheduling, when already running */ if (atomic_compare_and_swap (0, 1, &bh_scheduled)) return; dasd_tq.routine = (void *) (void *) func; queue_task (&dasd_tq, &tq_immediate); mark_bh (IMMEDIATE_BH); return;}voidsleep_done (struct semaphore *sem){ if (sem != NULL) { up (sem); }}voidsleep (int timeout){ struct semaphore sem; struct timer_list timer; init_MUTEX_LOCKED (&sem); init_timer (&timer); timer.data = (unsigned long) &sem; timer.expires = jiffies + timeout; timer.function = (void (*)(unsigned long)) sleep_done; printk (KERN_DEBUG PRINTK_HEADER "Sleeping for timer tics %d\n", timeout); add_timer (&timer); down (&sem); del_timer (&timer);}#ifdef CONFIG_DASD_ECKDextern dasd_operations_t dasd_eckd_operations;#endif /* CONFIG_DASD_ECKD */#ifdef CONFIG_DASD_MDSKextern dasd_operations_t dasd_mdsk_operations;#endif /* CONFIG_DASD_MDSK */dasd_operations_t *dasd_disciplines[] ={#ifdef CONFIG_DASD_ECKD &dasd_eckd_operations,#endif /* CONFIG_DASD_ECKD */#ifdef CONFIG_DASD_MDSK &dasd_mdsk_operations,#endif /* CONFIG_DASD_MDSK */#ifdef CONFIG_DASD_CKD &dasd_ckd_operations,#endif /* CONFIG_DASD_CKD */ NULL};char *dasd_name[] ={#ifdef CONFIG_DASD_ECKD "ECKD",#endif /* CONFIG_DASD_ECKD */#ifdef CONFIG_DASD_MDSK "MDSK",#endif /* CONFIG_DASD_MDSK */#ifdef CONFIG_DASD_CKD "CKD",#endif /* CONFIG_DASD_CKD */ "END"};static inline intdo_dasd_ioctl (struct inode *inp, unsigned int no, unsigned long data){ int rc; int di; dasd_information_t *dev; di = DEVICE_NR (inp->i_rdev); if (!dasd_info[di]) { PRINT_WARN ("No device registered as %d\n", inp->i_rdev); return -EINVAL; } if ((_IOC_DIR (no) != _IOC_NONE) && (data == 0)) { PRINT_DEBUG ("empty data ptr"); return -EINVAL; } dev = dasd_info[di]; if (!dev) { PRINT_WARN ("No device registered as %d\n", inp->i_rdev); return -EINVAL; } PRINT_INFO ("ioctl 0x%08x %s'0x%x'%d(%d) on dev %d/%d (%d) with data %8lx\n", no, _IOC_DIR (no) == _IOC_NONE ? "0" : _IOC_DIR (no) == _IOC_READ ? "r" : _IOC_DIR (no) == _IOC_WRITE ? "w" : _IOC_DIR (no) == (_IOC_READ | _IOC_WRITE) ? "rw" : "u", _IOC_TYPE (no), _IOC_NR (no), _IOC_SIZE (no), MAJOR (inp->i_rdev), MINOR (inp->i_rdev), di, data); switch (no) { case BLKGETSIZE:{ /* Return device size */ unsigned long blocks; if (inp->i_rdev & 0x01) { blocks = (dev->sizes.blocks - 3) << dev->sizes.s2b_shift; } else { blocks = dev->sizes.kbytes << dev->sizes.s2b_shift; } rc = copy_to_user ((long *) data, &blocks, sizeof (long)); break; } case BLKFLSBUF:{ rc = fsync_dev (inp->i_rdev); break; } case BLKRAGET:{ rc = copy_to_user ((long *) data, read_ahead + MAJOR_NR, sizeof (long)); break; } case BLKRASET:{ rc = copy_from_user (read_ahead + MAJOR_NR, (long *) data, sizeof (long)); break; } case BLKRRPART:{ INTERNAL_CHECK ("BLKRPART not implemented%s", ""); rc = -EINVAL; break; } case HDIO_GETGEO:{ INTERNAL_CHECK ("HDIO_GETGEO not implemented%s", ""); rc = -EINVAL; break; } case BIODASDRSID:{ rc = copy_to_user ((void *) data, &(dev->info.sid_data), sizeof (senseid_t)); break; } case BIODASDRWTB:{ int offset = 0; int xlt; rc = copy_from_user (&xlt, (void *) data, sizeof (int)); PRINT_INFO("Xlating %d to",xlt); if (rc) break; if (MINOR (inp->i_rdev) & 1) offset = 3; xlt += offset; printk(" %d \n",xlt); rc = copy_to_user ((void *) data, &xlt, sizeof (int)); break; } case BIODASDFORMAT:{ /* fdata == NULL is a valid arg to dasd_format ! */ format_data_t *fdata = NULL; if (data) { fdata = kmalloc (sizeof (format_data_t), GFP_ATOMIC); if (!fdata) { rc = -ENOMEM; break; } rc = copy_from_user (fdata, (void *) data, sizeof (format_data_t)); if (rc) break; } rc = dasd_format (inp->i_rdev, fdata); if (fdata) { kfree (fdata); } break; } default: rc = -EINVAL; break; } return rc;}static voiddasd_end_request (struct request *req, int uptodate){ struct buffer_head *bh; FUNCTION_ENTRY ("dasd_end_request");#if DASD_PARANOIA > 2 if (!req) { INTERNAL_CHECK ("end_request called with zero arg%s\n", ""); }#endif /* DASD_PARANOIA */ while ((bh = req->bh) != NULL) { req->bh = bh->b_reqnext; bh->b_reqnext = NULL; bh->b_end_io (bh, uptodate); } if (!end_that_request_first (req, uptodate, DEVICE_NAME)) {#ifndef DEVICE_NO_RANDOM add_blkdev_randomness (MAJOR (req->rq_dev));#endif DEVICE_OFF (req->rq_dev); end_that_request_last (req); } FUNCTION_EXIT ("dasd_end_request"); return;}voiddasd_wakeup (void){ wake_up (&dasd_waitq);}intdasd_unregister_dasd (int irq, dasd_type_t dt, dev_info_t * info){ int rc = 0; FUNCTION_ENTRY ("dasd_unregister_dasd"); INTERNAL_CHECK ("dasd_unregister_dasd not implemented%s\n", ""); FUNCTION_EXIT ("dasd_unregister_dasd"); return rc;}/* Below you find the functions already cleaned up */static dasd_type_tcheck_type (dev_info_t * info){ dasd_type_t type = dasd_none; FUNCTION_ENTRY ("check_type");#ifdef CONFIG_DASD_ECKD if (MATCH (info, == 0x3990, ||1, == 0x3390, ||1) || MATCH (info, == 0x9343, ||1, == 0x9345, ||1) || MATCH (info, == 0x3990, ||1, == 0x3380, ||1)) { type = dasd_eckd; } else#endif /* CONFIG_DASD_ECKD */#ifdef CONFIG_DASD_MDSK if ( MACHINE_IS_VM ) { type = dasd_mdsk; } else #endif /* CONFIG_DASD_MDSK */ {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -