aboutsummaryrefslogtreecommitdiff
path: root/sys/cam/ctl
diff options
context:
space:
mode:
authorEdward Tomasz Napierala <trasz@FreeBSD.org>2012-03-06 13:43:57 +0000
committerEdward Tomasz Napierala <trasz@FreeBSD.org>2012-03-06 13:43:57 +0000
commit811772950f6233aa95be544ad5e7cb7002488d68 (patch)
tree21a7004b7ca48373596470e3a58af3c4b396cd5a /sys/cam/ctl
parent7dfd88318b2c2e2e105fa66fba408ea5c034aa05 (diff)
downloadsrc-811772950f6233aa95be544ad5e7cb7002488d68.tar.gz
src-811772950f6233aa95be544ad5e7cb7002488d68.zip
Add LUN resizing to CTL. Also make it possible to explicitly set
size when creating file-backed or device-backed LUN. Reviewed by: ken (earlier version) Sponsored by: The FreeBSD Foundation
Notes
Notes: svn path=/head/; revision=232604
Diffstat (limited to 'sys/cam/ctl')
-rw-r--r--sys/cam/ctl/ctl.c23
-rw-r--r--sys/cam/ctl/ctl.h3
-rw-r--r--sys/cam/ctl/ctl_backend.h5
-rw-r--r--sys/cam/ctl/ctl_backend_block.c188
-rw-r--r--sys/cam/ctl/ctl_backend_ramdisk.c76
-rw-r--r--sys/cam/ctl/ctl_error.c5
-rw-r--r--sys/cam/ctl/ctl_ioctl.h18
7 files changed, 310 insertions, 8 deletions
diff --git a/sys/cam/ctl/ctl.c b/sys/cam/ctl/ctl.c
index 7a365099f1db..06920bb0ca41 100644
--- a/sys/cam/ctl/ctl.c
+++ b/sys/cam/ctl/ctl.c
@@ -1,7 +1,11 @@
/*-
* Copyright (c) 2003-2009 Silicon Graphics International Corp.
+ * Copyright (c) 2012 The FreeBSD Foundation
* All rights reserved.
*
+ * Portions of this software were developed by Edward Tomasz Napierala
+ * under sponsorship from the FreeBSD Foundation.
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@@ -4797,6 +4801,25 @@ ctl_lun_power_lock(struct ctl_be_lun *be_lun, struct ctl_nexus *nexus,
return (0);
}
+void
+ctl_lun_capacity_changed(struct ctl_be_lun *be_lun)
+{
+ struct ctl_lun *lun;
+ struct ctl_softc *softc;
+ int i;
+
+ softc = control_softc;
+
+ mtx_lock(&softc->ctl_lock);
+
+ lun = (struct ctl_lun *)be_lun->ctl_lun;
+
+ for (i = 0; i < CTL_MAX_INITIATORS; i++)
+ lun->pending_sense[i].ua_pending |= CTL_UA_CAPACITY_CHANGED;
+
+ mtx_unlock(&softc->ctl_lock);
+}
+
/*
* Backend "memory move is complete" callback for requests that never
* make it down to say RAIDCore's configuration code.
diff --git a/sys/cam/ctl/ctl.h b/sys/cam/ctl/ctl.h
index 0f7656fc918a..da382e6dbf3b 100644
--- a/sys/cam/ctl/ctl.h
+++ b/sys/cam/ctl/ctl.h
@@ -120,7 +120,8 @@ typedef enum {
CTL_UA_RES_PREEMPT = 0x0200,
CTL_UA_RES_RELEASE = 0x0400,
CTL_UA_REG_PREEMPT = 0x0800,
- CTL_UA_ASYM_ACC_CHANGE = 0x1000
+ CTL_UA_ASYM_ACC_CHANGE = 0x1000,
+ CTL_UA_CAPACITY_CHANGED = 0x2000
} ctl_ua_type;
#ifdef _KERNEL
diff --git a/sys/cam/ctl/ctl_backend.h b/sys/cam/ctl/ctl_backend.h
index e33b42b8cb56..c3798e423f4e 100644
--- a/sys/cam/ctl/ctl_backend.h
+++ b/sys/cam/ctl/ctl_backend.h
@@ -280,6 +280,11 @@ int ctl_lun_power_lock(struct ctl_be_lun *be_lun, struct ctl_nexus *nexus,
int ctl_lun_offline(struct ctl_be_lun *be_lun);
int ctl_lun_online(struct ctl_be_lun *be_lun);
+/*
+ * Let the backend notify the initiator about changed capacity.
+ */
+void ctl_lun_capacity_changed(struct ctl_be_lun *be_lun);
+
#endif /* _KERNEL */
#endif /* _CTL_BACKEND_H_ */
diff --git a/sys/cam/ctl/ctl_backend_block.c b/sys/cam/ctl/ctl_backend_block.c
index 320830427dba..cbd3c337e612 100644
--- a/sys/cam/ctl/ctl_backend_block.c
+++ b/sys/cam/ctl/ctl_backend_block.c
@@ -1,8 +1,12 @@
/*-
* Copyright (c) 2003 Silicon Graphics International Corp.
* Copyright (c) 2009-2011 Spectra Logic Corporation
+ * Copyright (c) 2012 The FreeBSD Foundation
* All rights reserved.
*
+ * Portions of this software were developed by Edward Tomasz Napierala
+ * under sponsorship from the FreeBSD Foundation.
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@@ -250,6 +254,12 @@ static int ctl_be_block_create(struct ctl_be_block_softc *softc,
struct ctl_lun_req *req);
static int ctl_be_block_rm(struct ctl_be_block_softc *softc,
struct ctl_lun_req *req);
+static int ctl_be_block_modify_file(struct ctl_be_block_lun *be_lun,
+ struct ctl_lun_req *req);
+static int ctl_be_block_modify_dev(struct ctl_be_block_lun *be_lun,
+ struct ctl_lun_req *req);
+static int ctl_be_block_modify(struct ctl_be_block_softc *softc,
+ struct ctl_lun_req *req);
static void ctl_be_block_lun_shutdown(void *be_lun);
static void ctl_be_block_lun_config_status(void *be_lun,
ctl_lun_config_status status);
@@ -1263,6 +1273,9 @@ ctl_be_block_ioctl(struct cdev *dev, u_long cmd, caddr_t addr,
case CTL_LUNREQ_RM:
error = ctl_be_block_rm(softc, lun_req);
break;
+ case CTL_LUNREQ_MODIFY:
+ error = ctl_be_block_modify(softc, lun_req);
+ break;
default:
lun_req->status = CTL_LUN_ERROR;
snprintf(lun_req->error_str, sizeof(lun_req->error_str),
@@ -1321,7 +1334,10 @@ ctl_be_block_open_file(struct ctl_be_block_lun *be_lun, struct ctl_lun_req *req)
file_data->cred = crhold(curthread->td_ucred);
- be_lun->size_bytes = vattr.va_size;
+ if (params->lun_size_bytes != 0)
+ be_lun->size_bytes = params->lun_size_bytes;
+ else
+ be_lun->size_bytes = vattr.va_size;
/*
* We set the multi thread flag for file operations because all
* filesystems (in theory) are capable of allowing multiple readers
@@ -1446,15 +1462,27 @@ ctl_be_block_open_dev(struct ctl_be_block_lun *be_lun, struct ctl_lun_req *req)
curthread);
if (error) {
snprintf(req->error_str, sizeof(req->error_str),
- "%s: error %d returned for DIOCGMEDIASIZE ioctl "
- "on %s!", __func__, error, be_lun->dev_path);
+ "%s: error %d returned for DIOCGMEDIASIZE "
+ " ioctl on %s!", __func__, error,
+ be_lun->dev_path);
return (error);
}
- return (0);
+ if (params->lun_size_bytes != 0) {
+ if (params->lun_size_bytes > be_lun->size_bytes) {
+ snprintf(req->error_str, sizeof(req->error_str),
+ "%s: requested LUN size %ju > backing device "
+ "size %ju", __func__,
+ (uintmax_t)params->lun_size_bytes,
+ (uintmax_t)be_lun->size_bytes);
+ return (EINVAL);
+ }
-}
+ be_lun->size_bytes = params->lun_size_bytes;
+ }
+ return (0);
+}
static int
ctl_be_block_close(struct ctl_be_block_lun *be_lun)
@@ -1599,7 +1627,6 @@ ctl_be_block_open(struct ctl_be_block_softc *softc,
be_lun->size_blocks = be_lun->size_bytes >> be_lun->blocksize_shift;
return (0);
-
}
static int
@@ -2007,6 +2034,155 @@ bailout_error:
return (0);
}
+static int
+ctl_be_block_modify_file(struct ctl_be_block_lun *be_lun,
+ struct ctl_lun_req *req)
+{
+ struct vattr vattr;
+ int error;
+ struct ctl_lun_modify_params *params;
+
+ params = &req->reqdata.modify;
+
+ if (params->lun_size_bytes != 0) {
+ be_lun->size_bytes = params->lun_size_bytes;
+ } else {
+ error = VOP_GETATTR(be_lun->vn, &vattr, curthread->td_ucred);
+ if (error != 0) {
+ snprintf(req->error_str, sizeof(req->error_str),
+ "error calling VOP_GETATTR() for file %s",
+ be_lun->dev_path);
+ return (error);
+ }
+
+ be_lun->size_bytes = vattr.va_size;
+ }
+
+ return (0);
+}
+
+static int
+ctl_be_block_modify_dev(struct ctl_be_block_lun *be_lun,
+ struct ctl_lun_req *req)
+{
+ struct cdev *dev;
+ struct cdevsw *devsw;
+ int error;
+ struct ctl_lun_modify_params *params;
+ uint64_t size_bytes;
+
+ params = &req->reqdata.modify;
+
+ dev = be_lun->vn->v_rdev;
+ devsw = dev->si_devsw;
+ if (!devsw->d_ioctl) {
+ snprintf(req->error_str, sizeof(req->error_str),
+ "%s: no d_ioctl for device %s!", __func__,
+ be_lun->dev_path);
+ return (ENODEV);
+ }
+
+ error = devsw->d_ioctl(dev, DIOCGMEDIASIZE,
+ (caddr_t)&size_bytes, FREAD,
+ curthread);
+ if (error) {
+ snprintf(req->error_str, sizeof(req->error_str),
+ "%s: error %d returned for DIOCGMEDIASIZE ioctl "
+ "on %s!", __func__, error, be_lun->dev_path);
+ return (error);
+ }
+
+ if (params->lun_size_bytes != 0) {
+ if (params->lun_size_bytes > size_bytes) {
+ snprintf(req->error_str, sizeof(req->error_str),
+ "%s: requested LUN size %ju > backing device "
+ "size %ju", __func__,
+ (uintmax_t)params->lun_size_bytes,
+ (uintmax_t)size_bytes);
+ return (EINVAL);
+ }
+
+ be_lun->size_bytes = params->lun_size_bytes;
+ } else {
+ be_lun->size_bytes = size_bytes;
+ }
+
+ return (0);
+}
+
+static int
+ctl_be_block_modify(struct ctl_be_block_softc *softc, struct ctl_lun_req *req)
+{
+ struct ctl_lun_modify_params *params;
+ struct ctl_be_block_lun *be_lun;
+ int vfs_is_locked, error;
+
+ params = &req->reqdata.modify;
+
+ mtx_lock(&softc->lock);
+
+ be_lun = NULL;
+
+ STAILQ_FOREACH(be_lun, &softc->lun_list, links) {
+ if (be_lun->ctl_be_lun.lun_id == params->lun_id)
+ break;
+ }
+ mtx_unlock(&softc->lock);
+
+ if (be_lun == NULL) {
+ snprintf(req->error_str, sizeof(req->error_str),
+ "%s: LUN %u is not managed by the block backend",
+ __func__, params->lun_id);
+ goto bailout_error;
+ }
+
+ if (params->lun_size_bytes != 0) {
+ if (params->lun_size_bytes < be_lun->blocksize) {
+ snprintf(req->error_str, sizeof(req->error_str),
+ "%s: LUN size %ju < blocksize %u", __func__,
+ params->lun_size_bytes, be_lun->blocksize);
+ goto bailout_error;
+ }
+ }
+
+ vfs_is_locked = VFS_LOCK_GIANT(be_lun->vn->v_mount);
+ vn_lock(be_lun->vn, LK_SHARED | LK_RETRY);
+
+ if (be_lun->vn->v_type == VREG)
+ error = ctl_be_block_modify_file(be_lun, req);
+ else
+ error = ctl_be_block_modify_dev(be_lun, req);
+
+ VOP_UNLOCK(be_lun->vn, 0);
+ VFS_UNLOCK_GIANT(vfs_is_locked);
+
+ if (error != 0)
+ goto bailout_error;
+
+ be_lun->size_blocks = be_lun->size_bytes >> be_lun->blocksize_shift;
+
+ /*
+ * The maximum LBA is the size - 1.
+ *
+ * XXX: Note that this field is being updated without locking,
+ * which might cause problems on 32-bit architectures.
+ */
+ be_lun->ctl_be_lun.maxlba = be_lun->size_blocks - 1;
+ ctl_lun_capacity_changed(&be_lun->ctl_be_lun);
+
+ /* Tell the user the exact size we ended up using */
+ params->lun_size_bytes = be_lun->size_bytes;
+
+ req->status = CTL_LUN_OK;
+
+ return (0);
+
+bailout_error:
+ req->status = CTL_LUN_ERROR;
+
+ return (0);
+}
+
static void
ctl_be_block_lun_shutdown(void *be_lun)
{
diff --git a/sys/cam/ctl/ctl_backend_ramdisk.c b/sys/cam/ctl/ctl_backend_ramdisk.c
index 88b5008197cf..b1d450212fda 100644
--- a/sys/cam/ctl/ctl_backend_ramdisk.c
+++ b/sys/cam/ctl/ctl_backend_ramdisk.c
@@ -1,7 +1,11 @@
/*-
* Copyright (c) 2003, 2008 Silicon Graphics International Corp.
+ * Copyright (c) 2012 The FreeBSD Foundation
* All rights reserved.
*
+ * Portions of this software were developed by Edward Tomasz Napierala
+ * under sponsorship from the FreeBSD Foundation.
+ *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
@@ -102,6 +106,8 @@ static int ctl_backend_ramdisk_rm(struct ctl_be_ramdisk_softc *softc,
struct ctl_lun_req *req);
static int ctl_backend_ramdisk_create(struct ctl_be_ramdisk_softc *softc,
struct ctl_lun_req *req, int do_wait);
+static int ctl_backend_ramdisk_modify(struct ctl_be_ramdisk_softc *softc,
+ struct ctl_lun_req *req);
static void ctl_backend_ramdisk_lun_shutdown(void *be_lun);
static void ctl_backend_ramdisk_lun_config_status(void *be_lun,
ctl_lun_config_status status);
@@ -376,6 +382,9 @@ ctl_backend_ramdisk_ioctl(struct cdev *dev, u_long cmd, caddr_t addr,
case CTL_LUNREQ_RM:
retval = ctl_backend_ramdisk_rm(softc, lun_req);
break;
+ case CTL_LUNREQ_MODIFY:
+ retval = ctl_backend_ramdisk_modify(softc, lun_req);
+ break;
default:
lun_req->status = CTL_LUN_ERROR;
snprintf(lun_req->error_str, sizeof(lun_req->error_str),
@@ -666,6 +675,73 @@ bailout_error:
return (retval);
}
+static int
+ctl_backend_ramdisk_modify(struct ctl_be_ramdisk_softc *softc,
+ struct ctl_lun_req *req)
+{
+ struct ctl_be_ramdisk_lun *be_lun;
+ struct ctl_lun_modify_params *params;
+ uint32_t blocksize;
+
+ params = &req->reqdata.modify;
+
+ be_lun = NULL;
+
+ mtx_lock(&softc->lock);
+ STAILQ_FOREACH(be_lun, &softc->lun_list, links) {
+ if (be_lun->ctl_be_lun.lun_id == params->lun_id)
+ break;
+ }
+ mtx_unlock(&softc->lock);
+
+ if (be_lun == NULL) {
+ snprintf(req->error_str, sizeof(req->error_str),
+ "%s: LUN %u is not managed by the ramdisk backend",
+ __func__, params->lun_id);
+ goto bailout_error;
+ }
+
+ if (params->lun_size_bytes == 0) {
+ snprintf(req->error_str, sizeof(req->error_str),
+ "%s: LUN size \"auto\" not supported "
+ "by the ramdisk backend", __func__);
+ goto bailout_error;
+ }
+
+ blocksize = be_lun->ctl_be_lun.blocksize;
+
+ if (params->lun_size_bytes < blocksize) {
+ snprintf(req->error_str, sizeof(req->error_str),
+ "%s: LUN size %ju < blocksize %u", __func__,
+ params->lun_size_bytes, blocksize);
+ goto bailout_error;
+ }
+
+ be_lun->size_blocks = params->lun_size_bytes / blocksize;
+ be_lun->size_bytes = be_lun->size_blocks * blocksize;
+
+ /*
+ * The maximum LBA is the size - 1.
+ *
+ * XXX: Note that this field is being updated without locking,
+ * which might cause problems on 32-bit architectures.
+ */
+ be_lun->ctl_be_lun.maxlba = be_lun->size_blocks - 1;
+ ctl_lun_capacity_changed(&be_lun->ctl_be_lun);
+
+ /* Tell the user the exact size we ended up using */
+ params->lun_size_bytes = be_lun->size_bytes;
+
+ req->status = CTL_LUN_OK;
+
+ return (0);
+
+bailout_error:
+ req->status = CTL_LUN_ERROR;
+
+ return (0);
+}
+
static void
ctl_backend_ramdisk_lun_shutdown(void *be_lun)
{
diff --git a/sys/cam/ctl/ctl_error.c b/sys/cam/ctl/ctl_error.c
index 617a5abad59b..bd9321f4ade9 100644
--- a/sys/cam/ctl/ctl_error.c
+++ b/sys/cam/ctl/ctl_error.c
@@ -454,6 +454,11 @@ ctl_build_ua(ctl_ua_type ua_type, struct scsi_sense_data *sense,
asc = 0x2A;
ascq = 0x06;
break;
+ case CTL_UA_CAPACITY_CHANGED:
+ /* 2Ah/09n CAPACITY DATA HAS CHANGED */
+ asc = 0x2A;
+ ascq = 0x09;
+ break;
default:
ua_to_build = CTL_UA_NONE;
return (ua_to_build);
diff --git a/sys/cam/ctl/ctl_ioctl.h b/sys/cam/ctl/ctl_ioctl.h
index 7ca78ebe587e..bc6342a0e409 100644
--- a/sys/cam/ctl/ctl_ioctl.h
+++ b/sys/cam/ctl/ctl_ioctl.h
@@ -397,7 +397,8 @@ struct ctl_be_arg {
typedef enum {
CTL_LUNREQ_CREATE,
- CTL_LUNREQ_RM
+ CTL_LUNREQ_RM,
+ CTL_LUNREQ_MODIFY,
} ctl_lunreq_type;
@@ -471,12 +472,27 @@ struct ctl_lun_rm_params {
};
/*
+ * LUN modification parameters:
+ *
+ * lun_id: The number of the LUN to modify. This must be set.
+ * The LUN must be backed by the given backend.
+ *
+ * lun_size_bytes: The size of the LUN in bytes. If zero, update
+ * the size using the backing file size, if possible.
+ */
+struct ctl_lun_modify_params {
+ uint32_t lun_id;
+ uint64_t lun_size_bytes;
+};
+
+/*
* Union of request type data. Fill in the appropriate union member for
* the request type.
*/
union ctl_lunreq_data {
struct ctl_lun_create_params create;
struct ctl_lun_rm_params rm;
+ struct ctl_lun_modify_params modify;
};
/*