Skip to contentSkip to conversation

fix to serialize unload and discovery

PATCH In review [email protected] 2 participants 4 messages Oct 19, 2006

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 fix to serialize unload and discovery

sas_discover_domain() and sas_deform_port() assume that they are single threaded and never run in parallel. That is mostly true as we have a single threaded work queue to handle discovery and other events. The only race I find is unloading a module will call sas_deform_port() without watching if there is a discovery event is in progress. This patch will stop queuing further events, flush the queue, and then call sas_deform_port() to avoid the race.

drivers/scsi/libsas/sas_discover.c +1 -1
diff -r 8fe3b862f968 drivers/scsi/libsas/sas_discover.c
@@ -722,7 +722,7 @@ int sas_discover_event(struct asd_sas_po
 	BUG_ON(ev >= DISC_NUM_EVENTS);
 
 	sas_queue_event(ev, &disc->disc_event_lock, &disc->pending,
-			&disc->disc_work[ev], port->ha->core.shost);
+			&disc->disc_work[ev], port->ha);
 
 	return 0;
 }
drivers/scsi/libsas/sas_event.c +3 -3
diff -r 8fe3b862f968 drivers/scsi/libsas/sas_event.c
@@ -31,7 +31,7 @@ static void notify_ha_event(struct sas_h
 	BUG_ON(event >= HA_NUM_EVENTS);
 
 	sas_queue_event(event, &sas_ha->event_lock, &sas_ha->pending,
-			&sas_ha->ha_events[event], sas_ha->core.shost);
+			&sas_ha->ha_events[event], sas_ha);
 }
 
 static void notify_port_event(struct asd_sas_phy *phy, enum port_event event)
@@ -41,7 +41,7 @@ static void notify_port_event(struct asd
 	BUG_ON(event >= PORT_NUM_EVENTS);
 
 	sas_queue_event(event, &ha->event_lock, &phy->port_events_pending,
-			&phy->port_events[event], ha->core.shost);
+			&phy->port_events[event], ha);
 }
 
 static void notify_phy_event(struct asd_sas_phy *phy, enum phy_event event)
@@ -51,7 +51,7 @@ static void notify_phy_event(struct asd_
 	BUG_ON(event >= PHY_NUM_EVENTS);
 
 	sas_queue_event(event, &ha->event_lock, &phy->phy_events_pending,
-			&phy->phy_events[event], ha->core.shost);
+			&phy->phy_events[event], ha);
 }
 
 int sas_init_events(struct sas_ha_struct *sas_ha)
