linux/drivers/target/iscsi/iscsi_target_datain_values.c
<<
>>
Prefs
   1/*******************************************************************************
   2 * This file contains the iSCSI Target DataIN value generation functions.
   3 *
   4 * \u00a9 Copyright 2007-2011 RisingTide Systems LLC.
   5 *
   6 * Licensed to the Linux Foundation under the General Public License (GPL) version 2.
   7 *
   8 * Author: Nicholas A. Bellinger <nab@linux-iscsi.org>
   9 *
  10 * This program is free software; you can redistribute it and/or modify
  11 * it under the terms of the GNU General Public License as published by
  12 * the Free Software Foundation; either version 2 of the License, or
  13 * (at your option) any later version.
  14 *
  15 * This program is distributed in the hope that it will be useful,
  16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18 * GNU General Public License for more details.
  19 ******************************************************************************/
  20
  21#include <scsi/iscsi_proto.h>
  22
  23#include "iscsi_target_core.h"
  24#include "iscsi_target_seq_pdu_list.h"
  25#include "iscsi_target_erl1.h"
  26#include "iscsi_target_util.h"
  27#include "iscsi_target.h"
  28#include "iscsi_target_datain_values.h"
  29
  30struct iscsi_datain_req *iscsit_allocate_datain_req(void)
  31{
  32        struct iscsi_datain_req *dr;
  33
  34        dr = kmem_cache_zalloc(lio_dr_cache, GFP_ATOMIC);
  35        if (!dr) {
  36                pr_err("Unable to allocate memory for"
  37                                " struct iscsi_datain_req\n");
  38                return NULL;
  39        }
  40        INIT_LIST_HEAD(&dr->dr_list);
  41
  42        return dr;
  43}
  44
  45void iscsit_attach_datain_req(struct iscsi_cmd *cmd, struct iscsi_datain_req *dr)
  46{
  47        spin_lock(&cmd->datain_lock);
  48        list_add_tail(&dr->dr_list, &cmd->datain_list);
  49        spin_unlock(&cmd->datain_lock);
  50}
  51
  52void iscsit_free_datain_req(struct iscsi_cmd *cmd, struct iscsi_datain_req *dr)
  53{
  54        spin_lock(&cmd->datain_lock);
  55        list_del(&dr->dr_list);
  56        spin_unlock(&cmd->datain_lock);
  57
  58        kmem_cache_free(lio_dr_cache, dr);
  59}
  60
  61void iscsit_free_all_datain_reqs(struct iscsi_cmd *cmd)
  62{
  63        struct iscsi_datain_req *dr, *dr_tmp;
  64
  65        spin_lock(&cmd->datain_lock);
  66        list_for_each_entry_safe(dr, dr_tmp, &cmd->datain_list, dr_list) {
  67                list_del(&dr->dr_list);
  68                kmem_cache_free(lio_dr_cache, dr);
  69        }
  70        spin_unlock(&cmd->datain_lock);
  71}
  72
  73struct iscsi_datain_req *iscsit_get_datain_req(struct iscsi_cmd *cmd)
  74{
  75        struct iscsi_datain_req *dr;
  76
  77        if (list_empty(&cmd->datain_list)) {
  78                pr_err("cmd->datain_list is empty for ITT:"
  79                        " 0x%08x\n", cmd->init_task_tag);
  80                return NULL;
  81        }
  82        list_for_each_entry(dr, &cmd->datain_list, dr_list)
  83                break;
  84
  85        return dr;
  86}
  87
  88/*
  89 *      For Normal and Recovery DataSequenceInOrder=Yes and DataPDUInOrder=Yes.
  90 */
  91static struct iscsi_datain_req *iscsit_set_datain_values_yes_and_yes(
  92        struct iscsi_cmd *cmd,
  93        struct iscsi_datain *datain)
  94{
  95        u32 next_burst_len, read_data_done, read_data_left;
  96        struct iscsi_conn *conn = cmd->conn;
  97        struct iscsi_datain_req *dr;
  98
  99        dr = iscsit_get_datain_req(cmd);
 100        if (!dr)
 101                return NULL;
 102
 103        if (dr->recovery && dr->generate_recovery_values) {
 104                if (iscsit_create_recovery_datain_values_datasequenceinorder_yes(
 105                                        cmd, dr) < 0)
 106                        return NULL;
 107
 108                dr->generate_recovery_values = 0;
 109        }
 110
 111        next_burst_len = (!dr->recovery) ?
 112                        cmd->next_burst_len : dr->next_burst_len;
 113        read_data_done = (!dr->recovery) ?
 114                        cmd->read_data_done : dr->read_data_done;
 115
 116        read_data_left = (cmd->data_length - read_data_done);
 117        if (!read_data_left) {
 118                pr_err("ITT: 0x%08x read_data_left is zero!\n",
 119                                cmd->init_task_tag);
 120                return NULL;
 121        }
 122
 123        if ((read_data_left <= conn->conn_ops->MaxRecvDataSegmentLength) &&
 124            (read_data_left <= (conn->sess->sess_ops->MaxBurstLength -
 125             next_burst_len))) {
 126                datain->length = read_data_left;
 127
 128                datain->flags |= (ISCSI_FLAG_CMD_FINAL | ISCSI_FLAG_DATA_STATUS);
 129                if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
 130                        datain->flags |= ISCSI_FLAG_DATA_ACK;
 131        } else {
 132                if ((next_burst_len +
 133                     conn->conn_ops->MaxRecvDataSegmentLength) <
 134                     conn->sess->sess_ops->MaxBurstLength) {
 135                        datain->length =
 136                                conn->conn_ops->MaxRecvDataSegmentLength;
 137                        next_burst_len += datain->length;
 138                } else {
 139                        datain->length = (conn->sess->sess_ops->MaxBurstLength -
 140                                          next_burst_len);
 141                        next_burst_len = 0;
 142
 143                        datain->flags |= ISCSI_FLAG_CMD_FINAL;
 144                        if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
 145                                datain->flags |= ISCSI_FLAG_DATA_ACK;
 146                }
 147        }
 148
 149        datain->data_sn = (!dr->recovery) ? cmd->data_sn++ : dr->data_sn++;
 150        datain->offset = read_data_done;
 151
 152        if (!dr->recovery) {
 153                cmd->next_burst_len = next_burst_len;
 154                cmd->read_data_done += datain->length;
 155        } else {
 156                dr->next_burst_len = next_burst_len;
 157                dr->read_data_done += datain->length;
 158        }
 159
 160        if (!dr->recovery) {
 161                if (datain->flags & ISCSI_FLAG_DATA_STATUS)
 162                        dr->dr_complete = DATAIN_COMPLETE_NORMAL;
 163
 164                return dr;
 165        }
 166
 167        if (!dr->runlength) {
 168                if (datain->flags & ISCSI_FLAG_DATA_STATUS) {
 169                        dr->dr_complete =
 170                            (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
 171                                DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
 172                                DATAIN_COMPLETE_CONNECTION_RECOVERY;
 173                }
 174        } else {
 175                if ((dr->begrun + dr->runlength) == dr->data_sn) {
 176                        dr->dr_complete =
 177                            (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
 178                                DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
 179                                DATAIN_COMPLETE_CONNECTION_RECOVERY;
 180                }
 181        }
 182
 183        return dr;
 184}
 185
 186/*
 187 *      For Normal and Recovery DataSequenceInOrder=No and DataPDUInOrder=Yes.
 188 */
 189static struct iscsi_datain_req *iscsit_set_datain_values_no_and_yes(
 190        struct iscsi_cmd *cmd,
 191        struct iscsi_datain *datain)
 192{
 193        u32 offset, read_data_done, read_data_left, seq_send_order;
 194        struct iscsi_conn *conn = cmd->conn;
 195        struct iscsi_datain_req *dr;
 196        struct iscsi_seq *seq;
 197
 198        dr = iscsit_get_datain_req(cmd);
 199        if (!dr)
 200                return NULL;
 201
 202        if (dr->recovery && dr->generate_recovery_values) {
 203                if (iscsit_create_recovery_datain_values_datasequenceinorder_no(
 204                                        cmd, dr) < 0)
 205                        return NULL;
 206
 207                dr->generate_recovery_values = 0;
 208        }
 209
 210        read_data_done = (!dr->recovery) ?
 211                        cmd->read_data_done : dr->read_data_done;
 212        seq_send_order = (!dr->recovery) ?
 213                        cmd->seq_send_order : dr->seq_send_order;
 214
 215        read_data_left = (cmd->data_length - read_data_done);
 216        if (!read_data_left) {
 217                pr_err("ITT: 0x%08x read_data_left is zero!\n",
 218                                cmd->init_task_tag);
 219                return NULL;
 220        }
 221
 222        seq = iscsit_get_seq_holder_for_datain(cmd, seq_send_order);
 223        if (!seq)
 224                return NULL;
 225
 226        seq->sent = 1;
 227
 228        if (!dr->recovery && !seq->next_burst_len)
 229                seq->first_datasn = cmd->data_sn;
 230
 231        offset = (seq->offset + seq->next_burst_len);
 232
 233        if ((offset + conn->conn_ops->MaxRecvDataSegmentLength) >=
 234             cmd->data_length) {
 235                datain->length = (cmd->data_length - offset);
 236                datain->offset = offset;
 237
 238                datain->flags |= ISCSI_FLAG_CMD_FINAL;
 239                if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
 240                        datain->flags |= ISCSI_FLAG_DATA_ACK;
 241
 242                seq->next_burst_len = 0;
 243                seq_send_order++;
 244        } else {
 245                if ((seq->next_burst_len +
 246                     conn->conn_ops->MaxRecvDataSegmentLength) <
 247                     conn->sess->sess_ops->MaxBurstLength) {
 248                        datain->length =
 249                                conn->conn_ops->MaxRecvDataSegmentLength;
 250                        datain->offset = (seq->offset + seq->next_burst_len);
 251
 252                        seq->next_burst_len += datain->length;
 253                } else {
 254                        datain->length = (conn->sess->sess_ops->MaxBurstLength -
 255                                          seq->next_burst_len);
 256                        datain->offset = (seq->offset + seq->next_burst_len);
 257
 258                        datain->flags |= ISCSI_FLAG_CMD_FINAL;
 259                        if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
 260                                datain->flags |= ISCSI_FLAG_DATA_ACK;
 261
 262                        seq->next_burst_len = 0;
 263                        seq_send_order++;
 264                }
 265        }
 266
 267        if ((read_data_done + datain->length) == cmd->data_length)
 268                datain->flags |= ISCSI_FLAG_DATA_STATUS;
 269
 270        datain->data_sn = (!dr->recovery) ? cmd->data_sn++ : dr->data_sn++;
 271        if (!dr->recovery) {
 272                cmd->seq_send_order = seq_send_order;
 273                cmd->read_data_done += datain->length;
 274        } else {
 275                dr->seq_send_order = seq_send_order;
 276                dr->read_data_done += datain->length;
 277        }
 278
 279        if (!dr->recovery) {
 280                if (datain->flags & ISCSI_FLAG_CMD_FINAL)
 281                        seq->last_datasn = datain->data_sn;
 282                if (datain->flags & ISCSI_FLAG_DATA_STATUS)
 283                        dr->dr_complete = DATAIN_COMPLETE_NORMAL;
 284
 285                return dr;
 286        }
 287
 288        if (!dr->runlength) {
 289                if (datain->flags & ISCSI_FLAG_DATA_STATUS) {
 290                        dr->dr_complete =
 291                            (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
 292                                DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
 293                                DATAIN_COMPLETE_CONNECTION_RECOVERY;
 294                }
 295        } else {
 296                if ((dr->begrun + dr->runlength) == dr->data_sn) {
 297                        dr->dr_complete =
 298                            (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
 299                                DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
 300                                DATAIN_COMPLETE_CONNECTION_RECOVERY;
 301                }
 302        }
 303
 304        return dr;
 305}
 306
 307/*
 308 *      For Normal and Recovery DataSequenceInOrder=Yes and DataPDUInOrder=No.
 309 */
 310static struct iscsi_datain_req *iscsit_set_datain_values_yes_and_no(
 311        struct iscsi_cmd *cmd,
 312        struct iscsi_datain *datain)
 313{
 314        u32 next_burst_len, read_data_done, read_data_left;
 315        struct iscsi_conn *conn = cmd->conn;
 316        struct iscsi_datain_req *dr;
 317        struct iscsi_pdu *pdu;
 318
 319        dr = iscsit_get_datain_req(cmd);
 320        if (!dr)
 321                return NULL;
 322
 323        if (dr->recovery && dr->generate_recovery_values) {
 324                if (iscsit_create_recovery_datain_values_datasequenceinorder_yes(
 325                                        cmd, dr) < 0)
 326                        return NULL;
 327
 328                dr->generate_recovery_values = 0;
 329        }
 330
 331        next_burst_len = (!dr->recovery) ?
 332                        cmd->next_burst_len : dr->next_burst_len;
 333        read_data_done = (!dr->recovery) ?
 334                        cmd->read_data_done : dr->read_data_done;
 335
 336        read_data_left = (cmd->data_length - read_data_done);
 337        if (!read_data_left) {
 338                pr_err("ITT: 0x%08x read_data_left is zero!\n",
 339                                cmd->init_task_tag);
 340                return dr;
 341        }
 342
 343        pdu = iscsit_get_pdu_holder_for_seq(cmd, NULL);
 344        if (!pdu)
 345                return dr;
 346
 347        if ((read_data_done + pdu->length) == cmd->data_length) {
 348                pdu->flags |= (ISCSI_FLAG_CMD_FINAL | ISCSI_FLAG_DATA_STATUS);
 349                if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
 350                        pdu->flags |= ISCSI_FLAG_DATA_ACK;
 351
 352                next_burst_len = 0;
 353        } else {
 354                if ((next_burst_len + conn->conn_ops->MaxRecvDataSegmentLength) <
 355                     conn->sess->sess_ops->MaxBurstLength)
 356                        next_burst_len += pdu->length;
 357                else {
 358                        pdu->flags |= ISCSI_FLAG_CMD_FINAL;
 359                        if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
 360                                pdu->flags |= ISCSI_FLAG_DATA_ACK;
 361
 362                        next_burst_len = 0;
 363                }
 364        }
 365
 366        pdu->data_sn = (!dr->recovery) ? cmd->data_sn++ : dr->data_sn++;
 367        if (!dr->recovery) {
 368                cmd->next_burst_len = next_burst_len;
 369                cmd->read_data_done += pdu->length;
 370        } else {
 371                dr->next_burst_len = next_burst_len;
 372                dr->read_data_done += pdu->length;
 373        }
 374
 375        datain->flags = pdu->flags;
 376        datain->length = pdu->length;
 377        datain->offset = pdu->offset;
 378        datain->data_sn = pdu->data_sn;
 379
 380        if (!dr->recovery) {
 381                if (datain->flags & ISCSI_FLAG_DATA_STATUS)
 382                        dr->dr_complete = DATAIN_COMPLETE_NORMAL;
 383
 384                return dr;
 385        }
 386
 387        if (!dr->runlength) {
 388                if (datain->flags & ISCSI_FLAG_DATA_STATUS) {
 389                        dr->dr_complete =
 390                            (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
 391                                DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
 392                                DATAIN_COMPLETE_CONNECTION_RECOVERY;
 393                }
 394        } else {
 395                if ((dr->begrun + dr->runlength) == dr->data_sn) {
 396                        dr->dr_complete =
 397                            (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
 398                                DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
 399                                DATAIN_COMPLETE_CONNECTION_RECOVERY;
 400                }
 401        }
 402
 403        return dr;
 404}
 405
 406/*
 407 *      For Normal and Recovery DataSequenceInOrder=No and DataPDUInOrder=No.
 408 */
 409static struct iscsi_datain_req *iscsit_set_datain_values_no_and_no(
 410        struct iscsi_cmd *cmd,
 411        struct iscsi_datain *datain)
 412{
 413        u32 read_data_done, read_data_left, seq_send_order;
 414        struct iscsi_conn *conn = cmd->conn;
 415        struct iscsi_datain_req *dr;
 416        struct iscsi_pdu *pdu;
 417        struct iscsi_seq *seq = NULL;
 418
 419        dr = iscsit_get_datain_req(cmd);
 420        if (!dr)
 421                return NULL;
 422
 423        if (dr->recovery && dr->generate_recovery_values) {
 424                if (iscsit_create_recovery_datain_values_datasequenceinorder_no(
 425                                        cmd, dr) < 0)
 426                        return NULL;
 427
 428                dr->generate_recovery_values = 0;
 429        }
 430
 431        read_data_done = (!dr->recovery) ?
 432                        cmd->read_data_done : dr->read_data_done;
 433        seq_send_order = (!dr->recovery) ?
 434                        cmd->seq_send_order : dr->seq_send_order;
 435
 436        read_data_left = (cmd->data_length - read_data_done);
 437        if (!read_data_left) {
 438                pr_err("ITT: 0x%08x read_data_left is zero!\n",
 439                                cmd->init_task_tag);
 440                return NULL;
 441        }
 442
 443        seq = iscsit_get_seq_holder_for_datain(cmd, seq_send_order);
 444        if (!seq)
 445                return NULL;
 446
 447        seq->sent = 1;
 448
 449        if (!dr->recovery && !seq->next_burst_len)
 450                seq->first_datasn = cmd->data_sn;
 451
 452        pdu = iscsit_get_pdu_holder_for_seq(cmd, seq);
 453        if (!pdu)
 454                return NULL;
 455
 456        if (seq->pdu_send_order == seq->pdu_count) {
 457                pdu->flags |= ISCSI_FLAG_CMD_FINAL;
 458                if (conn->sess->sess_ops->ErrorRecoveryLevel > 0)
 459                        pdu->flags |= ISCSI_FLAG_DATA_ACK;
 460
 461                seq->next_burst_len = 0;
 462                seq_send_order++;
 463        } else
 464                seq->next_burst_len += pdu->length;
 465
 466        if ((read_data_done + pdu->length) == cmd->data_length)
 467                pdu->flags |= ISCSI_FLAG_DATA_STATUS;
 468
 469        pdu->data_sn = (!dr->recovery) ? cmd->data_sn++ : dr->data_sn++;
 470        if (!dr->recovery) {
 471                cmd->seq_send_order = seq_send_order;
 472                cmd->read_data_done += pdu->length;
 473        } else {
 474                dr->seq_send_order = seq_send_order;
 475                dr->read_data_done += pdu->length;
 476        }
 477
 478        datain->flags = pdu->flags;
 479        datain->length = pdu->length;
 480        datain->offset = pdu->offset;
 481        datain->data_sn = pdu->data_sn;
 482
 483        if (!dr->recovery) {
 484                if (datain->flags & ISCSI_FLAG_CMD_FINAL)
 485                        seq->last_datasn = datain->data_sn;
 486                if (datain->flags & ISCSI_FLAG_DATA_STATUS)
 487                        dr->dr_complete = DATAIN_COMPLETE_NORMAL;
 488
 489                return dr;
 490        }
 491
 492        if (!dr->runlength) {
 493                if (datain->flags & ISCSI_FLAG_DATA_STATUS) {
 494                        dr->dr_complete =
 495                            (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
 496                                DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
 497                                DATAIN_COMPLETE_CONNECTION_RECOVERY;
 498                }
 499        } else {
 500                if ((dr->begrun + dr->runlength) == dr->data_sn) {
 501                        dr->dr_complete =
 502                            (dr->recovery == DATAIN_WITHIN_COMMAND_RECOVERY) ?
 503                                DATAIN_COMPLETE_WITHIN_COMMAND_RECOVERY :
 504                                DATAIN_COMPLETE_CONNECTION_RECOVERY;
 505                }
 506        }
 507
 508        return dr;
 509}
 510
 511struct iscsi_datain_req *iscsit_get_datain_values(
 512        struct iscsi_cmd *cmd,
 513        struct iscsi_datain *datain)
 514{
 515        struct iscsi_conn *conn = cmd->conn;
 516
 517        if (conn->sess->sess_ops->DataSequenceInOrder &&
 518            conn->sess->sess_ops->DataPDUInOrder)
 519                return iscsit_set_datain_values_yes_and_yes(cmd, datain);
 520        else if (!conn->sess->sess_ops->DataSequenceInOrder &&
 521                  conn->sess->sess_ops->DataPDUInOrder)
 522                return iscsit_set_datain_values_no_and_yes(cmd, datain);
 523        else if (conn->sess->sess_ops->DataSequenceInOrder &&
 524                 !conn->sess->sess_ops->DataPDUInOrder)
 525                return iscsit_set_datain_values_yes_and_no(cmd, datain);
 526        else if (!conn->sess->sess_ops->DataSequenceInOrder &&
 527                   !conn->sess->sess_ops->DataPDUInOrder)
 528                return iscsit_set_datain_values_no_and_no(cmd, datain);
 529
 530        return NULL;
 531}
 532