Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ccf21b69 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge branch 'for-4.4/reservations' of git://git.kernel.dk/linux-block

Pull block reservation support from Jens Axboe:
 "This adds support for persistent reservations, both at the core level,
  as well as for sd and NVMe"

[ Background from the docs: "Persistent Reservations allow restricting
  access to block devices to specific initiators in a shared storage
  setup.  All implementations are expected to ensure the reservations
  survive a power loss and cover all connections in a multi path
  environment" ]

* 'for-4.4/reservations' of git://git.kernel.dk/linux-block:
  NVMe: Precedence error in nvme_pr_clear()
  nvme: add missing endianess annotations in nvme_pr_command
  NVMe: Add persistent reservation ops
  sd: implement the Persistent Reservation API
  block: add an API for Persistent Reservations
  block: cleanup blkdev_ioctl
parents 527d1529 73fcf4e2
Loading
Loading
Loading
Loading
+119 −0
Original line number Original line Diff line number Diff line

Block layer support for Persistent Reservations
===============================================

The Linux kernel supports a user space interface for simplified
Persistent Reservations which map to block devices that support
these (like SCSI). Persistent Reservations allow restricting
access to block devices to specific initiators in a shared storage
setup.

This document gives a general overview of the support ioctl commands.
For a more detailed reference please refer the the SCSI Primary
Commands standard, specifically the section on Reservations and the
"PERSISTENT RESERVE IN" and "PERSISTENT RESERVE OUT" commands.

All implementations are expected to ensure the reservations survive
a power loss and cover all connections in a multi path environment.
These behaviors are optional in SPC but will be automatically applied
by Linux.


The following types of reservations are supported:
--------------------------------------------------

 - PR_WRITE_EXCLUSIVE

	Only the initiator that owns the reservation can write to the
	device.  Any initiator can read from the device.

 - PR_EXCLUSIVE_ACCESS

	Only the initiator that owns the reservation can access the
	device.

 - PR_WRITE_EXCLUSIVE_REG_ONLY

	Only initiators with a registered key can write to the device,
	Any initiator can read from the device.

 - PR_EXCLUSIVE_ACCESS_REG_ONLY

	Only initiators with a registered key can access the device.

 - PR_WRITE_EXCLUSIVE_ALL_REGS

	Only initiators with a registered key can write to the device,
	Any initiator can read from the device.
	All initiators with a registered key are considered reservation
	holders.
	Please reference the SPC spec on the meaning of a reservation
	holder if you want to use this type. 

 - PR_EXCLUSIVE_ACCESS_ALL_REGS

	Only initiators with a registered key can access the device.
	All initiators with a registered key are considered reservation
	holders.
	Please reference the SPC spec on the meaning of a reservation
	holder if you want to use this type. 


The following ioctl are supported:
----------------------------------

1. IOC_PR_REGISTER

This ioctl command registers a new reservation if the new_key argument
is non-null.  If no existing reservation exists old_key must be zero,
if an existing reservation should be replaced old_key must contain
the old reservation key.

If the new_key argument is 0 it unregisters the existing reservation passed
in old_key.


2. IOC_PR_RESERVE

This ioctl command reserves the device and thus restricts access for other
devices based on the type argument.  The key argument must be the existing
reservation key for the device as acquired by the IOC_PR_REGISTER,
IOC_PR_REGISTER_IGNORE, IOC_PR_PREEMPT or IOC_PR_PREEMPT_ABORT commands.


3. IOC_PR_RELEASE

This ioctl command releases the reservation specified by key and flags
and thus removes any access restriction implied by it.


4. IOC_PR_PREEMPT

This ioctl command releases the existing reservation referred to by
old_key and replaces it with a a new reservation of type for the
reservation key new_key.


5. IOC_PR_PREEMPT_ABORT

This ioctl command works like IOC_PR_PREEMPT except that it also aborts
any outstanding command sent over a connection identified by old_key.

6. IOC_PR_CLEAR

This ioctl command unregisters both key and any other reservation key
registered with the device and drops any existing reservation.


Flags
-----

All the ioctls have a flag field.  Currently only one flag is supported:

 - PR_FL_IGNORE_KEY

	Ignore the existing reservation key.  This is commonly supported for
	IOC_PR_REGISTER, and some implementation may support the flag for
	IOC_PR_RESERVE.

For all unknown flags the kernel will return -EOPNOTSUPP.
+230 −100
Original line number Original line Diff line number Diff line
@@ -7,6 +7,7 @@
#include <linux/backing-dev.h>
#include <linux/backing-dev.h>
#include <linux/fs.h>
#include <linux/fs.h>
#include <linux/blktrace_api.h>
#include <linux/blktrace_api.h>
#include <linux/pr.h>
#include <asm/uaccess.h>
#include <asm/uaccess.h>


static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user *arg)
static int blkpg_ioctl(struct block_device *bdev, struct blkpg_ioctl_arg __user *arg)
@@ -193,10 +194,20 @@ int blkdev_reread_part(struct block_device *bdev)
}
}
EXPORT_SYMBOL(blkdev_reread_part);
EXPORT_SYMBOL(blkdev_reread_part);


