diff options
author | Edward Tomasz Napierala <trasz@FreeBSD.org> | 2012-03-06 13:43:57 +0000 |
---|---|---|
committer | Edward Tomasz Napierala <trasz@FreeBSD.org> | 2012-03-06 13:43:57 +0000 |
commit | 811772950f6233aa95be544ad5e7cb7002488d68 (patch) | |
tree | 21a7004b7ca48373596470e3a58af3c4b396cd5a /sys/cam/ctl | |
parent | 7dfd88318b2c2e2e105fa66fba408ea5c034aa05 (diff) | |
download | src-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.c | 23 | ||||
-rw-r--r-- | sys/cam/ctl/ctl.h | 3 | ||||
-rw-r--r-- | sys/cam/ctl/ctl_backend.h | 5 | ||||
-rw-r--r-- | sys/cam/ctl/ctl_backend_block.c | 188 | ||||
-rw-r--r-- | sys/cam/ctl/ctl_backend_ramdisk.c | 76 | ||||
-rw-r--r-- | sys/cam/ctl/ctl_error.c | 5 | ||||
-rw-r--r-- | sys/cam/ctl/ctl_ioctl.h | 18 |
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; }; /* |