|
@@ -39,6 +39,7 @@
|
|
|
#include <linux/completion.h>
|
|
|
#include <linux/blkdev.h>
|
|
|
#include <scsi/scsi_host.h>
|
|
|
+#include <scsi/scsi_device.h>
|
|
|
#include <asm/semaphore.h>
|
|
|
|
|
|
#include "aacraid.h"
|
|
@@ -791,6 +792,268 @@ void aac_printf(struct aac_dev *dev, u32 val)
|
|
|
memset(cp, 0, 256);
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+/**
|
|
|
+ * aac_handle_aif - Handle a message from the firmware
|
|
|
+ * @dev: Which adapter this fib is from
|
|
|
+ * @fibptr: Pointer to fibptr from adapter
|
|
|
+ *
|
|
|
+ * This routine handles a driver notify fib from the adapter and
|
|
|
+ * dispatches it to the appropriate routine for handling.
|
|
|
+ */
|
|
|
+
|
|
|
+static void aac_handle_aif(struct aac_dev * dev, struct fib * fibptr)
|
|
|
+{
|
|
|
+ struct hw_fib * hw_fib = fibptr->hw_fib;
|
|
|
+ struct aac_aifcmd * aifcmd = (struct aac_aifcmd *)hw_fib->data;
|
|
|
+ int busy;
|
|
|
+ u32 container;
|
|
|
+ struct scsi_device *device;
|
|
|
+ enum {
|
|
|
+ NOTHING,
|
|
|
+ DELETE,
|
|
|
+ ADD,
|
|
|
+ CHANGE
|
|
|
+ } device_config_needed;
|
|
|
+
|
|
|
+ /* Sniff for container changes */
|
|
|
+
|
|
|
+ if (!dev)
|
|
|
+ return;
|
|
|
+ container = (u32)-1;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * We have set this up to try and minimize the number of
|
|
|
+ * re-configures that take place. As a result of this when
|
|
|
+ * certain AIF's come in we will set a flag waiting for another
|
|
|
+ * type of AIF before setting the re-config flag.
|
|
|
+ */
|
|
|
+ switch (le32_to_cpu(aifcmd->command)) {
|
|
|
+ case AifCmdDriverNotify:
|
|
|
+ switch (le32_to_cpu(((u32 *)aifcmd->data)[0])) {
|
|
|
+ /*
|
|
|
+ * Morph or Expand complete
|
|
|
+ */
|
|
|
+ case AifDenMorphComplete:
|
|
|
+ case AifDenVolumeExtendComplete:
|
|
|
+ container = le32_to_cpu(((u32 *)aifcmd->data)[1]);
|
|
|
+ if (container >= dev->maximum_num_containers)
|
|
|
+ break;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Find the Scsi_Device associated with the SCSI
|
|
|
+ * address. Make sure we have the right array, and if
|
|
|
+ * so set the flag to initiate a new re-config once we
|
|
|
+ * see an AifEnConfigChange AIF come through.
|
|
|
+ */
|
|
|
+
|
|
|
+ if ((dev != NULL) && (dev->scsi_host_ptr != NULL)) {
|
|
|
+ device = scsi_device_lookup(dev->scsi_host_ptr,
|
|
|
+ CONTAINER_TO_CHANNEL(container),
|
|
|
+ CONTAINER_TO_ID(container),
|
|
|
+ CONTAINER_TO_LUN(container));
|
|
|
+ if (device) {
|
|
|
+ dev->fsa_dev[container].config_needed = CHANGE;
|
|
|
+ dev->fsa_dev[container].config_waiting_on = AifEnConfigChange;
|
|
|
+ scsi_device_put(device);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If we are waiting on something and this happens to be
|
|
|
+ * that thing then set the re-configure flag.
|
|
|
+ */
|
|
|
+ if (container != (u32)-1) {
|
|
|
+ if (container >= dev->maximum_num_containers)
|
|
|
+ break;
|
|
|
+ if (dev->fsa_dev[container].config_waiting_on ==
|
|
|
+ le32_to_cpu(*(u32 *)aifcmd->data))
|
|
|
+ dev->fsa_dev[container].config_waiting_on = 0;
|
|
|
+ } else for (container = 0;
|
|
|
+ container < dev->maximum_num_containers; ++container) {
|
|
|
+ if (dev->fsa_dev[container].config_waiting_on ==
|
|
|
+ le32_to_cpu(*(u32 *)aifcmd->data))
|
|
|
+ dev->fsa_dev[container].config_waiting_on = 0;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ case AifCmdEventNotify:
|
|
|
+ switch (le32_to_cpu(((u32 *)aifcmd->data)[0])) {
|
|
|
+ /*
|
|
|
+ * Add an Array.
|
|
|
+ */
|
|
|
+ case AifEnAddContainer:
|
|
|
+ container = le32_to_cpu(((u32 *)aifcmd->data)[1]);
|
|
|
+ if (container >= dev->maximum_num_containers)
|
|
|
+ break;
|
|
|
+ dev->fsa_dev[container].config_needed = ADD;
|
|
|
+ dev->fsa_dev[container].config_waiting_on =
|
|
|
+ AifEnConfigChange;
|
|
|
+ break;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Delete an Array.
|
|
|
+ */
|
|
|
+ case AifEnDeleteContainer:
|
|
|
+ container = le32_to_cpu(((u32 *)aifcmd->data)[1]);
|
|
|
+ if (container >= dev->maximum_num_containers)
|
|
|
+ break;
|
|
|
+ dev->fsa_dev[container].config_needed = DELETE;
|
|
|
+ dev->fsa_dev[container].config_waiting_on =
|
|
|
+ AifEnConfigChange;
|
|
|
+ break;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Container change detected. If we currently are not
|
|
|
+ * waiting on something else, setup to wait on a Config Change.
|
|
|
+ */
|
|
|
+ case AifEnContainerChange:
|
|
|
+ container = le32_to_cpu(((u32 *)aifcmd->data)[1]);
|
|
|
+ if (container >= dev->maximum_num_containers)
|
|
|
+ break;
|
|
|
+ if (dev->fsa_dev[container].config_waiting_on)
|
|
|
+ break;
|
|
|
+ dev->fsa_dev[container].config_needed = CHANGE;
|
|
|
+ dev->fsa_dev[container].config_waiting_on =
|
|
|
+ AifEnConfigChange;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case AifEnConfigChange:
|
|
|
+ break;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If we are waiting on something and this happens to be
|
|
|
+ * that thing then set the re-configure flag.
|
|
|
+ */
|
|
|
+ if (container != (u32)-1) {
|
|
|
+ if (container >= dev->maximum_num_containers)
|
|
|
+ break;
|
|
|
+ if (dev->fsa_dev[container].config_waiting_on ==
|
|
|
+ le32_to_cpu(*(u32 *)aifcmd->data))
|
|
|
+ dev->fsa_dev[container].config_waiting_on = 0;
|
|
|
+ } else for (container = 0;
|
|
|
+ container < dev->maximum_num_containers; ++container) {
|
|
|
+ if (dev->fsa_dev[container].config_waiting_on ==
|
|
|
+ le32_to_cpu(*(u32 *)aifcmd->data))
|
|
|
+ dev->fsa_dev[container].config_waiting_on = 0;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+
|
|
|
+ case AifCmdJobProgress:
|
|
|
+ /*
|
|
|
+ * These are job progress AIF's. When a Clear is being
|
|
|
+ * done on a container it is initially created then hidden from
|
|
|
+ * the OS. When the clear completes we don't get a config
|
|
|
+ * change so we monitor the job status complete on a clear then
|
|
|
+ * wait for a container change.
|
|
|
+ */
|
|
|
+
|
|
|
+ if ((((u32 *)aifcmd->data)[1] == cpu_to_le32(AifJobCtrZero))
|
|
|
+ && ((((u32 *)aifcmd->data)[6] == ((u32 *)aifcmd->data)[5])
|
|
|
+ || (((u32 *)aifcmd->data)[4] == cpu_to_le32(AifJobStsSuccess)))) {
|
|
|
+ for (container = 0;
|
|
|
+ container < dev->maximum_num_containers;
|
|
|
+ ++container) {
|
|
|
+ /*
|
|
|
+ * Stomp on all config sequencing for all
|
|
|
+ * containers?
|
|
|
+ */
|
|
|
+ dev->fsa_dev[container].config_waiting_on =
|
|
|
+ AifEnContainerChange;
|
|
|
+ dev->fsa_dev[container].config_needed = ADD;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if ((((u32 *)aifcmd->data)[1] == cpu_to_le32(AifJobCtrZero))
|
|
|
+ && (((u32 *)aifcmd->data)[6] == 0)
|
|
|
+ && (((u32 *)aifcmd->data)[4] == cpu_to_le32(AifJobStsRunning))) {
|
|
|
+ for (container = 0;
|
|
|
+ container < dev->maximum_num_containers;
|
|
|
+ ++container) {
|
|
|
+ /*
|
|
|
+ * Stomp on all config sequencing for all
|
|
|
+ * containers?
|
|
|
+ */
|
|
|
+ dev->fsa_dev[container].config_waiting_on =
|
|
|
+ AifEnContainerChange;
|
|
|
+ dev->fsa_dev[container].config_needed = DELETE;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ device_config_needed = NOTHING;
|
|
|
+ for (container = 0; container < dev->maximum_num_containers;
|
|
|
+ ++container) {
|
|
|
+ if ((dev->fsa_dev[container].config_waiting_on == 0)
|
|
|
+ && (dev->fsa_dev[container].config_needed != NOTHING)) {
|
|
|
+ device_config_needed =
|
|
|
+ dev->fsa_dev[container].config_needed;
|
|
|
+ dev->fsa_dev[container].config_needed = NOTHING;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (device_config_needed == NOTHING)
|
|
|
+ return;
|
|
|
+
|
|
|
+ /*
|
|
|
+ * If we decided that a re-configuration needs to be done,
|
|
|
+ * schedule it here on the way out the door, please close the door
|
|
|
+ * behind you.
|
|
|
+ */
|
|
|
+
|
|
|
+ busy = 0;
|
|
|
+
|
|
|
+
|
|
|
+ /*
|
|
|
+ * Find the Scsi_Device associated with the SCSI address,
|
|
|
+ * and mark it as changed, invalidating the cache. This deals
|
|
|
+ * with changes to existing device IDs.
|
|
|
+ */
|
|
|
+
|
|
|
+ if (!dev || !dev->scsi_host_ptr)
|
|
|
+ return;
|
|
|
+ /*
|
|
|
+ * force reload of disk info via probe_container
|
|
|
+ */
|
|
|
+ if ((device_config_needed == CHANGE)
|
|
|
+ && (dev->fsa_dev[container].valid == 1))
|
|
|
+ dev->fsa_dev[container].valid = 2;
|
|
|
+ if ((device_config_needed == CHANGE) ||
|
|
|
+ (device_config_needed == ADD))
|
|
|
+ probe_container(dev, container);
|
|
|
+ device = scsi_device_lookup(dev->scsi_host_ptr,
|
|
|
+ CONTAINER_TO_CHANNEL(container),
|
|
|
+ CONTAINER_TO_ID(container),
|
|
|
+ CONTAINER_TO_LUN(container));
|
|
|
+ if (device) {
|
|
|
+ switch (device_config_needed) {
|
|
|
+ case DELETE:
|
|
|
+ scsi_remove_device(device);
|
|
|
+ break;
|
|
|
+ case CHANGE:
|
|
|
+ if (!dev->fsa_dev[container].valid) {
|
|
|
+ scsi_remove_device(device);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ scsi_rescan_device(&device->sdev_gendev);
|
|
|
+
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ scsi_device_put(device);
|
|
|
+ }
|
|
|
+ if (device_config_needed == ADD) {
|
|
|
+ scsi_add_device(dev->scsi_host_ptr,
|
|
|
+ CONTAINER_TO_CHANNEL(container),
|
|
|
+ CONTAINER_TO_ID(container),
|
|
|
+ CONTAINER_TO_LUN(container));
|
|
|
+ }
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* aac_command_thread - command processing thread
|
|
|
* @dev: Adapter to monitor
|
|
@@ -860,6 +1123,7 @@ int aac_command_thread(struct aac_dev * dev)
|
|
|
aifcmd = (struct aac_aifcmd *) hw_fib->data;
|
|
|
if (aifcmd->command == cpu_to_le32(AifCmdDriverNotify)) {
|
|
|
/* Handle Driver Notify Events */
|
|
|
+ aac_handle_aif(dev, fib);
|
|
|
*(__le32 *)hw_fib->data = cpu_to_le32(ST_OK);
|
|
|
fib_adapter_complete(fib, (u16)sizeof(u32));
|
|
|
} else {
|
|
@@ -872,7 +1136,15 @@ int aac_command_thread(struct aac_dev * dev)
|
|
|
unsigned num;
|
|
|
struct hw_fib ** hw_fib_pool, ** hw_fib_p;
|
|
|
struct fib ** fib_pool, ** fib_p;
|
|
|
-
|
|
|
+
|
|
|
+ /* Sniff events */
|
|
|
+ if ((aifcmd->command ==
|
|
|
+ cpu_to_le32(AifCmdEventNotify)) ||
|
|
|
+ (aifcmd->command ==
|
|
|
+ cpu_to_le32(AifCmdJobProgress))) {
|
|
|
+ aac_handle_aif(dev, fib);
|
|
|
+ }
|
|
|
+
|
|
|
time_now = jiffies/HZ;
|
|
|
|
|
|
/*
|