コード例 #1
0
ファイル: tcm_loop.c プロジェクト: JackWangCUMT/linux
static void tcm_loop_port_unlink(
    struct se_portal_group *se_tpg,
    struct se_lun *se_lun)
{
    struct scsi_device *sd;
    struct tcm_loop_hba *tl_hba;
    struct tcm_loop_tpg *tl_tpg;

    tl_tpg = container_of(se_tpg, struct tcm_loop_tpg, tl_se_tpg);
    tl_hba = tl_tpg->tl_hba;

    sd = scsi_device_lookup(tl_hba->sh, 0, tl_tpg->tl_tpgt,
                            se_lun->unpacked_lun);
    if (!sd) {
        pr_err("Unable to locate struct scsi_device for %d:%d:"
               "%d\n", 0, tl_tpg->tl_tpgt, se_lun->unpacked_lun);
        return;
    }
    /*
     * Remove Linux/SCSI struct scsi_device by HCTL
     */
    scsi_remove_device(sd);
    scsi_device_put(sd);

    atomic_dec(&tl_tpg->tl_tpg_port_count);
    smp_mb__after_atomic_dec();

    pr_debug("TCM_Loop_ConfigFS: Port Unlink Successful\n");
}
コード例 #2
0
static void channel_rescan(void *data)
{
    struct rescan_wrapper* rescan = (struct rescan_wrapper*)data;
    struct Scsi_Host *host;
    struct scsi_device *sdev = NULL;
    MV_U16 target;
    if (rescan->pAdapter->host[rescan->channelIndex] == NULL)
    {
        kfree(rescan);
        return;
    }
    host = rescan->pAdapter->host[rescan->channelIndex]->scsihost;
    down(&rescan->pAdapter->rescan_mutex);
    if (atomic_read(&rescan->pAdapter->stopped) > 0)
    {
        up(&rescan->pAdapter->rescan_mutex);
        kfree(rescan);
        return;
    }
    mvLogMsg(MV_IAL_LOG_ID, MV_DEBUG, "[%d %d] channel_rescan(): "
             "targets to add 0x%X, targets to remove 0x%X\n",
                                rescan->pAdapter->mvSataAdapter.adapterId,
                                rescan->channelIndex,
                                rescan->targetsToRemove,
                                rescan->targetsToAdd);

    for (target = 0; (rescan->targetsToRemove != 0) && (target < host->max_id); target++)
    {
        if (rescan->targetsToRemove & (1 << target))
        {
            sdev = scsi_device_lookup(host, 0, target, 0);
            if (sdev != NULL)
            {
                scsi_device_cancel(sdev, 0);
                scsi_remove_device(sdev);
                scsi_device_put(sdev);
            }
        }
    }
    sdev = NULL;
    for (target = 0; (rescan->targetsToAdd != 0) && (target < host->max_id); target++)
    {
        if (rescan->targetsToAdd & (1 << target))
        {
            sdev = scsi_add_device(host, 0, target, 0);
            if (NULL == sdev)
            {
                mvLogMsg(MV_IAL_LOG_ID,
                MV_DEBUG_ERROR,
                "[%d %d %d] Error adding scsi device\n",
                         rescan->pAdapter->mvSataAdapter.adapterId,
                         rescan->channelIndex,
                         target);
            }
        }
    }
    up(&rescan->pAdapter->rescan_mutex);
    kfree(rescan);
}
コード例 #3
0
ファイル: zfcp_sysfs.c プロジェクト: 3null/fastsocket
static ssize_t zfcp_sysfs_unit_remove_store(struct device *dev,
					    struct device_attribute *attr,
					    const char *buf, size_t count)
{
	struct zfcp_port *port = dev_get_drvdata(dev);
	struct zfcp_unit *unit;
	u64 fcp_lun;
	LIST_HEAD(unit_remove_lh);
	struct scsi_device *sdev;

	mutex_lock(&zfcp_data.config_mutex);
	if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_REMOVE) {
		mutex_unlock(&zfcp_data.config_mutex);
		return -EBUSY;
	}

	if (strict_strtoull(buf, 0, (unsigned long long *) &fcp_lun)) {
		mutex_unlock(&zfcp_data.config_mutex);
		return -EINVAL;
	}

	read_lock_irq(&zfcp_data.config_lock);
	unit = zfcp_get_unit_by_lun(port, fcp_lun);
	read_unlock_irq(&zfcp_data.config_lock);
	if (!unit) {
		mutex_unlock(&zfcp_data.config_mutex);
		return -ENXIO;
	}
	zfcp_unit_get(unit);
	mutex_unlock(&zfcp_data.config_mutex);

	sdev = scsi_device_lookup(port->adapter->scsi_host, 0,
				  port->starget_id,
				  scsilun_to_int((struct scsi_lun *)&fcp_lun));
	if (sdev) {
		scsi_remove_device(sdev);
		scsi_device_put(sdev);
	}

	mutex_lock(&zfcp_data.config_mutex);
	zfcp_unit_put(unit);
	if (atomic_read(&unit->refcount)) {
		mutex_unlock(&zfcp_data.config_mutex);
		return -ENXIO;
	}

	write_lock_irq(&zfcp_data.config_lock);
	atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status);
	list_move(&unit->list, &unit_remove_lh);
	write_unlock_irq(&zfcp_data.config_lock);
	mutex_unlock(&zfcp_data.config_mutex);

	zfcp_erp_unit_shutdown(unit, 0, "syurs_1", NULL);
	zfcp_erp_wait(unit->port->adapter);
	zfcp_unit_dequeue(unit);

	return (ssize_t)count;
}
コード例 #4
0
static void pvscsi_process_msg(const struct pvscsi_adapter *adapter,
			       const struct PVSCSIRingMsgDesc *e)
{
	struct PVSCSIRingsState *s = adapter->rings_state;
	struct Scsi_Host *host = adapter->host;
	struct scsi_device *sdev;

	printk(KERN_INFO "vmw_pvscsi: msg type: 0x%x - MSG RING: %u/%u (%u) \n",
	       e->type, s->msgProdIdx, s->msgConsIdx, s->msgNumEntriesLog2);

	BUILD_BUG_ON(PVSCSI_MSG_LAST != 2);

	if (e->type == PVSCSI_MSG_DEV_ADDED) {
		struct PVSCSIMsgDescDevStatusChanged *desc;
		desc = (struct PVSCSIMsgDescDevStatusChanged *)e;

		printk(KERN_INFO
		       "vmw_pvscsi: msg: device added at scsi%u:%u:%u\n",
		       desc->bus, desc->target, desc->lun[1]);

		if (!scsi_host_get(host))
			return;

		sdev = scsi_device_lookup(host, desc->bus, desc->target,
					  desc->lun[1]);
		if (sdev) {
			printk(KERN_INFO "vmw_pvscsi: device already exists\n");
			scsi_device_put(sdev);
		} else
			scsi_add_device(adapter->host, desc->bus,
					desc->target, desc->lun[1]);

		scsi_host_put(host);
	} else if (e->type == PVSCSI_MSG_DEV_REMOVED) {
		struct PVSCSIMsgDescDevStatusChanged *desc;
		desc = (struct PVSCSIMsgDescDevStatusChanged *)e;

		printk(KERN_INFO
		       "vmw_pvscsi: msg: device removed at scsi%u:%u:%u\n",
		       desc->bus, desc->target, desc->lun[1]);

		if (!scsi_host_get(host))
			return;

		sdev = scsi_device_lookup(host, desc->bus, desc->target,
					  desc->lun[1]);
		if (sdev) {
			scsi_remove_device(sdev);
			scsi_device_put(sdev);
		} else
			printk(KERN_INFO
			       "vmw_pvscsi: failed to lookup scsi%u:%u:%u\n",
			       desc->bus, desc->target, desc->lun[1]);

		scsi_host_put(host);
	}
}
コード例 #5
0
ファイル: hba_timer.c プロジェクト: ashang/xpenology-3.x
static void hba_proc_msg(struct mv_hba_msg *pmsg)
{
	PHBA_Extension phba;
	struct scsi_device *psdev;

	/* we don't do things without pmsg->data */
	if (NULL == pmsg->data)
		return;

	phba = (PHBA_Extension) pmsg->data;


	MV_DBG(DMSG_HBA, "__MV__ In hba_proc_msg.\n");

	MV_ASSERT(pmsg);

	switch (pmsg->msg) {
	case EVENT_DEVICE_ARRIVAL:
		if (scsi_add_device(phba->host, 0, pmsg->param, 0))
			MV_DBG(DMSG_SCSI,
			       "__MV__ add scsi disk %d-%d-%d failed.\n",
			       0, pmsg->param, 0);
		else
			MV_DBG(DMSG_SCSI,
			       "__MV__ add scsi disk %d-%d-%d.\n",
			       0, pmsg->param, 0);
		break;
	case EVENT_DEVICE_REMOVAL:
		psdev = scsi_device_lookup(phba->host, 0, pmsg->param, 0);

		if (NULL != psdev) {
			MV_DBG(DMSG_SCSI,
			       "__MV__ remove scsi disk %d-%d-%d.\n",
			       0, pmsg->param, 0);
			scsi_remove_device(psdev);
			scsi_device_put(psdev);
		} else {
			MV_DBG(DMSG_SCSI,
			       "__MV__ no disk to remove %d-%d-%d\n",
			       0, pmsg->param, 0);
		}
		break;
	case EVENT_HOT_PLUG:
		sata_hotplug(pmsg->data, pmsg->param);
		break;
	default:
		break;
	}
}
コード例 #6
0
ファイル: scsi_proc.c プロジェクト: sserg31/sca3_main
/**
 * scsi_remove_single_device - Respond to user request to remove a device
 * @host: user-supplied decimal integer
 * @channel: user-supplied decimal integer
 * @id: user-supplied decimal integer
 * @lun: user-supplied decimal integer
 *
 * Description: called by writing "scsi remove-single-device" to
 * /proc/scsi/scsi.  Does a scsi_device_lookup() and scsi_remove_device()
 */
