/**
* Copyright (C) 2005 - 2011 Emulex
* All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation. The full GNU General
* Public License is included in this distribution in the file called COPYING.
*
* Written by: Jayamohan Kallickal (jayamohan.kallickal@emulex.com)
*
* Contact Information:
* linux-drivers@emulex.com
*
* Emulex
* 3333 Susan Street
* Costa Mesa, CA 92626
*/
#include <scsi/libiscsi.h>
#include <scsi/scsi_transport_iscsi.h>
#include <scsi/scsi_transport.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_netlink.h>
#include <net/netlink.h>
#include <scsi/scsi.h>
#include "be_iscsi.h"
extern struct iscsi_transport beiscsi_iscsi_transport;
/**
* beiscsi_session_create - creates a new iscsi session
* @cmds_max: max commands supported
* @qdepth: max queue depth supported
* @initial_cmdsn: initial iscsi CMDSN
*/
struct iscsi_cls_session *beiscsi_session_create(struct iscsi_endpoint *ep,
u16 cmds_max,
u16 qdepth,
u32 initial_cmdsn)
{
struct Scsi_Host *shost;
struct beiscsi_endpoint *beiscsi_ep;
struct iscsi_cls_session *cls_session;
struct beiscsi_hba *phba;
struct iscsi_session *sess;
struct beiscsi_session *beiscsi_sess;
struct beiscsi_io_task *io_task;
SE_DEBUG(DBG_LVL_8, "In beiscsi_session_create\n");
if (!ep) {
SE_DEBUG(DBG_LVL_1, "beiscsi_session_create: invalid ep\n");
return NULL;
}
beiscsi_ep = ep->dd_data;
phba = beiscsi_ep->phba;
shost = phba->shost;
if (cmds_max > beiscsi_ep->phba->params.wrbs_per_cxn) {
shost_printk(KERN_ERR, shost, "Cannot handle %d cmds."
"Max cmds per session supported is %d. Using %d. "
"\n", cmds_max,
beiscsi_ep->phba->params.wrbs_per_cxn,
beiscsi_ep->phba->params.wrbs_per_cxn);
cmds_max = beiscsi_ep->phba->params.wrbs_per_cxn;
}
cls_session = iscsi_session_setup(&beiscsi_iscsi_transport,
shost, cmds_max,
sizeof(*beiscsi_sess),
sizeof(*io_task),
initial_cmdsn, ISCSI_MAX_TARGET);
if (!cls_session)
return NULL;
sess = cls_session->dd_data;
beiscsi_sess = sess->dd_data;
beiscsi_sess->bhs_pool = pci_pool_create("beiscsi_bhs_pool",
phba->pcidev,
sizeof(struct be_cmd_bhs),
64, 0);
if (!beiscsi_sess->bhs_pool)
goto destroy_sess;
return cls_session;
destroy_sess:
iscsi_session_teardown(cls_session);
return NULL;
}
/**
* beiscsi_session_destroy - destroys iscsi session
* @cls_session: pointer to iscsi cls session
*
* Destroys iSCSI session instance and releases
* resources allocated for it.
*/
void beiscsi_session_destroy(struct iscsi_cls_session *cls_session)
{
struct iscsi_session *sess = cls_session->dd_data;
struct beiscsi_session *beiscsi_sess = sess->dd_data;
SE_DEBUG(DBG_LVL_8, "In beiscsi_session_destroy\n");
pci_pool_destroy(beiscsi_sess->bhs_pool);
iscsi_session_teardown(cls_session);
}
/**
* beiscsi_conn_create - create an instance of iscsi connection
* @cls_session: ptr to iscsi_cls_session
* @cid: iscsi cid
*/
struct iscsi_cls_conn *
beiscsi_conn_create(struct iscsi_cls_session *cls_session, u32 cid)
{
struct beiscsi_hba *phba;
struct Scsi_Host *shost;
struct iscsi_cls_conn *cls_conn;
struct beiscsi_conn *beiscsi_conn;
struct iscsi_conn *conn;
struct iscsi_session *sess;
struct beiscsi_session *beiscsi_sess;
SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_create ,cid"
"from iscsi layer=%d\n", cid);
shost = iscsi_session_to_shost(cls_session);
phba = iscsi_host_priv(shost);
cls_conn = iscsi_conn_setup(cls_session, sizeof(*beiscsi_conn), cid);
if (!cls_conn)
return NULL;
conn = cls_conn->dd_data;
beiscsi_conn = conn->dd_data;
beiscsi_conn->ep = NULL;
beiscsi_conn->phba = phba;
beiscsi_conn->conn = conn;
sess = cls_session->dd_data;
beiscsi_sess = sess->dd_data;
beiscsi_conn->beiscsi_sess = beiscsi_sess;
return cls_conn;
}
/**
* beiscsi_bindconn_cid - Bind the beiscsi_conn with phba connection table
* @beiscsi_conn: The pointer to beiscsi_conn structure
* @phba: The phba instance
* @cid: The cid to free
*/
static int beiscsi_bindconn_cid(struct beiscsi_hba *phba,
struct beiscsi_conn *beiscsi_conn,
unsigned int cid)
{
if (phba->conn_table[cid]) {
SE_DEBUG(DBG_LVL_1,
"Connection table already occupied. Detected clash\n");
return -EINVAL;
} else {
SE_DEBUG(DBG_LVL_8, "phba->conn_table[%d]=%p(beiscsi_conn)\n",
cid, beiscsi_conn);
phba->conn_table[cid] = beiscsi_conn;
}
return 0;
}
/**
* beiscsi_conn_bind - Binds iscsi session/connection with TCP connection
* @cls_session: pointer to iscsi cls session
* @cls_conn: pointer to iscsi cls conn
* @transport_fd: EP handle(64 bit)
*
* This function binds the TCP Conn with iSCSI Connection and Session.
*/
int beiscsi_conn_bind(struct iscsi_cls_session *cls_session,
struct iscsi_cls_conn *cls_conn,
u64 transport_fd, int is_leading)
{
struct iscsi_conn *conn = cls_conn->dd_data;
struct beiscsi_conn *beiscsi_conn = conn->dd_data;
struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
struct beiscsi_hba *phba = iscsi_host_priv(shost);
struct beiscsi_endpoint *beiscsi_ep;
struct iscsi_endpoint *ep;
SE_DEBUG(DBG_LVL_8, "In beiscsi_conn_bind\n");
ep = iscsi_lookup_endpoint(transport_fd);
if (!ep)
return -EINVAL;
beiscsi_ep = ep->dd_data;
if (iscsi_conn_bind(cls_session, cls_conn, is_leading))
return -EINVAL;
if (beiscsi_ep->phba != phba) {
SE_DEBUG(DBG_LVL_8,
"beiscsi_ep->hba=%p not equal to phba=%p\n
|