static int __qedf_probe()

in qedf/qedf_main.c [3260:3694]


static int __qedf_probe(struct pci_dev *pdev, int mode)
{
	int rc = -EINVAL;
	struct fc_lport *lport;
	struct qedf_ctx *qedf = NULL;
	struct Scsi_Host *host;
	bool is_vf = false;
	struct qed_ll2_params params;
	char host_buf[20];
	struct qed_link_params link_params;
	int status;
	void *task_start, *task_end;
	struct qed_slowpath_params slowpath_params;
	struct qed_probe_params qed_params;
	u16 retry_cnt = 10;

	/*
	 * When doing error recovery we didn't reap the lport so don't try
	 * to reallocate it.
	 */
retry_probe:
	if (mode == QEDF_MODE_RECOVERY)
		msleep(2000);

	if (mode != QEDF_MODE_RECOVERY) {
		lport = libfc_host_alloc(&qedf_host_template,
		    sizeof(struct qedf_ctx));

		if (!lport) {
			QEDF_ERR(NULL, "Could not allocate lport.\n");
			rc = -ENOMEM;
			goto err0;
		}

		fc_disc_init(lport);

		/* Initialize qedf_ctx */
		qedf = lport_priv(lport);
		set_bit(QEDF_PROBING, &qedf->flags);
		qedf->lport = lport;
		qedf->ctlr.lp = lport;
		qedf->pdev = pdev;
		qedf->dbg_ctx.pdev = pdev;
		qedf->dbg_ctx.host_no = lport->host->host_no;
		spin_lock_init(&qedf->hba_lock);
		INIT_LIST_HEAD(&qedf->fcports);
		qedf->curr_conn_id = QEDF_MAX_SESSIONS - 1;
		atomic_set(&qedf->num_offloads, 0);
		qedf->stop_io_on_error = false;
		pci_set_drvdata(pdev, qedf);
		init_completion(&qedf->fipvlan_compl);
		mutex_init(&qedf->stats_mutex);
		mutex_init(&qedf->flush_mutex);
		qedf->flogi_pending = 0;

		QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_INFO,
		   "QLogic FastLinQ FCoE Module qedf %s, "
		   "FW %d.%d.%d.%d\n", QEDF_VERSION,
		   FW_MAJOR_VERSION, FW_MINOR_VERSION, FW_REVISION_VERSION,
		   FW_ENGINEERING_VERSION);
	} else {
		/* Init pointers during recovery */
		qedf = pci_get_drvdata(pdev);
		set_bit(QEDF_PROBING, &qedf->flags);
		lport = qedf->lport;
	}

	QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC, "Probe started.\n");

	host = lport->host;

	/* Allocate mempool for qedf_io_work structs */
	qedf->io_mempool = mempool_create_slab_pool(QEDF_IO_WORK_MIN,
	    qedf_io_work_cache);
	if (qedf->io_mempool == NULL) {
		QEDF_ERR(&(qedf->dbg_ctx), "qedf->io_mempool is NULL.\n");
		goto err1;
	}
	QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_INFO, "qedf->io_mempool=%p.\n",
	    qedf->io_mempool);

	sprintf(host_buf, "qedf_%u_link",
	    qedf->lport->host->host_no);
	qedf->link_update_wq = create_workqueue(host_buf);
	INIT_DELAYED_WORK(&qedf->link_update, qedf_handle_link_update);
	INIT_DELAYED_WORK(&qedf->link_recovery, qedf_link_recovery);
	INIT_DELAYED_WORK(&qedf->grcdump_work, qedf_wq_grcdump);
	INIT_DELAYED_WORK(&qedf->stag_work, qedf_stag_change_work);
	qedf->fipvlan_retries = qedf_fipvlan_retries;
	/* Set a default prio in case DCBX doesn't converge */
	if (qedf_default_prio > -1) {
		/*
		 * This is the case where we pass a modparam in so we want to
		 * honor it even if dcbx doesn't converge.
		 */
		qedf->prio = qedf_default_prio;
	} else
		qedf->prio = QEDF_DEFAULT_PRIO;

	/*
	 * Common probe. Takes care of basic hardware init and pci_*
	 * functions.
	 */
	memset(&qed_params, 0, sizeof(qed_params));
	qed_params.protocol = QED_PROTOCOL_FCOE;
	qed_params.dp_module = qedf_dp_module;
	qed_params.dp_level = qedf_dp_level;
	qed_params.is_vf = is_vf;
	qedf->cdev = qed_ops->common->probe(pdev, &qed_params);
	if (!qedf->cdev) {
		if ((mode == QEDF_MODE_RECOVERY) && retry_cnt) {
			QEDF_ERR(&qedf->dbg_ctx,
				"Retry %d initialize hardware\n", retry_cnt);
			retry_cnt--;
			goto retry_probe;
		}
		QEDF_ERR(&qedf->dbg_ctx, "common probe failed.\n");
		rc = -ENODEV;
		goto err1;
	}

	/* Learn information crucial for qedf to progress */
	rc = qed_ops->fill_dev_info(qedf->cdev, &qedf->dev_info);
	if (rc) {
		QEDF_ERR(&(qedf->dbg_ctx), "Failed to dev info.\n");
		goto err1;
	}

	QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC,
		  "dev_info: num_hwfns=%d affin_hwfn_idx=%d.\n",
		  qedf->dev_info.common.num_hwfns,
		  qed_ops->common->get_affin_hwfn_idx(qedf->cdev));

	/* queue allocation code should come here
	 * order should be
	 * 	slowpath_start
	 * 	status block allocation
	 *	interrupt registration (to get min number of queues)
	 *	set_fcoe_pf_param
	 *	qed_sp_fcoe_func_start
	 */
	rc = qedf_set_fcoe_pf_param(qedf);
	if (rc) {
		QEDF_ERR(&(qedf->dbg_ctx), "Cannot set fcoe pf param.\n");
		goto err2;
	}
	qed_ops->common->update_pf_params(qedf->cdev, &qedf->pf_params);

	/* Learn information crucial for qedf to progress */
	rc = qed_ops->fill_dev_info(qedf->cdev, &qedf->dev_info);
	if (rc) {
		QEDF_ERR(&qedf->dbg_ctx, "Failed to fill dev info.\n");
		goto err2;
	}

	if (mode != QEDF_MODE_RECOVERY) {
		qedf->devlink = qed_ops->common->devlink_register(qedf->cdev);
		if (IS_ERR(qedf->devlink)) {
			QEDF_ERR(&qedf->dbg_ctx, "Cannot register devlink\n");
			rc = PTR_ERR(qedf->devlink);
			qedf->devlink = NULL;
			goto err2;
		}
	}

	/* Record BDQ producer doorbell addresses */
	qedf->bdq_primary_prod = qedf->dev_info.primary_dbq_rq_addr;
	qedf->bdq_secondary_prod = qedf->dev_info.secondary_bdq_rq_addr;
	QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
	    "BDQ primary_prod=%p secondary_prod=%p.\n", qedf->bdq_primary_prod,
	    qedf->bdq_secondary_prod);

	qed_ops->register_ops(qedf->cdev, &qedf_cb_ops, qedf);

	rc = qedf_prepare_sb(qedf);
	if (rc) {

		QEDF_ERR(&(qedf->dbg_ctx), "Cannot start slowpath.\n");
		goto err2;
	}

	/* Start the Slowpath-process */
	slowpath_params.int_mode = QED_INT_MODE_MSIX;
	slowpath_params.drv_major = QEDF_DRIVER_MAJOR_VER;
	slowpath_params.drv_minor = QEDF_DRIVER_MINOR_VER;
	slowpath_params.drv_rev = QEDF_DRIVER_REV_VER;
	slowpath_params.drv_eng = QEDF_DRIVER_ENG_VER;
	strncpy(slowpath_params.name, "qedf", QED_DRV_VER_STR_SIZE);
	rc = qed_ops->common->slowpath_start(qedf->cdev, &slowpath_params);
	if (rc) {
		QEDF_ERR(&(qedf->dbg_ctx), "Cannot start slowpath.\n");
		goto err2;
	}

	/*
	 * update_pf_params needs to be called before and after slowpath
	 * start
	 */
	qed_ops->common->update_pf_params(qedf->cdev, &qedf->pf_params);

	/* Setup interrupts */
	rc = qedf_setup_int(qedf);
	if (rc) {
		QEDF_ERR(&qedf->dbg_ctx, "Setup interrupts failed.\n");
		goto err3;
	}

	rc = qed_ops->start(qedf->cdev, &qedf->tasks);
	if (rc) {
		QEDF_ERR(&(qedf->dbg_ctx), "Cannot start FCoE function.\n");
		goto err4;
	}
	task_start = qedf_get_task_mem(&qedf->tasks, 0);
	task_end = qedf_get_task_mem(&qedf->tasks, MAX_TID_BLOCKS_FCOE - 1);
	QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "Task context start=%p, "
		   "end=%p block_size=%u.\n", task_start, task_end,
		   qedf->tasks.size);

	/*
	 * We need to write the number of BDs in the BDQ we've preallocated so
	 * the f/w will do a prefetch and we'll get an unsolicited CQE when a
	 * packet arrives.
	 */
	qedf->bdq_prod_idx = QEDF_BDQ_SIZE;
	QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
	    "Writing %d to primary and secondary BDQ doorbell registers.\n",
	    qedf->bdq_prod_idx);
	writew(qedf->bdq_prod_idx, qedf->bdq_primary_prod);
	readw(qedf->bdq_primary_prod);
	writew(qedf->bdq_prod_idx, qedf->bdq_secondary_prod);
	readw(qedf->bdq_secondary_prod);

	qed_ops->common->set_power_state(qedf->cdev, PCI_D0);

	/* Now that the dev_info struct has been filled in set the MAC
	 * address
	 */
	ether_addr_copy(qedf->mac, qedf->dev_info.common.hw_mac);
	QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC, "MAC address is %pM.\n",
		   qedf->mac);

	/*
	 * Set the WWNN and WWPN in the following way:
	 *
	 * If the info we get from qed is non-zero then use that to set the
	 * WWPN and WWNN. Otherwise fall back to use fcoe_wwn_from_mac() based
	 * on the MAC address.
	 */
	if (qedf->dev_info.wwnn != 0 && qedf->dev_info.wwpn != 0) {
		QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
		    "Setting WWPN and WWNN from qed dev_info.\n");
		qedf->wwnn = qedf->dev_info.wwnn;
		qedf->wwpn = qedf->dev_info.wwpn;
	} else {
		QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
		    "Setting WWPN and WWNN using fcoe_wwn_from_mac().\n");
		qedf->wwnn = fcoe_wwn_from_mac(qedf->mac, 1, 0);
		qedf->wwpn = fcoe_wwn_from_mac(qedf->mac, 2, 0);
	}
	QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,  "WWNN=%016llx "
		   "WWPN=%016llx.\n", qedf->wwnn, qedf->wwpn);

	sprintf(host_buf, "host_%d", host->host_no);
	qed_ops->common->set_name(qedf->cdev, host_buf);

	/* Allocate cmd mgr */
	qedf->cmd_mgr = qedf_cmd_mgr_alloc(qedf);
	if (!qedf->cmd_mgr) {
		QEDF_ERR(&(qedf->dbg_ctx), "Failed to allocate cmd mgr.\n");
		rc = -ENOMEM;
		goto err5;
	}

	if (mode != QEDF_MODE_RECOVERY) {
		host->transportt = qedf_fc_transport_template;
		host->max_lun = qedf_max_lun;
		host->max_cmd_len = QEDF_MAX_CDB_LEN;
		host->max_id = QEDF_MAX_SESSIONS;
		host->can_queue = FCOE_PARAMS_NUM_TASKS;
		rc = scsi_add_host(host, &pdev->dev);
		if (rc) {
			QEDF_WARN(&qedf->dbg_ctx,
				  "Error adding Scsi_Host rc=0x%x.\n", rc);
			goto err6;
		}
	}

	memset(&params, 0, sizeof(params));
	params.mtu = QEDF_LL2_BUF_SIZE;
	ether_addr_copy(params.ll2_mac_address, qedf->mac);

	/* Start LL2 processing thread */
	snprintf(host_buf, 20, "qedf_%d_ll2", host->host_no);
	qedf->ll2_recv_wq =
		create_workqueue(host_buf);
	if (!qedf->ll2_recv_wq) {
		QEDF_ERR(&(qedf->dbg_ctx), "Failed to LL2 workqueue.\n");
		rc = -ENOMEM;
		goto err7;
	}

