18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright 2014 Cisco Systems, Inc.  All rights reserved.
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * This program is free software; you may redistribute it and/or modify
58c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License as published by
68c2ecf20Sopenharmony_ci * the Free Software Foundation; version 2 of the License.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
98c2ecf20Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
108c2ecf20Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
118c2ecf20Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
128c2ecf20Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
138c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
148c2ecf20Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
158c2ecf20Sopenharmony_ci * SOFTWARE.
168c2ecf20Sopenharmony_ci */
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <linux/errno.h>
198c2ecf20Sopenharmony_ci#include <linux/mempool.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include <scsi/scsi_tcq.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include "snic_disc.h"
248c2ecf20Sopenharmony_ci#include "snic.h"
258c2ecf20Sopenharmony_ci#include "snic_io.h"
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci/* snic target types */
298c2ecf20Sopenharmony_cistatic const char * const snic_tgt_type_str[] = {
308c2ecf20Sopenharmony_ci	[SNIC_TGT_DAS] = "DAS",
318c2ecf20Sopenharmony_ci	[SNIC_TGT_SAN] = "SAN",
328c2ecf20Sopenharmony_ci};
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_cistatic inline const char *
358c2ecf20Sopenharmony_cisnic_tgt_type_to_str(int typ)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	return ((typ > SNIC_TGT_NONE && typ <= SNIC_TGT_SAN) ?
388c2ecf20Sopenharmony_ci		 snic_tgt_type_str[typ] : "Unknown");
398c2ecf20Sopenharmony_ci}
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic const char * const snic_tgt_state_str[] = {
428c2ecf20Sopenharmony_ci	[SNIC_TGT_STAT_INIT]	= "INIT",
438c2ecf20Sopenharmony_ci	[SNIC_TGT_STAT_ONLINE]	= "ONLINE",
448c2ecf20Sopenharmony_ci	[SNIC_TGT_STAT_OFFLINE]	= "OFFLINE",
458c2ecf20Sopenharmony_ci	[SNIC_TGT_STAT_DEL]	= "DELETION IN PROGRESS",
468c2ecf20Sopenharmony_ci};
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ciconst char *
498c2ecf20Sopenharmony_cisnic_tgt_state_to_str(int state)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	return ((state >= SNIC_TGT_STAT_INIT && state <= SNIC_TGT_STAT_DEL) ?
528c2ecf20Sopenharmony_ci		snic_tgt_state_str[state] : "UNKNOWN");
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci/*
568c2ecf20Sopenharmony_ci * Initiate report_tgt req desc
578c2ecf20Sopenharmony_ci */
588c2ecf20Sopenharmony_cistatic void
598c2ecf20Sopenharmony_cisnic_report_tgt_init(struct snic_host_req *req, u32 hid, u8 *buf, u32 len,
608c2ecf20Sopenharmony_ci		     dma_addr_t rsp_buf_pa, ulong ctx)
618c2ecf20Sopenharmony_ci{
628c2ecf20Sopenharmony_ci	struct snic_sg_desc *sgd = NULL;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	snic_io_hdr_enc(&req->hdr, SNIC_REQ_REPORT_TGTS, 0, SCSI_NO_TAG, hid,
668c2ecf20Sopenharmony_ci			1, ctx);
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	req->u.rpt_tgts.sg_cnt = cpu_to_le16(1);
698c2ecf20Sopenharmony_ci	sgd = req_to_sgl(req);
708c2ecf20Sopenharmony_ci	sgd[0].addr = cpu_to_le64(rsp_buf_pa);
718c2ecf20Sopenharmony_ci	sgd[0].len = cpu_to_le32(len);
728c2ecf20Sopenharmony_ci	sgd[0]._resvd = 0;
738c2ecf20Sopenharmony_ci	req->u.rpt_tgts.sg_addr = cpu_to_le64((ulong)sgd);
748c2ecf20Sopenharmony_ci}
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci/*
778c2ecf20Sopenharmony_ci * snic_queue_report_tgt_req: Queues report target request.
788c2ecf20Sopenharmony_ci */
798c2ecf20Sopenharmony_cistatic int
808c2ecf20Sopenharmony_cisnic_queue_report_tgt_req(struct snic *snic)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	struct snic_req_info *rqi = NULL;
838c2ecf20Sopenharmony_ci	u32 ntgts, buf_len = 0;
848c2ecf20Sopenharmony_ci	u8 *buf = NULL;
858c2ecf20Sopenharmony_ci	dma_addr_t pa = 0;
868c2ecf20Sopenharmony_ci	int ret = 0;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	rqi = snic_req_init(snic, 1);
898c2ecf20Sopenharmony_ci	if (!rqi) {
908c2ecf20Sopenharmony_ci		ret = -ENOMEM;
918c2ecf20Sopenharmony_ci		goto error;
928c2ecf20Sopenharmony_ci	}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	if (snic->fwinfo.max_tgts)
958c2ecf20Sopenharmony_ci		ntgts = min_t(u32, snic->fwinfo.max_tgts, snic->shost->max_id);
968c2ecf20Sopenharmony_ci	else
978c2ecf20Sopenharmony_ci		ntgts = snic->shost->max_id;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	/* Allocate Response Buffer */
1008c2ecf20Sopenharmony_ci	SNIC_BUG_ON(ntgts == 0);
1018c2ecf20Sopenharmony_ci	buf_len = ntgts * sizeof(struct snic_tgt_id) + SNIC_SG_DESC_ALIGN;
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_ci	buf = kzalloc(buf_len, GFP_KERNEL|GFP_DMA);
1048c2ecf20Sopenharmony_ci	if (!buf) {
1058c2ecf20Sopenharmony_ci		snic_req_free(snic, rqi);
1068c2ecf20Sopenharmony_ci		SNIC_HOST_ERR(snic->shost, "Resp Buf Alloc Failed.\n");
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci		ret = -ENOMEM;
1098c2ecf20Sopenharmony_ci		goto error;
1108c2ecf20Sopenharmony_ci	}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	SNIC_BUG_ON((((unsigned long)buf) % SNIC_SG_DESC_ALIGN) != 0);
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	pa = dma_map_single(&snic->pdev->dev, buf, buf_len, DMA_FROM_DEVICE);
1158c2ecf20Sopenharmony_ci	if (dma_mapping_error(&snic->pdev->dev, pa)) {
1168c2ecf20Sopenharmony_ci		SNIC_HOST_ERR(snic->shost,
1178c2ecf20Sopenharmony_ci			      "Rpt-tgt rspbuf %p: PCI DMA Mapping Failed\n",
1188c2ecf20Sopenharmony_ci			      buf);
1198c2ecf20Sopenharmony_ci		kfree(buf);
1208c2ecf20Sopenharmony_ci		snic_req_free(snic, rqi);
1218c2ecf20Sopenharmony_ci		ret = -EINVAL;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci		goto error;
1248c2ecf20Sopenharmony_ci	}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci	SNIC_BUG_ON(pa == 0);
1288c2ecf20Sopenharmony_ci	rqi->sge_va = (ulong) buf;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	snic_report_tgt_init(rqi->req,
1318c2ecf20Sopenharmony_ci			     snic->config.hid,
1328c2ecf20Sopenharmony_ci			     buf,
1338c2ecf20Sopenharmony_ci			     buf_len,
1348c2ecf20Sopenharmony_ci			     pa,
1358c2ecf20Sopenharmony_ci			     (ulong)rqi);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	snic_handle_untagged_req(snic, rqi);
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	ret = snic_queue_wq_desc(snic, rqi->req, rqi->req_len);
1408c2ecf20Sopenharmony_ci	if (ret) {
1418c2ecf20Sopenharmony_ci		dma_unmap_single(&snic->pdev->dev, pa, buf_len,
1428c2ecf20Sopenharmony_ci				 DMA_FROM_DEVICE);
1438c2ecf20Sopenharmony_ci		kfree(buf);
1448c2ecf20Sopenharmony_ci		rqi->sge_va = 0;
1458c2ecf20Sopenharmony_ci		snic_release_untagged_req(snic, rqi);
1468c2ecf20Sopenharmony_ci		SNIC_HOST_ERR(snic->shost, "Queuing Report Tgts Failed.\n");
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci		goto error;
1498c2ecf20Sopenharmony_ci	}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	SNIC_DISC_DBG(snic->shost, "Report Targets Issued.\n");
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	return ret;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_cierror:
1568c2ecf20Sopenharmony_ci	SNIC_HOST_ERR(snic->shost,
1578c2ecf20Sopenharmony_ci		      "Queuing Report Targets Failed, err = %d\n",
1588c2ecf20Sopenharmony_ci		      ret);
1598c2ecf20Sopenharmony_ci	return ret;
1608c2ecf20Sopenharmony_ci} /* end of snic_queue_report_tgt_req */
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci/* call into SML */
1638c2ecf20Sopenharmony_cistatic void
1648c2ecf20Sopenharmony_cisnic_scsi_scan_tgt(struct work_struct *work)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	struct snic_tgt *tgt = container_of(work, struct snic_tgt, scan_work);
1678c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = dev_to_shost(&tgt->dev);
1688c2ecf20Sopenharmony_ci	unsigned long flags;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	SNIC_HOST_INFO(shost, "Scanning Target id 0x%x\n", tgt->id);
1718c2ecf20Sopenharmony_ci	scsi_scan_target(&tgt->dev,
1728c2ecf20Sopenharmony_ci			 tgt->channel,
1738c2ecf20Sopenharmony_ci			 tgt->scsi_tgt_id,
1748c2ecf20Sopenharmony_ci			 SCAN_WILD_CARD,
1758c2ecf20Sopenharmony_ci			 SCSI_SCAN_RESCAN);
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci	spin_lock_irqsave(shost->host_lock, flags);
1788c2ecf20Sopenharmony_ci	tgt->flags &= ~SNIC_TGT_SCAN_PENDING;
1798c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(shost->host_lock, flags);
1808c2ecf20Sopenharmony_ci} /* end of snic_scsi_scan_tgt */
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci/*
1838c2ecf20Sopenharmony_ci * snic_tgt_lookup :
1848c2ecf20Sopenharmony_ci */
1858c2ecf20Sopenharmony_cistatic struct snic_tgt *
1868c2ecf20Sopenharmony_cisnic_tgt_lookup(struct snic *snic, struct snic_tgt_id *tgtid)
1878c2ecf20Sopenharmony_ci{
1888c2ecf20Sopenharmony_ci	struct list_head *cur, *nxt;
1898c2ecf20Sopenharmony_ci	struct snic_tgt *tgt = NULL;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci	list_for_each_safe(cur, nxt, &snic->disc.tgt_list) {
1928c2ecf20Sopenharmony_ci		tgt = list_entry(cur, struct snic_tgt, list);
1938c2ecf20Sopenharmony_ci		if (tgt->id == le32_to_cpu(tgtid->tgt_id))
1948c2ecf20Sopenharmony_ci			return tgt;
1958c2ecf20Sopenharmony_ci		tgt = NULL;
1968c2ecf20Sopenharmony_ci	}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	return tgt;
1998c2ecf20Sopenharmony_ci} /* end of snic_tgt_lookup */
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci/*
2028c2ecf20Sopenharmony_ci * snic_tgt_dev_release : Called on dropping last ref for snic_tgt object
2038c2ecf20Sopenharmony_ci */
2048c2ecf20Sopenharmony_civoid
2058c2ecf20Sopenharmony_cisnic_tgt_dev_release(struct device *dev)
2068c2ecf20Sopenharmony_ci{
2078c2ecf20Sopenharmony_ci	struct snic_tgt *tgt = dev_to_tgt(dev);
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	SNIC_HOST_INFO(snic_tgt_to_shost(tgt),
2108c2ecf20Sopenharmony_ci		       "Target Device ID %d (%s) Permanently Deleted.\n",
2118c2ecf20Sopenharmony_ci		       tgt->id,
2128c2ecf20Sopenharmony_ci		       dev_name(dev));
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	SNIC_BUG_ON(!list_empty(&tgt->list));
2158c2ecf20Sopenharmony_ci	kfree(tgt);
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci/*
2198c2ecf20Sopenharmony_ci * snic_tgt_del : work function to delete snic_tgt
2208c2ecf20Sopenharmony_ci */
2218c2ecf20Sopenharmony_cistatic void
2228c2ecf20Sopenharmony_cisnic_tgt_del(struct work_struct *work)
2238c2ecf20Sopenharmony_ci{
2248c2ecf20Sopenharmony_ci	struct snic_tgt *tgt = container_of(work, struct snic_tgt, del_work);
2258c2ecf20Sopenharmony_ci	struct Scsi_Host *shost = snic_tgt_to_shost(tgt);
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	if (tgt->flags & SNIC_TGT_SCAN_PENDING)
2288c2ecf20Sopenharmony_ci		scsi_flush_work(shost);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	/* Block IOs on child devices, stops new IOs */
2318c2ecf20Sopenharmony_ci	scsi_target_block(&tgt->dev);
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	/* Cleanup IOs */
2348c2ecf20Sopenharmony_ci	snic_tgt_scsi_abort_io(tgt);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	/* Unblock IOs now, to flush if there are any. */
2378c2ecf20Sopenharmony_ci	scsi_target_unblock(&tgt->dev, SDEV_TRANSPORT_OFFLINE);
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	/* Delete SCSI Target and sdevs */
2408c2ecf20Sopenharmony_ci	scsi_remove_target(&tgt->dev);  /* ?? */
2418c2ecf20Sopenharmony_ci	device_del(&tgt->dev);
2428c2ecf20Sopenharmony_ci	put_device(&tgt->dev);
2438c2ecf20Sopenharmony_ci} /* end of snic_tgt_del */
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci/* snic_tgt_create: checks for existence of snic_tgt, if it doesn't
2468c2ecf20Sopenharmony_ci * it creates one.
2478c2ecf20Sopenharmony_ci */
2488c2ecf20Sopenharmony_cistatic struct snic_tgt *
2498c2ecf20Sopenharmony_cisnic_tgt_create(struct snic *snic, struct snic_tgt_id *tgtid)
2508c2ecf20Sopenharmony_ci{
2518c2ecf20Sopenharmony_ci	struct snic_tgt *tgt = NULL;
2528c2ecf20Sopenharmony_ci	unsigned long flags;
2538c2ecf20Sopenharmony_ci	int ret;
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	tgt = snic_tgt_lookup(snic, tgtid);
2568c2ecf20Sopenharmony_ci	if (tgt) {
2578c2ecf20Sopenharmony_ci		/* update the information if required */
2588c2ecf20Sopenharmony_ci		return tgt;
2598c2ecf20Sopenharmony_ci	}
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	tgt = kzalloc(sizeof(*tgt), GFP_KERNEL);
2628c2ecf20Sopenharmony_ci	if (!tgt) {
2638c2ecf20Sopenharmony_ci		SNIC_HOST_ERR(snic->shost, "Failure to allocate snic_tgt.\n");
2648c2ecf20Sopenharmony_ci		ret = -ENOMEM;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci		return tgt;
2678c2ecf20Sopenharmony_ci	}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&tgt->list);
2708c2ecf20Sopenharmony_ci	tgt->id = le32_to_cpu(tgtid->tgt_id);
2718c2ecf20Sopenharmony_ci	tgt->channel = 0;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	SNIC_BUG_ON(le16_to_cpu(tgtid->tgt_type) > SNIC_TGT_SAN);
2748c2ecf20Sopenharmony_ci	tgt->tdata.typ = le16_to_cpu(tgtid->tgt_type);
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	/*
2778c2ecf20Sopenharmony_ci	 * Plugging into SML Device Tree
2788c2ecf20Sopenharmony_ci	 */
2798c2ecf20Sopenharmony_ci	tgt->tdata.disc_id = 0;
2808c2ecf20Sopenharmony_ci	tgt->state = SNIC_TGT_STAT_INIT;
2818c2ecf20Sopenharmony_ci	device_initialize(&tgt->dev);
2828c2ecf20Sopenharmony_ci	tgt->dev.parent = get_device(&snic->shost->shost_gendev);
2838c2ecf20Sopenharmony_ci	tgt->dev.release = snic_tgt_dev_release;
2848c2ecf20Sopenharmony_ci	INIT_WORK(&tgt->scan_work, snic_scsi_scan_tgt);
2858c2ecf20Sopenharmony_ci	INIT_WORK(&tgt->del_work, snic_tgt_del);
2868c2ecf20Sopenharmony_ci	switch (tgt->tdata.typ) {
2878c2ecf20Sopenharmony_ci	case SNIC_TGT_DAS:
2888c2ecf20Sopenharmony_ci		dev_set_name(&tgt->dev, "snic_das_tgt:%d:%d-%d",
2898c2ecf20Sopenharmony_ci			     snic->shost->host_no, tgt->channel, tgt->id);
2908c2ecf20Sopenharmony_ci		break;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	case SNIC_TGT_SAN:
2938c2ecf20Sopenharmony_ci		dev_set_name(&tgt->dev, "snic_san_tgt:%d:%d-%d",
2948c2ecf20Sopenharmony_ci			     snic->shost->host_no, tgt->channel, tgt->id);
2958c2ecf20Sopenharmony_ci		break;
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	default:
2988c2ecf20Sopenharmony_ci		SNIC_HOST_INFO(snic->shost, "Target type Unknown Detected.\n");
2998c2ecf20Sopenharmony_ci		dev_set_name(&tgt->dev, "snic_das_tgt:%d:%d-%d",
3008c2ecf20Sopenharmony_ci			     snic->shost->host_no, tgt->channel, tgt->id);
3018c2ecf20Sopenharmony_ci		break;
3028c2ecf20Sopenharmony_ci	}
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	spin_lock_irqsave(snic->shost->host_lock, flags);
3058c2ecf20Sopenharmony_ci	list_add_tail(&tgt->list, &snic->disc.tgt_list);
3068c2ecf20Sopenharmony_ci	tgt->scsi_tgt_id = snic->disc.nxt_tgt_id++;
3078c2ecf20Sopenharmony_ci	tgt->state = SNIC_TGT_STAT_ONLINE;
3088c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(snic->shost->host_lock, flags);
3098c2ecf20Sopenharmony_ci
3108c2ecf20Sopenharmony_ci	SNIC_HOST_INFO(snic->shost,
3118c2ecf20Sopenharmony_ci		       "Tgt %d, type = %s detected. Adding..\n",
3128c2ecf20Sopenharmony_ci		       tgt->id, snic_tgt_type_to_str(tgt->tdata.typ));
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	ret = device_add(&tgt->dev);
3158c2ecf20Sopenharmony_ci	if (ret) {
3168c2ecf20Sopenharmony_ci		SNIC_HOST_ERR(snic->shost,
3178c2ecf20Sopenharmony_ci			      "Snic Tgt: device_add, with err = %d\n",
3188c2ecf20Sopenharmony_ci			      ret);
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci		put_device(&snic->shost->shost_gendev);
3218c2ecf20Sopenharmony_ci		spin_lock_irqsave(snic->shost->host_lock, flags);
3228c2ecf20Sopenharmony_ci		list_del(&tgt->list);
3238c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(snic->shost->host_lock, flags);
3248c2ecf20Sopenharmony_ci		put_device(&tgt->dev);
3258c2ecf20Sopenharmony_ci		tgt = NULL;
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci		return tgt;
3288c2ecf20Sopenharmony_ci	}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	SNIC_HOST_INFO(snic->shost, "Scanning %s.\n", dev_name(&tgt->dev));
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_ci	scsi_queue_work(snic->shost, &tgt->scan_work);
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci	return tgt;
3358c2ecf20Sopenharmony_ci} /* end of snic_tgt_create */
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci/* Handler for discovery */
3388c2ecf20Sopenharmony_civoid
3398c2ecf20Sopenharmony_cisnic_handle_tgt_disc(struct work_struct *work)
3408c2ecf20Sopenharmony_ci{
3418c2ecf20Sopenharmony_ci	struct snic *snic = container_of(work, struct snic, tgt_work);
3428c2ecf20Sopenharmony_ci	struct snic_tgt_id *tgtid = NULL;
3438c2ecf20Sopenharmony_ci	struct snic_tgt *tgt = NULL;
3448c2ecf20Sopenharmony_ci	unsigned long flags;
3458c2ecf20Sopenharmony_ci	int i;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	spin_lock_irqsave(&snic->snic_lock, flags);
3488c2ecf20Sopenharmony_ci	if (snic->in_remove) {
3498c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&snic->snic_lock, flags);
3508c2ecf20Sopenharmony_ci		kfree(snic->disc.rtgt_info);
3518c2ecf20Sopenharmony_ci
3528c2ecf20Sopenharmony_ci		return;
3538c2ecf20Sopenharmony_ci	}
3548c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&snic->snic_lock, flags);
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	mutex_lock(&snic->disc.mutex);
3578c2ecf20Sopenharmony_ci	/* Discover triggered during disc in progress */
3588c2ecf20Sopenharmony_ci	if (snic->disc.req_cnt) {
3598c2ecf20Sopenharmony_ci		snic->disc.state = SNIC_DISC_DONE;
3608c2ecf20Sopenharmony_ci		snic->disc.req_cnt = 0;
3618c2ecf20Sopenharmony_ci		mutex_unlock(&snic->disc.mutex);
3628c2ecf20Sopenharmony_ci		kfree(snic->disc.rtgt_info);
3638c2ecf20Sopenharmony_ci		snic->disc.rtgt_info = NULL;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci		SNIC_HOST_INFO(snic->shost, "tgt_disc: Discovery restart.\n");
3668c2ecf20Sopenharmony_ci		/* Start Discovery Again */
3678c2ecf20Sopenharmony_ci		snic_disc_start(snic);
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci		return;
3708c2ecf20Sopenharmony_ci	}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	tgtid = (struct snic_tgt_id *)snic->disc.rtgt_info;
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	SNIC_BUG_ON(snic->disc.rtgt_cnt == 0 || tgtid == NULL);
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	for (i = 0; i < snic->disc.rtgt_cnt; i++) {
3778c2ecf20Sopenharmony_ci		tgt = snic_tgt_create(snic, &tgtid[i]);
3788c2ecf20Sopenharmony_ci		if (!tgt) {
3798c2ecf20Sopenharmony_ci			int buf_sz = snic->disc.rtgt_cnt * sizeof(*tgtid);
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci			SNIC_HOST_ERR(snic->shost, "Failed to create tgt.\n");
3828c2ecf20Sopenharmony_ci			snic_hex_dump("rpt_tgt_rsp", (char *)tgtid, buf_sz);
3838c2ecf20Sopenharmony_ci			break;
3848c2ecf20Sopenharmony_ci		}
3858c2ecf20Sopenharmony_ci	}
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	snic->disc.rtgt_info = NULL;
3888c2ecf20Sopenharmony_ci	snic->disc.state = SNIC_DISC_DONE;
3898c2ecf20Sopenharmony_ci	mutex_unlock(&snic->disc.mutex);
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	SNIC_HOST_INFO(snic->shost, "Discovery Completed.\n");
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	kfree(tgtid);
3948c2ecf20Sopenharmony_ci} /* end of snic_handle_tgt_disc */
3958c2ecf20Sopenharmony_ci
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ciint
3988c2ecf20Sopenharmony_cisnic_report_tgt_cmpl_handler(struct snic *snic, struct snic_fw_req *fwreq)
3998c2ecf20Sopenharmony_ci{
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci	u8 typ, cmpl_stat;
4028c2ecf20Sopenharmony_ci	u32 cmnd_id, hid, tgt_cnt = 0;
4038c2ecf20Sopenharmony_ci	ulong ctx;
4048c2ecf20Sopenharmony_ci	struct snic_req_info *rqi = NULL;
4058c2ecf20Sopenharmony_ci	struct snic_tgt_id *tgtid;
4068c2ecf20Sopenharmony_ci	int i, ret = 0;
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	snic_io_hdr_dec(&fwreq->hdr, &typ, &cmpl_stat, &cmnd_id, &hid, &ctx);
4098c2ecf20Sopenharmony_ci	rqi = (struct snic_req_info *) ctx;
4108c2ecf20Sopenharmony_ci	tgtid = (struct snic_tgt_id *) rqi->sge_va;
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	tgt_cnt = le32_to_cpu(fwreq->u.rpt_tgts_cmpl.tgt_cnt);
4138c2ecf20Sopenharmony_ci	if (tgt_cnt == 0) {
4148c2ecf20Sopenharmony_ci		SNIC_HOST_ERR(snic->shost, "No Targets Found on this host.\n");
4158c2ecf20Sopenharmony_ci		ret = 1;
4168c2ecf20Sopenharmony_ci
4178c2ecf20Sopenharmony_ci		goto end;
4188c2ecf20Sopenharmony_ci	}
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	/* printing list of targets here */
4218c2ecf20Sopenharmony_ci	SNIC_HOST_INFO(snic->shost, "Target Count = %d\n", tgt_cnt);
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	SNIC_BUG_ON(tgt_cnt > snic->fwinfo.max_tgts);
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_ci	for (i = 0; i < tgt_cnt; i++)
4268c2ecf20Sopenharmony_ci		SNIC_HOST_INFO(snic->shost,
4278c2ecf20Sopenharmony_ci			       "Tgt id = 0x%x\n",
4288c2ecf20Sopenharmony_ci			       le32_to_cpu(tgtid[i].tgt_id));
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_ci	/*
4318c2ecf20Sopenharmony_ci	 * Queue work for further processing,
4328c2ecf20Sopenharmony_ci	 * Response Buffer Memory is freed after creating targets
4338c2ecf20Sopenharmony_ci	 */
4348c2ecf20Sopenharmony_ci	snic->disc.rtgt_cnt = tgt_cnt;
4358c2ecf20Sopenharmony_ci	snic->disc.rtgt_info = (u8 *) tgtid;
4368c2ecf20Sopenharmony_ci	queue_work(snic_glob->event_q, &snic->tgt_work);
4378c2ecf20Sopenharmony_ci	ret = 0;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ciend:
4408c2ecf20Sopenharmony_ci	/* Unmap Response Buffer */
4418c2ecf20Sopenharmony_ci	snic_pci_unmap_rsp_buf(snic, rqi);
4428c2ecf20Sopenharmony_ci	if (ret)
4438c2ecf20Sopenharmony_ci		kfree(tgtid);
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	rqi->sge_va = 0;
4468c2ecf20Sopenharmony_ci	snic_release_untagged_req(snic, rqi);
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	return ret;
4498c2ecf20Sopenharmony_ci} /* end of snic_report_tgt_cmpl_handler */
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci/* Discovery init fn */
4528c2ecf20Sopenharmony_civoid
4538c2ecf20Sopenharmony_cisnic_disc_init(struct snic_disc *disc)
4548c2ecf20Sopenharmony_ci{
4558c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&disc->tgt_list);
4568c2ecf20Sopenharmony_ci	mutex_init(&disc->mutex);
4578c2ecf20Sopenharmony_ci	disc->disc_id = 0;
4588c2ecf20Sopenharmony_ci	disc->nxt_tgt_id = 0;
4598c2ecf20Sopenharmony_ci	disc->state = SNIC_DISC_INIT;
4608c2ecf20Sopenharmony_ci	disc->req_cnt = 0;
4618c2ecf20Sopenharmony_ci	disc->rtgt_cnt = 0;
4628c2ecf20Sopenharmony_ci	disc->rtgt_info = NULL;
4638c2ecf20Sopenharmony_ci	disc->cb = NULL;
4648c2ecf20Sopenharmony_ci} /* end of snic_disc_init */
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci/* Discovery, uninit fn */
4678c2ecf20Sopenharmony_civoid
4688c2ecf20Sopenharmony_cisnic_disc_term(struct snic *snic)
4698c2ecf20Sopenharmony_ci{
4708c2ecf20Sopenharmony_ci	struct snic_disc *disc = &snic->disc;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	mutex_lock(&disc->mutex);
4738c2ecf20Sopenharmony_ci	if (disc->req_cnt) {
4748c2ecf20Sopenharmony_ci		disc->req_cnt = 0;
4758c2ecf20Sopenharmony_ci		SNIC_SCSI_DBG(snic->shost, "Terminating Discovery.\n");
4768c2ecf20Sopenharmony_ci	}
4778c2ecf20Sopenharmony_ci	mutex_unlock(&disc->mutex);
4788c2ecf20Sopenharmony_ci}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci/*
4818c2ecf20Sopenharmony_ci * snic_disc_start: Discovery Start ...
4828c2ecf20Sopenharmony_ci */
4838c2ecf20Sopenharmony_ciint
4848c2ecf20Sopenharmony_cisnic_disc_start(struct snic *snic)
4858c2ecf20Sopenharmony_ci{
4868c2ecf20Sopenharmony_ci	struct snic_disc *disc = &snic->disc;
4878c2ecf20Sopenharmony_ci	unsigned long flags;
4888c2ecf20Sopenharmony_ci	int ret = 0;
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	SNIC_SCSI_DBG(snic->shost, "Discovery Start.\n");
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci	spin_lock_irqsave(&snic->snic_lock, flags);
4938c2ecf20Sopenharmony_ci	if (snic->in_remove) {
4948c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&snic->snic_lock, flags);
4958c2ecf20Sopenharmony_ci		SNIC_ERR("snic driver removal in progress ...\n");
4968c2ecf20Sopenharmony_ci		ret = 0;
4978c2ecf20Sopenharmony_ci
4988c2ecf20Sopenharmony_ci		return ret;
4998c2ecf20Sopenharmony_ci	}
5008c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&snic->snic_lock, flags);
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	mutex_lock(&disc->mutex);
5038c2ecf20Sopenharmony_ci	if (disc->state == SNIC_DISC_PENDING) {
5048c2ecf20Sopenharmony_ci		disc->req_cnt++;
5058c2ecf20Sopenharmony_ci		mutex_unlock(&disc->mutex);
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci		return ret;
5088c2ecf20Sopenharmony_ci	}
5098c2ecf20Sopenharmony_ci	disc->state = SNIC_DISC_PENDING;
5108c2ecf20Sopenharmony_ci	mutex_unlock(&disc->mutex);
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	ret = snic_queue_report_tgt_req(snic);
5138c2ecf20Sopenharmony_ci	if (ret)
5148c2ecf20Sopenharmony_ci		SNIC_HOST_INFO(snic->shost, "Discovery Failed, err=%d.\n", ret);
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	return ret;
5178c2ecf20Sopenharmony_ci} /* end of snic_disc_start */
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci/*
5208c2ecf20Sopenharmony_ci * snic_disc_work :
5218c2ecf20Sopenharmony_ci */
5228c2ecf20Sopenharmony_civoid
5238c2ecf20Sopenharmony_cisnic_handle_disc(struct work_struct *work)
5248c2ecf20Sopenharmony_ci{
5258c2ecf20Sopenharmony_ci	struct snic *snic = container_of(work, struct snic, disc_work);
5268c2ecf20Sopenharmony_ci	int ret = 0;
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci	SNIC_HOST_INFO(snic->shost, "disc_work: Discovery\n");
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	ret = snic_disc_start(snic);
5318c2ecf20Sopenharmony_ci	if (ret)
5328c2ecf20Sopenharmony_ci		goto disc_err;
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_cidisc_err:
5358c2ecf20Sopenharmony_ci	SNIC_HOST_ERR(snic->shost,
5368c2ecf20Sopenharmony_ci		      "disc_work: Discovery Failed w/ err = %d\n",
5378c2ecf20Sopenharmony_ci		      ret);
5388c2ecf20Sopenharmony_ci} /* end of snic_disc_work */
5398c2ecf20Sopenharmony_ci
5408c2ecf20Sopenharmony_ci/*
5418c2ecf20Sopenharmony_ci * snic_tgt_del_all : cleanup all snic targets
5428c2ecf20Sopenharmony_ci * Called on unbinding the interface
5438c2ecf20Sopenharmony_ci */
5448c2ecf20Sopenharmony_civoid
5458c2ecf20Sopenharmony_cisnic_tgt_del_all(struct snic *snic)
5468c2ecf20Sopenharmony_ci{
5478c2ecf20Sopenharmony_ci	struct snic_tgt *tgt = NULL;
5488c2ecf20Sopenharmony_ci	struct list_head *cur, *nxt;
5498c2ecf20Sopenharmony_ci	unsigned long flags;
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci	scsi_flush_work(snic->shost);
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	mutex_lock(&snic->disc.mutex);
5548c2ecf20Sopenharmony_ci	spin_lock_irqsave(snic->shost->host_lock, flags);
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	list_for_each_safe(cur, nxt, &snic->disc.tgt_list) {
5578c2ecf20Sopenharmony_ci		tgt = list_entry(cur, struct snic_tgt, list);
5588c2ecf20Sopenharmony_ci		tgt->state = SNIC_TGT_STAT_DEL;
5598c2ecf20Sopenharmony_ci		list_del_init(&tgt->list);
5608c2ecf20Sopenharmony_ci		SNIC_HOST_INFO(snic->shost, "Tgt %d q'ing for del\n", tgt->id);
5618c2ecf20Sopenharmony_ci		queue_work(snic_glob->event_q, &tgt->del_work);
5628c2ecf20Sopenharmony_ci		tgt = NULL;
5638c2ecf20Sopenharmony_ci	}
5648c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(snic->shost->host_lock, flags);
5658c2ecf20Sopenharmony_ci	mutex_unlock(&snic->disc.mutex);
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ci	flush_workqueue(snic_glob->event_q);
5688c2ecf20Sopenharmony_ci} /* end of snic_tgt_del_all */
569