Final merge with IET (r201-204):
[mirror/scst/.git] / iscsi-scst / kernel / digest.c
1 /*
2  *  iSCSI digest handling.
3  *
4  *  Copyright (C) 2004 - 2006 Xiranet Communications GmbH
5  *                            <arne.redlich@xiranet.com>
6  *  Copyright (C) 2007 - 2008 Vladislav Bolkhovitin
7  *  Copyright (C) 2007 - 2008 CMS Distribution Limited
8  *
9  *  This program is free software; you can redistribute it and/or
10  *  modify it under the terms of the GNU General Public License
11  *  as published by the Free Software Foundation.
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 <linux/types.h>
20 #include <linux/scatterlist.h>
21
22 #include "iscsi.h"
23 #include "digest.h"
24 #include <linux/crc32c.h>
25
26 void digest_alg_available(int *val)
27 {
28 #if defined(CONFIG_LIBCRC32C_MODULE) || defined(CONFIG_LIBCRC32C)
29         int crc32c = 1;
30 #else
31         int crc32c = 0;
32 #endif
33
34         if ((*val & DIGEST_CRC32C) && !crc32c) {
35                 PRINT_ERROR("%s", "CRC32C digest algorithm not available "
36                         "in kernel");
37                 *val |= ~DIGEST_CRC32C;
38         }
39 }
40
41 /**
42  * initialize support for digest calculation.
43  *
44  * digest_init -
45  * @conn: ptr to connection to make use of digests
46  *
47  * @return: 0 on success, < 0 on error
48  */
49 int digest_init(struct iscsi_conn *conn)
50 {
51         if (!(conn->hdigest_type & DIGEST_ALL))
52                 conn->hdigest_type = DIGEST_NONE;
53
54         if (!(conn->ddigest_type & DIGEST_ALL))
55                 conn->ddigest_type = DIGEST_NONE;
56
57         return 0;
58 }
59
60 static u32 evaluate_crc32_from_sg(struct scatterlist *sg, int total,
61         int pad_bytes)
62 {
63         u32 crc = ~0;
64
65 #ifdef CONFIG_SCST_ISCSI_DEBUG_DIGEST_FAILURES
66         if (((scst_random() % 100000) == 752)) {
67                 PRINT_INFO("%s", "Simulating digest failure");
68                 return 0;
69         }
70 #endif
71
72 #if defined(CONFIG_LIBCRC32C_MODULE) || defined(CONFIG_LIBCRC32C)
73         while (total > 0) {
74                 int d = min(min(total, (int)(sg->length)),
75                         (int)(PAGE_SIZE - sg->offset));
76
77                 crc = crc32c(crc, sg_virt(sg), d);
78                 total -= d;
79                 sg++;
80         }
81
82         if (pad_bytes) {
83                 u32 padding = 0;
84                 /*
85                  * Digest includes also padding for aligned pdu length,
86                  * hopefully it is always filled with 0s in pdu (according to
87                  * crypto/crc32c.c
88                  */
89                 crc = crc32c(crc, (u8 *)&padding, pad_bytes);
90         }
91 #endif
92
93         return ~cpu_to_le32(crc);
94 }
95
96 static u32 digest_header(struct iscsi_pdu *pdu)
97 {
98         struct scatterlist sg[2];
99         unsigned int nbytes = sizeof(struct iscsi_hdr);
100
101         sg_init_table(sg, 2);
102
103         sg_set_buf(&sg[0], &pdu->bhs, nbytes);
104         if (pdu->ahssize) {
105                 sg_set_buf(&sg[1], pdu->ahs, pdu->ahssize);
106                 nbytes += pdu->ahssize;
107         }
108         return evaluate_crc32_from_sg(sg, nbytes, 0);
109 }
110
111 static u32 digest_data(struct iscsi_cmnd *cmd, u32 osize, u32 offset)
112 {
113         struct scatterlist *sg = cmd->sg;
114         int idx, count;
115         struct scatterlist saved_sg;
116         u32 size = (osize + 3) & ~3;
117         u32 crc;
118
119         offset += sg[0].offset;
120         idx = offset >> PAGE_SHIFT;
121         offset &= ~PAGE_MASK;
122
123         count = get_pgcnt(size, offset);
124
125         TRACE_DBG("req %p, idx %d, count %d, sg_cnt %d, size %d, "
126                 "offset %d", cmd, idx, count, cmd->sg_cnt, size, offset);
127         sBUG_ON(idx + count > cmd->sg_cnt);
128         sBUG_ON(count > (signed)ISCSI_CONN_IOV_MAX);
129
130         saved_sg = sg[idx];
131         sg[idx].offset = offset;
132         sg[idx].length -= offset - saved_sg.offset;
133
134         crc = evaluate_crc32_from_sg(sg + idx, osize, size - osize);
135
136         sg[idx] = saved_sg;
137         return crc;
138 }
139
140 int digest_rx_header(struct iscsi_cmnd *cmnd)
141 {
142         u32 crc;
143
144         crc = digest_header(&cmnd->pdu);
145         if (unlikely(crc != cmnd->hdigest)) {
146                 PRINT_ERROR("%s", "RX header digest failed");
147                 return -EIO;
148         } else
149                 TRACE_DBG("RX header digest OK for cmd %p", cmnd);
150
151         return 0;
152 }
153
154 void digest_tx_header(struct iscsi_cmnd *cmnd)
155 {
156         cmnd->hdigest = digest_header(&cmnd->pdu);
157         TRACE_DBG("TX header digest for cmd %p: %x", cmnd, cmnd->hdigest);
158 }
159
160 int digest_rx_data(struct iscsi_cmnd *cmnd)
161 {
162         struct iscsi_cmnd *req;
163         struct iscsi_data_out_hdr *req_hdr;
164         u32 offset, crc;
165         int res = 0;
166
167         if (unlikely(cmnd->rejected))
168                 goto out;
169
170         switch (cmnd_opcode(cmnd)) {
171         case ISCSI_OP_SCSI_DATA_OUT:
172                 req = cmnd->cmd_req;
173                 req_hdr = (struct iscsi_data_out_hdr *)&cmnd->pdu.bhs;
174                 offset = be32_to_cpu(req_hdr->buffer_offset);
175                 break;
176
177         default:
178                 req = cmnd;
179                 offset = 0;
180         }
181
182         crc = digest_data(req, cmnd->pdu.datasize, offset);
183
184         if (unlikely(crc != cmnd->ddigest)) {
185                 PRINT_ERROR("%s", "RX data digest failed");
186                 TRACE_MGMT_DBG("Calculated crc %x, ddigest %x, offset %d", crc,
187                         cmnd->ddigest, offset);
188                 iscsi_dump_pdu(&cmnd->pdu);
189                 res = -EIO;
190         } else
191                 TRACE_DBG("RX data digest OK for cmd %p", cmnd);
192
193 out:
194         return res;
195 }
196
197 void digest_tx_data(struct iscsi_cmnd *cmnd)
198 {
199         struct iscsi_data_in_hdr *hdr;
200         u32 offset;
201
202         TRACE_DBG("%s:%d req %p, own_sg %d, sg %p, sgcnt %d cmnd %p, "
203                 "own_sg %d, sg %p, sgcnt %d", __func__, __LINE__,
204                 cmnd->parent_req, cmnd->parent_req->own_sg,
205                 cmnd->parent_req->sg, cmnd->parent_req->sg_cnt,
206                 cmnd, cmnd->own_sg, cmnd->sg, cmnd->sg_cnt);
207
208         switch (cmnd_opcode(cmnd)) {
209         case ISCSI_OP_SCSI_DATA_IN:
210                 hdr = (struct iscsi_data_in_hdr *)&cmnd->pdu.bhs;
211                 offset = be32_to_cpu(hdr->buffer_offset);
212                 break;
213         default:
214                 offset = 0;
215         }
216
217         /*
218          * cmnd is used here regardless of its sg comes from parent or was
219          * allocated for this cmnd only, see cmnd_send_pdu()
220          */
221         cmnd->ddigest = digest_data(cmnd, cmnd->pdu.datasize, offset);
222         TRACE_DBG("TX data digest for cmd %p: %x (offset %d, opcode %x)", cmnd,
223                 cmnd->ddigest, offset, cmnd_opcode(cmnd));
224 }