4538 lines · 118.2 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* *      sd.c Copyright (C) 1992 Drew Eckhardt *           Copyright (C) 1993, 1994, 1995, 1999 Eric Youngdale * *      Linux scsi disk driver *              Initial versions: Drew Eckhardt *              Subsequent revisions: Eric Youngdale *	Modification history: *       - Drew Eckhardt <drew@colorado.edu> original *       - Eric Youngdale <eric@andante.org> add scatter-gather, multiple  *         outstanding request, and other enhancements. *         Support loadable low-level scsi drivers. *       - Jirka Hanika <geo@ff.cuni.cz> support more scsi disks using  *         eight major numbers. *       - Richard Gooch <rgooch@atnf.csiro.au> support devfs. *	 - Torben Mathiasen <tmm@image.dk> Resource allocation fixes in  *	   sd_init and cleanups. *	 - Alex Davis <letmein@erols.com> Fix problem where partition info *	   not being read in sd_open. Fix problem where removable media  *	   could be ejected after sd_open. *	 - Douglas Gilbert <dgilbert@interlog.com> cleanup for lk 2.5.x *	 - Badari Pulavarty <pbadari@us.ibm.com>, Matthew Wilcox  *	   <willy@debian.org>, Kurt Garloff <garloff@suse.de>:  *	   Support 32k/1M disks. * *	Logging policy (needs CONFIG_SCSI_LOGGING defined): *	 - setting up transfer: SCSI_LOG_HLQUEUE levels 1 and 2 *	 - end of transfer (bh + scsi_lib): SCSI_LOG_HLCOMPLETE level 1 *	 - entering sd_ioctl: SCSI_LOG_IOCTL level 1 *	 - entering other commands: SCSI_LOG_HLQUEUE level 3 *	Note: when the logging level is set by the user, it must be greater *	than the level indicated above to trigger output.	 */ #include <linux/bio-integrity.h>#include <linux/module.h>#include <linux/fs.h>#include <linux/kernel.h>#include <linux/mm.h>#include <linux/hdreg.h>#include <linux/errno.h>#include <linux/idr.h>#include <linux/interrupt.h>#include <linux/init.h>#include <linux/blkdev.h>#include <linux/blkpg.h>#include <linux/blk-pm.h>#include <linux/delay.h>#include <linux/rw_hint.h>#include <linux/major.h>#include <linux/mutex.h>#include <linux/string_helpers.h>#include <linux/slab.h>#include <linux/sed-opal.h>#include <linux/pm_runtime.h>#include <linux/pr.h>#include <linux/t10-pi.h>#include <linux/uaccess.h>#include <linux/unaligned.h> #include <scsi/scsi.h>#include <scsi/scsi_cmnd.h>#include <scsi/scsi_dbg.h>#include <scsi/scsi_device.h>#include <scsi/scsi_devinfo.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 <scsi/scsi_common.h> #include "sd.h"#include "scsi_priv.h"#include "scsi_logging.h" MODULE_AUTHOR("Eric Youngdale");MODULE_DESCRIPTION("SCSI disk (sd) driver");MODULE_LICENSE("GPL"); MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK0_MAJOR);MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK1_MAJOR);MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK2_MAJOR);MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK3_MAJOR);MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK4_MAJOR);MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK5_MAJOR);MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK6_MAJOR);MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK7_MAJOR);MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK8_MAJOR);MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK9_MAJOR);MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK10_MAJOR);MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK11_MAJOR);MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK12_MAJOR);MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK13_MAJOR);MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK14_MAJOR);MODULE_ALIAS_BLOCKDEV_MAJOR(SCSI_DISK15_MAJOR);MODULE_ALIAS_SCSI_DEVICE(TYPE_DISK);MODULE_ALIAS_SCSI_DEVICE(TYPE_MOD);MODULE_ALIAS_SCSI_DEVICE(TYPE_RBC);MODULE_ALIAS_SCSI_DEVICE(TYPE_ZBC); #define SD_MINORS	16 static void sd_config_write_same(struct scsi_disk *sdkp,		struct queue_limits *lim);static void  sd_revalidate_disk(struct gendisk *); static DEFINE_IDA(sd_index_ida);static DEFINE_MUTEX(sd_mutex_lock); static mempool_t *sd_page_pool;static mempool_t *sd_large_page_pool;static atomic_t sd_large_page_pool_users = ATOMIC_INIT(0);static struct lock_class_key sd_bio_compl_lkclass; static const char *sd_cache_types[] = {	"write through", "none", "write back",	"write back, no read (daft)"}; static int sd_large_pool_create(void){	mutex_lock(&sd_mutex_lock);	if (!sd_large_page_pool) {		sd_large_page_pool = mempool_create_page_pool(			SD_MEMPOOL_SIZE, get_order(BLK_MAX_BLOCK_SIZE));		if (!sd_large_page_pool) {			printk(KERN_ERR "sd: can't create large page mempool\n");			mutex_unlock(&sd_mutex_lock);			return -ENOMEM;		}	}	atomic_inc(&sd_large_page_pool_users);	mutex_unlock(&sd_mutex_lock);	return 0;} static void sd_large_pool_destroy(void){	mutex_lock(&sd_mutex_lock);	if (atomic_dec_and_test(&sd_large_page_pool_users)) {		mempool_destroy(sd_large_page_pool);		sd_large_page_pool = NULL;	}	mutex_unlock(&sd_mutex_lock);} static void sd_disable_discard(struct scsi_disk *sdkp){	sdkp->provisioning_mode = SD_LBP_DISABLE;	blk_queue_disable_discard(sdkp->disk->queue);} static void sd_config_discard(struct scsi_disk *sdkp, struct queue_limits *lim,		unsigned int mode){	unsigned int logical_block_size = sdkp->device->sector_size;	unsigned int max_blocks = 0; 	lim->discard_alignment = sdkp->unmap_alignment * logical_block_size;	lim->discard_granularity = max(sdkp->physical_block_size,			sdkp->unmap_granularity * logical_block_size);	sdkp->provisioning_mode = mode; 	switch (mode) { 	case SD_LBP_FULL:	case SD_LBP_DISABLE:		break; 	case SD_LBP_UNMAP:		max_blocks = min_not_zero(sdkp->max_unmap_blocks,					  (u32)SD_MAX_WS16_BLOCKS);		break; 	case SD_LBP_WS16:		if (sdkp->device->unmap_limit_for_ws)			max_blocks = sdkp->max_unmap_blocks;		else			max_blocks = sdkp->max_ws_blocks; 		max_blocks = min_not_zero(max_blocks, (u32)SD_MAX_WS16_BLOCKS);		break; 	case SD_LBP_WS10:		if (sdkp->device->unmap_limit_for_ws)			max_blocks = sdkp->max_unmap_blocks;		else			max_blocks = sdkp->max_ws_blocks; 		max_blocks = min_not_zero(max_blocks, (u32)SD_MAX_WS10_BLOCKS);		break; 	case SD_LBP_ZERO:		max_blocks = min_not_zero(sdkp->max_ws_blocks,					  (u32)SD_MAX_WS10_BLOCKS);		break;	} 	lim->max_hw_discard_sectors = max_blocks *		(logical_block_size >> SECTOR_SHIFT);} static void sd_set_flush_flag(struct scsi_disk *sdkp,		struct queue_limits *lim){	if (sdkp->WCE) {		lim->features |= BLK_FEAT_WRITE_CACHE;		if (sdkp->DPOFUA)			lim->features |= BLK_FEAT_FUA;		else			lim->features &= ~BLK_FEAT_FUA;	} else {		lim->features &= ~(BLK_FEAT_WRITE_CACHE | BLK_FEAT_FUA);	}} static ssize_tcache_type_store(struct device *dev, struct device_attribute *attr,		 const char *buf, size_t count){	int ct, rcd, wce, sp;	struct scsi_disk *sdkp = to_scsi_disk(dev);	struct scsi_device *sdp = sdkp->device;	char buffer[64];	char *buffer_data;	struct scsi_mode_data data;	struct scsi_sense_hdr sshdr;	static const char temp[] = "temporary ";	int len, ret; 	if (sdp->type != TYPE_DISK && sdp->type != TYPE_ZBC)		/* no cache control on RBC devices; theoretically they		 * can do it, but there's probably so many exceptions		 * it's not worth the risk */		return -EINVAL; 	if (strncmp(buf, temp, sizeof(temp) - 1) == 0) {		buf += sizeof(temp) - 1;		sdkp->cache_override = 1;	} else {		sdkp->cache_override = 0;	} 	ct = sysfs_match_string(sd_cache_types, buf);	if (ct < 0)		return -EINVAL; 	rcd = ct & 0x01 ? 1 : 0;	wce = (ct & 0x02) && !sdkp->write_prot ? 1 : 0; 	if (sdkp->cache_override) {		struct queue_limits lim; 		sdkp->WCE = wce;		sdkp->RCD = rcd; 		lim = queue_limits_start_update(sdkp->disk->queue);		sd_set_flush_flag(sdkp, &lim);		ret = queue_limits_commit_update_frozen(sdkp->disk->queue,				&lim);		if (ret)			return ret;		return count;	} 	if (scsi_mode_sense(sdp, 0x08, 8, 0, buffer, sizeof(buffer), SD_TIMEOUT,			    sdkp->max_retries, &data, NULL))		return -EINVAL;	len = min_t(size_t, sizeof(buffer), data.length - data.header_length -		  data.block_descriptor_length);	buffer_data = buffer + data.header_length +		data.block_descriptor_length;	buffer_data[2] &= ~0x05;	buffer_data[2] |= wce << 2 | rcd;	sp = buffer_data[0] & 0x80 ? 1 : 0;	buffer_data[0] &= ~0x80; 	/*	 * Ensure WP, DPOFUA, and RESERVED fields are cleared in	 * received mode parameter buffer before doing MODE SELECT.	 */	data.device_specific = 0; 	ret = scsi_mode_select(sdp, 1, sp, buffer_data, len, SD_TIMEOUT,			       sdkp->max_retries, &data, &sshdr);	if (ret) {		if (ret > 0 && scsi_sense_valid(&sshdr))			sd_print_sense_hdr(sdkp, &sshdr);		return -EINVAL;	}	sd_revalidate_disk(sdkp->disk);	return count;} static ssize_tmanage_start_stop_show(struct device *dev,		       struct device_attribute *attr, char *buf){	struct scsi_disk *sdkp = to_scsi_disk(dev);	struct scsi_device *sdp = sdkp->device; 	return sysfs_emit(buf, "%u\n",			  sdp->manage_system_start_stop &&			  sdp->manage_runtime_start_stop &&			  sdp->manage_shutdown);}static DEVICE_ATTR_RO(manage_start_stop); static ssize_tmanage_system_start_stop_show(struct device *dev,			      struct device_attribute *attr, char *buf){	struct scsi_disk *sdkp = to_scsi_disk(dev);	struct scsi_device *sdp = sdkp->device; 	return sysfs_emit(buf, "%u\n", sdp->manage_system_start_stop);} static ssize_tmanage_system_start_stop_store(struct device *dev,			       struct device_attribute *attr,			       const char *buf, size_t count){	struct scsi_disk *sdkp = to_scsi_disk(dev);	struct scsi_device *sdp = sdkp->device;	bool v; 	if (!capable(CAP_SYS_ADMIN))		return -EACCES; 	if (kstrtobool(buf, &v))		return -EINVAL; 	sdp->manage_system_start_stop = v; 	return count;}static DEVICE_ATTR_RW(manage_system_start_stop); static ssize_tmanage_runtime_start_stop_show(struct device *dev,			       struct device_attribute *attr, char *buf){	struct scsi_disk *sdkp = to_scsi_disk(dev);	struct scsi_device *sdp = sdkp->device; 	return sysfs_emit(buf, "%u\n", sdp->manage_runtime_start_stop);} static ssize_tmanage_runtime_start_stop_store(struct device *dev,				struct device_attribute *attr,				const char *buf, size_t count){	struct scsi_disk *sdkp = to_scsi_disk(dev);	struct scsi_device *sdp = sdkp->device;	bool v; 	if (!capable(CAP_SYS_ADMIN))		return -EACCES; 	if (kstrtobool(buf, &v))		return -EINVAL; 	sdp->manage_runtime_start_stop = v; 	return count;}static DEVICE_ATTR_RW(manage_runtime_start_stop); static ssize_t manage_shutdown_show(struct device *dev,				    struct device_attribute *attr, char *buf){	struct scsi_disk *sdkp = to_scsi_disk(dev);	struct scsi_device *sdp = sdkp->device; 	return sysfs_emit(buf, "%u\n", sdp->manage_shutdown);} static ssize_t manage_shutdown_store(struct device *dev,				     struct device_attribute *attr,				     const char *buf, size_t count){	struct scsi_disk *sdkp = to_scsi_disk(dev);	struct scsi_device *sdp = sdkp->device;	bool v; 	if (!capable(CAP_SYS_ADMIN))		return -EACCES; 	if (kstrtobool(buf, &v))		return -EINVAL; 	sdp->manage_shutdown = v; 	return count;}static DEVICE_ATTR_RW(manage_shutdown); static ssize_t manage_restart_show(struct device *dev,				   struct device_attribute *attr, char *buf){	struct scsi_disk *sdkp = to_scsi_disk(dev);	struct scsi_device *sdp = sdkp->device; 	return sysfs_emit(buf, "%u\n", sdp->manage_restart);} static ssize_t manage_restart_store(struct device *dev,				    struct device_attribute *attr,				    const char *buf, size_t count){	struct scsi_disk *sdkp = to_scsi_disk(dev);	struct scsi_device *sdp = sdkp->device;	bool v; 	if (!capable(CAP_SYS_ADMIN))		return -EACCES; 	if (kstrtobool(buf, &v))		return -EINVAL; 	sdp->manage_restart = v; 	return count;}static DEVICE_ATTR_RW(manage_restart); static ssize_tallow_restart_show(struct device *dev, struct device_attribute *attr, char *buf){	struct scsi_disk *sdkp = to_scsi_disk(dev); 	return sprintf(buf, "%u\n", sdkp->device->allow_restart);} static ssize_tallow_restart_store(struct device *dev, struct device_attribute *attr,		    const char *buf, size_t count){	bool v;	struct scsi_disk *sdkp = to_scsi_disk(dev);	struct scsi_device *sdp = sdkp->device; 	if (!capable(CAP_SYS_ADMIN))		return -EACCES; 	if (sdp->type != TYPE_DISK && sdp->type != TYPE_ZBC)		return -EINVAL; 	if (kstrtobool(buf, &v))		return -EINVAL; 	sdp->allow_restart = v; 	return count;}static DEVICE_ATTR_RW(allow_restart); static ssize_tcache_type_show(struct device *dev, struct device_attribute *attr, char *buf){	struct scsi_disk *sdkp = to_scsi_disk(dev);	int ct = sdkp->RCD + 2*sdkp->WCE; 	return sprintf(buf, "%s\n", sd_cache_types[ct]);}static DEVICE_ATTR_RW(cache_type); static ssize_tFUA_show(struct device *dev, struct device_attribute *attr, char *buf){	struct scsi_disk *sdkp = to_scsi_disk(dev); 	return sprintf(buf, "%u\n", sdkp->DPOFUA);}static DEVICE_ATTR_RO(FUA); static ssize_tprotection_type_show(struct device *dev, struct device_attribute *attr,		     char *buf){	struct scsi_disk *sdkp = to_scsi_disk(dev); 	return sprintf(buf, "%u\n", sdkp->protection_type);} static ssize_tprotection_type_store(struct device *dev, struct device_attribute *attr,		      const char *buf, size_t count){	struct scsi_disk *sdkp = to_scsi_disk(dev);	unsigned int val;	int err; 	if (!capable(CAP_SYS_ADMIN))		return -EACCES; 

Lines 1–500 of 4538.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t