Commit fb1a1633 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] s390: channel measurement block interface.

From: Martin Schwidefsky <schwidefsky@de.ibm.com>

Add channel measurement block interface.
parent 7131c7a6
...@@ -139,6 +139,7 @@ CONFIG_DASD=y ...@@ -139,6 +139,7 @@ CONFIG_DASD=y
CONFIG_DASD_ECKD=y CONFIG_DASD_ECKD=y
CONFIG_DASD_FBA=y CONFIG_DASD_FBA=y
CONFIG_DASD_DIAG=y CONFIG_DASD_DIAG=y
# CONFIG_DASD_CMB is not set
# #
# Multi-device support (RAID and LVM) # Multi-device support (RAID and LVM)
......
...@@ -47,3 +47,13 @@ config DASD_DIAG ...@@ -47,3 +47,13 @@ config DASD_DIAG
Select this option if you want to use CMS reserved Disks under VM Select this option if you want to use CMS reserved Disks under VM
with the Diagnose250 command. If you are not running under VM or with the Diagnose250 command. If you are not running under VM or
unsure what it is, say "N". unsure what it is, say "N".
config DASD_CMB
tristate "Compatibility interface for DASD channel measurement blocks"
depends on DASD
help
This driver provides an additional interface to the channel measurement
facility, which is normally accessed though sysfs, with a set of
ioctl functions specific to the dasd driver.
This is only needed if you want to use applications written for
linux-2.4 dasd channel measurement facility interface.
...@@ -12,4 +12,5 @@ obj-$(CONFIG_DASD) += dasd_mod.o ...@@ -12,4 +12,5 @@ obj-$(CONFIG_DASD) += dasd_mod.o
obj-$(CONFIG_DASD_DIAG) += dasd_diag_mod.o obj-$(CONFIG_DASD_DIAG) += dasd_diag_mod.o
obj-$(CONFIG_DASD_ECKD) += dasd_eckd_mod.o obj-$(CONFIG_DASD_ECKD) += dasd_eckd_mod.o
obj-$(CONFIG_DASD_FBA) += dasd_fba_mod.o obj-$(CONFIG_DASD_FBA) += dasd_fba_mod.o
obj-$(CONFIG_DASD_CMB) += dasd_cmb.o
obj-$(CONFIG_BLK_DEV_XPRAM) += xpram.o obj-$(CONFIG_BLK_DEV_XPRAM) += xpram.o
/*
* linux/drivers/s390/block/dasd_cmb.c ($Revision: 1.6 $)
*
* Linux on zSeries Channel Measurement Facility support
* (dasd device driver interface)
*
* Copyright 2000,2003 IBM Corporation
*
* Author: Arnd Bergmann <arndb@de.ibm.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/init.h>
#include <linux/ioctl32.h>
#include <linux/module.h>
#include <asm/ccwdev.h>
#include <asm/cmb.h>
#include "dasd_int.h"
static int
dasd_ioctl_cmf_enable(struct block_device *bdev, int no, long args)
{
struct dasd_device *device;
device = bdev->bd_disk->private_data;
if (!device)
return -EINVAL;
return enable_cmf(device->cdev);
}
static int
dasd_ioctl_cmf_disable(struct block_device *bdev, int no, long args)
{
struct dasd_device *device;
device = bdev->bd_disk->private_data;
if (!device)
return -EINVAL;
return disable_cmf(device->cdev);
}
static int
dasd_ioctl_readall_cmb(struct block_device *bdev, int no, long args)
{
struct dasd_device *device;
struct cmbdata * __user udata;
struct cmbdata data;
size_t size;
int ret;
device = bdev->bd_disk->private_data;
if (!device)
return -EINVAL;
udata = (void *) args;
size = _IOC_SIZE(no);
if (!access_ok(VERIFY_WRITE, udata, size))
return -EFAULT;
ret = cmf_readall(device->cdev, &data);
if (ret)
return ret;
if (copy_to_user(udata, &data, min(size, sizeof(*udata))))
return -EFAULT;
return 0;
}
/* module initialization below here. dasd already provides a mechanism
* to dynamically register ioctl functions, so we simply use this. */
static inline int
ioctl_reg(unsigned int no, dasd_ioctl_fn_t handler)
{
int ret;
ret = dasd_ioctl_no_register(THIS_MODULE, no, handler);
#ifdef CONFIG_COMPAT
if (ret)
return ret;
ret = register_ioctl32_conversion(no, NULL);
if (ret)
dasd_ioctl_no_unregister(THIS_MODULE, no, handler);
#endif
return ret;
}
static inline void
ioctl_unreg(unsigned int no, dasd_ioctl_fn_t handler)
{
dasd_ioctl_no_unregister(THIS_MODULE, no, handler);
#ifdef CONFIG_COMPAT
unregister_ioctl32_conversion(no);
#endif
}
static void
dasd_cmf_exit(void)
{
ioctl_unreg(BIODASDCMFENABLE, dasd_ioctl_cmf_enable);
ioctl_unreg(BIODASDCMFDISABLE, dasd_ioctl_cmf_disable);
ioctl_unreg(BIODASDREADALLCMB, dasd_ioctl_readall_cmb);
}
static int __init
dasd_cmf_init(void)
{
int ret;
ret = ioctl_reg (BIODASDCMFENABLE, dasd_ioctl_cmf_enable);
if (ret)
goto err;
ret = ioctl_reg (BIODASDCMFDISABLE, dasd_ioctl_cmf_disable);
if (ret)
goto err;
ret = ioctl_reg (BIODASDREADALLCMB, dasd_ioctl_readall_cmb);
if (ret)
goto err;
return 0;
err:
dasd_cmf_exit();
return ret;
}
module_init(dasd_cmf_init);
module_exit(dasd_cmf_exit);
MODULE_AUTHOR("Arnd Bergmann <arndb@de.ibm.com>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("channel measurement facility interface for dasd\n"
"Copyright 2003 IBM Corporation\n");
...@@ -5,6 +5,6 @@ ...@@ -5,6 +5,6 @@
obj-y += airq.o blacklist.o chsc.o cio.o css.o requestirq.o obj-y += airq.o blacklist.o chsc.o cio.o css.o requestirq.o
ccw_device-objs += device.o device_fsm.o device_ops.o ccw_device-objs += device.o device_fsm.o device_ops.o
ccw_device-objs += device_id.o device_pgid.o device_status.o ccw_device-objs += device_id.o device_pgid.o device_status.o
obj-y += ccw_device.o obj-y += ccw_device.o cmf.o
obj-$(CONFIG_CCWGROUP) += ccwgroup.o obj-$(CONFIG_CCWGROUP) += ccwgroup.o
obj-$(CONFIG_QDIO) += qdio.o obj-$(CONFIG_QDIO) += qdio.o
...@@ -37,7 +37,9 @@ struct pmcw { ...@@ -37,7 +37,9 @@ struct pmcw {
__u8 chpid[8]; /* CHPID 0-7 (if available) */ __u8 chpid[8]; /* CHPID 0-7 (if available) */
__u32 unused1 : 8; /* reserved zeros */ __u32 unused1 : 8; /* reserved zeros */
__u32 st : 3; /* subchannel type */ __u32 st : 3; /* subchannel type */
__u32 unused2 : 20; /* reserved zeros */ __u32 unused2 : 18; /* reserved zeros */
__u32 mbfc : 1; /* measurement block format control */
__u32 xmwme : 1; /* extended measurement word mode enable */
__u32 csense : 1; /* concurrent sense; can be enabled ...*/ __u32 csense : 1; /* concurrent sense; can be enabled ...*/
/* ... per MSCH, however, if facility */ /* ... per MSCH, however, if facility */
/* ... is not installed, this results */ /* ... is not installed, this results */
...@@ -50,7 +52,8 @@ struct pmcw { ...@@ -50,7 +52,8 @@ struct pmcw {
struct schib { struct schib {
struct pmcw pmcw; /* path management control word */ struct pmcw pmcw; /* path management control word */
struct scsw scsw; /* subchannel status word */ struct scsw scsw; /* subchannel status word */
__u8 mda[12]; /* model dependent area */ __u64 mba; /* measurement block address */
__u8 mda[4]; /* model dependent area */
} __attribute__ ((packed,aligned(4))); } __attribute__ ((packed,aligned(4)));
/* /*
......
This diff is collapsed.
...@@ -91,6 +91,10 @@ struct ccw_device_private { ...@@ -91,6 +91,10 @@ struct ccw_device_private {
struct work_struct kick_work; struct work_struct kick_work;
wait_queue_head_t wait_q; wait_queue_head_t wait_q;
struct timer_list timer; struct timer_list timer;
void *cmb; /* measurement information */
struct list_head cmb_list; /* list of measured devices */
u64 cmb_start_time; /* clock value of cmb reset */
void *cmb_wait; /* deferred cmb enable/disable */
}; };
/* /*
......
...@@ -404,6 +404,7 @@ static DEVICE_ATTR(devtype, 0444, devtype_show, NULL); ...@@ -404,6 +404,7 @@ static DEVICE_ATTR(devtype, 0444, devtype_show, NULL);
static DEVICE_ATTR(cutype, 0444, cutype_show, NULL); static DEVICE_ATTR(cutype, 0444, cutype_show, NULL);
static DEVICE_ATTR(online, 0644, online_show, online_store); static DEVICE_ATTR(online, 0644, online_show, online_store);
static DEVICE_ATTR(steal_lock, 0200, NULL, stlck_store); static DEVICE_ATTR(steal_lock, 0200, NULL, stlck_store);
extern struct device_attribute dev_attr_cmb_enable;
static DEVICE_ATTR(availability, 0444, available_show, NULL); static DEVICE_ATTR(availability, 0444, available_show, NULL);
/* A device has been unboxed. Start device recognition. */ /* A device has been unboxed. Start device recognition. */
...@@ -449,6 +450,7 @@ static struct attribute * ccwdev_attrs[] = { ...@@ -449,6 +450,7 @@ static struct attribute * ccwdev_attrs[] = {
&dev_attr_devtype.attr, &dev_attr_devtype.attr,
&dev_attr_cutype.attr, &dev_attr_cutype.attr,
&dev_attr_online.attr, &dev_attr_online.attr,
&dev_attr_cmb_enable.attr,
&dev_attr_availability.attr, &dev_attr_availability.attr,
NULL, NULL,
}; };
...@@ -634,6 +636,7 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch) ...@@ -634,6 +636,7 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch)
.devno = sch->schib.pmcw.dev, .devno = sch->schib.pmcw.dev,
.irq = sch->irq, .irq = sch->irq,
.state = DEV_STATE_NOT_OPER, .state = DEV_STATE_NOT_OPER,
.cmb_list = LIST_HEAD_INIT(cdev->private->cmb_list),
}; };
init_waitqueue_head(&cdev->private->wait_q); init_waitqueue_head(&cdev->private->wait_q);
init_timer(&cdev->private->timer); init_timer(&cdev->private->timer);
......
...@@ -21,6 +21,7 @@ enum dev_state { ...@@ -21,6 +21,7 @@ enum dev_state {
/* special states for devices gone not operational */ /* special states for devices gone not operational */
DEV_STATE_DISCONNECTED, DEV_STATE_DISCONNECTED,
DEV_STATE_DISCONNECTED_SENSE_ID, DEV_STATE_DISCONNECTED_SENSE_ID,
DEV_STATE_CMFCHANGE,
/* last element! */ /* last element! */
NR_DEV_STATES NR_DEV_STATES
}; };
...@@ -106,4 +107,6 @@ int ccw_device_stlck(struct ccw_device *); ...@@ -106,4 +107,6 @@ int ccw_device_stlck(struct ccw_device *);
/* qdio needs this. */ /* qdio needs this. */
void ccw_device_set_timeout(struct ccw_device *, int); void ccw_device_set_timeout(struct ccw_device *, int);
void retry_set_schib(struct ccw_device *cdev);
#endif #endif
...@@ -995,6 +995,15 @@ ccw_device_offline_irq(struct ccw_device *cdev, enum dev_event dev_event) ...@@ -995,6 +995,15 @@ ccw_device_offline_irq(struct ccw_device *cdev, enum dev_event dev_event)
cio_disable_subchannel(sch); cio_disable_subchannel(sch);
} }
static void
ccw_device_change_cmfstate(struct ccw_device *cdev, enum dev_event dev_event)
{
retry_set_schib(cdev);
cdev->private->state = DEV_STATE_ONLINE;
dev_fsm_event(cdev, dev_event);
}
/* /*
* No operation action. This is used e.g. to ignore a timeout event in * No operation action. This is used e.g. to ignore a timeout event in
* state offline. * state offline.
...@@ -1105,6 +1114,12 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = { ...@@ -1105,6 +1114,12 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = {
[DEV_EVENT_TIMEOUT] ccw_device_recog_timeout, [DEV_EVENT_TIMEOUT] ccw_device_recog_timeout,
[DEV_EVENT_VERIFY] ccw_device_nop, [DEV_EVENT_VERIFY] ccw_device_nop,
}, },
[DEV_STATE_CMFCHANGE] {
[DEV_EVENT_NOTOPER] ccw_device_change_cmfstate,
[DEV_EVENT_INTERRUPT] ccw_device_change_cmfstate,
[DEV_EVENT_TIMEOUT] ccw_device_change_cmfstate,
[DEV_EVENT_VERIFY] ccw_device_change_cmfstate,
},
}; };
/* /*
......
#ifndef S390_CMB_H
#define S390_CMB_H
/**
* struct cmbdata -- channel measurement block data for user space
*
* @size: size of the stored data
* @ssch_rsch_count: XXX
* @sample_count:
* @device_connect_time:
* @function_pending_time:
* @device_disconnect_time:
* @control_unit_queuing_time:
* @device_active_only_time:
* @device_busy_time:
* @initial_command_response_time:
*
* all values are stored as 64 bit for simplicity, especially
* in 32 bit emulation mode. All time values are normalized to
* nanoseconds.
* Currently, two formats are known, which differ by the size of
* this structure, i.e. the last two members are only set when
* the extended channel measurement facility (first shipped in
* z990 machines) is activated.
* Potentially, more fields could be added, which results in a
* new ioctl number.
**/
struct cmbdata {
__u64 size;
__u64 elapsed_time;
/* basic and exended format: */
__u64 ssch_rsch_count;
__u64 sample_count;
__u64 device_connect_time;
__u64 function_pending_time;
__u64 device_disconnect_time;
__u64 control_unit_queuing_time;
__u64 device_active_only_time;
/* extended format only: */
__u64 device_busy_time;
__u64 initial_command_response_time;
};
/* enable channel measurement */
#define BIODASDCMFENABLE _IO(DASD_IOCTL_LETTER,32)
/* enable channel measurement */
#define BIODASDCMFDISABLE _IO(DASD_IOCTL_LETTER,33)
/* reset channel measurement block */
#define BIODASDRESETCMB _IO(DASD_IOCTL_LETTER,34)
/* read channel measurement data */
#define BIODASDREADCMB _IOWR(DASD_IOCTL_LETTER,32,u64)
/* read channel measurement data */
#define BIODASDREADALLCMB _IOWR(DASD_IOCTL_LETTER,33,struct cmbdata)
#ifdef __KERNEL__
/**
* enable_cmf() - switch on the channel measurement for a specific device
* @cdev: The ccw device to be enabled
* returns 0 for success or a negative error value.
*
* Context:
* non-atomic
**/
extern int enable_cmf(struct ccw_device *cdev);
/**
* disable_cmf() - switch off the channel measurement for a specific device
* @cdev: The ccw device to be disabled
* returns 0 for success or a negative error value.
*
* Context:
* non-atomic
**/
extern int disable_cmf(struct ccw_device *cdev);
/**
* cmf_read() - read one value from the current channel measurement block
* @cmf: the channel to be read
* @index: the name of the value that is read
*
* Context:
* any
**/
extern u64 cmf_read(struct ccw_device *cdev, int index);
/**
* cmf_readall() - read one value from the current channel measurement block
* @cmf: the channel to be read
* @data: a pointer to a data block that will be filled
*
* Context:
* any
**/
extern int cmf_readall(struct ccw_device *cdev, struct cmbdata*data);
extern void cmf_reset(struct ccw_device *cdev);
#endif /* __KERNEL__ */
#endif /* S390_CMB_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment