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