static int blk_ioctl_discard(struct block_device *bdev, uint64_t start,
static int blk_ioctl_discard(struct block_device *bdev, fmode_t mode,
			     uint64_t len, int secure)
		unsigned long arg, unsigned long flags)
{
{
	unsigned long flags = 0;
	uint64_t range[2];
	uint64_t start, len;

	if (!(mode & FMODE_WRITE))
		return -EBADF;

	if (copy_from_user(range, (void __user *)arg, sizeof(range)))
		return -EFAULT;

	start = range[0];
	len = range[1];


	if (start & 511)
	if (start & 511)
		return -EINVAL;
		return -EINVAL;
@@ -207,14 +218,24 @@ static int blk_ioctl_discard(struct block_device *bdev, uint64_t start,


	if (start + len > (i_size_read(bdev->bd_inode) >> 9))
	if (start + len > (i_size_read(bdev->bd_inode) >> 9))
		return -EINVAL;
		return -EINVAL;
	if (secure)
		flags |= BLKDEV_DISCARD_SECURE;
	return blkdev_issue_discard(bdev, start, len, GFP_KERNEL, flags);
	return blkdev_issue_discard(bdev, start, len, GFP_KERNEL, flags);
}
}


static int blk_ioctl_zeroout(struct block_device *bdev, uint64_t start,
static int blk_ioctl_zeroout(struct block_device *bdev, fmode_t mode,
			     uint64_t len)
		unsigned long arg)
{
{
	uint64_t range[2];
	uint64_t start, len;

	if (!(mode & FMODE_WRITE))
		return -EBADF;

	if (copy_from_user(range, (void __user *)arg, sizeof(range)))
		return -EFAULT;

	start = range[0];
	len = range[1];

	if (start & 511)
	if (start & 511)
		return -EINVAL;
		return -EINVAL;
	if (len & 511)
	if (len & 511)
@@ -275,6 +296,96 @@ int __blkdev_driver_ioctl(struct block_device *bdev, fmode_t mode,
 */
 */
EXPORT_SYMBOL_GPL(__blkdev_driver_ioctl);
EXPORT_SYMBOL_GPL(__blkdev_driver_ioctl);


static int blkdev_pr_register(struct block_device *bdev,
		struct pr_registration __user *arg)
{
	const struct pr_ops *ops = bdev->bd_disk->fops->pr_ops;
	struct pr_registration reg;

	if (!capable(CAP_SYS_ADMIN))
		return -EPERM;
	if (!ops || !ops->pr_register)
		return -EOPNOTSUPP;
	if (copy_from_user(&reg, arg, sizeof(reg)))
		return -EFAULT;

	if (reg.flags & ~PR_FL_IGNORE_KEY)
		return -EOPNOTSUPP;
	return ops->pr_register(bdev, reg.old_key, reg.new_key, reg.flags);
}

static int blkdev_pr_reserve(struct block_device *bdev,
		struct pr_reservation __user *arg)
{
	const struct pr_ops *ops = bdev->bd_disk->fops->pr_ops;
	struct pr_reservation rsv;

	if (!capable(CAP_SYS_ADMIN))
		return -EPERM;
	if (!ops || !ops->pr_reserve)
		return -EOPNOTSUPP;
	if (copy_from_user(&rsv, arg, sizeof(rsv)))
		return -EFAULT;

	if (rsv.flags & ~PR_FL_IGNORE_KEY)
		return -EOPNOTSUPP;
	return ops->pr_reserve(bdev, rsv.key, rsv.type, rsv.flags);
}

static int blkdev_pr_release(struct block_device *bdev,
		struct pr_reservation __user *arg)
{
	const struct pr_ops *ops = bdev->bd_disk->fops->pr_ops;
	struct pr_reservation rsv;

	if (!capable(CAP_SYS_ADMIN))
		return -EPERM;
	if (!ops || !ops->pr_release)
		return -EOPNOTSUPP;
	if (copy_from_user(&rsv, arg, sizeof(rsv)))
		return -EFAULT;

	if (rsv.flags)
		return -EOPNOTSUPP;
	return ops->pr_release(bdev, rsv.key, rsv.type);
}

static int blkdev_pr_preempt(struct block_device *bdev,
		struct pr_preempt __user *arg, bool abort)
{
	const struct pr_ops *ops = bdev->bd_disk->fops->pr_ops;
	struct pr_preempt p;

	if (!capable(CAP_SYS_ADMIN))
		return -EPERM;
	if (!ops || !ops->pr_preempt)
		return -EOPNOTSUPP;
	if (copy_from_user(&p, arg, sizeof(p)))
		return -EFAULT;

	if (p.flags)
		return -EOPNOTSUPP;
	return ops->pr_preempt(bdev, p.old_key, p.new_key, p.type, abort);
}

static int blkdev_pr_clear(struct block_device *bdev,
		struct pr_clear __user *arg)
{
	const struct pr_ops *ops = bdev->bd_disk->fops->pr_ops;
	struct pr_clear c;

	if (!capable(CAP_SYS_ADMIN))
		return -EPERM;
	if (!ops || !ops->pr_clear)
		return -EOPNOTSUPP;
	if (copy_from_user(&c, arg, sizeof(c)))
		return -EFAULT;

	if (c.flags)
		return -EOPNOTSUPP;
	return ops->pr_clear(bdev, c.key);
}

/*
/*
 * Is it an unrecognized ioctl? The correct returns are either
 * Is it an unrecognized ioctl? The correct returns are either
 * ENOTTY (final) or ENOIOCTLCMD ("I don't know this one, try a
 * ENOTTY (final) or ENOIOCTLCMD ("I don't know this one, try a
@@ -295,20 +406,11 @@ static inline int is_unrecognized_ioctl(int ret)
		ret == -ENOIOCTLCMD;
		ret == -ENOIOCTLCMD;
}
}


/*
static int blkdev_flushbuf(struct block_device *bdev, fmode_t mode,
 * always keep this in sync with compat_blkdev_ioctl()
		unsigned cmd, unsigned long arg)
 */
int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
			unsigned long arg)
{
{
	struct gendisk *disk = bdev->bd_disk;
	int ret;
	struct backing_dev_info *bdi;
	loff_t size;
	int ret, n;
	unsigned int max_sectors;


	switch(cmd) {
	case BLKFLSBUF:
	if (!capable(CAP_SYS_ADMIN))
	if (!capable(CAP_SYS_ADMIN))
		return -EACCES;
		return -EACCES;


@@ -319,47 +421,32 @@ int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
	fsync_bdev(bdev);
	fsync_bdev(bdev);
	invalidate_bdev(bdev);
	invalidate_bdev(bdev);
	return 0;
	return 0;
}

static int blkdev_roset(struct block_device *bdev, fmode_t mode,
		unsigned cmd, unsigned long arg)
{
	int ret, n;


	case BLKROSET:
	ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg);
	ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg);
	if (!is_unrecognized_ioctl(ret))
	if (!is_unrecognized_ioctl(ret))
		return ret;
		return ret;
	if (!capable(CAP_SYS_ADMIN))
	if (!capable(CAP_SYS_ADMIN))
		return -EACCES;
		return -EACCES;
		if (get_user(n, (int __user *)(arg)))
	if (get_user(n, (int __user *)arg))
		return -EFAULT;
		return -EFAULT;
	set_device_ro(bdev, n);
	set_device_ro(bdev, n);
	return 0;
	return 0;

	case BLKDISCARD:
	case BLKSECDISCARD: {
		uint64_t range[2];

		if (!(mode & FMODE_WRITE))
			return -EBADF;

		if (copy_from_user(range, (void __user *)arg, sizeof(range)))
			return -EFAULT;

		return blk_ioctl_discard(bdev, range[0], range[1],
					 cmd == BLKSECDISCARD);
}
}
	case BLKZEROOUT: {
		uint64_t range[2];


		if (!(mode & FMODE_WRITE))
static int blkdev_getgeo(struct block_device *bdev,
			return -EBADF;
		struct hd_geometry __user *argp)

{
		if (copy_from_user(range, (void __user *)arg, sizeof(range)))
	struct gendisk *disk = bdev->bd_disk;
			return -EFAULT;

		return blk_ioctl_zeroout(bdev, range[0], range[1]);
	}

	case HDIO_GETGEO: {
	struct hd_geometry geo;
	struct hd_geometry geo;
	int ret;


		if (!arg)
	if (!argp)
		return -EINVAL;
		return -EINVAL;
	if (!disk->fops->getgeo)
	if (!disk->fops->getgeo)
		return -ENOTTY;
		return -ENOTTY;
@@ -373,11 +460,61 @@ int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
	ret = disk->fops->getgeo(bdev, &geo);
	ret = disk->fops->getgeo(bdev, &geo);
	if (ret)
	if (ret)
		return ret;
		return ret;
		if (copy_to_user((struct hd_geometry __user *)arg, &geo,
	if (copy_to_user(argp, &geo, sizeof(geo)))
					sizeof(geo)))
		return -EFAULT;
		return -EFAULT;
	return 0;
	return 0;
}
}

/* set the logical block size */
static int blkdev_bszset(struct block_device *bdev, fmode_t mode,
		int __user *argp)
{
	int ret, n;

	if (!capable(CAP_SYS_ADMIN))
		return -EACCES;
	if (!argp)
		return -EINVAL;
	if (get_user(n, argp))
		return -EFAULT;

	if (!(mode & FMODE_EXCL)) {
		bdgrab(bdev);
		if (blkdev_get(bdev, mode | FMODE_EXCL, &bdev) < 0)
			return -EBUSY;
	}

	ret = set_blocksize(bdev, n);
	if (!(mode & FMODE_EXCL))
		blkdev_put(bdev, mode | FMODE_EXCL);
	return ret;
}

/*
 * always keep this in sync with compat_blkdev_ioctl()
 */
int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
			unsigned long arg)
{
	struct backing_dev_info *bdi;
	void __user *argp = (void __user *)arg;
	loff_t size;
	unsigned int max_sectors;

	switch (cmd) {
	case BLKFLSBUF:
		return blkdev_flushbuf(bdev, mode, cmd, arg);
	case BLKROSET:
		return blkdev_roset(bdev, mode, cmd, arg);
	case BLKDISCARD:
		return blk_ioctl_discard(bdev, mode, arg, 0);
	case BLKSECDISCARD:
		return blk_ioctl_discard(bdev, mode, arg,
				BLKDEV_DISCARD_SECURE);
	case BLKZEROOUT:
		return blk_ioctl_zeroout(bdev, mode, arg);
	case HDIO_GETGEO:
		return blkdev_getgeo(bdev, argp);
	case BLKRAGET:
	case BLKRAGET:
	case BLKFRAGET:
	case BLKFRAGET:
		if (!arg)
		if (!arg)
@@ -414,28 +551,11 @@ int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
		bdi->ra_pages = (arg * 512) / PAGE_CACHE_SIZE;
		bdi->ra_pages = (arg * 512) / PAGE_CACHE_SIZE;
		return 0;
		return 0;
	case BLKBSZSET:
	case BLKBSZSET:
		/* set the logical block size */
		return blkdev_bszset(bdev, mode, argp);
		if (!capable(CAP_SYS_ADMIN))
			return -EACCES;
		if (!arg)
			return -EINVAL;
		if (get_user(n, (int __user *) arg))
			return -EFAULT;
		if (!(mode & FMODE_EXCL)) {
			bdgrab(bdev);
			if (blkdev_get(bdev, mode | FMODE_EXCL, &bdev) < 0)
				return -EBUSY;
		}
		ret = set_blocksize(bdev, n);
		if (!(mode & FMODE_EXCL))
			blkdev_put(bdev, mode | FMODE_EXCL);
		return ret;
	case BLKPG:
	case BLKPG:
		ret = blkpg_ioctl(bdev, (struct blkpg_ioctl_arg __user *) arg);
		return blkpg_ioctl(bdev, argp);
		break;
	case BLKRRPART:
	case BLKRRPART:
		ret = blkdev_reread_part(bdev);
		return blkdev_reread_part(bdev);
		break;
	case BLKGETSIZE:
	case BLKGETSIZE:
		size = i_size_read(bdev->bd_inode);
		size = i_size_read(bdev->bd_inode);
		if ((size >> 9) > ~0UL)
		if ((size >> 9) > ~0UL)
@@ -447,11 +567,21 @@ int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
	case BLKTRACESTOP:
	case BLKTRACESTOP:
	case BLKTRACESETUP:
	case BLKTRACESETUP:
	case BLKTRACETEARDOWN:
	case BLKTRACETEARDOWN:
		ret = blk_trace_ioctl(bdev, cmd, (char __user *) arg);
		return blk_trace_ioctl(bdev, cmd, argp);
		break;
	case IOC_PR_REGISTER:
		return blkdev_pr_register(bdev, argp);
	case IOC_PR_RESERVE:
		return blkdev_pr_reserve(bdev, argp);
	case IOC_PR_RELEASE:
		return blkdev_pr_release(bdev, argp);
	case IOC_PR_PREEMPT:
		return blkdev_pr_preempt(bdev, argp, false);
	case IOC_PR_PREEMPT_ABORT:
		return blkdev_pr_preempt(bdev, argp, true);
	case IOC_PR_CLEAR:
		return blkdev_pr_clear(bdev, argp);
	default:
	default:
		ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg);
		return __blkdev_driver_ioctl(bdev, mode, cmd, arg);
	}
	}
	return ret;
}
}
EXPORT_SYMBOL_GPL(blkdev_ioctl);
EXPORT_SYMBOL_GPL(blkdev_ioctl);
+95 −0
Original line number Original line Diff line number Diff line
@@ -39,8 +39,10 @@
#include <linux/slab.h>
#include <linux/slab.h>
#include <linux/t10-pi.h>
#include <linux/t10-pi.h>
#include <linux/types.h>
#include <linux/types.h>
#include <linux/pr.h>
#include <scsi/sg.h>
#include <scsi/sg.h>
#include <asm-generic/io-64-nonatomic-lo-hi.h>
#include <asm-generic/io-64-nonatomic-lo-hi.h>
#include <asm/unaligned.h>


