summaryrefslogtreecommitdiff
path: root/drivers/s390/cio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/s390/cio')
-rw-r--r--drivers/s390/cio/chp.c34
-rw-r--r--drivers/s390/cio/chp.h5
-rw-r--r--drivers/s390/cio/chsc.c59
-rw-r--r--drivers/s390/cio/chsc.h11
-rw-r--r--drivers/s390/cio/device.c16
-rw-r--r--drivers/s390/cio/device_ops.c4
-rw-r--r--drivers/s390/cio/qdio_main.c131
-rw-r--r--drivers/s390/cio/vfio_ccw_fsm.c5
8 files changed, 140 insertions, 125 deletions
diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c
index f95b452b8bbc..afbdee74147d 100644
--- a/drivers/s390/cio/chp.c
+++ b/drivers/s390/cio/chp.c
@@ -384,6 +384,28 @@ static ssize_t chp_chid_external_show(struct device *dev,
}
static DEVICE_ATTR(chid_external, 0444, chp_chid_external_show, NULL);
+static ssize_t util_string_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *attr, char *buf,
+ loff_t off, size_t count)
+{
+ struct channel_path *chp = to_channelpath(kobj_to_dev(kobj));
+ ssize_t rc;
+
+ mutex_lock(&chp->lock);
+ rc = memory_read_from_buffer(buf, count, &off, chp->desc_fmt3.util_str,
+ sizeof(chp->desc_fmt3.util_str));
+ mutex_unlock(&chp->lock);
+
+ return rc;
+}
+static BIN_ATTR_RO(util_string,
+ sizeof(((struct channel_path_desc_fmt3 *)0)->util_str));
+
+static struct bin_attribute *chp_bin_attrs[] = {
+ &bin_attr_util_string,
+ NULL,
+};
+
static struct attribute *chp_attrs[] = {
&dev_attr_status.attr,
&dev_attr_configure.attr,
@@ -396,6 +418,7 @@ static struct attribute *chp_attrs[] = {
};
static struct attribute_group chp_attr_group = {
.attrs = chp_attrs,
+ .bin_attrs = chp_bin_attrs,
};
static const struct attribute_group *chp_attr_groups[] = {
&chp_attr_group,
@@ -422,7 +445,7 @@ int chp_update_desc(struct channel_path *chp)
{
int rc;
- rc = chsc_determine_base_channel_path_desc(chp->chpid, &chp->desc);
+ rc = chsc_determine_fmt0_channel_path_desc(chp->chpid, &chp->desc);
if (rc)
return rc;
@@ -431,6 +454,7 @@ int chp_update_desc(struct channel_path *chp)
* hypervisors implement the required chsc commands.
*/
chsc_determine_fmt1_channel_path_desc(chp->chpid, &chp->desc_fmt1);
+ chsc_determine_fmt3_channel_path_desc(chp->chpid, &chp->desc_fmt3);
chsc_get_channel_measurement_chars(chp);
return 0;
@@ -506,20 +530,20 @@ out:
* On success return a newly allocated copy of the channel-path description
* data associated with the given channel-path ID. Return %NULL on error.
*/
-struct channel_path_desc *chp_get_chp_desc(struct chp_id chpid)
+struct channel_path_desc_fmt0 *chp_get_chp_desc(struct chp_id chpid)
{
struct channel_path *chp;
- struct channel_path_desc *desc;
+ struct channel_path_desc_fmt0 *desc;
chp = chpid_to_chp(chpid);
if (!chp)
return NULL;
- desc = kmalloc(sizeof(struct channel_path_desc), GFP_KERNEL);
+ desc = kmalloc(sizeof(*desc), GFP_KERNEL);
if (!desc)
return NULL;
mutex_lock(&chp->lock);
- memcpy(desc, &chp->desc, sizeof(struct channel_path_desc));
+ memcpy(desc, &chp->desc, sizeof(*desc));
mutex_unlock(&chp->lock);
return desc;
}
diff --git a/drivers/s390/cio/chp.h b/drivers/s390/cio/chp.h
index 7e80323cd261..20259f3fbf45 100644
--- a/drivers/s390/cio/chp.h
+++ b/drivers/s390/cio/chp.h
@@ -44,8 +44,9 @@ struct channel_path {
struct chp_id chpid;
struct mutex lock; /* Serialize access to below members. */
int state;
- struct channel_path_desc desc;
+ struct channel_path_desc_fmt0 desc;
struct channel_path_desc_fmt1 desc_fmt1;
+ struct channel_path_desc_fmt3 desc_fmt3;
/* Channel-measurement related stuff: */
int cmg;
int shared;
@@ -61,7 +62,7 @@ static inline struct channel_path *chpid_to_chp(struct chp_id chpid)
int chp_get_status(struct chp_id chpid);
u8 chp_get_sch_opm(struct subchannel *sch);
int chp_is_registered(struct chp_id chpid);
-struct channel_path_desc *chp_get_chp_desc(struct chp_id chpid);
+struct channel_path_desc_fmt0 *chp_get_chp_desc(struct chp_id chpid);
void chp_remove_cmg_attr(struct channel_path *chp);
int chp_add_cmg_attr(struct channel_path *chp);
int chp_update_desc(struct channel_path *chp);
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index c08fc5a8df0c..6652a49a49b1 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -915,6 +915,8 @@ int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt,
return -EINVAL;
if ((rfmt == 2) && !css_general_characteristics.cib)
return -EINVAL;
+ if ((rfmt == 3) && !css_general_characteristics.util_str)
+ return -EINVAL;
memset(page, 0, PAGE_SIZE);
scpd_area = page;
@@ -940,43 +942,30 @@ int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt,
}
EXPORT_SYMBOL_GPL(chsc_determine_channel_path_desc);
-int chsc_determine_base_channel_path_desc(struct chp_id chpid,
- struct channel_path_desc *desc)
-{
- struct chsc_scpd *scpd_area;
- unsigned long flags;
- int ret;
-
- spin_lock_irqsave(&chsc_page_lock, flags);
- scpd_area = chsc_page;
- ret = chsc_determine_channel_path_desc(chpid, 0, 0, 0, 0, scpd_area);
- if (ret)
- goto out;
-
- memcpy(desc, scpd_area->data, sizeof(*desc));
-out:
- spin_unlock_irqrestore(&chsc_page_lock, flags);
- return ret;
+#define chsc_det_chp_desc(FMT, c) \
+int chsc_determine_fmt##FMT##_channel_path_desc( \
+ struct chp_id chpid, struct channel_path_desc_fmt##FMT *desc) \
+{ \
+ struct chsc_scpd *scpd_area; \
+ unsigned long flags; \
+ int ret; \
+ \
+ spin_lock_irqsave(&chsc_page_lock, flags); \
+ scpd_area = chsc_page; \
+ ret = chsc_determine_channel_path_desc(chpid, 0, FMT, c, 0, \
+ scpd_area); \
+ if (ret) \
+ goto out; \
+ \
+ memcpy(desc, scpd_area->data, sizeof(*desc)); \
+out: \
+ spin_unlock_irqrestore(&chsc_page_lock, flags); \
+ return ret; \
}
-int chsc_determine_fmt1_channel_path_desc(struct chp_id chpid,
- struct channel_path_desc_fmt1 *desc)
-{
- struct chsc_scpd *scpd_area;
- unsigned long flags;
- int ret;
-
- spin_lock_irqsave(&chsc_page_lock, flags);
- scpd_area = chsc_page;
- ret = chsc_determine_channel_path_desc(chpid, 0, 1, 1, 0, scpd_area);
- if (ret)
- goto out;
-
- memcpy(desc, scpd_area->data, sizeof(*desc));
-out:
- spin_unlock_irqrestore(&chsc_page_lock, flags);
- return ret;
-}
+chsc_det_chp_desc(0, 0)
+chsc_det_chp_desc(1, 1)
+chsc_det_chp_desc(3, 0)
static void
chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv,
diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h
index dda5953534b7..5c9f0dd33f4e 100644
--- a/drivers/s390/cio/chsc.h
+++ b/drivers/s390/cio/chsc.h
@@ -40,6 +40,11 @@ struct channel_path_desc_fmt1 {
u32 zeros[2];
} __attribute__ ((packed));
+struct channel_path_desc_fmt3 {
+ struct channel_path_desc_fmt1 fmt1_desc;
+ u8 util_str[64];
+};
+
struct channel_path;
struct css_chsc_char {
@@ -147,10 +152,12 @@ int __chsc_do_secm(struct channel_subsystem *css, int enable);
int chsc_chp_vary(struct chp_id chpid, int on);
int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt,
int c, int m, void *page);
-int chsc_determine_base_channel_path_desc(struct chp_id chpid,
- struct channel_path_desc *desc);
+int chsc_determine_fmt0_channel_path_desc(struct chp_id chpid,
+ struct channel_path_desc_fmt0 *desc);
int chsc_determine_fmt1_channel_path_desc(struct chp_id chpid,
struct channel_path_desc_fmt1 *desc);
+int chsc_determine_fmt3_channel_path_desc(struct chp_id chpid,
+ struct channel_path_desc_fmt3 *desc);
void chsc_chp_online(struct chp_id chpid);
void chsc_chp_offline(struct chp_id chpid);
int chsc_get_channel_measurement_chars(struct channel_path *chp);
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index f50ea035aa9b..1540229a37bb 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -1073,8 +1073,7 @@ out_schedule:
return 0;
}
-static int
-io_subchannel_remove (struct subchannel *sch)
+static int io_subchannel_remove(struct subchannel *sch)
{
struct io_subchannel_private *io_priv = to_io_private(sch);
struct ccw_device *cdev;
@@ -1082,14 +1081,12 @@ io_subchannel_remove (struct subchannel *sch)
cdev = sch_get_cdev(sch);
if (!cdev)
goto out_free;
- io_subchannel_quiesce(sch);
- /* Set ccw device to not operational and drop reference. */
- spin_lock_irq(cdev->ccwlock);
+
+ ccw_device_unregister(cdev);
+ spin_lock_irq(sch->lock);
sch_set_cdev(sch, NULL);
set_io_private(sch, NULL);
- cdev->private->state = DEV_STATE_NOT_OPER;
- spin_unlock_irq(cdev->ccwlock);
- ccw_device_unregister(cdev);
+ spin_unlock_irq(sch->lock);
out_free:
kfree(io_priv);
sysfs_remove_group(&sch->dev.kobj, &io_subchannel_attr_group);
@@ -1721,6 +1718,7 @@ static int ccw_device_remove(struct device *dev)
{
struct ccw_device *cdev = to_ccwdev(dev);
struct ccw_driver *cdrv = cdev->drv;
+ struct subchannel *sch;
int ret;
if (cdrv->remove)
@@ -1746,7 +1744,9 @@ static int ccw_device_remove(struct device *dev)
ccw_device_set_timeout(cdev, 0);
cdev->drv = NULL;
cdev->private->int_class = IRQIO_CIO;
+ sch = to_subchannel(cdev->dev.parent);
spin_unlock_irq(cdev->ccwlock);
+ io_subchannel_quiesce(sch);
__disable_cmf(cdev);
return 0;
diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c
index 75ce12a24dc2..aecfebb74157 100644
--- a/drivers/s390/cio/device_ops.c
+++ b/drivers/s390/cio/device_ops.c
@@ -460,8 +460,8 @@ __u8 ccw_device_get_path_mask(struct ccw_device *cdev)
* On success return a newly allocated copy of the channel-path description
* data associated with the given channel path. Return %NULL on error.
*/
-struct channel_path_desc *ccw_device_get_chp_desc(struct ccw_device *cdev,
- int chp_idx)
+struct channel_path_desc_fmt0 *ccw_device_get_chp_desc(struct ccw_device *cdev,
+ int chp_idx)
{
struct subchannel *sch;
struct chp_id chpid;
diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c
index d5b02de02a3a..a337281337a7 100644
--- a/drivers/s390/cio/qdio_main.c
+++ b/drivers/s390/cio/qdio_main.c
@@ -98,22 +98,6 @@ static inline int do_siga_output(unsigned long schid, unsigned long mask,
return cc;
}
-static inline int qdio_check_ccq(struct qdio_q *q, unsigned int ccq)
-{
- /* all done or next buffer state different */
- if (ccq == 0 || ccq == 32)
- return 0;
- /* no buffer processed */
- if (ccq == 97)
- return 1;
- /* not all buffers processed */
- if (ccq == 96)
- return 2;
- /* notify devices immediately */
- DBF_ERROR("%4x ccq:%3d", SCH_NO(q), ccq);
- return -EIO;
-}
-
/**
* qdio_do_eqbs - extract buffer states for QEBSM
* @q: queue to manipulate
@@ -128,7 +112,7 @@ static inline int qdio_check_ccq(struct qdio_q *q, unsigned int ccq)
static int qdio_do_eqbs(struct qdio_q *q, unsigned char *state,
int start, int count, int auto_ack)
{
- int rc, tmp_count = count, tmp_start = start, nr = q->nr, retried = 0;
+ int tmp_count = count, tmp_start = start, nr = q->nr;
unsigned int ccq = 0;
qperf_inc(q, eqbs);
@@ -138,34 +122,30 @@ static int qdio_do_eqbs(struct qdio_q *q, unsigned char *state,
again:
ccq = do_eqbs(q->irq_ptr->sch_token, state, nr, &tmp_start, &tmp_count,
auto_ack);
- rc = qdio_check_ccq(q, ccq);
- if (!rc)
- return count - tmp_count;
- if (rc == 1) {
- DBF_DEV_EVENT(DBF_WARN, q->irq_ptr, "EQBS again:%2d", ccq);
- goto again;
- }
-
- if (rc == 2) {
+ switch (ccq) {
+ case 0:
+ case 32:
+ /* all done, or next buffer state different */
+ return count - tmp_count;
+ case 96:
+ /* not all buffers processed */
qperf_inc(q, eqbs_partial);
DBF_DEV_EVENT(DBF_WARN, q->irq_ptr, "EQBS part:%02x",
tmp_count);
- /*
- * Retry once, if that fails bail out and process the
- * extracted buffers before trying again.
- */
- if (!retried++)
- goto again;
- else
- return count - tmp_count;
+ return count - tmp_count;
+ case 97:
+ /* no buffer processed */
+ DBF_DEV_EVENT(DBF_WARN, q->irq_ptr, "EQBS again:%2d", ccq);
+ goto again;
+ default:
+ DBF_ERROR("%4x ccq:%3d", SCH_NO(q), ccq);
+ DBF_ERROR("%4x EQBS ERROR", SCH_NO(q));
+ DBF_ERROR("%3d%3d%2d", count, tmp_count, nr);
+ q->handler(q->irq_ptr->cdev, QDIO_ERROR_GET_BUF_STATE, q->nr,
+ q->first_to_kick, count, q->irq_ptr->int_parm);
+ return 0;
}
-
- DBF_ERROR("%4x EQBS ERROR", SCH_NO(q));
- DBF_ERROR("%3d%3d%2d", count, tmp_count, nr);
- q->handler(q->irq_ptr->cdev, QDIO_ERROR_GET_BUF_STATE,
- q->nr, q->first_to_kick, count, q->irq_ptr->int_parm);
- return 0;
}
/**
@@ -185,7 +165,6 @@ static int qdio_do_sqbs(struct qdio_q *q, unsigned char state, int start,
unsigned int ccq = 0;
int tmp_count = count, tmp_start = start;
int nr = q->nr;
- int rc;
if (!count)
return 0;
@@ -195,26 +174,32 @@ static int qdio_do_sqbs(struct qdio_q *q, unsigned char state, int start,
nr += q->irq_ptr->nr_input_qs;
again:
ccq = do_sqbs(q->irq_ptr->sch_token, state, nr, &tmp_start, &tmp_count);
- rc = qdio_check_ccq(q, ccq);
- if (!rc) {
+
+ switch (ccq) {
+ case 0:
+ case 32:
+ /* all done, or active buffer adapter-owned */
WARN_ON_ONCE(tmp_count);
return count - tmp_count;
- }
-
- if (rc == 1 || rc == 2) {
+ case 96:
+ /* not all buffers processed */
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "SQBS again:%2d", ccq);
qperf_inc(q, sqbs_partial);
goto again;
+ default:
+ DBF_ERROR("%4x ccq:%3d", SCH_NO(q), ccq);
+ DBF_ERROR("%4x SQBS ERROR", SCH_NO(q));
+ DBF_ERROR("%3d%3d%2d", count, tmp_count, nr);
+ q->handler(q->irq_ptr->cdev, QDIO_ERROR_SET_BUF_STATE, q->nr,
+ q->first_to_kick, count, q->irq_ptr->int_parm);
+ return 0;
}
-
- DBF_ERROR("%4x SQBS ERROR", SCH_NO(q));
- DBF_ERROR("%3d%3d%2d", count, tmp_count, nr);
- q->handler(q->irq_ptr->cdev, QDIO_ERROR_SET_BUF_STATE,
- q->nr, q->first_to_kick, count, q->irq_ptr->int_parm);
- return 0;
}
-/* returns number of examined buffers and their common state in *state */
+/*
+ * Returns number of examined buffers and their common state in *state.
+ * Requested number of buffers-to-examine must be > 0.
+ */
static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr,
unsigned char *state, unsigned int count,
int auto_ack, int merge_pending)
@@ -225,17 +210,23 @@ static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr,
if (is_qebsm(q))
return qdio_do_eqbs(q, state, bufnr, count, auto_ack);
- for (i = 0; i < count; i++) {
- if (!__state) {
- __state = q->slsb.val[bufnr];
- if (merge_pending && __state == SLSB_P_OUTPUT_PENDING)
- __state = SLSB_P_OUTPUT_EMPTY;
- } else if (merge_pending) {
- if ((q->slsb.val[bufnr] & __state) != __state)
- break;
- } else if (q->slsb.val[bufnr] != __state)
- break;
+ /* get initial state: */
+ __state = q->slsb.val[bufnr];
+ if (merge_pending && __state == SLSB_P_OUTPUT_PENDING)
+ __state = SLSB_P_OUTPUT_EMPTY;
+
+ for (i = 1; i < count; i++) {
bufnr = next_buf(bufnr);
+
+ /* merge PENDING into EMPTY: */
+ if (merge_pending &&
+ q->slsb.val[bufnr] == SLSB_P_OUTPUT_PENDING &&
+ __state == SLSB_P_OUTPUT_EMPTY)
+ continue;
+
+ /* stop if next state differs from initial state: */
+ if (q->slsb.val[bufnr] != __state)
+ break;
}
*state = __state;
return i;
@@ -502,8 +493,8 @@ static inline void inbound_primed(struct qdio_q *q, int count)
static int get_inbound_buffer_frontier(struct qdio_q *q)
{
- int count, stop;
unsigned char state = 0;
+ int count;
q->timestamp = get_tod_clock_fast();
@@ -512,9 +503,7 @@ static int get_inbound_buffer_frontier(struct qdio_q *q)
* would return 0.
*/
count = min(atomic_read(&q->nr_buf_used), QDIO_MAX_BUFFERS_MASK);
- stop = add_buf(q->first_to_check, count);
-
- if (q->first_to_check == stop)
+ if (!count)
goto out;
/*
@@ -734,8 +723,8 @@ void qdio_inbound_processing(unsigned long data)
static int get_outbound_buffer_frontier(struct qdio_q *q)
{
- int count, stop;
unsigned char state = 0;
+ int count;
q->timestamp = get_tod_clock_fast();
@@ -751,11 +740,11 @@ static int get_outbound_buffer_frontier(struct qdio_q *q)
* would return 0.
*/
count = min(atomic_read(&q->nr_buf_used), QDIO_MAX_BUFFERS_MASK);
- stop = add_buf(q->first_to_check, count);
- if (q->first_to_check == stop)
+ if (!count)
goto out;
- count = get_buf_states(q, q->first_to_check, &state, count, 0, 1);
+ count = get_buf_states(q, q->first_to_check, &state, count, 0,
+ q->u.out.use_cq);
if (!count)
goto out;
diff --git a/drivers/s390/cio/vfio_ccw_fsm.c b/drivers/s390/cio/vfio_ccw_fsm.c
index c30420c517b1..ff6963ad6e39 100644
--- a/drivers/s390/cio/vfio_ccw_fsm.c
+++ b/drivers/s390/cio/vfio_ccw_fsm.c
@@ -124,6 +124,11 @@ static void fsm_io_request(struct vfio_ccw_private *private,
if (scsw->cmd.fctl & SCSW_FCTL_START_FUNC) {
orb = (union orb *)io_region->orb_area;
+ /* Don't try to build a cp if transport mode is specified. */
+ if (orb->tm.b) {
+ io_region->ret_code = -EOPNOTSUPP;
+ goto err_out;
+ }
io_region->ret_code = cp_init(&private->cp, mdev_dev(mdev),
orb);
if (io_region->ret_code)