static int hpsa_wait_for_board_state(struct pci_dev *pdev, void __iomem *vaddr,
int wait_for_ready);
static inline void finish_cmd(struct CommandList *c);
+static unsigned char hpsa_format_in_progress(struct ctlr_info *h,
+ unsigned char scsi3addr[]);
#define BOARD_NOT_READY 0
#define BOARD_READY 1
return DEVICE_NOT_FOUND;
}
+#define OFFLINE_DEVICE_POLL_INTERVAL (120 * HZ)
+static int hpsa_offline_device_thread(void *v)
+{
+ struct ctlr_info *h = v;
+ unsigned long flags;
+ struct offline_device_entry *d;
+ unsigned char need_rescan = 0;
+ struct list_head *this, *tmp;
+
+ while (1) {
+ schedule_timeout_interruptible(OFFLINE_DEVICE_POLL_INTERVAL);
+ if (kthread_should_stop())
+ break;
+
+ /* Check if any of the offline devices have become ready */
+ spin_lock_irqsave(&h->offline_device_lock, flags);
+ list_for_each_safe(this, tmp, &h->offline_device_list) {
+ d = list_entry(this, struct offline_device_entry,
+ offline_list);
+ spin_unlock_irqrestore(&h->offline_device_lock, flags);
+ if (!hpsa_format_in_progress(h, d->scsi3addr)) {
+ need_rescan = 1;
+ goto do_rescan;
+ }
+ spin_lock_irqsave(&h->offline_device_lock, flags);
+ }
+ spin_unlock_irqrestore(&h->offline_device_lock, flags);
+ }
+
+do_rescan:
+
+ /* Remove all entries from the list and rescan and exit this thread.
+ * If there are still offline devices, the rescan will make a new list
+ * and create a new offline device monitor thread.
+ */
+ spin_lock_irqsave(&h->offline_device_lock, flags);
+ list_for_each_safe(this, tmp, &h->offline_device_list) {
+ d = list_entry(this, struct offline_device_entry, offline_list);
+ list_del_init(this);
+ kfree(d);
+ }
+ h->offline_device_monitor = NULL;
+ h->offline_device_thread_state = OFFLINE_DEVICE_THREAD_STOPPED;
+ spin_unlock_irqrestore(&h->offline_device_lock, flags);
+ if (need_rescan)
+ hpsa_scan_start(h->scsi_host);
+ return 0;
+}
+
+static void hpsa_monitor_offline_device(struct ctlr_info *h,
+ unsigned char scsi3addr[])
+{
+ struct offline_device_entry *device;
+ unsigned long flags;
+
+ /* Check to see if device is already on the list */
+ spin_lock_irqsave(&h->offline_device_lock, flags);
+ list_for_each_entry(device, &h->offline_device_list, offline_list) {
+ if (memcmp(device->scsi3addr, scsi3addr,
+ sizeof(device->scsi3addr)) == 0) {
+ spin_unlock_irqrestore(&h->offline_device_lock, flags);
+ return;
+ }
+ }
+ spin_unlock_irqrestore(&h->offline_device_lock, flags);
+
+ /* Device is not on the list, add it. */
+ device = kmalloc(sizeof(*device), GFP_KERNEL);
+ if (!device) {
+ dev_warn(&h->pdev->dev, "out of memory in %s\n", __func__);
+ return;
+ }
+ memcpy(device->scsi3addr, scsi3addr, sizeof(device->scsi3addr));
+ spin_lock_irqsave(&h->offline_device_lock, flags);
+ list_add_tail(&device->offline_list, &h->offline_device_list);
+ if (h->offline_device_thread_state == OFFLINE_DEVICE_THREAD_STOPPED) {
+ h->offline_device_thread_state = OFFLINE_DEVICE_THREAD_RUNNING;
+ spin_unlock_irqrestore(&h->offline_device_lock, flags);
+ h->offline_device_monitor =
+ kthread_run(hpsa_offline_device_thread, h, HPSA "-odm");
+ spin_lock_irqsave(&h->offline_device_lock, flags);
+ }
+ if (!h->offline_device_monitor) {
+ dev_warn(&h->pdev->dev, "failed to start offline device monitor thread.\n");
+ h->offline_device_thread_state = OFFLINE_DEVICE_THREAD_STOPPED;
+ }
+ spin_unlock_irqrestore(&h->offline_device_lock, flags);
+}
+
+static void stop_offline_device_monitor(struct ctlr_info *h)
+{
+ unsigned long flags;
+ int stop_thread;
+
+ spin_lock_irqsave(&h->offline_device_lock, flags);
+ stop_thread = (h->offline_device_thread_state ==
+ OFFLINE_DEVICE_THREAD_RUNNING);
+ if (stop_thread)
+ /* STOPPING state prevents new thread from starting. */
+ h->offline_device_thread_state =
+ OFFLINE_DEVICE_THREAD_STOPPING;
+ spin_unlock_irqrestore(&h->offline_device_lock, flags);
+ if (stop_thread)
+ kthread_stop(h->offline_device_monitor);
+}
+
static void adjust_hpsa_scsi_table(struct ctlr_info *h, int hostno,
struct hpsa_scsi_dev_t *sd[], int nsds)
{
*/
if (sd[i]->format_in_progress) {
dev_info(&h->pdev->dev,
- "Logical drive format in progress, device c%db%dt%dl%d offline.\n",
+ "c%db%dt%dl%d: Logical drive parity initialization, erase or format in progress\n",
+ h->scsi_host->host_no,
+ sd[i]->bus, sd[i]->target, sd[i]->lun);
+ dev_info(&h->pdev->dev, "c%db%dt%dl%d: temporarily offline\n",
h->scsi_host->host_no,
sd[i]->bus, sd[i]->target, sd[i]->lun);
continue;
}
spin_unlock_irqrestore(&h->devlock, flags);
+ /* Monitor devices which are NOT READY, FORMAT IN PROGRESS to be
+ * brought online later. This must be done without holding h->devlock,
+ * so don't touch h->dev[]
+ */
+ for (i = 0; i < nsds; i++) {
+ if (!sd[i]) /* if already added above. */
+ continue;
+ if (sd[i]->format_in_progress)
+ hpsa_monitor_offline_device(h, sd[i]->scsi3addr);
+ }
+
/* 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.
h->intr_mode = hpsa_simple_mode ? SIMPLE_MODE_INT : PERF_MODE_INT;
INIT_LIST_HEAD(&h->cmpQ);
INIT_LIST_HEAD(&h->reqQ);
+ INIT_LIST_HEAD(&h->offline_device_list);
spin_lock_init(&h->lock);
spin_lock_init(&h->scan_lock);
+ spin_lock_init(&h->offline_device_lock);
rc = hpsa_pci_init(h);
if (rc != 0)
goto clean1;
sprintf(h->devname, HPSA "%d", number_of_controllers);
h->ctlr = number_of_controllers;
number_of_controllers++;
+ h->offline_device_thread_state = OFFLINE_DEVICE_THREAD_STOPPED;
/* configure PCI DMA stuff */
rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(64));
}
h = pci_get_drvdata(pdev);
stop_controller_lockup_detector(h);
+ stop_offline_device_monitor(h);
hpsa_unregister_scsi(h); /* unhook from SCSI subsystem */
hpsa_shutdown(pdev);
iounmap(h->vaddr);