drivers/md/dm-ebs-target.c

Source file repositories/reference/linux-study-clean/drivers/md/dm-ebs-target.c

File Facts

System
Linux kernel
Corpus path
drivers/md/dm-ebs-target.c
Extension
.c
Size
12530 bytes
Lines
468
Domain
Driver Families
Bucket
drivers/md
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct ebs_c {
	struct dm_dev *dev;		/* Underlying device to emulate block size on. */
	struct dm_bufio_client *bufio;	/* Use dm-bufio for read and read-modify-write processing. */
	struct workqueue_struct *wq;	/* Workqueue for ^ processing of bios. */
	struct work_struct ws;		/* Work item used for ^. */
	struct bio_list bios_in;	/* Worker bios input list. */
	spinlock_t lock;		/* Guard bios input list above. */
	sector_t start;			/* <start> table line argument, see ebs_ctr below. */
	unsigned int e_bs;		/* Emulated block size in sectors exposed to upper layer. */
	unsigned int u_bs;		/* Underlying block size in sectors retrieved from/set on lower layer device. */
	unsigned char block_shift;	/* bitshift sectors -> blocks used in dm-bufio API. */
	bool u_bs_set:1;		/* Flag to indicate underlying block size is set on table line. */
};

static inline sector_t __sector_to_block(struct ebs_c *ec, sector_t sector)
{
	return sector >> ec->block_shift;
}

static inline sector_t __block_mod(sector_t sector, unsigned int bs)
{
	return sector & (bs - 1);
}

/* Return number of blocks for a bio, accounting for misalignment of start and end sectors. */
static inline unsigned int __nr_blocks(struct ebs_c *ec, struct bio *bio)
{
	sector_t end_sector = __block_mod(bio->bi_iter.bi_sector, ec->u_bs) + bio_sectors(bio);

	return __sector_to_block(ec, end_sector) + (__block_mod(end_sector, ec->u_bs) ? 1 : 0);
}

static inline bool __ebs_check_bs(unsigned int bs)
{
	return bs && is_power_of_2(bs);
}

/*
 * READ/WRITE:
 *
 * copy blocks between bufio blocks and bio vector's (partial/overlapping) pages.
 */
static int __ebs_rw_bvec(struct ebs_c *ec, enum req_op op, struct bio_vec *bv,
			 struct bvec_iter *iter)
{
	int r = 0;
	unsigned char *ba, *pa;
	unsigned int cur_len;
	unsigned int bv_len = bv->bv_len;
	unsigned int buf_off = to_bytes(__block_mod(iter->bi_sector, ec->u_bs));
	sector_t block = __sector_to_block(ec, iter->bi_sector);
	struct dm_buffer *b;

	if (unlikely(!bv->bv_page || !bv_len))
		return -EIO;

	pa = bvec_virt(bv);

	/* Handle overlapping page <-> blocks */
	while (bv_len) {
		cur_len = min(dm_bufio_get_block_size(ec->bufio) - buf_off, bv_len);

		/* Avoid reading for writes in case bio vector's page overwrites block completely. */
		if (op == REQ_OP_READ || buf_off || bv_len < dm_bufio_get_block_size(ec->bufio))
			ba = dm_bufio_read(ec->bufio, block, &b);
		else
			ba = dm_bufio_new(ec->bufio, block, &b);

		if (IS_ERR(ba)) {
			/*
			 * Carry on with next buffer, if any, to issue all possible
			 * data but return error.
			 */
			r = PTR_ERR(ba);
		} else {
			/* Copy data to/from bio to buffer if read/new was successful above. */
			ba += buf_off;
			if (op == REQ_OP_READ) {
				memcpy(pa, ba, cur_len);
				flush_dcache_page(bv->bv_page);
			} else {
				flush_dcache_page(bv->bv_page);
				memcpy(ba, pa, cur_len);
				dm_bufio_mark_buffer_dirty(b);
			}

			dm_bufio_release(b);
		}

		pa += cur_len;

Annotation

Implementation Notes