18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * sd_dif.c - SCSI Data Integrity Field
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2007, 2008 Oracle Corporation
68c2ecf20Sopenharmony_ci * Written by: Martin K. Petersen <martin.petersen@oracle.com>
78c2ecf20Sopenharmony_ci */
88c2ecf20Sopenharmony_ci
98c2ecf20Sopenharmony_ci#include <linux/blkdev.h>
108c2ecf20Sopenharmony_ci#include <linux/t10-pi.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <scsi/scsi.h>
138c2ecf20Sopenharmony_ci#include <scsi/scsi_cmnd.h>
148c2ecf20Sopenharmony_ci#include <scsi/scsi_dbg.h>
158c2ecf20Sopenharmony_ci#include <scsi/scsi_device.h>
168c2ecf20Sopenharmony_ci#include <scsi/scsi_driver.h>
178c2ecf20Sopenharmony_ci#include <scsi/scsi_eh.h>
188c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h>
198c2ecf20Sopenharmony_ci#include <scsi/scsi_ioctl.h>
208c2ecf20Sopenharmony_ci#include <scsi/scsicam.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include "sd.h"
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci/*
258c2ecf20Sopenharmony_ci * Configure exchange of protection information between OS and HBA.
268c2ecf20Sopenharmony_ci */
278c2ecf20Sopenharmony_civoid sd_dif_config_host(struct scsi_disk *sdkp)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	struct scsi_device *sdp = sdkp->device;
308c2ecf20Sopenharmony_ci	struct gendisk *disk = sdkp->disk;
318c2ecf20Sopenharmony_ci	u8 type = sdkp->protection_type;
328c2ecf20Sopenharmony_ci	struct blk_integrity bi;
338c2ecf20Sopenharmony_ci	int dif, dix;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	dif = scsi_host_dif_capable(sdp->host, type);
368c2ecf20Sopenharmony_ci	dix = scsi_host_dix_capable(sdp->host, type);
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	if (!dix && scsi_host_dix_capable(sdp->host, 0)) {
398c2ecf20Sopenharmony_ci		dif = 0; dix = 1;
408c2ecf20Sopenharmony_ci	}
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	if (!dix)
438c2ecf20Sopenharmony_ci		return;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	memset(&bi, 0, sizeof(bi));
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	/* Enable DMA of protection information */
488c2ecf20Sopenharmony_ci	if (scsi_host_get_guard(sdkp->device->host) & SHOST_DIX_GUARD_IP) {
498c2ecf20Sopenharmony_ci		if (type == T10_PI_TYPE3_PROTECTION)
508c2ecf20Sopenharmony_ci			bi.profile = &t10_pi_type3_ip;
518c2ecf20Sopenharmony_ci		else
528c2ecf20Sopenharmony_ci			bi.profile = &t10_pi_type1_ip;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci		bi.flags |= BLK_INTEGRITY_IP_CHECKSUM;
558c2ecf20Sopenharmony_ci	} else
568c2ecf20Sopenharmony_ci		if (type == T10_PI_TYPE3_PROTECTION)
578c2ecf20Sopenharmony_ci			bi.profile = &t10_pi_type3_crc;
588c2ecf20Sopenharmony_ci		else
598c2ecf20Sopenharmony_ci			bi.profile = &t10_pi_type1_crc;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	bi.tuple_size = sizeof(struct t10_pi_tuple);
628c2ecf20Sopenharmony_ci	sd_printk(KERN_NOTICE, sdkp,
638c2ecf20Sopenharmony_ci		  "Enabling DIX %s protection\n", bi.profile->name);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	if (dif && type) {
668c2ecf20Sopenharmony_ci		bi.flags |= BLK_INTEGRITY_DEVICE_CAPABLE;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci		if (!sdkp->ATO)
698c2ecf20Sopenharmony_ci			goto out;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci		if (type == T10_PI_TYPE3_PROTECTION)
728c2ecf20Sopenharmony_ci			bi.tag_size = sizeof(u16) + sizeof(u32);
738c2ecf20Sopenharmony_ci		else
748c2ecf20Sopenharmony_ci			bi.tag_size = sizeof(u16);
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci		sd_printk(KERN_NOTICE, sdkp, "DIF application tag size %u\n",
778c2ecf20Sopenharmony_ci			  bi.tag_size);
788c2ecf20Sopenharmony_ci	}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ciout:
818c2ecf20Sopenharmony_ci	blk_integrity_register(disk, &bi);
828c2ecf20Sopenharmony_ci}
838c2ecf20Sopenharmony_ci
84