drivers/scsi/libsas/sas_init.c +12 -0
diff -r 8fe3b862f968 drivers/scsi/libsas/sas_init.c
@@ -85,6 +85,9 @@ int sas_register_ha(struct sas_ha_struct
 	else if (sas_ha->lldd_queue_size == -1)
 		sas_ha->lldd_queue_size = 128; /* Sanity */
 
+	sas_ha->state = SAS_HA_REGISTERED;
+	spin_lock_init(&sas_ha->state_lock);
+
 	error = sas_register_phys(sas_ha);
 	if (error) {
 		printk(KERN_NOTICE "couldn't register sas phys:%d\n", error);
@@ -123,9 +126,18 @@ Undo_phys:
 
 int sas_unregister_ha(struct sas_ha_struct *sas_ha)
 {
+	unsigned long flags;
+
 	if (sas_ha->lldd_max_execute_num > 1) {
 		sas_shutdown_queue(sas_ha);
 	}
+
+	/* Set the state to unregistered to avoid further
+	 * events to be queued */
+	spin_lock_irqsave(&sas_ha->state_lock, flags);
+	sas_ha->state = SAS_HA_UNREGISTERED;
+	spin_unlock_irqrestore(&sas_ha->state_lock, flags);
+	scsi_flush_work(sas_ha->core.shost);
 
 	sas_unregister_ports(sas_ha);
 
drivers/scsi/libsas/sas_internal.h +7 -2
diff -r 8fe3b862f968 drivers/scsi/libsas/sas_internal.h
@@ -80,7 +80,7 @@ static inline void sas_queue_event(int e
 static inline void sas_queue_event(int event, spinlock_t *lock,
 				   unsigned long *pending,
 				   struct work_struct *work,
-				   struct Scsi_Host *shost)
+				   struct sas_ha_struct *sas_ha)
 {
 	unsigned long flags;
 
@@ -91,7 +91,12 @@ static inline void sas_queue_event(int e
 	}
 	__set_bit(event, pending);
 	spin_unlock_irqrestore(lock, flags);
-	scsi_queue_work(shost, work);
+
+	spin_lock_irqsave(&sas_ha->state_lock, flags);
+	if (sas_ha->state != SAS_HA_UNREGISTERED) {
+		scsi_queue_work(sas_ha->core.shost, work);
+	}
+	spin_unlock_irqrestore(&sas_ha->state_lock, flags);
 }
 
 static inline void sas_begin_event(int event, spinlock_t *lock,
include/scsi/libsas.h +8 -0
diff -r 8fe3b862f968 include/scsi/libsas.h
@@ -307,11 +307,19 @@ struct scsi_core {
 	int               queue_thread_kill;
 };
 
+enum sas_ha_state {
+       SAS_HA_REGISTERED,
+       SAS_HA_UNREGISTERED
+};
+
 struct sas_ha_struct {
 /* private: */
 	spinlock_t       event_lock;
 	struct work_struct ha_events[HA_NUM_EVENTS];
 	unsigned long	 pending;
+
+	enum sas_ha_state state;
+	spinlock_t 	  state_lock;
 
 	struct scsi_core core;
 
#1 lore raw
Quoted 2 lines · --- [email protected] wrote:

sas_discover_domain() and sas_deform_port() assume that they are single threaded and never run in parallel.

This is indeed not true.

Quoted 2 lines

That is mostly true as we have a
single threaded work queue to handle discovery and other events.

Hint: when the port is gone, it is gone...

Quoted 5 lines

The
only race I find is unloading a module will call sas_deform_port() without watching if there is a discovery event is in progress. This patch will stop queuing further events, flush the queue, and then call sas_deform_port() to avoid the race.

You need a much more elaborate (read: general solution, aka algorithm) framework to solve this.

Needless to say, I don't observe these bugs and errors in my version of the SAS Stack.

Luben

Quoted 132 lines

diff -r 8fe3b862f968 drivers/scsi/libsas/sas_discover.c

drivers/scsi/libsas/sas_discover.c +1 -1
@@ -722,7 +722,7 @@ int sas_discover_event(struct asd_sas_po
 	BUG_ON(ev >= DISC_NUM_EVENTS);
 
 	sas_queue_event(ev, &disc->disc_event_lock, &disc->pending,
-			&disc->disc_work[ev], port->ha->core.shost);
+			&disc->disc_work[ev], port->ha);
 
 	return 0;
 }

diff -r 8fe3b862f968 drivers/scsi/libsas/sas_event.c

drivers/scsi/libsas/sas_event.c +3 -3
@@ -31,7 +31,7 @@ static void notify_ha_event(struct sas_h
 	BUG_ON(event >= HA_NUM_EVENTS);
 
 	sas_queue_event(event, &sas_ha->event_lock, &sas_ha->pending,
-			&sas_ha->ha_events[event], sas_ha->core.shost);
+			&sas_ha->ha_events[event], sas_ha);
 }
 
 static void notify_port_event(struct asd_sas_phy *phy, enum port_event event)
@@ -41,7 +41,7 @@ static void notify_port_event(struct asd
 	BUG_ON(event >= PORT_NUM_EVENTS);
 
 	sas_queue_event(event, &ha->event_lock, &phy->port_events_pending,
-			&phy->port_events[event], ha->core.shost);
+			&phy->port_events[event], ha);
 }
 
 static void notify_phy_event(struct asd_sas_phy *phy, enum phy_event event)
@@ -51,7 +51,7 @@ static void notify_phy_event(struct asd_
 	BUG_ON(event >= PHY_NUM_EVENTS);
 
 	sas_queue_event(event, &ha->event_lock, &phy->phy_events_pending,
-			&phy->phy_events[event], ha->core.shost);
+			&phy->phy_events[event], ha);
 }
 
 int sas_init_events(struct sas_ha_struct *sas_ha)

diff -r 8fe3b862f968 drivers/scsi/libsas/sas_init.c

drivers/scsi/libsas/sas_init.c +12 -0
@@ -85,6 +85,9 @@ int sas_register_ha(struct sas_ha_struct
 	else if (sas_ha->lldd_queue_size == -1)
 		sas_ha->lldd_queue_size = 128; /* Sanity */
 
+	sas_ha->state = SAS_HA_REGISTERED;
+	spin_lock_init(&sas_ha->state_lock);
+
 	error = sas_register_phys(sas_ha);
 	if (error) {
 		printk(KERN_NOTICE "couldn't register sas phys:%d\n", error);
@@ -123,9 +126,18 @@ Undo_phys:
 
 int sas_unregister_ha(struct sas_ha_struct *sas_ha)
 {
+	unsigned long flags;
+
 	if (sas_ha->lldd_max_execute_num > 1) {
 		sas_shutdown_queue(sas_ha);
 	}
+
+	/* Set the state to unregistered to avoid further
+	 * events to be queued */
+	spin_lock_irqsave(&sas_ha->state_lock, flags);
+	sas_ha->state = SAS_HA_UNREGISTERED;
+	spin_unlock_irqrestore(&sas_ha->state_lock, flags);
+	scsi_flush_work(sas_ha->core.shost);
 
 	sas_unregister_ports(sas_ha);
 

diff -r 8fe3b862f968 drivers/scsi/libsas/sas_internal.h

drivers/scsi/libsas/sas_internal.h +7 -2
@@ -80,7 +80,7 @@ static inline void sas_queue_event(int e
 static inline void sas_queue_event(int event, spinlock_t *lock,
 				   unsigned long *pending,
 				   struct work_struct *work,
-				   struct Scsi_Host *shost)
+				   struct sas_ha_struct *sas_ha)
 {
 	unsigned long flags;
 
@@ -91,7 +91,12 @@ static inline void sas_queue_event(int e
 	}
 	__set_bit(event, pending);
 	spin_unlock_irqrestore(lock, flags);
-	scsi_queue_work(shost, work);
+
+	spin_lock_irqsave(&sas_ha->state_lock, flags);
+	if (sas_ha->state != SAS_HA_UNREGISTERED) {
+		scsi_queue_work(sas_ha->core.shost, work);
+	}
+	spin_unlock_irqrestore(&sas_ha->state_lock, flags);
 }
 
 static inline void sas_begin_event(int event, spinlock_t *lock,

diff -r 8fe3b862f968 include/scsi/libsas.h

include/scsi/libsas.h +8 -1
@@ -307,11 +307,19 @@ struct scsi_core {
 	int               queue_thread_kill;
 };
 
+enum sas_ha_state {
+       SAS_HA_REGISTERED,
+       SAS_HA_UNREGISTERED
+};
+
 struct sas_ha_struct {
 /* private: */
 	spinlock_t       event_lock;
 	struct work_struct ha_events[HA_NUM_EVENTS];
 	unsigned long	 pending;
+
+	enum sas_ha_state state;
+	spinlock_t 	  state_lock;
 
 	struct scsi_core core;
 
-

To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

#2 lore raw

Lubin, I am not sure if you are referring to your version of the driver or the mainline version! If I read the mainline version correctly, sas_deform_port() actually removes a phy from a port and also removes all attached devices if it is the last phy in the port.

When a phy/port is gone, an event is posted to the work queue to eventually call sas_deform_port(). Let me know if I misunderstood the mainline version.

Thanks, Malahal.

Quoted 25 lines · Luben Tuikov [[email protected]] wrote:
Quoted 2 lines · --- [email protected] wrote:

sas_discover_domain() and sas_deform_port() assume that they are single threaded and never run in parallel.

This is indeed not true.

Quoted 2 lines

That is mostly true as we have a
single threaded work queue to handle discovery and other events.

Hint: when the port is gone, it is gone...

Quoted 5 lines

The
only race I find is unloading a module will call sas_deform_port() without watching if there is a discovery event is in progress. This patch will stop queuing further events, flush the queue, and then call sas_deform_port() to avoid the race.

You need a much more elaborate (read: general solution, aka algorithm) framework to solve this.

Needless to say, I don't observe these bugs and errors in my version of the SAS Stack.

Luben

#3 lore raw
Quoted 2 lines · --- [email protected] wrote:

Lubin, I am not sure if you are referring to your version of the driver or the mainline version!

Malahal,

I'm not referring to the bottomleyised version of my code.

Quoted 7 lines

If I read the mainline version correctly, sas_deform_port() actually removes a phy from a port and also removes all attached devices if it is the last phy in the port.

When a phy/port is gone, an event is posted to the work queue to eventually call sas_deform_port(). Let me know if I misunderstood the mainline version.

I'm not sure if you misunderstood the mainline version of my code -- you have to ask bottomley for that, since this code has been appropriated by bottomley who has also done some "architectural" changes.

Good luck!
Luben

Quoted 34 lines

Thanks, Malahal.

Quoted 17 lines · Luben Tuikov [[email protected]] wrote:
Quoted 2 lines · --- [email protected] wrote:

sas_discover_domain() and sas_deform_port() assume that they are single threaded and never run in parallel.

This is indeed not true.

Quoted 2 lines

That is mostly true as we have a
single threaded work queue to handle discovery and other events.

Hint: when the port is gone, it is gone...

Quoted 5 lines

The
only race I find is unloading a module will call sas_deform_port() without watching if there is a discovery event is in progress. This patch will stop queuing further events, flush the queue, and then call sas_deform_port() to avoid the race.

Quoted 8 lines

You need a much more elaborate (read: general solution, aka algorithm) framework to solve this.

Needless to say, I don't observe these bugs and errors in my version of the SAS Stack.

Luben

-
To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html