linux/drivers/target/iscsi/iscsi_target_erl2.c
<<
>>
Prefs
   1/*******************************************************************************
   2 * This file contains error recovery level two functions used by
   3 * the iSCSI Target driver.
   4 *
   5 * \u00a9 Copyright 2007-2011 RisingTide Systems LLC.
   6 *
   7 * Licensed to the Linux Foundation under the General Public License (GPL) version 2.
   8 *
   9 * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
  10 *
  11 * This program is free software; you can redistribute it and/or modify
  12 * it under the terms of the GNU General Public License as published by
  13 * the Free Software Foundation; either version 2 of the License, or
  14 * (at your option) any later version.
  15 *
  16 * This program is distributed in the hope that it will be useful,
  17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19 * GNU General Public License for more details.
  20 ******************************************************************************/
  21
  22#include <scsi/iscsi_proto.h>
  23#include <target/target_core_base.h>
  24#include <target/target_core_fabric.h>
  25
  26#include "iscsi_target_core.h"
  27#include "iscsi_target_datain_values.h"
  28#include "iscsi_target_util.h"
  29#include "iscsi_target_erl0.h"
  30#include "iscsi_target_erl1.h"
  31#include "iscsi_target_erl2.h"
  32#include "iscsi_target.h"
  33
  34/*
  35 *      FIXME: Does RData SNACK apply here as well?
  36 */
  37void iscsit_create_conn_recovery_datain_values(
  38        struct iscsi_cmd *cmd,
  39        u32 exp_data_sn)
  40{
  41        u32 data_sn = 0;
  42        struct iscsi_conn *conn = cmd->conn;
  43
  44        cmd->next_burst_len = 0;
  45        cmd->read_data_done = 0;
  46
  47        while (exp_data_sn > data_sn) {
  48                if ((cmd->next_burst_len +
  49                     conn->conn_ops->MaxRecvDataSegmentLength) <
  50                     conn->sess->sess_ops->MaxBurstLength) {
  51                        cmd->read_data_done +=
  52                               conn->conn_ops->MaxRecvDataSegmentLength;
  53                        cmd->next_burst_len +=
  54                               conn->conn_ops->MaxRecvDataSegmentLength;
  55                } else {
  56                        cmd->read_data_done +=
  57                                (conn->sess->sess_ops->MaxBurstLength -
  58                                cmd->next_burst_len);
  59                        cmd->next_burst_len = 0;
  60                }
  61                data_sn++;
  62        }
  63}
  64
  65void iscsit_create_conn_recovery_dataout_values(
  66        struct iscsi_cmd *cmd)
  67{
  68        u32 write_data_done = 0;
  69        struct iscsi_conn *conn = cmd->conn;
  70
  71        cmd->data_sn = 0;
  72        cmd->next_burst_len = 0;
  73
  74        while (cmd->write_data_done > write_data_done) {
  75                if ((write_data_done + conn->sess->sess_ops->MaxBurstLength) <=
  76                     cmd->write_data_done)
  77                        write_data_done += conn->sess->sess_ops->MaxBurstLength;
  78                else
  79                        break;
  80        }
  81
  82        cmd->write_data_done = write_data_done;
  83}
  84
  85static int iscsit_attach_active_connection_recovery_entry(
  86        struct iscsi_session *sess,
  87        struct iscsi_conn_recovery *cr)
  88{
  89        spin_lock(&sess->cr_a_lock);
  90        list_add_tail(&cr->cr_list, &sess->cr_active_list);
  91        spin_unlock(&sess->cr_a_lock);
  92
  93        return 0;
  94}
  95
  96static int iscsit_attach_inactive_connection_recovery_entry(
  97        struct iscsi_session *sess,
  98        struct iscsi_conn_recovery *cr)
  99{
 100        spin_lock(&sess->cr_i_lock);
 101        list_add_tail(&cr->cr_list, &sess->cr_inactive_list);
 102
 103        sess->conn_recovery_count++;
 104        pr_debug("Incremented connection recovery count to %u for"
 105                " SID: %u\n", sess->conn_recovery_count, sess->sid);
 106        spin_unlock(&sess->cr_i_lock);
 107
 108        return 0;
 109}
 110
 111struct iscsi_conn_recovery *iscsit_get_inactive_connection_recovery_entry(
 112        struct iscsi_session *sess,
 113        u16 cid)
 114{
 115        struct iscsi_conn_recovery *cr;
 116
 117        spin_lock(&sess->cr_i_lock);
 118        list_for_each_entry(cr, &sess->cr_inactive_list, cr_list) {
 119                if (cr->cid == cid) {
 120                        spin_unlock(&sess->cr_i_lock);
 121                        return cr;
 122                }
 123        }
 124        spin_unlock(&sess->cr_i_lock);
 125
 126        return NULL;
 127}
 128
 129void iscsit_free_connection_recovery_entires(struct iscsi_session *sess)
 130{
 131        struct iscsi_cmd *cmd, *cmd_tmp;
 132        struct iscsi_conn_recovery *cr, *cr_tmp;
 133
 134        spin_lock(&sess->cr_a_lock);
 135        list_for_each_entry_safe(cr, cr_tmp, &sess->cr_active_list, cr_list) {
 136                list_del(&cr->cr_list);
 137                spin_unlock(&sess->cr_a_lock);
 138
 139                spin_lock(&cr->conn_recovery_cmd_lock);
 140                list_for_each_entry_safe(cmd, cmd_tmp,
 141                                &cr->conn_recovery_cmd_list, i_list) {
 142
 143                        list_del(&cmd->i_list);
 144                        cmd->conn = NULL;
 145                        spin_unlock(&cr->conn_recovery_cmd_lock);
 146                        iscsit_free_cmd(cmd);
 147                        spin_lock(&cr->conn_recovery_cmd_lock);
 148                }
 149                spin_unlock(&cr->conn_recovery_cmd_lock);
 150                spin_lock(&sess->cr_a_lock);
 151
 152                kfree(cr);
 153        }
 154        spin_unlock(&sess->cr_a_lock);
 155
 156        spin_lock(&sess->cr_i_lock);
 157        list_for_each_entry_safe(cr, cr_tmp, &sess->cr_inactive_list, cr_list) {
 158                list_del(&cr->cr_list);
 159                spin_unlock(&sess->cr_i_lock);
 160
 161                spin_lock(&cr->conn_recovery_cmd_lock);
 162                list_for_each_entry_safe(cmd, cmd_tmp,
 163                                &cr->conn_recovery_cmd_list, i_list) {
 164
 165                        list_del(&cmd->i_list);
 166                        cmd->conn = NULL;
 167                        spin_unlock(&cr->conn_recovery_cmd_lock);
 168                        iscsit_free_cmd(cmd);
 169                        spin_lock(&cr->conn_recovery_cmd_lock);
 170                }
 171                spin_unlock(&cr->conn_recovery_cmd_lock);
 172                spin_lock(&sess->cr_i_lock);
 173
 174                kfree(cr);
 175        }
 176        spin_unlock(&sess->cr_i_lock);
 177}
 178
 179int iscsit_remove_active_connection_recovery_entry(
 180        struct iscsi_conn_recovery *cr,
 181        struct iscsi_session *sess)
 182{
 183        spin_lock(&sess->cr_a_lock);
 184        list_del(&cr->cr_list);
 185
 186        sess->conn_recovery_count--;
 187        pr_debug("Decremented connection recovery count to %u for"
 188                " SID: %u\n", sess->conn_recovery_count, sess->sid);
 189        spin_unlock(&sess->cr_a_lock);
 190
 191        kfree(cr);
 192
 193        return 0;
 194}
 195
 196int iscsit_remove_inactive_connection_recovery_entry(
 197        struct iscsi_conn_recovery *cr,
 198        struct iscsi_session *sess)
 199{
 200        spin_lock(&sess->cr_i_lock);
 201        list_del(&cr->cr_list);
 202        spin_unlock(&sess->cr_i_lock);
 203
 204        return 0;
 205}
 206
 207/*
 208 *      Called with cr->conn_recovery_cmd_lock help.
 209 */
 210int iscsit_remove_cmd_from_connection_recovery(
 211        struct iscsi_cmd *cmd,
 212        struct iscsi_session *sess)
 213{
 214        struct iscsi_conn_recovery *cr;
 215
 216        if (!cmd->cr) {
 217                pr_err("struct iscsi_conn_recovery pointer for ITT: 0x%08x"
 218                        " is NULL!\n", cmd->init_task_tag);
 219                BUG();
 220        }
 221        cr = cmd->cr;
 222
 223        list_del(&cmd->i_list);
 224        return --cr->cmd_count;
 225}
 226
 227void iscsit_discard_cr_cmds_by_expstatsn(
 228        struct iscsi_conn_recovery *cr,
 229        u32 exp_statsn)
 230{
 231        u32 dropped_count = 0;
 232        struct iscsi_cmd *cmd, *cmd_tmp;
 233        struct iscsi_session *sess = cr->sess;
 234
 235        spin_lock(&cr->conn_recovery_cmd_lock);
 236        list_for_each_entry_safe(cmd, cmd_tmp,
 237                        &cr->conn_recovery_cmd_list, i_list) {
 238
 239                if (((cmd->deferred_i_state != ISTATE_SENT_STATUS) &&
 240                     (cmd->deferred_i_state != ISTATE_REMOVE)) ||
 241                     (cmd->stat_sn >= exp_statsn)) {
 242                        continue;
 243                }
 244
 245                dropped_count++;
 246                pr_debug("Dropping Acknowledged ITT: 0x%08x, StatSN:"
 247                        " 0x%08x, CID: %hu.\n", cmd->init_task_tag,
 248                                cmd->stat_sn, cr->cid);
 249
 250                iscsit_remove_cmd_from_connection_recovery(cmd, sess);
 251
 252                spin_unlock(&cr->conn_recovery_cmd_lock);
 253                iscsit_free_cmd(cmd);
 254                spin_lock(&cr->conn_recovery_cmd_lock);
 255        }
 256        spin_unlock(&cr->conn_recovery_cmd_lock);
 257
 258        pr_debug("Dropped %u total acknowledged commands on"
 259                " CID: %hu less than old ExpStatSN: 0x%08x\n",
 260                        dropped_count, cr->cid, exp_statsn);
 261
 262        if (!cr->cmd_count) {
 263                pr_debug("No commands to be reassigned for failed"
 264                        " connection CID: %hu on SID: %u\n",
 265                        cr->cid, sess->sid);
 266                iscsit_remove_inactive_connection_recovery_entry(cr, sess);
 267                iscsit_attach_active_connection_recovery_entry(sess, cr);
 268                pr_debug("iSCSI connection recovery successful for CID:"
 269                        " %hu on SID: %u\n", cr->cid, sess->sid);
 270                iscsit_remove_active_connection_recovery_entry(cr, sess);
 271        } else {
 272                iscsit_remove_inactive_connection_recovery_entry(cr, sess);
 273                iscsit_attach_active_connection_recovery_entry(sess, cr);
 274        }
 275}
 276
 277int iscsit_discard_unacknowledged_ooo_cmdsns_for_conn(struct iscsi_conn *conn)
 278{
 279        u32 dropped_count = 0;
 280        struct iscsi_cmd *cmd, *cmd_tmp;
 281        struct iscsi_ooo_cmdsn *ooo_cmdsn, *ooo_cmdsn_tmp;
 282        struct iscsi_session *sess = conn->sess;
 283
 284        mutex_lock(&sess->cmdsn_mutex);
 285        list_for_each_entry_safe(ooo_cmdsn, ooo_cmdsn_tmp,
 286                        &sess->sess_ooo_cmdsn_list, ooo_list) {
 287
 288                if (ooo_cmdsn->cid != conn->cid)
 289                        continue;
 290
 291                dropped_count++;
 292                pr_debug("Dropping unacknowledged CmdSN:"
 293                " 0x%08x during connection recovery on CID: %hu\n",
 294                        ooo_cmdsn->cmdsn, conn->cid);
 295                iscsit_remove_ooo_cmdsn(sess, ooo_cmdsn);
 296        }
 297        mutex_unlock(&sess->cmdsn_mutex);
 298
 299        spin_lock_bh(&conn->cmd_lock);
 300        list_for_each_entry_safe(cmd, cmd_tmp, &conn->conn_cmd_list, i_list) {
 301                if (!(cmd->cmd_flags & ICF_OOO_CMDSN))
 302                        continue;
 303
 304                list_del(&cmd->i_list);
 305
 306                spin_unlock_bh(&conn->cmd_lock);
 307                iscsit_free_cmd(cmd);
 308                spin_lock_bh(&conn->cmd_lock);
 309        }
 310        spin_unlock_bh(&conn->cmd_lock);
 311
 312        pr_debug("Dropped %u total unacknowledged commands on CID:"
 313                " %hu for ExpCmdSN: 0x%08x.\n", dropped_count, conn->cid,
 314                                sess->exp_cmd_sn);
 315        return 0;
 316}
 317
 318int iscsit_prepare_cmds_for_realligance(struct iscsi_conn *conn)
 319{
 320        u32 cmd_count = 0;
 321        struct iscsi_cmd *cmd, *cmd_tmp;
 322        struct iscsi_conn_recovery *cr;
 323
 324        /*
 325         * Allocate an struct iscsi_conn_recovery for this connection.
 326         * Each struct iscsi_cmd contains an struct iscsi_conn_recovery pointer
 327         * (struct iscsi_cmd->cr) so we need to allocate this before preparing the
 328         * connection's command list for connection recovery.
 329         */
 330        cr = kzalloc(sizeof(struct iscsi_conn_recovery), GFP_KERNEL);
 331        if (!cr) {
 332                pr_err("Unable to allocate memory for"
 333                        " struct iscsi_conn_recovery.\n");
 334                return -1;
 335        }
 336        INIT_LIST_HEAD(&cr->cr_list);
 337        INIT_LIST_HEAD(&cr->conn_recovery_cmd_list);
 338        spin_lock_init(&cr->conn_recovery_cmd_lock);
 339        /*
 340         * Only perform connection recovery on ISCSI_OP_SCSI_CMD or
 341         * ISCSI_OP_NOOP_OUT opcodes.  For all other opcodes call
 342         * list_del(&cmd->i_list); to release the command to the
 343         * session pool and remove it from the connection's list.
 344         *
 345         * Also stop the DataOUT timer, which will be restarted after
 346         * sending the TMR response.
 347         */
 348        spin_lock_bh(&conn->cmd_lock);
 349        list_for_each_entry_safe(cmd, cmd_tmp, &conn->conn_cmd_list, i_list) {
 350
 351                if ((cmd->iscsi_opcode != ISCSI_OP_SCSI_CMD) &&
 352                    (cmd->iscsi_opcode != ISCSI_OP_NOOP_OUT)) {
 353                        pr_debug("Not performing realligence on"
 354                                " Opcode: 0x%02x, ITT: 0x%08x, CmdSN: 0x%08x,"
 355                                " CID: %hu\n", cmd->iscsi_opcode,
 356                                cmd->init_task_tag, cmd->cmd_sn, conn->cid);
 357
 358                        list_del(&cmd->i_list);
 359                        spin_unlock_bh(&conn->cmd_lock);
 360                        iscsit_free_cmd(cmd);
 361                        spin_lock_bh(&conn->cmd_lock);
 362                        continue;
 363                }
 364
 365                /*
 366                 * Special case where commands greater than or equal to
 367                 * the session's ExpCmdSN are attached to the connection
 368                 * list but not to the out of order CmdSN list.  The one
 369                 * obvious case is when a command with immediate data
 370                 * attached must only check the CmdSN against ExpCmdSN
 371                 * after the data is received.  The special case below
 372                 * is when the connection fails before data is received,
 373                 * but also may apply to other PDUs, so it has been
 374                 * made generic here.
 375                 */
 376                if (!(cmd->cmd_flags & ICF_OOO_CMDSN) && !cmd->immediate_cmd &&
 377                     (cmd->cmd_sn >= conn->sess->exp_cmd_sn)) {
 378                        list_del(&cmd->i_list);
 379                        spin_unlock_bh(&conn->cmd_lock);
 380                        iscsit_free_cmd(cmd);
 381                        spin_lock_bh(&conn->cmd_lock);
 382                        continue;
 383                }
 384
 385                cmd_count++;
 386                pr_debug("Preparing Opcode: 0x%02x, ITT: 0x%08x,"
 387                        " CmdSN: 0x%08x, StatSN: 0x%08x, CID: %hu for"
 388                        " realligence.\n", cmd->iscsi_opcode,
 389                        cmd->init_task_tag, cmd->cmd_sn, cmd->stat_sn,
 390                        conn->cid);
 391
 392                cmd->deferred_i_state = cmd->i_state;
 393                cmd->i_state = ISTATE_IN_CONNECTION_RECOVERY;
 394
 395                if (cmd->data_direction == DMA_TO_DEVICE)
 396                        iscsit_stop_dataout_timer(cmd);
 397
 398                cmd->sess = conn->sess;
 399
 400                list_del(&cmd->i_list);
 401                spin_unlock_bh(&conn->cmd_lock);
 402
 403                iscsit_free_all_datain_reqs(cmd);
 404
 405                transport_wait_for_tasks(&cmd->se_cmd);
 406                /*
 407                 * Add the struct iscsi_cmd to the connection recovery cmd list
 408                 */
 409                spin_lock(&cr->conn_recovery_cmd_lock);
 410                list_add_tail(&cmd->i_list, &cr->conn_recovery_cmd_list);
 411                spin_unlock(&cr->conn_recovery_cmd_lock);
 412
 413                spin_lock_bh(&conn->cmd_lock);
 414                cmd->cr = cr;
 415                cmd->conn = NULL;
 416        }
 417        spin_unlock_bh(&conn->cmd_lock);
 418        /*
 419         * Fill in the various values in the preallocated struct iscsi_conn_recovery.
 420         */
 421        cr->cid = conn->cid;
 422        cr->cmd_count = cmd_count;
 423        cr->maxrecvdatasegmentlength = conn->conn_ops->MaxRecvDataSegmentLength;
 424        cr->sess = conn->sess;
 425
 426        iscsit_attach_inactive_connection_recovery_entry(conn->sess, cr);
 427
 428        return 0;
 429}
 430
 431int iscsit_connection_recovery_transport_reset(struct iscsi_conn *conn)
 432{
 433        atomic_set(&conn->connection_recovery, 1);
 434
 435        if (iscsit_close_connection(conn) < 0)
 436                return -1;
 437
 438        return 0;
 439}
 440