patch-2.4.22 linux-2.4.22/drivers/s390/block/dasd.c
Next file: linux-2.4.22/drivers/s390/block/dasd_3990_erp.c
Previous file: linux-2.4.22/drivers/s390/Config.in
Back to the patch index
Back to the overall index
- Lines: 5034
- Date:
2003-08-25 04:44:42.000000000 -0700
- Orig file:
linux-2.4.21/drivers/s390/block/dasd.c
- Orig date:
2003-06-13 07:51:35.000000000 -0700
diff -urN linux-2.4.21/drivers/s390/block/dasd.c linux-2.4.22/drivers/s390/block/dasd.c
@@ -6,6 +6,8 @@
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001
*
+ * $Revision: 1.289 $
+ *
* History of changes (starts July 2000)
* 11/09/00 complete redesign after code review
* 02/01/01 added dynamic registration of ioctls
@@ -33,6 +35,7 @@
* 06/26/01 hopefully fixed PL030172SBA,PL030234SBA
* 07/09/01 fixed PL030324MSH (wrong statistics output)
* 07/16/01 merged in new fixes for handling low-mem situations
+ * 01/22/01 fixed PL030579KBE (wrong statistics)
*/
#include <linux/config.h>
@@ -70,7 +73,6 @@
#include <asm/s390_ext.h>
#include <asm/s390dyn.h>
#include <asm/idals.h>
-#include <asm/dasd.h>
#include "dasd_int.h"
@@ -84,8 +86,9 @@
#include "dasd_diag.h"
#endif /* CONFIG_DASD_DIAG */
-/* SECTION: exported variables of dasd.c */
-
+/********************************************************************************
+ * SECTION: exported variables of dasd.c
+ ********************************************************************************/
debug_info_t *dasd_debug_area;
MODULE_AUTHOR ("Holger Smolinski <Holger.Smolinski@de.ibm.com>");
@@ -94,6 +97,9 @@
MODULE_SUPPORTED_DEVICE ("dasd");
MODULE_PARM (dasd, "1-" __MODULE_STRING (256) "s");
MODULE_PARM (dasd_disciplines, "1-" __MODULE_STRING (8) "s");
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,12))
+MODULE_LICENSE ("GPL");
+#endif
EXPORT_SYMBOL (dasd_chanq_enq_head);
EXPORT_SYMBOL (dasd_debug_area);
EXPORT_SYMBOL (dasd_chanq_enq);
@@ -103,6 +109,7 @@
EXPORT_SYMBOL (dasd_start_IO);
EXPORT_SYMBOL (dasd_term_IO);
EXPORT_SYMBOL (dasd_schedule_bh);
+EXPORT_SYMBOL (dasd_schedule_bh_timed);
EXPORT_SYMBOL (dasd_int_handler);
EXPORT_SYMBOL (dasd_oper_handler);
EXPORT_SYMBOL (dasd_alloc_request);
@@ -115,17 +122,24 @@
EXPORT_SYMBOL (dasd_set_normalized_cda);
EXPORT_SYMBOL (dasd_device_from_kdev);
-/* SECTION: Constant definitions to be used within this file */
+
+/********************************************************************************
+ * SECTION: Constant definitions to be used within this file
+ ********************************************************************************/
#define PRINTK_HEADER DASD_NAME":"
-#undef DASD_PROFILE /* fill profile information - used for */
+#define DASD_PROFILE /* fill profile information - used for */
/* statistics and perfomance */
-#define DASD_MIN_SIZE_FOR_QUEUE 32
-#undef CONFIG_DYNAMIC_QUEUE_MIN_SIZE
-#define DASD_CHANQ_MAX_SIZE 6
-
-/* SECTION: prototypes for static functions of dasd.c */
+#ifndef CONFIG_PROC_FS /* DASD_PROFILE doesn't make sense */
+#undef DASD_PROFILE /* without procfs */
+#endif /* not CONFIG_PROC_FS */
+
+#define DASD_CHANQ_MAX_SIZE 4
+
+/********************************************************************************
+ * SECTION: prototypes for static functions of dasd.c
+ ********************************************************************************/
static request_fn_proc do_dasd_request;
static int dasd_set_device_level (unsigned int, dasd_discipline_t *, int);
@@ -147,7 +161,11 @@
static struct block_device_operations dasd_device_operations;
static inline dasd_device_t ** dasd_device_from_devno (int);
static void dasd_process_queues (dasd_device_t * device);
-/* SECTION: static variables of dasd.c */
+static int dasd_sleep_on_immediate (ccw_req_t *cqr);
+
+/********************************************************************************
+ * SECTION: static variables of dasd.c
+ ********************************************************************************/
static devfs_handle_t dasd_devfs_handle;
static wait_queue_head_t dasd_init_waitq;
@@ -155,7 +173,9 @@
#ifdef CONFIG_DASD_DYNAMIC
-/* SECTION: managing dynamic configuration of dasd_driver */
+/********************************************************************************
+ * SECTION: managing dynamic configuration of dasd_driver
+ ********************************************************************************/
static struct list_head dasd_devreg_head = LIST_HEAD_INIT (dasd_devreg_head);
@@ -188,15 +208,17 @@
#endif /* CONFIG_DASD_DYNAMIC */
-/* SECTION: managing setup of dasd_driver */
+/********************************************************************************
+ * SECTION: managing setup of dasd_driver
+ ********************************************************************************/
/* default setting is probeonly, autodetect */
-static int dasd_probeonly = 1; /* is true, when probeonly mode is active */
-static int dasd_autodetect = 1; /* is true, when autodetection is active */
+static int dasd_probeonly = 0; /* is true, when probeonly mode is active */
+static int dasd_autodetect = 0; /* is true, when autodetection is active */
static dasd_range_t dasd_range_head =
{ list:LIST_HEAD_INIT (dasd_range_head.list) };
-static spinlock_t range_lock = SPIN_LOCK_UNLOCKED;
+static spinlock_t range_lock = SPIN_LOCK_UNLOCKED;
/*
* function: dasd_create_range
@@ -210,20 +232,24 @@
int i;
if ( from > to ) {
- printk (KERN_WARNING PRINTK_HEADER
- "Adding device range %04x-%04x: range invalid, ignoring.\n",
- from,
- to);
+
+ MESSAGE (KERN_WARNING,
+ "Adding device range %04x-%04x: "
+ "range invalid, ignoring.",
+ from,
+ to);
return NULL;
}
for (i=from;i<=to;i++) {
if (dasd_device_from_devno(i)) {
- printk (KERN_WARNING PRINTK_HEADER
- "device range %04x-%04x: device %04x is already in a range.\n",
- from,
- to,
- i);
+
+ MESSAGE (KERN_WARNING,
+ "device range %04x-%04x: device "
+ "%04x is already in a range.",
+ from,
+ to,
+ i);
}
}
range = (dasd_range_t *) kmalloc (sizeof (dasd_range_t), GFP_KERNEL);
@@ -392,7 +418,9 @@
return -ENODEV;
}
-/* SECTION: parsing the dasd= parameter of the parmline/insmod cmdline */
+/********************************************************************************
+ * SECTION: parsing the dasd= parameter of the parmline/insmod cmdline
+ ********************************************************************************/
/*
* char *dasd[] is intended to hold the ranges supplied by the dasd= statement
@@ -426,11 +454,14 @@
*end = '\0';
end++;
}
- dasd[count] = kmalloc (len * sizeof (char), GFP_ATOMIC);
+ dasd[count] = kmalloc (len * sizeof (char),
+ GFP_ATOMIC);
if (dasd[count] == NULL) {
- printk (KERN_WARNING PRINTK_HEADER
- "can't store dasd= parameter no %d\n",
- count + 1);
+
+ MESSAGE (KERN_WARNING,
+ "can't store dasd= parameter no"
+ " %d",
+ count + 1);
break;
}
memset (dasd[count], 0, len * sizeof (char));
@@ -500,10 +531,13 @@
int val,i,start;
buffer=(char*)kmalloc((strlen(str)+1)*sizeof(char),GFP_ATOMIC);
+
if (buffer==NULL) {
- printk (KERN_WARNING PRINTK_HEADER
- "can't parse dasd= parameter %s due to low memory\n",
- str);
+
+ MESSAGE (KERN_WARNING,
+ "can't parse dasd= parameter %s due "
+ "to low memory",
+ str);
}
/* remove leading '0x' */
@@ -528,7 +562,7 @@
val = simple_strtoul (buffer, &buffer, 16);
/* check for features - e.g. (ro) ; the '\0', ')' and '-' stops check */
- *features = DASD_DEFAULT_FEATURES;
+ *features = DASD_FEATURE_DEFAULT;
if (temp[i]=='(') {
@@ -545,14 +579,18 @@
(*features) |= DASD_FEATURE_READONLY;
break;
}
- printk (KERN_WARNING PRINTK_HEADER
- "unsupported feature: %s, ignoring setting",
- buffer);
+
+ MESSAGE (KERN_WARNING,
+ "unsupported feature: %s, "
+ "ignoring setting",
+ buffer);
}
}
}
*stra = temp+i;
+ if ((val > 65535) || (val < 0))
+ return -EINVAL;
return val;
}
@@ -569,22 +607,23 @@
int features;
int rc = 0;
- if (*str) {
- /* turn off probeonly mode, if any dasd parameter is present */
- dasd_probeonly = 0;
- dasd_autodetect = 0;
- }
while (*str) {
temp = *str;
from = 0;
to = 0;
if (strcmp ("autodetect", *str) == 0) {
dasd_autodetect = 1;
- printk (KERN_INFO "turning to autodetection mode\n");
+
+ MESSAGE (KERN_INFO, "%s",
+ "turning to autodetection mode");
+
break;
} else if (strcmp ("probeonly", *str) == 0) {
dasd_probeonly = 1;
- printk (KERN_INFO "turning to probeonly mode\n");
+
+ MESSAGE (KERN_INFO, "%s",
+ "turning to probeonly mode");
+
break;
} else {
/* turn off autodetect mode, if any range is present */
@@ -609,20 +648,16 @@
return rc;
}
-/* SECTION: Dealing with devices registered to multiple major numbers */
+/********************************************************************************
+ * SECTION: Dealing with devices registered to multiple major numbers
+ ********************************************************************************/
static spinlock_t dasd_major_lock = SPIN_LOCK_UNLOCKED;
-static major_info_t dasd_major_info[] = {
- {
- list:LIST_HEAD_INIT (dasd_major_info[1].list)
- },
- {
- list:LIST_HEAD_INIT (dasd_major_info[0].list),
- gendisk:{
- INIT_GENDISK (94, DASD_NAME, DASD_PARTN_BITS, DASD_PER_MAJOR)
- },
- flags:DASD_MAJOR_INFO_IS_STATIC}
+static struct list_head dasd_major_info = LIST_HEAD_INIT(dasd_major_info);
+static major_info_t dasd_major_static = {
+ gendisk:{INIT_GENDISK(94, DASD_NAME, DASD_PARTN_BITS, DASD_PER_MAJOR)},
+ flags: DASD_MAJOR_INFO_IS_STATIC
};
static major_info_t *
@@ -658,8 +693,11 @@
if (major_info == NULL) {
major_info = get_new_major_info ();
if (!major_info) {
- printk (KERN_WARNING PRINTK_HEADER
- "Cannot get memory to allocate another major number\n");
+
+ MESSAGE (KERN_WARNING, "%s",
+ "Cannot get memory to allocate another "
+ "major number");
+
return -ENOMEM;
}
}
@@ -686,10 +724,12 @@
/* register blockdevice */
rc = devfs_register_blkdev (major, DASD_NAME, &dasd_device_operations);
if (rc < 0) {
- printk (KERN_WARNING PRINTK_HEADER
- "Cannot register to major no %d, rc = %d\n",
- major,
- rc);
+
+ MESSAGE (KERN_WARNING,
+ "Cannot register to major no %d, rc = %d",
+ major,
+ rc);
+
goto out_reg_blkdev;
} else {
major_info->flags |= DASD_MAJOR_INFO_REGISTERED;
@@ -698,7 +738,7 @@
/* Insert the new major info into dasd_major_info if needed (dynamic major) */
if (!(major_info->flags & DASD_MAJOR_INFO_IS_STATIC)) {
spin_lock_irqsave (&dasd_major_lock, flags);
- list_add_tail (&major_info->list, &dasd_major_info[0].list);
+ list_add_tail (&major_info->list, &dasd_major_info);
spin_unlock_irqrestore (&dasd_major_lock, flags);
}
@@ -791,10 +831,11 @@
/* unregister blockdevice */
rc = devfs_unregister_blkdev (major, DASD_NAME);
if (rc < 0) {
- printk (KERN_WARNING PRINTK_HEADER
- "Unable to unregister from major no %d, rc = %d\n",
- major,
- rc);
+
+ MESSAGE (KERN_WARNING,
+ "Unable to unregister from major no %d, rc = %d",
+ major,
+ rc);
} else {
major_info->flags &= ~DASD_MAJOR_INFO_REGISTERED;
}
@@ -844,10 +885,12 @@
rc = devfs_unregister_blkdev (major, DASD_NAME);
if (rc < 0) {
- printk (KERN_WARNING PRINTK_HEADER
- "Cannot unregister from major no %d, rc = %d\n",
- major,
- rc);
+
+ MESSAGE (KERN_WARNING,
+ "Cannot unregister from major no %d, rc = %d",
+ major,
+ rc);
+
return rc;
} else {
major_info->flags &= ~DASD_MAJOR_INFO_REGISTERED;
@@ -874,20 +917,23 @@
dasd_device_t *
dasd_device_from_kdev (kdev_t kdev)
{
- major_info_t *major_info = NULL;
+ major_info_t *major_info;
+ dasd_device_t *device;
struct list_head *l;
unsigned long flags;
+ device = NULL;
spin_lock_irqsave (&dasd_major_lock, flags);
- list_for_each (l, &dasd_major_info[0].list) {
+ list_for_each (l, &dasd_major_info) {
major_info = list_entry (l, major_info_t, list);
- if (major_info->gendisk.major == MAJOR (kdev))
+ if (major_info->gendisk.major == MAJOR (kdev)) {
+ device = major_info->dasd_device[MINOR (kdev) >>
+ DASD_PARTN_BITS];
break;
+ }
}
spin_unlock_irqrestore (&dasd_major_lock, flags);
- if (major_info != &dasd_major_info[0])
- return major_info->dasd_device[MINOR (kdev) >> DASD_PARTN_BITS];
- return NULL;
+ return device;
}
/*
@@ -900,21 +946,29 @@
dasd_device_from_devno (int devno)
{
major_info_t *major_info;
+ dasd_device_t **device;
struct list_head *l;
- int devindex = dasd_devindex_from_devno (devno);
+ int devindex;
unsigned long flags;
spin_lock_irqsave (&dasd_major_lock, flags);
- list_for_each (l, &dasd_major_info[0].list) {
+ devindex = dasd_devindex_from_devno (devno);
+ if (devindex < 0) {
+ spin_unlock_irqrestore (&dasd_major_lock, flags);
+ return NULL;
+ }
+
+ device = NULL;
+ list_for_each (l, &dasd_major_info) {
major_info = list_entry (l, major_info_t, list);
if (devindex < DASD_PER_MAJOR) {
- spin_unlock_irqrestore (&dasd_major_lock, flags);
- return &major_info->dasd_device[devindex];
+ device = &major_info->dasd_device[devindex];
+ break;
}
devindex -= DASD_PER_MAJOR;
}
spin_unlock_irqrestore (&dasd_major_lock, flags);
- return NULL;
+ return device;
}
/*
@@ -945,9 +999,33 @@
return -ENODEV;
}
+/*
+ * function: dasd_check_bp_block
+ * checks the blocksize and returns 0 if valid.
+ */
+
+static int
+dasd_check_bp_block (dasd_device_t *device)
+{
+ int rc;
+ switch (device->sizes.bp_block) {
+ case 512:
+ case 1024:
+ case 2048:
+ case 4096:
+ rc = 0;
+ break;
+ default:
+ rc = -EMEDIUMTYPE;
+ }
+
+ return rc;
+}
-/* SECTION: managing dasd disciplines */
+/********************************************************************************
+ * SECTION: managing dasd disciplines
+ ********************************************************************************/
/* anchor and spinlock for list of disciplines */
static struct list_head dasd_disc_head = LIST_HEAD_INIT(dasd_disc_head);
@@ -955,14 +1033,18 @@
/*
* function dasd_discipline_enq
- * chains the discpline given as argument to the head of disiplines
- * head chaining policy is required to allow module disciplines to
- * be preferred against those, who are statically linked
+ * chains the discpline given as argument to the tail of disiplines.
+ * Exception: DIAG is always queued to the head, to ensure that CMS RESERVED
+ * minidisks are invariably accessed using DIAG.
*/
static inline void
-dasd_discipline_enq (dasd_discipline_t * d)
+dasd_discipline_enq (dasd_discipline_t *discipline)
{
- list_add(&d->list, &dasd_disc_head);
+ if (strncmp (discipline->name, "DIAG", 4) == 0) {
+ list_add (&discipline->list, &dasd_disc_head);
+ } else {
+ list_add_tail (&discipline->list, &dasd_disc_head);
+ }
}
/*
@@ -970,59 +1052,88 @@
* removes the discipline given as argument from the list of disciplines
*/
static inline void
-dasd_discipline_deq (dasd_discipline_t * d)
+dasd_discipline_deq (dasd_discipline_t * discipline)
{
- list_del(&d->list);
+ if (&discipline->list) {
+ list_del (&discipline->list);
+ }
}
void
-dasd_discipline_add (dasd_discipline_t * d)
+dasd_discipline_add (dasd_discipline_t * discipline)
{
unsigned long flags;
MOD_INC_USE_COUNT;
spin_lock_irqsave (&discipline_lock,flags);
- dasd_discipline_enq (d);
+ dasd_discipline_enq (discipline);
spin_unlock_irqrestore (&discipline_lock,flags);
- dasd_enable_ranges (&dasd_range_head, d, DASD_STATE_ONLINE);
+
+ dasd_enable_ranges (&dasd_range_head,
+ discipline,
+ DASD_STATE_ONLINE);
}
-void dasd_discipline_del (dasd_discipline_t * d)
+void dasd_discipline_del (dasd_discipline_t * discipline)
{
unsigned long flags;
+
+ dasd_disable_ranges(&dasd_range_head,
+ discipline,
+ DASD_STATE_DEL,
+ 1);
+
spin_lock_irqsave (&discipline_lock,flags);
- dasd_disable_ranges(&dasd_range_head, d, DASD_STATE_DEL, 1);
- dasd_discipline_deq (d);
+ dasd_discipline_deq (discipline);
spin_unlock_irqrestore (&discipline_lock,flags);
MOD_DEC_USE_COUNT;
}
+/*
+ * function dasd_find_disc
+ * checks the list of disciplines for the first one able to access the device
+ */
static inline dasd_discipline_t *
-dasd_find_disc (dasd_device_t * device, dasd_discipline_t *d)
+dasd_find_disc (dasd_device_t * device, dasd_discipline_t *discipline)
{
dasd_discipline_t *t;
- struct list_head *l = d ? &d->list : dasd_disc_head.next;
+ struct list_head *l = discipline ?
+ &discipline->list : dasd_disc_head.next;
+
do {
t = list_entry(l,dasd_discipline_t,list);
+
if ( ( t->id_check == NULL ||
t->id_check (&device->devinfo) == 0 ) &&
( t->check_characteristics == NULL ||
t->check_characteristics (device) == 0 ) )
break;
l = l->next;
- if ( d ||
+ if ( discipline ||
l == &dasd_disc_head ) {
t = NULL;
break;
}
} while ( 1 );
+
return t;
}
-/* SECTION: profiling stuff */
+/********************************************************************************
+ * SECTION: profiling stuff
+ ********************************************************************************/
+
+#ifdef CONFIG_PROC_FS
static dasd_profile_info_t dasd_global_profile;
+#endif /* CONFIG_PROC_FS */
#ifdef DASD_PROFILE
+
+#define DASD_PROFILE_ON 1
+#define DASD_PROFILE_OFF 0
+
+static unsigned int dasd_profile_level = DASD_PROFILE_OFF;
+
/*
* macro: dasd_profile_add_counter
* increments counter in global and local profiling structures
@@ -1032,7 +1143,7 @@
{ \
int ind; \
long help; \
- for (ind = 0, help = value >> 3; \
+ for (ind = 0, help = value >> 2; \
ind < 31 && help; \
help = help >> 1, ind++) {} \
dasd_global_profile.counter[ind]++; \
@@ -1086,56 +1197,134 @@
dasd_profile_add_counter (irqtime / sectors, dasd_io_time2ps, device);
dasd_profile_add_counter (endtime, dasd_io_time3, device);
}
-#endif
+#endif /* DASD_PROFILE */
-/* SECTION: All the gendisk stuff */
+/********************************************************************************
+ * SECTION: All the gendisk stuff
+ ********************************************************************************/
-/* SECTION: Managing wrappers for ccwcache */
+/********************************************************************************
+ * SECTION: Managing wrappers for ccwcache
+ ********************************************************************************/
/*
* function dasd_alloc_request
* tries to return space for a channel program of length cplength with
* additional data of size datasize.
- * If the ccwcache cannot fulfill the request it tries the emergeny requests
- * before giving up finally
+ * If the ccwcache cannot fulfill the request it tries the lowmem requests
+ * before giving up finally.
* FIXME: initialization of ccw_req_t should be done by function of ccwcache
*/
ccw_req_t *
-dasd_alloc_request (char *magic, int cplength, int datasize, dasd_device_t* device)
+dasd_alloc_request (char *magic, int cplength, int datasize,
+ dasd_device_t *device)
{
- ccw_req_t *rv = NULL;
+ ccw_req_t *cqr;
+ unsigned long size_needed;
+ unsigned long data_offset, ccw_offset;
+ dasd_lowmem_t *lowmem;
+
+ if ((cqr = ccw_alloc_request (magic, cplength, datasize)) != NULL) {
+ return cqr;
+ }
+
+ /* Sanity checks */
+ if (magic == NULL || datasize > PAGE_SIZE ||
+ cplength == 0 || (cplength * sizeof(ccw1_t)) > PAGE_SIZE)
+ BUG();
- if ((rv = ccw_alloc_request (magic, cplength, datasize)) != NULL) {
- return rv;
+ /* use lowmem page only for ERP or */
+ /* if there are less than 2 requests on queue */
+ if (device->queue.head != NULL &&
+ device->queue.head->next != NULL &&
+ device->queue.head->status != CQR_STATUS_ERROR) {
+ return NULL;
+ }
+
+ /* We try to keep things together in memory */
+ size_needed = (sizeof (ccw_req_t) + 7) & (~7L);
+ data_offset = ccw_offset = 0;
+ if (size_needed + datasize <= PAGE_SIZE) {
+ /* Keep data with the request */
+ data_offset = size_needed;
+ size_needed += (datasize + 7) & (~7L);
+ }
+ if (size_needed + cplength*sizeof(ccw1_t) <= PAGE_SIZE) {
+ /* Keep CCWs with request */
+ ccw_offset = size_needed;
+ size_needed += (cplength*sizeof(ccw1_t)) & (~7L);
}
- if ((((sizeof (ccw_req_t) + 7) & -8) +
- cplength * sizeof (ccw1_t) + datasize) > PAGE_SIZE) {
- BUG ();
+
+ /* take page from lowmem_pool for request */
+ list_for_each_entry (lowmem, &device->lowmem_pool, list) {
+ list_del (&lowmem->list);
+ cqr = (ccw_req_t *) lowmem;
+ memset (cqr, 0, PAGE_SIZE);
+ cqr->flags |= CQR_FLAGS_LM_CQR;
+ break;
+ }
+ if (cqr == NULL)
+ return NULL;
+
+ /* take page from lowmem_pool for the extra data */
+ if (data_offset == 0) {
+
+ list_for_each_entry (lowmem, &device->lowmem_pool, list) {
+ list_del (&lowmem->list);
+ cqr->data = (void *) lowmem;
+ memset (cqr->data, 0, PAGE_SIZE);
+ break;
+ }
+ if (cqr->data == NULL) {
+ printk(KERN_DEBUG PRINTK_HEADER
+ "Couldn't allocate data area\n");
+
+ lowmem = (dasd_lowmem_t *) cqr;
+ list_add (&lowmem->list, &device->lowmem_pool);
+ return NULL;
}
- if (device->lowmem_cqr==NULL) {
- DASD_DRIVER_DEBUG_EVENT (2, dasd_alloc_request,
- "(%04x) Low memory! Using emergency request %p.",
- device->devinfo.devno,
- device->lowmem_ccws);
-
- device->lowmem_cqr=device->lowmem_ccws;
- rv = device->lowmem_ccws;
- memset (rv, 0, PAGE_SIZE);
- strncpy ((char *) (&rv->magic), magic, 4);
- ASCEBC ((char *) (&rv->magic), 4);
- rv->cplength = cplength;
- rv->datasize = datasize;
- rv->data = (void *) ((long) rv + PAGE_SIZE - datasize);
- rv->cpaddr = (ccw1_t *) ((long) rv + sizeof (ccw_req_t));
- } else {
- DASD_DRIVER_DEBUG_EVENT (2, dasd_alloc_request,
- "(%04x) Refusing emergency mem for request "
- "NULL, already in use at %p.",
- device->devinfo.devno,
- device->lowmem_ccws);
- }
- return rv;
+ } else {
+ /* Extra data already allocated with the request */
+ cqr->data = (void *) ((addr_t) cqr + data_offset);
+ }
+
+ /* take page from lowmem_pool for the channel program */
+ if (ccw_offset == 0) {
+
+ list_for_each_entry (lowmem, &device->lowmem_pool, list) {
+ list_del (&lowmem->list);
+ cqr->cpaddr = (ccw1_t *) lowmem;
+ memset (cqr->cpaddr, 0, PAGE_SIZE);
+ break;
+ }
+
+ if (cqr->cpaddr == NULL) {
+ printk (KERN_DEBUG PRINTK_HEADER
+ "Couldn't allocate channel program area\n");
+ if (data_offset == 0) {
+ lowmem = (dasd_lowmem_t *) cqr->data;
+ list_add (&lowmem->list, &device->lowmem_pool);
+ }
+ lowmem = (dasd_lowmem_t *) cqr;
+ list_add (&lowmem->list, &device->lowmem_pool);
+ return NULL;
+ }
+ } else {
+ /* Channel program already allocated with the request */
+ cqr->cpaddr = (ccw1_t *) ((addr_t) cqr + ccw_offset);
+ }
+
+ /* use the remaining memory of the cqr page for IDALs */
+ cqr->lowmem_idal_ptr = (void *) ((addr_t) cqr + size_needed);
+
+ strncpy ((char *)(&cqr->magic), magic, 4);
+
+ ASCEBC((char *)(&cqr->magic), 4);
+ cqr->cplength = cplength;
+ cqr->datasize = datasize;
+
+ return cqr;
}
/*
@@ -1143,87 +1332,147 @@
* returns a ccw_req_t to the appropriate cache or emergeny request line
*/
void
-dasd_free_request (ccw_req_t * request, dasd_device_t* device)
+dasd_free_request (ccw_req_t *cqr, dasd_device_t* device)
{
+ unsigned long size_needed;
+ dasd_lowmem_t *lowmem;
+
#ifdef CONFIG_ARCH_S390X
ccw1_t* ccw;
- /* clear any idals used for chain */
- ccw=request->cpaddr-1;
+ /* clear any idals used for chain (might be in lowmen cqr page, */
+ /* in seperate lowmen page or kmalloced */
+ ccw=cqr->cpaddr-1;
do {
ccw++;
- if ((ccw->cda < (unsigned long) device->lowmem_idals ) ||
- (ccw->cda >= (unsigned long) device->lowmem_idals+PAGE_SIZE) )
- clear_normalized_cda (ccw);
- else {
- if (device->lowmem_idal_ptr != device->lowmem_idals)
- DASD_MESSAGE (KERN_WARNING, device,
- "Freeing emergency idals from request at %p.",
- request);
- device->lowmem_idal_ptr = device->lowmem_idals;
- device->lowmem_cqr=NULL;
+ if ((cqr->flags & CQR_FLAGS_LM_CQR) &&
+ (ccw->cda >= (unsigned long) cqr) &&
+ (ccw->cda < (unsigned long) cqr + PAGE_SIZE)) {
+ /* IDAL is on the car lowmem page */
+ continue;
+ }
+
+ if ((cqr->flags & CQR_FLAGS_LM_IDAL) &&
+ (ccw->cda >= (unsigned long) cqr->lowmem_idal) &&
+ (ccw->cda < (unsigned long) cqr->lowmem_idal + PAGE_SIZE)) {
+ /* IDAL is on seperate lowmem page */
+ continue;
}
+
+ /* IDAL was build by set_normalized_cda */
+ clear_normalized_cda (ccw);
+
} while ((ccw->flags & CCW_FLAG_CC) ||
(ccw->flags & CCW_FLAG_DC) );
#endif
- if (request != device->lowmem_ccws) {
- /* compare to lowmem_ccws to protect usage of lowmem_cqr for IDAL only ! */
- ccw_free_request (request);
- } else {
- DASD_MESSAGE (KERN_WARNING, device,
- "Freeing emergency request at %p",
- request);
- device->lowmem_cqr=NULL;
- }
+ /* give idal lowmem page back to lowmem_pool */
+ if (cqr->flags & CQR_FLAGS_LM_IDAL) {
+ lowmem = (dasd_lowmem_t *) cqr->lowmem_idal;
+ list_add (&lowmem->list, &device->lowmem_pool);
+ cqr->flags &= ~CQR_FLAGS_LM_IDAL;
+ }
+
+ /* give cqr lowmem pages back to lowmem_pool */
+ if (cqr->flags & CQR_FLAGS_LM_CQR) {
+
+ /* make the same decisions as in dasd_alloc_request */
+ size_needed = (sizeof (ccw_req_t) + 7) & (~7L);
+ if (size_needed + cqr->datasize <= PAGE_SIZE) {
+ /* We kept the data with the request */
+ size_needed += (cqr->datasize + 7) & (~7L);
+ } else {
+ lowmem = (dasd_lowmem_t *) cqr->data;
+ list_add (&lowmem->list, &device->lowmem_pool);
+ }
+
+ if (size_needed + cqr->cplength * sizeof(ccw1_t) > PAGE_SIZE) {
+ /* We didn't keep the CCWs with request */
+ lowmem = (dasd_lowmem_t *) cqr->cpaddr;
+ list_add (&lowmem->list, &device->lowmem_pool);
+ }
+ lowmem = (dasd_lowmem_t *) cqr;
+ list_add (&lowmem->list, &device->lowmem_pool);
+ } else {
+ ccw_free_request (cqr);
+ }
}
+/*
+ * function dasd_set_normalized_cda
+ * calls set_normalized_cda to build IDALs.
+ * If this did not work because of low memory, we try to use memory from the
+ * lowmem pool.
+ */
int
-dasd_set_normalized_cda (ccw1_t * cp, unsigned long address,
- ccw_req_t* request, dasd_device_t* device )
+dasd_set_normalized_cda (ccw1_t *cp, unsigned long address,
+ ccw_req_t *cqr, dasd_device_t *device)
{
#ifdef CONFIG_ARCH_S390X
+ int rc;
int nridaws;
+ dasd_lowmem_t *lowmem;
int count = cp->count;
- if (set_normalized_cda (cp, address)!=-ENOMEM) {
- return 0;
- }
-
- if ((device->lowmem_cqr!=NULL) && (device->lowmem_cqr!=request)) {
- DASD_MESSAGE (KERN_WARNING, device,
- "Refusing emergency idals for request %p, memory"
- " is already in use for request %p",
- request,
- device->lowmem_cqr);
- return -ENOMEM;
- }
- device->lowmem_cqr=request;
- if (device->lowmem_idal_ptr == device->lowmem_idals) {
- DASD_MESSAGE (KERN_WARNING,device,
- "Low memory! Using emergency IDALs for request %p.\n",
- request);
+ /* use lowmem idal page if already assinged */
+ if (!(cqr->flags & CQR_FLAGS_LM_IDAL)) {
+ rc = set_normalized_cda (cp, (void *)address);
+ if (rc !=-ENOMEM) {
+ return rc;
+ }
}
+
+ /* get number of idal words needed */
nridaws = ((address & (IDA_BLOCK_SIZE-1)) + count +
(IDA_BLOCK_SIZE-1)) >> IDA_SIZE_LOG;
- if ( device->lowmem_idal_ptr>=device->lowmem_idals + PAGE_SIZE ) {
- /* Ouch! No Idals left for emergency request */
- BUG();
- }
+
+ /* check if we need an additional IDALs page */
+ if (!(cqr->flags & CQR_FLAGS_LM_IDAL)) {
+ /* we got no lowmem cqr page OR */
+ /* there is no space left for IDALs */
+ if ((!(cqr->flags & CQR_FLAGS_LM_CQR)) ||
+ ((cqr->lowmem_idal_ptr + nridaws * sizeof(unsigned long)) >
+ ((void *) cqr + PAGE_SIZE))) {
+
+ /* use lowmem page only for ERP or */
+ /* if there are less than 2 requests on queue */
+ if (device->queue.head != NULL &&
+ device->queue.head->next != NULL &&
+ device->queue.head->status != CQR_STATUS_ERROR) {
+ return -ENOMEM;
+ }
+
+ list_for_each_entry (lowmem, &device->lowmem_pool,
+ list) {
+ list_del (&lowmem->list);
+ cqr->lowmem_idal = (void *)lowmem;
+ cqr->lowmem_idal_ptr = (void *) lowmem;
+ memset (cqr->lowmem_idal, 0, PAGE_SIZE);
+ cqr->flags |= CQR_FLAGS_LM_IDAL;
+ break;
+ }
+ }
+
+ }
+
+ /* now we (should) have an valid lowmem_idal_ptr and enough space for */
+ /* the IDALs - fill the idals table */
cp->flags |= CCW_FLAG_IDA;
- cp->cda = (__u32)(unsigned long)device->lowmem_idal_ptr;
+ cp->cda = (__u32)(unsigned long)cqr->lowmem_idal_ptr;
do {
- *((long*)device->lowmem_idal_ptr) = address;
- address = (address & -(IDA_BLOCK_SIZE)) + (IDA_BLOCK_SIZE);
+ *((long*)cqr->lowmem_idal_ptr) = address;
+ address = (address & -(IDA_BLOCK_SIZE)) + (IDA_BLOCK_SIZE);
+ cqr->lowmem_idal_ptr += sizeof(unsigned long);
nridaws --;
- device->lowmem_idal_ptr += sizeof(unsigned long);
} while ( nridaws > 0 );
#else
- cp -> cda = address;
+ cp->cda = address;
#endif
return 0;
}
-/* SECTION: (de)queueing of requests to channel program queues */
+/********************************************************************************
+ * SECTION: (de)queueing of requests to channel program queues
+ ********************************************************************************/
/*
* function dasd_chanq_enq
@@ -1245,22 +1494,26 @@
#ifdef DASD_PROFILE
- /* save profile information for non erp cqr */
- if (cqr->refers == NULL) {
- unsigned int counter = 0;
- ccw_req_t *ptr;
- dasd_device_t *device = cqr->device;
-
- /* count the length of the chanq for statistics */
- for (ptr = q->head;
- ptr->next != NULL && counter <=31;
- ptr = ptr->next) {
- counter++;
- }
-
- dasd_global_profile.dasd_io_nr_req[counter]++;
- device->profile.dasd_io_nr_req[counter]++;
- }
+ if (dasd_profile_level == DASD_PROFILE_ON) {
+
+ /* save profile information for non erp cqr */
+ if (cqr->refers == NULL) {
+ unsigned int counter = 0;
+ ccw_req_t *ptr;
+ dasd_device_t *device = cqr->device;
+
+ /* count the length of the chanq for statistics */
+ for (ptr = q->head;
+ ptr->next != NULL && counter <=31;
+ ptr = ptr->next) {
+ counter++;
+ }
+
+ dasd_global_profile.dasd_io_nr_req[counter]++;
+ device->profile.dasd_io_nr_req[counter]++;
+ }
+
+ } /* end if DASD_PROFILE_ON */
#endif
}
@@ -1276,7 +1529,10 @@
q->head = cqr;
if (q->tail == NULL)
q->tail = cqr;
- check_then_set (&cqr->status, CQR_STATUS_FILLED, CQR_STATUS_QUEUED);
+
+ check_then_set (&cqr->status,
+ CQR_STATUS_FILLED,
+ CQR_STATUS_QUEUED);
}
/*
@@ -1302,7 +1558,7 @@
while (prev && prev->next != cqr)
prev = prev->next;
if (prev == NULL)
- return;
+ return; /* request not in chanq */
prev->next = cqr->next;
if (prev->next == NULL)
q->tail = prev;
@@ -1310,7 +1566,87 @@
cqr->next = NULL;
}
-/* SECTION: Managing the device queues etc. */
+/********************************************************************************
+ * SECTION: Managing the device queues etc.
+ ********************************************************************************/
+
+/*
+ * DASD_RESREL_TIMEOUT
+ *
+ * A timer is used to suspend the current reserve/release request
+ * if it doesn't return within a certain time.
+ */
+void
+dasd_resrel_timeout (unsigned long cqr_ptr)
+{
+ dasd_device_t *device = ((ccw_req_t *) cqr_ptr)->device;
+ ccw_req_t *cqr;
+ unsigned long flags;
+
+ s390irq_spin_lock_irqsave (device->devinfo.irq,
+ flags);
+ cqr = device->queue.head;
+
+ switch (cqr->status) {
+ case CQR_STATUS_FILLED:
+ case CQR_STATUS_QUEUED:
+ /* request was not started - just set to failed */
+ cqr->status = CQR_STATUS_FAILED;
+ break;
+
+ case CQR_STATUS_IN_IO:
+ case CQR_STATUS_ERROR:
+ if (device->discipline->term_IO (cqr) != 0);
+ cqr->status = CQR_STATUS_FAILED;
+ break;
+
+ default:
+ ; /* DONE and FAILED are ok */
+ }
+
+ dasd_schedule_bh (device);
+
+ s390irq_spin_unlock_irqrestore (device->devinfo.irq,
+ flags);
+
+} /* end dasd_resrel_timeout */
+
+/*
+ * Call unconditional reserve to break the reserve of an other system.
+ * Timeout the request if it doesn't succseed within a certain time.
+ */
+static int
+dasd_steal_lock (dasd_device_t *device)
+{
+ ccw_req_t *cqr;
+ int rc = 0;
+
+ if (!device->discipline->steal_lock)
+ rc = -EINVAL;
+
+ cqr = device->discipline->steal_lock (device);
+
+ if (cqr) {
+ struct timer_list res_timer;
+
+ init_timer(&res_timer);
+ res_timer.function = dasd_resrel_timeout;
+ res_timer.data = (unsigned long) cqr;
+ res_timer.expires = jiffies + 4 * HZ;
+ add_timer(&res_timer);
+
+ rc = dasd_sleep_on_immediate (cqr);
+
+ del_timer_sync(&res_timer);
+ dasd_free_request (cqr,
+ device);
+ } else {
+ rc = -ENOMEM;
+ }
+
+ return rc;
+
+} /* end dasd_steal_lock */
/*
* DASD_TERM_IO
@@ -1331,55 +1667,57 @@
BUG ();
}
irq = device->devinfo.irq;
- if (strncmp ((char *) &cqr->magic, device->discipline->ebcname, 4)) {
- DASD_MESSAGE (KERN_WARNING, device,
- " ccw_req_t 0x%08x magic doesn't match"
- " discipline 0x%08x\n",
- cqr->magic,
- *(unsigned int *) device->discipline->name);
+ if (strncmp ((char *) &cqr->magic,
+ device->discipline->ebcname, 4)) {
+
+ DEV_MESSAGE (KERN_WARNING, device,
+ " ccw_req_t 0x%08x magic doesn't match"
+ " discipline 0x%08x",
+ cqr->magic,
+ *(unsigned int *) device->discipline->name);
+
return -EINVAL;
}
while ((retries < 5 ) &&
(cqr->status == CQR_STATUS_IN_IO) ) {
- if ( retries < 2 )
- rc = halt_IO(irq, (long)cqr,
- cqr->options | DOIO_WAIT_FOR_INTERRUPT);
- else
- rc = clear_IO(irq, (long)cqr,
- cqr->options | DOIO_WAIT_FOR_INTERRUPT);
-
+ rc = clear_IO (irq,
+ (long)cqr,
+ cqr->options);
+
switch (rc) {
case 0: /* termination successful */
check_then_set (&cqr->status,
CQR_STATUS_IN_IO,
CQR_STATUS_FAILED);
- asm volatile ("STCK %0":"=m" (cqr->stopclk));
+ cqr->stopclk = get_clock ();
+
break;
case -ENODEV:
- DASD_MESSAGE (KERN_WARNING, device, "%s",
- "device gone, retry\n");
+ DBF_DEV_EVENT (DBF_ERR, device, "%s",
+ "device gone, retry");
break;
case -EIO:
- DASD_MESSAGE (KERN_WARNING, device, "%s",
- "I/O error, retry\n");
+ DBF_DEV_EVENT (DBF_ERR, device, "%s",
+ "I/O error, retry");
break;
case -EBUSY:
- DASD_MESSAGE (KERN_WARNING, device, "%s",
- "device busy, retry later\n");
+ DBF_DEV_EVENT (DBF_ERR, device, "%s",
+ "device busy, retry later");
break;
default:
- DASD_MESSAGE (KERN_ERR, device,
- "line %d unknown RC=%d, please report"
- " to linux390@de.ibm.com\n",
- __LINE__,
- rc);
+ DEV_MESSAGE (KERN_ERR, device,
+ "line %d unknown RC=%d, please "
+ "report to linux390@de.ibm.com",
+ __LINE__,
+ rc);
BUG ();
break;
}
+ dasd_schedule_bh (device);
retries ++;
}
return rc;
@@ -1401,27 +1739,30 @@
BUG ();
}
irq = device->devinfo.irq;
- if (strncmp ((char *) &cqr->magic, device->discipline->ebcname, 4)) {
- DASD_MESSAGE (KERN_WARNING, device,
- " ccw_req_t 0x%08x magic doesn't match"
- " discipline 0x%08x\n",
- cqr->magic,
- *(unsigned int *) device->discipline->name);
+ if (strncmp ((char *) &cqr->magic,
+ device->discipline->ebcname, 4)) {
+
+ DEV_MESSAGE (KERN_ERR, device,
+ " ccw_req_t 0x%08x magic doesn't match"
+ " discipline 0x%08x",
+ cqr->magic,
+ *(unsigned int *) device->discipline->name);
+
return -EINVAL;
}
- asm volatile ("STCK %0":"=m" (now));
- cqr->startclk = now;
+ now = get_clock ();
- rc = do_IO (irq, cqr->cpaddr, (long) cqr, cqr->lpm, cqr->options);
+ cqr->startclk = now;
+ if (device->accessible)
+ rc = do_IO (irq, cqr->cpaddr, (long) cqr, cqr->lpm, cqr->options);
+ else
+ rc = -EBUSY;
switch (rc) {
case 0:
if (cqr->options & DOIO_WAIT_FOR_INTERRUPT) {
/* request already finished (synchronous IO) */
- DASD_MESSAGE (KERN_ERR, device, "%s",
- " do_IO finished request... "
- "DOIO_WAIT_FOR_INTERRUPT was set");
check_then_set (&cqr->status,
CQR_STATUS_QUEUED,
CQR_STATUS_DONE);
@@ -1436,12 +1777,22 @@
}
break;
case -EBUSY:
- DASD_MESSAGE (KERN_WARNING, device, "%s",
- "device busy, retry later\n");
+ DBF_DEV_EVENT (DBF_ERR, device, "%s",
+ "device busy, retry later");
+
+ if (!timer_pending(&device->timer)) {
+ init_timer (&device->timer);
+ device->timer.function = dasd_schedule_bh_timed;
+ device->timer.data = (unsigned long) device;
+ device->timer.expires = jiffies + (HZ >> 4);
+ add_timer (&device->timer);
+ } else {
+ mod_timer(&device->timer, jiffies + (HZ >> 4));
+ }
break;
case -ETIMEDOUT:
- DASD_MESSAGE (KERN_WARNING, device, "%s",
- "request timeout - terminated\n");
+ DBF_DEV_EVENT (DBF_ERR, device, "%s",
+ "request timeout - terminated");
case -ENODEV:
case -EIO:
check_then_set (&cqr->status,
@@ -1452,9 +1803,11 @@
dasd_schedule_bh (device);
break;
default:
- DASD_MESSAGE (KERN_ERR, device,
- "line %d unknown RC=%d, please report"
- " to linux390@de.ibm.com\n", __LINE__, rc);
+ DEV_MESSAGE (KERN_ERR, device,
+ "line %d unknown RC=%d, please report"
+ " to linux390@de.ibm.com",
+ __LINE__,
+ rc);
BUG ();
break;
}
@@ -1465,43 +1818,83 @@
/*
* function dasd_sleep_on_req
* attempts to start the IO and waits for completion
- * FIXME: replace handmade sleeping by wait_event
*/
int
-dasd_sleep_on_req (ccw_req_t * req)
+dasd_sleep_on_req (ccw_req_t * cqr)
{
unsigned long flags;
- int cs;
- int rc = 0;
- dasd_device_t *device = (dasd_device_t *) req->device;
+ dasd_device_t *device = (dasd_device_t *) cqr->device;
- if ( signal_pending(current) ) {
+ if (signal_pending(current)) {
return -ERESTARTSYS;
}
- s390irq_spin_lock_irqsave (device->devinfo.irq, flags);
- dasd_chanq_enq (&device->queue, req);
+ s390irq_spin_lock_irqsave (device->devinfo.irq,
+ flags);
+
+ dasd_chanq_enq (&device->queue,
+ cqr);
+
/* let the bh start the request to keep them in order */
dasd_schedule_bh (device);
- do {
- s390irq_spin_unlock_irqrestore (device->devinfo.irq, flags);
- wait_event ( device->wait_q,
- (((cs = req->status) == CQR_STATUS_DONE) ||
- (cs == CQR_STATUS_FAILED) ||
- signal_pending(current)));
- s390irq_spin_lock_irqsave (device->devinfo.irq, flags);
- if ( signal_pending(current) ) {
- rc = -ERESTARTSYS;
- if (req->status == CQR_STATUS_IN_IO )
- device->discipline->term_IO(req);
- break;
- } else if ( req->status == CQR_STATUS_FAILED) {
- rc = -EIO;
- break;
- }
- } while (cs != CQR_STATUS_DONE && cs != CQR_STATUS_FAILED);
- s390irq_spin_unlock_irqrestore (device->devinfo.irq, flags);
- return rc;
-} /* end dasd_sleep_on_req */
+
+ s390irq_spin_unlock_irqrestore (device->devinfo.irq,
+ flags);
+
+ wait_event (device->wait_q,
+ cqr->flags & CQR_FLAGS_FINALIZED);
+
+ if (cqr->status == CQR_STATUS_FAILED) {
+ return -EIO;
+ }
+
+ return 0;
+
+} /* end dasd_sleep_on_req */
+
+/*
+ * function dasd_sleep_on_immediate
+ * same as dasd_sleep_on_req, but attempts to start the IO immediately
+ * (killing the actual running IO).
+ */
+static int
+dasd_sleep_on_immediate (ccw_req_t *cqr)
+{
+ unsigned long flags;
+ dasd_device_t *device = (dasd_device_t *) cqr->device;
+
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+
+ s390irq_spin_lock_irqsave (device->devinfo.irq,
+ flags);
+
+ /* terminate currently running IO */
+ if (device->queue.head->status == CQR_STATUS_IN_IO) {
+
+ device->discipline->term_IO (device->queue.head);
+
+ device->queue.head->status = CQR_STATUS_QUEUED;
+ }
+
+ dasd_chanq_enq_head (&device->queue,
+ cqr);
+
+ /* let the bh start the request to keep them in order */
+ dasd_schedule_bh (device);
+
+ s390irq_spin_unlock_irqrestore (device->devinfo.irq,
+ flags);
+
+ wait_event (device->wait_q,
+ cqr->flags & CQR_FLAGS_FINALIZED);
+
+ if (cqr->status == CQR_STATUS_FAILED) {
+ return -EIO;
+ }
+
+ return 0;
+
+} /* end dasd_sleep_on_immediate */
/*
* function dasd_end_request
@@ -1547,12 +1940,17 @@
unsigned long long now;
int rc = 0;
- asm volatile ("STCK %0":"=m" (now));
- if (cqr->expires && cqr->expires + cqr->startclk < now) {
- DASD_MESSAGE (KERN_ERR, ((dasd_device_t *) cqr->device),
- "IO timeout 0x%08lx%08lx usecs in req %p\n",
- (long) (cqr->expires >> 44),
- (long) (cqr->expires >> 12), cqr);
+ now = get_clock ();
+
+ if (cqr->expires &&
+ cqr->expires + cqr->startclk < now) {
+
+ DBF_DEV_EVENT (DBF_WARNING, ((dasd_device_t *) cqr->device),
+ "IO timeout 0x%08lx%08lx usecs in req %p",
+ (long) (cqr->expires >> 44),
+ (long) (cqr->expires >> 12),
+ cqr);
+
cqr->expires <<= 1;
rc = -EIO;
}
@@ -1569,22 +1967,36 @@
{
dasd_device_t *device = cqr->device;
- asm volatile ("STCK %0":"=m" (cqr->endclk));
+ cqr->endclk = get_clock ();
+
if (cqr->req) {
+
#ifdef DASD_PROFILE
- dasd_profile_add (cqr);
+ if (dasd_profile_level == DASD_PROFILE_ON) {
+ dasd_profile_add (cqr);
+ }
#endif
+
dasd_end_request (cqr->req, (cqr->status == CQR_STATUS_DONE));
/* free request if nobody is waiting on it */
dasd_free_request (cqr, cqr->device);
} else {
- if ( cqr == device->init_cqr && /* bring late devices online */
- device->level <= DASD_STATE_ONLINE ) {
- device->timer.function = dasd_enable_single_device;
- device->timer.data = (unsigned long) device;
- device->timer.expires = jiffies;
- add_timer(&device->timer);
+ if (cqr == device->init_cqr && /* bring late devices online */
+ device->level <= DASD_STATE_ONLINE ) {
+ if (!timer_pending(&device->late_timer)) {
+ init_timer(&device->late_timer);
+ device->late_timer.function = dasd_enable_single_device;
+ device->late_timer.data = (unsigned long) device;
+ device->late_timer.expires = jiffies;
+ add_timer(&device->late_timer);
+ } else {
+ mod_timer(&device->late_timer, jiffies);
+ }
+ } else {
+ /* notify sleep_on_xxx about finished cqr */
+ cqr->flags |= CQR_FLAGS_FINALIZED;
}
+
/* notify sleeping task about finished postprocessing */
wake_up (&device->wait_q);
@@ -1606,12 +2018,10 @@
dasd_chanq_t *qp = &device->queue;
int irq = device->devinfo.irq;
ccw_req_t *final_requests = NULL;
- static int chanq_min_size = DASD_MIN_SIZE_FOR_QUEUE;
int chanq_max_size = DASD_CHANQ_MAX_SIZE;
ccw_req_t *cqr = NULL, *temp;
dasd_erp_postaction_fn_t erp_postaction;
-
s390irq_spin_lock_irqsave (irq, flags);
/* First we dechain the requests, processed with completed status */
@@ -1628,16 +2038,10 @@
qp->head->retries--;
- if (qp->head->dstat->flag & DEVSTAT_HALT_FUNCTION) {
-
- check_then_set (&qp->head->status,
- CQR_STATUS_ERROR,
- CQR_STATUS_FAILED);
-
- asm volatile ("STCK %0":"=m" (qp->head->stopclk));
-
- } else if ((device->discipline->erp_action == NULL ) ||
- ((erp_action = device->discipline->erp_action (qp->head)) == NULL) ) {
+ if ((qp->head->dstat == NULL ) ||
+ ((qp->head->dstat->flag & DEVSTAT_FLAG_SENSE_AVAIL) == 0 ) ||
+ (device->discipline->erp_action == NULL ) ||
+ ((erp_action = device->discipline->erp_action (qp->head)) == NULL) ) {
erp_cqr = dasd_default_erp_action (qp->head);
@@ -1651,12 +2055,12 @@
if (qp->head->status == CQR_STATUS_DONE) {
- DASD_MESSAGE (KERN_DEBUG, device, "%s",
- "ERP successful");
+ DBF_DEV_EVENT (DBF_NOTICE, device, "%s",
+ "ERP successful");
} else {
- DASD_MESSAGE (KERN_ERR, device, "%s",
- "ERP unsuccessful");
+ DEV_MESSAGE (KERN_WARNING, device, "%s",
+ "ERP unsuccessful");
}
if ((device->discipline->erp_postaction == NULL )||
@@ -1685,66 +2089,61 @@
} /* end while over completed requests */
if (cqr)
- cqr->next = NULL;
+ cqr->next = NULL; /* terminate final_requests queue */
+
/* Now clean the requests with final status */
while (final_requests) {
temp = final_requests;
final_requests = temp->next;
dasd_finalize_request (temp);
}
+
/* Now we try to fetch requests from the request queue */
- for (temp = cqr; temp != NULL; temp = temp->next)
+ for (temp = qp->head; temp != NULL; temp = temp->next) {
if (temp->status == CQR_STATUS_QUEUED)
chanq_max_size--;
+ }
+
while ((atomic_read(&device->plugged) == 0) &&
+ (queue) &&
(!queue->plugged) &&
(!list_empty (&queue->queue_head)) &&
- (req = dasd_next_request (queue)) != NULL) {
+ (req = dasd_next_request (queue)) &&
+ (qp->head == NULL || chanq_max_size > 0)) {
/* queue empty or certain critera fulfilled -> transfer */
- if (qp->head == NULL ||
- chanq_max_size > 0 || (req->nr_sectors >= chanq_min_size)) {
- ccw_req_t *cqr = NULL;
- if (is_read_only(device->kdev) && req->cmd == WRITE) {
-
- DASD_DRIVER_DEBUG_EVENT (3, dasd_int_handler,
- "(%04x) Rejecting write request %p\n",
- device->devinfo.devno,
- req);
+ cqr = NULL;
+ if (is_read_only(device->kdev) && req->cmd == WRITE) {
- dasd_end_request (req, 0);
- dasd_dequeue_request (queue,req);
- } else {
- /* relocate request according to partition table */
- req->sector +=
- device->major_info->gendisk.
- part[MINOR (req->rq_dev)].start_sect;
- cqr = device->discipline->build_cp_from_req (device, req);
- if (cqr == NULL) {
-
- DASD_DRIVER_DEBUG_EVENT (3, dasd_int_handler,
- "(%04x) CCW creation failed "
- "on request %p\n",
- device->devinfo.devno,
- req);
- /* revert relocation of request */
- req->sector -=
- device->major_info->gendisk.
- part[MINOR (req->rq_dev)].start_sect;
- break; /* terminate request queue loop */
-
- }
-#ifdef CONFIG_DYNAMIC_QUEUE_MIN_SIZE
- chanq_min_size =
- (chanq_min_size + req->nr_sectors) >> 1;
-#endif /* CONFIG_DYNAMIC_QUEUE_MIN_SIZE */
- dasd_dequeue_request (queue, req);
- dasd_chanq_enq (qp, cqr);
+ DBF_EVENT (DBF_ERR,
+ "(%04x) Rejecting write request %p",
+ device->devinfo.devno,
+ req);
+ dasd_dequeue_request (queue,req);
+ dasd_end_request (req, 0);
+ continue;
+ }
+ cqr = device->discipline->build_cp_from_req (device, req);
+
+ if (cqr == NULL || IS_ERR(cqr)) {
+ if (cqr == ERR_PTR(-ENOMEM)) {
+ break;
}
- } else { /* queue not empty OR criteria not met */
- break; /* terminate request queue loop */
- }
- }
- /* we process the requests with non-final status */
+
+ MESSAGE (KERN_EMERG,
+ "(%04x) CCW creation failed "
+ "on request %p",
+ device->devinfo.devno, req);
+ dasd_dequeue_request (queue,req);
+ dasd_end_request (req, 0);
+ continue;
+ }
+ dasd_dequeue_request (queue, req);
+ dasd_chanq_enq (qp, cqr);
+ chanq_max_size--;
+
+ }
+
+ /* we process the requests with non-final status */
if (qp->head) {
switch (qp->head->status) {
case CQR_STATUS_QUEUED:
@@ -1764,6 +2163,10 @@
/* just wait */
break;
default:
+ MESSAGE (KERN_EMERG,
+ "invalid cqr (%p) detected with status %02x ",
+ qp->head,
+ qp->head->status);
BUG ();
}
}
@@ -1786,6 +2189,18 @@
}
/*
+ * function dasd_schedule_bh_timed
+ * retriggers the dasd_schedule_bh function (called by timer queue)
+ */
+void
+dasd_schedule_bh_timed (unsigned long device_ptr)
+{
+ dasd_device_t *device = (dasd_device_t *) device_ptr;
+
+ dasd_schedule_bh (device);
+}
+
+/*
* function dasd_schedule_bh
* schedules the request_fn to run with next run_bh cycle
*/
@@ -1842,30 +2257,43 @@
if (device_addr == NULL) {
- printk (KERN_DEBUG PRINTK_HEADER
- "unable to find device for state change pending "
- "interrupt: devno%04x\n",
- stat->devno);
+ MESSAGE (KERN_DEBUG,
+ "unable to find device for state change pending "
+ "interrupt: devno%04x",
+ stat->devno);
return;
}
/* re-activate first request in queue */
cqr = (*device_addr)->queue.head;
+
+ if (cqr == NULL) {
+ MESSAGE (KERN_DEBUG,
+ "got state change pending interrupt on"
+ "idle device: %04x",
+ stat->devno);
+ return;
+ }
if (cqr->status == CQR_STATUS_PENDING) {
- DASD_MESSAGE (KERN_DEBUG, (*device_addr), "%s",
- "device request queue restarted by "
- "state change pending interrupt\n");
+ DEV_MESSAGE (KERN_DEBUG, (*device_addr), "%s",
+ "device request queue restarted by "
+ "state change pending interrupt");
- del_timer (&(*device_addr)->timer);
+ del_timer_sync (&(*device_addr)->blocking_timer);
check_then_set (&cqr->status,
CQR_STATUS_PENDING, CQR_STATUS_QUEUED);
- dasd_schedule_bh (*device_addr);
-
}
+ if (cqr->status == CQR_STATUS_IN_IO) {
+ cqr->status = CQR_STATUS_QUEUED;
+ DEV_MESSAGE (KERN_WARNING, (*device_addr), "%s",
+ "redriving state change pending condition while in IO");
+ }
+
+ dasd_schedule_bh (*device_addr);
} /* end dasd_handle_state_change_pending */
@@ -1879,50 +2307,50 @@
int ip;
ccw_req_t *cqr;
dasd_device_t *device;
- unsigned long long now;
- dasd_era_t era = dasd_era_none; /* default is everything is okay */
+ dasd_era_t era;
devstat_t *stat = (devstat_t *)ds;
if (stat == NULL) {
BUG();
}
- DASD_DRIVER_DEBUG_EVENT (6, dasd_int_handler,
- "Interrupt: IRQ %02x, stat %02x, devno %04x",
- irq,
- stat->dstat,
- stat->devno);
- asm volatile ("STCK %0":"=m" (now));
+
+ DBF_EVENT (DBF_DEBUG,
+ "Int: IRQ %02x, CS/DS %04x, flag %08x, devno %04x, ip %08x",
+ irq,
+ ((stat->cstat<<8)|stat->dstat),
+ stat->flag,
+ stat->devno,
+ stat->intparm);
/* first of all check for state change pending interrupt */
if ((stat->dstat & DEV_STAT_ATTENTION ) &&
(stat->dstat & DEV_STAT_DEV_END ) &&
(stat->dstat & DEV_STAT_UNIT_EXCEP) ) {
- DASD_DRIVER_DEBUG_EVENT (2, dasd_int_handler,
- "State change Interrupt: %04x",
- stat->devno);
+
+ DBF_EVENT (DBF_NOTICE,
+ "State change Interrupt: %04x",
+ stat->devno);
+
dasd_handle_state_change_pending (stat);
return;
}
ip = stat->intparm;
if (!ip) { /* no intparm: unsolicited interrupt */
- DASD_DRIVER_DEBUG_EVENT (2, dasd_int_handler,
- "Unsolicited Interrupt: %04x",
- stat->devno);
- printk (KERN_DEBUG PRINTK_HEADER
- "unsolicited interrupt: irq 0x%x devno %04x\n",
- irq,
- stat->devno);
+
+ MESSAGE (KERN_DEBUG,
+ "unsolicited interrupt: irq 0x%x devno %04x",
+ irq,
+ stat->devno);
return;
}
- if (ip & 0x80000001) {
- DASD_DRIVER_DEBUG_EVENT (2, dasd_int_handler,
- "spurious Interrupt: %04x",
- stat->devno);
- printk (KERN_DEBUG PRINTK_HEADER
- "spurious interrupt: irq 0x%x devno %04x, parm %08x\n",
- irq,
- stat->devno,ip);
+
+ if (ip & 0x80000001) { /* check for invalid 'cqr' address */
+
+ MESSAGE (KERN_DEBUG,
+ "spurious interrupt: irq 0x%x devno %04x, parm %08x",
+ irq,
+ stat->devno,ip);
return;
}
@@ -1930,19 +2358,16 @@
/* check status - the request might have been killed because of dyn dettach */
if (cqr->status != CQR_STATUS_IN_IO) {
- DASD_DRIVER_DEBUG_EVENT (2, dasd_int_handler,
- "invalid status %02x on device %04x",
- cqr->status,
- stat->devno);
-
- printk (KERN_DEBUG PRINTK_HEADER
- "invalid status: irq 0x%x devno %04x, status %02x\n",
- irq,
- stat->devno,
- cqr->status);
+
+ MESSAGE (KERN_DEBUG,
+ "invalid status: irq 0x%x devno %04x, status %02x",
+ irq,
+ stat->devno,
+ cqr->status);
return;
}
+ /* some consistency checks */
device = (dasd_device_t *) cqr->device;
if (device == NULL ||
device != ds-offsetof(dasd_device_t,dev_status)) {
@@ -1955,73 +2380,98 @@
BUG();
}
- /* first of all lets try to find out the appropriate era_action */
- DASD_DEVICE_DEBUG_EVENT (4, device," Int: CS/DS 0x%04x",
- ((stat->cstat<<8)|stat->dstat));
-
/* first of all lets try to find out the appropriate era_action */
- if (stat->flag & DEVSTAT_FLAG_SENSE_AVAIL ||
- stat->dstat & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END)) {
- /* anything abnormal ? */
- if (device->discipline->examine_error == NULL ||
- stat->flag & DEVSTAT_HALT_FUNCTION) {
- era = dasd_era_fatal;
- } else {
- era = device->discipline->examine_error (cqr, stat);
- }
- DASD_DRIVER_DEBUG_EVENT (1, dasd_int_handler," era_code %d",
- era);
- }
- if ( era == dasd_era_none ) {
- check_then_set(&cqr->status,
- CQR_STATUS_IN_IO,
- CQR_STATUS_DONE);
-
- cqr->stopclk=now;
- /* start the next queued request if possible -> fast_io */
- if (cqr->next &&
- cqr->next->status == CQR_STATUS_QUEUED) {
- if (device->discipline->start_IO (cqr->next) != 0) {
- printk (KERN_WARNING PRINTK_HEADER
- "Interrupt fastpath failed!\n");
- }
- }
- } else { /* error */
+ if (stat->flag & DEVSTAT_HALT_FUNCTION) {
+ era = dasd_era_fatal;
+
+ } else if (stat->flag & DEVSTAT_FINAL_STATUS &&
+ stat->dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END) &&
+ stat->cstat == 0) {
+ /* received 'ok' for running IO */
+ era = dasd_era_none;
+
+ } else if (stat->flag & DEVSTAT_FLAG_SENSE_AVAIL) {
+ /* got sense data */
if (cqr->dstat == NULL)
cqr->dstat = kmalloc (sizeof (devstat_t), GFP_ATOMIC);
if (cqr->dstat) {
memcpy (cqr->dstat, stat, sizeof (devstat_t));
} else {
- PRINT_ERR ("no memory for dstat...ignoring\n");
+ MESSAGE (KERN_DEBUG, "%s",
+ "no memory for dstat...ignoring");
}
-
#ifdef ERP_DEBUG
- /* dump sense data */
- if (device->discipline &&
+ if (device->discipline &&
device->discipline->dump_sense ) {
-
+
device->discipline->dump_sense (device,
cqr);
}
#endif
+ if (device->discipline->examine_error == NULL) {
+ era = dasd_era_recover;
+ } else {
+ era = device->discipline->examine_error (cqr, stat);
+ }
- switch (era) {
- case dasd_era_fatal:
- check_then_set (&cqr->status,
- CQR_STATUS_IN_IO,
- CQR_STATUS_FAILED);
+ } else if (stat->flag & DEVSTAT_NOT_OPER) {
+ /* path became offline or similar */
+ /* => retry to see if there are any other pathes available */
+ DEV_MESSAGE (KERN_DEBUG, device, "%s",
+ "Device or a path became not operational while in IO");
+ era = dasd_era_recover;
+
+ } else if (stat->dstat & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END) ||
+ stat->cstat & ~(SCHN_STAT_PCI | SCHN_STAT_INCORR_LEN) ) {
+ /* received device state apart from (channel end & device end) */
+ /* OR any kind of channel check (e.g. IFCC, DATA_CHECK or ..... */
+ /* we got no sense data, therefore we just retry */
+ DEV_MESSAGE (KERN_DEBUG, device,
+ "Status without sense (IFCC,...) CS/DS %04x flag %08x",
+ ((stat->cstat<<8)|stat->dstat),
+ stat->flag);
+ era = dasd_era_recover;
- cqr->stopclk = now;
- break;
- case dasd_era_recover:
- check_then_set (&cqr->status,
- CQR_STATUS_IN_IO,
- CQR_STATUS_ERROR);
- break;
- default:
- BUG ();
+ } else {
+ /* any other kind of interrupt - just retry */
+ DEV_MESSAGE (KERN_DEBUG, device,
+ "Got unclassified interrupt CS/DS %04x flag %08x",
+ ((stat->cstat<<8)|stat->dstat),
+ stat->flag);
+ era = dasd_era_recover;
+ }
+
+ switch (era) {
+ case dasd_era_none:
+ check_then_set(&cqr->status,
+ CQR_STATUS_IN_IO,
+ CQR_STATUS_DONE);
+ cqr->stopclk = get_clock ();
+ /* start the next queued request if possible -> fast_io */
+ if (cqr->next &&
+ cqr->next->status == CQR_STATUS_QUEUED) {
+ if (device->discipline->start_IO (cqr->next) != 0) {
+ MESSAGE (KERN_WARNING, "%s",
+ "Interrupt fastpath failed!");
+ }
}
- }
+ break;
+ case dasd_era_fatal:
+ check_then_set (&cqr->status,
+ CQR_STATUS_IN_IO,
+ CQR_STATUS_FAILED);
+ cqr->stopclk = get_clock ();
+ break;
+ case dasd_era_recover:
+ check_then_set (&cqr->status,
+ CQR_STATUS_IN_IO,
+ CQR_STATUS_ERROR);
+ break;
+ default:
+ BUG ();
+ }
+
+ /* handle special device initialization request */
if ( cqr == device->init_cqr &&
( cqr->status == CQR_STATUS_DONE ||
cqr->status == CQR_STATUS_FAILED )){
@@ -2033,59 +2483,47 @@
} /* end dasd_int_handler */
-/* SECTION: Some stuff related to error recovery */
+/********************************************************************************
+ * SECTION: Some stuff related to error recovery
+ ********************************************************************************/
/*
* DEFAULT_ERP_ACTION
*
* DESCRIPTION
- * sets up the default-ERP ccw_req_t, namely one, which performs a TIC
- * to the original channel program with a retry counter of 16
+ * just retries the current cqr
*
* PARAMETER
* cqr failed CQR
*
* RETURN VALUES
- * erp CQR performing the ERP
+ * cqr modified CQR
*/
ccw_req_t *
dasd_default_erp_action (ccw_req_t * cqr)
{
dasd_device_t *device = cqr->device;
- ccw_req_t *erp = dasd_alloc_request ((char *) &cqr->magic, 1, 0, cqr->device);
-
- printk (KERN_DEBUG PRINTK_HEADER "Default ERP called... \n");
-
- if (!erp) {
-
- DASD_MESSAGE (KERN_ERR, device, "%s",
- "Unable to allocate ERP request");
-
+ // just retry - there is nothing to save ... I got no sense data....
+ if (cqr->retries > 0) {
+ DEV_MESSAGE (KERN_DEBUG, device,
+ "default ERP called (%i retries left)",
+ cqr->retries);
+
check_then_set (&cqr->status,
CQR_STATUS_ERROR,
- CQR_STATUS_FAILED);
-
- asm volatile ("STCK %0":"=m" (cqr->stopclk));
-
- return cqr;
- }
-
- erp->cpaddr->cmd_code = CCW_CMD_TIC;
- erp->cpaddr->cda = (__u32) (addr_t) cqr->cpaddr;
- erp->function = dasd_default_erp_action;
- erp->refers = cqr;
- erp->device = cqr->device;
- erp->magic = cqr->magic;
- erp->retries = 16;
-
- erp->status = CQR_STATUS_FILLED;
-
- dasd_chanq_enq_head (&device->queue,
- erp);
-
- return erp;
-
+ CQR_STATUS_QUEUED);
+ } else {
+ DEV_MESSAGE (KERN_WARNING, device, "%s",
+ "default ERP called (NO retry left)");
+
+ check_then_set (&cqr->status,
+ CQR_STATUS_ERROR,
+ CQR_STATUS_FAILED);
+
+ cqr->stopclk = get_clock ();
+ }
+ return cqr;
} /* end dasd_default_erp_action */
/*
@@ -2141,26 +2579,21 @@
/* save ptr to original cqr */
cqr = erp;
- /* set corresponding status to original cqr */
+ /* set corresponding status for original cqr */
if (success) {
-
- check_then_set (&cqr->status,
- CQR_STATUS_ERROR,
- CQR_STATUS_DONE);
+ cqr->status = CQR_STATUS_DONE;
} else {
-
- check_then_set (&cqr->status,
- CQR_STATUS_ERROR,
- CQR_STATUS_FAILED);
-
- asm volatile ("STCK %0":"=m" (cqr->stopclk));
+ cqr->status = CQR_STATUS_FAILED;
+ cqr->stopclk = get_clock ();
}
return cqr;
} /* end default_erp_postaction */
-/* SECTION: The helpers of the struct file_operations */
+/********************************************************************************
+ * SECTION: The helpers of the struct file_operations
+ ********************************************************************************/
/*
* function dasd_format
@@ -2175,39 +2608,41 @@
{
int rc = 0;
int openct = atomic_read (&device->open_count);
+ ccw_req_t *req;
if (openct > 1) {
- DASD_MESSAGE (KERN_WARNING, device, "%s",
- "dasd_format: device is open! expect errors.");
+
+ DEV_MESSAGE (KERN_WARNING, device, "%s",
+ "dasd_format: device is open! "
+ "expect errors.");
}
- DASD_MESSAGE (KERN_INFO, device,
- "formatting units %d to %d (%d B blocks) flags %d",
- fdata->start_unit,
- fdata->stop_unit,
- fdata->blksize,
- fdata->intensity);
+
+ DBF_DEV_EVENT (DBF_NOTICE, device,
+ "formatting units %d to %d (%d B blocks) flags %d",
+ fdata->start_unit,
+ fdata->stop_unit,
+ fdata->blksize,
+ fdata->intensity);
+
while ((!rc) && (fdata->start_unit <= fdata->stop_unit)) {
- ccw_req_t *req;
- dasd_format_fn_t ffn = device->discipline->format_device;
- ffn = device->discipline->format_device;
- if (ffn == NULL)
+
+ if (device->discipline->format_device == NULL)
break;
- req = ffn (device, fdata);
+
+ req = device->discipline->format_device (device, fdata);
if (req == NULL) {
rc = -ENOMEM;
break;
}
if ((rc = dasd_sleep_on_req (req)) != 0) {
- DASD_MESSAGE (KERN_WARNING, device,
- " Formatting of unit %d failed with rc = %d\n",
- fdata->start_unit, rc);
+
+ DEV_MESSAGE (KERN_WARNING, device,
+ " Formatting of unit %d failed "
+ "with rc = %d",
+ fdata->start_unit, rc);
break;
}
dasd_free_request (req, device); /* request is no longer used */
- if ( signal_pending(current) ) {
- rc = -ERESTARTSYS;
- break;
- }
fdata->start_unit++;
}
return rc;
@@ -2258,6 +2693,9 @@
return 0;
}
+/*
+ * handle the re-read partition table IOCTL (BLKRRPART)
+ */
static int
dasd_revalidate (dasd_device_t * device)
{
@@ -2267,8 +2705,9 @@
int openct = atomic_read (&device->open_count);
int start = MINOR (kdev);
if (openct != 1) {
- DASD_MESSAGE (KERN_WARNING, device, "%s",
- "BLKRRPART: device is open! expect errors.");
+
+ DEV_MESSAGE (KERN_WARNING, device, "%s",
+ "BLKRRPART: device is open! expect errors.");
}
for (i = (1 << DASD_PARTN_BITS) - 1; i >= 0; i--) {
int major = device->major_info->gendisk.major;
@@ -2279,6 +2718,15 @@
return rc;
}
+
+/*
+ * function do_dasd_ioctl
+ * Implementation of the DASD API.
+ * Changes to the API should be binary compatible to privous versions
+ * of the user-space applications by means of any already existing tool
+ * (e.g. dasdfmt) must work with the new kernel API.
+ */
+
static int
do_dasd_ioctl (struct inode *inp, /* unsigned */ int no, unsigned long data)
{
@@ -2287,10 +2735,12 @@
major_info_t *major_info;
if (!device) {
- printk (KERN_WARNING PRINTK_HEADER
- "No device registered as device (%d:%d)\n",
- MAJOR (inp->i_rdev),
- MINOR (inp->i_rdev));
+
+ MESSAGE (KERN_WARNING,
+ "No device registered as device (%d:%d)",
+ MAJOR (inp->i_rdev),
+ MINOR (inp->i_rdev));
+
return -EINVAL;
}
if ((_IOC_DIR (no) != _IOC_NONE) && (data == 0)) {
@@ -2298,225 +2748,356 @@
return -EINVAL;
}
major_info = device->major_info;
-#if 0
- printk (KERN_DEBUG PRINTK_HEADER
- "ioctl 0x%08x %s'0x%x'%d(%d) on /dev/%s (%d:%d,"
- " devno 0x%04x on irq %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),
- device->name,
- MAJOR (inp->i_rdev),
- MINOR (inp->i_rdev),
- device->devinfo.devno,
- device->devinfo.irq,
- data);
-#endif
+
+ DBF_DEV_EVENT (DBF_DEBUG, device,
+ "ioctl 0x%08x %s'0x%x'%d(%d) with data %8lx",
+ 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),
+ data);
+
switch (no) {
- case DASDAPIVER: {
- int ver = DASD_API_VERSION;
- rc = put_user(ver, (int *) data);
- break;
+ case DASDAPIVER: { /* retrun dasd API version */
+ int ver = DASD_API_VERSION;
+ rc = put_user(ver, (int *) data);
+ break;
+ }
+ case BLKGETSIZE: { /* Return device size in # of sectors */
+ long blocks = major_info->gendisk.sizes
+ [MINOR (inp->i_rdev)] << 1;
+ rc = put_user(blocks, (long *) data);
+ break;
+ }
+ case BLKGETSIZE64:{
+ u64 blocks = major_info->gendisk.sizes
+ [MINOR (inp->i_rdev)];
+ rc = put_user(blocks << 10, (u64 *) data);
+ break;
+ }
+ case BLKRRPART: { /* reread partition table */
+ if (!capable (CAP_SYS_ADMIN)) {
+ rc = -EACCES;
+ break;
+ }
+ rc = dasd_revalidate (device);
+ break;
}
- case BLKGETSIZE:{ /* Return device size */
- long blocks = major_info->gendisk.sizes
- [MINOR (inp->i_rdev)] << 1;
- rc = put_user(blocks, (long *) data);
- break;
- }
- case BLKGETSIZE64:{
- u64 blocks = major_info->gendisk.sizes
- [MINOR (inp->i_rdev)];
- rc = put_user(blocks << 10, (u64 *) data);
- break;
- }
- case BLKRRPART:{
- if (!capable (CAP_SYS_ADMIN)) {
- rc = -EACCES;
- break;
- }
- rc = dasd_revalidate (device);
- break;
- }
- case HDIO_GETGEO:{
- struct hd_geometry geo = { 0, };
- rc = dasd_fillgeo (inp->i_rdev, &geo);
- if (rc)
- break;
+ case HDIO_GETGEO: { /* return disk geometry */
+ struct hd_geometry geo = { 0, };
+ rc = dasd_fillgeo (inp->i_rdev, &geo);
+ if (rc)
+ break;
- rc = copy_to_user ((struct hd_geometry *) data, &geo,
- sizeof (struct hd_geometry));
- if (rc)
- rc = -EFAULT;
- break;
- }
- case BIODASDDISABLE:{
- if (!capable (CAP_SYS_ADMIN)) {
- rc = -EACCES;
- break;
- }
- if ( device->level > DASD_STATE_ACCEPT) {
- dasd_deactivate_queue(device);
- if ( device->request_queue)
- dasd_flush_request_queues(device,0);
- dasd_flush_chanq(device,0);
- dasd_disable_blkdev(device);
- dasd_set_device_level (device->devinfo.devno,
- device->discipline,
- DASD_STATE_ACCEPT);
- }
+ rc = copy_to_user ((struct hd_geometry *) data, &geo,
+ sizeof (struct hd_geometry));
+ if (rc)
+ rc = -EFAULT;
+ break;
+ }
+ case BIODASDDISABLE: { /* disable device */
+ if (!capable (CAP_SYS_ADMIN)) {
+ rc = -EACCES;
break;
+ }
+
+ if ( device->level > DASD_STATE_ACCEPT) {
+ dasd_deactivate_queue(device);
+ if ( device->request_queue)
+ dasd_flush_request_queues(device,0);
+ dasd_flush_chanq(device,0);
+ dasd_disable_blkdev(device);
+ dasd_set_device_level (device->devinfo.devno,
+ device->discipline,
+ DASD_STATE_ACCEPT);
+ }
+
+ break;
}
- case BIODASDENABLE:{
- dasd_range_t range = {
- from: device->devinfo.devno,
- to: device->devinfo.devno
- };
- if (!capable (CAP_SYS_ADMIN)) {
- rc = -EACCES;
- break;
- }
- dasd_enable_ranges (&range, device->discipline, 0);
+ case BIODASDENABLE: { /* enable device */
+ dasd_range_t range = {
+ from: device->devinfo.devno,
+ to: device->devinfo.devno
+ };
+ if (!capable (CAP_SYS_ADMIN)) {
+ rc = -EACCES;
break;
+ }
+ dasd_enable_ranges (&range, device->discipline, 0);
+ break;
}
- case BIODASDFMT:{
- /* fdata == NULL is no longer a valid arg to dasd_format ! */
- int partn = MINOR (inp->i_rdev) &
- ((1 << major_info->gendisk.minor_shift) - 1);
- format_data_t fdata;
+ case BIODASDFMT: { /* format device */
+ /* fdata == NULL is no longer a valid arg to dasd_format ! */
+ int partn = MINOR (inp->i_rdev) &
+ ((1 << major_info->gendisk.minor_shift) - 1);
+ format_data_t fdata;
+
+ if (!capable (CAP_SYS_ADMIN)) {
+ rc = -EACCES;
+ break;
+ }
+ if (dasd_features_from_devno(device->devinfo.devno)&DASD_FEATURE_READONLY) {
+ rc = -EROFS;
+ break;
+ }
+ if (!data) {
+ rc = -EINVAL;
+ break;
+ }
+ rc = copy_from_user (&fdata, (void *) data,
+ sizeof (format_data_t));
+ if (rc) {
+ rc = -EFAULT;
+ break;
+ }
+ if (partn != 0) {
- if (!capable (CAP_SYS_ADMIN)) {
- rc = -EACCES;
- break;
- }
- if (dasd_features_from_devno(device->devinfo.devno)&DASD_FEATURE_READONLY) {
- rc = -EROFS;
- break;
+ DEV_MESSAGE (KERN_WARNING, device, "%s",
+ "Cannot low-level format a partition");
+
+ return -EINVAL;
+ }
+ rc = dasd_format (device, &fdata);
+ break;
+ }
+ case BIODASDSATTR: { /* Set Attributes (cache operations) */
+
+ attrib_data_t attrib;
+
+ if (!capable (CAP_SYS_ADMIN)) {
+ rc = -EACCES;
+ break;
+ }
+
+ if (!data) {
+ rc = -EINVAL;
+ break;
+ }
+
+ if (!device->discipline->set_attrib) {
+ rc = -EINVAL;
+ break;
+ }
+
+ rc = copy_from_user (&attrib, (void *) data,
+ sizeof (attrib_data_t));
+ if (rc) {
+ rc = -EFAULT;
+ break;
+ }
+
+ rc = device->discipline->set_attrib (device,
+ &attrib);
+ break;
+ }
+ case BIODASDPRRST: { /* reset device profile information */
+ if (!capable (CAP_SYS_ADMIN)) {
+ rc = -EACCES;
+ break;
+ }
+ memset (&device->profile, 0,
+ sizeof (dasd_profile_info_t));
+ break;
+ }
+ case BIODASDPRRD: { /* return device profile information */
+ rc = copy_to_user((long *)data,
+ (long *)&device->profile,
+ sizeof(dasd_profile_info_t));
+ if (rc)
+ rc = -EFAULT;
+ break;
+ }
+ case BIODASDRSRV: { /* reserve device */
+ ccw_req_t *cqr;
+ if (!capable (CAP_SYS_ADMIN)) {
+ rc = -EACCES;
+ break;
+ }
+
+ if (!device->discipline->reserve) {
+ rc = -EINVAL;
+ break;
+ }
+
+ cqr = device->discipline->reserve (device);
+
+ if (cqr) {
+ struct timer_list res_timer;
+
+ init_timer (&res_timer);
+ res_timer.function = dasd_resrel_timeout;
+ res_timer.data = (unsigned long) cqr;
+ res_timer.expires = jiffies + 2 * HZ;
+ add_timer (&res_timer);
+
+ rc = dasd_sleep_on_immediate (cqr);
+
+ del_timer_sync (&res_timer);
+ dasd_free_request (cqr,
+ device);
+ } else {
+ rc = -ENOMEM;
+ }
+ break;
+ }
+ case BIODASDRLSE: { /* release device */
+ ccw_req_t *cqr;
+ if (!capable (CAP_SYS_ADMIN)) {
+ rc = -EACCES;
+ break;
+ }
+
+ if (!device->discipline->release) {
+ rc = -EINVAL;
+ break;
+ }
+
+ cqr = device->discipline->release (device);
+
+ if (cqr) {
+ struct timer_list rel_timer;
+
+ init_timer (&rel_timer);
+ rel_timer.function = dasd_resrel_timeout;
+ rel_timer.data = (unsigned long) cqr;
+ rel_timer.expires = jiffies + 2 * HZ;
+ add_timer (&rel_timer);
+
+ rc = dasd_sleep_on_immediate (cqr);
+
+ del_timer_sync (&rel_timer); /* in case of interrupt */
+ dasd_free_request (cqr,
+ device);
+ } else {
+ rc = -ENOMEM;
+ }
+ break;
+ }
+ case BIODASDSLCK: { /* steal lock - unconditional reserve device */
+ if (!capable (CAP_SYS_ADMIN)) {
+ rc = -EACCES;
+ break;
+ }
+
+ rc = dasd_steal_lock (device);
+ break;
+ }
+ case BIODASDINFO: /* return dasd information */
+ case BIODASDINFO2: { /* return dasd information2 (incl. format and features) */
+ dasd_information2_t dasd_info;
+
+ unsigned long flags;
+
+ if (!device->discipline->fill_info) {
+ rc = -EINVAL;
+ break;
+ }
+
+ rc = device->discipline->fill_info (device,
+ &dasd_info);
+
+ dasd_info.label_block = device->sizes.pt_block;
+ dasd_info.devno = device->devinfo.devno;
+ dasd_info.schid = device->devinfo.irq;
+ dasd_info.cu_type = device->devinfo.sid_data.cu_type;
+ dasd_info.cu_model = device->devinfo.sid_data.cu_model;
+ dasd_info.dev_type = device->devinfo.sid_data.dev_type;
+ dasd_info.dev_model = device->devinfo.sid_data.dev_model;
+ dasd_info.open_count =
+ atomic_read (&device->open_count);
+ dasd_info.status = device->level;
+
+ /* check if device is really formatted - LDL / CDL was returned by 'fill_info' */
+ if ((device->level < DASD_STATE_READY) ||
+ (dasd_check_bp_block (device) ) ) {
+ dasd_info.format = DASD_FORMAT_NONE;
+ }
+
+ dasd_info.features =
+ dasd_features_from_devno (device->devinfo.devno);
+
+ if (device->discipline) {
+ memcpy (dasd_info.type,
+ device->discipline->name, 4);
+ } else {
+ memcpy (dasd_info.type, "none", 4);
+ }
+ dasd_info.req_queue_len = 0;
+ dasd_info.chanq_len = 0;
+
+ if ((device->request_queue ) &&
+ (device->request_queue->request_fn) ) {
+ struct list_head *l;
+ ccw_req_t *cqr = device->queue.head;
+ spin_lock_irqsave (&io_request_lock, flags);
+ list_for_each (l,
+ &device->request_queue->
+ queue_head) {
+ dasd_info.req_queue_len++;
}
- if (!data) {
- rc = -EINVAL;
- break;
- }
- rc = copy_from_user (&fdata, (void *) data,
- sizeof (format_data_t));
- if (rc) {
- rc = -EFAULT;
- break;
- }
- if (partn != 0) {
- DASD_MESSAGE (KERN_WARNING, device, "%s",
- "Cannot low-level format a partition");
- return -EINVAL;
- }
- rc = dasd_format (device, &fdata);
- break;
- }
- case BIODASDPRRST:{ /* reset device profile information */
- if (!capable (CAP_SYS_ADMIN)) {
- rc = -EACCES;
- break;
- }
- memset (&device->profile, 0,
- sizeof (dasd_profile_info_t));
- break;
- }
- case BIODASDPRRD:{ /* retrun device profile information */
- rc = copy_to_user((long *)data,
- (long *)&device->profile,
- sizeof(dasd_profile_info_t));
- if (rc)
- rc = -EFAULT;
- break;
- }
- case BIODASDRSRV:{ /* reserve */
- ccw_req_t *req;
- if (!capable (CAP_SYS_ADMIN)) {
- rc = -EACCES;
- break;
- }
- req = device->discipline->reserve (device);
- rc = dasd_sleep_on_req (req);
- dasd_free_request (req, device);
- break;
- }
- case BIODASDRLSE:{ /* release */
- ccw_req_t *req;
- if (!capable (CAP_SYS_ADMIN)) {
- rc = -EACCES;
- break;
- }
- req = device->discipline->release (device);
- rc = dasd_sleep_on_req (req);
- dasd_free_request (req, device);
- break;
- }
- case BIODASDSLCK:{ /* steal lock - unconditional reserve */
- ccw_req_t *req;
- if (!capable (CAP_SYS_ADMIN)) {
- rc = -EACCES;
- break;
- }
- req = device->discipline->steal_lock (device);
- rc = dasd_sleep_on_req (req);
- dasd_free_request (req, device);
- break;
- }
- case BIODASDINFO:{
- dasd_information_t dasd_info;
- unsigned long flags;
- rc = device->discipline->fill_info (device, &dasd_info);
- dasd_info.label_block = device->sizes.pt_block;
- dasd_info.devno = device->devinfo.devno;
- dasd_info.schid = device->devinfo.irq;
- dasd_info.cu_type = device->devinfo.sid_data.cu_type;
- dasd_info.cu_model = device->devinfo.sid_data.cu_model;
- dasd_info.dev_type = device->devinfo.sid_data.dev_type;
- dasd_info.dev_model = device->devinfo.sid_data.dev_model;
- dasd_info.open_count =
- atomic_read (&device->open_count);
- dasd_info.status = device->level;
- if (device->discipline) {
- memcpy (dasd_info.type,
- device->discipline->name, 4);
- } else {
- memcpy (dasd_info.type, "none", 4);
- }
- dasd_info.req_queue_len = 0;
- dasd_info.chanq_len = 0;
- if (device->request_queue->request_fn) {
- struct list_head *l;
- ccw_req_t *cqr = device->queue.head;
- spin_lock_irqsave (&io_request_lock, flags);
- list_for_each (l,
- &device->request_queue->
- queue_head) {
- dasd_info.req_queue_len++;
- }
- spin_unlock_irqrestore (&io_request_lock,
- flags);
- s390irq_spin_lock_irqsave (device->devinfo.irq,
- flags);
- while (cqr) {
- cqr = cqr->next;
- dasd_info.chanq_len++;
- }
- s390irq_spin_unlock_irqrestore (device->devinfo.
- irq, flags);
- }
- rc =
- copy_to_user ((long *) data, (long *) &dasd_info,
- sizeof (dasd_information_t));
- if (rc)
- rc = -EFAULT;
- break;
- }
+ spin_unlock_irqrestore (&io_request_lock,
+ flags);
+ s390irq_spin_lock_irqsave (device->devinfo.irq,
+ flags);
+ while (cqr) {
+ cqr = cqr->next;
+ dasd_info.chanq_len++;
+ }
+ s390irq_spin_unlock_irqrestore (device->devinfo.
+ irq, flags);
+ }
+
+ rc = copy_to_user ((long *) data, (long *) &dasd_info,
+ ((no == (unsigned int) BIODASDINFO2) ?
+ sizeof (dasd_information2_t) :
+ sizeof (dasd_information_t)));
+
+ if (rc)
+ rc = -EFAULT;
+ break;
+ }
+ case BIODASDPSRD: { /* Performance Statistics Read */
+
+ ccw_req_t *cqr;
+ dasd_rssd_perf_stats_t *stats;
+
+ if ((!device->discipline->read_stats) ||
+ (!device->discipline->ret_stats ) ) {
+ rc = -EINVAL;
+ break;
+ }
+
+ cqr = device->discipline->read_stats (device);
+
+ if (cqr) {
+
+ if ((rc = dasd_sleep_on_req (cqr)) == 0) {
+
+ if ((stats = device->discipline->ret_stats (cqr)) != NULL) {
+
+ rc = copy_to_user ((long *) data,
+ (long *) stats,
+ sizeof (dasd_rssd_perf_stats_t));
+ } else {
+
+ rc = -EFAULT;
+ }
+ }
+
+ dasd_free_request (cqr,
+ device);
+
+ } else {
+ rc = -ENOMEM;
+ }
+ break;
+ }
#if 0 /* needed for XFS */
- case BLKBSZSET:{
+ case BLKBSZSET: {
int bsz;
rc = copy_from_user ((long *)&bsz,(long *)data,sizeof(int));
if ( rc ) {
@@ -2528,10 +3109,46 @@
rc = -EINVAL;
}
break;
- }
+ }
#endif /* 0 */
+ case BLKROSET: {
+ int intval;
+ dasd_range_t *temp;
+ int devindex = 0;
+ unsigned long flags;
+ struct list_head *l;
+ int major=MAJOR(device->kdev);
+ int minor;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EACCES;
+ if (inp->i_rdev != device->kdev)
+ // ro setting is not allowed for partitions
+ return -EINVAL;
+ if (get_user(intval, (int *)(data)))
+ return -EFAULT;
+ spin_lock_irqsave (&range_lock, flags);
+ list_for_each (l, &dasd_range_head.list) {
+ temp = list_entry (l, dasd_range_t, list);
+ if (device->devinfo.devno >= temp->from && device->devinfo.devno <= temp->to) {
+ spin_unlock_irqrestore (&range_lock, flags);
+ if (intval)
+ temp->features |= DASD_FEATURE_READONLY;
+ else
+ temp->features &= ~DASD_FEATURE_READONLY;
+ goto continue_blkroset;
+ }
+ devindex += temp->to - temp->from + 1;
+ }
+ spin_unlock_irqrestore (&range_lock, flags);
+ return(-ENODEV);
+continue_blkroset:
+ for (minor = MINOR(device->kdev); minor < MINOR(device->kdev) + (1 << DASD_PARTN_BITS); minor++)
+ set_device_ro(MKDEV(major,minor), intval);
+ return 0;
+ }
+ case BLKBSZGET:
case BLKSSZGET:
- case BLKROSET:
case BLKROGET:
case BLKRASET:
case BLKRAGET:
@@ -2541,37 +3158,41 @@
case BLKELVSET:
return blk_ioctl (inp->i_rdev, no, data);
break;
- default:{
+ default: {
- dasd_ioctl_list_t *old = dasd_find_ioctl (no);
- if (old) {
- if ( old->owner )
- __MOD_INC_USE_COUNT(old->owner);
- rc = old->handler (inp, no, data);
- if ( old->owner )
- __MOD_DEC_USE_COUNT(old->owner);
- } else {
- DASD_MESSAGE (KERN_INFO, device,
- "ioctl 0x%08x=%s'0x%x'%d(%d) 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),
- data);
- rc = -ENOTTY;
- }
- break;
+ dasd_ioctl_list_t *old = dasd_find_ioctl (no);
+ if (old) {
+ if ( old->owner )
+ __MOD_INC_USE_COUNT(old->owner);
+ rc = old->handler (inp, no, data);
+ if ( old->owner )
+ __MOD_DEC_USE_COUNT(old->owner);
+ } else {
+
+ DBF_DEV_EVENT (DBF_INFO, device,
+ "unknown ioctl 0x%08x=%s'0x%x'%d(%d) data %8lx",
+ 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),
+ data);
+
+ rc = -ENOTTY;
+ }
+ break;
}
}
return rc;
}
-/* SECTION: The members of the struct file_operations */
+/********************************************************************************
+ * SECTION: The members of the struct file_operations
+ ********************************************************************************/
static int
dasd_ioctl (struct inode *inp, struct file *filp,
@@ -2597,26 +3218,32 @@
goto fail;
}
if (dasd_probeonly) {
- printk ("\n" KERN_INFO PRINTK_HEADER
- "No access to device (%d:%d) due to probeonly mode\n",
- MAJOR (inp->i_rdev),
- MINOR (inp->i_rdev));
+
+ MESSAGE (KERN_INFO,
+ "No access to device (%d:%d) due to probeonly mode",
+ MAJOR (inp->i_rdev),
+ MINOR (inp->i_rdev));
+
rc = -EPERM;
goto fail;
}
spin_lock_irqsave(&discipline_lock,flags);
device = dasd_device_from_kdev (inp->i_rdev);
if (!device) {
- printk (KERN_WARNING PRINTK_HEADER
- "No device registered as (%d:%d)\n",
- MAJOR (inp->i_rdev),
- MINOR (inp->i_rdev));
+
+ MESSAGE (KERN_WARNING,
+ "No device registered as (%d:%d)",
+ MAJOR (inp->i_rdev),
+ MINOR (inp->i_rdev));
+
rc = -ENODEV;
goto unlock;
}
if (device->level <= DASD_STATE_ACCEPT ) {
- DASD_MESSAGE (KERN_WARNING, device, " %s",
- " Cannot open unrecognized device\n");
+
+ DBF_DEV_EVENT (DBF_ERR, device, " %s",
+ " Cannot open unrecognized device");
+
rc = -ENODEV;
goto unlock;
}
@@ -2648,17 +3275,21 @@
}
device = dasd_device_from_kdev (inp->i_rdev);
if (!device) {
- printk (KERN_WARNING PRINTK_HEADER
- "No device registered as %d:%d\n",
- MAJOR (inp->i_rdev),
- MINOR (inp->i_rdev));
+
+ MESSAGE (KERN_WARNING,
+ "No device registered as %d:%d",
+ MAJOR (inp->i_rdev),
+ MINOR (inp->i_rdev));
+
rc = -EINVAL;
goto out;
}
if (device->level < DASD_STATE_ACCEPT ) {
- DASD_MESSAGE (KERN_WARNING, device, " %s",
- " Cannot release unrecognized device\n");
+
+ DBF_DEV_EVENT (DBF_ERR, device, " %s",
+ " Cannot release unrecognized device");
+
rc = -ENODEV;
goto out;
}
@@ -2669,8 +3300,9 @@
__MOD_DEC_USE_COUNT(device->discipline->owner);
} else if ( count == -1 ) { /* paranoia only */
atomic_set (&device->open_count,0);
- printk (KERN_WARNING PRINTK_HEADER
- "release called with open count==0\n");
+
+ MESSAGE (KERN_WARNING, "%s",
+ "release called with open count==0");
}
out:
return rc;
@@ -2685,7 +3317,9 @@
ioctl:dasd_ioctl,
};
-/* SECTION: Management of device list */
+/********************************************************************************
+ * SECTION: Management of device list
+ ********************************************************************************/
int
dasd_fillgeo(int kdev,struct hd_geometry *geo)
{
@@ -2708,24 +3342,27 @@
int
dasd_device_name (char *str, int index, int partition, struct gendisk *hd)
{
- int len = 0;
+ major_info_t *major_info;
+ struct list_head *l;
char first, second, third;
+ int len;
- if (hd) {
- major_info_t *major_info = NULL;
- struct list_head *l;
-
- list_for_each (l, &dasd_major_info[0].list) {
- major_info = list_entry (l, major_info_t, list);
- if (&major_info->gendisk == hd) {
- break;
- }
- index += DASD_PER_MAJOR;
- }
- if (major_info == &dasd_major_info[0]) {
- return -EINVAL;
- }
- }
+ if (hd == NULL)
+ return -EINVAL;
+
+ major_info = NULL;
+ list_for_each (l, &dasd_major_info) {
+ major_info = list_entry (l, major_info_t, list);
+ if (&major_info->gendisk == hd)
+ break;
+ index += DASD_PER_MAJOR;
+ }
+ if (major_info == NULL || &major_info->gendisk != hd) {
+ /* list empty or hd not found in list */
+ return -EINVAL;
+ }
+
+ len = 0;
third = index % 26;
second = ((index - 26) / 26) % 26;
first = (((index - 702) / 26) / 26) % 26;
@@ -2777,7 +3414,8 @@
cqr->status != CQR_STATUS_FAILED ) {
cqr->status = CQR_STATUS_FAILED;
- asm volatile ("STCK %0":"=m" (cqr->stopclk));
+
+ cqr->stopclk = get_clock ();
}
dasd_schedule_bh(device);
@@ -2802,6 +3440,56 @@
}
}
+static inline void dasd_do_hotplug_event (dasd_device_t* device, int eventid) {
+#ifdef CONFIG_HOTPLUG
+ int i;
+ char *argv[3], *envp[8];
+ char devno[20],major[20],minor[20],devname[26],action[20];
+
+ /* setup command line arguments */
+ i=0;
+ argv[i++] = hotplug_path;
+ argv[i++] = "dasd";
+ argv[i++] = 0;
+
+ /* minimal environment */
+ i=0;
+ envp[i++] = "HOME=/";
+ envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+
+ /* device information and event*/
+ sprintf (devno, "DEVNO=%04x", device->devinfo.devno);
+ sprintf (major, "MAJOR=%d", MAJOR(device->kdev));
+ sprintf (minor, "MINOR=%d", MINOR(device->kdev));
+ sprintf (devname, "DASDNAME=%s",device->name);
+ switch (eventid) {
+ case DASD_HOTPLUG_EVENT_ADD:
+ sprintf (action,"ACTION=add");
+ break;
+ case DASD_HOTPLUG_EVENT_REMOVE:
+ sprintf (action,"ACTION=remove");
+ break;
+ case DASD_HOTPLUG_EVENT_PARTCHK:
+ sprintf (action,"ACTION=partchk");
+ break;
+ case DASD_HOTPLUG_EVENT_PARTREMOVE:
+ sprintf (action,"ACTION=partremove");
+ break;
+ default:
+ BUG();
+ }
+ envp[i++] = devno;
+ envp[i++] = major;
+ envp[i++] = minor;
+ envp[i++] = devname;
+ envp[i++] = action;
+ envp[i++] = 0;
+
+ call_usermodehelper (argv [0], argv, envp);
+#endif
+}
+
+
static int
dasd_disable_volume ( dasd_device_t * device, int force )
{
@@ -2810,8 +3498,9 @@
int count = atomic_read (&device->open_count);
if ( count ) {
- DASD_MESSAGE (KERN_EMERG, device, "%s",
- "device has vanished although it was open!");
+
+ DEV_MESSAGE (KERN_EMERG, device, "%s",
+ "device has vanished although it was open!");
}
if ( force ) {
dasd_deactivate_queue(device);
@@ -2824,9 +3513,11 @@
/* unregister partitions ('ungrok_partitions') */
devfs_register_partitions(&device->major_info->gendisk,
MINOR(device->kdev),1);
+ dasd_do_hotplug_event (device, DASD_HOTPLUG_EVENT_PARTREMOVE);
- DASD_MESSAGE (KERN_WARNING, device,
- "disabling device, target state: %d",target);
+ DBF_DEV_EVENT (DBF_ERR, device,
+ "disabling device, target state: %d",
+ target);
dasd_set_device_level (device->devinfo.devno,
device->discipline,
@@ -2836,7 +3527,7 @@
static void
dasd_disable_ranges (dasd_range_t *range,
- dasd_discipline_t *d,
+ dasd_discipline_t *discipline,
int all, int force )
{
dasd_range_t *rrange;
@@ -2858,14 +3549,15 @@
}
device = *dptr;
if (device == NULL ||
- (d != NULL &&
- device -> discipline != d))
+ (discipline != NULL &&
+ device -> discipline != discipline))
continue;
dasd_disable_volume(device, force);
}
rrange = list_entry (rrange->list.next, dasd_range_t, list);
} while ( all && rrange && rrange != range );
+
}
static void
@@ -2877,10 +3569,13 @@
}
static void
-dasd_enable_ranges (dasd_range_t *range, dasd_discipline_t *d, int all )
+dasd_enable_ranges (dasd_range_t *range,
+ dasd_discipline_t *discipline,
+ int all)
{
int retries = 0;
int j;
+ int do_again;
kdev_t tempdev;
dasd_range_t *rrange;
@@ -2888,6 +3583,7 @@
return;
do {
+ do_again = 0;
if (range == &dasd_range_head) {
rrange = list_entry (range->list.next,
dasd_range_t, list);
@@ -2898,26 +3594,40 @@
for (j = rrange->from; j <= rrange->to; j++) {
if ( dasd_devindex_from_devno(j) < 0 )
continue;
- dasd_set_device_level (j, d, DASD_STATE_ONLINE);
+ if (-EAGAIN == dasd_set_device_level
+ (j, discipline, DASD_STATE_ONLINE))
+ do_again = 1;
}
rrange = list_entry (rrange->list.next, dasd_range_t, list);
} while ( all && rrange && rrange != range );
- if (atomic_read (&dasd_init_pending) == 0) /* we are done, exit loop */
+ if ((atomic_read (&dasd_init_pending) == 0) &&
+ (!do_again)) /* we are done, exit loop */
break;
if ( retries == 0 ) {
- printk (KERN_INFO PRINTK_HEADER
- "waiting for responses...\n");
+
+ MESSAGE (KERN_INFO, "%s",
+ "waiting for responses...");
+
} else if ( retries < 5 ) {
- printk (KERN_INFO PRINTK_HEADER
- "waiting a little bit longer...\n");
+
+ DBF_EVENT (DBF_NOTICE, "%s",
+ "waiting a little bit longer...");
+
} else {
- printk (KERN_INFO PRINTK_HEADER
- "giving up, enable late devices manually!\n");
+
+ MESSAGE (KERN_INFO, "%s",
+ "giving up, enable late devices manually!");
break;
}
- interruptible_sleep_on_timeout (&dasd_init_waitq, (1 * HZ));
+
+ /* prevent scheduling if called by bh (timer) */
+ if (!in_interrupt()) {
+ interruptible_sleep_on_timeout (&dasd_init_waitq,
+ (1 * HZ) );
+ }
+
retries ++;
} while (1);
/* now setup block devices */
@@ -2939,9 +3649,9 @@
device = *dptr;
if (device == NULL )
continue;
- if ( ((d == NULL && device->discipline != NULL) ||
- (device->discipline == d )) &&
- device->level >= DASD_STATE_READY &&
+ if ( ((discipline == NULL && device->discipline != NULL) ||
+ (device->discipline == discipline )) &&
+ device->level == DASD_STATE_ONLINE &&
device->request_queue == NULL ) {
if (dasd_features_from_devno(j)&DASD_FEATURE_READONLY) {
for (tempdev=device->kdev;
@@ -2949,9 +3659,8 @@
tempdev++)
set_device_ro (tempdev, 1);
- printk (KERN_WARNING PRINTK_HEADER
- "setting read-only mode for device /dev/%s\n",
- device->name);
+ DEV_MESSAGE (KERN_WARNING, device, "%s",
+ "setting read-only mode ");
}
dasd_setup_blkdev(device);
dasd_setup_partitions(device);
@@ -2971,13 +3680,17 @@
static void
dasd_not_oper_handler (int irq, int status)
{
- dasd_device_t *device = NULL;
- major_info_t *major_info = NULL;
+ dasd_device_t *device;
+ major_info_t *major_info;
+ ccw_req_t* cqr;
struct list_head *l;
- int i, devno = -ENODEV;
+ unsigned long flags;
+ int i, devno;
/* find out devno of leaving device: CIO has already deleted this information ! */
- list_for_each (l, &dasd_major_info[0].list) {
+ devno = -ENODEV;
+ device = NULL;
+ list_for_each (l, &dasd_major_info) {
major_info = list_entry (l, major_info_t, list);
for (i = 0; i < DASD_PER_MAJOR; i++) {
device = major_info->dasd_device[i];
@@ -2990,17 +3703,44 @@
break;
}
- DASD_DRIVER_DEBUG_EVENT (5, dasd_not_oper_handler,
- "called for devno %04x",
- devno);
-
if (devno < 0) {
- printk (KERN_WARNING PRINTK_HEADER
- "not_oper_handler called on irq 0x%04x no devno!\n",
- irq);
+
+ MESSAGE (KERN_WARNING,
+ "not_oper_handler called on irq 0x%04x no devno!",
+ irq);
return;
}
- dasd_disable_volume(device, 1);
+ switch (status) {
+ case DEVSTAT_DEVICE_GONE:
+ case DEVSTAT_REVALIDATE: //FIXME
+ DEV_MESSAGE (KERN_DEBUG, device, "%s",
+ "device is gone, disabling it permanently\n");
+ dasd_disable_volume(device, 1);
+ break;
+ case DEVSTAT_NOT_ACC:
+ case DEVSTAT_NOT_ACC_ERR:
+ DEV_MESSAGE (KERN_DEBUG, device, "%s",
+ "device is not accessible, disabling it temporary\n");
+ s390irq_spin_lock_irqsave (device->devinfo.irq,
+ flags);
+ device->accessible = 0;
+ if (status == DEVSTAT_NOT_ACC_ERR) {
+ cqr = device->queue.head;
+ while (cqr) {
+ if (cqr->status == CQR_STATUS_QUEUED)
+ break;
+ if (cqr->status == CQR_STATUS_IN_IO)
+ cqr->status = CQR_STATUS_QUEUED;
+ cqr = cqr->next;
+ }
+ }
+ s390irq_spin_unlock_irqrestore(device->devinfo.irq,
+ flags);
+
+ break;
+ default:
+ panic ("dasd not operational handler was called with illegal status\n");
+ }
}
/*
@@ -3014,56 +3754,62 @@
{
int devno;
int rc = 0;
- major_info_t *major_info = NULL;
+ major_info_t *major_info;
dasd_range_t *rptr,range;
- dasd_device_t *device = NULL;
+ dasd_device_t *device;
struct list_head *l;
+ unsigned long flags;
int i;
+
devno = get_devno_by_irq (irq);
if (devno == -ENODEV) {
rc = -ENODEV;
goto out;
}
- DASD_DRIVER_DEBUG_EVENT (5, dasd_oper_handler,
- "called for devno %04x",
- devno);
-
/* find out devno of device */
- list_for_each (l, &dasd_major_info[0].list) {
+ device = NULL;
+ list_for_each (l, &dasd_major_info) {
major_info = list_entry (l, major_info_t, list);
for (i = 0; i < DASD_PER_MAJOR; i++) {
device = major_info->dasd_device[i];
- if (device && device->devinfo.irq == irq) {
- devno = device->devinfo.devno;
+ if (device && device->devinfo.irq == irq)
break;
- }
+ else
+ device = NULL;
}
- if (devno != -ENODEV)
+ if (device)
break;
}
- if (devno < 0) {
- BUG();
- }
+
if ( device &&
- device->level == DASD_STATE_READY ) {
- dasd_set_device_level (device->devinfo.devno,
- device->discipline, DASD_STATE_ONLINE);
+ (device->level == DASD_STATE_ONLINE) &&
+ (!device->accessible) ) {
+ s390irq_spin_lock_irqsave (device->devinfo.irq,
+ flags);
+ DEV_MESSAGE (KERN_DEBUG, device, "%s",
+ "device is accessible again, reenabling it\n");
+ device->accessible = 1;
+ s390irq_spin_unlock_irqrestore(device->devinfo.irq,
+ flags);
+
+ dasd_schedule_bh(device);
} else {
- if (dasd_autodetect) {
- rptr = dasd_add_range (devno, devno, DASD_DEFAULT_FEATURES);
- if ( rptr == NULL ) {
- rc = -ENOMEM;
- goto out;
- }
- } else {
- range.from = devno;
- range.to = devno;
- rptr = ⦥
- }
- dasd_enable_ranges (rptr, NULL, 0);
+
+ if (dasd_autodetect) {
+ rptr = dasd_add_range (devno, devno, DASD_FEATURE_DEFAULT);
+ if ( rptr == NULL ) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ } else {
+ range.from = devno;
+ range.to = devno;
+ rptr = ⦥
+ }
+ dasd_enable_ranges (rptr, NULL, 0);
}
out:
return rc;
@@ -3075,13 +3821,16 @@
{
dasd_device_t **device_addr;
- DASD_DRIVER_DEBUG_EVENT (1, dasd_find_device_addr,
- "devno %04x",
- devno);
+ DBF_EVENT (DBF_INFO,
+ "devno %04x",
+ devno);
+
if ( dasd_devindex_from_devno (devno) < 0 ) {
- DASD_DRIVER_DEBUG_EXCEPTION (1, dasd_find_device_addr,
- "no dasd: devno %04x",
- devno);
+
+ DBF_EXC (DBF_ALERT,
+ "no dasd: devno %04x",
+ devno);
+
return NULL;
}
/* allocate major numbers on demand for new devices */
@@ -3090,9 +3839,8 @@
if ((rc = dasd_register_major (NULL)) <= 0) {
- DASD_DRIVER_DEBUG_EXCEPTION (1, dasd_find_device_addr,
- "%s",
- "out of major numbers!");
+ DBF_EXC (DBF_ALERT, "%s",
+ "out of major numbers!");
break;
}
}
@@ -3100,86 +3848,110 @@
}
static inline int
-dasd_state_del_to_new (dasd_device_t **addr )
+dasd_state_del_to_new (dasd_device_t **addr, int devno)
{
+ int i;
dasd_device_t* device;
- int rc = 0;
- if (*addr == NULL) { /* allocate device descriptor on demand for new device */
- device = kmalloc (sizeof (dasd_device_t), GFP_ATOMIC);
- if (device == NULL ) {
- rc = -ENOMEM;
- goto out;
- }
- memset (device, 0, sizeof (dasd_device_t));
+ dasd_lowmem_t *lowmem;
+ int rc;
+
+
+ /* allocate device descriptor on demand for new device */
+ if (*addr != NULL) {
+ BUG ();
+ }
+
+ device = kmalloc (sizeof (dasd_device_t), GFP_ATOMIC);
+ if (device == NULL) {
+ return -ENOMEM;
+ }
+
+ memset (device, 0, sizeof (dasd_device_t));
+ dasd_plug_device (device);
+ device->accessible = 1;
+ INIT_LIST_HEAD (&device->lowmem_pool);
+
+ /* allocate pages for lowmem pool */
+ for (i = 0; i < DASD_LOWMEM_PAGES; i++) {
+
+ lowmem = (void *) get_free_page (GFP_ATOMIC|GFP_DMA);
+ if (lowmem == NULL) {
+ break;
+ }
+
+ list_add (&lowmem->list, &device->lowmem_pool);
+ }
+
+ if (i < DASD_LOWMEM_PAGES) {
+ /* didn't get the needed lowmem pages */
+ list_for_each_entry (lowmem, &device->lowmem_pool, list) {
+ MESSAGE (KERN_DEBUG,
+ "<devno: %04x> not enough memory - "
+ "Free page again :%p",
+ devno, lowmem);
+ free_page ((unsigned long) lowmem);
+ }
+ kfree (device);
+ rc = -ENOMEM;
+ } else {
*addr = device;
- device->lowmem_ccws = (void*)get_free_page (GFP_ATOMIC|GFP_DMA);
- if (device->lowmem_ccws == NULL) {
- rc = -ENOMEM;
- goto noccw;
- }
-#ifdef CONFIG_ARCH_S390X
- device->lowmem_idals =
- device->lowmem_idal_ptr = (void*) get_free_page (GFP_ATOMIC|GFP_DMA);
- if (device->lowmem_idals == NULL) {
- rc = -ENOMEM;
- goto noidal;
- }
-#endif
-}
- goto out;
-#ifdef CONFIG_ARCH_S390X
- noidal:
- free_page ((long) device->lowmem_ccws);
-#endif
- noccw:
- kfree(device);
- out:
+ rc = 0;
+ }
return rc;
}
static inline int
-dasd_state_new_to_del (dasd_device_t **addr )
+dasd_state_new_to_del (dasd_device_t **addr, int devno)
{
+ dasd_lowmem_t *lowmem;
+
dasd_device_t *device = *addr;
+
+ /* free private area */
if (device && device->private) {
kfree(device->private);
- device->private = NULL;
}
-#ifdef CONFIG_ARCH_S390X
- free_page ((long)(device->lowmem_idals));
-#endif
- free_page((long)(device->lowmem_ccws));
+
+ /* free lowmem_pool */
+ list_for_each_entry (lowmem, &device->lowmem_pool, list) {
+ free_page ((unsigned long) lowmem);
+ }
+
+ /* free device */
kfree(device);
*addr = NULL;
return 0;
}
static inline int
-dasd_state_new_to_known (dasd_device_t **dptr, int devno, dasd_discipline_t *disc)
+dasd_state_new_to_known (dasd_device_t **dptr,
+ int devno,
+ dasd_discipline_t *discipline)
{
int rc = 0;
umode_t devfs_perm = S_IFBLK | S_IRUSR | S_IWUSR;
struct list_head *l;
- major_info_t *major_info = NULL;
+ major_info_t *major_info, *tmp;
int i;
dasd_device_t *device = *dptr;
devfs_handle_t dir;
char buffer[5];
-
- list_for_each (l, &dasd_major_info[0].list) {
- major_info = list_entry (l, major_info_t, list);
+ major_info = NULL;
+ list_for_each (l, &dasd_major_info) {
+ tmp = list_entry (l, major_info_t, list);
for (i = 0; i < DASD_PER_MAJOR; i++) {
- if (major_info->dasd_device[i] == device) {
- device->kdev = MKDEV (major_info->gendisk.major,
+ if (tmp->dasd_device[i] == device) {
+ device->kdev = MKDEV (tmp->gendisk.major,
i << DASD_PARTN_BITS);
+ major_info = tmp;
break;
}
}
- if (i < DASD_PER_MAJOR) /* we found one */
+ if (major_info != NULL) /* we found one */
break;
}
- if ( major_info == NULL || major_info == &dasd_major_info[0] )
+ if ( major_info == NULL )
BUG();
device->major_info = major_info;
@@ -3192,18 +3964,24 @@
rc = get_dev_info_by_devno (devno, &device->devinfo);
if ( rc ) {
+ /* returns -EUSERS if boxed !!*/
+ if (rc == -EUSERS) {
+ device->level = DASD_STATE_BOXED;
+ }
goto out;
}
- DASD_DRIVER_DEBUG_EVENT (5, dasd_state_new_to_known,
- "got devinfo CU-type %04x and dev-type %04x",
- device->devinfo.sid_data.cu_type,
- device->devinfo.sid_data.dev_type);
-
+ DBF_EVENT (DBF_NOTICE,
+ "got devinfo CU-type %04x and dev-type %04x",
+ device->devinfo.sid_data.cu_type,
+ device->devinfo.sid_data.dev_type);
+
if ( devno != device->devinfo.devno )
BUG();
- device->discipline = dasd_find_disc (device, disc);
+
+ device->discipline = dasd_find_disc (device,
+ discipline);
if ( device->discipline == NULL ) {
rc = -ENODEV;
goto out;
@@ -3221,6 +3999,7 @@
MINOR(device->kdev),
devfs_perm,
&dasd_device_operations,NULL);
+ dasd_do_hotplug_event (device, DASD_HOTPLUG_EVENT_ADD);
device->level = DASD_STATE_KNOWN;
out:
return rc;
@@ -3233,7 +4012,7 @@
/* don't reset to zeros because of persistent data durich detach/attach! */
devfs_unregister(device->devfs_entry);
devfs_unregister(device->major_info->gendisk.de_arr[MINOR(device->kdev) >> DASD_PARTN_BITS]);
-
+ dasd_do_hotplug_event (device, DASD_HOTPLUG_EVENT_REMOVE);
return rc;
}
@@ -3241,12 +4020,21 @@
dasd_state_known_to_accept (dasd_device_t *device)
{
int rc = 0;
- device->debug_area = debug_register (device->name, 0, 2,
- 3 * sizeof (long));
- debug_register_view (device->debug_area, &debug_sprintf_view);
- debug_register_view (device->debug_area, &debug_hex_ascii_view);
- DASD_DEVICE_DEBUG_EVENT (0, device,"%p debug area created",
- device);
+
+ /* register 'device' debug area, used for all DBF_DEV_XXX calls*/
+ device->debug_area = debug_register (device->name,
+ 0, /* size of debug area */
+ 2, /* number of areas */
+ 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");
if (device->discipline->int_handler) {
rc = s390_request_irq_special (device->devinfo.irq,
@@ -3255,7 +4043,10 @@
0, DASD_NAME,
&device->dev_status);
if ( rc ) {
- printk("No request IRQ\n");
+
+ MESSAGE (KERN_DEBUG, "%s",
+ "No request IRQ");
+
goto out;
}
}
@@ -3272,10 +4063,15 @@
if (device->discipline->int_handler) {
free_irq (device->devinfo.irq, &device->dev_status);
}
- DASD_DEVICE_DEBUG_EVENT (0, device,"%p debug area deleted",
- device);
- if ( device->debug_area != NULL )
+
+ 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->discipline = NULL;
device->level = DASD_STATE_KNOWN;
out:
@@ -3297,18 +4093,17 @@
s390irq_spin_lock_irqsave (device->devinfo.irq,
flags);
rc = device->discipline->start_IO (device->init_cqr);
+ if ( ! rc )
+ device->level = DASD_STATE_INIT;
s390irq_spin_unlock_irqrestore(device->devinfo.irq,
flags);
- if ( rc )
- goto out;
- device->level = DASD_STATE_INIT;
} else {
rc = -ENOMEM;
}
} else {
rc = dasd_state_init_to_ready ( device );
}
- out:
+
return rc;
}
@@ -3318,15 +4113,8 @@
int rc = 0;
if (device->discipline->do_analysis != NULL)
if ( device->discipline->do_analysis (device) == 0 )
- switch (device->sizes.bp_block) {
- case 512:
- case 1024:
- case 2048:
- case 4096:
- break;
- default:
- rc = -EMEDIUMTYPE;
- }
+ rc = dasd_check_bp_block (device);
+
if ( device->init_cqr ) {
/* This pointer is no longer needed, BUT dont't free the */
/* memory, because this is done in bh for finished request!!!! */
@@ -3362,8 +4150,10 @@
dasd_state_ready_to_online (dasd_device_t *device )
{
int rc = 0;
- dasd_unplug_device (device);
- device->level = DASD_STATE_ONLINE;
+ if (!(rc = dasd_check_bp_block (device))) {
+ dasd_unplug_device (device);
+ device->level = DASD_STATE_ONLINE;
+ }
return rc;
}
@@ -3383,6 +4173,7 @@
int i;
int major = MAJOR(device->kdev);
int minor = MINOR(device->kdev);
+ request_queue_t *request_queue;
for (i = 0; i < (1 << DASD_PARTN_BITS); i++) {
if (i == 0)
@@ -3399,11 +4190,16 @@
device->major_info->gendisk.part[minor+i].start_sect = 0;
device->major_info->gendisk.part[minor+i].nr_sects = 0;
}
- device->request_queue = kmalloc(sizeof(request_queue_t),GFP_KERNEL);
- device->request_queue->queuedata = device;
- blk_init_queue (device->request_queue, do_dasd_request);
- blk_queue_headactive (device->request_queue, 0);
- elevator_init (&(device->request_queue->elevator),ELEVATOR_NOOP);
+
+ request_queue = kmalloc(sizeof(request_queue_t),GFP_KERNEL);
+ if (request_queue) {
+ request_queue->queuedata = device;
+ blk_init_queue (request_queue, do_dasd_request);
+ blk_queue_headactive (request_queue, 1);
+ elevator_init (&(request_queue->elevator),ELEVATOR_NOOP);
+ }
+ device->request_queue = request_queue;
+
return rc;
}
@@ -3424,6 +4220,19 @@
int i;
int major = MAJOR(device->kdev);
int minor = MINOR(device->kdev);
+ request_queue_t *q = device->request_queue;
+ struct request *req;
+ long flags;
+
+ spin_lock_irqsave(&io_request_lock, flags);
+ while (q &&
+ !list_empty(&q->queue_head) &&
+ (req = dasd_next_request(q)) != NULL) {
+
+ dasd_end_request(req, 0);
+ dasd_dequeue_request(q, req);
+ }
+ spin_unlock_irqrestore(&io_request_lock, flags);
for (i = 0; i < (1 << DASD_PARTN_BITS); i++) {
destroy_buffers(MKDEV(major,minor+i));
@@ -3453,6 +4262,7 @@
1 << DASD_PARTN_BITS,
&dasd_device_operations,
(device->sizes.blocks << device->sizes.s2b_shift));
+ dasd_do_hotplug_event (device, DASD_HOTPLUG_EVENT_PARTCHK);
}
static inline void
@@ -3467,14 +4277,7 @@
}
devfs_register_partitions(&device->major_info->gendisk,
MINOR(device->kdev),1);
-}
-
-static inline void
-dasd_resetup_partitions ( dasd_device_t * device )
-{
- BUG();
- dasd_destroy_partitions ( device ) ;
- dasd_setup_partitions ( device ) ;
+ dasd_do_hotplug_event (device, DASD_HOTPLUG_EVENT_PARTREMOVE);
}
/*
@@ -3505,11 +4308,11 @@
from_state = device->level;
}
- DASD_DRIVER_DEBUG_EVENT (3, dasd_set_device_level,
- "devno %04x; from %i to %i",
- devno,
- from_state,
- to_state);
+ DBF_EVENT (DBF_INFO,
+ "devno %04x; from %i to %i",
+ devno,
+ from_state,
+ to_state);
if ( from_state == to_state )
goto out;
@@ -3520,20 +4323,30 @@
/* First check for bringup */
if ( from_state <= DASD_STATE_DEL &&
to_state >= DASD_STATE_NEW ) {
- rc = dasd_state_del_to_new(device_addr);
+ rc = dasd_state_del_to_new(device_addr, devno);
if ( rc ) {
goto bringup_fail;
}
device = *device_addr;
}
- if ( from_state <= DASD_STATE_NEW &&
+
+ /* reprobe boxed devices */
+ if (device->level == DASD_STATE_BOXED) {
+ rc = s390_trigger_resense (device->devinfo.irq);
+ if ( rc ) {
+ goto bringup_fail;
+ }
+ }
+
+ if ( device->level <= DASD_STATE_BOXED &&
to_state >= DASD_STATE_KNOWN ) {
rc = dasd_state_new_to_known( device_addr, devno, discipline );
if ( rc ) {
goto bringup_fail;
}
}
- if ( from_state <= DASD_STATE_KNOWN &&
+
+ if ( device->level <= DASD_STATE_KNOWN &&
to_state >= DASD_STATE_ACCEPT ) {
rc = dasd_state_known_to_accept(device);
if ( rc ) {
@@ -3543,19 +4356,21 @@
if ( dasd_probeonly ) {
goto out;
}
- if ( from_state <= DASD_STATE_ACCEPT &&
+
+ if ( device->level <= DASD_STATE_ACCEPT &&
to_state >= DASD_STATE_INIT ) {
rc = dasd_state_accept_to_init(device);
if ( rc ) {
goto bringup_fail;
}
}
- if ( from_state <= DASD_STATE_INIT &&
+ if ( device->level <= DASD_STATE_INIT &&
to_state >= DASD_STATE_READY ) {
rc = -EAGAIN;
goto out;
}
- if ( from_state <= DASD_STATE_READY &&
+
+ if ( device->level <= DASD_STATE_READY &&
to_state >= DASD_STATE_ONLINE ) {
rc = dasd_state_ready_to_online(device);
if ( rc ) {
@@ -3564,50 +4379,57 @@
}
goto out;
bringup_fail: /* revert changes */
-#if 0
- printk (KERN_DEBUG PRINTK_HEADER
- "failed to set device from state %d to %d at "
- "level %d rc %d. Reverting...\n",
- from_state,
- to_state,
- device->level,
- rc);
-#endif
- to_state = from_state;
- from_state = device->level;
+
+ DBF_DEV_EVENT (DBF_ERR, device,
+ "failed to set device from state %d to %d at "
+ "level %d rc %d. Reverting...",
+ from_state,
+ to_state,
+ device->level,
+ rc);
+
+ if (device->level <= DASD_STATE_NEW) {
+ /* Revert - device can not be accessed */
+ to_state = from_state;
+ from_state = device->level;
+ }
/* now do a shutdown */
shutdown:
- if ( from_state >= DASD_STATE_ONLINE &&
+ if ( device->level >= DASD_STATE_ONLINE &&
to_state <= DASD_STATE_READY )
if (dasd_state_online_to_ready(device))
BUG();
- if ( from_state >= DASD_STATE_READY &&
+ if ( device->level >= DASD_STATE_READY &&
to_state <= DASD_STATE_ACCEPT )
if ( dasd_state_ready_to_accept(device))
BUG();
- if ( from_state >= DASD_STATE_ACCEPT &&
+ if ( device->level >= DASD_STATE_ACCEPT &&
to_state <= DASD_STATE_KNOWN )
if ( dasd_state_accept_to_known(device))
BUG();
- if ( from_state >= DASD_STATE_KNOWN &&
+ if ( device->level >= DASD_STATE_KNOWN &&
to_state <= DASD_STATE_NEW )
if ( dasd_state_known_to_new(device))
BUG();
- if ( from_state >= DASD_STATE_NEW &&
+ if ( device->level >= DASD_STATE_NEW &&
to_state <= DASD_STATE_DEL)
- if (dasd_state_new_to_del(device_addr))
+ if (dasd_state_new_to_del(device_addr, devno))
BUG();
goto out;
out:
return rc;
}
-/* SECTION: Procfs stuff */
+/********************************************************************************
+ * SECTION: Procfs stuff
+ ********************************************************************************/
+#ifdef CONFIG_PROC_FS
+
typedef struct {
char *data;
int len;
@@ -3640,29 +4462,53 @@
int index = 0;
MOD_INC_USE_COUNT;
- spin_lock_irqsave(&discipline_lock,flags);
+
+ spin_lock_irqsave(&discipline_lock,
+ flags);
+
info = (tempinfo_t *) vmalloc (sizeof (tempinfo_t));
+
if (info == NULL) {
- printk (KERN_WARNING "No memory available for data\n");
+
+ MESSAGE (KERN_WARNING, "%s",
+ "No memory available for data (tempinfo)");
+
+ spin_unlock_irqrestore(&discipline_lock,
+ flags);
+
MOD_DEC_USE_COUNT;
+
return -ENOMEM;
+
} else {
file->private_data = (void *) info;
}
- list_for_each (l, &dasd_major_info[0].list) {
+ list_for_each (l, &dasd_major_info) {
size += 128 * 1 << (MINORBITS - DASD_PARTN_BITS);
}
info->data = (char *) vmalloc (size);
- DASD_DRIVER_DEBUG_EVENT (1, dasd_devices_open, "area: %p, size 0x%x",
- info->data,
- size);
+
if (size && info->data == NULL) {
- printk (KERN_WARNING "No memory available for data\n");
+
+ MESSAGE (KERN_WARNING, "%s",
+ "No memory available for data (info->data)");
+
vfree (info);
+
+ spin_unlock_irqrestore(&discipline_lock,
+ flags);
+
MOD_DEC_USE_COUNT;
+
return -ENOMEM;
}
- list_for_each (l, &dasd_major_info[0].list) {
+
+ DBF_EVENT (DBF_NOTICE,
+ "procfs-area: %p, size 0x%x allocated",
+ info->data,
+ size);
+
+ list_for_each (l, &dasd_major_info) {
temp = list_entry (l, major_info_t, list);
for (i = 0; i < 1 << (MINORBITS - DASD_PARTN_BITS); i++) {
dasd_device_t *device;
@@ -3673,8 +4519,8 @@
continue;
features = dasd_features_from_devno(devno);
- if (features < DASD_DEFAULT_FEATURES)
- features = DASD_DEFAULT_FEATURES;
+ if (features < DASD_FEATURE_DEFAULT)
+ features = DASD_FEATURE_DEFAULT;
device = temp->dasd_device[i];
if (device) {
@@ -3702,45 +4548,45 @@
sprintf (info->data + len,
"detected");
break;
+ case DASD_STATE_BOXED:
+ len +=
+ sprintf (info->data + len,
+ "boxed");
+ break;
case DASD_STATE_ACCEPT:
- len += sprintf (info->data + len,"accepted");
+ len += sprintf (info->data + len,
+ "accepted");
break;
case DASD_STATE_INIT:
- len +=
- sprintf (info->data + len,
- "busy ");
+ len += sprintf (info->data + len,
+ "busy ");
break;
case DASD_STATE_READY:
+ len += sprintf (info->data + len,
+ "ready ");
+ break;
case DASD_STATE_ONLINE:
- if ( atomic_read(&device->plugged) )
- len +=
- sprintf (info->data + len,
- "fenced ");
- else
- len +=
- sprintf (info->data + len,
- "active ");
- if ( device->sizes.bp_block == 512 ||
- device->sizes.bp_block == 1024 ||
- device->sizes.bp_block == 2048 ||
- device->sizes.bp_block == 4096 )
- len +=
- sprintf (info->data + len,
- "at blocksize: %d, %ld blocks, %ld MB",
- device->sizes.bp_block,
- device->sizes.blocks,
- ((device->
- sizes.bp_block >> 9) *
- device->sizes.
- blocks) >> 11);
- else
- len +=
- sprintf (info->data + len,
- "n/f ");
+ len += sprintf (info->data + len,
+ "active ");
+
+ if (dasd_check_bp_block (device))
+ len +=
+ sprintf (info->data + len,
+ "n/f ");
+ else
+ len +=
+ sprintf (info->data + len,
+ "at blocksize: %d, %ld blocks, %ld MB",
+ device->sizes.bp_block,
+ device->sizes.blocks,
+ ((device->
+ sizes.bp_block >> 9) *
+ device->sizes.
+ blocks) >> 11);
break;
default:
len +=
- sprintf (info->data + len,
+ sprintf (info->data + len,
"no stat");
break;
}
@@ -3769,7 +4615,9 @@
index += 1 << (MINORBITS - DASD_PARTN_BITS);
}
info->len = len;
- spin_unlock_irqrestore(&discipline_lock,flags);
+
+ spin_unlock_irqrestore(&discipline_lock,
+ flags);
return rc;
}
@@ -3793,22 +4641,250 @@
}
}
+/*
+ * scan for device range in given string (e.g. 0x0150-0x0155).
+ * devnos are always hex and leading 0x are ignored.
+ */
+static char *
+dasd_parse_range (char *buffer, dasd_range_t *range)
+{
+ char *str;
+
+ /* remove optional 'device ' and 'range=' and search for nexet digit */
+ for (str = buffer + 4; isspace(*str); str++);
+
+ if (strncmp (str, "device ", 7) == 0)
+ for (str = str + 7; isspace(*str); str++);
+
+ if (strncmp (str, "range=", 6) == 0)
+ for (str = str + 6; isspace(*str); str++);
+
+ range->to = range->from = dasd_strtoul (str,
+ &str,
+ &(range->features));
+
+ if (*str == '-') {
+ str++;
+ range->to = dasd_strtoul (str,
+ &str,
+ &(range->features));
+ }
+
+ /* remove blanks after device range */
+ for (; isspace(*str); str++);
+
+ if (range->from < 0 || range->to < 0) {
+ MESSAGE (KERN_WARNING,
+ "/proc/dasd/devices: range parse error in '%s'",
+ buffer);
+ return ERR_PTR (-EINVAL);
+ }
+
+ return str;
+
+} /* end dasd_parse_range */
+
+/*
+ * Enable / Disable the given devices
+ */
+static void
+dasd_proc_set (char *buffer)
+{
+ dasd_range_t range;
+ char *str;
+
+ str = dasd_parse_range (buffer,
+ &range);
+
+ /* Negative numbers in str/from/to indicate errors */
+ if (IS_ERR (str) || (range.from < 0) || (range.to < 0)
+ || (range.from > 65535) || (range.to > 65535))
+ return;
+
+ if (strncmp (str, "on", 2) == 0) {
+ dasd_enable_ranges (&range, NULL, 0);
+
+ } else if (strncmp (str, "off", 3) == 0) {
+ dasd_disable_ranges (&range, NULL, 0, 1);
+
+ } else {
+ MESSAGE (KERN_WARNING,
+ "/proc/dasd/devices: "
+ "only 'on' and 'off' are alowed in 'set' "
+ "command ('%s'/'%s')",
+ buffer,
+ str);
+ }
+
+ return;
+
+} /* end dasd_proc_set */
+
+/*
+ * Add the given devices
+ */
+static void
+dasd_proc_add (char *buffer)
+{
+ dasd_range_t range;
+ char *str;
+
+ str = dasd_parse_range (buffer,
+ &range);
+
+ /* Negative numbers in str/from/to indicate errors */
+ if (IS_ERR (str) || (range.from < 0) || (range.to < 0)
+ || (range.from > 65535) || (range.to > 65535))
+ return;
+
+ dasd_add_range (range.from, range.to, range.features);
+ dasd_enable_ranges (&range, NULL, 0);
+
+ return;
+
+} /* end dasd_proc_add */
+
+/*
+ * Break the lock of a given 'boxed' dasd.
+ * If the dasd in not in status 'boxed' just return.
+ */
+static int
+dasd_break_boxed (dasd_range_t *range,
+ dasd_device_t *device)
+{
+ int rc = 0;
+ dasd_discipline_t *discipline;
+ struct list_head *lh = dasd_disc_head.next;
+
+ /* check devixe status */
+ if (device->level != DASD_STATE_BOXED) {
+ MESSAGE (KERN_WARNING,
+ "/proc/dasd/devices: the given device (%04X) "
+ "is not 'boxed')",
+ device->devinfo.devno);
+ rc = -EINVAL;
+ goto out;
+ }
+
+ /* force eckd discipline */
+ do {
+ discipline = list_entry(lh,
+ dasd_discipline_t,
+ list);
+
+ if (strncmp (discipline->name, range->discipline, 4) == 0)
+ break; /* discipline found */
+
+ lh = lh->next; /* check next discipline in list */
+ if (lh == &dasd_disc_head) {
+ discipline = NULL;
+ break;
+ }
+ } while ( 1 );
+ device->discipline = discipline;
+
+ if (device->discipline == NULL) {
+ MESSAGE (KERN_WARNING, "%s",
+ "/proc/dasd/devices: discipline not found "
+ "in discipline list");
+ rc = -EINVAL;
+ goto out;
+ }
+
+ /* register the int handler to enable IO */
+ rc = s390_request_irq_special (device->devinfo.irq,
+ device->discipline->int_handler,
+ dasd_not_oper_handler,
+ 0,
+ DASD_NAME,
+ &device->dev_status);
+ if ( rc )
+ goto out;
+
+ rc = dasd_steal_lock (device);
+
+ /* unregister the int handler to enable re-sensing */
+ free_irq (device->devinfo.irq,
+ &device->dev_status);
+
+ device->discipline = NULL;
+ device->level = DASD_STATE_NEW;
+
+ out:
+ return rc;
+
+} /* end dasd_break_boxed */
+
+/*
+ * Handle the procfs call 'brk <devno> <discipline>.
+ */
+static void
+dasd_proc_brk (char *buffer)
+{
+ char *str;
+ dasd_range_t range;
+ dasd_device_t *device;
+ int rc = 0;
+
+ str = dasd_parse_range (buffer,
+ &range);
+
+ if (IS_ERR (str))
+ return;
+
+ if (range.from != range.to) {
+ MESSAGE (KERN_WARNING, "%s",
+ "/proc/dasd/devices: 'brk <devno> <discipline> "
+ "is only allowed for a single device (no ranges)");
+ return;
+ }
+
+ /* check for discipline = 'eckd' */
+ if (strncmp(str, "eckd", 4) != 0) {
+ MESSAGE (KERN_WARNING,
+ "/proc/dasd/devices: 'brk <devno> <discipline> "
+ "is only allowed for 'eckd' (%s)",
+ str);
+ return;
+ }
+
+ memcpy (range.discipline, "ECKD", 4);
+
+ device = *(dasd_device_from_devno (range.from));
+ if (device == NULL) {
+ MESSAGE (KERN_WARNING,
+ "/proc/dasd/devices: no device found for devno (%04X)",
+ range.from);
+ return;
+ }
+
+ rc = dasd_break_boxed (&range,
+ device);
+ if (rc == 0) {
+ /* trigger CIO to resense the device */
+ s390_trigger_resense (device->devinfo.irq);
+
+ // get the device online now
+ dasd_enable_ranges (&range,
+ NULL,
+ 0);
+ }
+
+} /* end dasd_proc_brk */
+
static ssize_t
dasd_devices_write (struct file *file, const char *user_buf,
size_t user_len, loff_t * offset)
{
char *buffer;
- int off = 0;
- char *temp;
- dasd_range_t range;
- int features;
if (user_len > PAGE_SIZE)
return -EINVAL;
-
+
buffer = vmalloc (user_len+1);
if (buffer == NULL)
return -ENOMEM;
+
if (copy_from_user (buffer, user_buf, user_len)) {
vfree (buffer);
return -EFAULT;
@@ -3821,60 +4897,29 @@
buffer[user_len] = '\0';
}
- printk (KERN_INFO PRINTK_HEADER "/proc/dasd/devices: '%s'\n", buffer);
- if (strncmp (buffer, "set ", 4) && strncmp (buffer, "add ", 4)) {
- printk (KERN_WARNING PRINTK_HEADER
- "/proc/dasd/devices: only 'set' and 'add' are supported verbs\n");
- return -EINVAL;
- }
- off += 4;
- while (buffer[off] && !isalnum (buffer[off]))
- off++;
- if (!strncmp (buffer + off, "device", strlen ("device"))) {
- off += strlen ("device");
- while (buffer[off] && !isalnum (buffer[off]))
- off++;
- }
- if (!strncmp (buffer + off, "range=", strlen ("range="))) {
- off += strlen ("range=");
- while (buffer[off] && !isalnum (buffer[off]))
- off++;
- }
-
- temp = buffer + off;
- range.from = dasd_strtoul (temp, &temp, &features);
- range.to = range.from;
-
- if (*temp == '-') {
- temp++;
- range.to = dasd_strtoul (temp, &temp, &features);
- }
+ MESSAGE (KERN_INFO,
+ "/proc/dasd/devices: '%s'",
+ buffer);
+
+ if (strncmp (buffer, "set ", 4) == 0) {
+ /* handle 'set <devno> on/off' */
+ dasd_proc_set (buffer);
+
+ } else if (strncmp (buffer, "add ", 4) == 0) {
+ /* handle 'add <devno>' */
+ dasd_proc_add (buffer);
+
+ } else if (strncmp (buffer, "brk ", 4) == 0) {
+ /* handle 'brk <devno> <discipline>' */
+ dasd_proc_brk (buffer);
- if (range.from == -EINVAL ||
- range.to == -EINVAL ) {
-
- printk (KERN_WARNING PRINTK_HEADER
- "/proc/dasd/devices: range parse error in '%s'\n",
- buffer);
} else {
- off = (long) temp - (long) buffer;
- if (!strncmp (buffer, "add", strlen ("add"))) {
- dasd_add_range (range.from, range.to, features);
- dasd_enable_ranges (&range, NULL, 0);
- } else {
- while (buffer[off] && !isalnum (buffer[off]))
- off++;
- if (!strncmp (buffer + off, "on", strlen ("on"))) {
- dasd_enable_ranges (&range, NULL, 0);
- } else if (!strncmp (buffer + off, "off", strlen ("off"))) {
- dasd_disable_ranges (&range, NULL, 0, 1);
- } else {
- printk (KERN_WARNING PRINTK_HEADER
- "/proc/dasd/devices: parse error in '%s'\n",
- buffer);
- }
- }
- }
+ MESSAGE (KERN_WARNING, "%s",
+ "/proc/dasd/devices: only 'set' ,'add' and "
+ "'brk' are supported verbs");
+ vfree (buffer);
+ return -EINVAL;
+ }
vfree (buffer);
return user_len;
@@ -3895,9 +4940,9 @@
}
static struct file_operations dasd_devices_file_ops = {
- read:dasd_generic_read, /* read */
+ read:dasd_generic_read, /* read */
write:dasd_devices_write, /* write */
- open:dasd_devices_open, /* open */
+ open:dasd_devices_open, /* open */
release:dasd_devices_close, /* close */
};
@@ -3914,22 +4959,41 @@
MOD_INC_USE_COUNT;
info = (tempinfo_t *) vmalloc (sizeof (tempinfo_t));
+
if (info == NULL) {
- printk (KERN_WARNING "No memory available for data\n");
+
+ MESSAGE (KERN_WARNING, "%s",
+ "No memory available for data (tempinfo)");
+
MOD_DEC_USE_COUNT;
return -ENOMEM;
} else {
file->private_data = (void *) info;
}
- info->data = (char *) vmalloc (PAGE_SIZE); /* FIXME! determine space needed in a better way */
+ /* FIXME! determine space needed in a better way */
+ info->data = (char *) vmalloc (PAGE_SIZE);
+
if (info->data == NULL) {
- printk (KERN_WARNING "No memory available for data\n");
+
+ MESSAGE (KERN_WARNING, "%s",
+ "No memory available for data (info->data)");
+
vfree (info);
file->private_data = NULL;
MOD_DEC_USE_COUNT;
return -ENOMEM;
}
+ /* check for active profiling */
+ if (dasd_profile_level == DASD_PROFILE_OFF) {
+
+ info->len = sprintf (info->data,
+ "Statistics are off - they might be "
+ "switched on using 'echo set on > "
+ "/proc/dasd/statistics'\n");
+ return rc;
+ }
+
/* prevent couter 'ouverflow' on output */
for (shift = 0, help = dasd_global_profile.dasd_io_reqs;
help > 9999999; help = help >> 1, shift++) ;
@@ -3958,7 +5022,15 @@
}
len += sprintf (info->data + len, "\n");
- len += sprintf (info->data + len, "Histogram of I/O times (microseconds)\n");
+ for (; i < 32; i++) {
+ len += sprintf (info->data + len, "%7d ",
+ dasd_global_profile.dasd_io_secs[i] >> shift);
+ }
+ len += sprintf (info->data + len, "\n");
+
+ len += sprintf (info->data + len,
+ "Histogram of I/O times (microseconds)\n");
+
for (i = 0; i < 16; i++) {
len += sprintf (info->data + len, "%7d ",
dasd_global_profile.dasd_io_times[i] >> shift);
@@ -4008,7 +5080,9 @@
len += sprintf (info->data + len, "\n");
len += sprintf (info->data + len,
- "Histogram of I/O time between ssch and irq per sector\n");
+ "Histogram of I/O time between ssch and irq per "
+ "sector\n");
+
for (i = 0; i < 16; i++) {
len += sprintf (info->data + len, "%7d ",
dasd_global_profile.dasd_io_time2ps[i] >> shift);
@@ -4064,23 +5138,89 @@
if (buffer == NULL)
return -ENOMEM;
+
if (copy_from_user (buffer, user_buf, user_len)) {
vfree (buffer);
return -EFAULT;
}
+
buffer[user_len] = 0;
- printk (KERN_INFO PRINTK_HEADER "/proc/dasd/statictics: '%s'\n",
- buffer);
- if (strncmp (buffer, "reset", 4)) {
- memset (&dasd_global_profile, 0, sizeof (dasd_profile_info_t));
+
+ MESSAGE (KERN_INFO,
+ "/proc/dasd/statictics: '%s'",
+ buffer);
+
+#ifdef DASD_PROFILE
+ /* check for valid verbs */
+ if (strncmp (buffer, "reset", 5) &&
+ strncmp (buffer, "set ", 4) ) {
+
+ MESSAGE (KERN_WARNING, "%s",
+ "/proc/dasd/statistics: only 'set' and "
+ "'reset' are supported verbs");
+
+ return -EINVAL;
}
+
+ if (!strncmp (buffer, "reset", 5)) {
+
+ /* reset the statistics */
+ memset (&dasd_global_profile,
+ 0,
+ sizeof (dasd_profile_info_t));
+
+ MESSAGE (KERN_INFO, "%s",
+ "Statictics reset");
+
+ } else {
+
+ /* 'set xxx' was given */
+ int offset = 4;
+
+ while (buffer[offset] && !isalnum (buffer[offset]))
+ offset++;
+
+ if (!strncmp (buffer + offset, "on", 2)) {
+
+ /* switch on statistics profiling */
+ dasd_profile_level = DASD_PROFILE_ON;
+
+ MESSAGE (KERN_INFO, "%s",
+ "Statictics switched on");
+
+ } else if (!strncmp (buffer + offset, "off", 3)) {
+
+ /* switch off and reset statistics profiling */
+ memset (&dasd_global_profile,
+ 0,
+ sizeof (dasd_profile_info_t));
+
+ dasd_profile_level = DASD_PROFILE_OFF;
+
+ MESSAGE (KERN_INFO, "%s",
+ "Statictics switched off");
+
+ } else {
+
+ MESSAGE (KERN_WARNING, "%s",
+ "/proc/dasd/statistics: only 'set on' and "
+ "'set off' are supported verbs");
+ }
+ }
+#else
+ MESSAGE (KERN_WARNING, "%s",
+ "/proc/dasd/statistics: is not activated in this "
+ "kernel");
+
+
+#endif /* DASD_PROFILE */
return user_len;
}
static struct file_operations dasd_statistics_file_ops = {
- read:dasd_generic_read, /* read */
- open:dasd_statistics_open, /* open */
+ read:dasd_generic_read, /* read */
write:dasd_statistics_write, /* write */
+ open:dasd_statistics_open, /* open */
release:dasd_devices_close, /* close */
};
@@ -4113,6 +5253,11 @@
remove_proc_entry ("dasd", &proc_root);
}
+#endif /* CONFIG_PROC_FS */
+
+/********************************************************************************
+ * SECTION: Initializing the driver
+ ********************************************************************************/
int
dasd_request_module ( void *name ) {
int rc = -ERESTARTSYS;
@@ -4124,16 +5269,17 @@
}
while ( (rc=request_module(name)) != 0 ) {
DECLARE_WAIT_QUEUE_HEAD(wait);
- printk ( KERN_INFO "request_module returned %d for %s\n",
+
+ MESSAGE (KERN_INFO,
+ "request_module returned %d for %s",
rc,
(char*)name);
+
sleep_on_timeout(&wait,5* HZ); /* wait in steps of 5 seconds */
}
return rc;
}
-
-/* SECTION: Initializing the driver */
int __init
dasd_init (void)
{
@@ -4142,40 +5288,56 @@
major_info_t *major_info = NULL;
struct list_head *l;
- printk (KERN_INFO PRINTK_HEADER "initializing...\n");
- dasd_debug_area = debug_register (DASD_NAME, 0, 2, 5 * sizeof (long));
- debug_register_view (dasd_debug_area, &debug_sprintf_view);
- debug_register_view (dasd_debug_area, &debug_hex_ascii_view);
+ MESSAGE (KERN_INFO, "%s",
+ "initializing...");
init_waitqueue_head (&dasd_init_waitq);
+ /* register 'common' DASD debug area, used faor all DBF_XXX calls*/
+ dasd_debug_area = debug_register (DASD_NAME,
+ 0, /* size of debug area */
+ 2, /* number of areas */
+ 8 * sizeof (long));
+
+ debug_register_view (dasd_debug_area,
+ &debug_sprintf_view);
+
if (dasd_debug_area == NULL) {
goto failed;
}
- DASD_DRIVER_DEBUG_EVENT (0, dasd_init, "%s",
- "ENTRY");
- dasd_devfs_handle = devfs_mk_dir (NULL, DASD_NAME, NULL);
+
+ debug_set_level (dasd_debug_area,
+ DBF_ERR);
+
+ DBF_EVENT (DBF_EMERG, "%s",
+ "debug area created");
+
+ dasd_devfs_handle = devfs_mk_dir (NULL,
+ DASD_NAME,
+ NULL);
+
if (dasd_devfs_handle < 0) {
- DASD_DRIVER_DEBUG_EVENT (1, dasd_init, "%s",
- "no devfs");
+
+ DBF_EVENT (DBF_ALERT, "%s",
+ "no devfs");
goto failed;
}
- list_for_each (l, &dasd_major_info[0].list) {
+
+ list_add_tail(&dasd_major_static.list, &dasd_major_info);
+ list_for_each (l, &dasd_major_info) {
major_info = list_entry (l, major_info_t, list);
if ((rc = dasd_register_major (major_info)) > 0) {
- DASD_DRIVER_DEBUG_EVENT (1, dasd_init,
- "major %d: success",
- major_info->gendisk.major);
- printk (KERN_INFO PRINTK_HEADER
- "Registered successfully to major no %u\n",
- major_info->gendisk.major);
+
+ MESSAGE (KERN_INFO,
+ "Registered successfully to major no %u",
+ major_info->gendisk.major);
} else {
- DASD_DRIVER_DEBUG_EVENT (1, dasd_init,
- "major %d: failed",
- major_info->gendisk.major);
- printk (KERN_WARNING PRINTK_HEADER
- "Couldn't register successfully to major no %d\n",
- major_info->gendisk.major);
+
+ MESSAGE (KERN_WARNING,
+ "Couldn't register successfully to "
+ "major no %d",
+ major_info->gendisk.major);
+
/* revert registration of major infos */
goto failed;
}
@@ -4185,18 +5347,24 @@
#endif /* ! MODULE */
rc = dasd_parse (dasd);
if (rc) {
- DASD_DRIVER_DEBUG_EVENT (1, dasd_init, "%s",
- "invalid range found");
+
+ DBF_EVENT (DBF_ALERT, "%s",
+ "invalid range found");
goto failed;
}
+#ifdef CONFIG_PROC_FS
rc = dasd_proc_init ();
if (rc) {
- DASD_DRIVER_DEBUG_EVENT (1, dasd_init, "%s", "no proc-FS");
+
+ DBF_EVENT (DBF_ALERT, "%s",
+ "no proc-FS");
goto failed;
}
+#endif /* CONFIG_PROC_FS */
+
genhd_dasd_name = dasd_device_name;
- genhd_dasd_ioctl = dasd_ioctl;
+ genhd_dasd_ioctl = dasd_ioctl;
if (dasd_autodetect) { /* update device range to all devices */
for (irq = get_irq_first (); irq != -ENODEV;
@@ -4204,10 +5372,14 @@
int devno = get_devno_by_irq (irq);
int index = dasd_devindex_from_devno (devno);
if (index == -ENODEV) { /* not included in ranges */
- DASD_DRIVER_DEBUG_EVENT (2, dasd_init,
- "add %04x to range",
- devno);
- dasd_add_range (devno, devno, DASD_DEFAULT_FEATURES);
+
+ DBF_EVENT (DBF_CRIT,
+ "add %04x to range",
+ devno);
+
+ dasd_add_range (devno,
+ devno,
+ DASD_FEATURE_DEFAULT);
}
}
}
@@ -4216,15 +5388,15 @@
#ifdef CONFIG_DASD_DIAG
rc = dasd_diag_init ();
if (rc == 0) {
- DASD_DRIVER_DEBUG_EVENT (1, dasd_init,
- "DIAG discipline %s",
- "success");
- printk (KERN_INFO PRINTK_HEADER
- "Registered DIAG discipline successfully\n");
+
+ MESSAGE (KERN_INFO, "%s",
+ "Registered DIAG discipline successfully");
+
} else {
- DASD_DRIVER_DEBUG_EVENT (1, dasd_init,
- "DIAG discipline %s",
- "failed");
+
+ DBF_EVENT (DBF_ALERT, "%s",
+ "Register DIAG discipline failed");
+
goto failed;
}
#endif /* CONFIG_DASD_DIAG */
@@ -4235,13 +5407,14 @@
#ifdef CONFIG_DASD_ECKD
rc = dasd_eckd_init ();
if (rc == 0) {
- DASD_DRIVER_DEBUG_EVENT (1, dasd_init,
- "ECKD discipline %s", "success");
- printk (KERN_INFO PRINTK_HEADER
- "Registered ECKD discipline successfully\n");
+
+ MESSAGE (KERN_INFO, "%s",
+ "Registered ECKD discipline successfully");
} else {
- DASD_DRIVER_DEBUG_EVENT (1, dasd_init,
- "ECKD discipline %s", "failed");
+
+ DBF_EVENT (DBF_ALERT, "%s",
+ "Register ECKD discipline failed");
+
goto failed;
}
#endif /* CONFIG_DASD_ECKD */
@@ -4251,14 +5424,14 @@
#ifdef CONFIG_DASD_FBA
rc = dasd_fba_init ();
if (rc == 0) {
- DASD_DRIVER_DEBUG_EVENT (1, dasd_init,
- "FBA discipline %s", "success");
-
- printk (KERN_INFO PRINTK_HEADER
- "Registered FBA discipline successfully\n");
+
+ MESSAGE (KERN_INFO, "%s",
+ "Registered FBA discipline successfully");
} else {
- DASD_DRIVER_DEBUG_EVENT (1, dasd_init,
- "FBA discipline %s", "failed");
+
+ DBF_EVENT (DBF_ALERT, "%s",
+ "Register FBA discipline failed");
+
goto failed;
}
#endif /* CONFIG_DASD_FBA */
@@ -4276,12 +5449,16 @@
rc = 0;
goto out;
failed:
- printk (KERN_INFO PRINTK_HEADER
- "initialization not performed due to errors\n");
+
+ MESSAGE (KERN_INFO, "%s",
+ "initialization not performed due to errors");
+
cleanup_dasd ();
out:
- DASD_DRIVER_DEBUG_EVENT (0, dasd_init, "%s", "LEAVE");
- printk (KERN_INFO PRINTK_HEADER "initialization finished\n");
+
+ MESSAGE (KERN_INFO, "%s",
+ "initialization finished");
+
return rc;
}
@@ -4293,57 +5470,60 @@
struct list_head *l,*n;
dasd_range_t *range;
- printk (KERN_INFO PRINTK_HEADER "shutting down\n");
- DASD_DRIVER_DEBUG_EVENT(0,"cleanup_dasd","%s","ENTRY");
- dasd_disable_ranges (&dasd_range_head, NULL, 1, 1);
- if (MACHINE_IS_VM) {
+ MESSAGE (KERN_INFO, "%s",
+ "shutting down");
+
+ dasd_disable_ranges (&dasd_range_head,
+ NULL, 1, 1);
+
#ifdef CONFIG_DASD_DIAG
+ if (MACHINE_IS_VM) {
dasd_diag_cleanup ();
- DASD_DRIVER_DEBUG_EVENT (1, "cleanup_dasd",
- "DIAG discipline %s", "success");
- printk (KERN_INFO PRINTK_HEADER
- "De-Registered DIAG discipline successfully\n");
-#endif /* CONFIG_DASD_ECKD_BUILTIN */
+
+ MESSAGE (KERN_INFO, "%s",
+ "De-Registered DIAG discipline successfully");
}
+#endif /* CONFIG_DASD_DIAG */
+
#ifdef CONFIG_DASD_FBA
dasd_fba_cleanup ();
- DASD_DRIVER_DEBUG_EVENT (1, "cleanup_dasd",
- "FBA discipline %s", "success");
- printk (KERN_INFO PRINTK_HEADER
- "De-Registered FBA discipline successfully\n");
-#endif /* CONFIG_DASD_ECKD_BUILTIN */
+
+ MESSAGE (KERN_INFO, "%s",
+ "De-Registered FBA discipline successfully");
+#endif /* CONFIG_DASD_FBA */
+
#ifdef CONFIG_DASD_ECKD
dasd_eckd_cleanup ();
- DASD_DRIVER_DEBUG_EVENT (1, "cleanup_dasd",
- "ECKD discipline %s", "success");
- printk (KERN_INFO PRINTK_HEADER
- "De-Registered ECKD discipline successfully\n");
-#endif /* CONFIG_DASD_ECKD_BUILTIN */
- genhd_dasd_name = NULL;
- genhd_dasd_ioctl = NULL;
+ MESSAGE (KERN_INFO, "%s",
+ "De-Registered ECKD discipline successfully");
+#endif /* CONFIG_DASD_ECKD */
+
+ genhd_dasd_name = NULL;
+ genhd_dasd_ioctl = NULL;
+
+#ifdef CONFIG_PROC_FS
dasd_proc_cleanup ();
+#endif /* CONFIG_PROC_FS */
- list_for_each_safe (l, n, &dasd_major_info[0].list) {
+ list_for_each_safe (l, n, &dasd_major_info) {
major_info = list_entry (l, major_info_t, list);
for (i = 0; i < DASD_PER_MAJOR; i++) {
kfree (major_info->dasd_device[i]);
}
if ((major_info->flags & DASD_MAJOR_INFO_REGISTERED) &&
(rc = dasd_unregister_major (major_info)) == 0) {
- DASD_DRIVER_DEBUG_EVENT (1, "cleanup_dasd",
- "major %d: success",
- major_info->gendisk.major);
- printk (KERN_INFO PRINTK_HEADER
- "Unregistered successfully from major no %u\n",
+
+ MESSAGE (KERN_INFO,
+ "Unregistered successfully from major no %u",
major_info->gendisk.major);
} else {
- DASD_DRIVER_DEBUG_EVENT (1, "cleanup_dasd",
- "major %d: failed",
- major_info->gendisk.major);
- printk (KERN_WARNING PRINTK_HEADER
- "Couldn't unregister successfully from major no %d rc = %d\n",
- major_info->gendisk.major, rc);
+
+ MESSAGE (KERN_WARNING,
+ "Couldn't unregister successfully from major "
+ "no %d rc = %d",
+ major_info->gendisk.major,
+ rc);
}
}
list_for_each_safe (l, n, &dasd_range_head.list) {
@@ -4360,10 +5540,14 @@
#endif /* MODULE */
if (dasd_devfs_handle)
devfs_unregister(dasd_devfs_handle);
- if (dasd_debug_area != NULL )
+
+ if (dasd_debug_area != NULL ) {
debug_unregister(dasd_debug_area);
- printk (KERN_INFO PRINTK_HEADER "shutdown completed\n");
- DASD_DRIVER_DEBUG_EVENT(0,"cleanup_dasd","%s","LEAVE");
+ dasd_debug_area = NULL;
+ }
+
+ MESSAGE (KERN_INFO, "%s",
+ "shutdown completed");
}
#ifdef MODULE
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)