⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ib_srp.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * Copyright (c) 2005 Cisco Systems.  All rights reserved. * * This software is available to you under a choice of one of two * licenses.  You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * OpenIB.org BSD license below: * *     Redistribution and use in source and binary forms, with or *     without modification, are permitted provided that the following *     conditions are met: * *      - Redistributions of source code must retain the above *        copyright notice, this list of conditions and the following *        disclaimer. * *      - Redistributions in binary form must reproduce the above *        copyright notice, this list of conditions and the following *        disclaimer in the documentation and/or other materials *        provided with the distribution. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * $Id: ib_srp.c 3932 2005-11-01 17:19:29Z roland $ */#include <linux/module.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/err.h>#include <linux/string.h>#include <linux/parser.h>#include <linux/random.h>#include <asm/atomic.h>#include <scsi/scsi.h>#include <scsi/scsi_device.h>#include <scsi/scsi_dbg.h>#include <scsi/srp.h>#include <rdma/ib_cache.h>#include "ib_srp.h"#define DRV_NAME	"ib_srp"#define PFX		DRV_NAME ": "#define DRV_VERSION	"0.2"#define DRV_RELDATE	"November 1, 2005"MODULE_AUTHOR("Roland Dreier");MODULE_DESCRIPTION("InfiniBand SCSI RDMA Protocol initiator "		   "v" DRV_VERSION " (" DRV_RELDATE ")");MODULE_LICENSE("Dual BSD/GPL");static int topspin_workarounds = 1;module_param(topspin_workarounds, int, 0444);MODULE_PARM_DESC(topspin_workarounds,		 "Enable workarounds for Topspin/Cisco SRP target bugs if != 0");static const u8 topspin_oui[3] = { 0x00, 0x05, 0xad };static void srp_add_one(struct ib_device *device);static void srp_remove_one(struct ib_device *device);static void srp_completion(struct ib_cq *cq, void *target_ptr);static int srp_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event);static struct ib_client srp_client = {	.name   = "srp",	.add    = srp_add_one,	.remove = srp_remove_one};static inline struct srp_target_port *host_to_target(struct Scsi_Host *host){	return (struct srp_target_port *) host->hostdata;}static const char *srp_target_info(struct Scsi_Host *host){	return host_to_target(host)->target_name;}static struct srp_iu *srp_alloc_iu(struct srp_host *host, size_t size,				   gfp_t gfp_mask,				   enum dma_data_direction direction){	struct srp_iu *iu;	iu = kmalloc(sizeof *iu, gfp_mask);	if (!iu)		goto out;	iu->buf = kzalloc(size, gfp_mask);	if (!iu->buf)		goto out_free_iu;	iu->dma = dma_map_single(host->dev->dma_device, iu->buf, size, direction);	if (dma_mapping_error(iu->dma))		goto out_free_buf;	iu->size      = size;	iu->direction = direction;	return iu;out_free_buf:	kfree(iu->buf);out_free_iu:	kfree(iu);out:	return NULL;}static void srp_free_iu(struct srp_host *host, struct srp_iu *iu){	if (!iu)		return;	dma_unmap_single(host->dev->dma_device, iu->dma, iu->size, iu->direction);	kfree(iu->buf);	kfree(iu);}static void srp_qp_event(struct ib_event *event, void *context){	printk(KERN_ERR PFX "QP event %d\n", event->event);}static int srp_init_qp(struct srp_target_port *target,		       struct ib_qp *qp){	struct ib_qp_attr *attr;	int ret;	attr = kmalloc(sizeof *attr, GFP_KERNEL);	if (!attr)		return -ENOMEM;	ret = ib_find_cached_pkey(target->srp_host->dev,				  target->srp_host->port,				  be16_to_cpu(target->path.pkey),				  &attr->pkey_index);	if (ret)		goto out;	attr->qp_state        = IB_QPS_INIT;	attr->qp_access_flags = (IB_ACCESS_REMOTE_READ |				    IB_ACCESS_REMOTE_WRITE);	attr->port_num        = target->srp_host->port;	ret = ib_modify_qp(qp, attr,			   IB_QP_STATE		|			   IB_QP_PKEY_INDEX	|			   IB_QP_ACCESS_FLAGS	|			   IB_QP_PORT);out:	kfree(attr);	return ret;}static int srp_create_target_ib(struct srp_target_port *target){	struct ib_qp_init_attr *init_attr;	int ret;	init_attr = kzalloc(sizeof *init_attr, GFP_KERNEL);	if (!init_attr)		return -ENOMEM;	target->cq = ib_create_cq(target->srp_host->dev, srp_completion,				  NULL, target, SRP_CQ_SIZE);	if (IS_ERR(target->cq)) {		ret = PTR_ERR(target->cq);		goto out;	}	ib_req_notify_cq(target->cq, IB_CQ_NEXT_COMP);	init_attr->event_handler       = srp_qp_event;	init_attr->cap.max_send_wr     = SRP_SQ_SIZE;	init_attr->cap.max_recv_wr     = SRP_RQ_SIZE;	init_attr->cap.max_recv_sge    = 1;	init_attr->cap.max_send_sge    = 1;	init_attr->sq_sig_type         = IB_SIGNAL_ALL_WR;	init_attr->qp_type             = IB_QPT_RC;	init_attr->send_cq             = target->cq;	init_attr->recv_cq             = target->cq;	target->qp = ib_create_qp(target->srp_host->pd, init_attr);	if (IS_ERR(target->qp)) {		ret = PTR_ERR(target->qp);		ib_destroy_cq(target->cq);		goto out;	}	ret = srp_init_qp(target, target->qp);	if (ret) {		ib_destroy_qp(target->qp);		ib_destroy_cq(target->cq);		goto out;	}out:	kfree(init_attr);	return ret;}static void srp_free_target_ib(struct srp_target_port *target){	int i;	ib_destroy_qp(target->qp);	ib_destroy_cq(target->cq);	for (i = 0; i < SRP_RQ_SIZE; ++i)		srp_free_iu(target->srp_host, target->rx_ring[i]);	for (i = 0; i < SRP_SQ_SIZE + 1; ++i)		srp_free_iu(target->srp_host, target->tx_ring[i]);}static void srp_path_rec_completion(int status,				    struct ib_sa_path_rec *pathrec,				    void *target_ptr){	struct srp_target_port *target = target_ptr;	target->status = status;	if (status)		printk(KERN_ERR PFX "Got failed path rec status %d\n", status);	else		target->path = *pathrec;	complete(&target->done);}static int srp_lookup_path(struct srp_target_port *target){	target->path.numb_path = 1;	init_completion(&target->done);	target->path_query_id = ib_sa_path_rec_get(target->srp_host->dev,						   target->srp_host->port,						   &target->path,						   IB_SA_PATH_REC_DGID		|						   IB_SA_PATH_REC_SGID		|						   IB_SA_PATH_REC_NUMB_PATH	|						   IB_SA_PATH_REC_PKEY,						   SRP_PATH_REC_TIMEOUT_MS,						   GFP_KERNEL,						   srp_path_rec_completion,						   target, &target->path_query);	if (target->path_query_id < 0)		return target->path_query_id;	wait_for_completion(&target->done);	if (target->status < 0)		printk(KERN_WARNING PFX "Path record query failed\n");	return target->status;}static int srp_send_req(struct srp_target_port *target){	struct {		struct ib_cm_req_param param;		struct srp_login_req   priv;	} *req = NULL;	int status;	req = kzalloc(sizeof *req, GFP_KERNEL);	if (!req)		return -ENOMEM;	req->param.primary_path 	      = &target->path;	req->param.alternate_path 	      = NULL;	req->param.service_id 		      = target->service_id;	req->param.qp_num 		      = target->qp->qp_num;	req->param.qp_type 		      = target->qp->qp_type;	req->param.private_data 	      = &req->priv;	req->param.private_data_len 	      = sizeof req->priv;	req->param.flow_control 	      = 1;	get_random_bytes(&req->param.starting_psn, 4);	req->param.starting_psn 	     &= 0xffffff;	/*	 * Pick some arbitrary defaults here; we could make these	 * module parameters if anyone cared about setting them.	 */	req->param.responder_resources	      = 4;	req->param.remote_cm_response_timeout = 20;	req->param.local_cm_response_timeout  = 20;	req->param.retry_count 		      = 7;	req->param.rnr_retry_count 	      = 7;	req->param.max_cm_retries 	      = 15;	req->priv.opcode     	= SRP_LOGIN_REQ;	req->priv.tag        	= 0;	req->priv.req_it_iu_len = cpu_to_be32(SRP_MAX_IU_LEN);	req->priv.req_buf_fmt 	= cpu_to_be16(SRP_BUF_FORMAT_DIRECT |					      SRP_BUF_FORMAT_INDIRECT);	memcpy(req->priv.initiator_port_id, target->srp_host->initiator_port_id, 16);	/*	 * Topspin/Cisco SRP targets will reject our login unless we	 * zero out the first 8 bytes of our initiator port ID.  The	 * second 8 bytes must be our local node GUID, but we always	 * use that anyway.	 */	if (topspin_workarounds && !memcmp(&target->ioc_guid, topspin_oui, 3)) {		printk(KERN_DEBUG PFX "Topspin/Cisco initiator port ID workaround "		       "activated for target GUID %016llx\n",		       (unsigned long long) be64_to_cpu(target->ioc_guid));		memset(req->priv.initiator_port_id, 0, 8);	}	memcpy(req->priv.target_port_id,     &target->id_ext, 8);	memcpy(req->priv.target_port_id + 8, &target->ioc_guid, 8);	status = ib_send_cm_req(target->cm_id, &req->param);	kfree(req);	return status;}static void srp_disconnect_target(struct srp_target_port *target){	/* XXX should send SRP_I_LOGOUT request */	init_completion(&target->done);	ib_send_cm_dreq(target->cm_id, NULL, 0);	wait_for_completion(&target->done);}static void srp_remove_work(void *target_ptr){	struct srp_target_port *target = target_ptr;	spin_lock_irq(target->scsi_host->host_lock);	if (target->state != SRP_TARGET_DEAD) {		spin_unlock_irq(target->scsi_host->host_lock);		scsi_host_put(target->scsi_host);		return;	}	target->state = SRP_TARGET_REMOVED;	spin_unlock_irq(target->scsi_host->host_lock);	down(&target->srp_host->target_mutex);	list_del(&target->list);	up(&target->srp_host->target_mutex);	scsi_remove_host(target->scsi_host);	ib_destroy_cm_id(target->cm_id);	srp_free_target_ib(target);	scsi_host_put(target->scsi_host);	/* And another put to really free the target port... */	scsi_host_put(target->scsi_host);}static int srp_connect_target(struct srp_target_port *target){	int ret;	ret = srp_lookup_path(target);	if (ret)		return ret;	while (1) {		init_completion(&target->done);		ret = srp_send_req(target);		if (ret)			return ret;		wait_for_completion(&target->done);		/*		 * The CM event handling code will set status to		 * SRP_PORT_REDIRECT if we get a port redirect REJ		 * back, or SRP_DLID_REDIRECT if we get a lid/qp		 * redirect REJ back.		 */		switch (target->status) {		case 0:			return 0;		case SRP_PORT_REDIRECT:			ret = srp_lookup_path(target);			if (ret)				return ret;			break;		case SRP_DLID_REDIRECT:			break;		default:			return target->status;		}	}}static int srp_reconnect_target(struct srp_target_port *target){	struct ib_cm_id *new_cm_id;	struct ib_qp_attr qp_attr;	struct srp_request *req;	struct ib_wc wc;	int ret;	int i;	spin_lock_irq(target->scsi_host->host_lock);	if (target->state != SRP_TARGET_LIVE) {		spin_unlock_irq(target->scsi_host->host_lock);		return -EAGAIN;	}	target->state = SRP_TARGET_CONNECTING;	spin_unlock_irq(target->scsi_host->host_lock);	srp_disconnect_target(target);	/*	 * Now get a new local CM ID so that we avoid confusing the	 * target in case things are really fouled up.	 */	new_cm_id = ib_create_cm_id(target->srp_host->dev,				    srp_cm_handler, target);	if (IS_ERR(new_cm_id)) {		ret = PTR_ERR(new_cm_id);		goto err;	}	ib_destroy_cm_id(target->cm_id);	target->cm_id = new_cm_id;	qp_attr.qp_state = IB_QPS_RESET;	ret = ib_modify_qp(target->qp, &qp_attr, IB_QP_STATE);	if (ret)		goto err;	ret = srp_init_qp(target, target->qp);	if (ret)		goto err;	while (ib_poll_cq(target->cq, 1, &wc) > 0)		; /* nothing */	list_for_each_entry(req, &target->req_queue, list) {		req->scmnd->result = DID_RESET << 16;		req->scmnd->scsi_done(req->scmnd);	}	target->rx_head	 = 0;	target->tx_head	 = 0;	target->tx_tail  = 0;	target->req_head = 0;	for (i = 0; i < SRP_SQ_SIZE - 1; ++i)		target->req_ring[i].next = i + 1;	target->req_ring[SRP_SQ_SIZE - 1].next = -1;	INIT_LIST_HEAD(&target->req_queue);	ret = srp_connect_target(target);	if (ret)		goto err;	spin_lock_irq(target->scsi_host->host_lock);	if (target->state == SRP_TARGET_CONNECTING) {		ret = 0;		target->state = SRP_TARGET_LIVE;	} else		ret = -EAGAIN;	spin_unlock_irq(target->scsi_host->host_lock);	return ret;err:	printk(KERN_ERR PFX "reconnect failed (%d), removing target port.\n", ret);	/*	 * We couldn't reconnect, so kill our target port off.	 * However, we have to defer the real removal because we might	 * be in the context of the SCSI error handler now, which	 * would deadlock if we call scsi_remove_host().	 */	spin_lock_irq(target->scsi_host->host_lock);	if (target->state == SRP_TARGET_CONNECTING) {		target->state = SRP_TARGET_DEAD;		INIT_WORK(&target->work, srp_remove_work, target);		schedule_work(&target->work);	}	spin_unlock_irq(target->scsi_host->host_lock);	return ret;}static int srp_map_data(struct scsi_cmnd *scmnd, struct srp_target_port *target,			struct srp_request *req){	struct srp_cmd *cmd = req->cmd->buf;	int len;	u8 fmt;	if (!scmnd->request_buffer || scmnd->sc_data_direction == DMA_NONE)		return sizeof (struct srp_cmd);	if (scmnd->sc_data_direction != DMA_FROM_DEVICE &&	    scmnd->sc_data_direction != DMA_TO_DEVICE) {		printk(KERN_WARNING PFX "Unhandled data direction %d\n",		       scmnd->sc_data_direction);		return -EINVAL;	}	if (scmnd->use_sg) {		struct scatterlist *scat = scmnd->request_buffer;		int n;		int i;		n = dma_map_sg(target->srp_host->dev->dma_device,			       scat, scmnd->use_sg, scmnd->sc_data_direction);		if (n == 1) {			struct srp_direct_buf *buf = (void *) cmd->add_data;			fmt = SRP_DATA_DESC_DIRECT;			buf->va  = cpu_to_be64(sg_dma_address(scat));			buf->key = cpu_to_be32(target->srp_host->mr->rkey);			buf->len = cpu_to_be32(sg_dma_len(scat));			len = sizeof (struct srp_cmd) +				sizeof (struct srp_direct_buf);		} else {			struct srp_indirect_buf *buf = (void *) cmd->add_data;			u32 datalen = 0;			fmt = SRP_DATA_DESC_INDIRECT;			if (scmnd->sc_data_direction == DMA_TO_DEVICE)				cmd->data_out_desc_cnt = n;			else				cmd->data_in_desc_cnt = n;			buf->table_desc.va  = cpu_to_be64(req->cmd->dma +							  sizeof *cmd +							  sizeof *buf);			buf->table_desc.key =				cpu_to_be32(target->srp_host->mr->rkey);			buf->table_desc.len =				cpu_to_be32(n * sizeof (struct srp_direct_buf));			for (i = 0; i < n; ++i) {				buf->desc_list[i].va  = cpu_to_be64(sg_dma_address(&scat[i]));				buf->desc_list[i].key =					cpu_to_be32(target->srp_host->mr->rkey);				buf->desc_list[i].len = cpu_to_be32(sg_dma_len(&scat[i]));				datalen += sg_dma_len(&scat[i]);			}			buf->len = cpu_to_be32(datalen);			len = sizeof (struct srp_cmd) +				sizeof (struct srp_indirect_buf) +

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -