~ubuntu-branches/ubuntu/precise/linux-ti-omap4/precise

« back to all changes in this revision

Viewing changes to ubuntu/iscsitarget/digest.c

  • Committer: Bazaar Package Importer
  • Author(s): Paolo Pisati
  • Date: 2011-06-29 15:23:51 UTC
  • mfrom: (26.1.1 natty-proposed)
  • Revision ID: james.westby@ubuntu.com-20110629152351-xs96tm303d95rpbk
Tags: 3.0.0-1200.2
* Rebased against 3.0.0-6.7
* BSP from TI based on 3.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * iSCSI digest handling.
3
 
 * (C) 2004 - 2006 Xiranet Communications GmbH <arne.redlich@xiranet.com>
4
 
 * This code is licensed under the GPL.
5
 
 */
6
 
 
7
 
#include <linux/types.h>
8
 
 
9
 
#include "iscsi.h"
10
 
#include "digest.h"
11
 
#include "iscsi_dbg.h"
12
 
 
13
 
void digest_alg_available(unsigned int *val)
14
 
{
15
 
        if (*val & DIGEST_CRC32C &&
16
 
            !crypto_has_alg("crc32c", 0, CRYPTO_ALG_ASYNC)) {
17
 
                printk("CRC32C digest algorithm not available in kernel\n");
18
 
                *val |= ~DIGEST_CRC32C;
19
 
        }
20
 
}
21
 
 
22
 
/**
23
 
 * initialize support for digest calculation.
24
 
 *
25
 
 * digest_init -
26
 
 * @conn: ptr to connection to make use of digests
27
 
 *
28
 
 * @return: 0 on success, < 0 on error
29
 
 */
30
 
int digest_init(struct iscsi_conn *conn)
31
 
{
32
 
        int err = 0;
33
 
 
34
 
        if (!(conn->hdigest_type & DIGEST_ALL))
35
 
                conn->hdigest_type = DIGEST_NONE;
36
 
 
37
 
        if (!(conn->ddigest_type & DIGEST_ALL))
38
 
                conn->ddigest_type = DIGEST_NONE;
39
 
 
40
 
        if (conn->hdigest_type & DIGEST_CRC32C ||
41
 
            conn->ddigest_type & DIGEST_CRC32C) {
42
 
                conn->rx_hash.tfm = crypto_alloc_hash("crc32c", 0,
43
 
                                                      CRYPTO_ALG_ASYNC);
44
 
                conn->rx_hash.flags = 0;
45
 
                if (IS_ERR(conn->rx_hash.tfm)) {
46
 
                        conn->rx_hash.tfm = NULL;
47
 
                        err = -ENOMEM;
48
 
                        goto out;
49
 
                }
50
 
 
51
 
                conn->tx_hash.tfm = crypto_alloc_hash("crc32c", 0,
52
 
                                                      CRYPTO_ALG_ASYNC);
53
 
                conn->tx_hash.flags = 0;
54
 
                if (IS_ERR(conn->tx_hash.tfm)) {
55
 
                        conn->tx_hash.tfm = NULL;
56
 
                        err = -ENOMEM;
57
 
                        goto out;
58
 
                }
59
 
        }
60
 
 
61
 
out:
62
 
        if (err)
63
 
                digest_cleanup(conn);
64
 
 
65
 
        return err;
66
 
}
67
 
 
68
 
/**
69
 
 * free resources used for digest calculation.
70
 
 *
71
 
 * digest_cleanup -
72
 
 * @conn: ptr to connection that made use of digests
73
 
 */
74
 
void digest_cleanup(struct iscsi_conn *conn)
75
 
{
76
 
        if (conn->tx_hash.tfm)
77
 
                crypto_free_hash(conn->tx_hash.tfm);
78
 
        if (conn->rx_hash.tfm)
79
 
                crypto_free_hash(conn->rx_hash.tfm);
80
 
}
81
 
 
82
 
/**
83
 
 * debug handling of header digest errors:
84
 
 * simulates a digest error after n PDUs / every n-th PDU of type
85
 
 * HDIGEST_ERR_CORRUPT_PDU_TYPE.
86
 
 */
87
 
static inline void __dbg_simulate_header_digest_error(struct iscsi_cmnd *cmnd)
88
 
