aboutsummaryrefslogtreecommitdiff
path: root/sys/cam
diff options
context:
space:
mode:
Diffstat (limited to 'sys/cam')
-rw-r--r--sys/cam/scsi/scsi_cd.c50
-rw-r--r--sys/cam/scsi/scsi_da.c33
2 files changed, 68 insertions, 15 deletions
diff --git a/sys/cam/scsi/scsi_cd.c b/sys/cam/scsi/scsi_cd.c
index aafde556551f..c36511053db6 100644
--- a/sys/cam/scsi/scsi_cd.c
+++ b/sys/cam/scsi/scsi_cd.c
@@ -103,8 +103,7 @@ typedef enum {
CD_FLAG_RETRY_UA = 0x0200,
CD_FLAG_VALID_MEDIA = 0x0400,
CD_FLAG_VALID_TOC = 0x0800,
- CD_FLAG_SCTX_INIT = 0x1000,
- CD_FLAG_OPEN = 0x2000
+ CD_FLAG_SCTX_INIT = 0x1000
} cd_flags;
typedef enum {
@@ -358,6 +357,20 @@ cdinit(void)
}
}
+/*
+ * Callback from GEOM, called when it has finished cleaning up its
+ * resources.
+ */
+static void
+cddiskgonecb(struct disk *dp)
+{
+ struct cam_periph *periph;
+
+ periph = (struct cam_periph *)dp->d_drv1;
+
+ cam_periph_release(periph);
+}
+
static void
cdoninvalidate(struct cam_periph *periph)
{
@@ -389,7 +402,7 @@ cdoninvalidate(struct cam_periph *periph)
camq_remove(&softc->changer->devq, softc->pinfo.index);
disk_gone(softc->disk);
- xpt_print(periph->path, "lost device\n");
+ xpt_print(periph->path, "lost device, %d refs\n", periph->refcount);
}
static void
@@ -726,6 +739,7 @@ cdregister(struct cam_periph *periph, void *arg)
softc->disk->d_open = cdopen;
softc->disk->d_close = cdclose;
softc->disk->d_strategy = cdstrategy;
+ softc->disk->d_gone = cddiskgonecb;
softc->disk->d_ioctl = cdioctl;
softc->disk->d_name = "cd";
cam_strvis(softc->disk->d_descr, cgd->inq_data.vendor,
@@ -747,6 +761,19 @@ cdregister(struct cam_periph *periph, void *arg)
softc->disk->d_hba_device = cpi.hba_device;
softc->disk->d_hba_subvendor = cpi.hba_subvendor;
softc->disk->d_hba_subdevice = cpi.hba_subdevice;
+
+ /*
+ * Acquire a reference to the periph before we register with GEOM.
+ * We'll release this reference once GEOM calls us back (via
+ * dadiskgonecb()) telling us that our provider has been freed.
+ */
+ if (cam_periph_acquire(periph) != CAM_REQ_CMP) {
+ xpt_print(periph->path, "%s: lost periph during "
+ "registration!\n", __func__);
+ cam_periph_lock(periph);
+ return (CAM_REQ_CMP_ERR);
+ }
+
disk_create(softc->disk, DISK_VERSION);
cam_periph_lock(periph);
@@ -1000,14 +1027,14 @@ cdopen(struct disk *dp)
cam_periph_lock(periph);
if (softc->flags & CD_FLAG_INVALID) {
+ cam_periph_release_locked(periph);
cam_periph_unlock(periph);
- cam_periph_release(periph);
return(ENXIO);
}
if ((error = cam_periph_hold(periph, PRIBIO | PCATCH)) != 0) {
+ cam_periph_release_locked(periph);
cam_periph_unlock(periph);
- cam_periph_release(periph);
return (error);
}
@@ -1024,14 +1051,7 @@ cdopen(struct disk *dp)
CAM_DEBUG(periph->path, CAM_DEBUG_TRACE, ("leaving cdopen\n"));
cam_periph_unhold(periph);
- /* Closes aren't symmetrical with opens, so fix up the refcounting. */
- if ((softc->flags & CD_FLAG_OPEN) == 0) {
- softc->flags |= CD_FLAG_OPEN;
- cam_periph_unlock(periph);
- } else {
- cam_periph_unlock(periph);
- cam_periph_release(periph);
- }
+ cam_periph_unlock(periph);
return (0);
}
@@ -1070,11 +1090,11 @@ cdclose(struct disk *dp)
/*
* We'll check the media and toc again at the next open().
*/
- softc->flags &= ~(CD_FLAG_VALID_MEDIA|CD_FLAG_VALID_TOC|CD_FLAG_OPEN);
+ softc->flags &= ~(CD_FLAG_VALID_MEDIA|CD_FLAG_VALID_TOC);
cam_periph_unhold(periph);
+ cam_periph_release_locked(periph);
cam_periph_unlock(periph);
- cam_periph_release(periph);
return (0);
}
diff --git a/sys/cam/scsi/scsi_da.c b/sys/cam/scsi/scsi_da.c
index 30cd11b12b42..89115ed80153 100644
--- a/sys/cam/scsi/scsi_da.c
+++ b/sys/cam/scsi/scsi_da.c
@@ -1233,6 +1233,20 @@ dainit(void)
}
}
+/*
+ * Callback from GEOM, called when it has finished cleaning up its
+ * resources.
+ */
+static void
+dadiskgonecb(struct disk *dp)
+{
+ struct cam_periph *periph;
+
+ periph = (struct cam_periph *)dp->d_drv1;
+
+ cam_periph_release(periph);
+}
+
static void
daoninvalidate(struct cam_periph *periph)
{
@@ -1255,7 +1269,12 @@ daoninvalidate(struct cam_periph *periph)
bioq_flush(&softc->bio_queue, NULL, ENXIO);
bioq_flush(&softc->delete_queue, NULL, ENXIO);
+ /*
+ * Tell GEOM that we've gone away, we'll get a callback when it is
+ * done cleaning up its resources.
+ */
disk_gone(softc->disk);
+
xpt_print(periph->path, "lost device - %d outstanding, %d refs\n",
softc->outstanding_cmds, periph->refcount);
}
@@ -1633,6 +1652,7 @@ daregister(struct cam_periph *periph, void *arg)
softc->disk->d_strategy = dastrategy;
softc->disk->d_dump = dadump;
softc->disk->d_getattr = dagetattr;
+ softc->disk->d_gone = dadiskgonecb;
softc->disk->d_name = "da";
softc->disk->d_drv1 = periph;
if (cpi.maxio == 0)
@@ -1655,6 +1675,19 @@ daregister(struct cam_periph *periph, void *arg)
softc->disk->d_hba_device = cpi.hba_device;
softc->disk->d_hba_subvendor = cpi.hba_subvendor;
softc->disk->d_hba_subdevice = cpi.hba_subdevice;
+
+ /*
+ * Acquire a reference to the periph before we register with GEOM.
+ * We'll release this reference once GEOM calls us back (via
+ * dadiskgonecb()) telling us that our provider has been freed.
+ */
+ if (cam_periph_acquire(periph) != CAM_REQ_CMP) {
+ xpt_print(periph->path, "%s: lost periph during "
+ "registration!\n", __func__);
+ mtx_lock(periph->sim->mtx);
+ return (CAM_REQ_CMP_ERR);
+ }
+
disk_create(softc->disk, DISK_VERSION);
mtx_lock(periph->sim->mtx);