sd_dif.c 10.0 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
/*
 * sd_dif.c - SCSI Data Integrity Field
 *
 * Copyright (C) 2007, 2008 Oracle Corporation
 * Written by: Martin K. Petersen <martin.petersen@oracle.com>
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License version
 * 2 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.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; see the file COPYING.  If not, write to
 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139,
 * USA.
 *
 */

#include <linux/blkdev.h>
#include <linux/crc-t10dif.h>

#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_dbg.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_driver.h>
#include <scsi/scsi_eh.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_ioctl.h>
#include <scsi/scsicam.h>

#include <net/checksum.h>

#include "sd.h"

typedef __u16 (csum_fn) (void *, unsigned int);

static __u16 sd_dif_crc_fn(void *data, unsigned int len)
{
	return cpu_to_be16(crc_t10dif(data, len));
}

static __u16 sd_dif_ip_fn(void *data, unsigned int len)
{
	return ip_compute_csum(data, len);
}

/*
 * Type 1 and Type 2 protection use the same format: 16 bit guard tag,
 * 16 bit app tag, 32 bit reference tag.
 */
static void sd_dif_type1_generate(struct blk_integrity_exchg *bix, csum_fn *fn)
{
	void *buf = bix->data_buf;
	struct sd_dif_tuple *sdt = bix->prot_buf;
60
	sector_t seed = bix->seed;
61 62
	unsigned int i;

63 64 65
	for (i = 0 ; i < bix->data_size ; i += bix->interval, sdt++) {
		sdt->guard_tag = fn(buf, bix->interval);
		sdt->ref_tag = cpu_to_be32(seed & 0xffffffff);
66 67
		sdt->app_tag = 0;

68 69
		buf += bix->interval;
		seed++;
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
	}
}

static void sd_dif_type1_generate_crc(struct blk_integrity_exchg *bix)
{
	sd_dif_type1_generate(bix, sd_dif_crc_fn);
}

static void sd_dif_type1_generate_ip(struct blk_integrity_exchg *bix)
{
	sd_dif_type1_generate(bix, sd_dif_ip_fn);
}

static int sd_dif_type1_verify(struct blk_integrity_exchg *bix, csum_fn *fn)
{
	void *buf = bix->data_buf;
	struct sd_dif_tuple *sdt = bix->prot_buf;
87
	sector_t seed = bix->seed;
88 89 90
	unsigned int i;
	__u16 csum;

91
	for (i = 0 ; i < bix->data_size ; i += bix->interval, sdt++) {
92 93 94 95
		/* Unwritten sectors */
		if (sdt->app_tag == 0xffff)
			return 0;

96
		if (be32_to_cpu(sdt->ref_tag) != (seed & 0xffffffff)) {
97 98
			printk(KERN_ERR
			       "%s: ref tag error on sector %lu (rcvd %u)\n",
99
			       bix->disk_name, (unsigned long)seed,
100 101 102 103
			       be32_to_cpu(sdt->ref_tag));
			return -EIO;
		}

104
		csum = fn(buf, bix->interval);
105 106 107 108

		if (sdt->guard_tag != csum) {
			printk(KERN_ERR "%s: guard tag error on sector %lu " \
			       "(rcvd %04x, data %04x)\n", bix->disk_name,
109
			       (unsigned long)seed,
110 111 112 113
			       be16_to_cpu(sdt->guard_tag), be16_to_cpu(csum));
			return -EIO;
		}

114 115
		buf += bix->interval;
		seed++;
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
	}

	return 0;
}

static int sd_dif_type1_verify_crc(struct blk_integrity_exchg *bix)
{
	return sd_dif_type1_verify(bix, sd_dif_crc_fn);
}

static int sd_dif_type1_verify_ip(struct blk_integrity_exchg *bix)
{
	return sd_dif_type1_verify(bix, sd_dif_ip_fn);
}

static struct blk_integrity dif_type1_integrity_crc = {
	.name			= "T10-DIF-TYPE1-CRC",
	.generate_fn		= sd_dif_type1_generate_crc,
	.verify_fn		= sd_dif_type1_verify_crc,
	.tuple_size		= sizeof(struct sd_dif_tuple),
	.tag_size		= 0,
};

static struct blk_integrity dif_type1_integrity_ip = {
	.name			= "T10-DIF-TYPE1-IP",
	.generate_fn		= sd_dif_type1_generate_ip,
	.verify_fn		= sd_dif_type1_verify_ip,
	.tuple_size		= sizeof(struct sd_dif_tuple),
	.tag_size		= 0,
};


/*
 * Type 3 protection has a 16-bit guard tag and 16 + 32 bits of opaque
 * tag space.
 */
