common define


#ifdef CONFIG_SCSI_PROC_FS
#include <linux/proc_fs.h>
static char *sg_version_date = "20140603";

static int sg_proc_init(void);
#endif

#define SG_ALLOW_DIO_DEF 0

#define SG_MAX_DEVS 32768

/* SG_MAX_CDB_SIZE should be 260 (spc4r37 section 3.1.30) however the type
 * of sg_io_hdr::cmd_len can only represent 255. All SCSI commands greater
 * than 16 bytes are "variable length" whose length is a multiple of 4
 */
#define SG_MAX_CDB_SIZE 252

#define SG_DEFAULT_TIMEOUT mult_frac(SG_DEFAULT_TIMEOUT_USER, HZ, USER_HZ)

static int sg_big_buff = SG_DEF_RESERVED_SIZE;
/* N.B. This variable is readable and writeable via
   /proc/scsi/sg/def_reserved_size . Each time sg_open() is called a buffer
   of this size (or less if there is not enough memory) will be reserved
   for use by this file descriptor. [Deprecated usage: this variable is also
   readable via /proc/sys/kernel/sg-big-buff if the sg driver is built into
   the kernel (i.e. it is not a module).] */
static int def_reserved_size = -1;	/* picks up init parameter */
static int sg_allow_dio = SG_ALLOW_DIO_DEF;

static int scatter_elem_sz = SG_SCATTER_SZ;
static int scatter_elem_sz_prev = SG_SCATTER_SZ;

#define SG_SECTOR_SZ 512

这是一些 sg.c 文件的片段,其中包含了一些宏定义和变量初始化。以下是每个片段的简要说明:

  1. #ifdef CONFIG_SCSI_PROC_FS:这是一个条件编译指令,用于检查是否启用了 SCSI proc 文件系统支持。如果启用了,将包含 linux/proc_fs.h 头文件,并定义一个名为 sg_version_date 的字符串变量。

  2. #define SG_ALLOW_DIO_DEF 0:定义了一个宏,用于设置默认的 SG_ALLOW_DIO 的值,这在后面的代码中可能会使用。

  3. #define SG_MAX_DEVS 32768:定义了一个宏,表示最大的设备数量。

  4. #define SG_MAX_CDB_SIZE 252:定义了一个宏,表示 SCSI 命令的最大长度(以字节为单位)。

  5. #define SG_DEFAULT_TIMEOUT mult_frac(SG_DEFAULT_TIMEOUT_USER, HZ, USER_HZ):定义了一个宏,用于设置默认的超时时间。

  6. static int sg_big_buff = SG_DEF_RESERVED_SIZE;:定义了一个静态整数变量 sg_big_buff,并将其初始化为默认的保留缓冲区大小。

  7. static int def_reserved_size = -1;:定义了一个静态整数变量 def_reserved_size,并将其初始化为 -1。

  8. static int sg_allow_dio = SG_ALLOW_DIO_DEF;:定义了一个静态整数变量 sg_allow_dio,并将其初始化为默认的 SG_ALLOW_DIO 值。

  9. static int scatter_elem_sz = SG_SCATTER_SZ;:定义了一个静态整数变量 scatter_elem_sz,并将其初始化为默认的 scatter-gather 元素大小。

  10. static int scatter_elem_sz_prev = SG_SCATTER_SZ;:定义了一个静态整数变量 scatter_elem_sz_prev,并将其初始化为默认的 scatter-gather 元素大小。

  11. #define SG_SECTOR_SZ 512:定义了一个宏,表示扇区的大小。

sg_interface


static int sg_add_device(struct device *);
static void sg_remove_device(struct device *);

static DEFINE_IDR(sg_index_idr);
static DEFINE_RWLOCK(sg_index_lock);	/* Also used to lock
							   file descriptor list for device */

static struct class_interface sg_interface = {
	.add_dev        = sg_add_device,
	.remove_dev     = sg_remove_device,
};

