From nobody Thu Sep 24 21:18:42 2026 Received: from mx0a-0031df01.pphosted.com (mx0a-0031df01.pphosted.com [205.220.168.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3E1CE43847E for ; Sun, 20 Sep 2026 12:25:01 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.168.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789907108; cv=none; b=dXtuYFZWMJ8yjImmf+jmg7GQqXYVZhcbVHHVm9kACdLs4g3RVjSJ1I/CiECo6C0s8KYhu/+VpaLINLscBrnvbfPupycwgAuPrxji8Sg18IU23BtE9W8767vA6avswW38f/ZGl2cPTLgpp4K/nbZSRFPOGELwO7pIRx4r8AK3iRc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789907108; c=relaxed/simple; bh=/U5AIbq6dpv9hYmCEtYWD/LG4LRF7AuC0v8lt9FBAFg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=DLWoc7hEoDWTkDITtAGkfBB0Hy5B3WDCvEv6bM0fq7f+QWFMpHDesl7+/4HzTlVB5Krn6tIA5r8AmgkGg4A80JamEAH7mnmDx7legJ9VQA9XqJWtPVbCFW1BJ3qiUidyf5LimKPK9JZMFXVMMIZnGWPIPfjfXRttzTz8T7MMtoQ= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com; spf=pass smtp.mailfrom=oss.qualcomm.com; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b=acCQDNFs; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b=YTUTMkE/; arc=none smtp.client-ip=205.220.168.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b="acCQDNFs"; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="YTUTMkE/" Received: from pps.filterd (m0279866.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 68KBnqBY1572111 for ; Sun, 20 Sep 2026 12:24:58 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h= cc:content-transfer-encoding:date:from:in-reply-to:message-id :mime-version:references:subject:to; s=qcppdkim1; bh=JXnBYwBrV/5 ZgHaXuhnSHXfV+y8bvDojjxpiwpZmEhE=; b=acCQDNFsvm9WSUfqgYZADNthi8m /uge79uOz95wTYj6x4RfHJFcc2ECmMqbSWI0WvO0EwjCBSkFgkcBTYkvaBRwgQN0 9c/zEJTxBX+YQYi7aQAs0ScM6uEYF7btICn7WfzWL6bjohRe5bAIsFr4CsxvX6vo vN2HJt4JZbkuhks9di6XQpym8r9/txW9Jv1I+3kEYI/B0AMS4ZRUyDSsxrrJuF2N UJiM2VlpynlOHmqblPL5B8H4Ov99Vfmn6sXgLDq74DNZocXRwcS2JrMtoJSQxhJo COg7eemr03viYursGy6Cx5M+DlFZw+GFa/ULn9/aliBlrBnSskLd7qf2UBg== Received: from mail-pl1-f197.google.com (mail-pl1-f197.google.com [209.85.214.197]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4gskp9tt08-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Sun, 20 Sep 2026 12:24:58 +0000 (GMT) Received: by mail-pl1-f197.google.com with SMTP id d9443c01a7336-2d55d8cd938so39045355ad.1 for ; Sun, 20 Sep 2026 05:24:58 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1789907098; x=1790511898; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=JXnBYwBrV/5ZgHaXuhnSHXfV+y8bvDojjxpiwpZmEhE=; b=YTUTMkE/M28WxGOhiZ3LUxzR/insg6Ma5W9OVbZxRdZEN/N1T/TLwTBX2t9ZzNxIDy YoFQt+zz4NAt+I4BUxW0mR8cW2x+UwIk0gnue6uUQDJvlUyzco0EqQDGgoo+vpJvmrQQ rxg+WdKnxz5woHGvYtUgenwFd/w2vlHH1WF0Ac4MTF9kQrzgWzoFvzDTJBJo4Nq+oM2z Uk7jRphL6f1oiqPgrg8p13HyKgwlKjjz4z2F9EH43PFXRkt+Z+XBf0BNq2qu/FAzJ040 6izmMNt+U/2qT5TCcrpMVFTZLzMrgd3y+2IWxkFSvN6F/w4Indhv+MPauAt7aX9fnapQ pXWQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789907098; x=1790511898; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=JXnBYwBrV/5ZgHaXuhnSHXfV+y8bvDojjxpiwpZmEhE=; b=oF1CMvZbgEFqi1zElCVPJwDGyg7nwnegkbQ8hEEHBFDmsGfVFDH05Pj82sQ/fo0tXo mnlLgObznALKw7ru0/vzbTPQ27QBJvaSNCJuan5latv+aoAXeJwgJInMCqF9MEKKC05n 3ZJ8vO2MYnnOEfD+gIKYgn5nkckpBt/ln9co8KNh3MzrBJhgdrsmopMVRVZHrsIyKFkK sfY++IEdW2FR0zrqHMy/IOAB2lCaKmbBV5bWjj1c3N6EX+sM7muFvRMFOM/pJBLsNIPz yLz5IiD3Tn/4AjxzIxogKfcjH8jjDZvsfQPxwz+2rqEQvLhriBFiikR4jOkMUhFhQjkL u+gw== X-Forwarded-Encrypted: i=1; AKwUvByjhDrMunuHX+oHsGzVU8453v5Cp6F14NOmgJq+iE3qUOK3lUVved3fhYmb0aItpLEFsfBwdP6vgKwzTwc=@vger.kernel.org X-Gm-Message-State: AFuF++nbflzcSeqV6Joda1d46MgvEjsG4mOMBbCN7S1UshPnCpyROaEI qgweGAScxmLy1QtoqcN2vY/HfwvKzZoA2dgk3ViJfQaVXt9IezQmztWVohHWvjBSrUPhs4NzZrE wF45Lo+sX3ysE74XE3AQAWSdu8wNKgBLDE8biqfrNM2uG1vYj+CC4Z/XzgsKdakiE5vA= X-Gm-Gg: AYBFou1McLZPcrqzc2VpbFHoSanTMuHSycpMGRwNYQWR6W7O9vFoxx4FZByJ/z8AmMP bBhbiJ4A1rCU/inqyXFTpLhZw8wb07nzrPOG8Hg2ktVsOFmDghOPSUmVK0q3zja/NZWiBavONmu 1PWI1oYg3f3OGFnNDR76Axh8RWD4dWvSVZueXY76ZrYB7edLMbWgvphE51XVe0vcpxvZKZo5/OK BFnYuhdR7HRjmj27YolgWhzyDE59bu2huSFzjAzsUYxsK80omtKjSzaX/h5+a9RdFM42T2FvBtw xvvWy1z21hiaOonkU2iyXs2oaU66Kpl3PLFuYyod33m/tO+5leKQw7cVFIPJ3XgmiNAH5bukswU kzfjs4G2y3w3KSGhazU8kue6k5PdrHBDxfK1p7F2pyH1W+638TwDkEIisSg== X-Received: by 2002:a17:903:3c70:b0:2d0:cc92:f7c2 with SMTP id d9443c01a7336-2ddb1aca947mr136164285ad.1.1789907097850; Sun, 20 Sep 2026 05:24:57 -0700 (PDT) X-Received: by 2002:a17:903:3c70:b0:2d0:cc92:f7c2 with SMTP id d9443c01a7336-2ddb1aca947mr136164035ad.1.1789907097339; Sun, 20 Sep 2026 05:24:57 -0700 (PDT) Received: from u24-san1p10108.qualcomm.com (i-global254.qualcomm.com. [199.106.103.254]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-33c331aef9bsm12007386eec.25.2026.09.20.05.24.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 20 Sep 2026 05:24:57 -0700 (PDT) From: Linlin Zhang To: mst@redhat.com, jasowangio@gmail.com, axboe@kernel.dk, ebiggers@kernel.org, stefanha@redhat.com Cc: pbonzini@redhat.com, eperezma@redhat.com, xuanzhuo@linux.alibaba.com, virtualization@lists.linux.dev, linux-block@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v3 1/2] virtio_blk: Add control virtqueue support Date: Sun, 20 Sep 2026 05:24:31 -0700 Message-ID: <20260920122444.2549493-2-linlin.zhang@oss.qualcomm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260920122444.2549493-1-linlin.zhang@oss.qualcomm.com> References: <20260920122444.2549493-1-linlin.zhang@oss.qualcomm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Proofpoint-ORIG-GUID: uVYrphYdYzEkCrvDwuQs5skBSCohL_7W X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwOTIwMDE4MCBTYWx0ZWRfX4hrjleT8SXmp EL5GkKsnZA0g2ppvdIC8won6l0JdyAzBI06+4ofea1C940EpWDfEddzOJFqE0vlEpy/JU5PmOtt K4mrZhrQSsNflJ4kVfQc7b9S/UvaPrNjWr40yo9qWGOCKB8c4/zbbxwmRPqPd/02+SrLBPnvbGU 3b1IveZbWLuCiqZMJZcnCitMUFsiIvh0MkK9oYwAJNbuaIPtSTH7jtdJkioUpMXgvswaYa0ra41 EXH38kTb8BDBRLu+r2n0YcPZ/G4gd3lAnmsidbQJwfvVdBWBhOYsu5+67s8zq+nyN89/l45Eq7d RMTPXfgLbWW0KsGIDMAne/9pqkhqBhB8EDSo4bkRvA6cnTWwNu3o7JtWCB9v66kAT8QMCUgfi+L chAu+RGRI7ATP3mruXDgg9ziJSEkilPzdzc0G0UqsNPPPhfr3iZPByhuZkOA5Qf5lVOotHwjHvx TCgBc56miDLWDeE8+9Q== X-Proofpoint-Spam-Info: AW1haW4tMjYwOTIwMDE4MCBTYWx0ZWRfX1L09C0uVbdpA anpe32KTXH5NVgOCMiJDQfv2TOhN1o55n87hMQl/4JoRXHK5WDO94jOOXrHr2GiM1B54RKQEn7N 2p1ypHLsrYQil0bJ8PI2xYs6nF4MpJw= X-Authority-Analysis: v=2.4 cv=BKAmP1QG c=1 sm=1 tr=0 ts=6aafd09a cx=c_pps a=cmESyDAEBpBGqyK7t0alAg==:117 a=JYp8KDb2vCoCEuGobkYCKw==:17 a=VdqzKS8jKosA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=YMgV9FUhrdKAYTUUvYB2:22 a=EUspDBNiAAAA:8 a=mWMrKjBDJ3x8XhIyD1MA:9 a=1OuFwYUASf3TG4hYMiVC:22 X-Proofpoint-GUID: uVYrphYdYzEkCrvDwuQs5skBSCohL_7W X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1176,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-09-20_04,2026-09-16_02,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 clxscore=1015 priorityscore=1501 suspectscore=0 impostorscore=0 spamscore=0 malwarescore=0 bulkscore=0 adultscore=0 lowpriorityscore=0 phishscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2609040000 definitions=main-2609200180 Content-Type: text/plain; charset="utf-8" From: linlzhan Add support for the optional virtio-blk control virtqueue. If control queue feature bit is negociated, this allows the driver to manage control-queue requests independently from the data path and to safely handle outstanding requests during device removal and suspend. No control command is submitted by this change. The control virtqueue will be used by a subsequent inline encryption implementation. Signed-off-by: linlzhan --- drivers/block/virtio_blk.c | 241 +++++++++++++++++++++++++++++++- include/uapi/linux/virtio_blk.h | 1 + 2 files changed, 238 insertions(+), 4 deletions(-) diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index 32bf3ba07a9d..2fad86e8f7a9 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -52,6 +53,15 @@ struct virtio_blk_vq { char name[VQ_NAME_LEN]; } ____cacheline_aligned_in_smp; =20 +struct virtio_blk_ctrl_vq { + struct virtqueue *vq; + struct mutex mutex; + spinlock_t lock; + unsigned int inflight; + bool dead; + struct completion drained; +}; + struct virtio_blk { /* * This mutex must be held by anything that may run after @@ -83,6 +93,9 @@ struct virtio_blk { =20 /* For zoned device */ unsigned int zone_sectors; + + /* Control virtqueue state. */ + struct virtio_blk_ctrl_vq ctrl_vq; }; =20 struct virtblk_req { @@ -110,6 +123,20 @@ struct virtblk_req { struct scatterlist sg[]; }; =20 +struct virtblk_ctrl_request { + __virtio32 type; + u8 status; + + struct completion *compl; + /* + * Set when virtblk_ctrl_vq_request()'s waiter timed out and moved on + * without freeing this request. Whichever of virtblk_ctrlq_callback() + * or virtblk_ctrl_vq_drain() later retrieves the buffer must free + * @compl and this struct instead of calling complete() on them. + */ + bool abandoned; +}; + static inline blk_status_t virtblk_result(u8 status) { switch (status) { @@ -863,11 +890,184 @@ static int virtblk_getgeo(struct gendisk *disk, stru= ct hd_geometry *geo) return ret; } =20 +#define VIRTBLK_CTRL_VQ_TIMEOUT (10 * HZ) + +/* Prevent new submissions and wait for in-flight requests to complete. */ +static void virtblk_ctrl_vq_quiesce(struct virtio_blk *vblk) +{ + unsigned long flags; + bool need_wait; + + if (!vblk->ctrl_vq.vq) + return; + + init_completion(&vblk->ctrl_vq.drained); + + spin_lock_irqsave(&vblk->ctrl_vq.lock, flags); + vblk->ctrl_vq.dead =3D true; + need_wait =3D vblk->ctrl_vq.inflight !=3D 0; + spin_unlock_irqrestore(&vblk->ctrl_vq.lock, flags); + + if (need_wait && + !wait_for_completion_timeout(&vblk->ctrl_vq.drained, + VIRTBLK_CTRL_VQ_TIMEOUT)) + dev_warn(&vblk->vdev->dev, + "timed out waiting for control queue requests to complete\n"); +} + +/* Fail requests left in the control queue after reset. */ +static void virtblk_ctrl_vq_drain(struct virtio_blk *vblk) +{ + struct virtblk_ctrl_request *creq; + unsigned long flags; + + if (!vblk->ctrl_vq.vq) + return; + + spin_lock_irqsave(&vblk->ctrl_vq.lock, flags); + while ((creq =3D virtqueue_detach_unused_buf(vblk->ctrl_vq.vq)) !=3D NULL= ) { + bool abandoned =3D creq->abandoned; + + if (WARN_ON_ONCE(!vblk->ctrl_vq.inflight)) + ; + else + vblk->ctrl_vq.inflight--; + if (!abandoned) + creq->status =3D VIRTIO_BLK_S_IOERR; + spin_unlock_irqrestore(&vblk->ctrl_vq.lock, flags); + if (abandoned) { + kfree(creq->compl); + kfree(creq); + } else { + complete(creq->compl); + } + spin_lock_irqsave(&vblk->ctrl_vq.lock, flags); + } + spin_unlock_irqrestore(&vblk->ctrl_vq.lock, flags); +} + +static void virtblk_ctrlq_callback(struct virtqueue *vq) +{ + struct virtio_blk *vblk =3D vq->vdev->priv; + struct virtblk_ctrl_request *creq; + unsigned long flags; + unsigned int len; + + spin_lock_irqsave(&vblk->ctrl_vq.lock, flags); + do { + virtqueue_disable_cb(vq); + while ((creq =3D virtqueue_get_buf(vq, &len)) !=3D NULL) { + bool drained =3D false; + bool abandoned =3D creq->abandoned; + + if (WARN_ON_ONCE(!vblk->ctrl_vq.inflight)) { + /* + * Still resolve the request. Never leave a + * synchronous caller blocked because the accounting + * state was already inconsistent. + */ + spin_unlock_irqrestore(&vblk->ctrl_vq.lock, flags); + if (abandoned) { + kfree(creq->compl); + kfree(creq); + } else { + complete(creq->compl); + } + spin_lock_irqsave(&vblk->ctrl_vq.lock, flags); + continue; + } + + if (--vblk->ctrl_vq.inflight =3D=3D 0 && vblk->ctrl_vq.dead) + drained =3D true; + spin_unlock_irqrestore(&vblk->ctrl_vq.lock, flags); + if (drained) + complete(&vblk->ctrl_vq.drained); + if (abandoned) { + kfree(creq->compl); + kfree(creq); + } else { + complete(creq->compl); + } + spin_lock_irqsave(&vblk->ctrl_vq.lock, flags); + } + } while (!virtqueue_enable_cb(vq)); + spin_unlock_irqrestore(&vblk->ctrl_vq.lock, flags); +} + +/* Submit a control-queue request and wait for completion. */ +static int virtblk_ctrl_vq_request(struct virtio_blk *vblk, + struct virtblk_ctrl_request *creq, + struct scatterlist *sgs[], + unsigned int out_sgs, unsigned int in_sgs) +{ + struct completion *comp; + unsigned long flags; + int err; + + /* + * GFP_NOIO: this may be reached on the bio-submission path + * (memory reclaim writing back dirty pages to this same device), + * so GFP_KERNEL could self-deadlock. + */ + comp =3D kmalloc_obj(*comp, GFP_NOIO); + if (!comp) + return -ENOMEM; + init_completion(comp); + + mutex_lock(&vblk->ctrl_vq.mutex); + creq->compl =3D comp; + creq->abandoned =3D false; + + spin_lock_irqsave(&vblk->ctrl_vq.lock, flags); + if (vblk->ctrl_vq.dead) { + spin_unlock_irqrestore(&vblk->ctrl_vq.lock, flags); + mutex_unlock(&vblk->ctrl_vq.mutex); + kfree(comp); + return -ENODEV; + } + err =3D virtqueue_add_sgs(vblk->ctrl_vq.vq, sgs, out_sgs, in_sgs, creq, G= FP_ATOMIC); + if (!err) { + vblk->ctrl_vq.inflight++; + virtqueue_kick(vblk->ctrl_vq.vq); + } + spin_unlock_irqrestore(&vblk->ctrl_vq.lock, flags); + if (err) { + mutex_unlock(&vblk->ctrl_vq.mutex); + kfree(comp); + return err; + } + + if (wait_for_completion_timeout(comp, VIRTBLK_CTRL_VQ_TIMEOUT)) { + mutex_unlock(&vblk->ctrl_vq.mutex); + kfree(comp); + return 0; + } + + /* + * The host hasn't responded within the timeout. @creq is still + * owned by the device, so don't touch its DMA-target fields or + * free it here. Mark it abandoned and hand ownership of both @creq + * and @comp to whichever of virtblk_ctrlq_callback() or + * virtblk_ctrl_vq_drain() retrieves the buffer later; unlock the + * mutex so subsequent requests aren't serialized behind an + * unresponsive host. + */ + spin_lock_irqsave(&vblk->ctrl_vq.lock, flags); + creq->abandoned =3D true; + spin_unlock_irqrestore(&vblk->ctrl_vq.lock, flags); + mutex_unlock(&vblk->ctrl_vq.mutex); + + dev_warn(&vblk->vdev->dev, + "control queue request timed out, abandoning\n"); + return -ETIMEDOUT; +} + static void virtblk_free_disk(struct gendisk *disk) { struct virtio_blk *vblk =3D disk->private_data; =20 ida_free(&vd_index_ida, vblk->index); + mutex_destroy(&vblk->ctrl_vq.mutex); mutex_destroy(&vblk->vdev_mutex); kfree(vblk); } @@ -965,6 +1165,8 @@ static int init_vq(struct virtio_blk *vblk) struct virtqueue **vqs; unsigned short num_vqs; unsigned short num_poll_vqs; + unsigned short total_vqs; + bool has_ctrl_vq; struct virtio_device *vdev =3D vblk->vdev; struct irq_affinity desc =3D { 0, }; =20 @@ -993,12 +1195,19 @@ static int init_vq(struct virtio_blk *vblk) vblk->io_queues[HCTX_TYPE_READ], vblk->io_queues[HCTX_TYPE_POLL]); =20 + /* + * The control vq is appended after the data vqs whenever + * F_CTRL_VQ is negotiated. + */ + has_ctrl_vq =3D virtio_has_feature(vdev, VIRTIO_BLK_F_CTRL_VQ); + total_vqs =3D num_vqs + (has_ctrl_vq ? 1 : 0); + vblk->vqs =3D kmalloc_objs(*vblk->vqs, num_vqs); if (!vblk->vqs) return -ENOMEM; =20 - vqs_info =3D kzalloc_objs(*vqs_info, num_vqs); - vqs =3D kmalloc_objs(*vqs, num_vqs); + vqs_info =3D kzalloc_objs(*vqs_info, total_vqs); + vqs =3D kmalloc_objs(*vqs, total_vqs); if (!vqs_info || !vqs) { err =3D -ENOMEM; goto out; @@ -1015,8 +1224,13 @@ static int init_vq(struct virtio_blk *vblk) vqs_info[i].name =3D vblk->vqs[i].name; } =20 + if (has_ctrl_vq) { + vqs_info[num_vqs].callback =3D virtblk_ctrlq_callback; + vqs_info[num_vqs].name =3D "control"; + } + /* Discover virtqueues and write information to configuration. */ - err =3D virtio_find_vqs(vdev, num_vqs, vqs, vqs_info, &desc); + err =3D virtio_find_vqs(vdev, total_vqs, vqs, vqs_info, &desc); if (err) goto out; =20 @@ -1025,6 +1239,9 @@ static int init_vq(struct virtio_blk *vblk) vblk->vqs[i].vq =3D vqs[i]; } vblk->num_vqs =3D num_vqs; + vblk->ctrl_vq.vq =3D has_ctrl_vq ? vqs[num_vqs] : NULL; + vblk->ctrl_vq.dead =3D false; + vblk->ctrl_vq.inflight =3D 0; =20 out: kfree(vqs); @@ -1464,14 +1681,18 @@ static int virtblk_probe(struct virtio_device *vdev) } =20 mutex_init(&vblk->vdev_mutex); + mutex_init(&vblk->ctrl_vq.mutex); + spin_lock_init(&vblk->ctrl_vq.lock); =20 vblk->vdev =3D vdev; =20 INIT_WORK(&vblk->config_work, virtblk_config_changed_work); =20 err =3D init_vq(vblk); - if (err) + if (err) { + dev_err(&vdev->dev, "init virt queue failed: err =3D %d\n", err); goto out_free_vblk; + } =20 /* Default queue sizing is to fill the ring. */ if (!virtblk_queue_depth) { @@ -1553,6 +1774,7 @@ static int virtblk_probe(struct virtio_device *vdev) out_free_vq: vdev->config->del_vqs(vdev); kfree(vblk->vqs); + vblk->ctrl_vq.vq =3D NULL; out_free_vblk: kfree(vblk); out_free_index: @@ -1571,16 +1793,21 @@ static void virtblk_remove(struct virtio_device *vd= ev) del_gendisk(vblk->disk); blk_mq_free_tag_set(&vblk->tag_set); =20 + virtblk_ctrl_vq_quiesce(vblk); + mutex_lock(&vblk->vdev_mutex); =20 /* Stop all the virtqueues. */ virtio_reset_device(vdev); + virtblk_ctrl_vq_drain(vblk); =20 /* Virtqueues are stopped, nothing can use vblk->vdev anymore. */ vblk->vdev =3D NULL; =20 vdev->config->del_vqs(vdev); kfree(vblk->vqs); + vblk->vqs =3D NULL; + vblk->ctrl_vq.vq =3D NULL; =20 mutex_unlock(&vblk->vdev_mutex); =20 @@ -1593,13 +1820,17 @@ static int virtblk_freeze_priv(struct virtio_device= *vdev) struct request_queue *q =3D vblk->disk->queue; unsigned int memflags; =20 + /* Ensure no requests in virtqueues before deleting vqs. */ memflags =3D blk_mq_freeze_queue(q); blk_mq_quiesce_queue_nowait(q); blk_mq_unfreeze_queue(q, memflags); =20 + virtblk_ctrl_vq_quiesce(vblk); + /* Ensure we don't receive any more interrupts */ virtio_reset_device(vdev); + virtblk_ctrl_vq_drain(vblk); =20 /* Make sure no work handler is accessing the device. */ flush_work(&vblk->config_work); @@ -1612,6 +1843,7 @@ static int virtblk_freeze_priv(struct virtio_device *= vdev) * pointers safely. */ vblk->vqs =3D NULL; + vblk->ctrl_vq.vq =3D NULL; =20 return 0; } @@ -1672,6 +1904,7 @@ static unsigned int features[] =3D { VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_TOPOLOGY, VIRTIO_BLK_F_CONFIG_WCE, VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_DISCARD, VIRTIO_BLK_F_WRITE_ZEROES, VIRTIO_BLK_F_SECURE_ERASE, VIRTIO_BLK_F_ZONED, + VIRTIO_BLK_F_CTRL_VQ, }; =20 static struct virtio_driver virtio_blk =3D { diff --git a/include/uapi/linux/virtio_blk.h b/include/uapi/linux/virtio_bl= k.h index 3744e4da1b2a..0a16972a1535 100644 --- a/include/uapi/linux/virtio_blk.h +++ b/include/uapi/linux/virtio_blk.h @@ -42,6 +42,7 @@ #define VIRTIO_BLK_F_WRITE_ZEROES 14 /* WRITE ZEROES is supported */ #define VIRTIO_BLK_F_SECURE_ERASE 16 /* Secure Erase is supported */ #define VIRTIO_BLK_F_ZONED 17 /* Zoned block device */ +#define VIRTIO_BLK_F_CTRL_VQ 22 /* Control queue */ =20 /* Legacy feature bits */ #ifndef VIRTIO_BLK_NO_LEGACY --=20 2.34.1 From nobody Thu Sep 24 21:18:42 2026 Received: from mx0a-0031df01.pphosted.com (mx0a-0031df01.pphosted.com [205.220.168.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 283EF3E1681 for ; Sun, 20 Sep 2026 12:25:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=205.220.168.131 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789907123; cv=none; b=M3VphBDZtmApHD/r79sFAtPmCQkwHa+Np+9megbnnCYuXYVxUgxe3+U2Nf2U6HUxTG/pkBG6JamGe2AijihQNzCwn7kY3N2Xj05fkL4QjCB5ShC8nUZFT2D9+His6Od5RnlrlInqpkCapT8a7eBAXZHFbbeDF1gIYmuwueTksjo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789907123; c=relaxed/simple; bh=jTDEcx0QAMA0lyxrUy4j0XDoCK3htmIZ3hOyMeVJEnY=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=FRUE8cENS4XsHdnzrwASejDuHHA9v5dCaWGFkerwoSa8v9jRzOmyp4ntQABdw3RpawPscB3bKKUdaJm+EF9a9jyreMk9Q0OoLW/jnNvl8/fkfBFDRH8eEqEOhJGZ9iGgWe9UTA8deQFRFSbGqBTRjQhb3eYKokS0b14wJnI21+E= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com; spf=pass smtp.mailfrom=oss.qualcomm.com; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b=j1si+E9H; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b=Ge1JE4nr; arc=none smtp.client-ip=205.220.168.131 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=oss.qualcomm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=qualcomm.com header.i=@qualcomm.com header.b="j1si+E9H"; dkim=pass (2048-bit key) header.d=oss.qualcomm.com header.i=@oss.qualcomm.com header.b="Ge1JE4nr" Received: from pps.filterd (m0279866.ppops.net [127.0.0.1]) by mx0a-0031df01.pphosted.com (8.18.1.11/8.18.1.11) with ESMTP id 68KBnvwk1572200 for ; Sun, 20 Sep 2026 12:25:00 GMT DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=qualcomm.com; h= cc:content-transfer-encoding:date:from:in-reply-to:message-id :mime-version:references:subject:to; s=qcppdkim1; bh=m5rUTyzfMIR fFWjBv3oHCftmU2JcMDhPJIv+az2123g=; b=j1si+E9HuGn+Zb2WuX989y/B5UY bhMmtfwRn/lmDvQA/cTnhaTpH56Y+3aI4AcKgLfaQ5kqlcZQ6emHgMYRCspjtZPK eLdHI6C/HubPBvYcy51yWWZIWzDYc/jZAwron6cB/KiPbx2TMIEEt+N6VA47Wsb6 lvRKhK/hfWMx3xRFdoSLOXHrhMkO262M9bbFXNKwyqvCX/CqhNyV9XPROtW3tRry dpF4Eu16YYkglkt/nDXn0VxNaw4tYDKcXlJvnAyAW+FHgvvTRyR6WtBBayO9XecY QTnxCY5jm5tFQ3cveXkHIx/XKN7QNP9nbpCzO0OwF2Gh+DrA4cYNbgH0ibQ== Received: from mail-pj1-f72.google.com (mail-pj1-f72.google.com [209.85.216.72]) by mx0a-0031df01.pphosted.com (PPS) with ESMTPS id 4gskp9tt0d-1 (version=TLSv1.3 cipher=TLS_AES_128_GCM_SHA256 bits=128 verify=NOT) for ; Sun, 20 Sep 2026 12:25:00 +0000 (GMT) Received: by mail-pj1-f72.google.com with SMTP id 98e67ed59e1d1-39e3dad7ab3so3091686a91.2 for ; Sun, 20 Sep 2026 05:25:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=oss.qualcomm.com; s=google; t=1789907100; x=1790511900; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=m5rUTyzfMIRfFWjBv3oHCftmU2JcMDhPJIv+az2123g=; b=Ge1JE4nr5hquKKHtsZjxBIuNHUG2MTt88t27jFURWOMobtq5yD6BuGjDYLMfzGqBSp OHxp4QhOord6LiPywVP3zCUR4IiMTiPMhYELHGwikSlYKobpFqoUOQ2cIUPToYavRPZs M2o2Fred4L+eVGfgE42T5IT14WAwOmbsAxV9st+nj5YX54ii362WmqW/d3G/XfS6cVe0 rD4OPTcql90bYyKbw+0yd8jjOHy1UrWRcfY8xkswTiCoy6mdYaQM9v+5qV2nRWzyLIbK jBRuLrEPKUhwQYmI6DM9yoQXak701XrTlTdtYoJh2KiQn+2FSuJYF+jWYu9QpbOB4NWQ VZzg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20260707; t=1789907100; x=1790511900; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=m5rUTyzfMIRfFWjBv3oHCftmU2JcMDhPJIv+az2123g=; b=EKND3vOVFFdj5FePIIq43KMeAJEogonFsJA6EDL6PJSORGiS1MwbwjRuAYl/KzBPxq 3KciIrq1tH910SPmwi8qOlidOmlm8cbgJ9Qri+avHq57C2eIIHfvo8vWg8HseoLxAwda 5Ea0fLiTXbwzCXT2jXTQyY3K+oFZqfgMlVl587d7sdy4Lau6DGf6dger4jpwdOco4OmV sJ/aaWDP+fRYpS0ZM9xm0b55AgvEkkOx7jZa/yB7tyIKRH+PVhVxqK7sEIMlr4muj8t7 R2VPE3juqzUEgOO34skX8kWC7+laGPtyR5E37Nv/fcSQfT9YQnIDTK1W/CdNhPtHmQfv BgvQ== X-Forwarded-Encrypted: i=1; AKwUvBx7PsgAl4i8X6AH7A21tmRjIcLzotdTZnzc4mH2cFNBxdK2GhIgKrdDEaXKTqDIzvX3HtkVKpecfGTvmPU=@vger.kernel.org X-Gm-Message-State: AFuF++mOGHh9P7+LysMb3BqAdjv6n/GywdEMbSNxfJpHK//MI6sf2Kp5 yE5s74Q20xVbSD7pKbmElHdpyPlTXXdkZ8RxYM2mz8r5bs14jKaveTAqzq4LJHH/SOxAAXecSnT BFSrLD5MF/pbmA1scdbg2fujth1eWreHPSj8u+Nrqiqw2ddDCH7/pCwVZr6252VvelBs= X-Gm-Gg: AYBFou1ChpA7DXwqS9gh4JlJ5Nw3emXFP3y4mBLerHgicrcxnFjcssf6t7wDkphm+z0 D3FqcLQFL5Go1lg9ScaDuJolWFGhEyLM4UNj8MWa4DIGwHxy0JvPGAEAP9RKzLUT09G8Lxx2INS w1DzExPNN/WuUeaZKPBDU7EqNVaOsdVPrbVznHeXPahxOEVlBl35c1qZOLi8FGlzntUDgZVMP82 4blqf/sKOcHaGjoUz8KwhJ/Kqdda60YTc0IzZSpJb7127Y5SIBXtVRHkivYUtvDqfyuBm55ogcC jTUrX5gE5spbIdEmcATp6oARB4YlUXUNMPrVXKhOuDHXWYEvR7OCebeW+/maYwBrbS6USzaD4Ei IrxgTIlG6zFQonZfP5czE2XsR804lq9OGa9vt2+Y/utlMEdJ/alw9Yy5dQQ== X-Received: by 2002:a17:90b:3f4e:b0:39e:6a80:dda0 with SMTP id 98e67ed59e1d1-39e6a810c46mr6720540a91.39.1789907099523; Sun, 20 Sep 2026 05:24:59 -0700 (PDT) X-Received: by 2002:a17:90b:3f4e:b0:39e:6a80:dda0 with SMTP id 98e67ed59e1d1-39e6a810c46mr6720503a91.39.1789907098785; Sun, 20 Sep 2026 05:24:58 -0700 (PDT) Received: from u24-san1p10108.qualcomm.com (i-global254.qualcomm.com. [199.106.103.254]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-33c331aef9bsm12007386eec.25.2026.09.20.05.24.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sun, 20 Sep 2026 05:24:58 -0700 (PDT) From: Linlin Zhang To: mst@redhat.com, jasowangio@gmail.com, axboe@kernel.dk, ebiggers@kernel.org, stefanha@redhat.com Cc: pbonzini@redhat.com, eperezma@redhat.com, xuanzhuo@linux.alibaba.com, virtualization@lists.linux.dev, linux-block@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v3 2/2] virtio_blk: add inline encryption support Date: Sun, 20 Sep 2026 05:24:32 -0700 Message-ID: <20260920122444.2549493-3-linlin.zhang@oss.qualcomm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20260920122444.2549493-1-linlin.zhang@oss.qualcomm.com> References: <20260920122444.2549493-1-linlin.zhang@oss.qualcomm.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Proofpoint-ORIG-GUID: sML-1E-c-AEBcq4jE7WZ25xdGafukU-a X-Proofpoint-Spam-Details-Enc: AW1haW4tMjYwOTIwMDE4MCBTYWx0ZWRfX1k4LIdvxcstq F77nwuu1LLhVgOTdYJ3E7pnsTE6CkgPDEQcwQe6yll7ox6ruLJ5aHeXva9V/CbTVnIAXWpjpFc8 jylqmD0Evu/fRjl9YMpBV5DKP6S6hkHlZD6OJdqYbqWG3dqdwZa4eY9jIt5QaAeklqGojcfpONb V34ojlIaRyh0B59adQwjxLMVAPCyDW2EPfwe4OsrNRSA11qBApvtZKoRN5OUu2Fpqk68BWQuPS8 wD538K4iqhq6lzXxxVe3oa7Cphir5GTIrxslRV6rrfUWLtxfg4uH5/A1rq2AfmQdi9h4PVlpkQJ sss1EUyF23j6lpCbE2nAZp+7EkEzbfC3id8q7nHSjU9m2l6SLumVGiB6jNN1dsoTOF2ZW+0qUPY 4yaa/AYe8enQwX8gT+70pEnYpalAPcM6tHjqG0krmkWRWYRiCRmFfsCGQYjeafhyd8a1NdkMh+9 J/zgWX2+V0rlnppoKdQ== X-Proofpoint-Spam-Info: AW1haW4tMjYwOTIwMDE4MCBTYWx0ZWRfX5T5UjCLdgoSv YTV8uQcWSddRpi9yIBmcALuUOB0T5TGgZCREMa9iKhtE0OXfL7Ch7EokLr/OcdlPdyrgmy5jMlN 0XshpZhz35/6YziIWqdE6X9qeWYtyVI= X-Authority-Analysis: v=2.4 cv=BKAmP1QG c=1 sm=1 tr=0 ts=6aafd09c cx=c_pps a=RP+M6JBNLl+fLTcSJhASfg==:117 a=JYp8KDb2vCoCEuGobkYCKw==:17 a=VdqzKS8jKosA:10 a=s4-Qcg_JpJYA:10 a=VkNPw1HP01LnGYTKEx00:22 a=u7WPNUs3qKkmUXheDGA7:22 a=YMgV9FUhrdKAYTUUvYB2:22 a=EUspDBNiAAAA:8 a=whdzJqZpIcB1mn4a2kEA:9 a=iS9zxrgQBfv6-_F4QbHw:22 X-Proofpoint-GUID: sML-1E-c-AEBcq4jE7WZ25xdGafukU-a X-Proofpoint-Virus-Version: vendor=baseguard engine=ICAP:2.0.293,Aquarius:18.0.1176,Hydra:6.1.134,FMLib:17.12.100.49 definitions=2026-09-20_04,2026-09-16_02,2025-10-01_01 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 clxscore=1015 priorityscore=1501 suspectscore=0 impostorscore=0 spamscore=0 malwarescore=0 bulkscore=0 adultscore=0 lowpriorityscore=0 phishscore=0 classifier=typeunknown authscore=0 authtc= authcc= route=outbound adjust=0 reason=mlx scancount=1 engine=8.22.0-2609040000 definitions=main-2609200180 Content-Type: text/plain; charset="utf-8" From: linlzhan Add support for the virtio-blk inline encryption feature (VIRTIO_BLK_F_INLINE_ENCRYPTION), which lets the guest offload per-I/O encryption to the host's inline crypto engine instead of doing it in software in the guest. Advertise the device's inline encryption characteristics (key types, supported crypto modes, max keyslots, DUN size) and wire them up to a struct blk_crypto_profile so upper layers can attach encryption contexts to bios as usual. Key management (program, evict, generate, import, prepare key, derive software secret) is carried out as control commands over the control virtqueue, and per-request keyslot and data unit number are carried in an extended request header for read/write commands. Inline encryption is only enabled when both the control virtqueue and inline encryption feature bits are negotiated, and is gated behind a new VIRTIO_BLK_INLINE_ENCRYPTION Kconfig option that depends on BLK_INLINE_ENCRYPTION, so kernels that don't select it are unaffected. Signed-off-by: linlzhan --- drivers/block/Kconfig | 12 + drivers/block/virtio_blk.c | 668 +++++++++++++++++++++++++++++++- include/linux/virtio_blk.h | 87 +++++ include/uapi/linux/virtio_blk.h | 123 +++++- 4 files changed, 874 insertions(+), 16 deletions(-) create mode 100644 include/linux/virtio_blk.h diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index 858320b6ebb7..58bb050d4617 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -372,4 +372,16 @@ config BLK_DEV_ZONED_LOOP =20 If unsure, say N. =20 +config VIRTIO_BLK_INLINE_ENCRYPTION + tristate "Virtio block inline encryption support" + depends on VIRTIO_BLK && BLK_INLINE_ENCRYPTION + help + Say 'Y or M' here will allow the virtio block driver to route crypto + requests to a different operating system in a virtualized + environment. This is useful to encrypt the data stored in the storage + by using storage inline crypto engine. The control queue feature bit + must be negotiated to enable this functionality. + + If unsure, say N. + endif # BLK_DEV diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index 2fad86e8f7a9..30c303364ca9 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c @@ -17,6 +17,7 @@ #include #include #include +#include =20 #define PART_BITS 4 #define VQ_NAME_LEN 16 @@ -94,13 +95,24 @@ struct virtio_blk { /* For zoned device */ unsigned int zone_sectors; =20 + /* For inline encryption support */ + struct blk_crypto_profile profile; + bool crypto_profile_initialized; + /* Control virtqueue state. */ struct virtio_blk_ctrl_vq ctrl_vq; }; =20 struct virtblk_req { /* Out header */ - struct virtio_blk_outhdr out_hdr; + union { + struct virtio_blk_outhdr base; + struct { + struct virtio_blk_outhdr base; + /* Crypto message (if VIRTIO_BLK_F_INLINE_ENCRYPTION) */ + struct virtio_blk_crypto_msg msg; + } crypto_append; + } out_hdr; =20 /* In header */ union { @@ -124,7 +136,21 @@ struct virtblk_req { }; =20 struct virtblk_ctrl_request { + /* Type byte, always its own out-sg for every command. */ __virtio32 type; + /* Out request, sent as a second, separate out-sg if any. */ + union { + struct virtio_blk_crypto_key_desc key_desc; + struct virtio_blk_crypto_key_blob blob; + } out_req; + + /* In response */ + union { + struct virtio_blk_crypto_key_blob blob; + struct virtio_blk_crypto_sw_secret secret; + struct virtio_blk_crypto_modes modes; + } in_resp; + /* Status byte, always its own in-sg for every command. */ u8 status; =20 struct completion *compl; @@ -167,12 +193,17 @@ static int virtblk_add_req(struct virtqueue *vq, stru= ct virtblk_req *vbr) { struct scatterlist out_hdr, in_hdr, *sgs[3]; unsigned int num_out =3D 0, num_in =3D 0; + size_t out_hdr_len =3D sizeof(vbr->out_hdr.base); =20 - sg_init_one(&out_hdr, &vbr->out_hdr, sizeof(vbr->out_hdr)); + if (vbr->out_hdr.base.type =3D=3D cpu_to_virtio32(vq->vdev, VIRTIO_BLK_T_= CRYPTO_IN) || + vbr->out_hdr.base.type =3D=3D cpu_to_virtio32(vq->vdev, VIRTIO_BLK_T_= CRYPTO_OUT)) + out_hdr_len =3D sizeof(vbr->out_hdr.crypto_append); + + sg_init_one(&out_hdr, &vbr->out_hdr, out_hdr_len); sgs[num_out++] =3D &out_hdr; =20 if (vbr->sg_table.nents) { - if (vbr->out_hdr.type & cpu_to_virtio32(vq->vdev, VIRTIO_BLK_T_OUT)) + if (vbr->out_hdr.base.type & cpu_to_virtio32(vq->vdev, VIRTIO_BLK_T_OUT)) sgs[num_out++] =3D vbr->sg_table.sgl; else sgs[num_out + num_in++] =3D vbr->sg_table.sgl; @@ -262,6 +293,22 @@ static void virtblk_cleanup_cmd(struct request *req) kfree(bvec_virt(&req->special_vec)); } =20 +#if IS_ENABLED(CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION) +static bool is_crypto_request(struct request *req) +{ + struct request_queue *q =3D req->q; + + return q->crypto_profile && + req->crypt_ctx && + req->crypt_keyslot; +} +#else +static inline bool is_crypto_request(struct request *req) +{ + return false; +} +#endif + static blk_status_t virtblk_setup_cmd(struct virtio_device *vdev, struct request *req, struct virtblk_req *vbr) @@ -270,20 +317,27 @@ static blk_status_t virtblk_setup_cmd(struct virtio_d= evice *vdev, bool unmap =3D false; u32 type; u64 sector =3D 0; + int i; =20 if (!IS_ENABLED(CONFIG_BLK_DEV_ZONED) && op_is_zone_mgmt(req_op(req))) return BLK_STS_NOTSUPP; =20 /* Set fields for all request types */ - vbr->out_hdr.ioprio =3D cpu_to_virtio32(vdev, req_get_ioprio(req)); + vbr->out_hdr.base.ioprio =3D cpu_to_virtio32(vdev, req_get_ioprio(req)); =20 switch (req_op(req)) { case REQ_OP_READ: - type =3D VIRTIO_BLK_T_IN; + if (is_crypto_request(req)) + type =3D VIRTIO_BLK_T_CRYPTO_IN; + else + type =3D VIRTIO_BLK_T_IN; sector =3D blk_rq_pos(req); break; case REQ_OP_WRITE: - type =3D VIRTIO_BLK_T_OUT; + if (is_crypto_request(req)) + type =3D VIRTIO_BLK_T_CRYPTO_OUT; + else + type =3D VIRTIO_BLK_T_OUT; sector =3D blk_rq_pos(req); break; case REQ_OP_FLUSH: @@ -336,8 +390,8 @@ static blk_status_t virtblk_setup_cmd(struct virtio_dev= ice *vdev, =20 /* Set fields for non-REQ_OP_DRV_IN request types */ vbr->in_hdr_len =3D in_hdr_len; - vbr->out_hdr.type =3D cpu_to_virtio32(vdev, type); - vbr->out_hdr.sector =3D cpu_to_virtio64(vdev, sector); + vbr->out_hdr.base.type =3D cpu_to_virtio32(vdev, type); + vbr->out_hdr.base.sector =3D cpu_to_virtio64(vdev, sector); =20 if (type =3D=3D VIRTIO_BLK_T_DISCARD || type =3D=3D VIRTIO_BLK_T_WRITE_ZE= ROES || type =3D=3D VIRTIO_BLK_T_SECURE_ERASE) { @@ -345,6 +399,18 @@ static blk_status_t virtblk_setup_cmd(struct virtio_de= vice *vdev, return BLK_STS_RESOURCE; } =20 + if (type =3D=3D VIRTIO_BLK_T_CRYPTO_IN || type =3D=3D VIRTIO_BLK_T_CRYPTO= _OUT) { + memset(&vbr->out_hdr.crypto_append.msg, 0, + sizeof(vbr->out_hdr.crypto_append.msg)); + vbr->out_hdr.crypto_append.msg.slot =3D + cpu_to_virtio32(vdev, + blk_crypto_keyslot_index(req->crypt_keyslot)); + for (i =3D 0; i < ARRAY_SIZE(vbr->out_hdr.crypto_append.msg.dun); i++) { + vbr->out_hdr.crypto_append.msg.dun[i] =3D + cpu_to_virtio64(vdev, req->crypt_ctx->bc_dun[i]); + } + } + return 0; } =20 @@ -595,8 +661,8 @@ static int virtblk_submit_zone_report(struct virtio_blk= *vblk, =20 vbr =3D blk_mq_rq_to_pdu(req); vbr->in_hdr_len =3D sizeof(vbr->in_hdr.status); - vbr->out_hdr.type =3D cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_ZONE_REPOR= T); - vbr->out_hdr.sector =3D cpu_to_virtio64(vblk->vdev, sector); + vbr->out_hdr.base.type =3D cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_ZONE_= REPORT); + vbr->out_hdr.base.sector =3D cpu_to_virtio64(vblk->vdev, sector); =20 err =3D blk_rq_map_kern(req, report_buf, report_len, GFP_KERNEL); if (err) @@ -844,8 +910,8 @@ static int virtblk_get_id(struct gendisk *disk, char *i= d_str) =20 vbr =3D blk_mq_rq_to_pdu(req); vbr->in_hdr_len =3D sizeof(vbr->in_hdr.status); - vbr->out_hdr.type =3D cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_GET_ID); - vbr->out_hdr.sector =3D 0; + vbr->out_hdr.base.type =3D cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_GET_I= D); + vbr->out_hdr.base.sector =3D 0; =20 err =3D blk_rq_map_kern(req, id_str, VIRTIO_BLK_ID_BYTES, GFP_KERNEL); if (err) @@ -1062,11 +1128,558 @@ static int virtblk_ctrl_vq_request(struct virtio_b= lk *vblk, return -ETIMEDOUT; } =20 +#if IS_ENABLED(CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION) +static int virtblk_get_crypto_modes(struct virtio_blk *vblk, + unsigned int *crypto_modes_supported) +{ + unsigned int nr_modes =3D VIRTIO_BLK_CRYPTO_MODE_MAX + 1; + struct scatterlist type_sg, resp_sg, status_sg, *sgs[3]; + struct virtblk_ctrl_request *creq; + unsigned int i; + int err; + + creq =3D kzalloc_obj(*creq, GFP_KERNEL); + if (!creq) + return -ENOMEM; + + creq->type =3D cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_GET_CRYPTO_MODES); + + sg_init_one(&type_sg, &creq->type, sizeof(creq->type)); + sg_init_one(&resp_sg, &creq->in_resp.modes, sizeof(creq->in_resp.modes)); + sg_init_one(&status_sg, &creq->status, sizeof(creq->status)); + sgs[0] =3D &type_sg; + sgs[1] =3D &resp_sg; + sgs[2] =3D &status_sg; + + err =3D virtblk_ctrl_vq_request(vblk, creq, sgs, 1, 2); + if (err =3D=3D -ETIMEDOUT) + return err; + if (err) + goto out_free; + + err =3D blk_status_to_errno(virtblk_result(creq->status)); + if (err) + goto out_free; + + for (i =3D 1; i < nr_modes; i++) { + u32 mode_mask =3D virtio32_to_cpu(vblk->vdev, + creq->in_resp.modes.modes[i]); + enum blk_crypto_mode_num mode =3D virtio_mode_to_blk(i); + + if (!mode_mask) + continue; + if (!mode) { + dev_warn(&vblk->vdev->dev, + "ignoring unknown crypto mode %u\n", i); + continue; + } + crypto_modes_supported[mode] =3D mode_mask; + } + +out_free: + kfree(creq); + return err; +} + +static int set_virtblk_crypto_key_desc(struct virtio_device *vdev, + struct virtblk_ctrl_request *creq, + const struct blk_crypto_key *key, + unsigned int slot) +{ + struct virtio_blk_crypto_key_desc *desc =3D &creq->out_req.key_desc; + unsigned int vtype =3D blk_key_type_to_virtio(key->crypto_cfg.key_type); + + if (sizeof(desc->bytes) < key->size) + return -EOVERFLOW; + if (!vtype) + return -EOPNOTSUPP; + + memset(desc, 0, sizeof(*desc)); + desc->slot =3D cpu_to_virtio32(vdev, slot); + memcpy(desc->bytes, key->bytes, key->size); + desc->key_size =3D cpu_to_virtio32(vdev, key->size); + desc->crypto_mode =3D cpu_to_virtio32(vdev, + blk_mode_to_virtio(key->crypto_cfg.crypto_mode)); + desc->key_type =3D cpu_to_virtio32(vdev, vtype); + desc->data_unit_size_bits =3D cpu_to_virtio32(vdev, key->data_unit_size_b= its); + desc->dun_bytes =3D cpu_to_virtio32(vdev, key->crypto_cfg.dun_bytes); + + return 0; +} + +static inline struct virtio_blk *virtblk_from_profile(struct blk_crypto_pr= ofile *profile) +{ + return container_of(profile, struct virtio_blk, profile); +} + +static int virtblk_crypto_keyslot_program(struct blk_crypto_profile *profi= le, + const struct blk_crypto_key *key, + unsigned int slot) +{ + struct virtio_blk *vblk =3D virtblk_from_profile(profile); + struct scatterlist type_sg, out_req_sg, status_sg, *sgs[3]; + struct virtblk_ctrl_request *creq; + int err; + + mutex_lock(&vblk->vdev_mutex); + if (!vblk->vdev) { + err =3D -ENXIO; + goto out_unlock; + } + + /* + * GFP_NOIO: this callback runs on the bio-submission path, which + * memory reclaim can reach while writing back dirty pages to this + * same device; GFP_KERNEL here could recurse into that same reclaim + * and self-deadlock. + */ + creq =3D kzalloc_obj(*creq, GFP_NOIO); + if (!creq) { + err =3D -ENOMEM; + goto out_unlock; + } + + creq->type =3D cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_CRYPTO_KEYSLOT_PR= OGRAM); + + err =3D set_virtblk_crypto_key_desc(vblk->vdev, creq, key, slot); + if (err) + goto out_free; + + sg_init_one(&type_sg, &creq->type, sizeof(creq->type)); + sg_init_one(&out_req_sg, &creq->out_req.key_desc, sizeof(creq->out_req.ke= y_desc)); + sg_init_one(&status_sg, &creq->status, sizeof(creq->status)); + sgs[0] =3D &type_sg; + sgs[1] =3D &out_req_sg; + sgs[2] =3D &status_sg; + + err =3D virtblk_ctrl_vq_request(vblk, creq, sgs, 2, 1); + if (err =3D=3D -ETIMEDOUT) + goto out_unlock; + if (err) + goto out_free; + + err =3D blk_status_to_errno(virtblk_result(creq->status)); +out_free: + kfree(creq); +out_unlock: + mutex_unlock(&vblk->vdev_mutex); + return err; +} + +static int virtblk_crypto_keyslot_evict(struct blk_crypto_profile *profile, + const struct blk_crypto_key *key, + unsigned int slot) +{ + struct virtio_blk *vblk =3D virtblk_from_profile(profile); + struct scatterlist type_sg, out_req_sg, status_sg, *sgs[3]; + struct virtblk_ctrl_request *creq; + int err; + + mutex_lock(&vblk->vdev_mutex); + if (!vblk->vdev) { + err =3D -ENXIO; + goto out_unlock; + } + + creq =3D kzalloc_obj(*creq, GFP_NOIO); + if (!creq) { + err =3D -ENOMEM; + goto out_unlock; + } + + creq->type =3D cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_CRYPTO_KEYSLOT_EV= ICT); + + err =3D set_virtblk_crypto_key_desc(vblk->vdev, creq, key, slot); + if (err) + goto out_free; + + sg_init_one(&type_sg, &creq->type, sizeof(creq->type)); + sg_init_one(&out_req_sg, &creq->out_req.key_desc, sizeof(creq->out_req.ke= y_desc)); + sg_init_one(&status_sg, &creq->status, sizeof(creq->status)); + sgs[0] =3D &type_sg; + sgs[1] =3D &out_req_sg; + sgs[2] =3D &status_sg; + + err =3D virtblk_ctrl_vq_request(vblk, creq, sgs, 2, 1); + if (err =3D=3D -ETIMEDOUT) + goto out_unlock; + if (err) + goto out_free; + + err =3D blk_status_to_errno(virtblk_result(creq->status)); +out_free: + kfree(creq); +out_unlock: + mutex_unlock(&vblk->vdev_mutex); + return err; +} + +static int virtblk_crypto_derive_sw_secret(struct blk_crypto_profile *prof= ile, + const u8 *eph_key, size_t eph_key_size, + u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE]) +{ + struct virtio_blk *vblk =3D virtblk_from_profile(profile); + struct scatterlist type_sg, out_req_sg, resp_sg, status_sg, *sgs[4]; + struct virtblk_ctrl_request *creq; + int err; + + mutex_lock(&vblk->vdev_mutex); + if (!vblk->vdev) { + err =3D -ENXIO; + goto out_unlock; + } + + if (eph_key_size > VIRTIO_BLK_CRYPTO_MAX_KEY_SIZE + || sizeof(creq->in_resp.secret.secret) < BLK_CRYPTO_SW_SECRET_SIZE) { + err =3D -EOVERFLOW; + goto out_unlock; + } + + creq =3D kzalloc_obj(*creq, GFP_KERNEL); + if (!creq) { + err =3D -ENOMEM; + goto out_unlock; + } + + creq->type =3D cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_CRYPTO_DERIVE_SW_= SECRET); + memcpy(creq->out_req.blob.key, eph_key, eph_key_size); + creq->out_req.blob.key_size =3D cpu_to_virtio32(vblk->vdev, eph_key_size); + + sg_init_one(&type_sg, &creq->type, sizeof(creq->type)); + sg_init_one(&out_req_sg, &creq->out_req.blob, sizeof(creq->out_req.blob)); + sg_init_one(&resp_sg, &creq->in_resp.secret, sizeof(creq->in_resp.secret)= ); + sg_init_one(&status_sg, &creq->status, sizeof(creq->status)); + sgs[0] =3D &type_sg; + sgs[1] =3D &out_req_sg; + sgs[2] =3D &resp_sg; + sgs[3] =3D &status_sg; + + err =3D virtblk_ctrl_vq_request(vblk, creq, sgs, 2, 2); + if (err =3D=3D -ETIMEDOUT) + goto out_unlock; + if (err) + goto out_free; + + err =3D blk_status_to_errno(virtblk_result(creq->status)); + if (err) + goto out_free; + + memcpy(sw_secret, creq->in_resp.secret.secret, BLK_CRYPTO_SW_SECRET_SIZE); +out_free: + kfree(creq); +out_unlock: + mutex_unlock(&vblk->vdev_mutex); + return err; +} + +static int virtblk_crypto_generate_key(struct blk_crypto_profile *profile, + u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]) +{ + struct virtio_blk *vblk =3D virtblk_from_profile(profile); + struct scatterlist type_sg, resp_sg, status_sg, *sgs[3]; + struct virtblk_ctrl_request *creq; + unsigned int key_size; + int err; + + mutex_lock(&vblk->vdev_mutex); + if (!vblk->vdev) { + err =3D -ENXIO; + goto out_unlock; + } + + creq =3D kzalloc_obj(*creq, GFP_KERNEL); + if (!creq) { + err =3D -ENOMEM; + goto out_unlock; + } + + creq->type =3D cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_CRYPTO_GENERATE_K= EY); + + sg_init_one(&type_sg, &creq->type, sizeof(creq->type)); + sg_init_one(&resp_sg, &creq->in_resp.blob, sizeof(creq->in_resp.blob)); + sg_init_one(&status_sg, &creq->status, sizeof(creq->status)); + sgs[0] =3D &type_sg; + sgs[1] =3D &resp_sg; + sgs[2] =3D &status_sg; + + err =3D virtblk_ctrl_vq_request(vblk, creq, sgs, 1, 2); + if (err =3D=3D -ETIMEDOUT) + goto out_unlock; + if (err) + goto out_free; + + err =3D blk_status_to_errno(virtblk_result(creq->status)); + if (err) + goto out_free; + + key_size =3D virtio32_to_cpu(vblk->vdev, creq->in_resp.blob.key_size); + if (!key_size || + key_size > BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE) { + dev_err(&vblk->vdev->dev, + "backend returned oversized generated key: %u\n", key_size); + err =3D -EOVERFLOW; + goto out_free; + } + memcpy(lt_key, creq->in_resp.blob.key, key_size); + err =3D key_size; +out_free: + kfree(creq); +out_unlock: + mutex_unlock(&vblk->vdev_mutex); + return err; +} + +static int virtblk_crypto_prepare_key(struct blk_crypto_profile *profile, + const u8 *lt_key, size_t lt_key_size, + u8 eph_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]) +{ + struct virtio_blk *vblk =3D virtblk_from_profile(profile); + struct scatterlist type_sg, out_req_sg, resp_sg, status_sg, *sgs[4]; + struct virtblk_ctrl_request *creq; + unsigned int key_size; + int err; + + mutex_lock(&vblk->vdev_mutex); + if (!vblk->vdev) { + err =3D -ENXIO; + goto out_unlock; + } + + if (lt_key_size > VIRTIO_BLK_CRYPTO_MAX_KEY_SIZE) { + err =3D -EOVERFLOW; + goto out_unlock; + } + + creq =3D kzalloc_obj(*creq, GFP_KERNEL); + if (!creq) { + err =3D -ENOMEM; + goto out_unlock; + } + + creq->type =3D cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_CRYPTO_PREPARE_KE= Y); + memcpy(creq->out_req.blob.key, lt_key, lt_key_size); + creq->out_req.blob.key_size =3D cpu_to_virtio32(vblk->vdev, lt_key_size); + + sg_init_one(&type_sg, &creq->type, sizeof(creq->type)); + sg_init_one(&out_req_sg, &creq->out_req.blob, sizeof(creq->out_req.blob)); + sg_init_one(&resp_sg, &creq->in_resp.blob, sizeof(creq->in_resp.blob)); + sg_init_one(&status_sg, &creq->status, sizeof(creq->status)); + sgs[0] =3D &type_sg; + sgs[1] =3D &out_req_sg; + sgs[2] =3D &resp_sg; + sgs[3] =3D &status_sg; + + err =3D virtblk_ctrl_vq_request(vblk, creq, sgs, 2, 2); + if (err =3D=3D -ETIMEDOUT) + goto out_unlock; + if (err) + goto out_free; + + err =3D blk_status_to_errno(virtblk_result(creq->status)); + if (err) + goto out_free; + + key_size =3D virtio32_to_cpu(vblk->vdev, creq->in_resp.blob.key_size); + if (!key_size || + key_size > BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE) { + dev_err(&vblk->vdev->dev, + "backend returned oversized prepared key: %u\n", key_size); + err =3D -EOVERFLOW; + goto out_free; + } + memcpy(eph_key, creq->in_resp.blob.key, key_size); + err =3D key_size; +out_free: + kfree(creq); +out_unlock: + mutex_unlock(&vblk->vdev_mutex); + return err; +} + +static int virtblk_crypto_import_key(struct blk_crypto_profile *profile, + const u8 *raw_key, size_t raw_key_size, + u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]) +{ + struct virtio_blk *vblk =3D virtblk_from_profile(profile); + struct scatterlist type_sg, out_req_sg, resp_sg, status_sg, *sgs[4]; + struct virtblk_ctrl_request *creq; + unsigned int key_size; + int err; + + mutex_lock(&vblk->vdev_mutex); + if (!vblk->vdev) { + err =3D -ENXIO; + goto out_unlock; + } + + if (raw_key_size > VIRTIO_BLK_CRYPTO_MAX_KEY_SIZE) { + err =3D -EOVERFLOW; + goto out_unlock; + } + + creq =3D kzalloc_obj(*creq, GFP_KERNEL); + if (!creq) { + err =3D -ENOMEM; + goto out_unlock; + } + + creq->type =3D cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_CRYPTO_IMPORT_KEY= ); + memcpy(creq->out_req.blob.key, raw_key, raw_key_size); + creq->out_req.blob.key_size =3D cpu_to_virtio32(vblk->vdev, raw_key_size); + + sg_init_one(&type_sg, &creq->type, sizeof(creq->type)); + sg_init_one(&out_req_sg, &creq->out_req.blob, sizeof(creq->out_req.blob)); + sg_init_one(&resp_sg, &creq->in_resp.blob, sizeof(creq->in_resp.blob)); + sg_init_one(&status_sg, &creq->status, sizeof(creq->status)); + sgs[0] =3D &type_sg; + sgs[1] =3D &out_req_sg; + sgs[2] =3D &resp_sg; + sgs[3] =3D &status_sg; + + err =3D virtblk_ctrl_vq_request(vblk, creq, sgs, 2, 2); + if (err =3D=3D -ETIMEDOUT) + goto out_unlock; + if (err) + goto out_free; + + err =3D blk_status_to_errno(virtblk_result(creq->status)); + if (err) + goto out_free; + + key_size =3D virtio32_to_cpu(vblk->vdev, creq->in_resp.blob.key_size); + if (!key_size || + key_size > BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE) { + dev_err(&vblk->vdev->dev, + "backend returned oversized imported key: %u\n", key_size); + err =3D -EOVERFLOW; + goto out_free; + } + memcpy(lt_key, creq->in_resp.blob.key, key_size); + err =3D key_size; +out_free: + kfree(creq); +out_unlock: + mutex_unlock(&vblk->vdev_mutex); + return err; +} + +static const struct blk_crypto_ll_ops virtblk_crypto_ops =3D { + .keyslot_program =3D virtblk_crypto_keyslot_program, + .keyslot_evict =3D virtblk_crypto_keyslot_evict, + .derive_sw_secret =3D virtblk_crypto_derive_sw_secret, + .generate_key =3D virtblk_crypto_generate_key, + .prepare_key =3D virtblk_crypto_prepare_key, + .import_key =3D virtblk_crypto_import_key, +}; + +static unsigned int get_supported_blk_key_types(u8 virtio_key_types) +{ + unsigned int supported =3D 0; + + if (virtio_key_types & VIRTIO_BLK_CRYPTO_KEY_TYPE_RAW) + supported |=3D virtio_key_type_to_blk(VIRTIO_BLK_CRYPTO_KEY_TYPE_RAW); + if (virtio_key_types & VIRTIO_BLK_CRYPTO_KEY_TYPE_HW_WRAPPED) + supported |=3D virtio_key_type_to_blk(VIRTIO_BLK_CRYPTO_KEY_TYPE_HW_WRAP= PED); + + return supported; +} + +static int virtblk_init_crypto(struct virtio_blk *vblk) +{ + struct virtio_device *vdev =3D vblk->vdev; + unsigned int crypto_modes_supported[BLK_ENCRYPTION_MODE_MAX] =3D { 0 }; + unsigned int key_type_supported; + u16 max_slots =3D 0; + /* virtio_cread() requires the variable size to match the config field ex= actly */ + u8 max_dun_bytes =3D 0, key_types =3D 0; + int err; + + virtio_cread(vdev, struct virtio_blk_config, + enc_characteristics.max_slots, &max_slots); + virtio_cread(vdev, struct virtio_blk_config, + enc_characteristics.max_dun_bytes, &max_dun_bytes); + virtio_cread(vdev, struct virtio_blk_config, + enc_characteristics.key_types, &key_types); + + dev_info_once(&vdev->dev, + "max_slots =3D %u, max_dun_bytes =3D %u, key_types =3D 0x%x\n", + max_slots, max_dun_bytes, key_types); + + if (!max_slots) + return -EINVAL; + + /* + * struct virtio_blk_crypto_msg.dun is a fixed array of four __virtio64 + * values (32 bytes total), matching the size of + * blk_crypto_ctx::bc_dun[4]. Refuse to advertise more than that as + * supported, or blk-crypto could negotiate a larger dun_bytes with the + * filesystem and have the high-order bytes of req->crypt_ctx->bc_dun + * silently dropped in virtblk_setup_cmd(). + */ + if (max_dun_bytes > sizeof_field(struct virtio_blk_crypto_msg, dun)) + return -EINVAL; + + key_type_supported =3D get_supported_blk_key_types(key_types); + if (!key_type_supported) + return -EINVAL; + + err =3D virtblk_get_crypto_modes(vblk, crypto_modes_supported); + if (err) { + dev_err(&vdev->dev, "get crypto modes failed: %d\n", err); + return err; + } + + /* + * Use the plain (non-devm) initializer: vblk->profile is embedded in + * struct virtio_blk, whose lifetime is tied to the gendisk, not to + * &vdev->dev. Tying destruction to the vdev via devm would run the + * destroy callback after virtblk_remove() has already freed vblk. + * virtblk_free_disk() calls blk_crypto_profile_destroy() explicitly + * instead, guarded by crypto_profile_initialized below. + */ + err =3D blk_crypto_profile_init(&vblk->profile, max_slots); + if (err) { + dev_err(&vdev->dev, "crypto profile initialization failed: %d\n", err); + return err; + } + + vblk->profile.ll_ops =3D virtblk_crypto_ops; + vblk->profile.max_dun_bytes_supported =3D max_dun_bytes; + vblk->profile.key_types_supported =3D key_type_supported; + vblk->profile.dev =3D &vdev->dev; + memcpy(vblk->profile.modes_supported, crypto_modes_supported, + BLK_ENCRYPTION_MODE_MAX * sizeof(unsigned int)); + + vblk->crypto_profile_initialized =3D true; + + dev_info(&vdev->dev, "inline crypto profile initialized\n"); + + return 0; +} + +static void virtblk_destroy_crypto(struct virtio_blk *vblk) +{ + if (vblk->crypto_profile_initialized) + blk_crypto_profile_destroy(&vblk->profile); +} +#else + +static inline int virtblk_init_crypto(struct virtio_blk *vblk) +{ + return -EOPNOTSUPP; +} + +static inline void virtblk_destroy_crypto(struct virtio_blk *vblk) +{ +} +#endif /* CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION */ + static void virtblk_free_disk(struct gendisk *disk) { struct virtio_blk *vblk =3D disk->private_data; =20 ida_free(&vd_index_ida, vblk->index); + virtblk_destroy_crypto(vblk); mutex_destroy(&vblk->ctrl_vq.mutex); mutex_destroy(&vblk->vdev_mutex); kfree(vblk); @@ -1661,6 +2274,7 @@ static int virtblk_probe(struct virtio_device *vdev) }; int err, index; unsigned int queue_depth; + bool zoned_disk =3D false; =20 if (!vdev->config->get) { dev_err(&vdev->dev, "%s failure: config access disabled\n", @@ -1684,6 +2298,7 @@ static int virtblk_probe(struct virtio_device *vdev) mutex_init(&vblk->ctrl_vq.mutex); spin_lock_init(&vblk->ctrl_vq.lock); =20 + vblk->crypto_profile_initialized =3D false; vblk->vdev =3D vdev; =20 INIT_WORK(&vblk->config_work, virtblk_config_changed_work); @@ -1759,6 +2374,28 @@ static int virtblk_probe(struct virtio_device *vdev) err =3D blk_revalidate_disk_zones(vblk->disk); if (err) goto out_cleanup_disk; + + zoned_disk =3D true; + } + + if (IS_ENABLED(CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION) && + virtio_has_feature(vdev, VIRTIO_BLK_F_INLINE_ENCRYPTION) && + virtio_has_feature(vdev, VIRTIO_BLK_F_CTRL_VQ)) { + if (zoned_disk) { + dev_info(&vdev->dev, + "inline crypto not supported on zoned device\n"); + } else { + err =3D virtblk_init_crypto(vblk); + if (!err) { + if (!blk_crypto_register(&vblk->profile, vblk->disk->queue)) + dev_warn(&vdev->dev, + "failed to register inline crypto profile\n"); + } else { + dev_warn(&vdev->dev, + "inline crypto init failed: %d, continuing without inline crypto supp= ort\n", + err); + } + } } =20 err =3D device_add_disk(&vdev->dev, vblk->disk, virtblk_attr_groups); @@ -1858,6 +2495,11 @@ static int virtblk_restore_priv(struct virtio_device= *vdev) return ret; =20 virtio_device_ready(vdev); + + /* Reprogram the keys to keyslots. */ + if (vblk->crypto_profile_initialized) + blk_crypto_reprogram_all_keys(&vblk->profile); + blk_mq_unquiesce_queue(vblk->disk->queue); =20 return 0; @@ -1904,7 +2546,7 @@ static unsigned int features[] =3D { VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_TOPOLOGY, VIRTIO_BLK_F_CONFIG_WCE, VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_DISCARD, VIRTIO_BLK_F_WRITE_ZEROES, VIRTIO_BLK_F_SECURE_ERASE, VIRTIO_BLK_F_ZONED, - VIRTIO_BLK_F_CTRL_VQ, + VIRTIO_BLK_F_CTRL_VQ, VIRTIO_BLK_F_INLINE_ENCRYPTION, }; =20 static struct virtio_driver virtio_blk =3D { diff --git a/include/linux/virtio_blk.h b/include/linux/virtio_blk.h new file mode 100644 index 000000000000..9f5aecad1907 --- /dev/null +++ b/include/linux/virtio_blk.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_VIRTIO_BLK_H +#define _LINUX_VIRTIO_BLK_H + +#include +#include + +#if IS_ENABLED(CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION) +/** + * virtio_mode_to_blk() - Convert a virtio_blk crypto mode number to a blo= ck mode + * @vmode: The virtio_blk crypto mode number (VIRTIO_BLK_CRYPTO_MODE_*). + * + * Return: The corresponding &enum blk_crypto_mode_num, or + * %BLK_ENCRYPTION_MODE_INVALID if @vmode is out of range. + */ +static inline enum blk_crypto_mode_num virtio_mode_to_blk(unsigned int vmo= de) +{ + /* Indexed by virtio_blk crypto mode number; unlisted entries are 0 (INVA= LID). */ + static const enum blk_crypto_mode_num modes[__VIRTIO_BLK_CRYPTO_MODE_MAX]= =3D { + [VIRTIO_BLK_CRYPTO_MODE_AES_256_XTS] =3D BLK_ENCRYPTION_MODE_AES_256_XTS, + }; + + if (vmode >=3D __VIRTIO_BLK_CRYPTO_MODE_MAX) + return BLK_ENCRYPTION_MODE_INVALID; + return modes[vmode]; +} + +/** + * blk_mode_to_virtio() - Convert a block crypto mode to a virtio_blk cryp= to mode number + * @bmode: The kernel &enum blk_crypto_mode_num. + * + * Return: The corresponding virtio_blk crypto mode number, or + * %VIRTIO_BLK_CRYPTO_MODE_INVALID if @bmode is out of range. + */ +static inline unsigned int blk_mode_to_virtio(enum blk_crypto_mode_num bmo= de) +{ + /* Indexed by blk_crypto_mode_num; unlisted entries are 0 (INVALID). */ + static const unsigned int modes[BLK_ENCRYPTION_MODE_MAX] =3D { + [BLK_ENCRYPTION_MODE_AES_256_XTS] =3D VIRTIO_BLK_CRYPTO_MODE_AES_256_XTS, + }; + + if (bmode >=3D BLK_ENCRYPTION_MODE_MAX) + return VIRTIO_BLK_CRYPTO_MODE_INVALID; + return modes[bmode]; +} + +/** + * virtio_key_type_to_blk() - Convert a virtio_blk crypto key type to a bl= ock key type + * @vtype: The virtio_blk crypto key type (VIRTIO_BLK_CRYPTO_KEY_TYPE_*). + * + * Return: The corresponding &enum blk_crypto_key_type, or 0 if @vtype does + * not name a single supported key type. + */ +static inline enum blk_crypto_key_type virtio_key_type_to_blk(unsigned int= vtype) +{ + switch (vtype) { + case VIRTIO_BLK_CRYPTO_KEY_TYPE_RAW: + return BLK_CRYPTO_KEY_TYPE_RAW; + case VIRTIO_BLK_CRYPTO_KEY_TYPE_HW_WRAPPED: + return BLK_CRYPTO_KEY_TYPE_HW_WRAPPED; + default: + return 0; + } +} + +/** + * blk_key_type_to_virtio() - Convert a block key type to a virtio_blk cry= pto key type + * @btype: The kernel &enum blk_crypto_key_type. + * + * Return: The corresponding virtio_blk crypto key type + * (VIRTIO_BLK_CRYPTO_KEY_TYPE_*), or 0 if @btype does not name a + * single supported key type. + */ +static inline unsigned int blk_key_type_to_virtio(enum blk_crypto_key_type= btype) +{ + switch (btype) { + case BLK_CRYPTO_KEY_TYPE_RAW: + return VIRTIO_BLK_CRYPTO_KEY_TYPE_RAW; + case BLK_CRYPTO_KEY_TYPE_HW_WRAPPED: + return VIRTIO_BLK_CRYPTO_KEY_TYPE_HW_WRAPPED; + default: + return 0; + } +} +#endif /* CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION */ + +#endif /* _LINUX_VIRTIO_BLK_H */ diff --git a/include/uapi/linux/virtio_blk.h b/include/uapi/linux/virtio_bl= k.h index 0a16972a1535..fc57407ff2c7 100644 --- a/include/uapi/linux/virtio_blk.h +++ b/include/uapi/linux/virtio_blk.h @@ -1,5 +1,5 @@ -#ifndef _LINUX_VIRTIO_BLK_H -#define _LINUX_VIRTIO_BLK_H +#ifndef _UAPI_LINUX_VIRTIO_BLK_H +#define _UAPI_LINUX_VIRTIO_BLK_H /* This header is BSD licensed so anyone can use the definitions to implem= ent * compatible drivers/servers. * @@ -43,6 +43,7 @@ #define VIRTIO_BLK_F_SECURE_ERASE 16 /* Secure Erase is supported */ #define VIRTIO_BLK_F_ZONED 17 /* Zoned block device */ #define VIRTIO_BLK_F_CTRL_VQ 22 /* Control queue */ +#define VIRTIO_BLK_F_INLINE_ENCRYPTION 23 /* Inline encryption */ =20 /* Legacy feature bits */ #ifndef VIRTIO_BLK_NO_LEGACY @@ -149,6 +150,15 @@ struct virtio_blk_config { __u8 model; __u8 unused2[3]; } zoned; + + /* Inline Encryption device characteristics (if VIRTIO_BLK_F_INLINE_ENCRY= PTION) */ + struct virtio_blk_enc_characteristics { + __virtio16 max_slots; + __u8 max_dun_bytes; + /* Bitmask of supported key types: VIRTIO_BLK_CRYPTO_KEY_TYPE_* */ + __u8 key_types; + __virtio32 unused3; + } enc_characteristics; } __attribute__((packed)); =20 /* @@ -207,6 +217,33 @@ struct virtio_blk_config { /* Reset All zones command */ #define VIRTIO_BLK_T_ZONE_RESET_ALL 26 =20 +/* Inline-encrypted write: crypto_msg set in outhdr */ +#define VIRTIO_BLK_T_CRYPTO_OUT 27 + +/* Inline-encrypted read: crypto_msg set in outhdr */ +#define VIRTIO_BLK_T_CRYPTO_IN 28 + +/* Get inline crypto modes */ +#define VIRTIO_BLK_T_GET_CRYPTO_MODES 29 + +/* Program a key into the keyslot */ +#define VIRTIO_BLK_T_CRYPTO_KEYSLOT_PROGRAM 30 + +/* Evict a key */ +#define VIRTIO_BLK_T_CRYPTO_KEYSLOT_EVICT 31 + +/* Derive the software secret from a hardware-wrapped key */ +#define VIRTIO_BLK_T_CRYPTO_DERIVE_SW_SECRET 32 + +/* Generate a new hardware-wrapped key */ +#define VIRTIO_BLK_T_CRYPTO_GENERATE_KEY 33 + +/* Import a raw key as a hardware-wrapped key */ +#define VIRTIO_BLK_T_CRYPTO_IMPORT_KEY 34 + +/* Convert a long-term wrapped key to its ephemerally-wrapped form */ +#define VIRTIO_BLK_T_CRYPTO_PREPARE_KEY 35 + #ifndef VIRTIO_BLK_NO_LEGACY /* Barrier before this op. */ #define VIRTIO_BLK_T_BARRIER 0x80000000 @@ -226,6 +263,86 @@ struct virtio_blk_outhdr { __virtio64 sector; }; =20 +/* + * Crypto message descriptor, appended to the outhdr of a + * VIRTIO_BLK_T_CRYPTO_OUT or VIRTIO_BLK_T_CRYPTO_IN request. + */ +struct virtio_blk_crypto_msg { + /* virtual key slot index */ + __virtio32 slot; + __u8 unused[4]; + /* data unit number (DUN / IV) for this request */ + __virtio64 dun[4]; +}; + +/* Key type for VIRTIO_BLK_F_INLINE_ENCRYPTION. */ +enum virtio_blk_crypto_key_type { + VIRTIO_BLK_CRYPTO_KEY_TYPE_RAW =3D 1, + VIRTIO_BLK_CRYPTO_KEY_TYPE_HW_WRAPPED, +}; + +/* + * Inline crypto key descriptor. Request part for + * VIRTIO_BLK_T_CRYPTO_KEYSLOT_PROGRAM/KEYSLOT_EVICT. + */ +/* Must be >=3D BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE in include/linux/blk-cr= ypto.h */ +#define VIRTIO_BLK_CRYPTO_MAX_KEY_SIZE 128 + +struct virtio_blk_crypto_key_desc { + __virtio32 slot; + __u8 bytes[VIRTIO_BLK_CRYPTO_MAX_KEY_SIZE]; + __virtio32 key_size; + __virtio32 crypto_mode; + __virtio32 key_type; + __virtio32 data_unit_size_bits; + __virtio32 dun_bytes; +}; + +/* + * A raw or hardware-wrapped key blob, used as the request and/or reply pa= rt + * of the VIRTIO_BLK_T_CRYPTO_GENERATE_KEY/IMPORT_KEY/PREPARE_KEY/ + * DERIVE_SW_SECRET commands. + */ +struct virtio_blk_crypto_key_blob { + __virtio32 key_size; + __u8 key[VIRTIO_BLK_CRYPTO_MAX_KEY_SIZE]; +}; + +/* Must match BLK_CRYPTO_SW_SECRET_SIZE in include/linux/blk-crypto.h */ +#define VIRTIO_BLK_CRYPTO_SW_SECRET_SIZE 32 + +/* Reply to a VIRTIO_BLK_T_CRYPTO_DERIVE_SW_SECRET request. */ +struct virtio_blk_crypto_sw_secret { + __u8 secret[VIRTIO_BLK_CRYPTO_SW_SECRET_SIZE]; +}; + +/* + * Crypto mode numbers used in VIRTIO_BLK_T_GET_CRYPTO_MODES replies, in + * struct virtio_blk_crypto_key_desc.crypto_mode and in indexing struct + * virtio_blk_crypto_modes.modes[] below. These numbers are assigned by + * the virtio spec and are stable: a number is never reused for a different + * crypto mode, and additional crypto modes are assigned new, higher numbe= rs. + */ +enum { + VIRTIO_BLK_CRYPTO_MODE_INVALID, + VIRTIO_BLK_CRYPTO_MODE_AES_256_XTS, + __VIRTIO_BLK_CRYPTO_MODE_MAX, /* sentinel: always one past the last real = mode */ +}; + +/* Highest crypto mode number defined by this version of the header. */ +#define VIRTIO_BLK_CRYPTO_MODE_MAX (__VIRTIO_BLK_CRYPTO_MODE_MAX - 1) + +/* Reply to a VIRTIO_BLK_T_GET_CRYPTO_MODES request. */ +struct virtio_blk_crypto_modes { + /* + * modes[N], for crypto mode number N <=3D VIRTIO_BLK_CRYPTO_MODE_MAX, is + * a bitmask of the data unit sizes with which crypto mode N can be + * used: bit i is set if a data unit size of (1 << i) bytes is + * supported. modes[0] is reserved and always 0. + */ + __virtio32 modes[__VIRTIO_BLK_CRYPTO_MODE_MAX]; +}; + /* * Supported zoned device models. */ @@ -325,4 +442,4 @@ struct virtio_scsi_inhdr { #define VIRTIO_BLK_S_ZONE_OPEN_RESOURCE 5 #define VIRTIO_BLK_S_ZONE_ACTIVE_RESOURCE 6 =20 -#endif /* _LINUX_VIRTIO_BLK_H */ +#endif /* _UAPI_LINUX_VIRTIO_BLK_H */ --=20 2.34.1