From 0e3aa3c51edea37dfa55265cc8ff7fe81aefbfaa Mon Sep 17 00:00:00 2001 From: Alexander Motin Date: Tue, 17 Nov 2009 20:49:26 +0000 Subject: MFC r198748, r198782: Fix reference counting bug, when device unreferenced before invalidated. To do it, do not handle validity flag as another reference, but explicitly modify reference count each time flag is modified. The async callback could free the device. If it is a broadcast async, it doesn't hold device reference, so take our own reference. --- sys/cam/ata/ata_xpt.c | 7 +++++- sys/cam/cam_xpt.c | 53 +++++++++++++++++++++++++++------------------- sys/cam/cam_xpt_internal.h | 6 +++--- sys/cam/scsi/scsi_xpt.c | 22 ++++++++++++++----- 4 files changed, 57 insertions(+), 31 deletions(-) (limited to 'sys/cam') diff --git a/sys/cam/ata/ata_xpt.c b/sys/cam/ata/ata_xpt.c index 9e781cd72d6d..065f3ee70a9d 100644 --- a/sys/cam/ata/ata_xpt.c +++ b/sys/cam/ata/ata_xpt.c @@ -733,6 +733,7 @@ noerror: case PROBE_SET_MULTI: if (periph->path->device->flags & CAM_DEV_UNCONFIGURED) { path->device->flags &= ~CAM_DEV_UNCONFIGURED; + xpt_acquire_device(path->device); done_ccb->ccb_h.func_code = XPT_GDEV_TYPE; xpt_action(done_ccb); xpt_async(AC_FOUND_DEVICE, done_ccb->ccb_h.path, @@ -777,6 +778,7 @@ noerror: ata_device_transport(path); if (periph->path->device->flags & CAM_DEV_UNCONFIGURED) { path->device->flags &= ~CAM_DEV_UNCONFIGURED; + xpt_acquire_device(path->device); done_ccb->ccb_h.func_code = XPT_GDEV_TYPE; xpt_action(done_ccb); xpt_async(AC_FOUND_DEVICE, done_ccb->ccb_h.path, done_ccb); @@ -810,6 +812,7 @@ noerror: path->device->flags |= CAM_DEV_IDENTIFY_DATA_VALID; if (periph->path->device->flags & CAM_DEV_UNCONFIGURED) { path->device->flags &= ~CAM_DEV_UNCONFIGURED; + xpt_acquire_device(path->device); done_ccb->ccb_h.func_code = XPT_GDEV_TYPE; xpt_action(done_ccb); xpt_async(AC_FOUND_DEVICE, done_ccb->ccb_h.path, @@ -1485,8 +1488,10 @@ ata_dev_async(u_int32_t async_code, struct cam_eb *bus, struct cam_et *target, CAM_EXPECT_INQ_CHANGE, NULL); } xpt_release_path(&newpath); - } else if (async_code == AC_LOST_DEVICE) { + } else if (async_code == AC_LOST_DEVICE && + (device->flags & CAM_DEV_UNCONFIGURED) == 0) { device->flags |= CAM_DEV_UNCONFIGURED; + xpt_release_device(device); } else if (async_code == AC_TRANSFER_NEG) { struct ccb_trans_settings *settings; diff --git a/sys/cam/cam_xpt.c b/sys/cam/cam_xpt.c index 187683ed8814..8c568ecc452e 100644 --- a/sys/cam/cam_xpt.c +++ b/sys/cam/cam_xpt.c @@ -217,9 +217,7 @@ static void xpt_release_devq_device(struct cam_ed *dev, u_int count, int run_queue); static struct cam_et* xpt_alloc_target(struct cam_eb *bus, target_id_t target_id); -static void xpt_release_target(struct cam_eb *bus, struct cam_et *target); -static void xpt_release_device(struct cam_eb *bus, struct cam_et *target, - struct cam_ed *device); +static void xpt_release_target(struct cam_et *target); static struct cam_eb* xpt_find_bus(path_id_t path_id); static struct cam_et* @@ -3521,9 +3519,9 @@ xpt_compile_path(struct cam_path *new_path, struct cam_periph *perph, CAM_DEBUG(new_path, CAM_DEBUG_TRACE, ("xpt_compile_path\n")); } else { if (device != NULL) - xpt_release_device(bus, target, device); + xpt_release_device(device); if (target != NULL) - xpt_release_target(bus, target); + xpt_release_target(target); if (bus != NULL) xpt_release_bus(bus); } @@ -3535,11 +3533,11 @@ xpt_release_path(struct cam_path *path) { CAM_DEBUG(path, CAM_DEBUG_TRACE, ("xpt_release_path\n")); if (path->device != NULL) { - xpt_release_device(path->bus, path->target, path->device); + xpt_release_device(path->device); path->device = NULL; } if (path->target != NULL) { - xpt_release_target(path->bus, path->target); + xpt_release_target(path->target); path->target = NULL; } if (path->bus != NULL) { @@ -4024,13 +4022,19 @@ xpt_async(u_int32_t async_code, struct cam_path *path, void *async_arg) && path->device->lun_id != CAM_LUN_WILDCARD && device->lun_id != CAM_LUN_WILDCARD) continue; - + /* + * The async callback could free the device. + * If it is a broadcast async, it doesn't hold + * device reference, so take our own reference. + */ + xpt_acquire_device(device); (*(bus->xport->async))(async_code, bus, target, device, async_arg); xpt_async_bcast(&device->asyncs, async_code, path, async_arg); + xpt_release_device(device); } } @@ -4375,15 +4379,15 @@ xpt_alloc_target(struct cam_eb *bus, target_id_t target_id) } static void -xpt_release_target(struct cam_eb *bus, struct cam_et *target) +xpt_release_target(struct cam_et *target) { if ((--target->refcount == 0) && (TAILQ_FIRST(&target->ed_entries) == NULL)) { - TAILQ_REMOVE(&bus->et_entries, target, links); - bus->generation++; + TAILQ_REMOVE(&target->bus->et_entries, target, links); + target->bus->generation++; + xpt_release_bus(target->bus); free(target, M_CAMXPT); - xpt_release_bus(bus); } } @@ -4470,13 +4474,18 @@ xpt_alloc_device(struct cam_eb *bus, struct cam_et *target, lun_id_t lun_id) return (device); } -static void -xpt_release_device(struct cam_eb *bus, struct cam_et *target, - struct cam_ed *device) +void +xpt_acquire_device(struct cam_ed *device) +{ + + device->refcount++; +} + +void +xpt_release_device(struct cam_ed *device) { - if ((--device->refcount == 0) - && ((device->flags & CAM_DEV_UNCONFIGURED) != 0)) { + if (--device->refcount == 0) { struct cam_devq *devq; if (device->alloc_ccb_entry.pinfo.index != CAM_UNQUEUED_INDEX @@ -4486,16 +4495,16 @@ xpt_release_device(struct cam_eb *bus, struct cam_et *target, if ((device->flags & CAM_DEV_REL_TIMEOUT_PENDING) != 0) callout_stop(&device->callout); - TAILQ_REMOVE(&target->ed_entries, device,links); - target->generation++; - bus->sim->max_ccbs -= device->ccbq.devq_openings; + TAILQ_REMOVE(&device->target->ed_entries, device,links); + device->target->generation++; + device->target->bus->sim->max_ccbs -= device->ccbq.devq_openings; /* Release our slot in the devq */ - devq = bus->sim->devq; + devq = device->target->bus->sim->devq; cam_devq_resize(devq, devq->alloc_queue.array_size - 1); camq_fini(&device->drvq); cam_ccbq_fini(&device->ccbq); + xpt_release_target(device->target); free(device, M_CAMXPT); - xpt_release_target(bus, target); } } diff --git a/sys/cam/cam_xpt_internal.h b/sys/cam/cam_xpt_internal.h index 9e5e3f1e9516..643ddf16f5b0 100644 --- a/sys/cam/cam_xpt_internal.h +++ b/sys/cam/cam_xpt_internal.h @@ -37,9 +37,7 @@ struct cam_ed; typedef struct cam_ed * (*xpt_alloc_device_func)(struct cam_eb *bus, struct cam_et *target, lun_id_t lun_id); -typedef void (*xpt_release_device_func)(struct cam_eb *bus, - struct cam_et *target, - struct cam_ed *device); +typedef void (*xpt_release_device_func)(struct cam_ed *device); typedef void (*xpt_action_func)(union ccb *start_ccb); typedef void (*xpt_dev_async_func)(u_int32_t async_code, struct cam_eb *bus, @@ -172,6 +170,8 @@ struct xpt_xport * ata_get_xport(void); struct cam_ed * xpt_alloc_device(struct cam_eb *bus, struct cam_et *target, lun_id_t lun_id); +void xpt_acquire_device(struct cam_ed *device); +void xpt_release_device(struct cam_ed *device); void xpt_run_dev_sendq(struct cam_eb *bus); int xpt_schedule_dev(struct camq *queue, cam_pinfo *dev_pinfo, u_int32_t new_priority); diff --git a/sys/cam/scsi/scsi_xpt.c b/sys/cam/scsi/scsi_xpt.c index a240db845cee..d780f9a90194 100644 --- a/sys/cam/scsi/scsi_xpt.c +++ b/sys/cam/scsi/scsi_xpt.c @@ -1076,8 +1076,10 @@ probedone(struct cam_periph *periph, union ccb *done_ccb) else PROBE_SET_ACTION(softc, PROBE_SERIAL_NUM_0); - path->device->flags &= ~CAM_DEV_UNCONFIGURED; - + if (path->device->flags & CAM_DEV_UNCONFIGURED) { + path->device->flags &= ~CAM_DEV_UNCONFIGURED; + xpt_acquire_device(path->device); + } xpt_release_ccb(done_ccb); xpt_schedule(periph, priority); return; @@ -1336,8 +1338,12 @@ probedone(struct cam_periph *periph, union ccb *done_ccb) CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("Leave Domain Validation\n")); } + if (path->device->flags & CAM_DEV_UNCONFIGURED) { + path->device->flags &= ~CAM_DEV_UNCONFIGURED; + xpt_acquire_device(path->device); + } path->device->flags &= - ~(CAM_DEV_UNCONFIGURED|CAM_DEV_IN_DV|CAM_DEV_DV_HIT_BOTTOM); + ~(CAM_DEV_IN_DV|CAM_DEV_DV_HIT_BOTTOM); if ((softc->flags & PROBE_NO_ANNOUNCE) == 0) { /* Inform the XPT that a new device has been found */ done_ccb->ccb_h.func_code = XPT_GDEV_TYPE; @@ -1387,8 +1393,12 @@ probedone(struct cam_periph *periph, union ccb *done_ccb) CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("Leave Domain Validation Successfully\n")); } + if (path->device->flags & CAM_DEV_UNCONFIGURED) { + path->device->flags &= ~CAM_DEV_UNCONFIGURED; + xpt_acquire_device(path->device); + } path->device->flags &= - ~(CAM_DEV_UNCONFIGURED|CAM_DEV_IN_DV|CAM_DEV_DV_HIT_BOTTOM); + ~(CAM_DEV_IN_DV|CAM_DEV_DV_HIT_BOTTOM); if ((softc->flags & PROBE_NO_ANNOUNCE) == 0) { /* Inform the XPT that a new device has been found */ done_ccb->ccb_h.func_code = XPT_GDEV_TYPE; @@ -2375,8 +2385,10 @@ scsi_dev_async(u_int32_t async_code, struct cam_eb *bus, struct cam_et *target, CAM_EXPECT_INQ_CHANGE, NULL); } xpt_release_path(&newpath); - } else if (async_code == AC_LOST_DEVICE) { + } else if (async_code == AC_LOST_DEVICE && + (device->flags & CAM_DEV_UNCONFIGURED) == 0) { device->flags |= CAM_DEV_UNCONFIGURED; + xpt_release_device(device); } else if (async_code == AC_TRANSFER_NEG) { struct ccb_trans_settings *settings; -- cgit v1.3