这段代码是针对 sg 驱动的一些设备管理和接口定义的片段,以下是对其中的各部分的简要解释:

  1. static int sg_add_device(struct device *);:这是一个函数声明,用于添加设备到 sg 驱动中。

  2. static void sg_remove_device(struct device *);:这是一个函数声明,用于从 sg 驱动中移除设备。

  3. static DEFINE_IDR(sg_index_idr);:这是一个宏,用于定义一个 IDR(ID Register),用于管理设备的索引。

  4. static DEFINE_RWLOCK(sg_index_lock);:这是一个宏,用于定义一个读写自旋锁,用于保护设备的索引和文件描述符列表。

  5. static struct class_interface sg_interface = { ... };:这是一个结构体定义,用于定义一个 sg 驱动的类接口。其中包含了两个函数指针,分别是在添加设备和移除设备时要调用的函数。

Sg_scatter_hold


typedef struct sg_scatter_hold { /* holding area for scsi scatter gather info */
	unsigned short k_use_sg; /* Count of kernel scatter-gather pieces */
	unsigned sglist_len; /* size of malloc'd scatter-gather list ++ */
	unsigned bufflen;	/* Size of (aggregate) data buffer */
	struct page **pages;
	int page_order;
	char dio_in_use;	/* 0->indirect IO (or mmap), 1->dio */
	unsigned char cmd_opcode; /* first byte of command */
} Sg_scatter_hold;

这段代码定义了一个结构体 Sg_scatter_hold,用于在 SCSI 命令执行过程中保存散列-收集(scatter-gather)信息的临时缓冲区。以下是各字段的简要解释:

  1. unsigned short k_use_sg:用于计算在内核中的散列-收集分段的数量。

  2. unsigned sglist_len:malloc 分配的散列-收集列表的大小。

  3. unsigned bufflen:数据缓冲区的总大小(聚合的数据大小)。

  4. struct page **pages:指向内存页面数组的指针,这些页面用于存储数据块。

  5. int page_order:内存页面的阶数,用于计算分配的页面大小。

  6. char dio_in_use:指示是否正在使用直接 I/O(Direct I/O)。

  7. unsigned char cmd_opcode:命令的第一个字节,用于确定执行的 SCSI 命令。

这个结构体主要用于在进行 SCSI 命令执行时管理数据传输、内存页面和相关信息。它提供了一种在执行期间存储和跟踪数据传输细节的方式。

Sg_request

struct sg_device;		/* forward declarations */
struct sg_fd;

typedef struct sg_request {	/* SG_MAX_QUEUE requests outstanding per file */
	struct list_head entry;	/* list entry */
	struct sg_fd *parentfp;	/* NULL -> not in use */
	Sg_scatter_hold data;	/* hold buffer, perhaps scatter list */
	sg_io_hdr_t header;	/* scsi command+info, see <scsi/sg.h> */
	unsigned char sense_b[SCSI_SENSE_BUFFERSIZE];
	char res_used;		/* 1 -> using reserve buffer, 0 -> not ... */
	char orphan;		/* 1 -> drop on sight, 0 -> normal */
	char sg_io_owned;	/* 1 -> packet belongs to SG_IO */
	/* done protected by rq_list_lock */
	char done;		/* 0->before bh, 1->before read, 2->read */
	struct request *rq;
	struct bio *bio;
	struct execute_work ew;
} Sg_request;

这段代码定义了一个名为 Sg_request 的结构体,用于管理每个文件的 SCSI 请求(SG 请求)。以下是各字段的简要解释:

  1. struct list_head entry:链表条目,用于将请求链接到请求队列中。

  2. struct sg_fd *parentfp:指向相关联文件描述符的指针。

  3. Sg_scatter_hold data:用于保持缓冲区和散列-收集信息的结构体,这将在请求执行期间用于数据传输。

  4. sg_io_hdr_t header:SG 请求中的 SCSI 命令和信息,定义在 <scsi/sg.h> 中。

  5. unsigned char sense_b[SCSI_SENSE_BUFFERSIZE]:用于存储 SCSI 设备感知缓冲区的数组。

  6. char res_used:表示是否使用了保留缓冲区(Reserve Buffer)。

  7. char orphan:标志,指示是否丢弃此请求。

  8. char sg_io_owned:标志,表示该请求是否属于 SG_IO。

  9. char done:表示请求的状态,0 表示在提交请求之前,1 表示在提交读取请求之前,2 表示读取完成。

  10. struct request *rq:与请求相关联的 Linux 内核请求结构。

  11. struct bio *bio:与请求相关联的 Linux 内核生物(BIO)结构,用于块设备 I/O。

  12. struct execute_work ew:用于处理请求执行的工作。

