diff options
author | Justin T. Gibbs <gibbs@FreeBSD.org> | 2011-06-14 17:10:32 +0000 |
---|---|---|
committer | Justin T. Gibbs <gibbs@FreeBSD.org> | 2011-06-14 17:10:32 +0000 |
commit | 416494d7c915a8d33f4f3e10ed61b9b079d03076 (patch) | |
tree | 2ba93494158658cf752e99e3930ef38a4872311c /sys/cam/scsi | |
parent | f528c3fdbc7a5a86413363b8a703a5f74c407953 (diff) | |
download | src-416494d7c915a8d33f4f3e10ed61b9b079d03076.tar.gz src-416494d7c915a8d33f4f3e10ed61b9b079d03076.zip |
Plumb device physical path reporting from CAM devices, through GEOM and
DEVFS, and make it accessible via the diskinfo utility.
Extend GEOM's generic attribute query mechanism into generic disk consumers.
sys/geom/geom_disk.c:
sys/geom/geom_disk.h:
sys/cam/scsi/scsi_da.c:
sys/cam/ata/ata_da.c:
- Allow disk providers to implement a new method which can override
the default BIO_GETATTR response, d_getattr(struct bio *). This
function returns -1 if not handled, otherwise it returns 0 or an
errno to be passed to g_io_deliver().
sys/cam/scsi/scsi_da.c:
sys/cam/ata/ata_da.c:
- Don't copy the serial number to dp->d_ident anymore, as the CAM XPT
is now responsible for returning this information via
d_getattr()->(a)dagetattr()->xpt_getatr().
sys/geom/geom_dev.c:
- Implement a new ioctl, DIOCGPHYSPATH, which returns the GEOM
attribute "GEOM::physpath", if possible. If the attribute request
returns a zero-length string, ENOENT is returned.
usr.sbin/diskinfo/diskinfo.c:
- If the DIOCGPHYSPATH ioctl is successful, report physical path
data when diskinfo is executed with the '-v' option.
Submitted by: will
Reviewed by: gibbs
Sponsored by: Spectra Logic Corporation
Add generic attribute change notification support to GEOM.
sys/sys/geom/geom.h:
Add a new attrchanged method field to both g_class
and g_geom.
sys/sys/geom/geom.h:
sys/geom/geom_event.c:
- Provide the g_attr_changed() function that providers
can use to advertise attribute changes.
- Perform delivery of attribute change notifications
from a thread context via the standard GEOM event
mechanism.
sys/geom/geom_subr.c:
Inherit the attrchanged method from class to geom (class instance).
sys/geom/geom_disk.c:
Provide disk_attr_changed() to provide g_attr_changed() access
to consumers of the disk API.
sys/cam/scsi/scsi_pass.c:
sys/cam/scsi/scsi_da.c:
sys/geom/geom_dev.c:
sys/geom/geom_disk.c:
Use attribute changed events to track updates to physical path
information.
sys/cam/scsi/scsi_da.c:
Add AC_ADVINFO_CHANGED to the registered asynchronous CAM
events for this driver. When this event occurs, and
the updated buffer type references our physical path
attribute, emit a GEOM attribute changed event via the
disk_attr_changed() API.
sys/cam/scsi/scsi_pass.c:
Add AC_ADVINFO_CHANGED to the registered asynchronous CAM
events for this driver. When this event occurs, update
the physical patch devfs alias for this pass instance.
Submitted by: gibbs
Sponsored by: Spectra Logic Corporation
Notes
Notes:
svn path=/head/; revision=223089
Diffstat (limited to 'sys/cam/scsi')
-rw-r--r-- | sys/cam/scsi/scsi_da.c | 67 | ||||
-rw-r--r-- | sys/cam/scsi/scsi_pass.c | 75 |
2 files changed, 120 insertions, 22 deletions
diff --git a/sys/cam/scsi/scsi_da.c b/sys/cam/scsi/scsi_da.c index b6f7cf5960ce..a4363186a0b8 100644 --- a/sys/cam/scsi/scsi_da.c +++ b/sys/cam/scsi/scsi_da.c @@ -44,6 +44,7 @@ __FBSDID("$FreeBSD$"); #include <sys/eventhandler.h> #include <sys/malloc.h> #include <sys/cons.h> +#include <geom/geom.h> #include <geom/geom_disk.h> #endif /* _KERNEL */ @@ -933,6 +934,25 @@ dadump(void *arg, void *virtual, vm_offset_t physical, off_t offset, size_t leng return (0); } +static int +dagetattr(struct bio *bp) +{ + int ret = -1; + struct cam_periph *periph; + + if (bp->bio_disk == NULL || bp->bio_disk->d_drv1 == NULL) + return ENXIO; + periph = (struct cam_periph *)bp->bio_disk->d_drv1; + if (periph->path == NULL) + return ENXIO; + + ret = xpt_getattr(bp->bio_data, bp->bio_length, bp->bio_attribute, + periph->path); + if (ret == 0) + bp->bio_completed = bp->bio_length; + return ret; +} + static void dainit(void) { @@ -1046,6 +1066,20 @@ daasync(void *callback_arg, u_int32_t code, && status != CAM_REQ_INPROG) printf("daasync: Unable to attach to new device " "due to status 0x%x\n", status); + return; + } + case AC_ADVINFO_CHANGED: + { + uintptr_t buftype; + + buftype = (uintptr_t)arg; + if (buftype == CDAI_TYPE_PHYS_PATH) { + struct da_softc *softc; + + softc = periph->softc; + disk_attr_changed(softc->disk, "GEOM::physpath", + M_NOWAIT); + } break; } case AC_SENT_BDR: @@ -1233,17 +1267,6 @@ daregister(struct cam_periph *periph, void *arg) TASK_INIT(&softc->sysctl_task, 0, dasysctlinit, periph); /* - * Add async callbacks for bus reset and - * bus device reset calls. I don't bother - * checking if this fails as, in most cases, - * the system will function just fine without - * them and the only alternative would be to - * not attach the device on failure. - */ - xpt_register_async(AC_SENT_BDR | AC_BUS_RESET | AC_LOST_DEVICE, - daasync, periph, periph->path); - - /* * Take an exclusive refcount on the periph while dastart is called * to finish the probe. The reference will be dropped in dadone at * the end of probe. @@ -1303,6 +1326,7 @@ daregister(struct cam_periph *periph, void *arg) softc->disk->d_close = daclose; softc->disk->d_strategy = dastrategy; softc->disk->d_dump = dadump; + softc->disk->d_getattr = dagetattr; softc->disk->d_name = "da"; softc->disk->d_drv1 = periph; if (cpi.maxio == 0) @@ -1315,8 +1339,6 @@ daregister(struct cam_periph *periph, void *arg) softc->disk->d_flags = 0; if ((softc->quirks & DA_Q_NO_SYNC_CACHE) == 0) softc->disk->d_flags |= DISKFLAG_CANFLUSHCACHE; - strlcpy(softc->disk->d_ident, cgd->serial_num, - MIN(sizeof(softc->disk->d_ident), cgd->serial_num_len + 1)); cam_strvis(softc->disk->d_descr, cgd->inq_data.vendor, sizeof(cgd->inq_data.vendor), sizeof(softc->disk->d_descr)); strlcat(softc->disk->d_descr, " ", sizeof(softc->disk->d_descr)); @@ -1330,6 +1352,25 @@ daregister(struct cam_periph *periph, void *arg) disk_create(softc->disk, DISK_VERSION); mtx_lock(periph->sim->mtx); + /* + * Add async callbacks for events of interest. + * I don't bother checking if this fails as, + * in most cases, the system will function just + * fine without them and the only alternative + * would be to not attach the device on failure. + */ + xpt_register_async(AC_SENT_BDR | AC_BUS_RESET + | AC_LOST_DEVICE | AC_ADVINFO_CHANGED, + daasync, periph, periph->path); + + /* + * Emit an attribute changed notification just in case + * physical path information arrived before our async + * event handler was registered, but after anyone attaching + * to our disk device polled it. + */ + disk_attr_changed(softc->disk, "GEOM::physpath", M_NOWAIT); + xpt_schedule(periph, CAM_PRIORITY_DEV); return(CAM_REQ_CMP); diff --git a/sys/cam/scsi/scsi_pass.c b/sys/cam/scsi/scsi_pass.c index 5b24d8238c23..a124468a56dc 100644 --- a/sys/cam/scsi/scsi_pass.c +++ b/sys/cam/scsi/scsi_pass.c @@ -39,6 +39,7 @@ __FBSDID("$FreeBSD$"); #include <sys/errno.h> #include <sys/devicestat.h> #include <sys/proc.h> +#include <sys/taskqueue.h> #include <cam/cam.h> #include <cam/cam_ccb.h> @@ -70,12 +71,14 @@ typedef enum { #define ccb_bp ppriv_ptr1 struct pass_softc { - pass_state state; - pass_flags flags; - u_int8_t pd_type; - union ccb saved_ccb; - struct devstat *device_stats; - struct cdev *dev; + pass_state state; + pass_flags flags; + u_int8_t pd_type; + union ccb saved_ccb; + struct devstat *device_stats; + struct cdev *dev; + struct cdev *alias_dev; + struct task add_physpath_task; }; @@ -88,6 +91,7 @@ static periph_ctor_t passregister; static periph_oninv_t passoninvalidate; static periph_dtor_t passcleanup; static periph_start_t passstart; +static void pass_add_physpath(void *context, int pending); static void passasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg); static void passdone(struct cam_periph *periph, @@ -168,17 +172,45 @@ passcleanup(struct cam_periph *periph) if (bootverbose) xpt_print(periph->path, "removing device entry\n"); devstat_remove_entry(softc->device_stats); + cam_periph_unlock(periph); + taskqueue_drain(taskqueue_thread, &softc->add_physpath_task); + /* * passcleanup() is indirectly a d_close method via passclose, * so using destroy_dev(9) directly can result in deadlock. */ destroy_dev_sched(softc->dev); cam_periph_lock(periph); + free(softc, M_DEVBUF); } static void +pass_add_physpath(void *context, int pending) +{ + struct cam_periph *periph; + struct pass_softc *softc; + char *physpath; + + /* + * If we have one, create a devfs alias for our + * physical path. + */ + periph = context; + softc = periph->softc; + physpath = malloc(MAXPATHLEN, M_DEVBUF, M_WAITOK); + if (xpt_getattr(physpath, MAXPATHLEN, + "GEOM::physpath", periph->path) == 0 + && strlen(physpath) != 0) { + + make_dev_physpath_alias(MAKEDEV_WAITOK, &softc->alias_dev, + softc->dev, softc->alias_dev, physpath); + } + free(physpath, M_DEVBUF); +} + +static void passasync(void *callback_arg, u_int32_t code, struct cam_path *path, void *arg) { @@ -219,6 +251,20 @@ passasync(void *callback_arg, u_int32_t code, break; } + case AC_ADVINFO_CHANGED: + { + uintptr_t buftype; + + buftype = (uintptr_t)arg; + if (buftype == CDAI_TYPE_PHYS_PATH) { + struct pass_softc *softc; + + softc = (struct pass_softc *)periph->softc; + taskqueue_enqueue(taskqueue_thread, + &softc->add_physpath_task); + } + break; + } default: cam_periph_async(periph, code, path, arg); break; @@ -292,11 +338,22 @@ passregister(struct cam_periph *periph, void *arg) mtx_lock(periph->sim->mtx); softc->dev->si_drv1 = periph; + TASK_INIT(&softc->add_physpath_task, /*priority*/0, + pass_add_physpath, periph); + + /* + * See if physical path information is already available. + */ + taskqueue_enqueue(taskqueue_thread, &softc->add_physpath_task); + /* - * Add an async callback so that we get - * notified if this device goes away. + * Add an async callback so that we get notified if + * this device goes away or its physical path + * (stored in the advanced info data of the EDT) has + * changed. */ - xpt_register_async(AC_LOST_DEVICE, passasync, periph, periph->path); + xpt_register_async(AC_LOST_DEVICE | AC_ADVINFO_CHANGED, + passasync, periph, periph->path); if (bootverbose) xpt_announce_periph(periph, NULL); |