static void sd_dif_type3_generate(struct blk_integrity_exchg *bix, csum_fn *fn)
{
	void *buf = bix->data_buf;
	struct sd_dif_tuple *sdt = bix->prot_buf;
	unsigned int i;

158 159
	for (i = 0 ; i < bix->data_size ; i += bix->interval, sdt++) {
		sdt->guard_tag = fn(buf, bix->interval);
160 161 162
		sdt->ref_tag = 0;
		sdt->app_tag = 0;

163
		buf += bix->interval;
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
	}
}

static void sd_dif_type3_generate_crc(struct blk_integrity_exchg *bix)
{
	sd_dif_type3_generate(bix, sd_dif_crc_fn);
}

static void sd_dif_type3_generate_ip(struct blk_integrity_exchg *bix)
{
	sd_dif_type3_generate(bix, sd_dif_ip_fn);
}

static int sd_dif_type3_verify(struct blk_integrity_exchg *bix, csum_fn *fn)
{
	void *buf = bix->data_buf;
	struct sd_dif_tuple *sdt = bix->prot_buf;
181
	sector_t seed = bix->seed;
182 183 184
	unsigned int i;
	__u16 csum;

185
	for (i = 0 ; i < bix->data_size ; i += bix->interval, sdt++) {
186 187 188 189
		/* Unwritten sectors */
		if (sdt->app_tag == 0xffff && sdt->ref_tag == 0xffffffff)
			return 0;

190
		csum = fn(buf, bix->interval);
191 192 193 194

		if (sdt->guard_tag != csum) {
			printk(KERN_ERR "%s: guard tag error on sector %lu " \
			       "(rcvd %04x, data %04x)\n", bix->disk_name,
195
			       (unsigned long)seed,
196 197 198 199
			       be16_to_cpu(sdt->guard_tag), be16_to_cpu(csum));
			return -EIO;
		}

200 201
		buf += bix->interval;
		seed++;
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
	}

	return 0;
}

static int sd_dif_type3_verify_crc(struct blk_integrity_exchg *bix)
{
	return sd_dif_type3_verify(bix, sd_dif_crc_fn);
}

static int sd_dif_type3_verify_ip(struct blk_integrity_exchg *bix)
{
	return sd_dif_type3_verify(bix, sd_dif_ip_fn);
}

static struct blk_integrity dif_type3_integrity_crc = {
	.name			= "T10-DIF-TYPE3-CRC",
	.generate_fn		= sd_dif_type3_generate_crc,
	.verify_fn		= sd_dif_type3_verify_crc,
	.tuple_size		= sizeof(struct sd_dif_tuple),
	.tag_size		= 0,
};

static struct blk_integrity dif_type3_integrity_ip = {
	.name			= "T10-DIF-TYPE3-IP",
	.generate_fn		= sd_dif_type3_generate_ip,
	.verify_fn		= sd_dif_type3_verify_ip,
	.tuple_size		= sizeof(struct sd_dif_tuple),
	.tag_size		= 0,
};

/*
 * Configure exchange of protection information between OS and HBA.
 */
void sd_dif_config_host(struct scsi_disk *sdkp)
{
	struct scsi_device *sdp = sdkp->device;
	struct gendisk *disk = sdkp->disk;
	u8 type = sdkp->protection_type;
241
	int dif, dix;
242

243 244
	dif = scsi_host_dif_capable(sdp->host, type);
	dix = scsi_host_dix_capable(sdp->host, type);
245

246 247 248
	if (!dix && scsi_host_dix_capable(sdp->host, 0)) {
		dif = 0; dix = 1;
	}
249

250
	if (!dix)
251 252 253 254 255 256 257 258 259 260 261 262 263 264
		return;

	/* Enable DMA of protection information */
	if (scsi_host_get_guard(sdkp->device->host) & SHOST_DIX_GUARD_IP)
		if (type == SD_DIF_TYPE3_PROTECTION)
			blk_integrity_register(disk, &dif_type3_integrity_ip);
		else
			blk_integrity_register(disk, &dif_type1_integrity_ip);
	else
		if (type == SD_DIF_TYPE3_PROTECTION)
			blk_integrity_register(disk, &dif_type3_integrity_crc);
		else
			blk_integrity_register(disk, &dif_type1_integrity_crc);

265
	sd_printk(KERN_NOTICE, sdkp,
266
		  "Enabling DIX %s protection\n", disk->integrity->name);
267 268

	/* Signal to block layer that we support sector tagging */
269
	if (dif && type && sdkp->ATO) {
270 271 272 273 274
		if (type == SD_DIF_TYPE3_PROTECTION)
			disk->integrity->tag_size = sizeof(u16) + sizeof(u32);
		else
			disk->integrity->tag_size = sizeof(u16);

275
		sd_printk(KERN_NOTICE, sdkp, "DIF application tag size %u\n",
276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
			  disk->integrity->tag_size);
	}
}