#include <uapi/linux/nvme_ioctl.h>
#include <uapi/linux/nvme_ioctl.h>
#include "nvme.h"
#include "nvme.h"
@@ -2064,6 +2066,98 @@ static int nvme_revalidate_disk(struct gendisk *disk)
	return 0;
	return 0;
}
}


static char nvme_pr_type(enum pr_type type)
{
	switch (type) {
	case PR_WRITE_EXCLUSIVE:
		return 1;
	case PR_EXCLUSIVE_ACCESS:
		return 2;
	case PR_WRITE_EXCLUSIVE_REG_ONLY:
		return 3;
	case PR_EXCLUSIVE_ACCESS_REG_ONLY:
		return 4;
	case PR_WRITE_EXCLUSIVE_ALL_REGS:
		return 5;
	case PR_EXCLUSIVE_ACCESS_ALL_REGS:
		return 6;
	default:
		return 0;
	}
};

static int nvme_pr_command(struct block_device *bdev, u32 cdw10,
				u64 key, u64 sa_key, u8 op)
{
	struct nvme_ns *ns = bdev->bd_disk->private_data;
	struct nvme_command c;
	u8 data[16] = { 0, };

	put_unaligned_le64(key, &data[0]);
	put_unaligned_le64(sa_key, &data[8]);

	memset(&c, 0, sizeof(c));
	c.common.opcode = op;
	c.common.nsid = cpu_to_le32(ns->ns_id);
	c.common.cdw10[0] = cpu_to_le32(cdw10);

	return nvme_submit_sync_cmd(ns->queue, &c, data, 16);
}