#ifdef CONFIG_DEBUG_FS
	qedf_dbg_host_init(&(qedf->dbg_ctx), qedf_debugfs_ops,
			    qedf_dbg_fops);
#endif

	/* Start LL2 */
	qed_ops->ll2->register_cb_ops(qedf->cdev, &qedf_ll2_cb_ops, qedf);
	rc = qed_ops->ll2->start(qedf->cdev, &params);
	if (rc) {
		QEDF_ERR(&(qedf->dbg_ctx), "Could not start Light L2.\n");
		goto err7;
	}
	set_bit(QEDF_LL2_STARTED, &qedf->flags);

	/* Set initial FIP/FCoE VLAN to NULL */
	qedf->vlan_id = 0;

	/*
	 * No need to setup fcoe_ctlr or fc_lport objects during recovery since
	 * they were not reaped during the unload process.
	 */
	if (mode != QEDF_MODE_RECOVERY) {
		/* Setup imbedded fcoe controller */
		qedf_fcoe_ctlr_setup(qedf);

		/* Setup lport */
		rc = qedf_lport_setup(qedf);
		if (rc) {
			QEDF_ERR(&(qedf->dbg_ctx),
			    "qedf_lport_setup failed.\n");
			goto err7;
		}
	}

	sprintf(host_buf, "qedf_%u_timer", qedf->lport->host->host_no);
	qedf->timer_work_queue =
		create_workqueue(host_buf);
	if (!qedf->timer_work_queue) {
		QEDF_ERR(&(qedf->dbg_ctx), "Failed to start timer "
			  "workqueue.\n");
		rc = -ENOMEM;
		goto err7;
	}

	/* DPC workqueue is not reaped during recovery unload */
	if (mode != QEDF_MODE_RECOVERY) {
		sprintf(host_buf, "qedf_%u_dpc",
		    qedf->lport->host->host_no);
		qedf->dpc_wq = create_workqueue(host_buf);
	}
	INIT_DELAYED_WORK(&qedf->recovery_work, qedf_recovery_handler);

	/*
	 * GRC dump and sysfs parameters are not reaped during the recovery
	 * unload process.
	 */
	if (mode != QEDF_MODE_RECOVERY) {
		qedf->grcdump_size =
		    qed_ops->common->dbg_all_data_size(qedf->cdev);
		if (qedf->grcdump_size) {
			rc = qedf_alloc_grc_dump_buf(&qedf->grcdump,
			    qedf->grcdump_size);
			if (rc) {
				QEDF_ERR(&(qedf->dbg_ctx),
				    "GRC Dump buffer alloc failed.\n");
				qedf->grcdump = NULL;
			}

			QEDF_INFO(&(qedf->dbg_ctx), QEDF_LOG_DISC,
			    "grcdump: addr=%p, size=%u.\n",
			    qedf->grcdump, qedf->grcdump_size);
		}
		qedf_create_sysfs_ctx_attr(qedf);

		/* Initialize I/O tracing for this adapter */
		spin_lock_init(&qedf->io_trace_lock);
		qedf->io_trace_idx = 0;
	}

	init_completion(&qedf->flogi_compl);

	status = qed_ops->common->update_drv_state(qedf->cdev, true);
	if (status)
		QEDF_ERR(&(qedf->dbg_ctx),
			"Failed to send drv state to MFW.\n");

	memset(&link_params, 0, sizeof(struct qed_link_params));
	link_params.link_up = true;
	status = qed_ops->common->set_link(qedf->cdev, &link_params);
	if (status)
		QEDF_WARN(&(qedf->dbg_ctx), "set_link failed.\n");

	/* Start/restart discovery */
	if (mode == QEDF_MODE_RECOVERY)
		fcoe_ctlr_link_up(&qedf->ctlr);
	else
		fc_fabric_login(lport);

	QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC, "Probe done.\n");

	clear_bit(QEDF_PROBING, &qedf->flags);

	/* All good */
	return 0;

err7:
	if (qedf->ll2_recv_wq)
		destroy_workqueue(qedf->ll2_recv_wq);
	fc_remove_host(qedf->lport->host);
	scsi_remove_host(qedf->lport->host);
#ifdef CONFIG_DEBUG_FS
	qedf_dbg_host_exit(&(qedf->dbg_ctx));
#endif
err6:
	qedf_cmd_mgr_free(qedf->cmd_mgr);
err5:
	qed_ops->stop(qedf->cdev);
err4:
	qedf_free_fcoe_pf_param(qedf);
	qedf_sync_free_irqs(qedf);
err3:
	qed_ops->common->slowpath_stop(qedf->cdev);
err2:
	qed_ops->common->remove(qedf->cdev);
err1:
	scsi_host_put(lport->host);
err0:
	if (qedf) {
		QEDF_INFO(&qedf->dbg_ctx, QEDF_LOG_DISC, "Probe done.\n");

		clear_bit(QEDF_PROBING, &qedf->flags);
	}
	return rc;
}