Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d5ea4e26 authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge master.kernel.org:/pub/scm/linux/kernel/git/jejb/scsi-rc-fixes-2.6

parents c7ac6b42 863a930a
Loading
Loading
Loading
Loading
+38 −10
Original line number Diff line number Diff line
@@ -400,6 +400,35 @@ static struct scsi_target *scsi_alloc_target(struct device *parent,
	return found_target;
}

struct work_queue_wrapper {
	struct work_struct	work;
	struct scsi_target	*starget;
};

static void scsi_target_reap_work(void *data) {
	struct work_queue_wrapper *wqw = (struct work_queue_wrapper *)data;
	struct scsi_target *starget = wqw->starget;
	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
	unsigned long flags;

	kfree(wqw);

	spin_lock_irqsave(shost->host_lock, flags);

	if (--starget->reap_ref == 0 && list_empty(&starget->devices)) {
		list_del_init(&starget->siblings);
		spin_unlock_irqrestore(shost->host_lock, flags);
		device_del(&starget->dev);
		transport_unregister_device(&starget->dev);
		put_device(&starget->dev);
		return;

	}
	spin_unlock_irqrestore(shost->host_lock, flags);

	return;
}

/**
 * scsi_target_reap - check to see if target is in use and destroy if not
 *
@@ -411,19 +440,18 @@ static struct scsi_target *scsi_alloc_target(struct device *parent,
 */
void scsi_target_reap(struct scsi_target *starget)
{
	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
	unsigned long flags;
	spin_lock_irqsave(shost->host_lock, flags);
	struct work_queue_wrapper *wqw = 
		kzalloc(sizeof(struct work_queue_wrapper), GFP_ATOMIC);

	if (--starget->reap_ref == 0 && list_empty(&starget->devices)) {
		list_del_init(&starget->siblings);
		spin_unlock_irqrestore(shost->host_lock, flags);
		device_del(&starget->dev);
		transport_unregister_device(&starget->dev);
		put_device(&starget->dev);
	if (!wqw) {
		starget_printk(KERN_ERR, starget,
			       "Failed to allocate memory in scsi_reap_target()\n");
		return;
	}
	spin_unlock_irqrestore(shost->host_lock, flags);

	INIT_WORK(&wqw->work, scsi_target_reap_work, wqw);
	wqw->starget = starget;
	schedule_work(&wqw->work);
}

/**
+55 −4
Original line number Diff line number Diff line
@@ -105,6 +105,7 @@ static struct {
	{ FC_PORTSTATE_LINKDOWN,	"Linkdown" },
	{ FC_PORTSTATE_ERROR,		"Error" },
	{ FC_PORTSTATE_LOOPBACK,	"Loopback" },
	{ FC_PORTSTATE_DELETED,		"Deleted" },
};
fc_enum_name_search(port_state, fc_port_state, fc_port_state_names)
#define FC_PORTSTATE_MAX_NAMELEN	20
@@ -211,6 +212,7 @@ fc_bitfield_name_search(remote_port_roles, fc_remote_port_role_names)
#define FC_MGMTSRVR_PORTID		0x00000a


static void fc_shost_remove_rports(void  *data);
static void fc_timeout_deleted_rport(void *data);
static void fc_scsi_scan_rport(void *data);
static void fc_rport_terminate(struct fc_rport  *rport);
@@ -318,6 +320,8 @@ static int fc_host_setup(struct transport_container *tc, struct device *dev,
	fc_host_next_rport_number(shost) = 0;
	fc_host_next_target_id(shost) = 0;

	fc_host_flags(shost) = 0;
	INIT_WORK(&fc_host_rport_del_work(shost), fc_shost_remove_rports, shost);
	return 0;
}

@@ -387,6 +391,7 @@ show_fc_rport_##field (struct class_device *cdev, char *buf) \
	struct fc_internal *i = to_fc_internal(shost->transportt);	\
	if ((i->f->get_rport_##field) &&				\
	    !((rport->port_state == FC_PORTSTATE_BLOCKED) ||		\
	      (rport->port_state == FC_PORTSTATE_DELETED) ||		\
	      (rport->port_state == FC_PORTSTATE_NOTPRESENT)))		\
		i->f->get_rport_##field(rport);				\
	return snprintf(buf, sz, format_string, cast rport->field); 	\
@@ -402,6 +407,7 @@ store_fc_rport_##field(struct class_device *cdev, const char *buf, \
	struct Scsi_Host *shost = rport_to_shost(rport);		\
	struct fc_internal *i = to_fc_internal(shost->transportt);	\
	if ((rport->port_state == FC_PORTSTATE_BLOCKED) ||		\
	    (rport->port_state == FC_PORTSTATE_DELETED) ||		\
	    (rport->port_state == FC_PORTSTATE_NOTPRESENT))		\
		return -EBUSY;						\
	val = simple_strtoul(buf, NULL, 0);				\
@@ -519,6 +525,7 @@ store_fc_rport_dev_loss_tmo(struct class_device *cdev, const char *buf,
	struct Scsi_Host *shost = rport_to_shost(rport);
	struct fc_internal *i = to_fc_internal(shost->transportt);
	if ((rport->port_state == FC_PORTSTATE_BLOCKED) ||
	    (rport->port_state == FC_PORTSTATE_DELETED) ||
	    (rport->port_state == FC_PORTSTATE_NOTPRESENT))
		return -EBUSY;
	val = simple_strtoul(buf, NULL, 0);
@@ -1769,7 +1776,7 @@ fc_timeout_deleted_rport(void *data)
	rport->maxframe_size = -1;
	rport->supported_classes = FC_COS_UNSPECIFIED;
	rport->roles = FC_RPORT_ROLE_UNKNOWN;
	rport->port_state = FC_PORTSTATE_NOTPRESENT;
	rport->port_state = FC_PORTSTATE_DELETED;

	/* remove the identifiers that aren't used in the consisting binding */
	switch (fc_host_tgtid_bind_type(shost)) {
@@ -1789,14 +1796,23 @@ fc_timeout_deleted_rport(void *data)
		break;
	}

	spin_unlock_irqrestore(shost->host_lock, flags);

	/*
	 * As this only occurs if the remote port (scsi target)
	 * went away and didn't come back - we'll remove
	 * all attached scsi devices.
	 *
	 * We'll schedule the shost work item to perform the actual removal
	 * to avoid recursion in the different flush calls if we perform
	 * the removal in each target - and there are lots of targets
	 * whose timeouts fire at the same time.
	 */
	fc_rport_tgt_remove(rport);

	if ( !(fc_host_flags(shost) & FC_SHOST_RPORT_DEL_SCHEDULED)) {
		fc_host_flags(shost) |= FC_SHOST_RPORT_DEL_SCHEDULED;
		scsi_queue_work(shost, &fc_host_rport_del_work(shost));
	}

	spin_unlock_irqrestore(shost->host_lock, flags);
}