{
89
 
#define HDIGEST_ERR_AFTER_N_CMNDS 1000
90
 
#define HDIGEST_ERR_ONLY_ONCE     1
91
 
#define HDIGEST_ERR_CORRUPT_PDU_TYPE ISCSI_OP_SCSI_CMD
92
 
#define HDIGEST_ERR_CORRUPT_PDU_WITH_DATA_ONLY 0
93
 
 
94
 
        static int num_cmnds = 0;
95
 
        static int num_errs = 0;
96
 
 
97
 
        if (cmnd_opcode(cmnd) == HDIGEST_ERR_CORRUPT_PDU_TYPE) {
98
 
                if (HDIGEST_ERR_CORRUPT_PDU_WITH_DATA_ONLY) {
99
 
                        if (cmnd->pdu.datasize)
100
 
                                num_cmnds++;
101
 
                } else
102
 
                        num_cmnds++;
103
 
        }
104
 
 
105
 
        if ((num_cmnds == HDIGEST_ERR_AFTER_N_CMNDS)
106
 
            && (!(HDIGEST_ERR_ONLY_ONCE && num_errs))) {
107
 
                printk("*** Faking header digest error ***\n");
108
 
                printk("\tcmnd: 0x%x, itt 0x%x, sn 0x%x\n",
109
 
                       cmnd_opcode(cmnd),
110
 
                       be32_to_cpu(cmnd->pdu.bhs.itt),
111
 
                       be32_to_cpu(cmnd->pdu.bhs.sn));
112
 
                cmnd->hdigest = ~cmnd->hdigest;
113
 
                /* make things even worse by manipulating header fields */
114
 
                cmnd->pdu.datasize += 8;
115
 
                num_errs++;
116
 
                num_cmnds = 0;
117
 
        }
118
 
        return;
119
 
}
120
 
 
121
 
/**
122
 
 * debug handling of data digest errors:
123
 
 * simulates a digest error after n PDUs / every n-th PDU of type
124
 
 * DDIGEST_ERR_CORRUPT_PDU_TYPE.
125
 
 */
126
 
static inline void __dbg_simulate_data_digest_error(struct iscsi_cmnd *cmnd)
127
 
{
128
 
#define DDIGEST_ERR_AFTER_N_CMNDS 50
129
 
#define DDIGEST_ERR_ONLY_ONCE     1
130
 
#define DDIGEST_ERR_CORRUPT_PDU_TYPE   ISCSI_OP_SCSI_DATA_OUT
131
 
#define DDIGEST_ERR_CORRUPT_UNSOL_DATA_ONLY 0
132
 
 
133
 
        static int num_cmnds = 0;
134
 
        static int num_errs = 0;
135
 
 
136
 
        if ((cmnd->pdu.datasize)
137
 
            && (cmnd_opcode(cmnd) == DDIGEST_ERR_CORRUPT_PDU_TYPE)) {
138
 
                switch (cmnd_opcode(cmnd)) {
139
 
                case ISCSI_OP_SCSI_DATA_OUT:
140
 
                        if ((DDIGEST_ERR_CORRUPT_UNSOL_DATA_ONLY)
141
 
                            && (cmnd->pdu.bhs.ttt != ISCSI_RESERVED_TAG))
142
 
                                break;
143
 
                default:
144
 
                        num_cmnds++;
145
 
                }
146
 
        }
147
 
 
148
 
        if ((num_cmnds == DDIGEST_ERR_AFTER_N_CMNDS)
149
 
            && (!(DDIGEST_ERR_ONLY_ONCE && num_errs))
150
 
            && (cmnd->pdu.datasize)
151
 
            && (!cmnd->conn->read_overflow)) {
152
 
                printk("*** Faking data digest error: ***");
153
 
                printk("\tcmnd 0x%x, itt 0x%x, sn 0x%x\n",
154
 
                       cmnd_opcode(cmnd),
155
 
                       be32_to_cpu(cmnd->pdu.bhs.itt),
156
 
                       be32_to_cpu(cmnd->pdu.bhs.sn));
157
 
                cmnd->ddigest = ~cmnd->ddigest;
158
 
                num_errs++;
159
 
                num_cmnds = 0;
160
 
        }
161
 
}
162
 
 
163
 
static void digest_header(struct hash_desc *hash, struct iscsi_pdu *pdu,
164
 
                          u8 *crc)
165
 
{
166
 
        struct scatterlist sg[2];
167
 
        unsigned int nbytes = sizeof(struct iscsi_hdr);
168
 
 
169
 
        sg_init_table(sg, pdu->ahssize ? 2 : 1);
170
 
 
171
 
        sg_set_buf(&sg[0], &pdu->bhs, nbytes);
172
 
        if (pdu->ahssize) {
173
 
                sg_set_buf(&sg[1], pdu->ahs, pdu->ahssize);
174
 
                nbytes += pdu->ahssize;
175
 
        }
176
 
 
177
 
        crypto_hash_init(hash);
178
 
        crypto_hash_update(hash, sg, nbytes);
179
 
        crypto_hash_final(hash, crc);
180
 
}
181
 
 
182
 
