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

📄 ib_srp.c

📁 linux内核源码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * 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 <linux/jiffies.h>#include <asm/atomic.h>#include <scsi/scsi.h>#include <scsi/scsi_device.h>#include <scsi/scsi_dbg.h>#include <scsi/srp.h>#include <scsi/scsi_transport_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 srp_sg_tablesize = SRP_DEF_SG_TABLESIZE;static int srp_max_iu_len;module_param(srp_sg_tablesize, int, 0444);MODULE_PARM_DESC(srp_sg_tablesize,		 "Max number of gather/scatter entries per I/O (default is 12)");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 int mellanox_workarounds = 1;module_param(mellanox_workarounds, int, 0444);MODULE_PARM_DESC(mellanox_workarounds,		 "Enable workarounds for Mellanox SRP target bugs if != 0");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 scsi_transport_template *ib_srp_transport_template;static struct ib_client srp_client = {	.name   = "srp",	.add    = srp_add_one,	.remove = srp_remove_one};static struct ib_sa_client srp_sa_client;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 int srp_target_is_topspin(struct srp_target_port *target){	static const u8 topspin_oui[3] = { 0x00, 0x05, 0xad };	static const u8 cisco_oui[3]   = { 0x00, 0x1b, 0x0d };	return topspin_workarounds &&		(!memcmp(&target->ioc_guid, topspin_oui, sizeof topspin_oui) ||		 !memcmp(&target->ioc_guid, cisco_oui, sizeof cisco_oui));}static int srp_target_is_mellanox(struct srp_target_port *target){	static const u8 mellanox_oui[3] = { 0x00, 0x02, 0xc9 };	return mellanox_workarounds &&		!memcmp(&target->ioc_guid, mellanox_oui, sizeof mellanox_oui);}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 = ib_dma_map_single(host->dev->dev, iu->buf, size, direction);	if (ib_dma_mapping_error(host->dev->dev, 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;	ib_dma_unmap_single(host->dev->dev, 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->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->dev, srp_completion,				  NULL, target, SRP_CQ_SIZE, 0);	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->dev->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(&srp_sa_client,						   target->srp_host->dev->dev,						   target->srp_host->port,						   &target->path,						   IB_SA_PATH_REC_SERVICE_ID	|						   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);	/*	 * In the published SRP specification (draft rev. 16a), the	 * port identifier format is 8 bytes of ID extension followed	 * by 8 bytes of GUID.  Older drafts put the two halves in the	 * opposite order, so that the GUID comes first.	 *	 * Targets conforming to these obsolete drafts can be	 * recognized by the I/O Class they report.	 */	if (target->io_class == SRP_REV10_IB_IO_CLASS) {		memcpy(req->priv.initiator_port_id,		       &target->path.sgid.global.interface_id, 8);		memcpy(req->priv.initiator_port_id + 8,		       &target->initiator_ext, 8);		memcpy(req->priv.target_port_id,     &target->ioc_guid, 8);		memcpy(req->priv.target_port_id + 8, &target->id_ext, 8);	} else {		memcpy(req->priv.initiator_port_id,		       &target->initiator_ext, 8);		memcpy(req->priv.initiator_port_id + 8,		       &target->path.sgid.global.interface_id, 8);		memcpy(req->priv.target_port_id,     &target->id_ext, 8);		memcpy(req->priv.target_port_id + 8, &target->ioc_guid, 8);	}	/*	 * Topspin/Cisco SRP targets will reject our login unless we	 * zero out the first 8 bytes of our initiator port ID and set	 * the second 8 bytes to the local node GUID.	 */	if (srp_target_is_topspin(target)) {		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.initiator_port_id + 8,		       &target->srp_host->dev->dev->node_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);	if (ib_send_cm_dreq(target->cm_id, NULL, 0)) {		printk(KERN_DEBUG PFX "Sending CM DREQ failed\n");		return;	}	wait_for_completion(&target->done);}static void srp_remove_work(struct work_struct *work){	struct srp_target_port *target =		container_of(work, struct srp_target_port, work);	spin_lock_irq(target->scsi_host->host_lock);	if (target->state != SRP_TARGET_DEAD) {		spin_unlock_irq(target->scsi_host->host_lock);		return;	}	target->state = SRP_TARGET_REMOVED;	spin_unlock_irq(target->scsi_host->host_lock);	spin_lock(&target->srp_host->target_lock);	list_del(&target->list);	spin_unlock(&target->srp_host->target_lock);	srp_remove_host(target->scsi_host);	scsi_remove_host(target->scsi_host);	ib_destroy_cm_id(target->cm_id);	srp_free_target_ib(target);	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 void srp_unmap_data(struct scsi_cmnd *scmnd,			   struct srp_target_port *target,			   struct srp_request *req){	if (!scsi_sglist(scmnd) ||	    (scmnd->sc_data_direction != DMA_TO_DEVICE &&	     scmnd->sc_data_direction != DMA_FROM_DEVICE))		return;	if (req->fmr) {		ib_fmr_pool_unmap(req->fmr);		req->fmr = NULL;	}	ib_dma_unmap_sg(target->srp_host->dev->dev, scsi_sglist(scmnd),			scsi_sg_count(scmnd), scmnd->sc_data_direction);}static void srp_remove_req(struct srp_target_port *target, struct srp_request *req){	srp_unmap_data(req->scmnd, target, req);	list_move_tail(&req->list, &target->free_reqs);}static void srp_reset_req(struct srp_target_port *target, struct srp_request *req){	req->scmnd->result = DID_RESET << 16;	req->scmnd->scsi_done(req->scmnd);	srp_remove_req(target, req);}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, *tmp;	struct ib_wc wc;	int ret;	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->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);

⌨️ 快捷键说明

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