Commit 8c6aabd1 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by Jens Axboe
Browse files

nfsd/blocklayout: use ->get_unique_id instead of sending SCSI commands



Call the ->get_unique_id method to query the SCSI identifiers.  This can
use the cached VPD page in the sd driver instead of sending a command
on every LAYOUTGET.  It will also allow to support NVMe based volumes
if the draft for that ever takes off.

Signed-off-by: default avatarChristoph Hellwig <hch@lst.de>
Acked-by: default avatarJ. Bruce Fields <bfields@redhat.com>
Reviewed-by: default avatarHannes Reinecke <hare@suse.de>
Link: https://lore.kernel.org/r/20211021060607.264371-4-hch@lst.de


Signed-off-by: default avatarJens Axboe <axboe@kernel.dk>
parent b83ce214
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -109,7 +109,6 @@ config NFSD_SCSILAYOUT
	depends on NFSD_V4 && BLOCK
	select NFSD_PNFS
	select EXPORTFS_BLOCK_OPS
	select SCSI_COMMON
	help
	  This option enables support for the exporting pNFS SCSI layouts
	  in the kernel's NFS server. The pNFS SCSI layout enables NFS
+41 −117
Original line number Diff line number Diff line
@@ -9,9 +9,6 @@
#include <linux/pr.h>

#include <linux/nfsd/debug.h>
#include <scsi/scsi_proto.h>
#include <scsi/scsi_common.h>
#include <scsi/scsi_request.h>

#include "blocklayoutxdr.h"
#include "pnfs.h"
@@ -211,118 +208,40 @@ const struct nfsd4_layout_ops bl_layout_ops = {
#endif /* CONFIG_NFSD_BLOCKLAYOUT */

#ifdef CONFIG_NFSD_SCSILAYOUT
static int nfsd4_scsi_identify_device(struct block_device *bdev,
		struct pnfs_block_volume *b)
{
	struct request_queue *q = bdev->bd_disk->queue;
	struct request *rq;
	struct scsi_request *req;
#define NFSD_MDS_PR_KEY		0x0100000000000000ULL

/*
	 * The allocation length (passed in bytes 3 and 4 of the INQUIRY
	 * command descriptor block) specifies the number of bytes that have
	 * been allocated for the data-in buffer.
	 * 252 is the highest one-byte value that is a multiple of 4.
	 * 65532 is the highest two-byte value that is a multiple of 4.
 * We use the client ID as a unique key for the reservations.
 * This allows us to easily fence a client when recalls fail.
 */
	size_t bufflen = 252, maxlen = 65532, len, id_len;
	u8 *buf, *d, type, assoc;
	int retries = 1, error;

	if (WARN_ON_ONCE(!blk_queue_scsi_passthrough(q)))
		return -EINVAL;

again:
	buf = kzalloc(bufflen, GFP_KERNEL);
	if (!buf)
		return -ENOMEM;

	rq = blk_get_request(q, REQ_OP_DRV_IN, 0);
	if (IS_ERR(rq)) {
		error = -ENOMEM;
		goto out_free_buf;
	}
	req = scsi_req(rq);

	error = blk_rq_map_kern(q, rq, buf, bufflen, GFP_KERNEL);
	if (error)
		goto out_put_request;

	req->cmd[0] = INQUIRY;
	req->cmd[1] = 1;
	req->cmd[2] = 0x83;
	req->cmd[3] = bufflen >> 8;
	req->cmd[4] = bufflen & 0xff;
	req->cmd_len = COMMAND_SIZE(INQUIRY);

	blk_execute_rq(NULL, rq, 1);
	if (req->result) {
		pr_err("pNFS: INQUIRY 0x83 failed with: %x\n",
			req->result);
		error = -EIO;
		goto out_put_request;
static u64 nfsd4_scsi_pr_key(struct nfs4_client *clp)
{
	return ((u64)clp->cl_clientid.cl_boot << 32) | clp->cl_clientid.cl_id;
}

	len = (buf[2] << 8) + buf[3] + 4;
	if (len > bufflen) {
		if (len <= maxlen && retries--) {
			blk_put_request(rq);
			kfree(buf);
			bufflen = len;
			goto again;
		}
		pr_err("pNFS: INQUIRY 0x83 response invalid (len = %zd)\n",
			len);
		goto out_put_request;
	}
static const u8 designator_types[] = {
	PS_DESIGNATOR_EUI64,
	PS_DESIGNATOR_NAA,
};

	d = buf + 4;
	for (d = buf + 4; d < buf + len; d += id_len + 4) {
		id_len = d[3];
		type = d[1] & 0xf;
		assoc = (d[1] >> 4) & 0x3;
static int
nfsd4_block_get_unique_id(struct gendisk *disk, struct pnfs_block_volume *b)
{
	int ret, i;

		/*
		 * We only care about a EUI-64 and NAA designator types
		 * with LU association.
		 */
		if (assoc != 0x00)
			continue;
		if (type != 0x02 && type != 0x03)
			continue;
		if (id_len != 8 && id_len != 12 && id_len != 16)
			continue;
	for (i = 0; i < ARRAY_SIZE(designator_types); i++) {
		u8 type = designator_types[i];

		ret = disk->fops->get_unique_id(disk, b->scsi.designator, type);
		if (ret > 0) {
			b->scsi.code_set = PS_CODE_SET_BINARY;
		b->scsi.designator_type = type == 0x02 ?
			PS_DESIGNATOR_EUI64 : PS_DESIGNATOR_NAA;
		b->scsi.designator_len = id_len;
		memcpy(b->scsi.designator, d + 4, id_len);

		/*
		 * If we found a 8 or 12 byte descriptor continue on to
		 * see if a 16 byte one is available.  If we find a
		 * 16 byte descriptor we're done.
		 */
		if (id_len == 16)
			break;
			b->scsi.designator_type = type;
			b->scsi.designator_len = ret;
			return 0;
		}

out_put_request:
	blk_put_request(rq);
out_free_buf:
	kfree(buf);
	return error;
	}

#define NFSD_MDS_PR_KEY		0x0100000000000000ULL

/*
 * We use the client ID as a unique key for the reservations.
 * This allows us to easily fence a client when recalls fail.
 */
static u64 nfsd4_scsi_pr_key(struct nfs4_client *clp)
{
	return ((u64)clp->cl_clientid.cl_boot << 32) | clp->cl_clientid.cl_id;
	return -EINVAL;
}

static int
@@ -333,7 +252,7 @@ nfsd4_block_get_device_info_scsi(struct super_block *sb,
	struct pnfs_block_deviceaddr *dev;
	struct pnfs_block_volume *b;
	const struct pr_ops *ops;
	int error;
	int ret;

	dev = kzalloc(sizeof(struct pnfs_block_deviceaddr) +
		      sizeof(struct pnfs_block_volume), GFP_KERNEL);
@@ -347,33 +266,38 @@ nfsd4_block_get_device_info_scsi(struct super_block *sb,
	b->type = PNFS_BLOCK_VOLUME_SCSI;
	b->scsi.pr_key = nfsd4_scsi_pr_key(clp);

	error = nfsd4_scsi_identify_device(sb->s_bdev, b);
	if (error)
		return error;
	ret = nfsd4_block_get_unique_id(sb->s_bdev->bd_disk, b);
	if (ret < 0)
		goto out_free_dev;

	ret = -EINVAL;
	ops = sb->s_bdev->bd_disk->fops->pr_ops;
	if (!ops) {
		pr_err("pNFS: device %s does not support PRs.\n",
			sb->s_id);
		return -EINVAL;
		goto out_free_dev;
	}

	error = ops->pr_register(sb->s_bdev, 0, NFSD_MDS_PR_KEY, true);
	if (error) {
	ret = ops->pr_register(sb->s_bdev, 0, NFSD_MDS_PR_KEY, true);
	if (ret) {
		pr_err("pNFS: failed to register key for device %s.\n",
			sb->s_id);
		return -EINVAL;
		goto out_free_dev;
	}

	error = ops->pr_reserve(sb->s_bdev, NFSD_MDS_PR_KEY,
	ret = ops->pr_reserve(sb->s_bdev, NFSD_MDS_PR_KEY,
			PR_EXCLUSIVE_ACCESS_REG_ONLY, 0);
	if (error) {
	if (ret) {
		pr_err("pNFS: failed to reserve device %s.\n",
			sb->s_id);
		return -EINVAL;
		goto out_free_dev;
	}

	return 0;

out_free_dev:
	kfree(dev);
	return ret;
}

static __be32
+3 −2
Original line number Diff line number Diff line
@@ -145,8 +145,9 @@ void nfsd4_setup_layout_type(struct svc_export *exp)
#ifdef CONFIG_NFSD_SCSILAYOUT
	if (sb->s_export_op->map_blocks &&
	    sb->s_export_op->commit_blocks &&
	    sb->s_bdev && sb->s_bdev->bd_disk->fops->pr_ops &&
		blk_queue_scsi_passthrough(sb->s_bdev->bd_disk->queue))
	    sb->s_bdev &&
	    sb->s_bdev->bd_disk->fops->pr_ops &&
	    sb->s_bdev->bd_disk->fops->get_unique_id)
		exp->ex_layout_types |= 1 << LAYOUT_SCSI;
#endif
}