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