static int nvme_pr_register(struct block_device *bdev, u64 old,
		u64 new, unsigned flags)
{
	u32 cdw10;

	if (flags & ~PR_FL_IGNORE_KEY)
		return -EOPNOTSUPP;

	cdw10 = old ? 2 : 0;
	cdw10 |= (flags & PR_FL_IGNORE_KEY) ? 1 << 3 : 0;
	cdw10 |= (1 << 30) | (1 << 31); /* PTPL=1 */
	return nvme_pr_command(bdev, cdw10, old, new, nvme_cmd_resv_register);
}

static int nvme_pr_reserve(struct block_device *bdev, u64 key,
		enum pr_type type, unsigned flags)
{
	u32 cdw10;

	if (flags & ~PR_FL_IGNORE_KEY)
		return -EOPNOTSUPP;

	cdw10 = nvme_pr_type(type) << 8;
	cdw10 |= ((flags & PR_FL_IGNORE_KEY) ? 1 << 3 : 0);
	return nvme_pr_command(bdev, cdw10, key, 0, nvme_cmd_resv_acquire);
}

static int nvme_pr_preempt(struct block_device *bdev, u64 old, u64 new,
		enum pr_type type, bool abort)
{
	u32 cdw10 = nvme_pr_type(type) << 8 | abort ? 2 : 1;
	return nvme_pr_command(bdev, cdw10, old, new, nvme_cmd_resv_acquire);
}