这个结构体用于在 SCSI 请求的整个生命周期中跟踪请求状态、数据传输和相关信息。

Sg_fd


typedef struct sg_fd {		/* holds the state of a file descriptor */
	struct list_head sfd_siblings;  /* protected by device's sfd_lock */
	struct sg_device *parentdp;	/* owning device */
	wait_queue_head_t read_wait;	/* queue read until command done */
	rwlock_t rq_list_lock;	/* protect access to list in req_arr */
	struct mutex f_mutex;	/* protect against changes in this fd */
	int timeout;		/* defaults to SG_DEFAULT_TIMEOUT      */
	int timeout_user;	/* defaults to SG_DEFAULT_TIMEOUT_USER */
	Sg_scatter_hold reserve;	/* buffer held for this file descriptor */
	struct list_head rq_list; /* head of request list */
	struct fasync_struct *async_qp;	/* used by asynchronous notification */
	Sg_request req_arr[SG_MAX_QUEUE];	/* used as singly-linked list */
	char force_packid;	/* 1 -> pack_id input to read(), 0 -> ignored */
	char cmd_q;		/* 1 -> allow command queuing, 0 -> don't */
	unsigned char next_cmd_len; /* 0: automatic, >0: use on next write() */
	char keep_orphan;	/* 0 -> drop orphan (def), 1 -> keep for read() */
	char mmap_called;	/* 0 -> mmap() never called on this fd */
	char res_in_use;	/* 1 -> 'reserve' array in use */
	struct kref f_ref;
	struct execute_work ew;
} Sg_fd;

这段代码定义了一个名为 Sg_fd 的结构体,用于保存文件描述符的状态。以下是各字段的简要解释:

  1. struct list_head sfd_siblings:链表条目,用于将文件描述符链接到同一设备的文件描述符列表中,受设备的 sfd_lock 保护。

  2. struct sg_device *parentdp:指向拥有该文件描述符的设备的指针。

  3. wait_queue_head_t read_wait:等待队列头,用于将读取操作推迟直到命令执行完毕。

  4. rwlock_t rq_list_lock:读写锁,保护对 rq_list 请求列表的访问。

  5. struct mutex f_mutex:互斥锁,保护对该文件描述符的更改。

  6. int timeout:超时时间,单位为 jiffies,默认为 SG_DEFAULT_TIMEOUT

  7. int timeout_user:用户级超时时间,单位为 jiffies,默认为 SG_DEFAULT_TIMEOUT_USER

  8. Sg_scatter_hold reserve:为该文件描述符保留的缓冲区。

  9. struct list_head rq_list:请求列表的头,用于跟踪提交给设备的请求。

  10. struct fasync_struct *async_qp:用于异步通知的 fasync 结构。

  11. Sg_request req_arr[SG_MAX_QUEUE]:SG 请求的数组,最多支持 SG_MAX_QUEUE 个请求。

  12. char force_packid:标志,表示是否将 pack_id 输入到 read() 函数。

  13. char cmd_q:标志,表示是否允许命令排队。

  14. unsigned char next_cmd_len:表示下一次写入操作时要使用的命令长度。

  15. char keep_orphan:标志,表示是否保留孤立(orphan)请求以供读取。

  16. char mmap_called:标志,表示是否在该文件描述符上调用了 mmap()

  17. char res_in_use:标志,表示是否正在使用保留(reserve)数组。

  18. struct kref f_ref:内核引用计数结构,用于管理该文件描述符的引用计数。

  19. struct execute_work ew:用于处理文件描述符

Sg_device