static int scsi_remove_single_device(uint host, uint channel, uint id, uint lun)
{
    struct scsi_device *sdev;
    struct Scsi_Host *shost;
    int error = -ENXIO;

    shost = scsi_host_lookup(host);
    if (IS_ERR(shost))
        return PTR_ERR(shost);
    sdev = scsi_device_lookup(shost, channel, id, lun);
    if (sdev) {
        scsi_remove_device(sdev);
        scsi_device_put(sdev);
        error = 0;
    }

    scsi_host_put(shost);
    return error;
}
コード例 #7
0
ファイル: zfcp_sysfs.c プロジェクト: 3sOx/asuswrt-merlin
static ssize_t zfcp_sysfs_unit_remove_store(struct device *dev,
					    struct device_attribute *attr,
					    const char *buf, size_t count)
{
	struct zfcp_port *port = container_of(dev, struct zfcp_port, dev);
	struct zfcp_unit *unit;
	u64 fcp_lun;
	int retval = -EINVAL;
	struct scsi_device *sdev;

	if (!(port && get_device(&port->dev)))
		return -EBUSY;

	if (strict_strtoull(buf, 0, (unsigned long long *) &fcp_lun))
		goto out;

	unit = zfcp_get_unit_by_lun(port, fcp_lun);
	if (!unit)
		goto out;
	else
		retval = 0;

	sdev = scsi_device_lookup(port->adapter->scsi_host, 0,
				  port->starget_id,
				  scsilun_to_int((struct scsi_lun *)&fcp_lun));
	if (sdev) {
		scsi_remove_device(sdev);
		scsi_device_put(sdev);
	}

	write_lock_irq(&port->unit_list_lock);
	list_del(&unit->list);
	write_unlock_irq(&port->unit_list_lock);

	put_device(&unit->dev);

	zfcp_erp_unit_shutdown(unit, 0, "syurs_1", NULL);
	zfcp_device_unregister(&unit->dev, &zfcp_sysfs_unit_attrs);
out:
	put_device(&port->dev);
	return retval ? retval : (ssize_t) count;
}
コード例 #8
0
ファイル: xenbus.c プロジェクト: zhoupeng/spice4xen
static void scsifront_free(struct vscsifrnt_info *info)
{
	struct Scsi_Host *host = info->host;

#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,14)
	if (host->shost_state != SHOST_DEL) {
#else
	if (!test_bit(SHOST_DEL, &host->shost_state)) {
#endif
		scsi_remove_host(info->host);
	}

	if (info->ring_ref != GRANT_INVALID_REF) {
		gnttab_end_foreign_access(info->ring_ref,
					(unsigned long)info->ring.sring);
		info->ring_ref = GRANT_INVALID_REF;
		info->ring.sring = NULL;
	}

	if (info->irq)
		unbind_from_irqhandler(info->irq, info);
	info->irq = 0;

	scsi_host_put(info->host);
}


static int scsifront_alloc_ring(struct vscsifrnt_info *info)
{
	struct xenbus_device *dev = info->dev;
	struct vscsiif_sring *sring;
	int err = -ENOMEM;


	info->ring_ref = GRANT_INVALID_REF;

	/***** Frontend to Backend ring start *****/
	sring = (struct vscsiif_sring *) __get_free_page(GFP_KERNEL);
	if (!sring) {
		xenbus_dev_fatal(dev, err, "fail to allocate shared ring (Front to Back)");
		return err;
	}
	SHARED_RING_INIT(sring);
	FRONT_RING_INIT(&info->ring, sring, PAGE_SIZE);

	err = xenbus_grant_ring(dev, virt_to_mfn(sring));
	if (err < 0) {
		free_page((unsigned long) sring);
		info->ring.sring = NULL;
		xenbus_dev_fatal(dev, err, "fail to grant shared ring (Front to Back)");
		goto free_sring;
	}
	info->ring_ref = err;

	err = bind_listening_port_to_irqhandler(
			dev->otherend_id, scsifront_intr,
			SA_SAMPLE_RANDOM, "scsifront", info);

	if (err <= 0) {
		xenbus_dev_fatal(dev, err, "bind_listening_port_to_irqhandler");
		goto free_sring;
	}
	info->irq = err;

	return 0;

/* free resource */
free_sring:
	scsifront_free(info);

	return err;
}


static int scsifront_init_ring(struct vscsifrnt_info *info)
{
	struct xenbus_device *dev = info->dev;
	struct xenbus_transaction xbt;
	int err;

	DPRINTK("%s\n",__FUNCTION__);

	err = scsifront_alloc_ring(info);
	if (err)
		return err;
	DPRINTK("%u %u\n", info->ring_ref, info->evtchn);

again:
	err = xenbus_transaction_start(&xbt);
	if (err) {
		xenbus_dev_fatal(dev, err, "starting transaction");
	}

	err = xenbus_printf(xbt, dev->nodename, "ring-ref", "%u",
				info->ring_ref);
	if (err) {
		xenbus_dev_fatal(dev, err, "%s", "writing ring-ref");
		goto fail;
	}

	err = xenbus_printf(xbt, dev->nodename, "event-channel", "%u",
				irq_to_evtchn_port(info->irq));

	if (err) {
		xenbus_dev_fatal(dev, err, "%s", "writing event-channel");
		goto fail;
	}

	err = xenbus_transaction_end(xbt, 0);
	if (err) {
		if (err == -EAGAIN)
			goto again;
		xenbus_dev_fatal(dev, err, "completing transaction");
		goto free_sring;
	}

	return 0;

fail:
	xenbus_transaction_end(xbt, 1);
free_sring:
	/* free resource */
	scsifront_free(info);
	
	return err;
}


static int scsifront_probe(struct xenbus_device *dev,
				const struct xenbus_device_id *id)
{
	struct vscsifrnt_info *info;
	struct Scsi_Host *host;
	int i, err = -ENOMEM;
	char name[DEFAULT_TASK_COMM_LEN];

	host = scsi_host_alloc(&scsifront_sht, sizeof(*info));
	if (!host) {
		xenbus_dev_fatal(dev, err, "fail to allocate scsi host");
		return err;
	}
	info = (struct vscsifrnt_info *) host->hostdata;
	info->host = host;


	dev->dev.driver_data = info;
	info->dev  = dev;

	for (i = 0; i < VSCSIIF_MAX_REQS; i++) {
		info->shadow[i].next_free = i + 1;
		init_waitqueue_head(&(info->shadow[i].wq_reset));
		info->shadow[i].wait_reset = 0;
	}
	info->shadow[VSCSIIF_MAX_REQS - 1].next_free = 0x0fff;

	err = scsifront_init_ring(info);
	if (err) {
		scsi_host_put(host);
		return err;
	}

	init_waitqueue_head(&info->wq);
	spin_lock_init(&info->io_lock);
	spin_lock_init(&info->shadow_lock);

	snprintf(name, DEFAULT_TASK_COMM_LEN, "vscsiif.%d", info->host->host_no);

	info->kthread = kthread_run(scsifront_schedule, info, name);
	if (IS_ERR(info->kthread)) {
		err = PTR_ERR(info->kthread);
		info->kthread = NULL;
		printk(KERN_ERR "scsifront: kthread start err %d\n", err);
		goto free_sring;
	}

	host->max_id      = VSCSIIF_MAX_TARGET;
	host->max_channel = 0;
	host->max_lun     = VSCSIIF_MAX_LUN;
	host->max_sectors = (VSCSIIF_SG_TABLESIZE - 1) * PAGE_SIZE / 512;

	err = scsi_add_host(host, &dev->dev);
	if (err) {
		printk(KERN_ERR "scsifront: fail to add scsi host %d\n", err);
		goto free_sring;
	}

	xenbus_switch_state(dev, XenbusStateInitialised);

	return 0;

free_sring:
	/* free resource */
	scsifront_free(info);
	return err;
}

static int scsifront_remove(struct xenbus_device *dev)
{
	struct vscsifrnt_info *info = dev->dev.driver_data;

	DPRINTK("%s: %s removed\n",__FUNCTION__ ,dev->nodename);

	if (info->kthread) {
		kthread_stop(info->kthread);
		info->kthread = NULL;
	}

	scsifront_free(info);
	
	return 0;
}


static int scsifront_disconnect(struct vscsifrnt_info *info)
{
	struct xenbus_device *dev = info->dev;
	struct Scsi_Host *host = info->host;

	DPRINTK("%s: %s disconnect\n",__FUNCTION__ ,dev->nodename);

	/* 
	  When this function is executed,  all devices of 
	  Frontend have been deleted. 
	  Therefore, it need not block I/O before remove_host.
	*/

	scsi_remove_host(host);
	xenbus_frontend_closed(dev);

	return 0;
}

#define VSCSIFRONT_OP_ADD_LUN	1
#define VSCSIFRONT_OP_DEL_LUN	2

static void scsifront_do_lun_hotplug(struct vscsifrnt_info *info, int op)
{
	struct xenbus_device *dev = info->dev;
	int i, err = 0;
	char str[64], state_str[64];
	char **dir;
	unsigned int dir_n = 0;
	unsigned int device_state;
	unsigned int hst, chn, tgt, lun;
	struct scsi_device *sdev;

	dir = xenbus_directory(XBT_NIL, dev->otherend, "vscsi-devs", &dir_n);
	if (IS_ERR(dir))
		return;

	for (i = 0; i < dir_n; i++) {
		/* read status */
		snprintf(str, sizeof(str), "vscsi-devs/%s/state", dir[i]);
		err = xenbus_scanf(XBT_NIL, dev->otherend, str, "%u",
			&device_state);
		if (XENBUS_EXIST_ERR(err))
			continue;
		
		/* virtual SCSI device */
		snprintf(str, sizeof(str), "vscsi-devs/%s/v-dev", dir[i]);
		err = xenbus_scanf(XBT_NIL, dev->otherend, str,
			"%u:%u:%u:%u", &hst, &chn, &tgt, &lun);
		if (XENBUS_EXIST_ERR(err))
			continue;

		/* front device state path */
		snprintf(state_str, sizeof(state_str), "vscsi-devs/%s/state", dir[i]);

		switch (op) {
		case VSCSIFRONT_OP_ADD_LUN:
			if (device_state == XenbusStateInitialised) {
				sdev = scsi_device_lookup(info->host, chn, tgt, lun);
				if (sdev) {
					printk(KERN_ERR "scsifront: Device already in use.\n");
					scsi_device_put(sdev);
					xenbus_printf(XBT_NIL, dev->nodename,
						state_str, "%d", XenbusStateClosed);
				} else {
					scsi_add_device(info->host, chn, tgt, lun);
					xenbus_printf(XBT_NIL, dev->nodename,
						state_str, "%d", XenbusStateConnected);
				}
			}
			break;
		case VSCSIFRONT_OP_DEL_LUN:
			if (device_state == XenbusStateClosing) {
				sdev = scsi_device_lookup(info->host, chn, tgt, lun);
				if (sdev) {
					scsi_remove_device(sdev);
					scsi_device_put(sdev);
					xenbus_printf(XBT_NIL, dev->nodename,
						state_str, "%d", XenbusStateClosed);
				}
			}
			break;
		default:
			break;
		}
	}
	
	kfree(dir);
	return;
}




static void scsifront_backend_changed(struct xenbus_device *dev,
				enum xenbus_state backend_state)
{
	struct vscsifrnt_info *info = dev->dev.driver_data;

	DPRINTK("%p %u %u\n", dev, dev->state, backend_state);

	switch (backend_state) {
	case XenbusStateUnknown:
	case XenbusStateInitialising:
	case XenbusStateInitWait:
	case XenbusStateClosed:
		break;

	case XenbusStateInitialised:
		break;

	case XenbusStateConnected:
		if (xenbus_read_driver_state(dev->nodename) ==
			XenbusStateInitialised) {
			scsifront_do_lun_hotplug(info, VSCSIFRONT_OP_ADD_LUN);
		}
		
		if (dev->state == XenbusStateConnected)
			break;
			
		xenbus_switch_state(dev, XenbusStateConnected);
		break;

	case XenbusStateClosing:
		scsifront_disconnect(info);
		break;

	case XenbusStateReconfiguring:
		scsifront_do_lun_hotplug(info, VSCSIFRONT_OP_DEL_LUN);
		xenbus_switch_state(dev, XenbusStateReconfiguring);
		break;

	case XenbusStateReconfigured:
		scsifront_do_lun_hotplug(info, VSCSIFRONT_OP_ADD_LUN);
		xenbus_switch_state(dev, XenbusStateConnected);
		break;
	}
}


static struct xenbus_device_id scsifront_ids[] = {
	{ "vscsi" },
	{ "" }
};
MODULE_ALIAS("xen:vscsi");

static struct xenbus_driver scsifront_driver = {
	.name			= "vscsi",
	.owner			= THIS_MODULE,
	.ids			= scsifront_ids,
	.probe			= scsifront_probe,
	.remove			= scsifront_remove,
/* 	.resume			= scsifront_resume, */
	.otherend_changed	= scsifront_backend_changed,
};

int scsifront_xenbus_init(void)
{
	return xenbus_register_frontend(&scsifront_driver);
}

void scsifront_xenbus_unregister(void)
{
	xenbus_unregister_driver(&scsifront_driver);
}
コード例 #9
0
ファイル: cciss_scsi.c プロジェクト: lineuman/linux
static int
adjust_cciss_scsi_table(ctlr_info_t *h, int hostno,
	struct cciss_scsi_dev_t sd[], int nsds)
{
	/* sd contains scsi3 addresses and devtypes, but
	   bus target and lun are not filled in.  This funciton
	   takes what's in sd to be the current and adjusts
	   ccissscsi[] to be in line with what's in sd. */ 

	int i,j, found, changes=0;
	struct cciss_scsi_dev_t *csd;
	unsigned long flags;
	struct scsi2map *added, *removed;
	int nadded, nremoved;
	struct Scsi_Host *sh = NULL;

	added = kzalloc(sizeof(*added) * CCISS_MAX_SCSI_DEVS_PER_HBA,
			GFP_KERNEL);
	removed = kzalloc(sizeof(*removed) * CCISS_MAX_SCSI_DEVS_PER_HBA,
			GFP_KERNEL);

	if (!added || !removed) {
		dev_warn(&h->pdev->dev,
			"Out of memory in adjust_cciss_scsi_table\n");
		goto free_and_out;
	}

	CPQ_TAPE_LOCK(h, flags);

	if (hostno != -1)  /* if it's not the first time... */
		sh = h->scsi_ctlr->scsi_host;

	/* find any devices in ccissscsi[] that are not in 
	   sd[] and remove them from ccissscsi[] */

	i = 0;
	nremoved = 0;
	nadded = 0;
	while (i < ccissscsi[h->ctlr].ndevices) {
		csd = &ccissscsi[h->ctlr].dev[i];
		found=0;
		for (j=0;j<nsds;j++) {
			if (SCSI3ADDR_EQ(sd[j].scsi3addr,
				csd->scsi3addr)) {
				if (device_is_the_same(&sd[j], csd))
					found=2;
				else
					found=1;
				break;
			}
		}

		if (found == 0) { /* device no longer present. */ 
			changes++;
			cciss_scsi_remove_entry(h, hostno, i,
				removed, &nremoved);
			/* remove ^^^, hence i not incremented */
		} else if (found == 1) { /* device is different in some way */
			changes++;
			dev_info(&h->pdev->dev,
				"device c%db%dt%dl%d has changed.\n",
				hostno, csd->bus, csd->target, csd->lun);
			cciss_scsi_remove_entry(h, hostno, i,
				removed, &nremoved);
			/* remove ^^^, hence i not incremented */
			if (cciss_scsi_add_entry(h, hostno, &sd[j],
				added, &nadded) != 0)
				/* we just removed one, so add can't fail. */
					BUG();
			csd->devtype = sd[j].devtype;
			memcpy(csd->device_id, sd[j].device_id,
				sizeof(csd->device_id));
			memcpy(csd->vendor, sd[j].vendor,
				sizeof(csd->vendor));
			memcpy(csd->model, sd[j].model,
				sizeof(csd->model));
			memcpy(csd->revision, sd[j].revision,
				sizeof(csd->revision));
		} else 		/* device is same as it ever was, */
			i++;	/* so just move along. */
	}

	/* Now, make sure every device listed in sd[] is also
 	   listed in ccissscsi[], adding them if they aren't found */

	for (i=0;i<nsds;i++) {
		found=0;
		for (j = 0; j < ccissscsi[h->ctlr].ndevices; j++) {
			csd = &ccissscsi[h->ctlr].dev[j];
			if (SCSI3ADDR_EQ(sd[i].scsi3addr,
				csd->scsi3addr)) {
				if (device_is_the_same(&sd[i], csd))
					found=2;	/* found device */
				else
					found=1; 	/* found a bug. */
				break;
			}
		}
		if (!found) {
			changes++;
			if (cciss_scsi_add_entry(h, hostno, &sd[i],
				added, &nadded) != 0)
				break;
		} else if (found == 1) {
			/* should never happen... */
			changes++;
			dev_warn(&h->pdev->dev,
				"device unexpectedly changed\n");
			/* but if it does happen, we just ignore that device */
		}
	}
	CPQ_TAPE_UNLOCK(h, flags);

	/* Don't notify scsi mid layer of any changes the first time through */
	/* (or if there are no changes) scsi_scan_host will do it later the */
	/* first time through. */
	if (hostno == -1 || !changes)
		goto free_and_out;

	/* Notify scsi mid layer of any removed devices */
	for (i = 0; i < nremoved; i++) {
		struct scsi_device *sdev =
			scsi_device_lookup(sh, removed[i].bus,
				removed[i].target, removed[i].lun);
		if (sdev != NULL) {
			scsi_remove_device(sdev);
			scsi_device_put(sdev);
		} else {
			/* We don't expect to get here. */
			/* future cmds to this device will get selection */
			/* timeout as if the device was gone. */
			dev_warn(&h->pdev->dev, "didn't find "
				"c%db%dt%dl%d\n for removal.",
				hostno, removed[i].bus,
				removed[i].target, removed[i].lun);
		}
	}

	/* Notify scsi mid layer of any added devices */
	for (i = 0; i < nadded; i++) {
		int rc;
		rc = scsi_add_device(sh, added[i].bus,
			added[i].target, added[i].lun);
		if (rc == 0)
			continue;
		dev_warn(&h->pdev->dev, "scsi_add_device "
			"c%db%dt%dl%d failed, device not added.\n",
			hostno, added[i].bus, added[i].target, added[i].lun);
		/* now we have to remove it from ccissscsi, */
		/* since it didn't get added to scsi mid layer */
		fixup_botched_add(h, added[i].scsi3addr);
	}

free_and_out:
	kfree(added);
	kfree(removed);
	return 0;
}