static int nvme_pr_clear(struct block_device *bdev, u64 key)
{
	u32 cdw10 = 1 | (key ? 1 << 3 : 0);
	return nvme_pr_command(bdev, cdw10, key, 0, nvme_cmd_resv_register);
}

static int nvme_pr_release(struct block_device *bdev, u64 key, enum pr_type type)
{
	u32 cdw10 = nvme_pr_type(type) << 8 | key ? 1 << 3 : 0;
	return nvme_pr_command(bdev, cdw10, key, 0, nvme_cmd_resv_release);
}

static const struct pr_ops nvme_pr_ops = {
	.pr_register	= nvme_pr_register,
	.pr_reserve	= nvme_pr_reserve,
	.pr_release	= nvme_pr_release,
	.pr_preempt	= nvme_pr_preempt,
	.pr_clear	= nvme_pr_clear,
};

static const struct block_device_operations nvme_fops = {
static const struct block_device_operations nvme_fops = {
	.owner		= THIS_MODULE,
	.owner		= THIS_MODULE,
	.ioctl		= nvme_ioctl,
	.ioctl		= nvme_ioctl,
@@ -2072,6 +2166,7 @@ static const struct block_device_operations nvme_fops = {
	.release	= nvme_release,
	.release	= nvme_release,
	.getgeo		= nvme_getgeo,
	.getgeo		= nvme_getgeo,
	.revalidate_disk= nvme_revalidate_disk,
	.revalidate_disk= nvme_revalidate_disk,
	.pr_ops		= &nvme_pr_ops,
};
};


static int nvme_kthread(void *data)
static int nvme_kthread(void *data)
+96 −0
Original line number Original line Diff line number Diff line
@@ -51,6 +51,7 @@
#include <linux/async.h>
#include <linux/async.h>
#include <linux/slab.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
#include <linux/pm_runtime.h>
#include <linux/pr.h>
#include <asm/uaccess.h>
#include <asm/uaccess.h>
#include <asm/unaligned.h>
#include <asm/unaligned.h>


@@ -1535,6 +1536,100 @@ static int sd_compat_ioctl(struct block_device *bdev, fmode_t mode,
}
}
#endif
#endif