typedef struct sg_device { /* holds the state of each scsi generic device */
	struct scsi_device *device;
	wait_queue_head_t open_wait;    /* queue open() when O_EXCL present */
	struct mutex open_rel_lock;     /* held when in open() or release() */
	int sg_tablesize;	/* adapter's max scatter-gather table size */
	u32 index;		/* device index number */
	struct list_head sfds;
	rwlock_t sfd_lock;      /* protect access to sfd list */
	atomic_t detaching;     /* 0->device usable, 1->device detaching */
	bool exclude;		/* 1->open(O_EXCL) succeeded and is active */
	int open_cnt;		/* count of opens (perhaps < num(sfds) ) */
	char sgdebug;		/* 0->off, 1->sense, 9->dump dev, 10-> all devs */
	char name[DISK_NAME_LEN];
	struct cdev * cdev;	/* char_dev [sysfs: /sys/cdev/major/sg<n>] */
	struct kref d_ref;
} Sg_device;

这段代码定义了一个名为 Sg_device 的结构体,用于保存每个 SCSI generic 设备的状态。以下是各字段的简要解释:

  1. struct scsi_device *device:指向底层 SCSI 设备的指针。

  2. wait_queue_head_t open_wait:等待队列头,用于在使用 O_EXCL 打开设备时排队等待。

  3. struct mutex open_rel_lock:互斥锁,用于保护在打开(open())或释放(release())过程中的访问。

  4. int sg_tablesize:适配器的最大散射-聚集表大小。

  5. u32 index:设备的索引号。

  6. struct list_head sfds:文件描述符列表的头。

  7. rwlock_t sfd_lock:读写锁,用于保护对文件描述符列表的访问。

  8. atomic_t detaching:原子整数,用于标识设备是否正在被移除。

  9. bool exclude:布尔值,表示是否使用 O_EXCL 打开设备。

  10. int open_cnt:设备被打开的次数。

  11. char sgdebug:调试标志,用于控制调试模式。

  12. char name[DISK_NAME_LEN]:设备的名称。

  13. struct cdev * cdev:字符设备结构的指针,用于在 /sys/cdev/major/sg<n> 中表示设备。

  14. struct kref d_ref:内核引用计数结构,用于管理该设备的引用计数。

在上述代码片段中,有三个主要的数据结构:Sg_deviceSg_fdSg_request。它们之间的关系可以通过如下方式解释:

  1. Sg_device(设备结构):

    • Sg_device 结构用于表示每个 SCSI generic 设备的状态。
    • 它包含了设备的基本信息,如底层 SCSI 设备指针、打开等待队列、设备索引号等。
    • 在每个设备结构中,还有一个 sfds 字段,它是一个链表的头,用于管理打开的文件描述符(Sg_fd 结构)。
  2. Sg_fd(文件描述符结构):

    • Sg_fd 结构用于表示每个打开的文件描述符的状态。
    • 每个打开的文件描述符与一个特定的 Sg_device 相关联,通过 parentdp 字段来指向它。
    • Sg_fd 结构中,有一个 req_arr 字段,它是一个数组,用于存储正在进行的请求(Sg_request 结构)。
  3. Sg_request(请求结构):

    • Sg_request 结构用于表示每个正在处理的请求。
    • 每个请求与一个特定的文件描述符相关联,通过 parentfp 字段来指向它。
    • 请求可能涉及的信息包括 SCSI 命令、散射-聚集列表、数据缓冲、状态等。

综上所述,关系如下:

  • 每个 Sg_device 结构可以有多个 Sg_fd 结构(文件描述符),每个 Sg_fd 结构代表一个打开的文件描述符。
  • 每个 Sg_fd 结构可以有多个 Sg_request 结构(请求),每个 Sg_request 结构代表一个正在处理的请求。

tasklet or soft irq callback

/* tasklet or soft irq callback */
static enum rq_end_io_ret sg_rq_end_io(struct request *rq, blk_status_t status);
static int sg_start_req(Sg_request *srp, unsigned char *cmd);
static int sg_finish_rem_req(Sg_request * srp);
static int sg_build_indirect(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size);
static ssize_t sg_new_read(Sg_fd * sfp, char __user *buf, size_t count,
			   Sg_request * srp);