int digest_rx_header(struct iscsi_cmnd *cmnd)
183
 
{
184
 
        u32 crc;
185
 
 
186
 
        digest_header(&cmnd->conn->rx_hash, &cmnd->pdu, (u8 *) &crc);
187
 
        if (crc != cmnd->hdigest)
188
 
                return -EIO;
189
 
 
190
 
        return 0;
191
 
}
192
 
 
193
 
void digest_tx_header(struct iscsi_cmnd *cmnd)
194
 
{
195
 
        digest_header(&cmnd->conn->tx_hash, &cmnd->pdu, (u8 *) &cmnd->hdigest);
196
 
}
197
 
 
198
 
static void digest_data(struct hash_desc *hash, struct iscsi_cmnd *cmnd,
199
 
                        struct tio *tio, u32 offset, u8 *crc)
200
 
{
201
 
        struct scatterlist *sg = cmnd->conn->hash_sg;
202
 
        u32 size, length;
203
 
        int i, idx, count;
204
 
        unsigned int nbytes;
205
 
 
206
 
        size = cmnd->pdu.datasize;
207
 
        nbytes = size = (size + 3) & ~3;
208
 
 
209
 
        offset += tio->offset;
210
 
        idx = offset >> PAGE_CACHE_SHIFT;
211
 
        offset &= ~PAGE_CACHE_MASK;
212
 
        count = get_pgcnt(size, offset);
213
 
        assert(idx + count <= tio->pg_cnt);
214
 
 
215
 
        assert(count <= ISCSI_CONN_IOV_MAX);
216
 
 
217
 
        sg_init_table(sg, ARRAY_SIZE(cmnd->conn->hash_sg));
218
 
        crypto_hash_init(hash);
219
 
 
220
 
        for (i = 0; size; i++) {
221
 
                if (offset + size > PAGE_CACHE_SIZE)
222
 
                        length = PAGE_CACHE_SIZE - offset;
223
 
                else
224
 
                        length = size;
225
 
 
226
 
                sg_set_page(&sg[i], tio->pvec[idx + i], length, offset);
227
 
                size -= length;
228
 
                offset = 0;
229
 
        }
230
 
 
231
 
        sg_mark_end(&sg[i - 1]);
232
 
 
233
 
        crypto_hash_update(hash, sg, nbytes);
234
 
        crypto_hash_final(hash, crc);
235
 
}
236
 
 
237
 
int digest_rx_data(struct iscsi_cmnd *cmnd)
238
 
{
239
 
        struct tio *tio;
240
 
        struct iscsi_cmnd *scsi_cmnd;
241
 
        struct iscsi_data_out_hdr *req;
242
 
        u32 offset, crc;
243
 
 
244
 
        switch (cmnd_opcode(cmnd)) {
245
 
        case ISCSI_OP_SCSI_REJECT:
246
 
        case ISCSI_OP_PDU_REJECT:
247
 
        case ISCSI_OP_DATA_REJECT:
248
 
                return 0;
249
 
        case ISCSI_OP_SCSI_DATA_OUT:
250
 
                scsi_cmnd = cmnd->req;
251
 
                req = (struct iscsi_data_out_hdr *) &cmnd->pdu.bhs;
252
 
                tio = scsi_cmnd->tio;
253
 
                offset = be32_to_cpu(req->buffer_offset);
254
 
                break;
255
 
        default:
256
 
                tio = cmnd->tio;
257
 
                offset = 0;
258
 
        }
259
 
 
260
 
        digest_data(&cmnd->conn->rx_hash, cmnd, tio, offset, (u8 *) &crc);
261
 
 
262
 
        if (!cmnd->conn->read_overflow &&
263
 
            (cmnd_opcode(cmnd) != ISCSI_OP_PDU_REJECT)) {
264
 
                if (crc != cmnd->ddigest)
265
 
                        return -EIO;
266
 
        }
267
 
 
268
 
        return 0;
269
 
}
270
 
 
271
 
void digest_tx_data(struct iscsi_cmnd *cmnd)
272
 
{
273
 
        struct tio *tio = cmnd->tio;
274
 
        struct iscsi_data_out_hdr *req = (struct iscsi_data_out_hdr *)&cmnd->pdu.bhs;
275
 
 
276
 
        assert(tio);
277
 
        digest_data(&cmnd->conn->tx_hash, cmnd, tio,
278
 
                    be32_to_cpu(req->buffer_offset), (u8 *) &cmnd->ddigest);
279
 
}