From patchwork Tue Sep 18 00:09:47 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bijan Mottahedeh X-Patchwork-Id: 10603573 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 4F93715A6 for ; Tue, 18 Sep 2018 00:10:08 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 381DB2A94F for ; Tue, 18 Sep 2018 00:10:08 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 365D72A96C; Tue, 18 Sep 2018 00:10:08 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 77DA22A923 for ; Tue, 18 Sep 2018 00:10:07 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1728705AbeIRFjv (ORCPT ); Tue, 18 Sep 2018 01:39:51 -0400 Received: from userp2130.oracle.com ([156.151.31.86]:34820 "EHLO userp2130.oracle.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1728574AbeIRFjv (ORCPT ); Tue, 18 Sep 2018 01:39:51 -0400 Received: from pps.filterd (userp2130.oracle.com [127.0.0.1]) by userp2130.oracle.com (8.16.0.22/8.16.0.22) with SMTP id w8I0A2dK006496; Tue, 18 Sep 2018 00:10:02 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oracle.com; h=from : to : cc : subject : date : message-id : in-reply-to : references; s=corp-2018-07-02; bh=mcgF9QGVXrITv9BkIMJY7RFg9llKgxcuIDYmVnVFkPM=; b=yjuEMMMRpo2zg4X44mwutU62TuPTnlwUBPNpYvRHTTgY9WGK8lxtwh6aOdbKdjEiRiaO TLD578mc6ViZvXLET8RyjocTiXJQowPiQER44QNg2pCc02uO6RmoRg9BnFH63NMZ3h7O Kjn9i1ciUj6nkqdmBy0foiBOP4qJR4Wi34TN9xrIQs1Vm9eKNjI0zh9UaIj12VoXkcpp jWavEOWdMOYdw4Y/U75rduCKTOb+0m5PRu2RRoAHnXUM0ULP9c74pkS5C1byZhZc6d0X 5FZyOj8s2T0us7+YXDlzbTgXcJncCJ+7gGUEyyBTECTPBdt7nPCo1a5wzgHH4z9gyo5O +g== Received: from aserv0022.oracle.com (aserv0022.oracle.com [141.146.126.234]) by userp2130.oracle.com with ESMTP id 2mgsgthbub-1 (version=TLSv1.2 cipher=ECDHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 18 Sep 2018 00:10:02 +0000 Received: from aserv0121.oracle.com (aserv0121.oracle.com [141.146.126.235]) by aserv0022.oracle.com (8.14.4/8.14.4) with ESMTP id w8I09u1Z028060 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-GCM-SHA384 bits=256 verify=OK); Tue, 18 Sep 2018 00:09:56 GMT Received: from abhmp0015.oracle.com (abhmp0015.oracle.com [141.146.116.21]) by aserv0121.oracle.com (8.14.4/8.13.8) with ESMTP id w8I09uO6012042; Tue, 18 Sep 2018 00:09:56 GMT Received: from ca-ldom147.us.oracle.com (/10.129.68.131) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Mon, 17 Sep 2018 17:09:56 -0700 From: Bijan Mottahedeh To: kvm@vger.kernel.org, target-devel@vger.kernel.org Cc: mst@redhat.com, jasowang@redhat.com, silviu.smarandache@oracle.com, bijan.mottahedeh@oracle.com Subject: [PATCH 1/3] vhost/scsi: Respond to control queue operations Date: Mon, 17 Sep 2018 17:09:47 -0700 Message-Id: <1537229389-16176-2-git-send-email-bijan.mottahedeh@oracle.com> X-Mailer: git-send-email 1.8.3.1 In-Reply-To: <1537229389-16176-1-git-send-email-bijan.mottahedeh@oracle.com> References: <1537229389-16176-1-git-send-email-bijan.mottahedeh@oracle.com> X-Proofpoint-Virus-Version: vendor=nai engine=5900 definitions=9019 signatures=668708 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 suspectscore=0 malwarescore=0 phishscore=0 bulkscore=0 spamscore=0 mlxscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1807170000 definitions=main-1809180000 Sender: kvm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: kvm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The vhost-scsi driver currently does not handle any control queue operations. In particular, vhost_scsi_ctl_handle_kick, merely prints out a debug message but does nothing else. This can cause guest VMs to hang. As part of SCSI recovery from an error, e.g., an I/O timeout, the SCSI midlayer attempts to abort the failed operation. The SCSI virtio driver translates the abort to a SCSI TMF request that gets put on the control queue (virtscsi_abort -> virtscsi_tmf). The SCSI virtio driver then waits indefinitely for this request to be completed, but it never will because vhost-scsi never responds to that request. To avoid a hang, always respond to control queue operations; explicitly reject TMF requests, and return a no-op response to event requests. Signed-off-by: Bijan Mottahedeh --- drivers/vhost/scsi.c | 190 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 190 insertions(+) diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c index c24bb69..faf0dcf 100644 --- a/drivers/vhost/scsi.c +++ b/drivers/vhost/scsi.c @@ -1048,9 +1048,199 @@ static void vhost_scsi_submission_work(struct work_struct *work) mutex_unlock(&vq->mutex); } +static void +vhost_scsi_send_tmf_resp(struct vhost_scsi *vs, + struct vhost_virtqueue *vq, + int head, unsigned int out) +{ + struct virtio_scsi_ctrl_tmf_resp __user *resp; + struct virtio_scsi_ctrl_tmf_resp rsp; + int ret; + + pr_debug("%s\n", __func__); + memset(&rsp, 0, sizeof(rsp)); + rsp.response = VIRTIO_SCSI_S_FUNCTION_REJECTED; + resp = vq->iov[out].iov_base; + ret = __copy_to_user(resp, &rsp, sizeof(rsp)); + if (!ret) + vhost_add_used_and_signal(&vs->dev, vq, head, 0); + else + pr_err("Faulted on virtio_scsi_ctrl_tmf_resp\n"); +} + +static void +vhost_scsi_send_an_resp(struct vhost_scsi *vs, + struct vhost_virtqueue *vq, + int head, unsigned int out) +{ + struct virtio_scsi_ctrl_an_resp __user *resp; + struct virtio_scsi_ctrl_an_resp rsp; + int ret; + + pr_debug("%s\n", __func__); + memset(&rsp, 0, sizeof(rsp)); /* event_actual = 0 */ + rsp.response = VIRTIO_SCSI_S_OK; + resp = vq->iov[out].iov_base; + ret = __copy_to_user(resp, &rsp, sizeof(rsp)); + if (!ret) + vhost_add_used_and_signal(&vs->dev, vq, head, 0); + else + pr_err("Faulted on virtio_scsi_ctrl_an_resp\n"); +} + +static void +vhost_scsi_ctl_handle_vq(struct vhost_scsi *vs, struct vhost_virtqueue *vq) +{ + union { + __virtio32 type; + struct virtio_scsi_ctrl_an_req an; + struct virtio_scsi_ctrl_tmf_req tmf; + } v_req; + struct iov_iter out_iter; + unsigned int out = 0, in = 0; + int head; + size_t req_size, rsp_size, typ_size; + size_t out_size, in_size; + u8 *lunp; + void *req; + + mutex_lock(&vq->mutex); + /* + * We can handle the vq only after the endpoint is setup by calling the + * VHOST_SCSI_SET_ENDPOINT ioctl. + */ + if (!vq->private_data) + goto out; + + vhost_disable_notify(&vs->dev, vq); + + for (;;) { + head = vhost_get_vq_desc(vq, vq->iov, + ARRAY_SIZE(vq->iov), &out, &in, + NULL, NULL); + pr_debug("vhost_get_vq_desc: head: %d, out: %u in: %u\n", + head, out, in); + /* On error, stop handling until the next kick. */ + if (unlikely(head < 0)) + break; + /* Nothing new? Wait for eventfd to tell us they refilled. */ + if (head == vq->num) { + if (unlikely(vhost_enable_notify(&vs->dev, vq))) { + vhost_disable_notify(&vs->dev, vq); + continue; + } + break; + } + + /* + * Get the size of request and response buffers. + */ + out_size = iov_length(vq->iov, out); + in_size = iov_length(&vq->iov[out], in); + + /* + * Copy over the virtio-scsi request header, which for a + * ANY_LAYOUT enabled guest may span multiple iovecs, or a + * single iovec may contain both the header + outgoing + * WRITE payloads. + * + * copy_from_iter() will advance out_iter, so that it will + * point at the start of the outgoing WRITE payload, if + * DMA_TO_DEVICE is set. + */ + iov_iter_init(&out_iter, WRITE, vq->iov, out, out_size); + + req = &v_req.type; + typ_size = sizeof(v_req.type); + + if (unlikely(!copy_from_iter_full(req, typ_size, &out_iter))) { + vq_err(vq, "Faulted on copy_from_iter tmf type\n"); + /* + * The size of the response buffer varies based on + * the request type and must be validated against it. + * Since the request type is not known, don't send + * a response. + */ + continue; + } + + switch (v_req.type) { + case VIRTIO_SCSI_T_TMF: + req = &v_req.tmf; + lunp = &v_req.tmf.lun[0]; + req_size = sizeof(struct virtio_scsi_ctrl_tmf_req); + rsp_size = sizeof(struct virtio_scsi_ctrl_tmf_resp); + break; + case VIRTIO_SCSI_T_AN_QUERY: + case VIRTIO_SCSI_T_AN_SUBSCRIBE: + req = &v_req.an; + lunp = &v_req.an.lun[0]; + req_size = sizeof(struct virtio_scsi_ctrl_an_req); + rsp_size = sizeof(struct virtio_scsi_ctrl_an_resp); + break; + default: + vq_err(vq, "Unknown control request %d", v_req.type); + continue; + } + + /* + * Check for a sane response buffer so we can report early + * errors back to the guest. + */ + if (unlikely(in_size < rsp_size)) { + vq_err(vq, + "Resp buf too small, need min %zu bytes got %zu", + rsp_size, in_size); + /* + * Notifications are disabled at this point; + * continue so they can be eventually enabled + * when processing terminates. + */ + continue; + } + + if (unlikely(out_size < req_size)) { + vq_err(vq, + "Req buf too small, need min %zu bytes got %zu", + req_size, out_size); + vhost_scsi_send_bad_target(vs, vq, head, out); + continue; + } + + req += typ_size; + req_size -= typ_size; + + if (unlikely(!copy_from_iter_full(req, req_size, &out_iter))) { + vq_err(vq, "Faulted on copy_from_iter\n"); + vhost_scsi_send_bad_target(vs, vq, head, out); + continue; + } + + /* virtio-scsi spec requires byte 0 of the lun to be 1 */ + if (unlikely(*lunp != 1)) { + vq_err(vq, "Illegal virtio-scsi lun: %u\n", *lunp); + vhost_scsi_send_bad_target(vs, vq, head, out); + continue; + } + + if (v_req.type == VIRTIO_SCSI_T_TMF) { + pr_debug("%s tmf %d\n", __func__, v_req.tmf.subtype); + vhost_scsi_send_tmf_resp(vs, vq, head, out); + } else + vhost_scsi_send_an_resp(vs, vq, head, out); + } +out: + mutex_unlock(&vq->mutex); +} + static void vhost_scsi_ctl_handle_kick(struct vhost_work *work) { + struct vhost_virtqueue *vq = container_of(work, struct vhost_virtqueue, + poll.work); + struct vhost_scsi *vs = container_of(vq->dev, struct vhost_scsi, dev); + pr_debug("%s: The handling func for control queue.\n", __func__); + vhost_scsi_ctl_handle_vq(vs, vq); } static void