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
This commit is contained in:
commit
ccf21b69a8
119
Documentation/block/pr.txt
Normal file
119
Documentation/block/pr.txt
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
|
||||||
|
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.
|
332
block/ioctl.c
332
block/ioctl.c
@ -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(®, 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,89 +406,115 @@ static inline int is_unrecognized_ioctl(int ret)
|
|||||||
ret == -ENOIOCTLCMD;
|
ret == -ENOIOCTLCMD;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int blkdev_flushbuf(struct block_device *bdev, fmode_t mode,
|
||||||
|
unsigned cmd, unsigned long arg)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!capable(CAP_SYS_ADMIN))
|
||||||
|
return -EACCES;
|
||||||
|
|
||||||
|
ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg);
|
||||||
|
if (!is_unrecognized_ioctl(ret))
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
fsync_bdev(bdev);
|
||||||
|
invalidate_bdev(bdev);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int blkdev_roset(struct block_device *bdev, fmode_t mode,
|
||||||
|
unsigned cmd, unsigned long arg)
|
||||||
|
{
|
||||||
|
int ret, n;
|
||||||
|
|
||||||
|
ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg);
|
||||||
|
if (!is_unrecognized_ioctl(ret))
|
||||||
|
return ret;
|
||||||
|
if (!capable(CAP_SYS_ADMIN))
|
||||||
|
return -EACCES;
|
||||||
|
if (get_user(n, (int __user *)arg))
|
||||||
|
return -EFAULT;
|
||||||
|
set_device_ro(bdev, n);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int blkdev_getgeo(struct block_device *bdev,
|
||||||
|
struct hd_geometry __user *argp)
|
||||||
|
{
|
||||||
|
struct gendisk *disk = bdev->bd_disk;
|
||||||
|
struct hd_geometry geo;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
if (!argp)
|
||||||
|
return -EINVAL;
|
||||||
|
if (!disk->fops->getgeo)
|
||||||
|
return -ENOTTY;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We need to set the startsect first, the driver may
|
||||||
|
* want to override it.
|
||||||
|
*/
|
||||||
|
memset(&geo, 0, sizeof(geo));
|
||||||
|
geo.start = get_start_sect(bdev);
|
||||||
|
ret = disk->fops->getgeo(bdev, &geo);
|
||||||
|
if (ret)
|
||||||
|
return ret;
|
||||||
|
if (copy_to_user(argp, &geo, sizeof(geo)))
|
||||||
|
return -EFAULT;
|
||||||
|
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()
|
* always keep this in sync with compat_blkdev_ioctl()
|
||||||
*/
|
*/
|
||||||
int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
|
int blkdev_ioctl(struct block_device *bdev, fmode_t mode, unsigned cmd,
|
||||||
unsigned long arg)
|
unsigned long arg)
|
||||||
{
|
{
|
||||||
struct gendisk *disk = bdev->bd_disk;
|
|
||||||
struct backing_dev_info *bdi;
|
struct backing_dev_info *bdi;
|
||||||
|
void __user *argp = (void __user *)arg;
|
||||||
loff_t size;
|
loff_t size;
|
||||||
int ret, n;
|
|
||||||
unsigned int max_sectors;
|
unsigned int max_sectors;
|
||||||
|
|
||||||
switch(cmd) {
|
switch (cmd) {
|
||||||
case BLKFLSBUF:
|
case BLKFLSBUF:
|
||||||
if (!capable(CAP_SYS_ADMIN))
|
return blkdev_flushbuf(bdev, mode, cmd, arg);
|
||||||
return -EACCES;
|
|
||||||
|
|
||||||
ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg);
|
|
||||||
if (!is_unrecognized_ioctl(ret))
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
fsync_bdev(bdev);
|
|
||||||
invalidate_bdev(bdev);
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
case BLKROSET:
|
case BLKROSET:
|
||||||
ret = __blkdev_driver_ioctl(bdev, mode, cmd, arg);
|
return blkdev_roset(bdev, mode, cmd, arg);
|
||||||
if (!is_unrecognized_ioctl(ret))
|
|
||||||
return ret;
|
|
||||||
if (!capable(CAP_SYS_ADMIN))
|
|
||||||
return -EACCES;
|
|
||||||
if (get_user(n, (int __user *)(arg)))
|
|
||||||
return -EFAULT;
|
|
||||||
set_device_ro(bdev, n);
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
case BLKDISCARD:
|
case BLKDISCARD:
|
||||||
case BLKSECDISCARD: {
|
return blk_ioctl_discard(bdev, mode, arg, 0);
|
||||||
uint64_t range[2];
|
case BLKSECDISCARD:
|
||||||
|
return blk_ioctl_discard(bdev, mode, arg,
|
||||||
if (!(mode & FMODE_WRITE))
|
BLKDEV_DISCARD_SECURE);
|
||||||
return -EBADF;
|
case BLKZEROOUT:
|
||||||
|
return blk_ioctl_zeroout(bdev, mode, arg);
|
||||||
if (copy_from_user(range, (void __user *)arg, sizeof(range)))
|
case HDIO_GETGEO:
|
||||||
return -EFAULT;
|
return blkdev_getgeo(bdev, argp);
|
||||||
|
|
||||||
return blk_ioctl_discard(bdev, range[0], range[1],
|
|
||||||
cmd == BLKSECDISCARD);
|
|
||||||
}
|
|
||||||
case BLKZEROOUT: {
|
|
||||||
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_zeroout(bdev, range[0], range[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
case HDIO_GETGEO: {
|
|
||||||
struct hd_geometry geo;
|
|
||||||
|
|
||||||
if (!arg)
|
|
||||||
return -EINVAL;
|
|
||||||
if (!disk->fops->getgeo)
|
|
||||||
return -ENOTTY;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* We need to set the startsect first, the driver may
|
|
||||||
* want to override it.
|
|
||||||
*/
|
|
||||||
memset(&geo, 0, sizeof(geo));
|
|
||||||
geo.start = get_start_sect(bdev);
|
|
||||||
ret = disk->fops->getgeo(bdev, &geo);
|
|
||||||
if (ret)
|
|
||||||
return ret;
|
|
||||||
if (copy_to_user((struct hd_geometry __user *)arg, &geo,
|
|
||||||
sizeof(geo)))
|
|
||||||
return -EFAULT;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
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);
|
||||||
|
@ -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)
|
||||||
|
@ -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,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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,
|
||||||
|
18
include/linux/pr.h
Normal file
18
include/linux/pr.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#ifndef LINUX_PR_H
|
||||||
|
#define LINUX_PR_H
|
||||||
|
|
||||||
|
#include <uapi/linux/pr.h>
|
||||||
|
|
||||||
|
struct pr_ops {
|
||||||
|
int (*pr_register)(struct block_device *bdev, u64 old_key, u64 new_key,
|
||||||
|
u32 flags);
|
||||||
|
int (*pr_reserve)(struct block_device *bdev, u64 key,
|
||||||
|
enum pr_type type, u32 flags);
|
||||||
|
int (*pr_release)(struct block_device *bdev, u64 key,
|
||||||
|
enum pr_type type);
|
||||||
|
int (*pr_preempt)(struct block_device *bdev, u64 old_key, u64 new_key,
|
||||||
|
enum pr_type type, bool abort);
|
||||||
|
int (*pr_clear)(struct block_device *bdev, u64 key);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* LINUX_PR_H */
|
48
include/uapi/linux/pr.h
Normal file
48
include/uapi/linux/pr.h
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
#ifndef _UAPI_PR_H
|
||||||
|
#define _UAPI_PR_H
|
||||||
|
|
||||||
|
enum pr_type {
|
||||||
|
PR_WRITE_EXCLUSIVE = 1,
|
||||||
|
PR_EXCLUSIVE_ACCESS = 2,
|
||||||
|
PR_WRITE_EXCLUSIVE_REG_ONLY = 3,
|
||||||
|
PR_EXCLUSIVE_ACCESS_REG_ONLY = 4,
|
||||||
|
PR_WRITE_EXCLUSIVE_ALL_REGS = 5,
|
||||||
|
PR_EXCLUSIVE_ACCESS_ALL_REGS = 6,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pr_reservation {
|
||||||
|
__u64 key;
|
||||||
|
__u32 type;
|
||||||
|
__u32 flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pr_registration {
|
||||||
|
__u64 old_key;
|
||||||
|
__u64 new_key;
|
||||||
|
__u32 flags;
|
||||||
|
__u32 __pad;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pr_preempt {
|
||||||
|
__u64 old_key;
|
||||||
|
__u64 new_key;
|
||||||
|
__u32 type;
|
||||||
|
__u32 flags;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct pr_clear {
|
||||||
|
__u64 key;
|
||||||
|
__u32 flags;
|
||||||
|
__u32 __pad;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define PR_FL_IGNORE_KEY (1 << 0) /* ignore existing key */
|
||||||
|
|
||||||
|
#define IOC_PR_REGISTER _IOW('p', 200, struct pr_registration)
|
||||||
|
#define IOC_PR_RESERVE _IOW('p', 201, struct pr_reservation)
|
||||||
|
#define IOC_PR_RELEASE _IOW('p', 202, struct pr_reservation)
|
||||||
|
#define IOC_PR_PREEMPT _IOW('p', 203, struct pr_preempt)
|
||||||
|
#define IOC_PR_PREEMPT_ABORT _IOW('p', 204, struct pr_preempt)
|
||||||
|
#define IOC_PR_CLEAR _IOW('p', 205, struct pr_clear)
|
||||||
|
|
||||||
|
#endif /* _UAPI_PR_H */
|
Loading…
x
Reference in New Issue
Block a user