/*
 * The virtual start sector is the one that was originally submitted
 * by the block layer.	Due to partitioning, MD/DM cloning, etc. the
 * actual physical start sector is likely to be different.  Remap
 * protection information to match the physical LBA.
 *
 * From a protocol perspective there's a slight difference between
 * Type 1 and 2.  The latter uses 32-byte CDBs exclusively, and the
 * reference tag is seeded in the CDB.  This gives us the potential to
 * avoid virt->phys remapping during write.  However, at read time we
 * don't know whether the virt sector is the same as when we wrote it
 * (we could be reading from real disk as opposed to MD/DM device.  So
 * we always remap Type 2 making it identical to Type 1.
 *
 * Type 3 does not have a reference tag so no remapping is required.
 */
296 297
void sd_dif_prepare(struct request *rq, sector_t hw_sector,
		    unsigned int sector_sz)
298 299 300 301 302 303 304 305 306 307
{
	const int tuple_sz = sizeof(struct sd_dif_tuple);
	struct bio *bio;
	struct scsi_disk *sdkp;
	struct sd_dif_tuple *sdt;
	u32 phys, virt;

	sdkp = rq->bio->bi_bdev->bd_disk->private_data;

	if (sdkp->protection_type == SD_DIF_TYPE3_PROTECTION)
308
		return;
309 310 311 312

	phys = hw_sector & 0xffffffff;

	__rq_for_each_bio(bio, rq) {
313 314 315
		struct bio_vec iv;
		struct bvec_iter iter;
		unsigned int j;
316

317 318 319 320
		/* Already remapped? */
		if (bio_flagged(bio, BIO_MAPPED_INTEGRITY))
			break;

321
		virt = bio_integrity(bio)->bip_iter.bi_sector & 0xffffffff;
322

323
		bip_for_each_vec(iv, bio_integrity(bio), iter) {
324 325
			sdt = kmap_atomic(iv.bv_page)
				+ iv.bv_offset;
326

327
			for (j = 0; j < iv.bv_len; j += tuple_sz, sdt++) {
328

329 330
				if (be32_to_cpu(sdt->ref_tag) == virt)
					sdt->ref_tag = cpu_to_be32(phys);
331 332 333 334 335

				virt++;
				phys++;
			}

336
			kunmap_atomic(sdt);
337
		}
338

339
		bio->bi_flags |= (1 << BIO_MAPPED_INTEGRITY);
340 341 342 343 344 345 346 347 348 349 350 351 352
	}
}

/*
 * Remap physical sector values in the reference tag to the virtual
 * values expected by the block layer.
 */
void sd_dif_complete(struct scsi_cmnd *scmd, unsigned int good_bytes)
{
	const int tuple_sz = sizeof(struct sd_dif_tuple);
	struct scsi_disk *sdkp;
	struct bio *bio;
	struct sd_dif_tuple *sdt;
353
	unsigned int j, sectors, sector_sz;
354 355 356 357 358 359 360 361 362 363
	u32 phys, virt;

	sdkp = scsi_disk(scmd->request->rq_disk);

	if (sdkp->protection_type == SD_DIF_TYPE3_PROTECTION || good_bytes == 0)
		return;

	sector_sz = scmd->device->sector_size;
	sectors = good_bytes / sector_sz;

364
	phys = blk_rq_pos(scmd->request) & 0xffffffff;
365 366 367 368
	if (sector_sz == 4096)
		phys >>= 3;

	__rq_for_each_bio(bio, scmd->request) {
369 370
		struct bio_vec iv;
		struct bvec_iter iter;
371

372
		virt = bio_integrity(bio)->bip_iter.bi_sector & 0xffffffff;
373

374
		bip_for_each_vec(iv, bio_integrity(bio), iter) {
375 376
			sdt = kmap_atomic(iv.bv_page)
				+ iv.bv_offset;
377

378
			for (j = 0; j < iv.bv_len; j += tuple_sz, sdt++) {
379 380

				if (sectors == 0) {
381
					kunmap_atomic(sdt);
382 383 384
					return;
				}

385
				if (be32_to_cpu(sdt->ref_tag) == phys)
386 387 388 389 390 391 392
					sdt->ref_tag = cpu_to_be32(virt);

				virt++;
				phys++;
				sectors--;
			}

393
			kunmap_atomic(sdt);
394 395 396 397
		}
	}
}