static void zfcp_erp_action_cleanup(struct zfcp_erp_action *act, int result) { struct zfcp_adapter *adapter = act->adapter; struct zfcp_port *port = act->port; struct zfcp_unit *unit = act->unit; switch (act->action) { case ZFCP_ERP_ACTION_REOPEN_UNIT: if ((result == ZFCP_ERP_SUCCEEDED) && !unit->device) { zfcp_unit_get(unit); if (scsi_queue_work(unit->port->adapter->scsi_host, &unit->scsi_work) <= 0) zfcp_unit_put(unit); } zfcp_unit_put(unit); break; case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED: case ZFCP_ERP_ACTION_REOPEN_PORT: if (result == ZFCP_ERP_SUCCEEDED) zfcp_scsi_schedule_rport_register(port); zfcp_port_put(port); break; case ZFCP_ERP_ACTION_REOPEN_ADAPTER: if (result == ZFCP_ERP_SUCCEEDED) { register_service_level(&adapter->service_level); schedule_work(&adapter->scan_work); } else unregister_service_level(&adapter->service_level); zfcp_adapter_put(adapter); break; } }
static ssize_t zfcp_sysfs_unit_remove_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct zfcp_port *port = dev_get_drvdata(dev); struct zfcp_unit *unit; u64 fcp_lun; LIST_HEAD(unit_remove_lh); struct scsi_device *sdev; mutex_lock(&zfcp_data.config_mutex); if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_REMOVE) { mutex_unlock(&zfcp_data.config_mutex); return -EBUSY; } if (strict_strtoull(buf, 0, (unsigned long long *) &fcp_lun)) { mutex_unlock(&zfcp_data.config_mutex); return -EINVAL; } read_lock_irq(&zfcp_data.config_lock); unit = zfcp_get_unit_by_lun(port, fcp_lun); read_unlock_irq(&zfcp_data.config_lock); if (!unit) { mutex_unlock(&zfcp_data.config_mutex); return -ENXIO; } zfcp_unit_get(unit); mutex_unlock(&zfcp_data.config_mutex); sdev = scsi_device_lookup(port->adapter->scsi_host, 0, port->starget_id, scsilun_to_int((struct scsi_lun *)&fcp_lun)); if (sdev) { scsi_remove_device(sdev); scsi_device_put(sdev); } mutex_lock(&zfcp_data.config_mutex); zfcp_unit_put(unit); if (atomic_read(&unit->refcount)) { mutex_unlock(&zfcp_data.config_mutex); return -ENXIO; } write_lock_irq(&zfcp_data.config_lock); atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status); list_move(&unit->list, &unit_remove_lh); write_unlock_irq(&zfcp_data.config_lock); mutex_unlock(&zfcp_data.config_mutex); zfcp_erp_unit_shutdown(unit, 0, "syurs_1", NULL); zfcp_erp_wait(unit->port->adapter); zfcp_unit_dequeue(unit); return (ssize_t)count; }
static ssize_t zfcp_sysfs_unit_remove_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct zfcp_port *port = dev_get_drvdata(dev); struct zfcp_unit *unit; u64 fcp_lun; int retval = 0; LIST_HEAD(unit_remove_lh); mutex_lock(&zfcp_data.config_mutex); if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_REMOVE) { retval = -EBUSY; goto out; } if (strict_strtoull(buf, 0, (unsigned long long *) &fcp_lun)) { retval = -EINVAL; goto out; } write_lock_irq(&zfcp_data.config_lock); unit = zfcp_get_unit_by_lun(port, fcp_lun); if (unit) { write_unlock_irq(&zfcp_data.config_lock); flush_work(&unit->scsi_work); write_lock_irq(&zfcp_data.config_lock); if (atomic_read(&unit->refcount) == 0) { zfcp_unit_get(unit); atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status); list_move(&unit->list, &unit_remove_lh); } else { unit = NULL; } } write_unlock_irq(&zfcp_data.config_lock); if (!unit) { retval = -ENXIO; goto out; } zfcp_erp_unit_shutdown(unit, 0, "syurs_1", NULL); zfcp_erp_wait(unit->port->adapter); zfcp_unit_put(unit); zfcp_unit_dequeue(unit); out: mutex_unlock(&zfcp_data.config_mutex); return retval ? retval : (ssize_t) count; }
static struct zfcp_erp_action *zfcp_erp_setup_act(int need, struct zfcp_adapter *adapter, struct zfcp_port *port, struct zfcp_unit *unit) { struct zfcp_erp_action *erp_action; u32 status = 0; switch (need) { case ZFCP_ERP_ACTION_REOPEN_UNIT: zfcp_unit_get(unit); atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &unit->status); erp_action = &unit->erp_action; if (!(atomic_read(&unit->status) & ZFCP_STATUS_COMMON_RUNNING)) status = ZFCP_STATUS_ERP_CLOSE_ONLY; break; case ZFCP_ERP_ACTION_REOPEN_PORT: case ZFCP_ERP_ACTION_REOPEN_PORT_FORCED: zfcp_port_get(port); zfcp_erp_action_dismiss_port(port); atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &port->status); erp_action = &port->erp_action; if (!(atomic_read(&port->status) & ZFCP_STATUS_COMMON_RUNNING)) status = ZFCP_STATUS_ERP_CLOSE_ONLY; break; case ZFCP_ERP_ACTION_REOPEN_ADAPTER: zfcp_adapter_get(adapter); zfcp_erp_action_dismiss_adapter(adapter); atomic_set_mask(ZFCP_STATUS_COMMON_ERP_INUSE, &adapter->status); erp_action = &adapter->erp_action; if (!(atomic_read(&adapter->status) & ZFCP_STATUS_COMMON_RUNNING)) status = ZFCP_STATUS_ERP_CLOSE_ONLY; break; default: return NULL; } memset(erp_action, 0, sizeof(struct zfcp_erp_action)); erp_action->adapter = adapter; erp_action->port = port; erp_action->unit = unit; erp_action->action = need; erp_action->status = status; return erp_action; }
/** * zfcp_sysfs_unit_remove_store - remove a unit from sysfs tree * @dev: pointer to belonging device * @buf: pointer to input buffer * @count: number of bytes in buffer */ static ssize_t zfcp_sysfs_unit_remove_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct zfcp_port *port; struct zfcp_unit *unit; fcp_lun_t fcp_lun; char *endp; int retval = 0; down(&zfcp_data.config_sema); port = dev_get_drvdata(dev); if (atomic_test_mask(ZFCP_STATUS_COMMON_REMOVE, &port->status)) { retval = -EBUSY; goto out; } fcp_lun = simple_strtoull(buf, &endp, 0); if ((endp + 1) < (buf + count)) { retval = -EINVAL; goto out; } write_lock_irq(&zfcp_data.config_lock); unit = zfcp_get_unit_by_lun(port, fcp_lun); if (unit && (atomic_read(&unit->refcount) == 0)) { zfcp_unit_get(unit); atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status); list_move(&unit->list, &port->unit_remove_lh); } else { unit = NULL; } write_unlock_irq(&zfcp_data.config_lock); if (!unit) { retval = -ENXIO; goto out; } zfcp_erp_unit_shutdown(unit, 0); zfcp_erp_wait(unit->port->adapter); zfcp_unit_put(unit); zfcp_unit_dequeue(unit); out: up(&zfcp_data.config_sema); return retval ? retval : (ssize_t) count; }
/** * zfcp_unit_enqueue - enqueue unit to unit list of a port. * @port: pointer to port where unit is added * @fcp_lun: FCP LUN of unit to be enqueued * Returns: pointer to enqueued unit on success, ERR_PTR on error * Locks: config_sema must be held to serialize changes to the unit list * * Sets up some unit internal structures and creates sysfs entry. */ struct zfcp_unit *zfcp_unit_enqueue(struct zfcp_port *port, u64 fcp_lun) { struct zfcp_unit *unit; unit = kzalloc(sizeof(struct zfcp_unit), GFP_KERNEL); if (!unit) return ERR_PTR(-ENOMEM); atomic_set(&unit->refcount, 0); init_waitqueue_head(&unit->remove_wq); unit->port = port; unit->fcp_lun = fcp_lun; dev_set_name(&unit->sysfs_device, "0x%016llx", (unsigned long long) fcp_lun); unit->sysfs_device.parent = &port->sysfs_device; unit->sysfs_device.release = zfcp_sysfs_unit_release; dev_set_drvdata(&unit->sysfs_device, unit); /* mark unit unusable as long as sysfs registration is not complete */ atomic_set_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status); spin_lock_init(&unit->latencies.lock); unit->latencies.write.channel.min = 0xFFFFFFFF; unit->latencies.write.fabric.min = 0xFFFFFFFF; unit->latencies.read.channel.min = 0xFFFFFFFF; unit->latencies.read.fabric.min = 0xFFFFFFFF; unit->latencies.cmd.channel.min = 0xFFFFFFFF; unit->latencies.cmd.fabric.min = 0xFFFFFFFF; read_lock_irq(&zfcp_data.config_lock); if (zfcp_get_unit_by_lun(port, fcp_lun)) { read_unlock_irq(&zfcp_data.config_lock); goto err_out_free; } read_unlock_irq(&zfcp_data.config_lock); if (device_register(&unit->sysfs_device)) goto err_out_free; if (sysfs_create_group(&unit->sysfs_device.kobj, &zfcp_sysfs_unit_attrs)) { device_unregister(&unit->sysfs_device); return ERR_PTR(-EIO); } zfcp_unit_get(unit); write_lock_irq(&zfcp_data.config_lock); list_add_tail(&unit->list, &port->unit_list_head); atomic_clear_mask(ZFCP_STATUS_COMMON_REMOVE, &unit->status); atomic_set_mask(ZFCP_STATUS_COMMON_RUNNING, &unit->status); write_unlock_irq(&zfcp_data.config_lock); zfcp_port_get(port); return unit; err_out_free: kfree(unit); return ERR_PTR(-EINVAL); }