Merge branch 'fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/linux-arm-soc
[pandora-kernel.git] / drivers / target / iscsi / iscsi_target_datain_values.c
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
30 struct 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
45 void 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
52 void 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
61 void 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
73 struct 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  */
91 static 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  */
189 static 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  */
310 static 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  */
409 static 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
511 struct 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 }