Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
iscsi_target_erl2.c
Go to the documentation of this file.
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 <[email protected]>
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>
25 
26 #include "iscsi_target_core.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  */
38  struct iscsi_cmd *cmd,
39  __be32 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 (be32_to_cpu(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 
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 
85 static 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 
96 static 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 
112  struct iscsi_session *sess,
113  u16 cid)
114 {
115  struct iscsi_conn_recovery *cr;
116 
117  spin_lock(&sess->cr_i_lock);
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 
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_conn_node) {
142 
143  list_del(&cmd->i_conn_node);
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_conn_node) {
164 
165  list_del(&cmd->i_conn_node);
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 
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 
196 static void 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 
205 /*
206  * Called with cr->conn_recovery_cmd_lock help.
207  */
209  struct iscsi_cmd *cmd,
210  struct iscsi_session *sess)
211 {
212  struct iscsi_conn_recovery *cr;
213 
214  if (!cmd->cr) {
215  pr_err("struct iscsi_conn_recovery pointer for ITT: 0x%08x"
216  " is NULL!\n", cmd->init_task_tag);
217  BUG();
218  }
219  cr = cmd->cr;
220 
221  list_del(&cmd->i_conn_node);
222  return --cr->cmd_count;
223 }
224 
226  struct iscsi_conn_recovery *cr,
227  u32 exp_statsn)
228 {
229  u32 dropped_count = 0;
230  struct iscsi_cmd *cmd, *cmd_tmp;
231  struct iscsi_session *sess = cr->sess;
232 
233  spin_lock(&cr->conn_recovery_cmd_lock);
234  list_for_each_entry_safe(cmd, cmd_tmp,
235  &cr->conn_recovery_cmd_list, i_conn_node) {
236 
237  if (((cmd->deferred_i_state != ISTATE_SENT_STATUS) &&
238  (cmd->deferred_i_state != ISTATE_REMOVE)) ||
239  (cmd->stat_sn >= exp_statsn)) {
240  continue;
241  }
242 
243  dropped_count++;
244  pr_debug("Dropping Acknowledged ITT: 0x%08x, StatSN:"
245  " 0x%08x, CID: %hu.\n", cmd->init_task_tag,
246  cmd->stat_sn, cr->cid);
247 
249 
250  spin_unlock(&cr->conn_recovery_cmd_lock);
251  iscsit_free_cmd(cmd);
252  spin_lock(&cr->conn_recovery_cmd_lock);
253  }
254  spin_unlock(&cr->conn_recovery_cmd_lock);
255 
256  pr_debug("Dropped %u total acknowledged commands on"
257  " CID: %hu less than old ExpStatSN: 0x%08x\n",
258  dropped_count, cr->cid, exp_statsn);
259 
260  if (!cr->cmd_count) {
261  pr_debug("No commands to be reassigned for failed"
262  " connection CID: %hu on SID: %u\n",
263  cr->cid, sess->sid);
264  iscsit_remove_inactive_connection_recovery_entry(cr, sess);
265  iscsit_attach_active_connection_recovery_entry(sess, cr);
266  pr_debug("iSCSI connection recovery successful for CID:"
267  " %hu on SID: %u\n", cr->cid, sess->sid);
269  } else {
270  iscsit_remove_inactive_connection_recovery_entry(cr, sess);
271  iscsit_attach_active_connection_recovery_entry(sess, cr);
272  }
273 }
274 
276 {
277  u32 dropped_count = 0;
278  struct iscsi_cmd *cmd, *cmd_tmp;
279  struct iscsi_ooo_cmdsn *ooo_cmdsn, *ooo_cmdsn_tmp;
280  struct iscsi_session *sess = conn->sess;
281 
282  mutex_lock(&sess->cmdsn_mutex);
283  list_for_each_entry_safe(ooo_cmdsn, ooo_cmdsn_tmp,
284  &sess->sess_ooo_cmdsn_list, ooo_list) {
285 
286  if (ooo_cmdsn->cid != conn->cid)
287  continue;
288 
289  dropped_count++;
290  pr_debug("Dropping unacknowledged CmdSN:"
291  " 0x%08x during connection recovery on CID: %hu\n",
292  ooo_cmdsn->cmdsn, conn->cid);
293  iscsit_remove_ooo_cmdsn(sess, ooo_cmdsn);
294  }
295  mutex_unlock(&sess->cmdsn_mutex);
296 
297  spin_lock_bh(&conn->cmd_lock);
298  list_for_each_entry_safe(cmd, cmd_tmp, &conn->conn_cmd_list, i_conn_node) {
299  if (!(cmd->cmd_flags & ICF_OOO_CMDSN))
300  continue;
301 
302  list_del(&cmd->i_conn_node);
303 
304  spin_unlock_bh(&conn->cmd_lock);
305  iscsit_free_cmd(cmd);
306  spin_lock_bh(&conn->cmd_lock);
307  }
308  spin_unlock_bh(&conn->cmd_lock);
309 
310  pr_debug("Dropped %u total unacknowledged commands on CID:"
311  " %hu for ExpCmdSN: 0x%08x.\n", dropped_count, conn->cid,
312  sess->exp_cmd_sn);
313  return 0;
314 }
315 
317 {
318  u32 cmd_count = 0;
319  struct iscsi_cmd *cmd, *cmd_tmp;
320  struct iscsi_conn_recovery *cr;
321 
322  /*
323  * Allocate an struct iscsi_conn_recovery for this connection.
324  * Each struct iscsi_cmd contains an struct iscsi_conn_recovery pointer
325  * (struct iscsi_cmd->cr) so we need to allocate this before preparing the
326  * connection's command list for connection recovery.
327  */
328  cr = kzalloc(sizeof(struct iscsi_conn_recovery), GFP_KERNEL);
329  if (!cr) {
330  pr_err("Unable to allocate memory for"
331  " struct iscsi_conn_recovery.\n");
332  return -1;
333  }
334  INIT_LIST_HEAD(&cr->cr_list);
335  INIT_LIST_HEAD(&cr->conn_recovery_cmd_list);
337  /*
338  * Only perform connection recovery on ISCSI_OP_SCSI_CMD or
339  * ISCSI_OP_NOOP_OUT opcodes. For all other opcodes call
340  * list_del(&cmd->i_conn_node); to release the command to the
341  * session pool and remove it from the connection's list.
342  *
343  * Also stop the DataOUT timer, which will be restarted after
344  * sending the TMR response.
345  */
346  spin_lock_bh(&conn->cmd_lock);
347  list_for_each_entry_safe(cmd, cmd_tmp, &conn->conn_cmd_list, i_conn_node) {
348 
349  if ((cmd->iscsi_opcode != ISCSI_OP_SCSI_CMD) &&
350  (cmd->iscsi_opcode != ISCSI_OP_NOOP_OUT)) {
351  pr_debug("Not performing realligence on"
352  " Opcode: 0x%02x, ITT: 0x%08x, CmdSN: 0x%08x,"
353  " CID: %hu\n", cmd->iscsi_opcode,
354  cmd->init_task_tag, cmd->cmd_sn, conn->cid);
355 
356  list_del(&cmd->i_conn_node);
357  spin_unlock_bh(&conn->cmd_lock);
358  iscsit_free_cmd(cmd);
359  spin_lock_bh(&conn->cmd_lock);
360  continue;
361  }
362 
363  /*
364  * Special case where commands greater than or equal to
365  * the session's ExpCmdSN are attached to the connection
366  * list but not to the out of order CmdSN list. The one
367  * obvious case is when a command with immediate data
368  * attached must only check the CmdSN against ExpCmdSN
369  * after the data is received. The special case below
370  * is when the connection fails before data is received,
371  * but also may apply to other PDUs, so it has been
372  * made generic here.
373  */
374  if (!(cmd->cmd_flags & ICF_OOO_CMDSN) && !cmd->immediate_cmd &&
375  (cmd->cmd_sn >= conn->sess->exp_cmd_sn)) {
376  list_del(&cmd->i_conn_node);
377  spin_unlock_bh(&conn->cmd_lock);
378  iscsit_free_cmd(cmd);
379  spin_lock_bh(&conn->cmd_lock);
380  continue;
381  }
382 
383  cmd_count++;
384  pr_debug("Preparing Opcode: 0x%02x, ITT: 0x%08x,"
385  " CmdSN: 0x%08x, StatSN: 0x%08x, CID: %hu for"
386  " realligence.\n", cmd->iscsi_opcode,
387  cmd->init_task_tag, cmd->cmd_sn, cmd->stat_sn,
388  conn->cid);
389 
390  cmd->deferred_i_state = cmd->i_state;
392 
393  if (cmd->data_direction == DMA_TO_DEVICE)
395 
396  cmd->sess = conn->sess;
397 
398  list_del(&cmd->i_conn_node);
399  spin_unlock_bh(&conn->cmd_lock);
400 
402 
404  /*
405  * Add the struct iscsi_cmd to the connection recovery cmd list
406  */
407  spin_lock(&cr->conn_recovery_cmd_lock);
409  spin_unlock(&cr->conn_recovery_cmd_lock);
410 
411  spin_lock_bh(&conn->cmd_lock);
412  cmd->cr = cr;
413  cmd->conn = NULL;
414  }
415  spin_unlock_bh(&conn->cmd_lock);
416  /*
417  * Fill in the various values in the preallocated struct iscsi_conn_recovery.
418  */
419  cr->cid = conn->cid;
420  cr->cmd_count = cmd_count;
421  cr->maxrecvdatasegmentlength = conn->conn_ops->MaxRecvDataSegmentLength;
422  cr->maxxmitdatasegmentlength = conn->conn_ops->MaxXmitDataSegmentLength;
423  cr->sess = conn->sess;
424 
425  iscsit_attach_inactive_connection_recovery_entry(conn->sess, cr);
426 
427  return 0;
428 }
429 
431 {
432  atomic_set(&conn->connection_recovery, 1);
433 
434  if (iscsit_close_connection(conn) < 0)
435  return -1;
436 
437  return 0;
438 }