/**
@@ -1818,6 +1834,41 @@ fc_scsi_scan_rport(void *data)
}


/**
 * fc_shost_remove_rports - called to remove all rports that are marked
 *                       as in a deleted (not connected) state.
 * 
 * @data:	shost whose rports are to be looked at
 **/
static void
fc_shost_remove_rports(void  *data)
{
	struct Scsi_Host *shost = (struct Scsi_Host *)data;
	struct fc_rport *rport, *next_rport;
	unsigned long flags;

	spin_lock_irqsave(shost->host_lock, flags);
	while (fc_host_flags(shost) & FC_SHOST_RPORT_DEL_SCHEDULED) {

		fc_host_flags(shost) &= ~FC_SHOST_RPORT_DEL_SCHEDULED;

restart_search:
		list_for_each_entry_safe(rport, next_rport,
				&fc_host_rport_bindings(shost), peers) {
			if (rport->port_state == FC_PORTSTATE_DELETED) {
				rport->port_state = FC_PORTSTATE_NOTPRESENT;
				spin_unlock_irqrestore(shost->host_lock, flags);
				fc_rport_tgt_remove(rport);
				spin_lock_irqsave(shost->host_lock, flags);
				goto restart_search;
			}
		}

	}
	spin_unlock_irqrestore(shost->host_lock, flags);
}


MODULE_AUTHOR("Martin Hicks");
MODULE_DESCRIPTION("FC Transport Attributes");
MODULE_LICENSE("GPL");
+11 −0
Original line number Diff line number Diff line
@@ -79,6 +79,7 @@ enum fc_port_state {
	FC_PORTSTATE_LINKDOWN,
	FC_PORTSTATE_ERROR,
	FC_PORTSTATE_LOOPBACK,
	FC_PORTSTATE_DELETED,
};


@@ -325,8 +326,14 @@ struct fc_host_attrs {
	struct list_head rport_bindings;
	u32 next_rport_number;
	u32 next_target_id;
	u8 flags;
 	struct work_struct rport_del_work;
};

/* values for struct fc_host_attrs "flags" field: */
#define FC_SHOST_RPORT_DEL_SCHEDULED	0x01


#define fc_host_node_name(x) \
	(((struct fc_host_attrs *)(x)->shost_data)->node_name)
#define fc_host_port_name(x)	\
@@ -365,6 +372,10 @@ struct fc_host_attrs {
	(((struct fc_host_attrs *)(x)->shost_data)->next_rport_number)
#define fc_host_next_target_id(x) \
	(((struct fc_host_attrs *)(x)->shost_data)->next_target_id)
#define fc_host_flags(x) \
	(((struct fc_host_attrs *)(x)->shost_data)->flags)
#define fc_host_rport_del_work(x) \
	(((struct fc_host_attrs *)(x)->shost_data)->rport_del_work)


/* The functions by which the transport class and the driver communicate */