static ssize_t sg_new_write(Sg_fd *sfp, struct file *file,
			const char __user *buf, size_t count, int blocking,
			int read_only, int sg_io_owned, Sg_request **o_srp);
static int sg_common_write(Sg_fd * sfp, Sg_request * srp,
			   unsigned char *cmnd, int timeout, int blocking);
static int sg_read_oxfer(Sg_request * srp, char __user *outp, int num_read_xfer);
static void sg_remove_scat(Sg_fd * sfp, Sg_scatter_hold * schp);
static void sg_build_reserve(Sg_fd * sfp, int req_size);
static void sg_link_reserve(Sg_fd * sfp, Sg_request * srp, int size);
static void sg_unlink_reserve(Sg_fd * sfp, Sg_request * srp);
static Sg_fd *sg_add_sfp(Sg_device * sdp);
static void sg_remove_sfp(struct kref *);
static Sg_request *sg_get_rq_mark(Sg_fd * sfp, int pack_id, bool *busy);
static Sg_request *sg_add_request(Sg_fd * sfp);
static int sg_remove_request(Sg_fd * sfp, Sg_request * srp);
static Sg_device *sg_get_dev(int dev);
static void sg_device_destroy(struct kref *kref);

#define SZ_SG_HEADER sizeof(struct sg_header)
#define SZ_SG_IO_HDR sizeof(sg_io_hdr_t)
#define SZ_SG_IOVEC sizeof(sg_iovec_t)
#define SZ_SG_REQ_INFO sizeof(sg_req_info_t)