static char sd_pr_type(enum pr_type type)
{
	switch (type) {
	case PR_WRITE_EXCLUSIVE:
		return 0x01;
	case PR_EXCLUSIVE_ACCESS:
		return 0x03;
	case PR_WRITE_EXCLUSIVE_REG_ONLY:
		return 0x05;
	case PR_EXCLUSIVE_ACCESS_REG_ONLY:
		return 0x06;
	case PR_WRITE_EXCLUSIVE_ALL_REGS:
		return 0x07;
	case PR_EXCLUSIVE_ACCESS_ALL_REGS:
		return 0x08;
	default:
		return 0;
	}
};

static int sd_pr_command(struct block_device *bdev, u8 sa,
		u64 key, u64 sa_key, u8 type, u8 flags)
{
	struct scsi_device *sdev = scsi_disk(bdev->bd_disk)->device;
	struct scsi_sense_hdr sshdr;
	int result;
	u8 cmd[16] = { 0, };
	u8 data[24] = { 0, };

	cmd[0] = PERSISTENT_RESERVE_OUT;
	cmd[1] = sa;
	cmd[2] = type;
	put_unaligned_be32(sizeof(data), &cmd[5]);

	put_unaligned_be64(key, &data[0]);
	put_unaligned_be64(sa_key, &data[8]);
	data[20] = flags;

	result = scsi_execute_req(sdev, cmd, DMA_TO_DEVICE, &data, sizeof(data),
			&sshdr, SD_TIMEOUT, SD_MAX_RETRIES, NULL);

	if ((driver_byte(result) & DRIVER_SENSE) &&
	    (scsi_sense_valid(&sshdr))) {
		sdev_printk(KERN_INFO, sdev, "PR command failed: %d\n", result);
		scsi_print_sense_hdr(sdev, NULL, &sshdr);
	}

	return result;
}

