diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-19 10:09:22 -0700 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2026-08-19 10:09:22 -0700 |
| commit | f4cdf7ca9a1fdcca413157df19753f388a5a224e (patch) | |
| tree | 60a31895b2446cd7fa27514e8c2843534a0de0da /block/blk-integrity.c | |
| download | linux-stable-f4cdf7ca9a1fdcca413157df19753f388a5a224e.tar.gz linux-stable-f4cdf7ca9a1fdcca413157df19753f388a5a224e.zip | |
Merge tag 'media/v7.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-mediagrafted
Pull media updates from Mauro Carvalho Chehab:
- v4l2-core: added ISP statistics support and per-block validation
- v4l2-core: Allow unknown HDR10 white point and luminance
- New camera sensors: Sony IMX678 and IMX471m, Himax HM1092 IR sensor
- New codec: Milos: VPU v2.0 codec support
- isp driver: gained support for Dreamchip RPPX1 ISP framework
- vsp1 driver: gained support for RZ/T2H and RZ/N2H
- Novalake driver: gained CVS support for new NVL hardware
- dvb-core: fix feed leak on failed DMX_ADD_PID
- several driver fixes, cleanups and minor improvements
* tag 'media/v7.3-1' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media: (308 commits)
media: ipu-bridge: check all DMI entries when overriding sensor rotation
media: v4l2-async: avoid deleting unlinked ASC entry on link error
media: rzg2l-cru: Align bytesperline to hardware DMA stride requirement
media: intel/ipu6: fix async notifier cleanup leak on parse error
media: staging/ipu7: fix async notifier UAF on probe error path
media: amd: isp4: fix self-deadlock in isp4sd_pwron_and_init() error path
media: amd: isp4: release partial allocations in isp4if_alloc_fw_gpumem()
media: rcar-isp: Fix VSPX reference leaks
media: rcar-isp: Release ISPCORE resources
media: i2c: imx415: Release runtime PM reference on VBLANK error
media: i2c: imx415: Return test pattern write errors
media: renesas: vsp1: Declare index variables in for loop statement
media: renesas: vsp1: Make reset control optional to support platforms without a reset line
media: dt-bindings: media: renesas,vsp1: Document RZ/T2H and RZ/N2H SoCs
media: dt-bindings: media: renesas,fcp: Document RZ/T2H and RZ/N2H SoCs
media: nxp: imx8-isi: Add additional 32-bit RGB format support
media: nxp: imx8-isi: Add 16-bit raw Bayer format support
media: nxp: imx8-isi: Implement per-stream reference counting for multiplexed streams
media: nxp: imx8-isi: Use BIT_ULL() for 64-bit stream masks
media: nxp: imx8-isi: Correct color map between V4L2 and ISI
...
Diffstat (limited to 'block/blk-integrity.c')
| -rw-r--r-- | block/blk-integrity.c | 342 |
1 files changed, 342 insertions, 0 deletions
diff --git a/block/blk-integrity.c b/block/blk-integrity.c new file mode 100644 index 000000000..964eebbee --- /dev/null +++ b/block/blk-integrity.c @@ -0,0 +1,342 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * blk-integrity.c - Block layer data integrity extensions + * + * Copyright (C) 2007, 2008 Oracle Corporation + * Written by: Martin K. Petersen <martin.petersen@oracle.com> + */ + +#include <linux/blk-integrity.h> +#include <linux/backing-dev.h> +#include <linux/mempool.h> +#include <linux/bio.h> +#include <linux/scatterlist.h> +#include <linux/export.h> +#include <linux/slab.h> +#include <linux/t10-pi.h> + +#include "blk.h" + +/** + * blk_rq_count_integrity_sg - Count number of integrity scatterlist elements + * @q: request queue + * @bio: bio with integrity metadata attached + * + * Description: Returns the number of elements required in a + * scatterlist corresponding to the integrity metadata in a bio. + */ +int blk_rq_count_integrity_sg(struct request_queue *q, struct bio *bio) +{ + struct bio_vec iv, ivprv = { NULL }; + unsigned int segments = 0; + unsigned int seg_size = 0; + struct bvec_iter iter; + int prev = 0; + + bio_for_each_integrity_vec(iv, bio, iter) { + + if (prev) { + if (!biovec_phys_mergeable(q, &ivprv, &iv)) + goto new_segment; + if (seg_size + iv.bv_len > queue_max_segment_size(q)) + goto new_segment; + + seg_size += iv.bv_len; + } else { +new_segment: + segments++; + seg_size = iv.bv_len; + } + + prev = 1; + ivprv = iv; + } + + return segments; +} + +int blk_get_meta_cap(struct block_device *bdev, unsigned int cmd, + struct logical_block_metadata_cap __user *argp) +{ + struct blk_integrity *bi; + struct logical_block_metadata_cap meta_cap = {}; + size_t usize = _IOC_SIZE(cmd); + + if (!extensible_ioctl_valid(cmd, FS_IOC_GETLBMD_CAP, LBMD_SIZE_VER0)) + return -ENOIOCTLCMD; + + bi = blk_get_integrity(bdev->bd_disk); + if (!bi) + goto out; + + if (bi->flags & BLK_INTEGRITY_DEVICE_CAPABLE) + meta_cap.lbmd_flags |= LBMD_PI_CAP_INTEGRITY; + if (bi->flags & BLK_INTEGRITY_REF_TAG) + meta_cap.lbmd_flags |= LBMD_PI_CAP_REFTAG; + meta_cap.lbmd_interval = 1 << bi->interval_exp; + meta_cap.lbmd_size = bi->metadata_size; + meta_cap.lbmd_pi_size = bi->pi_tuple_size; + meta_cap.lbmd_pi_offset = bi->pi_offset; + meta_cap.lbmd_opaque_size = bi->metadata_size - bi->pi_tuple_size; + if (meta_cap.lbmd_opaque_size && !bi->pi_offset) + meta_cap.lbmd_opaque_offset = bi->pi_tuple_size; + + switch (bi->csum_type) { + case BLK_INTEGRITY_CSUM_NONE: + meta_cap.lbmd_guard_tag_type = LBMD_PI_CSUM_NONE; + break; + case BLK_INTEGRITY_CSUM_IP: + meta_cap.lbmd_guard_tag_type = LBMD_PI_CSUM_IP; + break; + case BLK_INTEGRITY_CSUM_CRC: + meta_cap.lbmd_guard_tag_type = LBMD_PI_CSUM_CRC16_T10DIF; + break; + case BLK_INTEGRITY_CSUM_CRC64: + meta_cap.lbmd_guard_tag_type = LBMD_PI_CSUM_CRC64_NVME; + break; + } + + if (bi->csum_type != BLK_INTEGRITY_CSUM_NONE) + meta_cap.lbmd_app_tag_size = 2; + + if (bi->flags & BLK_INTEGRITY_REF_TAG) { + switch (bi->csum_type) { + case BLK_INTEGRITY_CSUM_CRC64: + meta_cap.lbmd_ref_tag_size = + sizeof_field(struct crc64_pi_tuple, ref_tag); + break; + case BLK_INTEGRITY_CSUM_CRC: + case BLK_INTEGRITY_CSUM_IP: + meta_cap.lbmd_ref_tag_size = + sizeof_field(struct t10_pi_tuple, ref_tag); + break; + default: + break; + } + } + +out: + return copy_struct_to_user(argp, usize, &meta_cap, sizeof(meta_cap), + NULL); +} + +int blk_rq_integrity_map_user(struct request *rq, void __user *ubuf, + ssize_t bytes) +{ + int ret; + struct iov_iter iter; + + iov_iter_ubuf(&iter, rq_data_dir(rq), ubuf, bytes); + ret = bio_integrity_map_user(rq->bio, &iter); + if (ret) + return ret; + + rq->nr_integrity_segments = blk_rq_count_integrity_sg(rq->q, rq->bio); + rq->cmd_flags |= REQ_INTEGRITY; + return 0; +} +EXPORT_SYMBOL_GPL(blk_rq_integrity_map_user); + +bool blk_integrity_merge_rq(struct request_queue *q, struct request *req, + struct request *next) +{ + struct bio_integrity_payload *bip, *bip_next; + + if (blk_integrity_rq(req) == 0 && blk_integrity_rq(next) == 0) + return true; + + if (blk_integrity_rq(req) == 0 || blk_integrity_rq(next) == 0) + return false; + + bip = bio_integrity(req->bio); + bip_next = bio_integrity(next->bio); + if (bip->bip_flags != bip_next->bip_flags) + return false; + + if (bip->bip_flags & BIP_CHECK_APPTAG && + bip->app_tag != bip_next->app_tag) + return false; + + if (req->nr_integrity_segments + next->nr_integrity_segments > + q->limits.max_integrity_segments) + return false; + + if (integrity_req_gap_back_merge(req, next->bio)) + return false; + + return true; +} + +bool blk_integrity_merge_bio(struct request_queue *q, struct request *req, + struct bio *bio) +{ + struct bio_integrity_payload *bip, *bip_bio = bio_integrity(bio); + int nr_integrity_segs; + + if (blk_integrity_rq(req) == 0 && bip_bio == NULL) + return true; + + if (blk_integrity_rq(req) == 0 || bip_bio == NULL) + return false; + + bip = bio_integrity(req->bio); + if (bip->bip_flags != bip_bio->bip_flags) + return false; + + if (bip->bip_flags & BIP_CHECK_APPTAG && + bip->app_tag != bip_bio->app_tag) + return false; + + nr_integrity_segs = blk_rq_count_integrity_sg(q, bio); + if (req->nr_integrity_segments + nr_integrity_segs > + q->limits.max_integrity_segments) + return false; + + return true; +} + +static inline struct blk_integrity *dev_to_bi(struct device *dev) +{ + return &dev_to_disk(dev)->queue->limits.integrity; +} + +const char *blk_integrity_profile_name(struct blk_integrity *bi) +{ + switch (bi->csum_type) { + case BLK_INTEGRITY_CSUM_IP: + if (bi->flags & BLK_INTEGRITY_REF_TAG) + return "T10-DIF-TYPE1-IP"; + return "T10-DIF-TYPE3-IP"; + case BLK_INTEGRITY_CSUM_CRC: + if (bi->flags & BLK_INTEGRITY_REF_TAG) + return "T10-DIF-TYPE1-CRC"; + return "T10-DIF-TYPE3-CRC"; + case BLK_INTEGRITY_CSUM_CRC64: + if (bi->flags & BLK_INTEGRITY_REF_TAG) + return "EXT-DIF-TYPE1-CRC64"; + return "EXT-DIF-TYPE3-CRC64"; + case BLK_INTEGRITY_CSUM_NONE: + break; + } + + return "nop"; +} +EXPORT_SYMBOL_GPL(blk_integrity_profile_name); + +static ssize_t flag_store(struct device *dev, const char *page, size_t count, + unsigned char flag) +{ + struct request_queue *q = dev_to_disk(dev)->queue; + struct queue_limits lim; + unsigned long val; + int err; + + err = kstrtoul(page, 10, &val); + if (err) + return err; + + /* note that the flags are inverted vs the values in the sysfs files */ + lim = queue_limits_start_update(q); + if (val) + lim.integrity.flags &= ~flag; + else + lim.integrity.flags |= flag; + + err = queue_limits_commit_update_frozen(q, &lim); + if (err) + return err; + return count; +} + +static ssize_t flag_show(struct device *dev, char *page, unsigned char flag) +{ + struct blk_integrity *bi = dev_to_bi(dev); + + return sysfs_emit(page, "%d\n", !(bi->flags & flag)); +} + +static ssize_t format_show(struct device *dev, struct device_attribute *attr, + char *page) +{ + struct blk_integrity *bi = dev_to_bi(dev); + + if (!bi->metadata_size) + return sysfs_emit(page, "none\n"); + return sysfs_emit(page, "%s\n", blk_integrity_profile_name(bi)); +} + +static ssize_t tag_size_show(struct device *dev, struct device_attribute *attr, + char *page) +{ + struct blk_integrity *bi = dev_to_bi(dev); + + return sysfs_emit(page, "%u\n", bi->tag_size); +} + +static ssize_t protection_interval_bytes_show(struct device *dev, + struct device_attribute *attr, + char *page) +{ + struct blk_integrity *bi = dev_to_bi(dev); + + return sysfs_emit(page, "%u\n", + bi->interval_exp ? 1 << bi->interval_exp : 0); +} + +static ssize_t read_verify_store(struct device *dev, + struct device_attribute *attr, + const char *page, size_t count) +{ + return flag_store(dev, page, count, BLK_INTEGRITY_NOVERIFY); +} + +static ssize_t read_verify_show(struct device *dev, + struct device_attribute *attr, char *page) +{ + return flag_show(dev, page, BLK_INTEGRITY_NOVERIFY); +} + +static ssize_t write_generate_store(struct device *dev, + struct device_attribute *attr, + const char *page, size_t count) +{ + return flag_store(dev, page, count, BLK_INTEGRITY_NOGENERATE); +} + +static ssize_t write_generate_show(struct device *dev, + struct device_attribute *attr, char *page) +{ + return flag_show(dev, page, BLK_INTEGRITY_NOGENERATE); +} + +static ssize_t device_is_integrity_capable_show(struct device *dev, + struct device_attribute *attr, + char *page) +{ + struct blk_integrity *bi = dev_to_bi(dev); + + return sysfs_emit(page, "%u\n", + !!(bi->flags & BLK_INTEGRITY_DEVICE_CAPABLE)); +} + +static DEVICE_ATTR_RO(format); +static DEVICE_ATTR_RO(tag_size); +static DEVICE_ATTR_RO(protection_interval_bytes); +static DEVICE_ATTR_RW(read_verify); +static DEVICE_ATTR_RW(write_generate); +static DEVICE_ATTR_RO(device_is_integrity_capable); + +static struct attribute *integrity_attrs[] = { + &dev_attr_format.attr, + &dev_attr_tag_size.attr, + &dev_attr_protection_interval_bytes.attr, + &dev_attr_read_verify.attr, + &dev_attr_write_generate.attr, + &dev_attr_device_is_integrity_capable.attr, + NULL +}; + +const struct attribute_group blk_integrity_attr_group = { + .name = "integrity", + .attrs = integrity_attrs, +}; |
