/* * iSCSI digest handling. * * Copyright (C) 2004 - 2006 Xiranet Communications GmbH * * Copyright (C) 2007 - 2018 Vladislav Bolkhovitin * Copyright (C) 2007 - 2018 Western Digital Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include "iscsi_trace_flag.h" #include "iscsi.h" #include "digest.h" #include void digest_alg_available(int *val) { #if defined(CONFIG_LIBCRC32C_MODULE) || defined(CONFIG_LIBCRC32C) || \ defined(CONFIG_CRC32_MODULE) || defined(CONFIG_CRC32) int crc32c = 1; #else int crc32c = 0; #endif if ((*val & DIGEST_CRC32C) && !crc32c) { PRINT_ERROR("CRC32C digest algorithm not available in kernel"); *val &= ~DIGEST_CRC32C; } } /** * digest_init - initialize support for digest calculation. * @conn: ptr to connection to make use of digests * Returns: 0 on success, < 0 on error */ int digest_init(struct iscsi_conn *conn) { if (!(conn->hdigest_type & DIGEST_ALL)) conn->hdigest_type = DIGEST_NONE; if (!(conn->ddigest_type & DIGEST_ALL)) conn->ddigest_type = DIGEST_NONE; return 0; } static __be32 evaluate_crc32_from_sg(struct scatterlist *sg, int nbytes, uint32_t padding) { u32 crc = ~0; #ifdef CONFIG_SCST_ISCSI_DEBUG_DIGEST_FAILURES if (((scst_random() % 100000) == 752)) { PRINT_INFO("%s", "Simulating digest failure"); return 0; } #endif #if defined(CONFIG_LIBCRC32C_MODULE) || defined(CONFIG_LIBCRC32C) || \ defined(CONFIG_CRC32_MODULE) || defined(CONFIG_CRC32) { int pad_bytes = ((nbytes + 3) & -4) - nbytes; while (nbytes > 0) { int d = min(nbytes, (int)(sg->length)); crc = crc32c(crc, sg_virt(sg), d); nbytes -= d; sg++; } if (pad_bytes) crc = crc32c(crc, (u8 *)&padding, pad_bytes); } #endif return (__force __be32)~cpu_to_le32(crc); } static __be32 digest_header(struct iscsi_pdu *pdu) { struct scatterlist sg[2]; unsigned int nbytes = sizeof(struct iscsi_hdr); int asize = (pdu->ahssize + 3) & -4; sg_init_table(sg, 2); sg_set_buf(&sg[0], &pdu->bhs, nbytes); if (pdu->ahssize) { sg_set_buf(&sg[1], pdu->ahs, asize); nbytes += asize; } EXTRACHECKS_BUG_ON((nbytes & 3) != 0); return evaluate_crc32_from_sg(sg, nbytes, 0); } static __be32 digest_data(struct iscsi_cmnd *cmd, u32 size, u32 offset, uint32_t padding) { struct scatterlist *sg = cmd->sg; int idx, count; struct scatterlist saved_sg; __be32 crc; offset += sg[0].offset; idx = offset >> PAGE_SHIFT; offset &= ~PAGE_MASK; count = get_pgcnt(size, offset); TRACE_DBG("req %p, idx %d, count %d, sg_cnt %d, size %d, offset %d", cmd, idx, count, cmd->sg_cnt, size, offset); sBUG_ON(idx + count > cmd->sg_cnt); saved_sg = sg[idx]; sg[idx].offset = offset; sg[idx].length -= offset - saved_sg.offset; crc = evaluate_crc32_from_sg(sg + idx, size, padding); sg[idx] = saved_sg; return crc; } int digest_rx_header(struct iscsi_cmnd *cmnd) { __be32 crc; crc = digest_header(&cmnd->pdu); if (unlikely(crc != cmnd->hdigest)) { PRINT_ERROR("%s", "RX header digest failed"); return -EIO; } TRACE_DBG("RX header digest OK for cmd %p", cmnd); return 0; } void digest_tx_header(struct iscsi_cmnd *cmnd) { cmnd->hdigest = digest_header(&cmnd->pdu); TRACE_DBG("TX header digest for cmd %p: %x", cmnd, cmnd->hdigest); } int digest_rx_data(struct iscsi_cmnd *cmnd) { struct iscsi_cmnd *req; struct iscsi_data_out_hdr *req_hdr; u32 offset; __be32 crc; int res = 0; switch (cmnd_opcode(cmnd)) { case ISCSI_OP_SCSI_DATA_OUT: req = cmnd->cmd_req; if (unlikely(!req)) { /* It can be for prelim completed commands */ req = cmnd; goto out; } req_hdr = (struct iscsi_data_out_hdr *)&cmnd->pdu.bhs; offset = be32_to_cpu(req_hdr->buffer_offset); break; default: req = cmnd; offset = 0; } /* * We need to skip the digest check for prelim completed commands, * because we use shared data buffer for them, so, most likely, the * check will fail. Plus, for such commands we sometimes don't have * sg_cnt set correctly (cmnd_prepare_get_rejected_cmd_data() doesn't * do it). */ if (unlikely(req->prelim_compl_flags != 0)) goto out; /* * Temporary to not crash with write residual overflows. ToDo. Until * that let's always have succeeded data digests for such overflows. * In ideal, we should allocate additional one or more sg's for the * overflowed data and free them here or on req release. It's quite * not trivial for such virtually never used case, so let's do it, * when it gets needed. */ if (unlikely(offset + cmnd->pdu.datasize > req->bufflen)) { PRINT_WARNING("Skipping RX data digest check for residual overflow command op %x (data size %d, buffer size %d)", cmnd_hdr(req)->scb[0], offset + cmnd->pdu.datasize, req->bufflen); goto out; } crc = digest_data(req, cmnd->pdu.datasize, offset, cmnd->conn->rpadding); if (unlikely(crc != cmnd->ddigest)) { PRINT_ERROR("RX data digest failed, stable pages disabled?"); TRACE_MGMT_DBG("Calculated crc %x, ddigest %x, offset %d", crc, cmnd->ddigest, offset); iscsi_dump_pdu(&cmnd->pdu); res = -EIO; } else { TRACE_DBG("RX data digest OK for cmd %p", cmnd); } out: return res; } void digest_tx_data(struct iscsi_cmnd *cmnd) { struct iscsi_data_in_hdr *hdr; u32 offset; TRACE_DBG("%s:%d req %p, own_sg %d, sg %p, sgcnt %d cmnd %p, own_sg %d, sg %p, sgcnt %d", __func__, __LINE__, cmnd->parent_req, cmnd->parent_req->own_sg, cmnd->parent_req->sg, cmnd->parent_req->sg_cnt, cmnd, cmnd->own_sg, cmnd->sg, cmnd->sg_cnt); switch (cmnd_opcode(cmnd)) { case ISCSI_OP_SCSI_DATA_IN: hdr = (struct iscsi_data_in_hdr *)&cmnd->pdu.bhs; offset = be32_to_cpu(hdr->buffer_offset); break; default: offset = 0; } cmnd->ddigest = digest_data(cmnd, cmnd->pdu.datasize, offset, 0); TRACE_DBG("TX data digest for cmd %p: %x (offset %d, opcode %x)", cmnd, cmnd->ddigest, offset, cmnd_opcode(cmnd)); }