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