mirror of
https://github.com/torvalds/linux
synced 2024-11-05 18:23:50 +00:00
Merge branch 'srp' into for-next
This commit is contained in:
commit
fb57e1dbbd
6 changed files with 406 additions and 163 deletions
156
Documentation/ABI/stable/sysfs-driver-ib_srp
Normal file
156
Documentation/ABI/stable/sysfs-driver-ib_srp
Normal file
|
@ -0,0 +1,156 @@
|
|||
What: /sys/class/infiniband_srp/srp-<hca>-<port_number>/add_target
|
||||
Date: January 2, 2006
|
||||
KernelVersion: 2.6.15
|
||||
Contact: linux-rdma@vger.kernel.org
|
||||
Description: Interface for making ib_srp connect to a new target.
|
||||
One can request ib_srp to connect to a new target by writing
|
||||
a comma-separated list of login parameters to this sysfs
|
||||
attribute. The supported parameters are:
|
||||
* id_ext, a 16-digit hexadecimal number specifying the eight
|
||||
byte identifier extension in the 16-byte SRP target port
|
||||
identifier. The target port identifier is sent by ib_srp
|
||||
to the target in the SRP_LOGIN_REQ request.
|
||||
* ioc_guid, a 16-digit hexadecimal number specifying the eight
|
||||
byte I/O controller GUID portion of the 16-byte target port
|
||||
identifier.
|
||||
* dgid, a 32-digit hexadecimal number specifying the
|
||||
destination GID.
|
||||
* pkey, a four-digit hexadecimal number specifying the
|
||||
InfiniBand partition key.
|
||||
* service_id, a 16-digit hexadecimal number specifying the
|
||||
InfiniBand service ID used to establish communication with
|
||||
the SRP target. How to find out the value of the service ID
|
||||
is specified in the documentation of the SRP target.
|
||||
* max_sect, a decimal number specifying the maximum number of
|
||||
512-byte sectors to be transferred via a single SCSI command.
|
||||
* max_cmd_per_lun, a decimal number specifying the maximum
|
||||
number of outstanding commands for a single LUN.
|
||||
* io_class, a hexadecimal number specifying the SRP I/O class.
|
||||
Must be either 0xff00 (rev 10) or 0x0100 (rev 16a). The I/O
|
||||
class defines the format of the SRP initiator and target
|
||||
port identifiers.
|
||||
* initiator_ext, a 16-digit hexadecimal number specifying the
|
||||
identifier extension portion of the SRP initiator port
|
||||
identifier. This data is sent by the initiator to the target
|
||||
in the SRP_LOGIN_REQ request.
|
||||
* cmd_sg_entries, a number in the range 1..255 that specifies
|
||||
the maximum number of data buffer descriptors stored in the
|
||||
SRP_CMD information unit itself. With allow_ext_sg=0 the
|
||||
parameter cmd_sg_entries defines the maximum S/G list length
|
||||
for a single SRP_CMD, and commands whose S/G list length
|
||||
exceeds this limit after S/G list collapsing will fail.
|
||||
* allow_ext_sg, whether ib_srp is allowed to include a partial
|
||||
memory descriptor list in an SRP_CMD instead of the entire
|
||||
list. If a partial memory descriptor list has been included
|
||||
in an SRP_CMD the remaining memory descriptors are
|
||||
communicated from initiator to target via an additional RDMA
|
||||
transfer. Setting allow_ext_sg to 1 increases the maximum
|
||||
amount of data that can be transferred between initiator and
|
||||
target via a single SCSI command. Since not all SRP target
|
||||
implementations support partial memory descriptor lists the
|
||||
default value for this option is 0.
|
||||
* sg_tablesize, a number in the range 1..2048 specifying the
|
||||
maximum S/G list length the SCSI layer is allowed to pass to
|
||||
ib_srp. Specifying a value that exceeds cmd_sg_entries is
|
||||
only safe with partial memory descriptor list support enabled
|
||||
(allow_ext_sg=1).
|
||||
|
||||
What: /sys/class/infiniband_srp/srp-<hca>-<port_number>/ibdev
|
||||
Date: January 2, 2006
|
||||
KernelVersion: 2.6.15
|
||||
Contact: linux-rdma@vger.kernel.org
|
||||
Description: HCA name (<hca>).
|
||||
|
||||
What: /sys/class/infiniband_srp/srp-<hca>-<port_number>/port
|
||||
Date: January 2, 2006
|
||||
KernelVersion: 2.6.15
|
||||
Contact: linux-rdma@vger.kernel.org
|
||||
Description: HCA port number (<port_number>).
|
||||
|
||||
What: /sys/class/scsi_host/host<n>/allow_ext_sg
|
||||
Date: May 19, 2011
|
||||
KernelVersion: 2.6.39
|
||||
Contact: linux-rdma@vger.kernel.org
|
||||
Description: Whether ib_srp is allowed to include a partial memory
|
||||
descriptor list in an SRP_CMD when communicating with an SRP
|
||||
target.
|
||||
|
||||
What: /sys/class/scsi_host/host<n>/cmd_sg_entries
|
||||
Date: May 19, 2011
|
||||
KernelVersion: 2.6.39
|
||||
Contact: linux-rdma@vger.kernel.org
|
||||
Description: Maximum number of data buffer descriptors that may be sent to
|
||||
the target in a single SRP_CMD request.
|
||||
|
||||
What: /sys/class/scsi_host/host<n>/dgid
|
||||
Date: June 17, 2006
|
||||
KernelVersion: 2.6.17
|
||||
Contact: linux-rdma@vger.kernel.org
|
||||
Description: InfiniBand destination GID used for communication with the SRP
|
||||
target. Differs from orig_dgid if port redirection has happened.
|
||||
|
||||
What: /sys/class/scsi_host/host<n>/id_ext
|
||||
Date: June 17, 2006
|
||||
KernelVersion: 2.6.17
|
||||
Contact: linux-rdma@vger.kernel.org
|
||||
Description: Eight-byte identifier extension portion of the 16-byte target
|
||||
port identifier.
|
||||
|
||||
What: /sys/class/scsi_host/host<n>/ioc_guid
|
||||
Date: June 17, 2006
|
||||
KernelVersion: 2.6.17
|
||||
Contact: linux-rdma@vger.kernel.org
|
||||
Description: Eight-byte I/O controller GUID portion of the 16-byte target
|
||||
port identifier.
|
||||
|
||||
What: /sys/class/scsi_host/host<n>/local_ib_device
|
||||
Date: November 29, 2006
|
||||
KernelVersion: 2.6.19
|
||||
Contact: linux-rdma@vger.kernel.org
|
||||
Description: Name of the InfiniBand HCA used for communicating with the
|
||||
SRP target.
|
||||
|
||||
What: /sys/class/scsi_host/host<n>/local_ib_port
|
||||
Date: November 29, 2006
|
||||
KernelVersion: 2.6.19
|
||||
Contact: linux-rdma@vger.kernel.org
|
||||
Description: Number of the HCA port used for communicating with the
|
||||
SRP target.
|
||||
|
||||
What: /sys/class/scsi_host/host<n>/orig_dgid
|
||||
Date: June 17, 2006
|
||||
KernelVersion: 2.6.17
|
||||
Contact: linux-rdma@vger.kernel.org
|
||||
Description: InfiniBand destination GID specified in the parameters
|
||||
written to the add_target sysfs attribute.
|
||||
|
||||
What: /sys/class/scsi_host/host<n>/pkey
|
||||
Date: June 17, 2006
|
||||
KernelVersion: 2.6.17
|
||||
Contact: linux-rdma@vger.kernel.org
|
||||
Description: A 16-bit number representing the InfiniBand partition key used
|
||||
for communication with the SRP target.
|
||||
|
||||
What: /sys/class/scsi_host/host<n>/req_lim
|
||||
Date: October 20, 2010
|
||||
KernelVersion: 2.6.36
|
||||
Contact: linux-rdma@vger.kernel.org
|
||||
Description: Number of requests ib_srp can send to the target before it has
|
||||
to wait for more credits. For more information see also the
|
||||
SRP credit algorithm in the SRP specification.
|
||||
|
||||
What: /sys/class/scsi_host/host<n>/service_id
|
||||
Date: June 17, 2006
|
||||
KernelVersion: 2.6.17
|
||||
Contact: linux-rdma@vger.kernel.org
|
||||
Description: InfiniBand service ID used for establishing communication with
|
||||
the SRP target.
|
||||
|
||||
What: /sys/class/scsi_host/host<n>/zero_req_lim
|
||||
Date: September 20, 2006
|
||||
KernelVersion: 2.6.18
|
||||
Contact: linux-rdma@vger.kernel.org
|
||||
Description: Number of times the initiator had to wait before sending a
|
||||
request to the target because it ran out of credits. For more
|
||||
information see also the SRP credit algorithm in the SRP
|
||||
specification.
|
19
Documentation/ABI/stable/sysfs-transport-srp
Normal file
19
Documentation/ABI/stable/sysfs-transport-srp
Normal file
|
@ -0,0 +1,19 @@
|
|||
What: /sys/class/srp_remote_ports/port-<h>:<n>/delete
|
||||
Date: June 1, 2012
|
||||
KernelVersion: 3.7
|
||||
Contact: linux-scsi@vger.kernel.org, linux-rdma@vger.kernel.org
|
||||
Description: Instructs an SRP initiator to disconnect from a target and to
|
||||
remove all LUNs imported from that target.
|
||||
|
||||
What: /sys/class/srp_remote_ports/port-<h>:<n>/port_id
|
||||
Date: June 27, 2007
|
||||
KernelVersion: 2.6.24
|
||||
Contact: linux-scsi@vger.kernel.org
|
||||
Description: 16-byte local SRP port identifier in hexadecimal format. An
|
||||
example: 4c:49:4e:55:58:20:56:49:4f:00:00:00:00:00:00:00.
|
||||
|
||||
What: /sys/class/srp_remote_ports/port-<h>:<n>/roles
|
||||
Date: June 27, 2007
|
||||
KernelVersion: 2.6.24
|
||||
Contact: linux-scsi@vger.kernel.org
|
||||
Description: Role of the remote port. Either "SRP Initiator" or "SRP Target".
|
|
@ -222,27 +222,29 @@ static int srp_new_cm_id(struct srp_target_port *target)
|
|||
static int srp_create_target_ib(struct srp_target_port *target)
|
||||
{
|
||||
struct ib_qp_init_attr *init_attr;
|
||||
struct ib_cq *recv_cq, *send_cq;
|
||||
struct ib_qp *qp;
|
||||
int ret;
|
||||
|
||||
init_attr = kzalloc(sizeof *init_attr, GFP_KERNEL);
|
||||
if (!init_attr)
|
||||
return -ENOMEM;
|
||||
|
||||
target->recv_cq = ib_create_cq(target->srp_host->srp_dev->dev,
|
||||
srp_recv_completion, NULL, target, SRP_RQ_SIZE, 0);
|
||||
if (IS_ERR(target->recv_cq)) {
|
||||
ret = PTR_ERR(target->recv_cq);
|
||||
recv_cq = ib_create_cq(target->srp_host->srp_dev->dev,
|
||||
srp_recv_completion, NULL, target, SRP_RQ_SIZE, 0);
|
||||
if (IS_ERR(recv_cq)) {
|
||||
ret = PTR_ERR(recv_cq);
|
||||
goto err;
|
||||
}
|
||||
|
||||
target->send_cq = ib_create_cq(target->srp_host->srp_dev->dev,
|
||||
srp_send_completion, NULL, target, SRP_SQ_SIZE, 0);
|
||||
if (IS_ERR(target->send_cq)) {
|
||||
ret = PTR_ERR(target->send_cq);
|
||||
send_cq = ib_create_cq(target->srp_host->srp_dev->dev,
|
||||
srp_send_completion, NULL, target, SRP_SQ_SIZE, 0);
|
||||
if (IS_ERR(send_cq)) {
|
||||
ret = PTR_ERR(send_cq);
|
||||
goto err_recv_cq;
|
||||
}
|
||||
|
||||
ib_req_notify_cq(target->recv_cq, IB_CQ_NEXT_COMP);
|
||||
ib_req_notify_cq(recv_cq, IB_CQ_NEXT_COMP);
|
||||
|
||||
init_attr->event_handler = srp_qp_event;
|
||||
init_attr->cap.max_send_wr = SRP_SQ_SIZE;
|
||||
|
@ -251,30 +253,41 @@ static int srp_create_target_ib(struct srp_target_port *target)
|
|||
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->send_cq;
|
||||
init_attr->recv_cq = target->recv_cq;
|
||||
init_attr->send_cq = send_cq;
|
||||
init_attr->recv_cq = recv_cq;
|
||||
|
||||
target->qp = ib_create_qp(target->srp_host->srp_dev->pd, init_attr);
|
||||
if (IS_ERR(target->qp)) {
|
||||
ret = PTR_ERR(target->qp);
|
||||
qp = ib_create_qp(target->srp_host->srp_dev->pd, init_attr);
|
||||
if (IS_ERR(qp)) {
|
||||
ret = PTR_ERR(qp);
|
||||
goto err_send_cq;
|
||||
}
|
||||
|
||||
ret = srp_init_qp(target, target->qp);
|
||||
ret = srp_init_qp(target, qp);
|
||||
if (ret)
|
||||
goto err_qp;
|
||||
|
||||
if (target->qp)
|
||||
ib_destroy_qp(target->qp);
|
||||
if (target->recv_cq)
|
||||
ib_destroy_cq(target->recv_cq);
|
||||
if (target->send_cq)
|
||||
ib_destroy_cq(target->send_cq);
|
||||
|
||||
target->qp = qp;
|
||||
target->recv_cq = recv_cq;
|
||||
target->send_cq = send_cq;
|
||||
|
||||
kfree(init_attr);
|
||||
return 0;
|
||||
|
||||
err_qp:
|
||||
ib_destroy_qp(target->qp);
|
||||
ib_destroy_qp(qp);
|
||||
|
||||
err_send_cq:
|
||||
ib_destroy_cq(target->send_cq);
|
||||
ib_destroy_cq(send_cq);
|
||||
|
||||
err_recv_cq:
|
||||
ib_destroy_cq(target->recv_cq);
|
||||
ib_destroy_cq(recv_cq);
|
||||
|
||||
err:
|
||||
kfree(init_attr);
|
||||
|
@ -289,6 +302,9 @@ static void srp_free_target_ib(struct srp_target_port *target)
|
|||
ib_destroy_cq(target->send_cq);
|
||||
ib_destroy_cq(target->recv_cq);
|
||||
|
||||
target->qp = NULL;
|
||||
target->send_cq = target->recv_cq = NULL;
|
||||
|
||||
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; ++i)
|
||||
|
@ -428,34 +444,50 @@ static int srp_send_req(struct srp_target_port *target)
|
|||
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)) {
|
||||
shost_printk(KERN_DEBUG, target->scsi_host,
|
||||
PFX "Sending CM DREQ failed\n");
|
||||
return;
|
||||
}
|
||||
wait_for_completion(&target->done);
|
||||
}
|
||||
|
||||
static bool srp_change_state(struct srp_target_port *target,
|
||||
enum srp_target_state old,
|
||||
enum srp_target_state new)
|
||||
static bool srp_queue_remove_work(struct srp_target_port *target)
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
spin_lock_irq(&target->lock);
|
||||
if (target->state == old) {
|
||||
target->state = new;
|
||||
if (target->state != SRP_TARGET_REMOVED) {
|
||||
target->state = SRP_TARGET_REMOVED;
|
||||
changed = true;
|
||||
}
|
||||
spin_unlock_irq(&target->lock);
|
||||
|
||||
if (changed)
|
||||
queue_work(system_long_wq, &target->remove_work);
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
static bool srp_change_conn_state(struct srp_target_port *target,
|
||||
bool connected)
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
spin_lock_irq(&target->lock);
|
||||
if (target->connected != connected) {
|
||||
target->connected = connected;
|
||||
changed = true;
|
||||
}
|
||||
spin_unlock_irq(&target->lock);
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
static void srp_disconnect_target(struct srp_target_port *target)
|
||||
{
|
||||
if (srp_change_conn_state(target, false)) {
|
||||
/* XXX should send SRP_I_LOGOUT request */
|
||||
|
||||
if (ib_send_cm_dreq(target->cm_id, NULL, 0)) {
|
||||
shost_printk(KERN_DEBUG, target->scsi_host,
|
||||
PFX "Sending CM DREQ failed\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void srp_free_req_data(struct srp_target_port *target)
|
||||
{
|
||||
struct ib_device *ibdev = target->srp_host->srp_dev->dev;
|
||||
|
@ -489,32 +521,50 @@ static void srp_del_scsi_host_attr(struct Scsi_Host *shost)
|
|||
device_remove_file(&shost->shost_dev, *attr);
|
||||
}
|
||||
|
||||
static void srp_remove_work(struct work_struct *work)
|
||||
static void srp_remove_target(struct srp_target_port *target)
|
||||
{
|
||||
struct srp_target_port *target =
|
||||
container_of(work, struct srp_target_port, work);
|
||||
|
||||
if (!srp_change_state(target, SRP_TARGET_DEAD, SRP_TARGET_REMOVED))
|
||||
return;
|
||||
|
||||
spin_lock(&target->srp_host->target_lock);
|
||||
list_del(&target->list);
|
||||
spin_unlock(&target->srp_host->target_lock);
|
||||
WARN_ON_ONCE(target->state != SRP_TARGET_REMOVED);
|
||||
|
||||
srp_del_scsi_host_attr(target->scsi_host);
|
||||
srp_remove_host(target->scsi_host);
|
||||
scsi_remove_host(target->scsi_host);
|
||||
srp_disconnect_target(target);
|
||||
ib_destroy_cm_id(target->cm_id);
|
||||
srp_free_target_ib(target);
|
||||
srp_free_req_data(target);
|
||||
scsi_host_put(target->scsi_host);
|
||||
}
|
||||
|
||||
static void srp_remove_work(struct work_struct *work)
|
||||
{
|
||||
struct srp_target_port *target =
|
||||
container_of(work, struct srp_target_port, remove_work);
|
||||
|
||||
WARN_ON_ONCE(target->state != SRP_TARGET_REMOVED);
|
||||
|
||||
spin_lock(&target->srp_host->target_lock);
|
||||
list_del(&target->list);
|
||||
spin_unlock(&target->srp_host->target_lock);
|
||||
|
||||
srp_remove_target(target);
|
||||
}
|
||||
|
||||
static void srp_rport_delete(struct srp_rport *rport)
|
||||
{
|
||||
struct srp_target_port *target = rport->lld_data;
|
||||
|
||||
srp_queue_remove_work(target);
|
||||
}
|
||||
|
||||
static int srp_connect_target(struct srp_target_port *target)
|
||||
{
|
||||
int retries = 3;
|
||||
int ret;
|
||||
|
||||
WARN_ON_ONCE(target->connected);
|
||||
|
||||
target->qp_in_error = false;
|
||||
|
||||
ret = srp_lookup_path(target);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -534,6 +584,7 @@ static int srp_connect_target(struct srp_target_port *target)
|
|||
*/
|
||||
switch (target->status) {
|
||||
case 0:
|
||||
srp_change_conn_state(target, true);
|
||||
return 0;
|
||||
|
||||
case SRP_PORT_REDIRECT:
|
||||
|
@ -646,13 +697,14 @@ static void srp_reset_req(struct srp_target_port *target, struct srp_request *re
|
|||
|
||||
static int srp_reconnect_target(struct srp_target_port *target)
|
||||
{
|
||||
struct ib_qp_attr qp_attr;
|
||||
struct ib_wc wc;
|
||||
struct Scsi_Host *shost = target->scsi_host;
|
||||
int i, ret;
|
||||
|
||||
if (!srp_change_state(target, SRP_TARGET_LIVE, SRP_TARGET_CONNECTING))
|
||||
if (target->state != SRP_TARGET_LIVE)
|
||||
return -EAGAIN;
|
||||
|
||||
scsi_target_block(&shost->shost_gendev);
|
||||
|
||||
srp_disconnect_target(target);
|
||||
/*
|
||||
* Now get a new local CM ID so that we avoid confusing the
|
||||
|
@ -660,21 +712,11 @@ static int srp_reconnect_target(struct srp_target_port *target)
|
|||
*/
|
||||
ret = srp_new_cm_id(target);
|
||||
if (ret)
|
||||
goto err;
|
||||
goto unblock;
|
||||
|
||||
qp_attr.qp_state = IB_QPS_RESET;
|
||||
ret = ib_modify_qp(target->qp, &qp_attr, IB_QP_STATE);
|
||||
ret = srp_create_target_ib(target);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
ret = srp_init_qp(target, target->qp);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
while (ib_poll_cq(target->recv_cq, 1, &wc) > 0)
|
||||
; /* nothing */
|
||||
while (ib_poll_cq(target->send_cq, 1, &wc) > 0)
|
||||
; /* nothing */
|
||||
goto unblock;
|
||||
|
||||
for (i = 0; i < SRP_CMD_SQ_SIZE; ++i) {
|
||||
struct srp_request *req = &target->req_ring[i];
|
||||
|
@ -686,13 +728,16 @@ static int srp_reconnect_target(struct srp_target_port *target)
|
|||
for (i = 0; i < SRP_SQ_SIZE; ++i)
|
||||
list_add(&target->tx_ring[i]->list, &target->free_tx);
|
||||
|
||||
target->qp_in_error = 0;
|
||||
ret = srp_connect_target(target);
|
||||
|
||||
unblock:
|
||||
scsi_target_unblock(&shost->shost_gendev, ret == 0 ? SDEV_RUNNING :
|
||||
SDEV_TRANSPORT_OFFLINE);
|
||||
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
if (!srp_change_state(target, SRP_TARGET_CONNECTING, SRP_TARGET_LIVE))
|
||||
ret = -EAGAIN;
|
||||
shost_printk(KERN_INFO, target->scsi_host, PFX "reconnect succeeded\n");
|
||||
|
||||
return ret;
|
||||
|
||||
|
@ -705,17 +750,8 @@ static int srp_reconnect_target(struct srp_target_port *target)
|
|||
* However, we have to defer the real removal because we
|
||||
* are in the context of the SCSI error handler now, which
|
||||
* will deadlock if we call scsi_remove_host().
|
||||
*
|
||||
* Schedule our work inside the lock to avoid a race with
|
||||
* the flush_scheduled_work() in srp_remove_one().
|
||||
*/
|
||||
spin_lock_irq(&target->lock);
|
||||
if (target->state == SRP_TARGET_CONNECTING) {
|
||||
target->state = SRP_TARGET_DEAD;
|
||||
INIT_WORK(&target->work, srp_remove_work);
|
||||
queue_work(ib_wq, &target->work);
|
||||
}
|
||||
spin_unlock_irq(&target->lock);
|
||||
srp_queue_remove_work(target);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
@ -1262,6 +1298,19 @@ static void srp_handle_recv(struct srp_target_port *target, struct ib_wc *wc)
|
|||
PFX "Recv failed with error code %d\n", res);
|
||||
}
|
||||
|
||||
static void srp_handle_qp_err(enum ib_wc_status wc_status,
|
||||
enum ib_wc_opcode wc_opcode,
|
||||
struct srp_target_port *target)
|
||||
{
|
||||
if (target->connected && !target->qp_in_error) {
|
||||
shost_printk(KERN_ERR, target->scsi_host,
|
||||
PFX "failed %s status %d\n",
|
||||
wc_opcode & IB_WC_RECV ? "receive" : "send",
|
||||
wc_status);
|
||||
}
|
||||
target->qp_in_error = true;
|
||||
}
|
||||
|
||||
static void srp_recv_completion(struct ib_cq *cq, void *target_ptr)
|
||||
{
|
||||
struct srp_target_port *target = target_ptr;
|
||||
|
@ -1269,15 +1318,11 @@ static void srp_recv_completion(struct ib_cq *cq, void *target_ptr)
|
|||
|
||||
ib_req_notify_cq(cq, IB_CQ_NEXT_COMP);
|
||||
while (ib_poll_cq(cq, 1, &wc) > 0) {
|
||||
if (wc.status) {
|
||||
shost_printk(KERN_ERR, target->scsi_host,
|
||||
PFX "failed receive status %d\n",
|
||||
wc.status);
|
||||
target->qp_in_error = 1;
|
||||
break;
|
||||
if (likely(wc.status == IB_WC_SUCCESS)) {
|
||||
srp_handle_recv(target, &wc);
|
||||
} else {
|
||||
srp_handle_qp_err(wc.status, wc.opcode, target);
|
||||
}
|
||||
|
||||
srp_handle_recv(target, &wc);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1288,16 +1333,12 @@ static void srp_send_completion(struct ib_cq *cq, void *target_ptr)
|
|||
struct srp_iu *iu;
|
||||
|
||||
while (ib_poll_cq(cq, 1, &wc) > 0) {
|
||||
if (wc.status) {
|
||||
shost_printk(KERN_ERR, target->scsi_host,
|
||||
PFX "failed send status %d\n",
|
||||
wc.status);
|
||||
target->qp_in_error = 1;
|
||||
break;
|
||||
if (likely(wc.status == IB_WC_SUCCESS)) {
|
||||
iu = (struct srp_iu *) (uintptr_t) wc.wr_id;
|
||||
list_add(&iu->list, &target->free_tx);
|
||||
} else {
|
||||
srp_handle_qp_err(wc.status, wc.opcode, target);
|
||||
}
|
||||
|
||||
iu = (struct srp_iu *) (uintptr_t) wc.wr_id;
|
||||
list_add(&iu->list, &target->free_tx);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1311,16 +1352,6 @@ static int srp_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *scmnd)
|
|||
unsigned long flags;
|
||||
int len;
|
||||
|
||||
if (target->state == SRP_TARGET_CONNECTING)
|
||||
goto err;
|
||||
|
||||
if (target->state == SRP_TARGET_DEAD ||
|
||||
target->state == SRP_TARGET_REMOVED) {
|
||||
scmnd->result = DID_BAD_TARGET << 16;
|
||||
scmnd->scsi_done(scmnd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&target->lock, flags);
|
||||
iu = __srp_get_tx_iu(target, SRP_IU_CMD);
|
||||
if (!iu)
|
||||
|
@ -1377,7 +1408,6 @@ static int srp_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *scmnd)
|
|||
err_unlock:
|
||||
spin_unlock_irqrestore(&target->lock, flags);
|
||||
|
||||
err:
|
||||
return SCSI_MLQUEUE_HOST_BUSY;
|
||||
}
|
||||
|
||||
|
@ -1419,6 +1449,33 @@ static int srp_alloc_iu_bufs(struct srp_target_port *target)
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static uint32_t srp_compute_rq_tmo(struct ib_qp_attr *qp_attr, int attr_mask)
|
||||
{
|
||||
uint64_t T_tr_ns, max_compl_time_ms;
|
||||
uint32_t rq_tmo_jiffies;
|
||||
|
||||
/*
|
||||
* According to section 11.2.4.2 in the IBTA spec (Modify Queue Pair,
|
||||
* table 91), both the QP timeout and the retry count have to be set
|
||||
* for RC QP's during the RTR to RTS transition.
|
||||
*/
|
||||
WARN_ON_ONCE((attr_mask & (IB_QP_TIMEOUT | IB_QP_RETRY_CNT)) !=
|
||||
(IB_QP_TIMEOUT | IB_QP_RETRY_CNT));
|
||||
|
||||
/*
|
||||
* Set target->rq_tmo_jiffies to one second more than the largest time
|
||||
* it can take before an error completion is generated. See also
|
||||
* C9-140..142 in the IBTA spec for more information about how to
|
||||
* convert the QP Local ACK Timeout value to nanoseconds.
|
||||
*/
|
||||
T_tr_ns = 4096 * (1ULL << qp_attr->timeout);
|
||||
max_compl_time_ms = qp_attr->retry_cnt * 4 * T_tr_ns;
|
||||
do_div(max_compl_time_ms, NSEC_PER_MSEC);
|
||||
rq_tmo_jiffies = msecs_to_jiffies(max_compl_time_ms + 1000);
|
||||
|
||||
return rq_tmo_jiffies;
|
||||
}
|
||||
|
||||
static void srp_cm_rep_handler(struct ib_cm_id *cm_id,
|
||||
struct srp_login_rsp *lrsp,
|
||||
struct srp_target_port *target)
|
||||
|
@ -1478,6 +1535,8 @@ static void srp_cm_rep_handler(struct ib_cm_id *cm_id,
|
|||
if (ret)
|
||||
goto error_free;
|
||||
|
||||
target->rq_tmo_jiffies = srp_compute_rq_tmo(qp_attr, attr_mask);
|
||||
|
||||
ret = ib_modify_qp(target->qp, qp_attr, attr_mask);
|
||||
if (ret)
|
||||
goto error_free;
|
||||
|
@ -1599,6 +1658,7 @@ static int srp_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event)
|
|||
case IB_CM_DREQ_RECEIVED:
|
||||
shost_printk(KERN_WARNING, target->scsi_host,
|
||||
PFX "DREQ received - connection closed\n");
|
||||
srp_change_conn_state(target, false);
|
||||
if (ib_send_cm_drep(cm_id, NULL, 0))
|
||||
shost_printk(KERN_ERR, target->scsi_host,
|
||||
PFX "Sending CM DREP failed\n");
|
||||
|
@ -1608,7 +1668,6 @@ static int srp_cm_handler(struct ib_cm_id *cm_id, struct ib_cm_event *event)
|
|||
shost_printk(KERN_ERR, target->scsi_host,
|
||||
PFX "connection closed\n");
|
||||
|
||||
comp = 1;
|
||||
target->status = 0;
|
||||
break;
|
||||
|
||||
|
@ -1636,10 +1695,6 @@ static int srp_send_tsk_mgmt(struct srp_target_port *target,
|
|||
struct srp_iu *iu;
|
||||
struct srp_tsk_mgmt *tsk_mgmt;
|
||||
|
||||
if (target->state == SRP_TARGET_DEAD ||
|
||||
target->state == SRP_TARGET_REMOVED)
|
||||
return -1;
|
||||
|
||||
init_completion(&target->tsk_mgmt_done);
|
||||
|
||||
spin_lock_irq(&target->lock);
|
||||
|
@ -1729,6 +1784,21 @@ static int srp_reset_host(struct scsi_cmnd *scmnd)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int srp_slave_configure(struct scsi_device *sdev)
|
||||
{
|
||||
struct Scsi_Host *shost = sdev->host;
|
||||
struct srp_target_port *target = host_to_target(shost);
|
||||
struct request_queue *q = sdev->request_queue;
|
||||
unsigned long timeout;
|
||||
|
||||
if (sdev->type == TYPE_DISK) {
|
||||
timeout = max_t(unsigned, 30 * HZ, target->rq_tmo_jiffies);
|
||||
blk_queue_rq_timeout(q, timeout);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t show_id_ext(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
|
@ -1861,6 +1931,7 @@ static struct scsi_host_template srp_template = {
|
|||
.module = THIS_MODULE,
|
||||
.name = "InfiniBand SRP initiator",
|
||||
.proc_name = DRV_NAME,
|
||||
.slave_configure = srp_slave_configure,
|
||||
.info = srp_target_info,
|
||||
.queuecommand = srp_queuecommand,
|
||||
.eh_abort_handler = srp_abort,
|
||||
|
@ -1894,11 +1965,14 @@ static int srp_add_target(struct srp_host *host, struct srp_target_port *target)
|
|||
return PTR_ERR(rport);
|
||||
}
|
||||
|
||||
rport->lld_data = target;
|
||||
|
||||
spin_lock(&host->target_lock);
|
||||
list_add_tail(&target->list, &host->target_list);
|
||||
spin_unlock(&host->target_lock);
|
||||
|
||||
target->state = SRP_TARGET_LIVE;
|
||||
target->connected = false;
|
||||
|
||||
scsi_scan_target(&target->scsi_host->shost_gendev,
|
||||
0, target->scsi_id, SCAN_WILD_CARD, 0);
|
||||
|
@ -2188,6 +2262,7 @@ static ssize_t srp_create_target(struct device *dev,
|
|||
sizeof (struct srp_indirect_buf) +
|
||||
target->cmd_sg_cnt * sizeof (struct srp_direct_buf);
|
||||
|
||||
INIT_WORK(&target->remove_work, srp_remove_work);
|
||||
spin_lock_init(&target->lock);
|
||||
INIT_LIST_HEAD(&target->free_tx);
|
||||
INIT_LIST_HEAD(&target->free_reqs);
|
||||
|
@ -2232,7 +2307,6 @@ static ssize_t srp_create_target(struct device *dev,
|
|||
if (ret)
|
||||
goto err_free_ib;
|
||||
|
||||
target->qp_in_error = 0;
|
||||
ret = srp_connect_target(target);
|
||||
if (ret) {
|
||||
shost_printk(KERN_ERR, target->scsi_host,
|
||||
|
@ -2422,8 +2496,7 @@ static void srp_remove_one(struct ib_device *device)
|
|||
{
|
||||
struct srp_device *srp_dev;
|
||||
struct srp_host *host, *tmp_host;
|
||||
LIST_HEAD(target_list);
|
||||
struct srp_target_port *target, *tmp_target;
|
||||
struct srp_target_port *target;
|
||||
|
||||
srp_dev = ib_get_client_data(device, &srp_client);
|
||||
|
||||
|
@ -2436,35 +2509,17 @@ static void srp_remove_one(struct ib_device *device)
|
|||
wait_for_completion(&host->released);
|
||||
|
||||
/*
|
||||
* Mark all target ports as removed, so we stop queueing
|
||||
* commands and don't try to reconnect.
|
||||
* Remove all target ports.
|
||||
*/
|
||||
spin_lock(&host->target_lock);
|
||||
list_for_each_entry(target, &host->target_list, list) {
|
||||
spin_lock_irq(&target->lock);
|
||||
target->state = SRP_TARGET_REMOVED;
|
||||
spin_unlock_irq(&target->lock);
|
||||
}
|
||||
list_for_each_entry(target, &host->target_list, list)
|
||||
srp_queue_remove_work(target);
|
||||
spin_unlock(&host->target_lock);
|
||||
|
||||
/*
|
||||
* Wait for any reconnection tasks that may have
|
||||
* started before we marked our target ports as
|
||||
* removed, and any target port removal tasks.
|
||||
* Wait for target port removal tasks.
|
||||
*/
|
||||
flush_workqueue(ib_wq);
|
||||
|
||||
list_for_each_entry_safe(target, tmp_target,
|
||||
&host->target_list, list) {
|
||||
srp_del_scsi_host_attr(target->scsi_host);
|
||||
srp_remove_host(target->scsi_host);
|
||||
scsi_remove_host(target->scsi_host);
|
||||
srp_disconnect_target(target);
|
||||
ib_destroy_cm_id(target->cm_id);
|
||||
srp_free_target_ib(target);
|
||||
srp_free_req_data(target);
|
||||
scsi_host_put(target->scsi_host);
|
||||
}
|
||||
flush_workqueue(system_long_wq);
|
||||
|
||||
kfree(host);
|
||||
}
|
||||
|
@ -2478,6 +2533,7 @@ static void srp_remove_one(struct ib_device *device)
|
|||
}
|
||||
|
||||
static struct srp_function_template ib_srp_transport_functions = {
|
||||
.rport_delete = srp_rport_delete,
|
||||
};
|
||||
|
||||
static int __init srp_init_module(void)
|
||||
|
|
|
@ -80,9 +80,7 @@ enum {
|
|||
|
||||
enum srp_target_state {
|
||||
SRP_TARGET_LIVE,
|
||||
SRP_TARGET_CONNECTING,
|
||||
SRP_TARGET_DEAD,
|
||||
SRP_TARGET_REMOVED
|
||||
SRP_TARGET_REMOVED,
|
||||
};
|
||||
|
||||
enum srp_iu_type {
|
||||
|
@ -163,6 +161,9 @@ struct srp_target_port {
|
|||
struct ib_sa_query *path_query;
|
||||
int path_query_id;
|
||||
|
||||
u32 rq_tmo_jiffies;
|
||||
bool connected;
|
||||
|
||||
struct ib_cm_id *cm_id;
|
||||
|
||||
int max_ti_iu_len;
|
||||
|
@ -173,12 +174,12 @@ struct srp_target_port {
|
|||
struct srp_iu *rx_ring[SRP_RQ_SIZE];
|
||||
struct srp_request req_ring[SRP_CMD_SQ_SIZE];
|
||||
|
||||
struct work_struct work;
|
||||
struct work_struct remove_work;
|
||||
|
||||
struct list_head list;
|
||||
struct completion done;
|
||||
int status;
|
||||
int qp_in_error;
|
||||
bool qp_in_error;
|
||||
|
||||
struct completion tsk_mgmt_done;
|
||||
u8 tsk_mgmt_status;
|
||||
|
|
|
@ -38,7 +38,7 @@ struct srp_host_attrs {
|
|||
#define to_srp_host_attrs(host) ((struct srp_host_attrs *)(host)->shost_data)
|
||||
|
||||
#define SRP_HOST_ATTRS 0
|
||||
#define SRP_RPORT_ATTRS 2
|
||||
#define SRP_RPORT_ATTRS 3
|
||||
|
||||
struct srp_internal {
|
||||
struct scsi_transport_template t;
|
||||
|
@ -47,7 +47,6 @@ struct srp_internal {
|
|||
struct device_attribute *host_attrs[SRP_HOST_ATTRS + 1];
|
||||
|
||||
struct device_attribute *rport_attrs[SRP_RPORT_ATTRS + 1];
|
||||
struct device_attribute private_rport_attrs[SRP_RPORT_ATTRS];
|
||||
struct transport_container rport_attr_cont;
|
||||
};
|
||||
|
||||
|
@ -72,24 +71,6 @@ static DECLARE_TRANSPORT_CLASS(srp_host_class, "srp_host", srp_host_setup,
|
|||
static DECLARE_TRANSPORT_CLASS(srp_rport_class, "srp_remote_ports",
|
||||
NULL, NULL, NULL);
|
||||
|
||||
#define SETUP_TEMPLATE(attrb, field, perm, test, ro_test, ro_perm) \
|
||||
i->private_##attrb[count] = dev_attr_##field; \
|
||||
i->private_##attrb[count].attr.mode = perm; \
|
||||
if (ro_test) { \
|
||||
i->private_##attrb[count].attr.mode = ro_perm; \
|
||||
i->private_##attrb[count].store = NULL; \
|
||||
} \
|
||||
i->attrb[count] = &i->private_##attrb[count]; \
|
||||
if (test) \
|
||||
count++
|
||||
|
||||
#define SETUP_RPORT_ATTRIBUTE_RD(field) \
|
||||
SETUP_TEMPLATE(rport_attrs, field, S_IRUGO, 1, 0, 0)
|
||||
|
||||
#define SETUP_RPORT_ATTRIBUTE_RW(field) \
|
||||
SETUP_TEMPLATE(rport_attrs, field, S_IRUGO | S_IWUSR, \
|
||||
1, 1, S_IRUGO)
|
||||
|
||||
#define SRP_PID(p) \
|
||||
(p)->port_id[0], (p)->port_id[1], (p)->port_id[2], (p)->port_id[3], \
|
||||
(p)->port_id[4], (p)->port_id[5], (p)->port_id[6], (p)->port_id[7], \
|
||||
|
@ -135,6 +116,24 @@ show_srp_rport_roles(struct device *dev, struct device_attribute *attr,
|
|||
|
||||
static DEVICE_ATTR(roles, S_IRUGO, show_srp_rport_roles, NULL);
|
||||
|
||||
static ssize_t store_srp_rport_delete(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
struct srp_rport *rport = transport_class_to_srp_rport(dev);
|
||||
struct Scsi_Host *shost = dev_to_shost(dev);
|
||||
struct srp_internal *i = to_srp_internal(shost->transportt);
|
||||
|
||||
if (i->f->rport_delete) {
|
||||
i->f->rport_delete(rport);
|
||||
return count;
|
||||
} else {
|
||||
return -ENOSYS;
|
||||
}
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(delete, S_IWUSR, NULL, store_srp_rport_delete);
|
||||
|
||||
static void srp_rport_release(struct device *dev)
|
||||
{
|
||||
struct srp_rport *rport = dev_to_rport(dev);
|
||||
|
@ -324,12 +323,16 @@ srp_attach_transport(struct srp_function_template *ft)
|
|||
i->rport_attr_cont.ac.attrs = &i->rport_attrs[0];
|
||||
i->rport_attr_cont.ac.class = &srp_rport_class.class;
|
||||
i->rport_attr_cont.ac.match = srp_rport_match;
|
||||
transport_container_register(&i->rport_attr_cont);
|
||||
|
||||
count = 0;
|
||||
SETUP_RPORT_ATTRIBUTE_RD(port_id);
|
||||
SETUP_RPORT_ATTRIBUTE_RD(roles);
|
||||
i->rport_attrs[count] = NULL;
|
||||
i->rport_attrs[count++] = &dev_attr_port_id;
|
||||
i->rport_attrs[count++] = &dev_attr_roles;
|
||||
if (ft->rport_delete)
|
||||
i->rport_attrs[count++] = &dev_attr_delete;
|
||||
i->rport_attrs[count++] = NULL;
|
||||
BUG_ON(count > ARRAY_SIZE(i->rport_attrs));
|
||||
|
||||
transport_container_register(&i->rport_attr_cont);
|
||||
|
||||
i->f = ft;
|
||||
|
||||
|
|
|
@ -14,13 +14,21 @@ struct srp_rport_identifiers {
|
|||
};
|
||||
|
||||
struct srp_rport {
|
||||
/* for initiator and target drivers */
|
||||
|
||||
struct device dev;
|
||||
|
||||
u8 port_id[16];
|
||||
u8 roles;
|
||||
|
||||
/* for initiator drivers */
|
||||
|
||||
void *lld_data; /* LLD private data */
|
||||
};
|
||||
|
||||
struct srp_function_template {
|
||||
/* for initiator drivers */
|
||||
void (*rport_delete)(struct srp_rport *rport);
|
||||
/* for target drivers */
|
||||
int (* tsk_mgmt_response)(struct Scsi_Host *, u64, u64, int);
|
||||
int (* it_nexus_response)(struct Scsi_Host *, u64, int);
|
||||
|
|
Loading…
Reference in a new issue