#define sg_printk(prefix, sdp, fmt, a...) \
	sdev_prefix_printk(prefix, (sdp)->device, (sdp)->name, fmt, ##a)

这些函数的名称和注释提供了关于它们的一些信息,下面是对这些函数的功能和用途的简要说明:

  1. sg_rq_end_io:任务完成时的回调,处理请求完成的情况。
  2. sg_start_req:开始处理请求,构建并发送 SCSI 命令。
  3. sg_finish_rem_req:结束处理请求,清理剩余的请求数据。
  4. sg_build_indirect:构建间接 I/O,用于处理散射-聚集列表。
  5. sg_new_read:执行新的读取请求。
  6. sg_new_write:执行新的写入请求。
  7. sg_common_write:通用的写入请求函数。
  8. sg_read_oxfer:读取已传输的数据。
  9. sg_remove_scat:移除散射-聚集列表。
  10. sg_build_reserve:构建保留区域,为请求保留缓冲区。
  11. sg_link_reserve:链接保留区域到请求。
  12. sg_unlink_reserve:从请求中取消链接保留区域。
  13. sg_add_sfp:添加一个文件描述符到设备。
  14. sg_remove_sfp:从设备移除文件描述符。
  15. sg_get_rq_mark:获取标记的请求。
  16. sg_add_request:添加一个请求到文件描述符。
  17. sg_remove_request:从文件描述符中移除请求。
  18. sg_get_dev:获取设备的状态。
  19. sg_device_destroy:销毁设备。

这些函数在 SCSI generic 驱动程序中用于处理不同的请求、IO 操作和设备状态。它们相互协作以实现 SCSI 命令的发送和接收、请求的处理、保留区域的管理等。

sg_check_file_access & sg_allow_access

/*
 * The SCSI interfaces that use read() and write() as an asynchronous variant of
 * ioctl(..., SG_IO, ...) are fundamentally unsafe, since there are lots of ways
 * to trigger read() and write() calls from various contexts with elevated
 * privileges. This can lead to kernel memory corruption (e.g. if these
 * interfaces are called through splice()) and privilege escalation inside
 * userspace (e.g. if a process with access to such a device passes a file
 * descriptor to a SUID binary as stdin/stdout/stderr).
 *
 * This function provides protection for the legacy API by restricting the
 * calling context.
 */
static int sg_check_file_access(struct file *filp, const char *caller)
{
	if (filp->f_cred != current_real_cred()) {
		pr_err_once("%s: process %d (%s) changed security contexts after opening file descriptor, this is not allowed.\n",
			caller, task_tgid_vnr(current), current->comm);
		return -EPERM;
	}
	return 0;
}

static int sg_allow_access(struct file *filp, unsigned char *cmd)
{
	struct sg_fd *sfp = filp->private_data;

	if (sfp->parentdp->device->type == TYPE_SCANNER)
		return 0;
	if (!scsi_cmd_allowed(cmd, filp->f_mode))
		return -EPERM;
	return 0;
}

这段代码涉及到对使用 read()write() 的异步 I/O 接口进行保护的操作,以防止潜在的内核内存损坏和特权升级的问题。

  1. sg_check_file_access:此函数用于检查调用者的权限,以确保不会出现在不受控制的情况下调用异步 I/O 接口的情况。它比较文件描述符的安全上下文,如果安全上下文发生变化,则拒绝访问并返回 -EPERM,表示权限不足。这有助于防止潜在的特权升级攻击。

  2. sg_allow_access:该函数用于检查是否允许访问文件,基于指定的 SCSI 命令和文件模式。如果设备类型为扫描仪(TYPE_SCANNER),则允许访问。否则,通过 scsi_cmd_allowed 函数检查给定的 SCSI 命令和文件模式是否允许访问,如果不允许,则返回 -EPERM

这两个函数一起实现了对异步 I/O 接口的安全性检查,以确保不会发生潜在的内核损坏或特权升级的情况。

总结

SG(SCSI Generic)驱动是Linux内核中用于与SCSI设备通信的驱动程序。它允许用户空间应用程序通过文件系统接口来进行与SCSI设备的通信,支持传统的 ioctl 调用,同时还提供了异步的 read()write() 接口。以下是与SG驱动相关的主要数据结构和函数的概述:

数据结构:

  1. sg_io_hdr_t:代表一个SCSI命令及其相关信息,如数据传输方向、传输长度等。

  2. sg_request:用于跟踪SG命令的请求,包含了SG命令的各种信息以及处理的状态。

  3. sg_fd:表示文件描述符的状态,包括命令队列、超时设置、保留区域等。

  4. sg_device:表示每个SCSI通用设备的状态,包括所属SCSI设备、打开等待队列、索引号等。

函数:

  1. sg_check_file_access:检查文件描述符的安全上下文,以确保异步I/O接口的调用来自于合法的权限上下文,防止潜在的内核内存损坏和特权升级。

  2. sg_allow_access:检查是否允许访问文件,基于给定的SCSI命令和文件模式。

  3. sg_rq_end_io:SCSI请求完成的回调函数,用于处理请求完成后的后续操作。

  4. sg_start_req:开始一个SG请求,将SCSI命令添加到请求队列中。

  5. sg_finish_rem_req:完成一个SG请求,释放相关资源。

  6. sg_build_indirect:构建间接I/O的scatter-gather列表。

  7. sg_new_readsg_new_write:新的异步读写函数,用于进行读取和写入操作。

  8. sg_common_write:通用的写入函数,执行SG命令的写入操作。

  9. sg_read_oxfer:从请求的数据缓冲区读取数据。

  10. sg_build_reserve:构建保留区域,用于存储文件描述符相关的缓冲区。

  11. sg_link_reservesg_unlink_reserve:链接和解除链接保留区域。

  12. sg_add_sfpsg_remove_sfp:添加和移除文件描述符对象。

  13. sg_get_rq_mark:根据pack_id获取请求对象。

  14. sg_add_requestsg_remove_request:添加和移除请求对象。

  15. sg_get_devsg_device_destroy:获取设备对象和销毁设备对象。

总结:

SG驱动提供了一种通用的接口,允许用户空间应用程序与SCSI设备进行通信。它通过一系列的数据结构和函数,实现了SCSI命令的传输、异步I/O的控制以及请求的管理等功能,同时也提供了对安全性的保护,防止潜在的内核损坏和特权升级问题。

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