Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux
Pull s390 updates from Martin Schwidefsky: "There is only one new feature in this pull for the 4.4 merge window, most of it is small enhancements, cleanup and bug fixes: - Add the s390 backend for the software dirty bit tracking. This adds two new pgtable functions pte_clear_soft_dirty and pmd_clear_soft_dirty which is why there is a hit to arch/x86/include/asm/pgtable.h in this pull request. - A series of cleanup patches for the AP bus, this includes the removal of the support for two outdated crypto cards (PCICC and PCICA). - The irq handling / signaling on buffer full in the runtime instrumentation code is dropped. - Some micro optimizations: remove unnecessary memory barriers for a couple of functions: [smb_]rmb, [smb_]wmb, atomics, bitops, and for spin_unlock. Use the builtin bswap if available and make test_and_set_bit_lock more cache friendly. - Statistics and a tracepoint for the diagnose calls to the hypervisor. - The CPU measurement facility support to sample KVM guests is improved. - The vector instructions are now always enabled for user space processes if the hardware has the vector facility. This simplifies the FPU handling code. The fpu-internal.h header is split into fpu internals, api and types just like x86. - Cleanup and improvements for the common I/O layer. - Rework udelay to solve a problem with kprobe. udelay has busy loop semantics but still uses an idle processor state for the wait" * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux: (66 commits) s390: remove runtime instrumentation interrupts s390/cio: de-duplicate subchannel validation s390/css: unneeded initialization in for_each_subchannel s390/Kconfig: use builtin bswap s390/dasd: fix disconnected device with valid path mask s390/dasd: fix invalid PAV assignment after suspend/resume s390/dasd: fix double free in dasd_eckd_read_conf s390/kernel: fix ptrace peek/poke for floating point registers s390/cio: move ccw_device_stlck functions s390/cio: move ccw_device_call_handler s390/topology: reduce per_cpu() invocations s390/nmi: reduce size of percpu variable s390/nmi: fix terminology s390/nmi: remove casts s390/nmi: remove pointless error strings s390: don't store registers on disabled wait anymore s390: get rid of __set_psw_mask() s390/fpu: split fpu-internal.h into fpu internals, api, and type headers s390/dasd: fix list_del corruption after lcu changes s390/spinlock: remove unneeded serializations at unlock ...
This commit is contained in:
@@ -476,26 +476,6 @@ static int cio_check_devno_blacklisted(struct subchannel *sch)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cio_validate_io_subchannel(struct subchannel *sch)
|
||||
{
|
||||
/* Initialization for io subchannels. */
|
||||
if (!css_sch_is_valid(&sch->schib))
|
||||
return -ENODEV;
|
||||
|
||||
/* Devno is valid. */
|
||||
return cio_check_devno_blacklisted(sch);
|
||||
}
|
||||
|
||||
static int cio_validate_msg_subchannel(struct subchannel *sch)
|
||||
{
|
||||
/* Initialization for message subchannels. */
|
||||
if (!css_sch_is_valid(&sch->schib))
|
||||
return -ENODEV;
|
||||
|
||||
/* Devno is valid. */
|
||||
return cio_check_devno_blacklisted(sch);
|
||||
}
|
||||
|
||||
/**
|
||||
* cio_validate_subchannel - basic validation of subchannel
|
||||
* @sch: subchannel structure to be filled out
|
||||
@@ -533,10 +513,11 @@ int cio_validate_subchannel(struct subchannel *sch, struct subchannel_id schid)
|
||||
|
||||
switch (sch->st) {
|
||||
case SUBCHANNEL_TYPE_IO:
|
||||
err = cio_validate_io_subchannel(sch);
|
||||
break;
|
||||
case SUBCHANNEL_TYPE_MSG:
|
||||
err = cio_validate_msg_subchannel(sch);
|
||||
if (!css_sch_is_valid(&sch->schib))
|
||||
err = -ENODEV;
|
||||
else
|
||||
err = cio_check_devno_blacklisted(sch);
|
||||
break;
|
||||
default:
|
||||
err = 0;
|
||||
@@ -826,11 +807,11 @@ static atomic_t chpid_reset_count;
|
||||
static void s390_reset_chpids_mcck_handler(void)
|
||||
{
|
||||
struct crw crw;
|
||||
struct mci *mci;
|
||||
union mci mci;
|
||||
|
||||
/* Check for pending channel report word. */
|
||||
mci = (struct mci *)&S390_lowcore.mcck_interruption_code;
|
||||
if (!mci->cp)
|
||||
mci.val = S390_lowcore.mcck_interruption_code;
|
||||
if (!mci.cp)
|
||||
return;
|
||||
/* Process channel report words. */
|
||||
while (stcrw(&crw) == 0) {
|
||||
|
@@ -113,7 +113,6 @@ module_param(format, bint, 0444);
|
||||
* @readall: read a measurement block in a common format
|
||||
* @reset: clear the data in the associated measurement block and
|
||||
* reset its time stamp
|
||||
* @align: align an allocated block so that the hardware can use it
|
||||
*/
|
||||
struct cmb_operations {
|
||||
int (*alloc) (struct ccw_device *);
|
||||
@@ -122,7 +121,6 @@ struct cmb_operations {
|
||||
u64 (*read) (struct ccw_device *, int);
|
||||
int (*readall)(struct ccw_device *, struct cmbdata *);
|
||||
void (*reset) (struct ccw_device *);
|
||||
void *(*align) (void *);
|
||||
/* private: */
|
||||
struct attribute_group *attr_group;
|
||||
};
|
||||
@@ -186,9 +184,8 @@ static inline void cmf_activate(void *area, unsigned int onoff)
|
||||
static int set_schib(struct ccw_device *cdev, u32 mme, int mbfc,
|
||||
unsigned long address)
|
||||
{
|
||||
struct subchannel *sch;
|
||||
|
||||
sch = to_subchannel(cdev->dev.parent);
|
||||
struct subchannel *sch = to_subchannel(cdev->dev.parent);
|
||||
int ret;
|
||||
|
||||
sch->config.mme = mme;
|
||||
sch->config.mbfc = mbfc;
|
||||
@@ -198,7 +195,15 @@ static int set_schib(struct ccw_device *cdev, u32 mme, int mbfc,
|
||||
else
|
||||
sch->config.mbi = address;
|
||||
|
||||
return cio_commit_config(sch);
|
||||
ret = cio_commit_config(sch);
|
||||
if (!mme && ret == -ENODEV) {
|
||||
/*
|
||||
* The task was to disable measurement block updates but
|
||||
* the subchannel is already gone. Report success.
|
||||
*/
|
||||
ret = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct set_schib_struct {
|
||||
@@ -314,7 +319,7 @@ static int cmf_copy_block(struct ccw_device *cdev)
|
||||
return -EBUSY;
|
||||
}
|
||||
cmb_data = cdev->private->cmb;
|
||||
hw_block = cmbops->align(cmb_data->hw_block);
|
||||
hw_block = cmb_data->hw_block;
|
||||
if (!memcmp(cmb_data->last_block, hw_block, cmb_data->size))
|
||||
/* No need to copy. */
|
||||
return 0;
|
||||
@@ -425,7 +430,7 @@ static void cmf_generic_reset(struct ccw_device *cdev)
|
||||
* Need to reset hw block as well to make the hardware start
|
||||
* from 0 again.
|
||||
*/
|
||||
memset(cmbops->align(cmb_data->hw_block), 0, cmb_data->size);
|
||||
memset(cmb_data->hw_block, 0, cmb_data->size);
|
||||
cmb_data->last_update = 0;
|
||||
}
|
||||
cdev->private->cmb_start_time = get_tod_clock();
|
||||
@@ -606,12 +611,6 @@ static void free_cmb(struct ccw_device *cdev)
|
||||
spin_lock_irq(cdev->ccwlock);
|
||||
|
||||
priv = cdev->private;
|
||||
|
||||
if (list_empty(&priv->cmb_list)) {
|
||||
/* already freed */
|
||||
goto out;
|
||||
}
|
||||
|
||||
cmb_data = priv->cmb;
|
||||
priv->cmb = NULL;
|
||||
if (cmb_data)
|
||||
@@ -626,7 +625,6 @@ static void free_cmb(struct ccw_device *cdev)
|
||||
free_pages((unsigned long)cmb_area.mem, get_order(size));
|
||||
cmb_area.mem = NULL;
|
||||
}
|
||||
out:
|
||||
spin_unlock_irq(cdev->ccwlock);
|
||||
spin_unlock(&cmb_area.lock);
|
||||
}
|
||||
@@ -755,11 +753,6 @@ static void reset_cmb(struct ccw_device *cdev)
|
||||
cmf_generic_reset(cdev);
|
||||
}
|
||||
|
||||
static void * align_cmb(void *area)
|
||||
{
|
||||
return area;
|
||||
}
|
||||
|
||||
static struct attribute_group cmf_attr_group;
|
||||
|
||||
static struct cmb_operations cmbops_basic = {
|
||||
@@ -769,7 +762,6 @@ static struct cmb_operations cmbops_basic = {
|
||||
.read = read_cmb,
|
||||
.readall = readall_cmb,
|
||||
.reset = reset_cmb,
|
||||
.align = align_cmb,
|
||||
.attr_group = &cmf_attr_group,
|
||||
};
|
||||
|
||||
@@ -804,64 +796,57 @@ struct cmbe {
|
||||
u32 device_busy_time;
|
||||
u32 initial_command_response_time;
|
||||
u32 reserved[7];
|
||||
};
|
||||
} __packed __aligned(64);
|
||||
|
||||
/*
|
||||
* kmalloc only guarantees 8 byte alignment, but we need cmbe
|
||||
* pointers to be naturally aligned. Make sure to allocate
|
||||
* enough space for two cmbes.
|
||||
*/
|
||||
static inline struct cmbe *cmbe_align(struct cmbe *c)
|
||||
{
|
||||
unsigned long addr;
|
||||
addr = ((unsigned long)c + sizeof (struct cmbe) - sizeof(long)) &
|
||||
~(sizeof (struct cmbe) - sizeof(long));
|
||||
return (struct cmbe*)addr;
|
||||
}
|
||||
static struct kmem_cache *cmbe_cache;
|
||||
|
||||
static int alloc_cmbe(struct ccw_device *cdev)
|
||||
{
|
||||
struct cmbe *cmbe;
|
||||
struct cmb_data *cmb_data;
|
||||
int ret;
|
||||
struct cmbe *cmbe;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
cmbe = kzalloc (sizeof (*cmbe) * 2, GFP_KERNEL);
|
||||
cmbe = kmem_cache_zalloc(cmbe_cache, GFP_KERNEL);
|
||||
if (!cmbe)
|
||||
return -ENOMEM;
|
||||
cmb_data = kzalloc(sizeof(struct cmb_data), GFP_KERNEL);
|
||||
if (!cmb_data) {
|
||||
ret = -ENOMEM;
|
||||
return ret;
|
||||
|
||||
cmb_data = kzalloc(sizeof(*cmb_data), GFP_KERNEL);
|
||||
if (!cmb_data)
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
cmb_data->last_block = kzalloc(sizeof(struct cmbe), GFP_KERNEL);
|
||||
if (!cmb_data->last_block) {
|
||||
ret = -ENOMEM;
|
||||
if (!cmb_data->last_block)
|
||||
goto out_free;
|
||||
}
|
||||
cmb_data->size = sizeof(struct cmbe);
|
||||
spin_lock_irq(cdev->ccwlock);
|
||||
if (cdev->private->cmb) {
|
||||
spin_unlock_irq(cdev->ccwlock);
|
||||
ret = -EBUSY;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
cmb_data->size = sizeof(*cmbe);
|
||||
cmb_data->hw_block = cmbe;
|
||||
|
||||
spin_lock(&cmb_area.lock);
|
||||
spin_lock_irq(cdev->ccwlock);
|
||||
if (cdev->private->cmb)
|
||||
goto out_unlock;
|
||||
|
||||
cdev->private->cmb = cmb_data;
|
||||
spin_unlock_irq(cdev->ccwlock);
|
||||
|
||||
/* activate global measurement if this is the first channel */
|
||||
spin_lock(&cmb_area.lock);
|
||||
if (list_empty(&cmb_area.list))
|
||||
cmf_activate(NULL, 1);
|
||||
list_add_tail(&cdev->private->cmb_list, &cmb_area.list);
|
||||
spin_unlock(&cmb_area.lock);
|
||||
|
||||
spin_unlock_irq(cdev->ccwlock);
|
||||
spin_unlock(&cmb_area.lock);
|
||||
return 0;
|
||||
|
||||
out_unlock:
|
||||
spin_unlock_irq(cdev->ccwlock);
|
||||
spin_unlock(&cmb_area.lock);
|
||||
ret = -EBUSY;
|
||||
out_free:
|
||||
if (cmb_data)
|
||||
kfree(cmb_data->last_block);
|
||||
kfree(cmb_data);
|
||||
kfree(cmbe);
|
||||
kmem_cache_free(cmbe_cache, cmbe);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -869,19 +854,21 @@ static void free_cmbe(struct ccw_device *cdev)
|
||||
{
|
||||
struct cmb_data *cmb_data;
|
||||
|
||||
spin_lock(&cmb_area.lock);
|
||||
spin_lock_irq(cdev->ccwlock);
|
||||
cmb_data = cdev->private->cmb;
|
||||
cdev->private->cmb = NULL;
|
||||
if (cmb_data)
|
||||
if (cmb_data) {
|
||||
kfree(cmb_data->last_block);
|
||||
kmem_cache_free(cmbe_cache, cmb_data->hw_block);
|
||||
}
|
||||
kfree(cmb_data);
|
||||
spin_unlock_irq(cdev->ccwlock);
|
||||
|
||||
/* deactivate global measurement if this is the last channel */
|
||||
spin_lock(&cmb_area.lock);
|
||||
list_del_init(&cdev->private->cmb_list);
|
||||
if (list_empty(&cmb_area.list))
|
||||
cmf_activate(NULL, 0);
|
||||
spin_unlock_irq(cdev->ccwlock);
|
||||
spin_unlock(&cmb_area.lock);
|
||||
}
|
||||
|
||||
@@ -897,7 +884,7 @@ static int set_cmbe(struct ccw_device *cdev, u32 mme)
|
||||
return -EINVAL;
|
||||
}
|
||||
cmb_data = cdev->private->cmb;
|
||||
mba = mme ? (unsigned long) cmbe_align(cmb_data->hw_block) : 0;
|
||||
mba = mme ? (unsigned long) cmb_data->hw_block : 0;
|
||||
spin_unlock_irqrestore(cdev->ccwlock, flags);
|
||||
|
||||
return set_schib_wait(cdev, mme, 1, mba);
|
||||
@@ -1022,11 +1009,6 @@ static void reset_cmbe(struct ccw_device *cdev)
|
||||
cmf_generic_reset(cdev);
|
||||
}
|
||||
|
||||
static void * align_cmbe(void *area)
|
||||
{
|
||||
return cmbe_align(area);
|
||||
}
|
||||
|
||||
static struct attribute_group cmf_attr_group_ext;
|
||||
|
||||
static struct cmb_operations cmbops_extended = {
|
||||
@@ -1036,7 +1018,6 @@ static struct cmb_operations cmbops_extended = {
|
||||
.read = read_cmbe,
|
||||
.readall = readall_cmbe,
|
||||
.reset = reset_cmbe,
|
||||
.align = align_cmbe,
|
||||
.attr_group = &cmf_attr_group_ext,
|
||||
};
|
||||
|
||||
@@ -1171,23 +1152,28 @@ static ssize_t cmb_enable_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return sprintf(buf, "%d\n", to_ccwdev(dev)->private->cmb ? 1 : 0);
|
||||
struct ccw_device *cdev = to_ccwdev(dev);
|
||||
int enabled;
|
||||
|
||||
spin_lock_irq(cdev->ccwlock);
|
||||
enabled = !!cdev->private->cmb;
|
||||
spin_unlock_irq(cdev->ccwlock);
|
||||
|
||||
return sprintf(buf, "%d\n", enabled);
|
||||
}
|
||||
|
||||
static ssize_t cmb_enable_store(struct device *dev,
|
||||
struct device_attribute *attr, const char *buf,
|
||||
size_t c)
|
||||
{
|
||||
struct ccw_device *cdev;
|
||||
int ret;
|
||||
struct ccw_device *cdev = to_ccwdev(dev);
|
||||
unsigned long val;
|
||||
int ret;
|
||||
|
||||
ret = kstrtoul(buf, 16, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
cdev = to_ccwdev(dev);
|
||||
|
||||
switch (val) {
|
||||
case 0:
|
||||
ret = disable_cmf(cdev);
|
||||
@@ -1195,12 +1181,13 @@ static ssize_t cmb_enable_store(struct device *dev,
|
||||
case 1:
|
||||
ret = enable_cmf(cdev);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
}
|
||||
|
||||
return c;
|
||||
return ret ? ret : c;
|
||||
}
|
||||
|
||||
DEVICE_ATTR(cmb_enable, 0644, cmb_enable_show, cmb_enable_store);
|
||||
DEVICE_ATTR_RW(cmb_enable);
|
||||
|
||||
int ccw_set_cmf(struct ccw_device *cdev, int enable)
|
||||
{
|
||||
@@ -1220,20 +1207,51 @@ int enable_cmf(struct ccw_device *cdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
device_lock(&cdev->dev);
|
||||
get_device(&cdev->dev);
|
||||
ret = cmbops->alloc(cdev);
|
||||
cmbops->reset(cdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = cmbops->set(cdev, 2);
|
||||
goto out;
|
||||
cmbops->reset(cdev);
|
||||
ret = sysfs_create_group(&cdev->dev.kobj, cmbops->attr_group);
|
||||
if (ret) {
|
||||
cmbops->free(cdev);
|
||||
return ret;
|
||||
goto out;
|
||||
}
|
||||
ret = sysfs_create_group(&cdev->dev.kobj, cmbops->attr_group);
|
||||
if (!ret)
|
||||
return 0;
|
||||
cmbops->set(cdev, 0); //FIXME: this can fail
|
||||
ret = cmbops->set(cdev, 2);
|
||||
if (ret) {
|
||||
sysfs_remove_group(&cdev->dev.kobj, cmbops->attr_group);
|
||||
cmbops->free(cdev);
|
||||
}
|
||||
out:
|
||||
if (ret)
|
||||
put_device(&cdev->dev);
|
||||
|
||||
device_unlock(&cdev->dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* __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, device_lock() held.
|
||||
*/
|
||||
int __disable_cmf(struct ccw_device *cdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = cmbops->set(cdev, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sysfs_remove_group(&cdev->dev.kobj, cmbops->attr_group);
|
||||
cmbops->free(cdev);
|
||||
put_device(&cdev->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1250,11 +1268,10 @@ int disable_cmf(struct ccw_device *cdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = cmbops->set(cdev, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
cmbops->free(cdev);
|
||||
sysfs_remove_group(&cdev->dev.kobj, cmbops->attr_group);
|
||||
device_lock(&cdev->dev);
|
||||
ret = __disable_cmf(cdev);
|
||||
device_unlock(&cdev->dev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -1295,10 +1312,32 @@ int cmf_reenable(struct ccw_device *cdev)
|
||||
return cmbops->set(cdev, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* cmf_reactivate() - reactivate measurement block updates
|
||||
*
|
||||
* Use this during resume from hibernate.
|
||||
*/
|
||||
void cmf_reactivate(void)
|
||||
{
|
||||
spin_lock(&cmb_area.lock);
|
||||
if (!list_empty(&cmb_area.list))
|
||||
cmf_activate(cmb_area.mem, 1);
|
||||
spin_unlock(&cmb_area.lock);
|
||||
}
|
||||
|
||||
static int __init init_cmbe(void)
|
||||
{
|
||||
cmbe_cache = kmem_cache_create("cmbe_cache", sizeof(struct cmbe),
|
||||
__alignof__(struct cmbe), 0, NULL);
|
||||
|
||||
return cmbe_cache ? 0 : -ENOMEM;
|
||||
}
|
||||
|
||||
static int __init init_cmf(void)
|
||||
{
|
||||
char *format_string;
|
||||
char *detect_string = "parameter";
|
||||
char *detect_string;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* If the user did not give a parameter, see if we are running on a
|
||||
@@ -1324,15 +1363,18 @@ static int __init init_cmf(void)
|
||||
case CMF_EXTENDED:
|
||||
format_string = "extended";
|
||||
cmbops = &cmbops_extended;
|
||||
|
||||
ret = init_cmbe();
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
return -EINVAL;
|
||||
}
|
||||
pr_info("Channel measurement facility initialized using format "
|
||||
"%s (mode %s)\n", format_string, detect_string);
|
||||
return 0;
|
||||
}
|
||||
|
||||
module_init(init_cmf);
|
||||
|
||||
|
||||
|
@@ -44,7 +44,6 @@ for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *data)
|
||||
int ret;
|
||||
|
||||
init_subchannel_id(&schid);
|
||||
ret = -ENODEV;
|
||||
do {
|
||||
do {
|
||||
ret = fn(schid, data);
|
||||
@@ -1089,6 +1088,7 @@ void channel_subsystem_reinit(void)
|
||||
if (chp)
|
||||
chp_update_desc(chp);
|
||||
}
|
||||
cmf_reactivate();
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
|
@@ -1787,6 +1787,8 @@ static int ccw_device_remove(struct device *dev)
|
||||
cdev->drv = NULL;
|
||||
cdev->private->int_class = IRQIO_CIO;
|
||||
spin_unlock_irq(cdev->ccwlock);
|
||||
__disable_cmf(cdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1797,7 +1799,7 @@ static void ccw_device_shutdown(struct device *dev)
|
||||
cdev = to_ccwdev(dev);
|
||||
if (cdev->drv && cdev->drv->shutdown)
|
||||
cdev->drv->shutdown(cdev);
|
||||
disable_cmf(cdev);
|
||||
__disable_cmf(cdev);
|
||||
}
|
||||
|
||||
static int ccw_device_pm_prepare(struct device *dev)
|
||||
|
@@ -125,11 +125,6 @@ void ccw_device_verify_done(struct ccw_device *, int);
|
||||
void ccw_device_disband_start(struct ccw_device *);
|
||||
void ccw_device_disband_done(struct ccw_device *, int);
|
||||
|
||||
void ccw_device_stlck_start(struct ccw_device *, void *, void *, void *);
|
||||
void ccw_device_stlck_done(struct ccw_device *, void *, int);
|
||||
|
||||
int ccw_device_call_handler(struct ccw_device *);
|
||||
|
||||
int ccw_device_stlck(struct ccw_device *);
|
||||
|
||||
/* Helper function for machine check handling. */
|
||||
@@ -145,6 +140,7 @@ void ccw_device_set_timeout(struct ccw_device *, int);
|
||||
void retry_set_schib(struct ccw_device *cdev);
|
||||
void cmf_retry_copy_block(struct ccw_device *);
|
||||
int cmf_reenable(struct ccw_device *);
|
||||
void cmf_reactivate(void);
|
||||
int ccw_set_cmf(struct ccw_device *cdev, int enable);
|
||||
extern struct device_attribute dev_attr_cmb_enable;
|
||||
#endif
|
||||
|
@@ -730,6 +730,44 @@ static void ccw_device_boxed_verify(struct ccw_device *cdev,
|
||||
css_schedule_eval(sch->schid);
|
||||
}
|
||||
|
||||
/*
|
||||
* Pass interrupt to device driver.
|
||||
*/
|
||||
static int ccw_device_call_handler(struct ccw_device *cdev)
|
||||
{
|
||||
unsigned int stctl;
|
||||
int ending_status;
|
||||
|
||||
/*
|
||||
* we allow for the device action handler if .
|
||||
* - we received ending status
|
||||
* - the action handler requested to see all interrupts
|
||||
* - we received an intermediate status
|
||||
* - fast notification was requested (primary status)
|
||||
* - unsolicited interrupts
|
||||
*/
|
||||
stctl = scsw_stctl(&cdev->private->irb.scsw);
|
||||
ending_status = (stctl & SCSW_STCTL_SEC_STATUS) ||
|
||||
(stctl == (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)) ||
|
||||
(stctl == SCSW_STCTL_STATUS_PEND);
|
||||
if (!ending_status &&
|
||||
!cdev->private->options.repall &&
|
||||
!(stctl & SCSW_STCTL_INTER_STATUS) &&
|
||||
!(cdev->private->options.fast &&
|
||||
(stctl & SCSW_STCTL_PRIM_STATUS)))
|
||||
return 0;
|
||||
|
||||
if (ending_status)
|
||||
ccw_device_set_timeout(cdev, 0);
|
||||
|
||||
if (cdev->handler)
|
||||
cdev->handler(cdev, cdev->private->intparm,
|
||||
&cdev->private->irb);
|
||||
|
||||
memset(&cdev->private->irb, 0, sizeof(struct irb));
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Got an interrupt for a normal io (state online).
|
||||
*/
|
||||
|
@@ -412,52 +412,6 @@ int ccw_device_resume(struct ccw_device *cdev)
|
||||
return cio_resume(sch);
|
||||
}
|
||||
|
||||
/*
|
||||
* Pass interrupt to device driver.
|
||||
*/
|
||||
int
|
||||
ccw_device_call_handler(struct ccw_device *cdev)
|
||||
{
|
||||
unsigned int stctl;
|
||||
int ending_status;
|
||||
|
||||
/*
|
||||
* we allow for the device action handler if .
|
||||
* - we received ending status
|
||||
* - the action handler requested to see all interrupts
|
||||
* - we received an intermediate status
|
||||
* - fast notification was requested (primary status)
|
||||
* - unsolicited interrupts
|
||||
*/
|
||||
stctl = scsw_stctl(&cdev->private->irb.scsw);
|
||||
ending_status = (stctl & SCSW_STCTL_SEC_STATUS) ||
|
||||
(stctl == (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND)) ||
|
||||
(stctl == SCSW_STCTL_STATUS_PEND);
|
||||
if (!ending_status &&
|
||||
!cdev->private->options.repall &&
|
||||
!(stctl & SCSW_STCTL_INTER_STATUS) &&
|
||||
!(cdev->private->options.fast &&
|
||||
(stctl & SCSW_STCTL_PRIM_STATUS)))
|
||||
return 0;
|
||||
|
||||
/* Clear pending timers for device driver initiated I/O. */
|
||||
if (ending_status)
|
||||
ccw_device_set_timeout(cdev, 0);
|
||||
/*
|
||||
* Now we are ready to call the device driver interrupt handler.
|
||||
*/
|
||||
if (cdev->handler)
|
||||
cdev->handler(cdev, cdev->private->intparm,
|
||||
&cdev->private->irb);
|
||||
|
||||
/*
|
||||
* Clear the old and now useless interrupt response block.
|
||||
*/
|
||||
memset(&cdev->private->irb, 0, sizeof(struct irb));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* ccw_device_get_ciw() - Search for CIW command in extended sense data.
|
||||
* @cdev: ccw device to inspect
|
||||
@@ -502,67 +456,6 @@ __u8 ccw_device_get_path_mask(struct ccw_device *cdev)
|
||||
return sch->lpm;
|
||||
}
|
||||
|
||||
struct stlck_data {
|
||||
struct completion done;
|
||||
int rc;
|
||||
};
|
||||
|
||||
void ccw_device_stlck_done(struct ccw_device *cdev, void *data, int rc)
|
||||
{
|
||||
struct stlck_data *sdata = data;
|
||||
|
||||
sdata->rc = rc;
|
||||
complete(&sdata->done);
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform unconditional reserve + release.
|
||||
*/
|
||||
int ccw_device_stlck(struct ccw_device *cdev)
|
||||
{
|
||||
struct subchannel *sch = to_subchannel(cdev->dev.parent);
|
||||
struct stlck_data data;
|
||||
u8 *buffer;
|
||||
int rc;
|
||||
|
||||
/* Check if steal lock operation is valid for this device. */
|
||||
if (cdev->drv) {
|
||||
if (!cdev->private->options.force)
|
||||
return -EINVAL;
|
||||
}
|
||||
buffer = kzalloc(64, GFP_DMA | GFP_KERNEL);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
init_completion(&data.done);
|
||||
data.rc = -EIO;
|
||||
spin_lock_irq(sch->lock);
|
||||
rc = cio_enable_subchannel(sch, (u32) (addr_t) sch);
|
||||
if (rc)
|
||||
goto out_unlock;
|
||||
/* Perform operation. */
|
||||
cdev->private->state = DEV_STATE_STEAL_LOCK;
|
||||
ccw_device_stlck_start(cdev, &data, &buffer[0], &buffer[32]);
|
||||
spin_unlock_irq(sch->lock);
|
||||
/* Wait for operation to finish. */
|
||||
if (wait_for_completion_interruptible(&data.done)) {
|
||||
/* Got a signal. */
|
||||
spin_lock_irq(sch->lock);
|
||||
ccw_request_cancel(cdev);
|
||||
spin_unlock_irq(sch->lock);
|
||||
wait_for_completion(&data.done);
|
||||
}
|
||||
rc = data.rc;
|
||||
/* Check results. */
|
||||
spin_lock_irq(sch->lock);
|
||||
cio_disable_subchannel(sch);
|
||||
cdev->private->state = DEV_STATE_BOXED;
|
||||
out_unlock:
|
||||
spin_unlock_irq(sch->lock);
|
||||
kfree(buffer);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* chp_get_chp_desc - return newly allocated channel-path descriptor
|
||||
* @cdev: device to obtain the descriptor for
|
||||
|
@@ -9,9 +9,10 @@
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/ccwdev.h>
|
||||
#include <asm/cio.h>
|
||||
|
||||
@@ -133,7 +134,7 @@ static void spid_build_cp(struct ccw_device *cdev, u8 fn)
|
||||
{
|
||||
struct ccw_request *req = &cdev->private->req;
|
||||
struct ccw1 *cp = cdev->private->iccws;
|
||||
int i = 8 - ffs(req->lpm);
|
||||
int i = pathmask_to_pos(req->lpm);
|
||||
struct pgid *pgid = &cdev->private->pgid[i];
|
||||
|
||||
pgid->inf.fc = fn;
|
||||
@@ -434,7 +435,7 @@ static void snid_build_cp(struct ccw_device *cdev)
|
||||
{
|
||||
struct ccw_request *req = &cdev->private->req;
|
||||
struct ccw1 *cp = cdev->private->iccws;
|
||||
int i = 8 - ffs(req->lpm);
|
||||
int i = pathmask_to_pos(req->lpm);
|
||||
|
||||
/* Channel program setup. */
|
||||
cp->cmd_code = CCW_CMD_SENSE_PGID;
|
||||
@@ -616,6 +617,11 @@ void ccw_device_disband_start(struct ccw_device *cdev)
|
||||
ccw_request_start(cdev);
|
||||
}
|
||||
|
||||
struct stlck_data {
|
||||
struct completion done;
|
||||
int rc;
|
||||
};
|
||||
|
||||
static void stlck_build_cp(struct ccw_device *cdev, void *buf1, void *buf2)
|
||||
{
|
||||
struct ccw_request *req = &cdev->private->req;
|
||||
@@ -634,7 +640,10 @@ static void stlck_build_cp(struct ccw_device *cdev, void *buf1, void *buf2)
|
||||
|
||||
static void stlck_callback(struct ccw_device *cdev, void *data, int rc)
|
||||
{
|
||||
ccw_device_stlck_done(cdev, data, rc);
|
||||
struct stlck_data *sdata = data;
|
||||
|
||||
sdata->rc = rc;
|
||||
complete(&sdata->done);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -645,11 +654,9 @@ static void stlck_callback(struct ccw_device *cdev, void *data, int rc)
|
||||
* @buf2: data pointer used in channel program
|
||||
*
|
||||
* Execute a channel program on @cdev to release an existing PGID reservation.
|
||||
* When finished, call ccw_device_stlck_done with a return code specifying the
|
||||
* result.
|
||||
*/
|
||||
void ccw_device_stlck_start(struct ccw_device *cdev, void *data, void *buf1,
|
||||
void *buf2)
|
||||
static void ccw_device_stlck_start(struct ccw_device *cdev, void *data,
|
||||
void *buf1, void *buf2)
|
||||
{
|
||||
struct subchannel *sch = to_subchannel(cdev->dev.parent);
|
||||
struct ccw_request *req = &cdev->private->req;
|
||||
@@ -667,3 +674,50 @@ void ccw_device_stlck_start(struct ccw_device *cdev, void *data, void *buf1,
|
||||
ccw_request_start(cdev);
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform unconditional reserve + release.
|
||||
*/
|
||||
int ccw_device_stlck(struct ccw_device *cdev)
|
||||
{
|
||||
struct subchannel *sch = to_subchannel(cdev->dev.parent);
|
||||
struct stlck_data data;
|
||||
u8 *buffer;
|
||||
int rc;
|
||||
|
||||
/* Check if steal lock operation is valid for this device. */
|
||||
if (cdev->drv) {
|
||||
if (!cdev->private->options.force)
|
||||
return -EINVAL;
|
||||
}
|
||||
buffer = kzalloc(64, GFP_DMA | GFP_KERNEL);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
init_completion(&data.done);
|
||||
data.rc = -EIO;
|
||||
spin_lock_irq(sch->lock);
|
||||
rc = cio_enable_subchannel(sch, (u32) (addr_t) sch);
|
||||
if (rc)
|
||||
goto out_unlock;
|
||||
/* Perform operation. */
|
||||
cdev->private->state = DEV_STATE_STEAL_LOCK;
|
||||
ccw_device_stlck_start(cdev, &data, &buffer[0], &buffer[32]);
|
||||
spin_unlock_irq(sch->lock);
|
||||
/* Wait for operation to finish. */
|
||||
if (wait_for_completion_interruptible(&data.done)) {
|
||||
/* Got a signal. */
|
||||
spin_lock_irq(sch->lock);
|
||||
ccw_request_cancel(cdev);
|
||||
spin_unlock_irq(sch->lock);
|
||||
wait_for_completion(&data.done);
|
||||
}
|
||||
rc = data.rc;
|
||||
/* Check results. */
|
||||
spin_lock_irq(sch->lock);
|
||||
cio_disable_subchannel(sch);
|
||||
cdev->private->state = DEV_STATE_BOXED;
|
||||
out_unlock:
|
||||
spin_unlock_irq(sch->lock);
|
||||
kfree(buffer);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
Reference in New Issue
Block a user