linux/block/t10-pi.c
<<
>>
Prefs
   1/*
   2 * t10_pi.c - Functions for generating and verifying T10 Protection
   3 *            Information.
   4 *
   5 * Copyright (C) 2007, 2008, 2014 Oracle Corporation
   6 * Written by: Martin K. Petersen <martin.petersen@oracle.com>
   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 version
  10 * 2 as published by the Free Software Foundation.
  11 *
  12 * This program is distributed in the hope that it will be useful, but
  13 * WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15 * General Public License for more details.
  16 *
  17 * You should have received a copy of the GNU General Public License
  18 * along with this program; see the file COPYING.  If not, write to
  19 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
  20 * USA.
  21 *
  22 */
  23
  24#include <linux/t10-pi.h>
  25#include <linux/blkdev.h>
  26#include <linux/crc-t10dif.h>
  27#include <net/checksum.h>
  28
  29typedef __be16 (csum_fn) (void *, unsigned int);
  30
  31static __be16 t10_pi_crc_fn(void *data, unsigned int len)
  32{
  33        return cpu_to_be16(crc_t10dif(data, len));
  34}
  35
  36static __be16 t10_pi_ip_fn(void *data, unsigned int len)
  37{
  38        return (__force __be16)ip_compute_csum(data, len);
  39}
  40
  41/*
  42 * Type 1 and Type 2 protection use the same format: 16 bit guard tag,
  43 * 16 bit app tag, 32 bit reference tag. Type 3 does not define the ref
  44 * tag.
  45 */
  46static blk_status_t t10_pi_generate(struct blk_integrity_iter *iter,
  47                csum_fn *fn, enum t10_dif_type type)
  48{
  49        unsigned int i;
  50
  51        for (i = 0 ; i < iter->data_size ; i += iter->interval) {
  52                struct t10_pi_tuple *pi = iter->prot_buf;
  53
  54                pi->guard_tag = fn(iter->data_buf, iter->interval);
  55                pi->app_tag = 0;
  56
  57                if (type == T10_PI_TYPE1_PROTECTION)
  58                        pi->ref_tag = cpu_to_be32(lower_32_bits(iter->seed));
  59                else
  60                        pi->ref_tag = 0;
  61
  62                iter->data_buf += iter->interval;
  63                iter->prot_buf += sizeof(struct t10_pi_tuple);
  64                iter->seed++;
  65        }
  66
  67        return BLK_STS_OK;
  68}
  69
  70static blk_status_t t10_pi_verify(struct blk_integrity_iter *iter,
  71                csum_fn *fn, enum t10_dif_type type)
  72{
  73        unsigned int i;
  74
  75        BUG_ON(type == T10_PI_TYPE0_PROTECTION);
  76
  77        for (i = 0 ; i < iter->data_size ; i += iter->interval) {
  78                struct t10_pi_tuple *pi = iter->prot_buf;
  79                __be16 csum;
  80
  81                if (type == T10_PI_TYPE1_PROTECTION ||
  82                    type == T10_PI_TYPE2_PROTECTION) {
  83                        if (pi->app_tag == T10_PI_APP_ESCAPE)
  84                                goto next;
  85
  86                        if (be32_to_cpu(pi->ref_tag) !=
  87                            lower_32_bits(iter->seed)) {
  88                                pr_err("%s: ref tag error at location %llu " \
  89                                       "(rcvd %u)\n", iter->disk_name,
  90                                       (unsigned long long)
  91                                       iter->seed, be32_to_cpu(pi->ref_tag));
  92                                return BLK_STS_PROTECTION;
  93                        }
  94                } else if (type == T10_PI_TYPE3_PROTECTION) {
  95                        if (pi->app_tag == T10_PI_APP_ESCAPE &&
  96                            pi->ref_tag == T10_PI_REF_ESCAPE)
  97                                goto next;
  98                }
  99
 100                csum = fn(iter->data_buf, iter->interval);
 101
 102                if (pi->guard_tag != csum) {
 103                        pr_err("%s: guard tag error at sector %llu " \
 104                               "(rcvd %04x, want %04x)\n", iter->disk_name,
 105                               (unsigned long long)iter->seed,
 106                               be16_to_cpu(pi->guard_tag), be16_to_cpu(csum));
 107                        return BLK_STS_PROTECTION;
 108                }
 109
 110next:
 111                iter->data_buf += iter->interval;
 112                iter->prot_buf += sizeof(struct t10_pi_tuple);
 113                iter->seed++;
 114        }
 115
 116        return BLK_STS_OK;
 117}
 118
 119static blk_status_t t10_pi_type1_generate_crc(struct blk_integrity_iter *iter)
 120{
 121        return t10_pi_generate(iter, t10_pi_crc_fn, T10_PI_TYPE1_PROTECTION);
 122}
 123
 124static blk_status_t t10_pi_type1_generate_ip(struct blk_integrity_iter *iter)
 125{
 126        return t10_pi_generate(iter, t10_pi_ip_fn, T10_PI_TYPE1_PROTECTION);
 127}
 128
 129static blk_status_t t10_pi_type1_verify_crc(struct blk_integrity_iter *iter)
 130{
 131        return t10_pi_verify(iter, t10_pi_crc_fn, T10_PI_TYPE1_PROTECTION);
 132}
 133
 134static blk_status_t t10_pi_type1_verify_ip(struct blk_integrity_iter *iter)
 135{
 136        return t10_pi_verify(iter, t10_pi_ip_fn, T10_PI_TYPE1_PROTECTION);
 137}
 138
 139/**
 140 * t10_pi_type1_prepare - prepare PI prior submitting request to device
 141 * @rq:              request with PI that should be prepared
 142 *
 143 * For Type 1/Type 2, the virtual start sector is the one that was
 144 * originally submitted by the block layer for the ref_tag usage. Due to
 145 * partitioning, MD/DM cloning, etc. the actual physical start sector is
 146 * likely to be different. Remap protection information to match the
 147 * physical LBA.
 148 */
 149static void t10_pi_type1_prepare(struct request *rq)
 150{
 151        const int tuple_sz = rq->q->integrity.tuple_size;
 152        u32 ref_tag = t10_pi_ref_tag(rq);
 153        struct bio *bio;
 154
 155        __rq_for_each_bio(bio, rq) {
 156                struct bio_integrity_payload *bip = bio_integrity(bio);
 157                u32 virt = bip_get_seed(bip) & 0xffffffff;
 158                struct bio_vec iv;
 159                struct bvec_iter iter;
 160
 161                /* Already remapped? */
 162                if (bip->bip_flags & BIP_MAPPED_INTEGRITY)
 163                        break;
 164
 165                bip_for_each_vec(iv, bip, iter) {
 166                        void *p, *pmap;
 167                        unsigned int j;
 168
 169                        pmap = kmap_atomic(iv.bv_page);
 170                        p = pmap + iv.bv_offset;
 171                        for (j = 0; j < iv.bv_len; j += tuple_sz) {
 172                                struct t10_pi_tuple *pi = p;
 173
 174                                if (be32_to_cpu(pi->ref_tag) == virt)
 175                                        pi->ref_tag = cpu_to_be32(ref_tag);
 176                                virt++;
 177                                ref_tag++;
 178                                p += tuple_sz;
 179                        }
 180
 181                        kunmap_atomic(pmap);
 182                }
 183
 184                bip->bip_flags |= BIP_MAPPED_INTEGRITY;
 185        }
 186}
 187
 188/**
 189 * t10_pi_type1_complete - prepare PI prior returning request to the blk layer
 190 * @rq:              request with PI that should be prepared
 191 * @nr_bytes:        total bytes to prepare
 192 *
 193 * For Type 1/Type 2, the virtual start sector is the one that was
 194 * originally submitted by the block layer for the ref_tag usage. Due to
 195 * partitioning, MD/DM cloning, etc. the actual physical start sector is
 196 * likely to be different. Since the physical start sector was submitted
 197 * to the device, we should remap it back to virtual values expected by the
 198 * block layer.
 199 */
 200static void t10_pi_type1_complete(struct request *rq, unsigned int nr_bytes)
 201{
 202        unsigned intervals = nr_bytes >> rq->q->integrity.interval_exp;
 203        const int tuple_sz = rq->q->integrity.tuple_size;
 204        u32 ref_tag = t10_pi_ref_tag(rq);
 205        struct bio *bio;
 206
 207        __rq_for_each_bio(bio, rq) {
 208                struct bio_integrity_payload *bip = bio_integrity(bio);
 209                u32 virt = bip_get_seed(bip) & 0xffffffff;
 210                struct bio_vec iv;
 211                struct bvec_iter iter;
 212
 213                bip_for_each_vec(iv, bip, iter) {
 214                        void *p, *pmap;
 215                        unsigned int j;
 216
 217                        pmap = kmap_atomic(iv.bv_page);
 218                        p = pmap + iv.bv_offset;
 219                        for (j = 0; j < iv.bv_len && intervals; j += tuple_sz) {
 220                                struct t10_pi_tuple *pi = p;
 221
 222                                if (be32_to_cpu(pi->ref_tag) == ref_tag)
 223                                        pi->ref_tag = cpu_to_be32(virt);
 224                                virt++;
 225                                ref_tag++;
 226                                intervals--;
 227                                p += tuple_sz;
 228                        }
 229
 230                        kunmap_atomic(pmap);
 231                }
 232        }
 233}
 234
 235static blk_status_t t10_pi_type3_generate_crc(struct blk_integrity_iter *iter)
 236{
 237        return t10_pi_generate(iter, t10_pi_crc_fn, T10_PI_TYPE3_PROTECTION);
 238}
 239
 240static blk_status_t t10_pi_type3_generate_ip(struct blk_integrity_iter *iter)
 241{
 242        return t10_pi_generate(iter, t10_pi_ip_fn, T10_PI_TYPE3_PROTECTION);
 243}
 244
 245static blk_status_t t10_pi_type3_verify_crc(struct blk_integrity_iter *iter)
 246{
 247        return t10_pi_verify(iter, t10_pi_crc_fn, T10_PI_TYPE3_PROTECTION);
 248}
 249
 250static blk_status_t t10_pi_type3_verify_ip(struct blk_integrity_iter *iter)
 251{
 252        return t10_pi_verify(iter, t10_pi_ip_fn, T10_PI_TYPE3_PROTECTION);
 253}
 254
 255/* Type 3 does not have a reference tag so no remapping is required. */
 256static void t10_pi_type3_prepare(struct request *rq)
 257{
 258}
 259
 260/* Type 3 does not have a reference tag so no remapping is required. */
 261static void t10_pi_type3_complete(struct request *rq, unsigned int nr_bytes)
 262{
 263}
 264
 265static const struct blk_integrity_profile_ext_ops t10_pi_type1_ops = {
 266        .prepare_fn             = t10_pi_type1_prepare,
 267        .complete_fn            = t10_pi_type1_complete,
 268};
 269
 270const struct blk_integrity_profile t10_pi_type1_crc = {
 271        .name                   = "T10-DIF-TYPE1-CRC",
 272        .generate_fn            = t10_pi_type1_generate_crc,
 273        .verify_fn              = t10_pi_type1_verify_crc,
 274        .ext_ops                = &t10_pi_type1_ops,
 275};
 276EXPORT_SYMBOL(t10_pi_type1_crc);
 277
 278const struct blk_integrity_profile t10_pi_type1_ip = {
 279        .name                   = "T10-DIF-TYPE1-IP",
 280        .generate_fn            = t10_pi_type1_generate_ip,
 281        .verify_fn              = t10_pi_type1_verify_ip,
 282        .ext_ops                = &t10_pi_type1_ops,
 283};
 284EXPORT_SYMBOL(t10_pi_type1_ip);
 285
 286static const struct blk_integrity_profile_ext_ops t10_pi_type3_ops = {
 287        .prepare_fn             = t10_pi_type3_prepare,
 288        .complete_fn            = t10_pi_type3_complete,
 289};
 290
 291const struct blk_integrity_profile t10_pi_type3_crc = {
 292        .name                   = "T10-DIF-TYPE3-CRC",
 293        .generate_fn            = t10_pi_type3_generate_crc,
 294        .verify_fn              = t10_pi_type3_verify_crc,
 295        .ext_ops                = &t10_pi_type3_ops,
 296};
 297EXPORT_SYMBOL(t10_pi_type3_crc);
 298
 299const struct blk_integrity_profile t10_pi_type3_ip = {
 300        .name                   = "T10-DIF-TYPE3-IP",
 301        .generate_fn            = t10_pi_type3_generate_ip,
 302        .verify_fn              = t10_pi_type3_verify_ip,
 303        .ext_ops                = &t10_pi_type3_ops,
 304};
 305EXPORT_SYMBOL(t10_pi_type3_ip);
 306