static int sd_pr_register(struct block_device *bdev, u64 old_key, u64 new_key,
		u32 flags)
{
	if (flags & ~PR_FL_IGNORE_KEY)
		return -EOPNOTSUPP;
	return sd_pr_command(bdev, (flags & PR_FL_IGNORE_KEY) ? 0x06 : 0x00,
			old_key, new_key, 0,
			(1 << 0) /* APTPL */ |
			(1 << 2) /* ALL_TG_PT */);
}

static int sd_pr_reserve(struct block_device *bdev, u64 key, enum pr_type type,
		u32 flags)
{
	if (flags)
		return -EOPNOTSUPP;
	return sd_pr_command(bdev, 0x01, key, 0, sd_pr_type(type), 0);
}

static int sd_pr_release(struct block_device *bdev, u64 key, enum pr_type type)
{
	return sd_pr_command(bdev, 0x02, key, 0, sd_pr_type(type), 0);
}

static int sd_pr_preempt(struct block_device *bdev, u64 old_key, u64 new_key,
		enum pr_type type, bool abort)
{
	return sd_pr_command(bdev, abort ? 0x05 : 0x04, old_key, new_key,
			     sd_pr_type(type), 0);
}

static int sd_pr_clear(struct block_device *bdev, u64 key)
{
	return sd_pr_command(bdev, 0x03, key, 0, 0, 0);
}

static const struct pr_ops sd_pr_ops = {
	.pr_register	= sd_pr_register,
	.pr_reserve	= sd_pr_reserve,
	.pr_release	= sd_pr_release,
	.pr_preempt	= sd_pr_preempt,
	.pr_clear	= sd_pr_clear,
};

static const struct block_device_operations sd_fops = {
static const struct block_device_operations sd_fops = {
	.owner			= THIS_MODULE,
	.owner			= THIS_MODULE,
	.open			= sd_open,
	.open			= sd_open,
@@ -1547,6 +1642,7 @@ static const struct block_device_operations sd_fops = {
	.check_events		= sd_check_events,
	.check_events		= sd_check_events,
	.revalidate_disk	= sd_revalidate_disk,
	.revalidate_disk	= sd_revalidate_disk,
	.unlock_native_capacity	= sd_unlock_native_capacity,
	.unlock_native_capacity	= sd_unlock_native_capacity,
	.pr_ops			= &sd_pr_ops,
};
};


/**
/**
+2 −0
Original line number Original line Diff line number Diff line
@@ -35,6 +35,7 @@ struct sg_io_hdr;
struct bsg_job;
struct bsg_job;
struct blkcg_gq;
struct blkcg_gq;
struct blk_flush_queue;
struct blk_flush_queue;
struct pr_ops;


#define BLKDEV_MIN_RQ	4
#define BLKDEV_MIN_RQ	4
#define BLKDEV_MAX_RQ	128	/* Default maximum */
#define BLKDEV_MAX_RQ	128	/* Default maximum */
@@ -1629,6 +1630,7 @@ struct block_device_operations {
	/* this callback is with swap_lock and sometimes page table lock held */
	/* this callback is with swap_lock and sometimes page table lock held */
	void (*swap_slot_free_notify) (struct block_device *, unsigned long);
	void (*swap_slot_free_notify) (struct block_device *, unsigned long);
	struct module *owner;
	struct module *owner;
	const struct pr_ops *pr_ops;
};
};


extern int __blkdev_driver_ioctl(struct block_device *, fmode_t, unsigned int,
extern int __blkdev_driver_ioctl(struct block_device *, fmode_t, unsigned int,
Loading