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:
Linus Torvalds
2015-11-04 11:31:31 -08:00
101개의 변경된 파일2424개의 추가작업 그리고 3680개의 파일을 삭제

파일 보기

@@ -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;
}