From nobody Mon May 6 19:26:59 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1505310014382814.1219210782579; Wed, 13 Sep 2017 06:40:14 -0700 (PDT) Received: from localhost ([::1]:42589 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ds7tl-00045X-FP for importer@patchew.org; Wed, 13 Sep 2017 09:40:13 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:40479) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ds7iE-00005e-Cr for qemu-devel@nongnu.org; Wed, 13 Sep 2017 09:28:19 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ds7iB-0006NV-4t for qemu-devel@nongnu.org; Wed, 13 Sep 2017 09:28:18 -0400 Received: from mx0a-001b2d01.pphosted.com ([148.163.156.1]:41114) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1ds7iA-0006Mr-S0 for qemu-devel@nongnu.org; Wed, 13 Sep 2017 09:28:15 -0400 Received: from pps.filterd (m0098409.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.16.0.21/8.16.0.21) with SMTP id v8DDOp95037844 for ; Wed, 13 Sep 2017 09:28:13 -0400 Received: from e06smtp13.uk.ibm.com (e06smtp13.uk.ibm.com [195.75.94.109]) by mx0a-001b2d01.pphosted.com with ESMTP id 2cy388yywk-1 (version=TLSv1.2 cipher=AES256-SHA bits=256 verify=NOT) for ; Wed, 13 Sep 2017 09:28:13 -0400 Received: from localhost by e06smtp13.uk.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Wed, 13 Sep 2017 14:28:10 +0100 Received: from b06cxnps4076.portsmouth.uk.ibm.com (9.149.109.198) by e06smtp13.uk.ibm.com (192.168.101.143) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Wed, 13 Sep 2017 14:28:07 +0100 Received: from d06av23.portsmouth.uk.ibm.com (d06av23.portsmouth.uk.ibm.com [9.149.105.59]) by b06cxnps4076.portsmouth.uk.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id v8DDS7jC6488294; Wed, 13 Sep 2017 13:28:07 GMT Received: from d06av23.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 51E0BA4051; Wed, 13 Sep 2017 14:24:17 +0100 (BST) Received: from d06av23.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 1CEB3A4040; Wed, 13 Sep 2017 14:24:17 +0100 (BST) Received: from tuxmaker.boeblingen.de.ibm.com (unknown [9.152.85.9]) by d06av23.portsmouth.uk.ibm.com (Postfix) with ESMTPS; Wed, 13 Sep 2017 14:24:17 +0100 (BST) From: Halil Pasic To: Cornelia Huck Date: Wed, 13 Sep 2017 15:27:51 +0200 X-Mailer: git-send-email 2.13.5 In-Reply-To: <20170913132752.8484-1-pasic@linux.vnet.ibm.com> References: <20170913132752.8484-1-pasic@linux.vnet.ibm.com> X-TM-AS-GCONF: 00 x-cbid: 17091313-0012-0000-0000-00000578B37A X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 17091313-0013-0000-0000-000018F1D015 Message-Id: <20170913132752.8484-2-pasic@linux.vnet.ibm.com> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:, , definitions=2017-09-13_04:, , signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 spamscore=0 suspectscore=2 malwarescore=0 phishscore=0 adultscore=0 bulkscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1707230000 definitions=main-1709130210 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x [generic] [fuzzy] X-Received-From: 148.163.156.1 Subject: [Qemu-devel] [PATCH 1/2] s390x/ccs: add ccw-tester emulated device X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Dong Jia Shi , Halil Pasic , Pierre Morel , qemu-devel@nongnu.org Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Add a fake device meant for testing the correctness of our css emulation. What we currently have is writing a Fibonacci sequence of uint32_t to the device via ccw write. The write is going to fail if it ain't a Fibonacci and indicate a device exception in scsw together with the proper residual count. Of course lot's of invalid inputs (besides basic data processing) can be tested with that as well. Usage: 1) fire up a qemu with something like -device ccw-tester,devno=3Dfe.0.0001 on the command line 2) exercise the device from the guest Signed-off-by: Halil Pasic --- Depends on the series 'add CCW indirect data access support' --- hw/s390x/Makefile.objs | 1 + hw/s390x/ccw-tester.c | 179 +++++++++++++++++++++++++++++++++++++++++++++= ++++ 2 files changed, 180 insertions(+) create mode 100644 hw/s390x/ccw-tester.c diff --git a/hw/s390x/Makefile.objs b/hw/s390x/Makefile.objs index 7ee19d3abc..28eb58a3cb 100644 --- a/hw/s390x/Makefile.objs +++ b/hw/s390x/Makefile.objs @@ -18,3 +18,4 @@ obj-y +=3D s390-stattrib.o obj-$(CONFIG_KVM) +=3D s390-skeys-kvm.o obj-$(CONFIG_KVM) +=3D s390-stattrib-kvm.o obj-y +=3D s390-ccw.o +obj-y +=3D ccw-tester.o diff --git a/hw/s390x/ccw-tester.c b/hw/s390x/ccw-tester.c new file mode 100644 index 0000000000..c8017818c4 --- /dev/null +++ b/hw/s390x/ccw-tester.c @@ -0,0 +1,179 @@ +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/module.h" +#include "cpu.h" +#include "hw/s390x/css.h" +#include "hw/s390x/css-bridge.h" +#include "hw/s390x/3270-ccw.h" +#include "exec/address-spaces.h" +#include + +typedef struct CcwTesterDevice { + CcwDevice parent_obj; + uint16_t cu_type; + uint8_t chpid_type; + struct { + uint32_t ring[4]; + unsigned int next; + } fib; +} CcwTesterDevice; + + +typedef struct CcwTesterClass { + CCWDeviceClass parent_class; + DeviceRealize parent_realize; +} CcwTesterClass; + +#define TYPE_CCW_TESTER "ccw-tester" + +#define CCW_TESTER(obj) \ + OBJECT_CHECK(CcwTesterDevice, (obj), TYPE_CCW_TESTER) +#define CCW_TESTER_CLASS(klass) \ + OBJECT_CLASS_CHECK(CcwTesterClass, (klass), TYPE_CCW_TESTER) +#define CCW_TESTER_GET_CLASS(obj) \ + OBJECT_GET_CLASS(CcwTesterClass, (obj), TYPE_CCW_TESTER) + +#define CCW_CMD_READ 0x01U +#define CCW_CMD_WRITE 0x02U + +static unsigned int abs_to_ring(unsigned int i) +{ + return i & 0x3; +} + +static int ccw_tester_write_fib(SubchDev *sch, CCW1 ccw) +{ + CcwTesterDevice *d =3D sch->driver_data; + bool is_fib =3D true; + uint32_t sum; + int ret =3D 0; + + ccw_dstream_init(&sch->cds, &ccw, &sch->orb); + d->fib.next =3D 0; + while (ccw_dstream_avail(&sch->cds) > 0) { + ret =3D ccw_dstream_read(&sch->cds, + d->fib.ring[abs_to_ring(d->fib.next)]); + if (ret) { + error(0, -ret, "fib"); + break; + } + if (d->fib.next > 2) { + sum =3D (d->fib.ring[abs_to_ring(d->fib.next - 1)] + + d->fib.ring[abs_to_ring(d->fib.next - 2)]); + is_fib =3D sum =3D=3D d->fib.ring[abs_to_ring(d->fib.next)]; + if (!is_fib) { + break; + } + } + ++(d->fib.next); + } + if (!is_fib) { + sch->curr_status.scsw.ctrl &=3D ~SCSW_ACTL_START_PEND; + sch->curr_status.scsw.ctrl |=3D SCSW_STCTL_PRIMARY | + SCSW_STCTL_SECONDARY | + SCSW_STCTL_ALERT | + SCSW_STCTL_STATUS_PEND; + sch->curr_status.scsw.count =3D ccw_dstream_residual_count(&sch->c= ds); + sch->curr_status.scsw.cpa =3D sch->channel_prog + 8; + sch->curr_status.scsw.dstat =3D SCSW_DSTAT_UNIT_EXCEP; + return -EIO; + } + return ret; +} + +static int ccw_tester_ccw_cb_impl(SubchDev *sch, CCW1 ccw) +{ + switch (ccw.cmd_code) { + case CCW_CMD_READ: + break; + case CCW_CMD_WRITE: + return ccw_tester_write_fib(sch, ccw); + default: + return -EINVAL; + } + return 0; +} + +static void ccw_tester_realize(DeviceState *ds, Error **errp) +{ + uint16_t chpid; + CcwTesterDevice *dev =3D CCW_TESTER(ds); + CcwTesterClass *ctc =3D CCW_TESTER_GET_CLASS(dev); + CcwDevice *cdev =3D CCW_DEVICE(ds); + BusState *qbus =3D qdev_get_parent_bus(ds); + VirtualCssBus *cbus =3D VIRTUAL_CSS_BUS(qbus); + SubchDev *sch; + Error *err =3D NULL; + + sch =3D css_create_sch(cdev->devno, true, cbus->squash_mcss, errp); + if (!sch) { + return; + } + + sch->driver_data =3D dev; + cdev->sch =3D sch; + chpid =3D css_find_free_chpid(sch->cssid); + + if (chpid > MAX_CHPID) { + error_setg(&err, "No available chpid to use."); + goto out_err; + } + + sch->id.reserved =3D 0xff; + sch->id.cu_type =3D dev->cu_type; + css_sch_build_virtual_schib(sch, (uint8_t)chpid, + dev->chpid_type); + sch->ccw_cb =3D ccw_tester_ccw_cb_impl; + sch->do_subchannel_work =3D do_subchannel_work_virtual; + + + ctc->parent_realize(ds, &err); + if (err) { + goto out_err; + } + + css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid, + ds->hotplugged, 1); + return; + +out_err: + error_propagate(errp, err); + css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL); + cdev->sch =3D NULL; + g_free(sch); +} + +static Property ccw_tester_properties[] =3D { + DEFINE_PROP_UINT16("cu_type", CcwTesterDevice, cu_type, + 0x3831), + DEFINE_PROP_UINT8("chpid_type", CcwTesterDevice, chpid_type, + 0x98), + DEFINE_PROP_END_OF_LIST(), +}; + +static void ccw_tester_class_init(ObjectClass *klass, void *data) +{ + DeviceClass *dc =3D DEVICE_CLASS(klass); + CcwTesterClass *ctc =3D CCW_TESTER_CLASS(klass); + + dc->props =3D ccw_tester_properties; + dc->bus_type =3D TYPE_VIRTUAL_CSS_BUS; + ctc->parent_realize =3D dc->realize; + dc->realize =3D ccw_tester_realize; + dc->hotpluggable =3D false; +} + +static const TypeInfo ccw_tester_info =3D { + .name =3D TYPE_CCW_TESTER, + .parent =3D TYPE_CCW_DEVICE, + .instance_size =3D sizeof(CcwTesterDevice), + .class_init =3D ccw_tester_class_init, + .class_size =3D sizeof(CcwTesterClass), +}; + +static void ccw_tester_register(void) +{ + type_register_static(&ccw_tester_info); +} + +type_init(ccw_tester_register) --=20 2.13.5 From nobody Mon May 6 19:26:59 2024 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) client-ip=208.118.235.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of gnu.org designates 208.118.235.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) by mx.zohomail.com with SMTPS id 1505309914088526.625152895017; Wed, 13 Sep 2017 06:38:34 -0700 (PDT) Received: from localhost ([::1]:42582 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ds7s9-00025p-CC for importer@patchew.org; Wed, 13 Sep 2017 09:38:33 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:40461) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ds7iD-0008W3-3U for qemu-devel@nongnu.org; Wed, 13 Sep 2017 09:28:19 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ds7i9-0006MC-Rz for qemu-devel@nongnu.org; Wed, 13 Sep 2017 09:28:17 -0400 Received: from mx0b-001b2d01.pphosted.com ([148.163.158.5]:45222 helo=mx0a-001b2d01.pphosted.com) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1ds7i9-0006Ls-Le for qemu-devel@nongnu.org; Wed, 13 Sep 2017 09:28:13 -0400 Received: from pps.filterd (m0098417.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.16.0.21/8.16.0.21) with SMTP id v8DDO5Bj097576 for ; Wed, 13 Sep 2017 09:28:13 -0400 Received: from e06smtp15.uk.ibm.com (e06smtp15.uk.ibm.com [195.75.94.111]) by mx0a-001b2d01.pphosted.com with ESMTP id 2cy3ddqbtr-1 (version=TLSv1.2 cipher=AES256-SHA bits=256 verify=NOT) for ; Wed, 13 Sep 2017 09:28:12 -0400 Received: from localhost by e06smtp15.uk.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Wed, 13 Sep 2017 14:28:10 +0100 Received: from b06cxnps4074.portsmouth.uk.ibm.com (9.149.109.196) by e06smtp15.uk.ibm.com (192.168.101.145) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Wed, 13 Sep 2017 14:28:09 +0100 Received: from d06av23.portsmouth.uk.ibm.com (d06av23.portsmouth.uk.ibm.com [9.149.105.59]) by b06cxnps4074.portsmouth.uk.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id v8DDS8Af26476786; Wed, 13 Sep 2017 13:28:08 GMT Received: from d06av23.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 9596BA4040; Wed, 13 Sep 2017 14:24:18 +0100 (BST) Received: from d06av23.portsmouth.uk.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id 5C80DA4051; Wed, 13 Sep 2017 14:24:18 +0100 (BST) Received: from tuxmaker.boeblingen.de.ibm.com (unknown [9.152.85.9]) by d06av23.portsmouth.uk.ibm.com (Postfix) with ESMTPS; Wed, 13 Sep 2017 14:24:18 +0100 (BST) From: Halil Pasic To: Cornelia Huck Date: Wed, 13 Sep 2017 15:27:52 +0200 X-Mailer: git-send-email 2.13.5 In-Reply-To: <20170913132752.8484-1-pasic@linux.vnet.ibm.com> References: <20170913132752.8484-1-pasic@linux.vnet.ibm.com> X-TM-AS-GCONF: 00 x-cbid: 17091313-0020-0000-0000-000003B7A9D0 X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 17091313-0021-0000-0000-000042491B3E Message-Id: <20170913132752.8484-3-pasic@linux.vnet.ibm.com> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:, , definitions=2017-09-13_04:, , signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 spamscore=0 suspectscore=2 malwarescore=0 phishscore=0 adultscore=0 bulkscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1707230000 definitions=main-1709130210 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x [generic] [fuzzy] X-Received-From: 148.163.158.5 Subject: [Qemu-devel] [PATCH 2/2 NOT QEMU] a tester device for ccw I/O X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Dong Jia Shi , Halil Pasic , Pierre Morel , qemu-devel@nongnu.org Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Transfer-Encoding: quoted-printable MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Let's introduce a device driver for doing ccw I/O tests. The initial focus is on indirect data access. The driver is impemented as an out-of-tree Linux kernel module. A module parameter cu_type is used for matching ccw devices. The parameter defaults to 0x3831 which is the default cu_type for the qemu counterpart of this (a fully emulated ccw device just for test). The current status of the module is means to an end where the end is testing my IDA implementation. Usage: You load the module. The driver is supposed to auto detect and auto online the device and provide sysfs atributes for the tests available. Tests are triggered by writing to the attributes. Reoprting is done via printk in almost TAP format (could use improvement if more ambitious). We run one test at a time and do that async to the write. If you try to start more in parallel you will get -EBUSY. Currently all you can do something like: * echo 1 > /sys/bus/ccw/devices//w_fib To test good old ccw. * echo 1 > /sys/bus/ccw/devices//w_fib_idal To test IDA ccw. These tests are designed to wrok together with the qemu device mentioned before. The basic idea is that a device is expecting a stream of words such that the sequence words interpreted as uint32_t is a Fibonacci sequence (that is for n > 2 a_{n} =3D a_{n-1} + a{n-2}). Using his simple scheme one can check that the right bytes are transferred (with reasonable confidence). If the device detects an element violating the Fibonacci property the driver expects the device posts a device exception indicating that element. Signed-off-by: Halil Pasic --- Do not try to apply this to a QEMU tree. Use an empty repo. --- .gitignore | 8 ++ Makefile | 10 ++ ccw_tester.c | 420 +++++++++++++++++++++++++++++++++++++++++++++++++++++++= ++++ 3 files changed, 438 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 ccw_tester.c diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1b9eac9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +#ignore these +*.o +*.cmd +*.ko +*.mod.c +Module.symvers +modules.order +.tmp_versions/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..0583456 --- /dev/null +++ b/Makefile @@ -0,0 +1,10 @@ +ifneq ($(KERNELRELEASE),) +obj-m :=3D ccw_tester.o +else +# normal makefile +KDIR ?=3D /lib/modules/`uname -r`/build + +default: + $(MAKE) -C $(KDIR) M=3D$$PWD + +endif diff --git a/ccw_tester.c b/ccw_tester.c new file mode 100644 index 0000000..320486a --- /dev/null +++ b/ccw_tester.c @@ -0,0 +1,420 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +inline bool _ccw_test_assert(bool expr, const char *loc, int ln, + const char *expl) +{ + if (expr) + printk(KERN_NOTICE "ok -- %s:%d\n", loc, ln); + else + printk(KERN_WARNING "not ok -- %s:%d (%s)\n", loc, ln, expl); + return expr; +} + + +#define ccw_test_assert(_expr, _expl) ({_ccw_test_assert((_expr), \ + __func__, __LINE__, (_expl)); }) + +struct workqueue_struct *work_q; + +static __u16 cu_type =3D 0x3831; +module_param(cu_type, ushort, 0000); +MODULE_PARM_DESC(cu_type, "Use this cu type for matching (default 0x3831)"= ); + + +static struct ccw_device_id ccw_tester_ids[] =3D { + { CCW_DEVICE(0, 0) }, /* placeholder */ + {}, +}; + +struct ccw_test_work { + struct work_struct work; + struct ccw1 *ccw; + __u32 intparm; + void *private; + void (*setup)(struct ccw_test_work *w); + void (*do_test)(struct ccw_test_work *w); + void (*teardown)(struct ccw_test_work *w); + struct irb irb; + int ret; + bool doing_io; +}; + +struct ccw_tester_device { + spinlock_t lock; + wait_queue_head_t wait_q; + struct ccw_device *cdev; + struct ccw_test_work work; + bool work_pending; +}; + +static struct ccw_tester_device *to_mydev(struct ccw_device *cdev) +{ + return dev_get_drvdata(&(cdev->dev)); +} + + +static void ccw_tester_auto_online(void *data, async_cookie_t cookie) +{ + struct ccw_device *cdev =3D data; + int ret; + + ret =3D ccw_device_set_online(cdev); + if (ret) + dev_warn(&cdev->dev, "Failed to set online: %d\n", ret); +} + +static void do_io_work(struct ccw_tester_device *tdev) +{ + struct ccw_test_work *w =3D &tdev->work; + unsigned long flags; + int retry =3D 124; + + do { + spin_lock_irqsave(get_ccwdev_lock(tdev->cdev), flags); + tdev->work.doing_io =3D true; + w->ret =3D ccw_device_start(tdev->cdev, w->ccw, w->intparm, 0, 0); + spin_unlock_irqrestore(get_ccwdev_lock(tdev->cdev), flags); + cpu_relax(); + } while (w->ret =3D=3D -EBUSY && --retry > 0); + wait_event(tdev->wait_q, w->doing_io =3D=3D false); +} + + +static void w_fib_w_setup(struct ccw_test_work *w) +{ + const int test_fib_length =3D 32; + u32 *test_fib; + int i; + + test_fib =3D kcalloc(test_fib_length, sizeof(u32), + GFP_DMA | GFP_KERNEL); + if (!test_fib) + w->ret =3D -ENOMEM; + w->private =3D test_fib; + + test_fib[0] =3D 1; + test_fib[1] =3D 2; + for (i =3D 2; i < test_fib_length; ++i) + test_fib[i] =3D test_fib[i - 1] + test_fib[i - 2]; + + w->ccw->cmd_code =3D 0x02; + w->ccw->count =3D sizeof(*test_fib) * test_fib_length; + w->ccw->cda =3D (__u32)(unsigned long) test_fib; +} + +static void do_test_do_io(struct ccw_test_work *w) +{ + struct ccw_tester_device *tdev; + + tdev =3D container_of(w, struct ccw_tester_device, work); + do_io_work(tdev); +} + +static void basic_teardown(struct ccw_test_work *w) +{ + kfree(w->private); + w->private =3D NULL; + if (w->ret) + printk(KERN_WARNING "w_fib_w_teardown ret =3D %d\n", w->ret); +} + +static int irb_is_error(struct irb *irb) +{ + if (scsw_cstat(&irb->scsw) !=3D 0) + return 1; + if (scsw_dstat(&irb->scsw) & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END)) + return 1; + if (scsw_cc(&irb->scsw) !=3D 0) + return 1; + return 0; +} + +static void ccw_tester_int_handler(struct ccw_device *cdev, + unsigned long intparm, + struct irb *irb) +{ + struct ccw_tester_device *tdev =3D to_mydev(cdev); + + memcpy(&tdev->work.irb, irb, sizeof(*irb)); + tdev->work.doing_io =3D false; + wake_up(&tdev->wait_q); +} + +static bool expect_is_not_fib(struct irb *irb, int count_expected) +{ + if (!(irb_is_error(irb) + && (scsw_dstat(&irb->scsw) & DEV_STAT_UNIT_EXCEP) + && scsw_stctl(&irb->scsw) & SCSW_STCTL_ALERT_STATUS)) + return false; + if (irb->scsw.cmd.count =3D=3D count_expected) + return true; + printk(KERN_NOTICE + "expected residual count of %d got %d (fib at wrong place)\n", + count_expected, irb->scsw.cmd.count); + return false; +} + + +static void w_fib_do_test(struct ccw_test_work *w) +{ + u32 *test_fib =3D w->private; + + do_test_do_io(w); + ccw_test_assert(!irb_is_error(&w->irb), "completion expected"); + test_fib[25] =3D 0; + do_test_do_io(w); + ccw_test_assert(expect_is_not_fib(&w->irb, + (31-25)*sizeof(u32)), "expected non fib"); +} + + +static int queue_ccw_test_work(struct ccw_tester_device *tdev, + void (*setup)(struct ccw_test_work *), + void (*do_test)(struct ccw_test_work *), + void (*teardown)(struct ccw_test_work *)) +{ + if (!spin_trylock(&tdev->lock)) + return -EBUSY; + if (tdev->work_pending) { + spin_unlock(&tdev->lock); + return -EBUSY; + } + tdev->work_pending =3D true; + tdev->work.setup =3D setup; + tdev->work.do_test =3D do_test; + tdev->work.teardown =3D teardown; + queue_work(work_q, &tdev->work.work); + spin_unlock(&tdev->lock); + return 0; +} + + +static ssize_t w_fib_store(struct device *dev, struct device_attribute *at= tr, + const char *buf, size_t count) +{ + struct ccw_tester_device *tdev =3D to_mydev(to_ccwdev(dev)); + int ret; + + ret =3D queue_ccw_test_work(tdev, + w_fib_w_setup, w_fib_do_test, basic_teardown); + return ret ? ret : count; +} + +static u32 *u32_arr_in_idal_buf_at(struct idal_buffer const *ib, int i) +{ + u64 b =3D IDA_BLOCK_SIZE/sizeof(u32); + + return (u32 *)(ib->data[i/b]) + i % b; +} + +#define IDAL_TEST_BYTES (IDA_BLOCK_SIZE * 3 + IDA_BLOCK_SIZE/2) +#define IDAL_TEST_ELEMENTS (IDAL_TEST_BYTES/sizeof(u32)) + +static void w_fib_idal_setup(struct ccw_test_work *w) +{ + struct idal_buffer *ib =3D NULL; + u32 n, n_1 =3D 2, n_2 =3D 1; + int i =3D 0; + + ib =3D idal_buffer_alloc(IDAL_TEST_BYTES, 0); + if (IS_ERR(ib)) { + w->ret =3D PTR_ERR(ib); + return; + } + w->private =3D ib; + *u32_arr_in_idal_buf_at(ib, 0) =3D n_2; + *u32_arr_in_idal_buf_at(ib, 1) =3D n_1; + for (i =3D 2; i < IDAL_TEST_ELEMENTS; ++i) { + n =3D n_1 + n_2; + n_2 =3D n_1; + n_1 =3D n; + *u32_arr_in_idal_buf_at(ib, i) =3D n; + } + idal_buffer_set_cda(ib, w->ccw); + w->ccw->count =3D IDAL_TEST_BYTES; + w->ccw->cmd_code =3D 0x02; +} + +static void idal_teardown(struct ccw_test_work *w) +{ + if (w->private) { + idal_buffer_free(w->private); + w->private =3D NULL; + } + if (w->ret) + printk(KERN_WARNING "w_fib_w_teardown ret =3D %d\n", w->ret); +} + +static void w_fib_do_idal_test(struct ccw_test_work *w) +{ + struct idal_buffer *ib =3D w->private; + + /* we have one already set up, fire it */ + do_test_do_io(w); + ccw_test_assert(!irb_is_error(&w->irb), "completion expected"); + + /* let's break fib and check if the device detects it */ + ++(*u32_arr_in_idal_buf_at(ib, IDAL_TEST_ELEMENTS - 5)); + do_test_do_io(w); + ccw_test_assert(expect_is_not_fib(&w->irb, + 4 * sizeof(u32)), "expected non fib"); + /* shorten the seq so the broken element is not included */ + w->ccw->count =3D IDAL_TEST_BYTES - 5 * sizeof(u32); + do_test_do_io(w); + ccw_test_assert(!irb_is_error(&w->irb), "completion expected"); +} + +static ssize_t w_fib_idal_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int ret; + struct ccw_tester_device *tdev =3D to_mydev(to_ccwdev(dev)); + + ret =3D queue_ccw_test_work(tdev, + w_fib_idal_setup, w_fib_do_idal_test, idal_teardown); + return ret ? ret : count; +} + + +static DEVICE_ATTR_WO(w_fib); +static DEVICE_ATTR_WO(w_fib_idal); + +static void do_ccw_test_work(struct work_struct *work) +{ + + struct ccw_test_work *w; + struct ccw_tester_device *tdev; + + w =3D container_of(work, struct ccw_test_work, work); + tdev =3D container_of(w, struct ccw_tester_device, work); + + w->ret =3D 0; + w->setup(w); + w->do_test(w); + w->teardown(w); + spin_lock(&tdev->lock); + tdev->work_pending =3D false; + spin_unlock(&tdev->lock); + memset(w->ccw, 0, sizeof(*(w->ccw))); + memset(&w->irb, 0, sizeof(w->irb)); +} + +static int ccw_tester_offline(struct ccw_device *cdev) +{ + struct ccw_tester_device *tdev =3D to_mydev(cdev); + + if (!tdev) + return 0; + device_remove_file(&(cdev->dev), &dev_attr_w_fib); + device_remove_file(&(cdev->dev), &dev_attr_w_fib_idal); + spin_lock(&tdev->lock); + tdev->work_pending =3D true; + spin_unlock(&tdev->lock); + kfree(tdev->work.ccw); + tdev->work.ccw =3D NULL; + kfree(tdev); + dev_set_drvdata(&cdev->dev, NULL); + return 0; +} + +static int ccw_tester_online(struct ccw_device *cdev) +{ + int ret; + struct ccw_tester_device *tdev; + + tdev =3D kzalloc(sizeof(*tdev), GFP_KERNEL); + if (!tdev) { + dev_warn(&cdev->dev, "Could not get memory\n"); + return -ENOMEM; + } + init_waitqueue_head(&tdev->wait_q); + INIT_WORK(&(tdev->work.work), do_ccw_test_work); + spin_lock_init(&tdev->lock); + tdev->work.ccw =3D kzalloc(sizeof(*tdev->work.ccw), GFP_DMA | GFP_KERNEL); + if (!tdev) { + dev_warn(&cdev->dev, "Could not get memory\n"); + ret =3D -ENOMEM; + goto out_free; + } + dev_set_drvdata(&cdev->dev, tdev); + tdev->cdev =3D cdev; + + ret =3D device_create_file(&(cdev->dev), &dev_attr_w_fib); + if (ret) + goto out_free; + ret =3D device_create_file(&(cdev->dev), &dev_attr_w_fib_idal); + if (ret) + goto out_free; + return ret; +out_free: + ccw_tester_offline(cdev); + return ret; +} + +static void ccw_tester_remove(struct ccw_device *cdev) +{ + ccw_device_set_offline(cdev); +} + +static int ccw_tester_probe(struct ccw_device *cdev) +{ + cdev->handler =3D ccw_tester_int_handler; + async_schedule(ccw_tester_auto_online, cdev); + return 0; +} + +static struct ccw_driver ccw_tester_driver =3D { + .driver =3D { + .owner =3D THIS_MODULE, + .name =3D "ccw_tester", + }, + .ids =3D ccw_tester_ids, + .probe =3D ccw_tester_probe, + .set_online =3D ccw_tester_online, + .set_offline =3D ccw_tester_offline, + .remove =3D ccw_tester_remove, + .int_class =3D IRQIO_VIR, +}; + + +static int __init ccw_tester_init(void) +{ + work_q =3D create_singlethread_workqueue("ccw-tester"); + ccw_tester_ids[0].cu_type =3D cu_type; + return ccw_driver_register(&ccw_tester_driver); +} +module_init(ccw_tester_init); + +static void __exit ccw_tester_exit(void) +{ + ccw_driver_unregister(&ccw_tester_driver); +} +module_exit(ccw_tester_exit); + +MODULE_DESCRIPTION("ccw test driver -- throw ccws at devices"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Halil Pasic "); --=20 2.13.5