[PATCH 2.6.13 8/14] sas-class: sas_event.c SAS Event management and processing

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Signed-off-by: Luben Tuikov <[email protected]>

diff -X linux-2.6.13/Documentation/dontdiff -Naur linux-2.6.13-orig/drivers/scsi/sas-class/sas_event.c linux-2.6.13/drivers/scsi/sas-class/sas_event.c
--- linux-2.6.13-orig/drivers/scsi/sas-class/sas_event.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-2.6.13/drivers/scsi/sas-class/sas_event.c	2005-09-09 11:14:29.000000000 -0400
@@ -0,0 +1,294 @@
+/*
+ * Serial Attached SCSI (SAS) Event processing
+ *
+ * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
+ * Copyright (C) 2005 Luben Tuikov <[email protected]>
+ *
+ * This file is licensed under GPLv2.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ * $Id: //depot/sas-class/sas_event.c#25 $
+ */
+
+/**
+ * Implementation Of Priority Queue Without Duplication
+ * Luben Tuikov 2005/07/11
+ *
+ * The SAS class implements priority queue without duplication for
+ * handling ha/port/phy/discover events.  That is, we want to process
+ * the last N unique/non-duplicating events, in the order they arrived.
+ *
+ * The requirement is that insertion is O(1), and ordered removal is O(1).
+ *
+ * Suppose events are identified by integers.  Then what is required
+ * is that for a given sequence of any random integers R, to find a
+ * sorted sequence E, where
+ *     a) |E| <= |R|.  If the number of types of events is bounded,
+ *        then E is also bounded by that number, from b).
+ *     b) For all i and k, E[i] != E[k], except when i == k,
+ *        this gives us uniqueness/non duplication.
+ *     c) For all i < k, Order(E[i]) < Order(E[k]), this gives us
+ *        ordering.
+ *     d) If T(R) = E, then O(T) <= |R|, this ensures that insertion
+ *        is O(1), and ordered removal is O(1) trivially, since we
+ *        remove at the head of E.
+ *
+ * Example:
+ * If R = {4, 5, 1, 2, 5, 3, 3, 4, 4, 3, 1}, then
+ *    E = {2, 5, 4, 3, 1}.
+ *
+ * The algorithm, T, makes use of an array of list elements, indexed
+ * by event type, and an event list head which is a linked list of the
+ * elements of the array.  When the next event arrives, we index the
+ * array by the event, and add that event to the tail of the event
+ * list head, deleting it from its previous list position (if it had
+ * one).
+ *
+ * Clearly insertion is O(1).
+ *
+ * E is given by the elements of the event list, traversed from head
+ * to tail.
+ */
+
+#include <scsi/scsi_host.h>
+#include "sas_internal.h"
+#include "sas_dump.h"
+#include <scsi/sas/sas_discover.h>
+
+static void sas_process_phy_event(struct sas_phy *phy)
+{
+	unsigned long flags;
+	struct sas_ha_struct *sas_ha = phy->ha;
+	enum phy_event phy_event;
+
+	spin_lock_irqsave(&sas_ha->event_lock, flags);
+	while (!list_empty(&phy->phy_event_list)) {
+		struct list_head *head = phy->phy_event_list.next;
+		phy_event = container_of(head, struct sas_event, el)->event;
+		list_del_init(head);
+		spin_unlock_irqrestore(&sas_ha->event_lock, flags);
+
+		sas_dprint_phye(phy->id, phy_event);
+
+		switch(phy_event) {
+		case PHYE_LOSS_OF_SIGNAL:
+			sas_phye_loss_of_signal(phy);
+			break;
+		case PHYE_OOB_DONE:
+			sas_phye_oob_done(phy);
+			break;
+		case PHYE_OOB_ERROR:
+			sas_phye_oob_error(phy);
+			break;
+		case PHYE_SPINUP_HOLD:
+			sas_phye_spinup_hold(phy);
+			break;
+		}
+		spin_lock_irqsave(&sas_ha->event_lock, flags);
+	}
+	/* Clear the bit in case we received events in due time. */
+	sas_ha->phye_mask &= ~(1 << phy->id);
+	spin_unlock_irqrestore(&sas_ha->event_lock, flags);
+}
+
+static void sas_process_port_event(struct sas_phy *phy)
+{
+	unsigned long flags;
+	struct sas_ha_struct *sas_ha = phy->ha;
+	enum port_event port_event;
+
+	spin_lock_irqsave(&sas_ha->event_lock, flags);
+	while (!list_empty(&phy->port_event_list)) {
+		struct list_head *head = phy->port_event_list.next;
+		port_event = container_of(head, struct sas_event, el)->event;
+		list_del_init(head);
+		spin_unlock_irqrestore(&sas_ha->event_lock, flags);
+
+		sas_dprint_porte(phy->id, port_event);
+
+		switch (port_event) {
+		case PORTE_BYTES_DMAED:
+			sas_porte_bytes_dmaed(phy);
+			break;
+		case PORTE_BROADCAST_RCVD:
+			sas_porte_broadcast_rcvd(phy);
+			break;
+		case PORTE_LINK_RESET_ERR:
+			sas_porte_link_reset_err(phy);
+			break;
+		case PORTE_TIMER_EVENT:
+			sas_porte_timer_event(phy);
+			break;
+		case PORTE_HARD_RESET:
+			sas_porte_hard_reset(phy);
+			break;
+		}
+		spin_lock_irqsave(&sas_ha->event_lock, flags);
+	}
+	/* Clear the bit in case we received events in due time. */
+	sas_ha->porte_mask &= ~(1 << phy->id);
+	spin_unlock_irqrestore(&sas_ha->event_lock, flags);
+}
+
+static void sas_process_ha_event(struct sas_ha_struct *sas_ha)
+{
+	unsigned long flags;
+	enum ha_event ha_event;
+
+	spin_lock_irqsave(&sas_ha->event_lock, flags);
+	while (!list_empty(&sas_ha->ha_event_list)) {
+		struct list_head *head = sas_ha->ha_event_list.next;
+		ha_event = container_of(head, struct sas_event, el)->event;
+		list_del_init(head);
+		spin_unlock_irqrestore(&sas_ha->event_lock, flags);
+
+		sas_dprint_hae(sas_ha, ha_event);
+		
+		switch (ha_event) {
+		case HAE_RESET:
+			sas_hae_reset(sas_ha);
+			break;
+		}
+		spin_lock_irqsave(&sas_ha->event_lock, flags);
+	}
+	spin_unlock_irqrestore(&sas_ha->event_lock, flags);
+}
+
+static void sas_process_events(struct sas_ha_struct *sas_ha)
+{
+	unsigned long flags;
+	u32 porte_mask, phye_mask;
+	int p;
+
+	spin_lock_irqsave(&sas_ha->event_lock, flags);
+	phye_mask = sas_ha->phye_mask;
+	sas_ha->phye_mask = 0;
+	spin_unlock_irqrestore(&sas_ha->event_lock, flags);
+
+	for (p = 0; phye_mask != 0; phye_mask >>= 1, p++)
+		if (phye_mask & 01)
+			sas_process_phy_event(sas_ha->sas_phy[p]);
+
+	spin_lock_irqsave(&sas_ha->event_lock, flags);
+	porte_mask = sas_ha->porte_mask;
+	sas_ha->porte_mask = 0;
+	spin_unlock_irqrestore(&sas_ha->event_lock, flags);
+
+	for (p = 0; porte_mask != 0; porte_mask >>= 1, p++)
+		if (porte_mask & 01)
+			sas_process_port_event(sas_ha->sas_phy[p]);
+
+	sas_process_ha_event(sas_ha);
+}
+
+static void notify_ha_event(struct sas_ha_struct *sas_ha, enum ha_event event)
+{
+	unsigned long flags;
+	
+	spin_lock_irqsave(&sas_ha->event_lock, flags);
+	list_move_tail(&sas_ha->ha_events[event].el, &sas_ha->ha_event_list);
+	up(&sas_ha->event_sema);
+	spin_unlock_irqrestore(&sas_ha->event_lock, flags);
+}
+
+static void notify_port_event(struct sas_phy *phy, enum port_event event)
+{
+	struct sas_ha_struct *ha = phy->ha;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ha->event_lock, flags);
+	list_move_tail(&phy->port_events[event].el, &phy->port_event_list);
+	ha->porte_mask |= (1 << phy->id);
+	up(&ha->event_sema);
+	spin_unlock_irqrestore(&ha->event_lock, flags);
+}
+
+static void notify_phy_event(struct sas_phy *phy, enum phy_event event)
+{
+	struct sas_ha_struct *ha = phy->ha;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ha->event_lock, flags);
+	list_move_tail(&phy->phy_events[event].el, &phy->phy_event_list);
+	ha->phye_mask |= (1 << phy->id);
+	up(&ha->event_sema);
+	spin_unlock_irqrestore(&ha->event_lock, flags);
+}
+
+static DECLARE_COMPLETION(event_th_comp);
+
+static int sas_event_thread(void *_sas_ha)
+{
+	struct sas_ha_struct *sas_ha = _sas_ha;
+
+	daemonize("sas_event_%d", sas_ha->core.shost->host_no);
+	current->flags |= PF_NOFREEZE;
+
+	complete(&event_th_comp);
+	
+	while (1) {
+		down_interruptible(&sas_ha->event_sema);
+		if (sas_ha->event_thread_kill)
+			break;
+		sas_process_events(sas_ha);
+	}
+
+	complete(&event_th_comp);
+
+	return 0;
+}
+
+int sas_start_event_thread(struct sas_ha_struct *sas_ha)
+{
+	int i;
+
+	init_MUTEX_LOCKED(&sas_ha->event_sema);
+	sas_ha->event_thread_kill = 0;
+
+	spin_lock_init(&sas_ha->event_lock);
+	INIT_LIST_HEAD(&sas_ha->ha_event_list);
+	sas_ha->porte_mask = 0;
+	sas_ha->phye_mask = 0;
+
+	for (i = 0; i < HA_NUM_EVENTS; i++) {
+		struct sas_event *ev = &sas_ha->ha_events[i];
+		ev->event = i;
+		INIT_LIST_HEAD(&ev->el);
+	}
+
+	sas_ha->notify_ha_event = notify_ha_event;
+	sas_ha->notify_port_event = notify_port_event;
+	sas_ha->notify_phy_event = notify_phy_event;
+
+	i = kernel_thread(sas_event_thread, sas_ha, 0);
+	if (i >= 0)
+		wait_for_completion(&event_th_comp);
+
+	return i < 0 ? i : 0;
+}
+
+void sas_kill_event_thread(struct sas_ha_struct *sas_ha)
+{
+	int i;
+	
+	init_completion(&event_th_comp);
+	sas_ha->event_thread_kill = 1;
+	up(&sas_ha->event_sema);
+	wait_for_completion(&event_th_comp);
+
+	for (i = 0; i < sas_ha->num_phys; i++)
+		sas_kill_disc_thread(sas_ha->sas_port[i]);
+}


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

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]
  Powered by Linux