mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-05-20 14:35:06 +02:00
203247c5cb
A bio segment may have partial interval block data with the rest continuing into the next segments because direct-io data payloads only need to align in memory to the device's DMA limits. At the same time, the protection information may also be split in multiple segments. The most likely way that may happen is if two requests merge, or if we're directly using the io_uring user metadata. The generate/verify, however, only ever accessed the first bip_vec. Further, it may be possible to unalign the protection fields from the user space buffer, or if there are odd additional opaque bytes in front or in back of the protection information metadata region. Change up the iteration to allow spanning multiple segments. This patch is mostly a re-write of the protection information handling to allow any arbitrary alignments, so it's probably easier to review the end result rather than the diff. Many controllers are not able to handle interval data composed of multiple segments when PI is used, so this patch introduces a new integrity limit that a low level driver can set to notify that it is capable, default to false. The nvme driver is the first one to enable it in this patch. Everyone else will force DMA alignment to the logical block size as before to ensure interval data is always aligned within a single segment. Reviewed-by: Martin K. Petersen <martin.petersen@oracle.com> Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Keith Busch <kbusch@kernel.org> Link: https://patch.msgid.link/20260313144701.1221652-2-kbusch@meta.com Signed-off-by: Jens Axboe <axboe@kernel.dk>
203 lines
5.6 KiB
C
203 lines
5.6 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
#ifndef _LINUX_BLK_INTEGRITY_H
|
|
#define _LINUX_BLK_INTEGRITY_H
|
|
|
|
#include <linux/blk-mq.h>
|
|
#include <linux/bio-integrity.h>
|
|
#include <linux/blk-mq-dma.h>
|
|
|
|
struct request;
|
|
|
|
enum blk_integrity_flags {
|
|
BLK_INTEGRITY_NOVERIFY = 1 << 0,
|
|
BLK_INTEGRITY_NOGENERATE = 1 << 1,
|
|
BLK_INTEGRITY_DEVICE_CAPABLE = 1 << 2,
|
|
BLK_INTEGRITY_REF_TAG = 1 << 3,
|
|
BLK_INTEGRITY_STACKED = 1 << 4,
|
|
BLK_SPLIT_INTERVAL_CAPABLE = 1 << 5,
|
|
};
|
|
|
|
const char *blk_integrity_profile_name(struct blk_integrity *bi);
|
|
bool queue_limits_stack_integrity(struct queue_limits *t,
|
|
struct queue_limits *b);
|
|
static inline bool queue_limits_stack_integrity_bdev(struct queue_limits *t,
|
|
struct block_device *bdev)
|
|
{
|
|
return queue_limits_stack_integrity(t, &bdev->bd_disk->queue->limits);
|
|
}
|
|
|
|
#ifdef CONFIG_BLK_DEV_INTEGRITY
|
|
int blk_rq_map_integrity_sg(struct request *, struct scatterlist *);
|
|
|
|
int blk_rq_count_integrity_sg(struct request_queue *, struct bio *);
|
|
int blk_rq_integrity_map_user(struct request *rq, void __user *ubuf,
|
|
ssize_t bytes);
|
|
int blk_get_meta_cap(struct block_device *bdev, unsigned int cmd,
|
|
struct logical_block_metadata_cap __user *argp);
|
|
bool blk_rq_integrity_dma_map_iter_start(struct request *req,
|
|
struct device *dma_dev, struct dma_iova_state *state,
|
|
struct blk_dma_iter *iter);
|
|
bool blk_rq_integrity_dma_map_iter_next(struct request *req,
|
|
struct device *dma_dev, struct blk_dma_iter *iter);
|
|
|
|
static inline bool
|
|
blk_integrity_queue_supports_integrity(struct request_queue *q)
|
|
{
|
|
return q->limits.integrity.metadata_size;
|
|
}
|
|
|
|
static inline struct blk_integrity *blk_get_integrity(struct gendisk *disk)
|
|
{
|
|
if (!blk_integrity_queue_supports_integrity(disk->queue))
|
|
return NULL;
|
|
return &disk->queue->limits.integrity;
|
|
}
|
|
|
|
static inline struct blk_integrity *
|
|
bdev_get_integrity(struct block_device *bdev)
|
|
{
|
|
return blk_get_integrity(bdev->bd_disk);
|
|
}
|
|
|
|
static inline unsigned short
|
|
queue_max_integrity_segments(const struct request_queue *q)
|
|
{
|
|
return q->limits.max_integrity_segments;
|
|
}
|
|
|
|
/**
|
|
* bio_integrity_intervals - Return number of integrity intervals for a bio
|
|
* @bi: blk_integrity profile for device
|
|
* @sectors: Size of the bio in 512-byte sectors
|
|
*
|
|
* Description: The block layer calculates everything in 512 byte
|
|
* sectors but integrity metadata is done in terms of the data integrity
|
|
* interval size of the storage device. Convert the block layer sectors
|
|
* to the appropriate number of integrity intervals.
|
|
*/
|
|
static inline unsigned int bio_integrity_intervals(struct blk_integrity *bi,
|
|
unsigned int sectors)
|
|
{
|
|
return sectors >> (bi->interval_exp - 9);
|
|
}
|
|
|
|
static inline unsigned int bio_integrity_bytes(struct blk_integrity *bi,
|
|
unsigned int sectors)
|
|
{
|
|
return bio_integrity_intervals(bi, sectors) * bi->metadata_size;
|
|
}
|
|
|
|
static inline bool blk_integrity_rq(const struct request *rq)
|
|
{
|
|
return rq->cmd_flags & REQ_INTEGRITY;
|
|
}
|
|
|
|
/*
|
|
* Return the current bvec that contains the integrity data. bip_iter may be
|
|
* advanced to iterate over the integrity data.
|
|
*/
|
|
static inline struct bio_vec rq_integrity_vec(struct request *rq)
|
|
{
|
|
return mp_bvec_iter_bvec(rq->bio->bi_integrity->bip_vec,
|
|
rq->bio->bi_integrity->bip_iter);
|
|
}
|
|
#else /* CONFIG_BLK_DEV_INTEGRITY */
|
|
static inline int blk_get_meta_cap(struct block_device *bdev, unsigned int cmd,
|
|
struct logical_block_metadata_cap __user *argp)
|
|
{
|
|
return -ENOIOCTLCMD;
|
|
}
|
|
static inline int blk_rq_count_integrity_sg(struct request_queue *q,
|
|
struct bio *b)
|
|
{
|
|
return 0;
|
|
}
|
|
static inline int blk_rq_map_integrity_sg(struct request *q,
|
|
struct scatterlist *s)
|
|
{
|
|
return 0;
|
|
}
|
|
static inline int blk_rq_integrity_map_user(struct request *rq,
|
|
void __user *ubuf,
|
|
ssize_t bytes)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
static inline bool blk_rq_integrity_dma_map_iter_start(struct request *req,
|
|
struct device *dma_dev, struct dma_iova_state *state,
|
|
struct blk_dma_iter *iter)
|
|
{
|
|
return false;
|
|
}
|
|
static inline bool blk_rq_integrity_dma_map_iter_next(struct request *req,
|
|
struct device *dma_dev, struct blk_dma_iter *iter)
|
|
{
|
|
return false;
|
|
}
|
|
static inline struct blk_integrity *bdev_get_integrity(struct block_device *b)
|
|
{
|
|
return NULL;
|
|
}
|
|
static inline struct blk_integrity *blk_get_integrity(struct gendisk *disk)
|
|
{
|
|
return NULL;
|
|
}
|
|
static inline bool
|
|
blk_integrity_queue_supports_integrity(struct request_queue *q)
|
|
{
|
|
return false;
|
|
}
|
|
static inline unsigned short
|
|
queue_max_integrity_segments(const struct request_queue *q)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static inline unsigned int bio_integrity_intervals(struct blk_integrity *bi,
|
|
unsigned int sectors)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static inline unsigned int bio_integrity_bytes(struct blk_integrity *bi,
|
|
unsigned int sectors)
|
|
{
|
|
return 0;
|
|
}
|
|
static inline bool blk_integrity_rq(const struct request *rq)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
static inline struct bio_vec rq_integrity_vec(struct request *rq)
|
|
{
|
|
/* the optimizer will remove all calls to this function */
|
|
return (struct bio_vec){ };
|
|
}
|
|
#endif /* CONFIG_BLK_DEV_INTEGRITY */
|
|
|
|
enum bio_integrity_action {
|
|
BI_ACT_BUFFER = (1u << 0), /* allocate buffer */
|
|
BI_ACT_CHECK = (1u << 1), /* generate / verify PI */
|
|
BI_ACT_ZERO = (1u << 2), /* zero buffer */
|
|
};
|
|
|
|
/**
|
|
* bio_integrity_action - return the integrity action needed for a bio
|
|
* @bio: bio to operate on
|
|
*
|
|
* Returns the mask of integrity actions (BI_ACT_*) that need to be performed
|
|
* for @bio.
|
|
*/
|
|
unsigned int __bio_integrity_action(struct bio *bio);
|
|
static inline unsigned int bio_integrity_action(struct bio *bio)
|
|
{
|
|
if (!blk_get_integrity(bio->bi_bdev->bd_disk))
|
|
return 0;
|
|
if (bio_integrity(bio))
|
|
return 0;
|
|
return __bio_integrity_action(bio);
|
|
}
|
|
|
|
#endif /* _LINUX_BLK_INTEGRITY_H */
|