@@ -4405,6 +4405,9 @@ fdls_process_rscn(struct fnic_iport_s *iport, struct fc_frame_header *fchdr)
int newports = 0;
struct fnic_fdls_fabric_s *fdls = &iport->fabric;
struct fnic *fnic = iport->fnic;
+ int rscn_type = NOT_PC_RSCN;
+ uint32_t sid = ntoh24(fchdr->fh_s_id);
+ unsigned long reset_fnic_list_lock_flags = 0;
uint16_t rscn_payload_len;
atomic64_inc(&iport->iport_stats.num_rscns);
@@ -4427,16 +4430,24 @@ fdls_process_rscn(struct fnic_iport_s *iport, struct fc_frame_header *fchdr)
|| (rscn_payload_len > 1024)
|| (rscn->els.rscn_page_len != 4)) {
num_ports = 0;
- FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ if ((rscn_payload_len == 0xFFFF)
+ && (sid == FC_FID_FCTRL)) {
+ rscn_type = PC_RSCN;
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "pcrscn: PCRSCN received. sid: 0x%x payload len: 0x%x",
+ sid, rscn_payload_len);
+ } else {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
"RSCN payload_len: 0x%x page_len: 0x%x",
rscn_payload_len, rscn->els.rscn_page_len);
- /* if this happens then we need to send ADISC to all the tports. */
- list_for_each_entry_safe(tport, next, &iport->tport_list, links) {
- if (tport->state == FDLS_TGT_STATE_READY)
- tport->flags |= FNIC_FDLS_TPORT_SEND_ADISC;
- FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
- "RSCN for port id: 0x%x", tport->fcid);
- }
+ /* if this happens then we need to send ADISC to all the tports. */
+ list_for_each_entry_safe(tport, next, &iport->tport_list, links) {
+ if (tport->state == FDLS_TGT_STATE_READY)
+ tport->flags |= FNIC_FDLS_TPORT_SEND_ADISC;
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "RSCN for port id: 0x%x", tport->fcid);
+ }
+ } /* end else */
} else {
num_ports = (rscn_payload_len - 4) / rscn->els.rscn_page_len;
rscn_port = (struct fc_els_rscn_page *)(rscn + 1);
@@ -4483,10 +4494,37 @@ fdls_process_rscn(struct fnic_iport_s *iport, struct fc_frame_header *fchdr)
tport->flags |= FNIC_FDLS_TPORT_SEND_ADISC;
}
- FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ if (pc_rscn_handling_feature_flag == PC_RSCN_HANDLING_FEATURE_ON &&
+ rscn_type == PC_RSCN && fnic->role == FNIC_ROLE_FCP_INITIATOR) {
+
+ if (fnic->pc_rscn_handling_status == PC_RSCN_HANDLING_IN_PROGRESS) {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "PCRSCN handling already in progress. Skip host reset: %d",
+ iport->fnic->fnic_num);
+ return;
+ }
+
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
+ "Processing PCRSCN. Queuing fnic for host reset: %d",
+ iport->fnic->fnic_num);
+ fnic->pc_rscn_handling_status = PC_RSCN_HANDLING_IN_PROGRESS;
+
+ spin_unlock_irqrestore(&fnic->fnic_lock, fnic->lock_flags);
+
+ spin_lock_irqsave(&reset_fnic_list_lock,
+ reset_fnic_list_lock_flags);
+ list_add_tail(&fnic->links, &reset_fnic_list);
+ spin_unlock_irqrestore(&reset_fnic_list_lock,
+ reset_fnic_list_lock_flags);
+
+ queue_work(reset_fnic_work_queue, &reset_fnic_work);
+ spin_lock_irqsave(&fnic->fnic_lock, fnic->lock_flags);
+ } else {
+ FNIC_FCS_DBG(KERN_INFO, fnic->host, fnic->fnic_num,
"FDLS process RSCN sending GPN_FT: newports: %d", newports);
fdls_send_gpn_ft(iport, FDLS_STATE_RSCN_GPN_FT);
fdls_send_rscn_resp(iport, fchdr);
+ }
}
void fnic_fdls_disc_start(struct fnic_iport_s *iport)
@@ -211,11 +211,33 @@ enum reset_states {
RESET_ERROR
};
+enum rscn_type {
+ NOT_PC_RSCN = 0,
+ PC_RSCN
+};
+
+enum pc_rscn_handling_status {
+ PC_RSCN_HANDLING_NOT_IN_PROGRESS = 0,
+ PC_RSCN_HANDLING_IN_PROGRESS
+};
+
+enum pc_rscn_handling_feature {
+ PC_RSCN_HANDLING_FEATURE_OFF = 0,
+ PC_RSCN_HANDLING_FEATURE_ON
+};
+
extern unsigned int fnic_fdmi_support;
extern unsigned int fnic_log_level;
extern unsigned int io_completions;
extern struct workqueue_struct *fnic_event_queue;
+extern unsigned int pc_rscn_handling_feature_flag;
+extern spinlock_t reset_fnic_list_lock;
+extern struct list_head reset_fnic_list;
+extern struct workqueue_struct *reset_fnic_work_queue;
+extern struct work_struct reset_fnic_work;
+
+
#define FNIC_MAIN_LOGGING 0x01
#define FNIC_FCS_LOGGING 0x02
#define FNIC_SCSI_LOGGING 0x04
@@ -396,6 +418,7 @@ struct fnic {
int link_status;
struct list_head list;
+ struct list_head links;
struct pci_dev *pdev;
struct vnic_fc_config config;
struct vnic_dev *vdev;
@@ -424,6 +447,7 @@ struct fnic {
char subsys_desc[14];
int subsys_desc_len;
+ int pc_rscn_handling_status;
/*** FIP related data members -- start ***/
void (*set_vlan)(struct fnic *, u16 vlan);
@@ -508,6 +532,7 @@ void fnic_stats_debugfs_remove(struct fnic *fnic);
int fnic_is_abts_pending(struct fnic *, struct scsi_cmnd *);
void fnic_handle_fip_frame(struct work_struct *work);
+void fnic_reset_work_handler(struct work_struct *work);
void fnic_handle_fip_event(struct fnic *fnic);
void fnic_fcoe_reset_vlans(struct fnic *fnic);
extern void fnic_handle_fip_timer(struct timer_list *t);
@@ -1071,3 +1071,39 @@ void fnic_flush_tport_event_list(struct fnic *fnic)
}
spin_unlock_irqrestore(&fnic->fnic_lock, flags);
}
+
+void fnic_reset_work_handler(struct work_struct *work)
+{
+ struct fnic *cur_fnic, *next_fnic;
+ unsigned long reset_fnic_list_lock_flags;
+ int host_reset_ret_code;
+
+ /*
+ * This is a single thread. It is per fnic module, not per fnic
+ * All the fnics that need to be reset
+ * have been serialized via the reset fnic list.
+ */
+ spin_lock_irqsave(&reset_fnic_list_lock, reset_fnic_list_lock_flags);
+ list_for_each_entry_safe(cur_fnic, next_fnic, &reset_fnic_list, links) {
+ list_del(&cur_fnic->links);
+ spin_unlock_irqrestore(&reset_fnic_list_lock,
+ reset_fnic_list_lock_flags);
+
+ dev_err(&cur_fnic->pdev->dev, "fnic: <%d>: issuing a host reset\n",
+ cur_fnic->fnic_num);
+ host_reset_ret_code = fnic_host_reset(cur_fnic->host);
+ dev_err(&cur_fnic->pdev->dev,
+ "fnic: <%d>: returned from host reset with status: %d\n",
+ cur_fnic->fnic_num, host_reset_ret_code);
+
+ spin_lock_irqsave(&cur_fnic->fnic_lock, cur_fnic->lock_flags);
+ cur_fnic->pc_rscn_handling_status =
+ PC_RSCN_HANDLING_NOT_IN_PROGRESS;
+ spin_unlock_irqrestore(&cur_fnic->fnic_lock, cur_fnic->lock_flags);
+
+ spin_lock_irqsave(&reset_fnic_list_lock,
+ reset_fnic_list_lock_flags);
+ }
+ spin_unlock_irqrestore(&reset_fnic_list_lock,
+ reset_fnic_list_lock_flags);
+}
@@ -45,6 +45,10 @@ static LIST_HEAD(fnic_list);
static DEFINE_SPINLOCK(fnic_list_lock);
static DEFINE_IDA(fnic_ida);
+struct work_struct reset_fnic_work;
+LIST_HEAD(reset_fnic_list);
+DEFINE_SPINLOCK(reset_fnic_list_lock);
+
/* Supported devices by fnic module */
static struct pci_device_id fnic_id_table[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_CISCO, PCI_DEVICE_ID_CISCO_FNIC) },
@@ -89,6 +93,12 @@ static unsigned int fnic_max_qdepth = FNIC_DFLT_QUEUE_DEPTH;
module_param(fnic_max_qdepth, uint, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(fnic_max_qdepth, "Queue depth to report for each LUN");
+unsigned int pc_rscn_handling_feature_flag = PC_RSCN_HANDLING_FEATURE_ON;
+module_param(pc_rscn_handling_feature_flag, uint, 0644);
+MODULE_PARM_DESC(pc_rscn_handling_feature_flag,
+ "PCRSCN handling (0 for none. 1 to handle PCRSCN (default))");
+
+struct workqueue_struct *reset_fnic_work_queue;
struct workqueue_struct *fnic_fip_queue;
static int fnic_slave_alloc(struct scsi_device *sdev)
@@ -1275,6 +1285,19 @@ static int __init fnic_init_module(void)
goto err_create_fip_workq;
}
+ if (pc_rscn_handling_feature_flag == PC_RSCN_HANDLING_FEATURE_ON) {
+ reset_fnic_work_queue =
+ create_singlethread_workqueue("reset_fnic_work_queue");
+ if (!reset_fnic_work_queue) {
+ pr_err("reset fnic work queue create failed\n");
+ err = -ENOMEM;
+ goto err_create_reset_fnic_workq;
+ }
+ spin_lock_init(&reset_fnic_list_lock);
+ INIT_LIST_HEAD(&reset_fnic_list);
+ INIT_WORK(&reset_fnic_work, fnic_reset_work_handler);
+ }
+
fnic_fc_transport = fc_attach_transport(&fnic_fc_functions);
if (!fnic_fc_transport) {
printk(KERN_ERR PFX "fc_attach_transport error\n");
@@ -1295,6 +1318,9 @@ static int __init fnic_init_module(void)
err_fc_transport:
destroy_workqueue(fnic_fip_queue);
err_create_fip_workq:
+ if (pc_rscn_handling_feature_flag == PC_RSCN_HANDLING_FEATURE_ON)
+ destroy_workqueue(reset_fnic_work_queue);
+err_create_reset_fnic_workq:
destroy_workqueue(fnic_event_queue);
err_create_fnic_workq:
kmem_cache_destroy(fdls_frame_elem_cache);
@@ -1317,6 +1343,10 @@ static void __exit fnic_cleanup_module(void)
{
pci_unregister_driver(&fnic_driver);
destroy_workqueue(fnic_event_queue);
+
+ if (pc_rscn_handling_feature_flag == PC_RSCN_HANDLING_FEATURE_ON)
+ destroy_workqueue(reset_fnic_work_queue);
+
if (fnic_fip_queue) {
flush_workqueue(fnic_fip_queue);
destroy_workqueue(fnic_fip_queue);