block/ioctl.c

Source file repositories/reference/linux-study-clean/block/ioctl.c

File Facts

System
Linux kernel
Corpus path
block/ioctl.c
Extension
.c
Size
24632 bytes
Lines
975
Domain
Representative Device Path
Bucket
PCIe NVMe Storage Path
Inferred role
Representative Device Path: exported/initcall integration point
Status
integration implementation candidate

Why This File Exists

Part of the selected hardware vertical slice: PCI discovery, driver binding, NVMe queues, block requests, DMA, interrupts, and completion.

Dependency Surface

Detected Declarations

Annotated Snippet

struct compat_blkpg_ioctl_arg {
	compat_int_t op;
	compat_int_t flags;
	compat_int_t datalen;
	compat_caddr_t data;
};

static int compat_blkpg_ioctl(struct block_device *bdev,
			      struct compat_blkpg_ioctl_arg __user *arg)
{
	compat_caddr_t udata;
	int op;

	if (get_user(op, &arg->op) || get_user(udata, &arg->data))
		return -EFAULT;

	return blkpg_do_ioctl(bdev, compat_ptr(udata), op);
}
#endif

/*
 * Check that [start, start + len) is a valid range from the block device's
 * perspective, including verifying that it can be correctly translated into
 * logical block addresses.
 */
static int blk_validate_byte_range(struct block_device *bdev,
				   uint64_t start, uint64_t len)
{
	unsigned int bs_mask = bdev_logical_block_size(bdev) - 1;
	uint64_t end;

	if ((start | len) & bs_mask)
		return -EINVAL;
	if (!len)
		return -EINVAL;
	if (check_add_overflow(start, len, &end) || end > bdev_nr_bytes(bdev))
		return -EINVAL;

	return 0;
}

static int blk_ioctl_discard(struct block_device *bdev, blk_mode_t mode,
		unsigned long arg)
{
	uint64_t range[2], start, len;
	struct bio *prev = NULL, *bio;
	sector_t sector, nr_sects;
	struct blk_plug plug;
	int err;

	if (copy_from_user(range, (void __user *)arg, sizeof(range)))
		return -EFAULT;
	start = range[0];
	len = range[1];

	if (!bdev_max_discard_sectors(bdev))
		return -EOPNOTSUPP;

	if (!(mode & BLK_OPEN_WRITE))
		return -EBADF;
	if (bdev_read_only(bdev))
		return -EPERM;
	err = blk_validate_byte_range(bdev, start, len);
	if (err)
		return err;

	inode_lock(bdev->bd_mapping->host);
	filemap_invalidate_lock(bdev->bd_mapping);
	err = truncate_bdev_range(bdev, mode, start, start + len - 1);
	if (err)
		goto fail;

	sector = start >> SECTOR_SHIFT;
	nr_sects = len >> SECTOR_SHIFT;

	blk_start_plug(&plug);
	while (!fatal_signal_pending(current)) {
		bio = blk_alloc_discard_bio(bdev, &sector, &nr_sects,
				GFP_KERNEL);
		if (!bio)
			break;
		prev = bio_chain_and_submit(prev, bio);
	}
	if (prev) {
		err = bio_submit_or_kill(prev, BLKDEV_ZERO_KILLABLE);
		if (err == -EOPNOTSUPP)
			err = 0;
		bio_put(prev);
	}
	blk_finish_plug(&plug);

Annotation

Implementation Notes