18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * History: 48c2ecf20Sopenharmony_ci * Started: Aug 9 by Lawrence Foard (entropy@world.std.com), 58c2ecf20Sopenharmony_ci * to allow user process control of SCSI devices. 68c2ecf20Sopenharmony_ci * Development Sponsored by Killy Corp. NY NY 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Original driver (sg.c): 98c2ecf20Sopenharmony_ci * Copyright (C) 1992 Lawrence Foard 108c2ecf20Sopenharmony_ci * Version 2 and 3 extensions to driver: 118c2ecf20Sopenharmony_ci * Copyright (C) 1998 - 2014 Douglas Gilbert 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_cistatic int sg_version_num = 30536; /* 2 digits for each component */ 158c2ecf20Sopenharmony_ci#define SG_VERSION_STR "3.5.36" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci/* 188c2ecf20Sopenharmony_ci * D. P. Gilbert (dgilbert@interlog.com), notes: 198c2ecf20Sopenharmony_ci * - scsi logging is available via SCSI_LOG_TIMEOUT macros. First 208c2ecf20Sopenharmony_ci * the kernel/module needs to be built with CONFIG_SCSI_LOGGING 218c2ecf20Sopenharmony_ci * (otherwise the macros compile to empty statements). 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci */ 248c2ecf20Sopenharmony_ci#include <linux/module.h> 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#include <linux/fs.h> 278c2ecf20Sopenharmony_ci#include <linux/kernel.h> 288c2ecf20Sopenharmony_ci#include <linux/sched.h> 298c2ecf20Sopenharmony_ci#include <linux/string.h> 308c2ecf20Sopenharmony_ci#include <linux/mm.h> 318c2ecf20Sopenharmony_ci#include <linux/errno.h> 328c2ecf20Sopenharmony_ci#include <linux/mtio.h> 338c2ecf20Sopenharmony_ci#include <linux/ioctl.h> 348c2ecf20Sopenharmony_ci#include <linux/slab.h> 358c2ecf20Sopenharmony_ci#include <linux/fcntl.h> 368c2ecf20Sopenharmony_ci#include <linux/init.h> 378c2ecf20Sopenharmony_ci#include <linux/poll.h> 388c2ecf20Sopenharmony_ci#include <linux/moduleparam.h> 398c2ecf20Sopenharmony_ci#include <linux/cdev.h> 408c2ecf20Sopenharmony_ci#include <linux/idr.h> 418c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 428c2ecf20Sopenharmony_ci#include <linux/blkdev.h> 438c2ecf20Sopenharmony_ci#include <linux/delay.h> 448c2ecf20Sopenharmony_ci#include <linux/blktrace_api.h> 458c2ecf20Sopenharmony_ci#include <linux/mutex.h> 468c2ecf20Sopenharmony_ci#include <linux/atomic.h> 478c2ecf20Sopenharmony_ci#include <linux/ratelimit.h> 488c2ecf20Sopenharmony_ci#include <linux/uio.h> 498c2ecf20Sopenharmony_ci#include <linux/cred.h> /* for sg_check_file_access() */ 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#include "scsi.h" 528c2ecf20Sopenharmony_ci#include <scsi/scsi_dbg.h> 538c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h> 548c2ecf20Sopenharmony_ci#include <scsi/scsi_driver.h> 558c2ecf20Sopenharmony_ci#include <scsi/scsi_ioctl.h> 568c2ecf20Sopenharmony_ci#include <scsi/sg.h> 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci#include "scsi_logging.h" 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci#ifdef CONFIG_SCSI_PROC_FS 618c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 628c2ecf20Sopenharmony_cistatic char *sg_version_date = "20140603"; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic int sg_proc_init(void); 658c2ecf20Sopenharmony_ci#endif 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci#define SG_ALLOW_DIO_DEF 0 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci#define SG_MAX_DEVS 32768 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci/* SG_MAX_CDB_SIZE should be 260 (spc4r37 section 3.1.30) however the type 728c2ecf20Sopenharmony_ci * of sg_io_hdr::cmd_len can only represent 255. All SCSI commands greater 738c2ecf20Sopenharmony_ci * than 16 bytes are "variable length" whose length is a multiple of 4 748c2ecf20Sopenharmony_ci */ 758c2ecf20Sopenharmony_ci#define SG_MAX_CDB_SIZE 252 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci#define SG_DEFAULT_TIMEOUT mult_frac(SG_DEFAULT_TIMEOUT_USER, HZ, USER_HZ) 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ciint sg_big_buff = SG_DEF_RESERVED_SIZE; 808c2ecf20Sopenharmony_ci/* N.B. This variable is readable and writeable via 818c2ecf20Sopenharmony_ci /proc/scsi/sg/def_reserved_size . Each time sg_open() is called a buffer 828c2ecf20Sopenharmony_ci of this size (or less if there is not enough memory) will be reserved 838c2ecf20Sopenharmony_ci for use by this file descriptor. [Deprecated usage: this variable is also 848c2ecf20Sopenharmony_ci readable via /proc/sys/kernel/sg-big-buff if the sg driver is built into 858c2ecf20Sopenharmony_ci the kernel (i.e. it is not a module).] */ 868c2ecf20Sopenharmony_cistatic int def_reserved_size = -1; /* picks up init parameter */ 878c2ecf20Sopenharmony_cistatic int sg_allow_dio = SG_ALLOW_DIO_DEF; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cistatic int scatter_elem_sz = SG_SCATTER_SZ; 908c2ecf20Sopenharmony_cistatic int scatter_elem_sz_prev = SG_SCATTER_SZ; 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci#define SG_SECTOR_SZ 512 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic int sg_add_device(struct device *, struct class_interface *); 958c2ecf20Sopenharmony_cistatic void sg_remove_device(struct device *, struct class_interface *); 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_cistatic DEFINE_IDR(sg_index_idr); 988c2ecf20Sopenharmony_cistatic DEFINE_RWLOCK(sg_index_lock); /* Also used to lock 998c2ecf20Sopenharmony_ci file descriptor list for device */ 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_cistatic struct class_interface sg_interface = { 1028c2ecf20Sopenharmony_ci .add_dev = sg_add_device, 1038c2ecf20Sopenharmony_ci .remove_dev = sg_remove_device, 1048c2ecf20Sopenharmony_ci}; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_citypedef struct sg_scatter_hold { /* holding area for scsi scatter gather info */ 1078c2ecf20Sopenharmony_ci unsigned short k_use_sg; /* Count of kernel scatter-gather pieces */ 1088c2ecf20Sopenharmony_ci unsigned sglist_len; /* size of malloc'd scatter-gather list ++ */ 1098c2ecf20Sopenharmony_ci unsigned bufflen; /* Size of (aggregate) data buffer */ 1108c2ecf20Sopenharmony_ci struct page **pages; 1118c2ecf20Sopenharmony_ci int page_order; 1128c2ecf20Sopenharmony_ci char dio_in_use; /* 0->indirect IO (or mmap), 1->dio */ 1138c2ecf20Sopenharmony_ci unsigned char cmd_opcode; /* first byte of command */ 1148c2ecf20Sopenharmony_ci} Sg_scatter_hold; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_cistruct sg_device; /* forward declarations */ 1178c2ecf20Sopenharmony_cistruct sg_fd; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_citypedef struct sg_request { /* SG_MAX_QUEUE requests outstanding per file */ 1208c2ecf20Sopenharmony_ci struct list_head entry; /* list entry */ 1218c2ecf20Sopenharmony_ci struct sg_fd *parentfp; /* NULL -> not in use */ 1228c2ecf20Sopenharmony_ci Sg_scatter_hold data; /* hold buffer, perhaps scatter list */ 1238c2ecf20Sopenharmony_ci sg_io_hdr_t header; /* scsi command+info, see <scsi/sg.h> */ 1248c2ecf20Sopenharmony_ci unsigned char sense_b[SCSI_SENSE_BUFFERSIZE]; 1258c2ecf20Sopenharmony_ci char res_used; /* 1 -> using reserve buffer, 0 -> not ... */ 1268c2ecf20Sopenharmony_ci char orphan; /* 1 -> drop on sight, 0 -> normal */ 1278c2ecf20Sopenharmony_ci char sg_io_owned; /* 1 -> packet belongs to SG_IO */ 1288c2ecf20Sopenharmony_ci /* done protected by rq_list_lock */ 1298c2ecf20Sopenharmony_ci char done; /* 0->before bh, 1->before read, 2->read */ 1308c2ecf20Sopenharmony_ci struct request *rq; 1318c2ecf20Sopenharmony_ci struct bio *bio; 1328c2ecf20Sopenharmony_ci struct execute_work ew; 1338c2ecf20Sopenharmony_ci} Sg_request; 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_citypedef struct sg_fd { /* holds the state of a file descriptor */ 1368c2ecf20Sopenharmony_ci struct list_head sfd_siblings; /* protected by device's sfd_lock */ 1378c2ecf20Sopenharmony_ci struct sg_device *parentdp; /* owning device */ 1388c2ecf20Sopenharmony_ci wait_queue_head_t read_wait; /* queue read until command done */ 1398c2ecf20Sopenharmony_ci rwlock_t rq_list_lock; /* protect access to list in req_arr */ 1408c2ecf20Sopenharmony_ci struct mutex f_mutex; /* protect against changes in this fd */ 1418c2ecf20Sopenharmony_ci int timeout; /* defaults to SG_DEFAULT_TIMEOUT */ 1428c2ecf20Sopenharmony_ci int timeout_user; /* defaults to SG_DEFAULT_TIMEOUT_USER */ 1438c2ecf20Sopenharmony_ci Sg_scatter_hold reserve; /* buffer held for this file descriptor */ 1448c2ecf20Sopenharmony_ci struct list_head rq_list; /* head of request list */ 1458c2ecf20Sopenharmony_ci struct fasync_struct *async_qp; /* used by asynchronous notification */ 1468c2ecf20Sopenharmony_ci Sg_request req_arr[SG_MAX_QUEUE]; /* used as singly-linked list */ 1478c2ecf20Sopenharmony_ci char force_packid; /* 1 -> pack_id input to read(), 0 -> ignored */ 1488c2ecf20Sopenharmony_ci char cmd_q; /* 1 -> allow command queuing, 0 -> don't */ 1498c2ecf20Sopenharmony_ci unsigned char next_cmd_len; /* 0: automatic, >0: use on next write() */ 1508c2ecf20Sopenharmony_ci char keep_orphan; /* 0 -> drop orphan (def), 1 -> keep for read() */ 1518c2ecf20Sopenharmony_ci char mmap_called; /* 0 -> mmap() never called on this fd */ 1528c2ecf20Sopenharmony_ci char res_in_use; /* 1 -> 'reserve' array in use */ 1538c2ecf20Sopenharmony_ci struct kref f_ref; 1548c2ecf20Sopenharmony_ci struct execute_work ew; 1558c2ecf20Sopenharmony_ci} Sg_fd; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_citypedef struct sg_device { /* holds the state of each scsi generic device */ 1588c2ecf20Sopenharmony_ci struct scsi_device *device; 1598c2ecf20Sopenharmony_ci wait_queue_head_t open_wait; /* queue open() when O_EXCL present */ 1608c2ecf20Sopenharmony_ci struct mutex open_rel_lock; /* held when in open() or release() */ 1618c2ecf20Sopenharmony_ci int sg_tablesize; /* adapter's max scatter-gather table size */ 1628c2ecf20Sopenharmony_ci u32 index; /* device index number */ 1638c2ecf20Sopenharmony_ci struct list_head sfds; 1648c2ecf20Sopenharmony_ci rwlock_t sfd_lock; /* protect access to sfd list */ 1658c2ecf20Sopenharmony_ci atomic_t detaching; /* 0->device usable, 1->device detaching */ 1668c2ecf20Sopenharmony_ci bool exclude; /* 1->open(O_EXCL) succeeded and is active */ 1678c2ecf20Sopenharmony_ci int open_cnt; /* count of opens (perhaps < num(sfds) ) */ 1688c2ecf20Sopenharmony_ci char sgdebug; /* 0->off, 1->sense, 9->dump dev, 10-> all devs */ 1698c2ecf20Sopenharmony_ci struct gendisk *disk; 1708c2ecf20Sopenharmony_ci struct cdev * cdev; /* char_dev [sysfs: /sys/cdev/major/sg<n>] */ 1718c2ecf20Sopenharmony_ci struct kref d_ref; 1728c2ecf20Sopenharmony_ci} Sg_device; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci/* tasklet or soft irq callback */ 1758c2ecf20Sopenharmony_cistatic void sg_rq_end_io(struct request *rq, blk_status_t status); 1768c2ecf20Sopenharmony_cistatic int sg_start_req(Sg_request *srp, unsigned char *cmd); 1778c2ecf20Sopenharmony_cistatic int sg_finish_rem_req(Sg_request * srp); 1788c2ecf20Sopenharmony_cistatic int sg_build_indirect(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size); 1798c2ecf20Sopenharmony_cistatic ssize_t sg_new_read(Sg_fd * sfp, char __user *buf, size_t count, 1808c2ecf20Sopenharmony_ci Sg_request * srp); 1818c2ecf20Sopenharmony_cistatic ssize_t sg_new_write(Sg_fd *sfp, struct file *file, 1828c2ecf20Sopenharmony_ci const char __user *buf, size_t count, int blocking, 1838c2ecf20Sopenharmony_ci int read_only, int sg_io_owned, Sg_request **o_srp); 1848c2ecf20Sopenharmony_cistatic int sg_common_write(Sg_fd * sfp, Sg_request * srp, 1858c2ecf20Sopenharmony_ci unsigned char *cmnd, int timeout, int blocking); 1868c2ecf20Sopenharmony_cistatic int sg_read_oxfer(Sg_request * srp, char __user *outp, int num_read_xfer); 1878c2ecf20Sopenharmony_cistatic void sg_remove_scat(Sg_fd * sfp, Sg_scatter_hold * schp); 1888c2ecf20Sopenharmony_cistatic void sg_build_reserve(Sg_fd * sfp, int req_size); 1898c2ecf20Sopenharmony_cistatic void sg_link_reserve(Sg_fd * sfp, Sg_request * srp, int size); 1908c2ecf20Sopenharmony_cistatic void sg_unlink_reserve(Sg_fd * sfp, Sg_request * srp); 1918c2ecf20Sopenharmony_cistatic Sg_fd *sg_add_sfp(Sg_device * sdp); 1928c2ecf20Sopenharmony_cistatic void sg_remove_sfp(struct kref *); 1938c2ecf20Sopenharmony_cistatic Sg_request *sg_get_rq_mark(Sg_fd * sfp, int pack_id, bool *busy); 1948c2ecf20Sopenharmony_cistatic Sg_request *sg_add_request(Sg_fd * sfp); 1958c2ecf20Sopenharmony_cistatic int sg_remove_request(Sg_fd * sfp, Sg_request * srp); 1968c2ecf20Sopenharmony_cistatic Sg_device *sg_get_dev(int dev); 1978c2ecf20Sopenharmony_cistatic void sg_device_destroy(struct kref *kref); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci#define SZ_SG_HEADER sizeof(struct sg_header) 2008c2ecf20Sopenharmony_ci#define SZ_SG_IO_HDR sizeof(sg_io_hdr_t) 2018c2ecf20Sopenharmony_ci#define SZ_SG_IOVEC sizeof(sg_iovec_t) 2028c2ecf20Sopenharmony_ci#define SZ_SG_REQ_INFO sizeof(sg_req_info_t) 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci#define sg_printk(prefix, sdp, fmt, a...) \ 2058c2ecf20Sopenharmony_ci sdev_prefix_printk(prefix, (sdp)->device, \ 2068c2ecf20Sopenharmony_ci (sdp)->disk->disk_name, fmt, ##a) 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci/* 2098c2ecf20Sopenharmony_ci * The SCSI interfaces that use read() and write() as an asynchronous variant of 2108c2ecf20Sopenharmony_ci * ioctl(..., SG_IO, ...) are fundamentally unsafe, since there are lots of ways 2118c2ecf20Sopenharmony_ci * to trigger read() and write() calls from various contexts with elevated 2128c2ecf20Sopenharmony_ci * privileges. This can lead to kernel memory corruption (e.g. if these 2138c2ecf20Sopenharmony_ci * interfaces are called through splice()) and privilege escalation inside 2148c2ecf20Sopenharmony_ci * userspace (e.g. if a process with access to such a device passes a file 2158c2ecf20Sopenharmony_ci * descriptor to a SUID binary as stdin/stdout/stderr). 2168c2ecf20Sopenharmony_ci * 2178c2ecf20Sopenharmony_ci * This function provides protection for the legacy API by restricting the 2188c2ecf20Sopenharmony_ci * calling context. 2198c2ecf20Sopenharmony_ci */ 2208c2ecf20Sopenharmony_cistatic int sg_check_file_access(struct file *filp, const char *caller) 2218c2ecf20Sopenharmony_ci{ 2228c2ecf20Sopenharmony_ci if (filp->f_cred != current_real_cred()) { 2238c2ecf20Sopenharmony_ci pr_err_once("%s: process %d (%s) changed security contexts after opening file descriptor, this is not allowed.\n", 2248c2ecf20Sopenharmony_ci caller, task_tgid_vnr(current), current->comm); 2258c2ecf20Sopenharmony_ci return -EPERM; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci if (uaccess_kernel()) { 2288c2ecf20Sopenharmony_ci pr_err_once("%s: process %d (%s) called from kernel context, this is not allowed.\n", 2298c2ecf20Sopenharmony_ci caller, task_tgid_vnr(current), current->comm); 2308c2ecf20Sopenharmony_ci return -EACCES; 2318c2ecf20Sopenharmony_ci } 2328c2ecf20Sopenharmony_ci return 0; 2338c2ecf20Sopenharmony_ci} 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_cistatic int sg_allow_access(struct file *filp, unsigned char *cmd) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci struct sg_fd *sfp = filp->private_data; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci if (sfp->parentdp->device->type == TYPE_SCANNER) 2408c2ecf20Sopenharmony_ci return 0; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci return blk_verify_command(cmd, filp->f_mode); 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_cistatic int 2468c2ecf20Sopenharmony_ciopen_wait(Sg_device *sdp, int flags) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci int retval = 0; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci if (flags & O_EXCL) { 2518c2ecf20Sopenharmony_ci while (sdp->open_cnt > 0) { 2528c2ecf20Sopenharmony_ci mutex_unlock(&sdp->open_rel_lock); 2538c2ecf20Sopenharmony_ci retval = wait_event_interruptible(sdp->open_wait, 2548c2ecf20Sopenharmony_ci (atomic_read(&sdp->detaching) || 2558c2ecf20Sopenharmony_ci !sdp->open_cnt)); 2568c2ecf20Sopenharmony_ci mutex_lock(&sdp->open_rel_lock); 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci if (retval) /* -ERESTARTSYS */ 2598c2ecf20Sopenharmony_ci return retval; 2608c2ecf20Sopenharmony_ci if (atomic_read(&sdp->detaching)) 2618c2ecf20Sopenharmony_ci return -ENODEV; 2628c2ecf20Sopenharmony_ci } 2638c2ecf20Sopenharmony_ci } else { 2648c2ecf20Sopenharmony_ci while (sdp->exclude) { 2658c2ecf20Sopenharmony_ci mutex_unlock(&sdp->open_rel_lock); 2668c2ecf20Sopenharmony_ci retval = wait_event_interruptible(sdp->open_wait, 2678c2ecf20Sopenharmony_ci (atomic_read(&sdp->detaching) || 2688c2ecf20Sopenharmony_ci !sdp->exclude)); 2698c2ecf20Sopenharmony_ci mutex_lock(&sdp->open_rel_lock); 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci if (retval) /* -ERESTARTSYS */ 2728c2ecf20Sopenharmony_ci return retval; 2738c2ecf20Sopenharmony_ci if (atomic_read(&sdp->detaching)) 2748c2ecf20Sopenharmony_ci return -ENODEV; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci } 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci return retval; 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_ci/* Returns 0 on success, else a negated errno value */ 2828c2ecf20Sopenharmony_cistatic int 2838c2ecf20Sopenharmony_cisg_open(struct inode *inode, struct file *filp) 2848c2ecf20Sopenharmony_ci{ 2858c2ecf20Sopenharmony_ci int dev = iminor(inode); 2868c2ecf20Sopenharmony_ci int flags = filp->f_flags; 2878c2ecf20Sopenharmony_ci struct request_queue *q; 2888c2ecf20Sopenharmony_ci Sg_device *sdp; 2898c2ecf20Sopenharmony_ci Sg_fd *sfp; 2908c2ecf20Sopenharmony_ci int retval; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci nonseekable_open(inode, filp); 2938c2ecf20Sopenharmony_ci if ((flags & O_EXCL) && (O_RDONLY == (flags & O_ACCMODE))) 2948c2ecf20Sopenharmony_ci return -EPERM; /* Can't lock it with read only access */ 2958c2ecf20Sopenharmony_ci sdp = sg_get_dev(dev); 2968c2ecf20Sopenharmony_ci if (IS_ERR(sdp)) 2978c2ecf20Sopenharmony_ci return PTR_ERR(sdp); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, 3008c2ecf20Sopenharmony_ci "sg_open: flags=0x%x\n", flags)); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci /* This driver's module count bumped by fops_get in <linux/fs.h> */ 3038c2ecf20Sopenharmony_ci /* Prevent the device driver from vanishing while we sleep */ 3048c2ecf20Sopenharmony_ci retval = scsi_device_get(sdp->device); 3058c2ecf20Sopenharmony_ci if (retval) 3068c2ecf20Sopenharmony_ci goto sg_put; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci retval = scsi_autopm_get_device(sdp->device); 3098c2ecf20Sopenharmony_ci if (retval) 3108c2ecf20Sopenharmony_ci goto sdp_put; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci /* scsi_block_when_processing_errors() may block so bypass 3138c2ecf20Sopenharmony_ci * check if O_NONBLOCK. Permits SCSI commands to be issued 3148c2ecf20Sopenharmony_ci * during error recovery. Tread carefully. */ 3158c2ecf20Sopenharmony_ci if (!((flags & O_NONBLOCK) || 3168c2ecf20Sopenharmony_ci scsi_block_when_processing_errors(sdp->device))) { 3178c2ecf20Sopenharmony_ci retval = -ENXIO; 3188c2ecf20Sopenharmony_ci /* we are in error recovery for this device */ 3198c2ecf20Sopenharmony_ci goto error_out; 3208c2ecf20Sopenharmony_ci } 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_ci mutex_lock(&sdp->open_rel_lock); 3238c2ecf20Sopenharmony_ci if (flags & O_NONBLOCK) { 3248c2ecf20Sopenharmony_ci if (flags & O_EXCL) { 3258c2ecf20Sopenharmony_ci if (sdp->open_cnt > 0) { 3268c2ecf20Sopenharmony_ci retval = -EBUSY; 3278c2ecf20Sopenharmony_ci goto error_mutex_locked; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci } else { 3308c2ecf20Sopenharmony_ci if (sdp->exclude) { 3318c2ecf20Sopenharmony_ci retval = -EBUSY; 3328c2ecf20Sopenharmony_ci goto error_mutex_locked; 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci } 3358c2ecf20Sopenharmony_ci } else { 3368c2ecf20Sopenharmony_ci retval = open_wait(sdp, flags); 3378c2ecf20Sopenharmony_ci if (retval) /* -ERESTARTSYS or -ENODEV */ 3388c2ecf20Sopenharmony_ci goto error_mutex_locked; 3398c2ecf20Sopenharmony_ci } 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci /* N.B. at this point we are holding the open_rel_lock */ 3428c2ecf20Sopenharmony_ci if (flags & O_EXCL) 3438c2ecf20Sopenharmony_ci sdp->exclude = true; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci if (sdp->open_cnt < 1) { /* no existing opens */ 3468c2ecf20Sopenharmony_ci sdp->sgdebug = 0; 3478c2ecf20Sopenharmony_ci q = sdp->device->request_queue; 3488c2ecf20Sopenharmony_ci sdp->sg_tablesize = queue_max_segments(q); 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci sfp = sg_add_sfp(sdp); 3518c2ecf20Sopenharmony_ci if (IS_ERR(sfp)) { 3528c2ecf20Sopenharmony_ci retval = PTR_ERR(sfp); 3538c2ecf20Sopenharmony_ci goto out_undo; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci filp->private_data = sfp; 3578c2ecf20Sopenharmony_ci sdp->open_cnt++; 3588c2ecf20Sopenharmony_ci mutex_unlock(&sdp->open_rel_lock); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci retval = 0; 3618c2ecf20Sopenharmony_cisg_put: 3628c2ecf20Sopenharmony_ci kref_put(&sdp->d_ref, sg_device_destroy); 3638c2ecf20Sopenharmony_ci return retval; 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ciout_undo: 3668c2ecf20Sopenharmony_ci if (flags & O_EXCL) { 3678c2ecf20Sopenharmony_ci sdp->exclude = false; /* undo if error */ 3688c2ecf20Sopenharmony_ci wake_up_interruptible(&sdp->open_wait); 3698c2ecf20Sopenharmony_ci } 3708c2ecf20Sopenharmony_cierror_mutex_locked: 3718c2ecf20Sopenharmony_ci mutex_unlock(&sdp->open_rel_lock); 3728c2ecf20Sopenharmony_cierror_out: 3738c2ecf20Sopenharmony_ci scsi_autopm_put_device(sdp->device); 3748c2ecf20Sopenharmony_cisdp_put: 3758c2ecf20Sopenharmony_ci scsi_device_put(sdp->device); 3768c2ecf20Sopenharmony_ci goto sg_put; 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci/* Release resources associated with a successful sg_open() 3808c2ecf20Sopenharmony_ci * Returns 0 on success, else a negated errno value */ 3818c2ecf20Sopenharmony_cistatic int 3828c2ecf20Sopenharmony_cisg_release(struct inode *inode, struct file *filp) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci Sg_device *sdp; 3858c2ecf20Sopenharmony_ci Sg_fd *sfp; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp))) 3888c2ecf20Sopenharmony_ci return -ENXIO; 3898c2ecf20Sopenharmony_ci SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, "sg_release\n")); 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci mutex_lock(&sdp->open_rel_lock); 3928c2ecf20Sopenharmony_ci scsi_autopm_put_device(sdp->device); 3938c2ecf20Sopenharmony_ci kref_put(&sfp->f_ref, sg_remove_sfp); 3948c2ecf20Sopenharmony_ci sdp->open_cnt--; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci /* possibly many open()s waiting on exlude clearing, start many; 3978c2ecf20Sopenharmony_ci * only open(O_EXCL)s wait on 0==open_cnt so only start one */ 3988c2ecf20Sopenharmony_ci if (sdp->exclude) { 3998c2ecf20Sopenharmony_ci sdp->exclude = false; 4008c2ecf20Sopenharmony_ci wake_up_interruptible_all(&sdp->open_wait); 4018c2ecf20Sopenharmony_ci } else if (0 == sdp->open_cnt) { 4028c2ecf20Sopenharmony_ci wake_up_interruptible(&sdp->open_wait); 4038c2ecf20Sopenharmony_ci } 4048c2ecf20Sopenharmony_ci mutex_unlock(&sdp->open_rel_lock); 4058c2ecf20Sopenharmony_ci return 0; 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_cistatic int get_sg_io_pack_id(int *pack_id, void __user *buf, size_t count) 4098c2ecf20Sopenharmony_ci{ 4108c2ecf20Sopenharmony_ci struct sg_header __user *old_hdr = buf; 4118c2ecf20Sopenharmony_ci int reply_len; 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci if (count >= SZ_SG_HEADER) { 4148c2ecf20Sopenharmony_ci /* negative reply_len means v3 format, otherwise v1/v2 */ 4158c2ecf20Sopenharmony_ci if (get_user(reply_len, &old_hdr->reply_len)) 4168c2ecf20Sopenharmony_ci return -EFAULT; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci if (reply_len >= 0) 4198c2ecf20Sopenharmony_ci return get_user(*pack_id, &old_hdr->pack_id); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci if (in_compat_syscall() && 4228c2ecf20Sopenharmony_ci count >= sizeof(struct compat_sg_io_hdr)) { 4238c2ecf20Sopenharmony_ci struct compat_sg_io_hdr __user *hp = buf; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci return get_user(*pack_id, &hp->pack_id); 4268c2ecf20Sopenharmony_ci } 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci if (count >= sizeof(struct sg_io_hdr)) { 4298c2ecf20Sopenharmony_ci struct sg_io_hdr __user *hp = buf; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci return get_user(*pack_id, &hp->pack_id); 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci /* no valid header was passed, so ignore the pack_id */ 4368c2ecf20Sopenharmony_ci *pack_id = -1; 4378c2ecf20Sopenharmony_ci return 0; 4388c2ecf20Sopenharmony_ci} 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_cistatic ssize_t 4418c2ecf20Sopenharmony_cisg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) 4428c2ecf20Sopenharmony_ci{ 4438c2ecf20Sopenharmony_ci Sg_device *sdp; 4448c2ecf20Sopenharmony_ci Sg_fd *sfp; 4458c2ecf20Sopenharmony_ci Sg_request *srp; 4468c2ecf20Sopenharmony_ci int req_pack_id = -1; 4478c2ecf20Sopenharmony_ci bool busy; 4488c2ecf20Sopenharmony_ci sg_io_hdr_t *hp; 4498c2ecf20Sopenharmony_ci struct sg_header *old_hdr; 4508c2ecf20Sopenharmony_ci int retval; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci /* 4538c2ecf20Sopenharmony_ci * This could cause a response to be stranded. Close the associated 4548c2ecf20Sopenharmony_ci * file descriptor to free up any resources being held. 4558c2ecf20Sopenharmony_ci */ 4568c2ecf20Sopenharmony_ci retval = sg_check_file_access(filp, __func__); 4578c2ecf20Sopenharmony_ci if (retval) 4588c2ecf20Sopenharmony_ci return retval; 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ci if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp))) 4618c2ecf20Sopenharmony_ci return -ENXIO; 4628c2ecf20Sopenharmony_ci SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, 4638c2ecf20Sopenharmony_ci "sg_read: count=%d\n", (int) count)); 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_ci if (sfp->force_packid) 4668c2ecf20Sopenharmony_ci retval = get_sg_io_pack_id(&req_pack_id, buf, count); 4678c2ecf20Sopenharmony_ci if (retval) 4688c2ecf20Sopenharmony_ci return retval; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci srp = sg_get_rq_mark(sfp, req_pack_id, &busy); 4718c2ecf20Sopenharmony_ci if (!srp) { /* now wait on packet to arrive */ 4728c2ecf20Sopenharmony_ci if (filp->f_flags & O_NONBLOCK) 4738c2ecf20Sopenharmony_ci return -EAGAIN; 4748c2ecf20Sopenharmony_ci retval = wait_event_interruptible(sfp->read_wait, 4758c2ecf20Sopenharmony_ci ((srp = sg_get_rq_mark(sfp, req_pack_id, &busy)) || 4768c2ecf20Sopenharmony_ci (!busy && atomic_read(&sdp->detaching)))); 4778c2ecf20Sopenharmony_ci if (!srp) 4788c2ecf20Sopenharmony_ci /* signal or detaching */ 4798c2ecf20Sopenharmony_ci return retval ? retval : -ENODEV; 4808c2ecf20Sopenharmony_ci } 4818c2ecf20Sopenharmony_ci if (srp->header.interface_id != '\0') 4828c2ecf20Sopenharmony_ci return sg_new_read(sfp, buf, count, srp); 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci hp = &srp->header; 4858c2ecf20Sopenharmony_ci old_hdr = kzalloc(SZ_SG_HEADER, GFP_KERNEL); 4868c2ecf20Sopenharmony_ci if (!old_hdr) 4878c2ecf20Sopenharmony_ci return -ENOMEM; 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_ci old_hdr->reply_len = (int) hp->timeout; 4908c2ecf20Sopenharmony_ci old_hdr->pack_len = old_hdr->reply_len; /* old, strange behaviour */ 4918c2ecf20Sopenharmony_ci old_hdr->pack_id = hp->pack_id; 4928c2ecf20Sopenharmony_ci old_hdr->twelve_byte = 4938c2ecf20Sopenharmony_ci ((srp->data.cmd_opcode >= 0xc0) && (12 == hp->cmd_len)) ? 1 : 0; 4948c2ecf20Sopenharmony_ci old_hdr->target_status = hp->masked_status; 4958c2ecf20Sopenharmony_ci old_hdr->host_status = hp->host_status; 4968c2ecf20Sopenharmony_ci old_hdr->driver_status = hp->driver_status; 4978c2ecf20Sopenharmony_ci if ((CHECK_CONDITION & hp->masked_status) || 4988c2ecf20Sopenharmony_ci (DRIVER_SENSE & hp->driver_status)) 4998c2ecf20Sopenharmony_ci memcpy(old_hdr->sense_buffer, srp->sense_b, 5008c2ecf20Sopenharmony_ci sizeof (old_hdr->sense_buffer)); 5018c2ecf20Sopenharmony_ci switch (hp->host_status) { 5028c2ecf20Sopenharmony_ci /* This setup of 'result' is for backward compatibility and is best 5038c2ecf20Sopenharmony_ci ignored by the user who should use target, host + driver status */ 5048c2ecf20Sopenharmony_ci case DID_OK: 5058c2ecf20Sopenharmony_ci case DID_PASSTHROUGH: 5068c2ecf20Sopenharmony_ci case DID_SOFT_ERROR: 5078c2ecf20Sopenharmony_ci old_hdr->result = 0; 5088c2ecf20Sopenharmony_ci break; 5098c2ecf20Sopenharmony_ci case DID_NO_CONNECT: 5108c2ecf20Sopenharmony_ci case DID_BUS_BUSY: 5118c2ecf20Sopenharmony_ci case DID_TIME_OUT: 5128c2ecf20Sopenharmony_ci old_hdr->result = EBUSY; 5138c2ecf20Sopenharmony_ci break; 5148c2ecf20Sopenharmony_ci case DID_BAD_TARGET: 5158c2ecf20Sopenharmony_ci case DID_ABORT: 5168c2ecf20Sopenharmony_ci case DID_PARITY: 5178c2ecf20Sopenharmony_ci case DID_RESET: 5188c2ecf20Sopenharmony_ci case DID_BAD_INTR: 5198c2ecf20Sopenharmony_ci old_hdr->result = EIO; 5208c2ecf20Sopenharmony_ci break; 5218c2ecf20Sopenharmony_ci case DID_ERROR: 5228c2ecf20Sopenharmony_ci old_hdr->result = (srp->sense_b[0] == 0 && 5238c2ecf20Sopenharmony_ci hp->masked_status == GOOD) ? 0 : EIO; 5248c2ecf20Sopenharmony_ci break; 5258c2ecf20Sopenharmony_ci default: 5268c2ecf20Sopenharmony_ci old_hdr->result = EIO; 5278c2ecf20Sopenharmony_ci break; 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci /* Now copy the result back to the user buffer. */ 5318c2ecf20Sopenharmony_ci if (count >= SZ_SG_HEADER) { 5328c2ecf20Sopenharmony_ci if (copy_to_user(buf, old_hdr, SZ_SG_HEADER)) { 5338c2ecf20Sopenharmony_ci retval = -EFAULT; 5348c2ecf20Sopenharmony_ci goto free_old_hdr; 5358c2ecf20Sopenharmony_ci } 5368c2ecf20Sopenharmony_ci buf += SZ_SG_HEADER; 5378c2ecf20Sopenharmony_ci if (count > old_hdr->reply_len) 5388c2ecf20Sopenharmony_ci count = old_hdr->reply_len; 5398c2ecf20Sopenharmony_ci if (count > SZ_SG_HEADER) { 5408c2ecf20Sopenharmony_ci if (sg_read_oxfer(srp, buf, count - SZ_SG_HEADER)) { 5418c2ecf20Sopenharmony_ci retval = -EFAULT; 5428c2ecf20Sopenharmony_ci goto free_old_hdr; 5438c2ecf20Sopenharmony_ci } 5448c2ecf20Sopenharmony_ci } 5458c2ecf20Sopenharmony_ci } else 5468c2ecf20Sopenharmony_ci count = (old_hdr->result == 0) ? 0 : -EIO; 5478c2ecf20Sopenharmony_ci sg_finish_rem_req(srp); 5488c2ecf20Sopenharmony_ci sg_remove_request(sfp, srp); 5498c2ecf20Sopenharmony_ci retval = count; 5508c2ecf20Sopenharmony_cifree_old_hdr: 5518c2ecf20Sopenharmony_ci kfree(old_hdr); 5528c2ecf20Sopenharmony_ci return retval; 5538c2ecf20Sopenharmony_ci} 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_cistatic ssize_t 5568c2ecf20Sopenharmony_cisg_new_read(Sg_fd * sfp, char __user *buf, size_t count, Sg_request * srp) 5578c2ecf20Sopenharmony_ci{ 5588c2ecf20Sopenharmony_ci sg_io_hdr_t *hp = &srp->header; 5598c2ecf20Sopenharmony_ci int err = 0, err2; 5608c2ecf20Sopenharmony_ci int len; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci if (in_compat_syscall()) { 5638c2ecf20Sopenharmony_ci if (count < sizeof(struct compat_sg_io_hdr)) { 5648c2ecf20Sopenharmony_ci err = -EINVAL; 5658c2ecf20Sopenharmony_ci goto err_out; 5668c2ecf20Sopenharmony_ci } 5678c2ecf20Sopenharmony_ci } else if (count < SZ_SG_IO_HDR) { 5688c2ecf20Sopenharmony_ci err = -EINVAL; 5698c2ecf20Sopenharmony_ci goto err_out; 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci hp->sb_len_wr = 0; 5728c2ecf20Sopenharmony_ci if ((hp->mx_sb_len > 0) && hp->sbp) { 5738c2ecf20Sopenharmony_ci if ((CHECK_CONDITION & hp->masked_status) || 5748c2ecf20Sopenharmony_ci (DRIVER_SENSE & hp->driver_status)) { 5758c2ecf20Sopenharmony_ci int sb_len = SCSI_SENSE_BUFFERSIZE; 5768c2ecf20Sopenharmony_ci sb_len = (hp->mx_sb_len > sb_len) ? sb_len : hp->mx_sb_len; 5778c2ecf20Sopenharmony_ci len = 8 + (int) srp->sense_b[7]; /* Additional sense length field */ 5788c2ecf20Sopenharmony_ci len = (len > sb_len) ? sb_len : len; 5798c2ecf20Sopenharmony_ci if (copy_to_user(hp->sbp, srp->sense_b, len)) { 5808c2ecf20Sopenharmony_ci err = -EFAULT; 5818c2ecf20Sopenharmony_ci goto err_out; 5828c2ecf20Sopenharmony_ci } 5838c2ecf20Sopenharmony_ci hp->sb_len_wr = len; 5848c2ecf20Sopenharmony_ci } 5858c2ecf20Sopenharmony_ci } 5868c2ecf20Sopenharmony_ci if (hp->masked_status || hp->host_status || hp->driver_status) 5878c2ecf20Sopenharmony_ci hp->info |= SG_INFO_CHECK; 5888c2ecf20Sopenharmony_ci err = put_sg_io_hdr(hp, buf); 5898c2ecf20Sopenharmony_cierr_out: 5908c2ecf20Sopenharmony_ci err2 = sg_finish_rem_req(srp); 5918c2ecf20Sopenharmony_ci sg_remove_request(sfp, srp); 5928c2ecf20Sopenharmony_ci return err ? : err2 ? : count; 5938c2ecf20Sopenharmony_ci} 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_cistatic ssize_t 5968c2ecf20Sopenharmony_cisg_write(struct file *filp, const char __user *buf, size_t count, loff_t * ppos) 5978c2ecf20Sopenharmony_ci{ 5988c2ecf20Sopenharmony_ci int mxsize, cmd_size, k; 5998c2ecf20Sopenharmony_ci int input_size, blocking; 6008c2ecf20Sopenharmony_ci unsigned char opcode; 6018c2ecf20Sopenharmony_ci Sg_device *sdp; 6028c2ecf20Sopenharmony_ci Sg_fd *sfp; 6038c2ecf20Sopenharmony_ci Sg_request *srp; 6048c2ecf20Sopenharmony_ci struct sg_header old_hdr; 6058c2ecf20Sopenharmony_ci sg_io_hdr_t *hp; 6068c2ecf20Sopenharmony_ci unsigned char cmnd[SG_MAX_CDB_SIZE]; 6078c2ecf20Sopenharmony_ci int retval; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci retval = sg_check_file_access(filp, __func__); 6108c2ecf20Sopenharmony_ci if (retval) 6118c2ecf20Sopenharmony_ci return retval; 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp))) 6148c2ecf20Sopenharmony_ci return -ENXIO; 6158c2ecf20Sopenharmony_ci SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, 6168c2ecf20Sopenharmony_ci "sg_write: count=%d\n", (int) count)); 6178c2ecf20Sopenharmony_ci if (atomic_read(&sdp->detaching)) 6188c2ecf20Sopenharmony_ci return -ENODEV; 6198c2ecf20Sopenharmony_ci if (!((filp->f_flags & O_NONBLOCK) || 6208c2ecf20Sopenharmony_ci scsi_block_when_processing_errors(sdp->device))) 6218c2ecf20Sopenharmony_ci return -ENXIO; 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci if (count < SZ_SG_HEADER) 6248c2ecf20Sopenharmony_ci return -EIO; 6258c2ecf20Sopenharmony_ci if (copy_from_user(&old_hdr, buf, SZ_SG_HEADER)) 6268c2ecf20Sopenharmony_ci return -EFAULT; 6278c2ecf20Sopenharmony_ci blocking = !(filp->f_flags & O_NONBLOCK); 6288c2ecf20Sopenharmony_ci if (old_hdr.reply_len < 0) 6298c2ecf20Sopenharmony_ci return sg_new_write(sfp, filp, buf, count, 6308c2ecf20Sopenharmony_ci blocking, 0, 0, NULL); 6318c2ecf20Sopenharmony_ci if (count < (SZ_SG_HEADER + 6)) 6328c2ecf20Sopenharmony_ci return -EIO; /* The minimum scsi command length is 6 bytes. */ 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci buf += SZ_SG_HEADER; 6358c2ecf20Sopenharmony_ci if (get_user(opcode, buf)) 6368c2ecf20Sopenharmony_ci return -EFAULT; 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci if (!(srp = sg_add_request(sfp))) { 6398c2ecf20Sopenharmony_ci SCSI_LOG_TIMEOUT(1, sg_printk(KERN_INFO, sdp, 6408c2ecf20Sopenharmony_ci "sg_write: queue full\n")); 6418c2ecf20Sopenharmony_ci return -EDOM; 6428c2ecf20Sopenharmony_ci } 6438c2ecf20Sopenharmony_ci mutex_lock(&sfp->f_mutex); 6448c2ecf20Sopenharmony_ci if (sfp->next_cmd_len > 0) { 6458c2ecf20Sopenharmony_ci cmd_size = sfp->next_cmd_len; 6468c2ecf20Sopenharmony_ci sfp->next_cmd_len = 0; /* reset so only this write() effected */ 6478c2ecf20Sopenharmony_ci } else { 6488c2ecf20Sopenharmony_ci cmd_size = COMMAND_SIZE(opcode); /* based on SCSI command group */ 6498c2ecf20Sopenharmony_ci if ((opcode >= 0xc0) && old_hdr.twelve_byte) 6508c2ecf20Sopenharmony_ci cmd_size = 12; 6518c2ecf20Sopenharmony_ci } 6528c2ecf20Sopenharmony_ci mutex_unlock(&sfp->f_mutex); 6538c2ecf20Sopenharmony_ci SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sdp, 6548c2ecf20Sopenharmony_ci "sg_write: scsi opcode=0x%02x, cmd_size=%d\n", (int) opcode, cmd_size)); 6558c2ecf20Sopenharmony_ci/* Determine buffer size. */ 6568c2ecf20Sopenharmony_ci input_size = count - cmd_size; 6578c2ecf20Sopenharmony_ci mxsize = (input_size > old_hdr.reply_len) ? input_size : old_hdr.reply_len; 6588c2ecf20Sopenharmony_ci mxsize -= SZ_SG_HEADER; 6598c2ecf20Sopenharmony_ci input_size -= SZ_SG_HEADER; 6608c2ecf20Sopenharmony_ci if (input_size < 0) { 6618c2ecf20Sopenharmony_ci sg_remove_request(sfp, srp); 6628c2ecf20Sopenharmony_ci return -EIO; /* User did not pass enough bytes for this command. */ 6638c2ecf20Sopenharmony_ci } 6648c2ecf20Sopenharmony_ci hp = &srp->header; 6658c2ecf20Sopenharmony_ci hp->interface_id = '\0'; /* indicator of old interface tunnelled */ 6668c2ecf20Sopenharmony_ci hp->cmd_len = (unsigned char) cmd_size; 6678c2ecf20Sopenharmony_ci hp->iovec_count = 0; 6688c2ecf20Sopenharmony_ci hp->mx_sb_len = 0; 6698c2ecf20Sopenharmony_ci if (input_size > 0) 6708c2ecf20Sopenharmony_ci hp->dxfer_direction = (old_hdr.reply_len > SZ_SG_HEADER) ? 6718c2ecf20Sopenharmony_ci SG_DXFER_TO_FROM_DEV : SG_DXFER_TO_DEV; 6728c2ecf20Sopenharmony_ci else 6738c2ecf20Sopenharmony_ci hp->dxfer_direction = (mxsize > 0) ? SG_DXFER_FROM_DEV : SG_DXFER_NONE; 6748c2ecf20Sopenharmony_ci hp->dxfer_len = mxsize; 6758c2ecf20Sopenharmony_ci if ((hp->dxfer_direction == SG_DXFER_TO_DEV) || 6768c2ecf20Sopenharmony_ci (hp->dxfer_direction == SG_DXFER_TO_FROM_DEV)) 6778c2ecf20Sopenharmony_ci hp->dxferp = (char __user *)buf + cmd_size; 6788c2ecf20Sopenharmony_ci else 6798c2ecf20Sopenharmony_ci hp->dxferp = NULL; 6808c2ecf20Sopenharmony_ci hp->sbp = NULL; 6818c2ecf20Sopenharmony_ci hp->timeout = old_hdr.reply_len; /* structure abuse ... */ 6828c2ecf20Sopenharmony_ci hp->flags = input_size; /* structure abuse ... */ 6838c2ecf20Sopenharmony_ci hp->pack_id = old_hdr.pack_id; 6848c2ecf20Sopenharmony_ci hp->usr_ptr = NULL; 6858c2ecf20Sopenharmony_ci if (copy_from_user(cmnd, buf, cmd_size)) { 6868c2ecf20Sopenharmony_ci sg_remove_request(sfp, srp); 6878c2ecf20Sopenharmony_ci return -EFAULT; 6888c2ecf20Sopenharmony_ci } 6898c2ecf20Sopenharmony_ci /* 6908c2ecf20Sopenharmony_ci * SG_DXFER_TO_FROM_DEV is functionally equivalent to SG_DXFER_FROM_DEV, 6918c2ecf20Sopenharmony_ci * but is is possible that the app intended SG_DXFER_TO_DEV, because there 6928c2ecf20Sopenharmony_ci * is a non-zero input_size, so emit a warning. 6938c2ecf20Sopenharmony_ci */ 6948c2ecf20Sopenharmony_ci if (hp->dxfer_direction == SG_DXFER_TO_FROM_DEV) { 6958c2ecf20Sopenharmony_ci printk_ratelimited(KERN_WARNING 6968c2ecf20Sopenharmony_ci "sg_write: data in/out %d/%d bytes " 6978c2ecf20Sopenharmony_ci "for SCSI command 0x%x-- guessing " 6988c2ecf20Sopenharmony_ci "data in;\n program %s not setting " 6998c2ecf20Sopenharmony_ci "count and/or reply_len properly\n", 7008c2ecf20Sopenharmony_ci old_hdr.reply_len - (int)SZ_SG_HEADER, 7018c2ecf20Sopenharmony_ci input_size, (unsigned int) cmnd[0], 7028c2ecf20Sopenharmony_ci current->comm); 7038c2ecf20Sopenharmony_ci } 7048c2ecf20Sopenharmony_ci k = sg_common_write(sfp, srp, cmnd, sfp->timeout, blocking); 7058c2ecf20Sopenharmony_ci return (k < 0) ? k : count; 7068c2ecf20Sopenharmony_ci} 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_cistatic ssize_t 7098c2ecf20Sopenharmony_cisg_new_write(Sg_fd *sfp, struct file *file, const char __user *buf, 7108c2ecf20Sopenharmony_ci size_t count, int blocking, int read_only, int sg_io_owned, 7118c2ecf20Sopenharmony_ci Sg_request **o_srp) 7128c2ecf20Sopenharmony_ci{ 7138c2ecf20Sopenharmony_ci int k; 7148c2ecf20Sopenharmony_ci Sg_request *srp; 7158c2ecf20Sopenharmony_ci sg_io_hdr_t *hp; 7168c2ecf20Sopenharmony_ci unsigned char cmnd[SG_MAX_CDB_SIZE]; 7178c2ecf20Sopenharmony_ci int timeout; 7188c2ecf20Sopenharmony_ci unsigned long ul_timeout; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci if (count < SZ_SG_IO_HDR) 7218c2ecf20Sopenharmony_ci return -EINVAL; 7228c2ecf20Sopenharmony_ci 7238c2ecf20Sopenharmony_ci sfp->cmd_q = 1; /* when sg_io_hdr seen, set command queuing on */ 7248c2ecf20Sopenharmony_ci if (!(srp = sg_add_request(sfp))) { 7258c2ecf20Sopenharmony_ci SCSI_LOG_TIMEOUT(1, sg_printk(KERN_INFO, sfp->parentdp, 7268c2ecf20Sopenharmony_ci "sg_new_write: queue full\n")); 7278c2ecf20Sopenharmony_ci return -EDOM; 7288c2ecf20Sopenharmony_ci } 7298c2ecf20Sopenharmony_ci srp->sg_io_owned = sg_io_owned; 7308c2ecf20Sopenharmony_ci hp = &srp->header; 7318c2ecf20Sopenharmony_ci if (get_sg_io_hdr(hp, buf)) { 7328c2ecf20Sopenharmony_ci sg_remove_request(sfp, srp); 7338c2ecf20Sopenharmony_ci return -EFAULT; 7348c2ecf20Sopenharmony_ci } 7358c2ecf20Sopenharmony_ci if (hp->interface_id != 'S') { 7368c2ecf20Sopenharmony_ci sg_remove_request(sfp, srp); 7378c2ecf20Sopenharmony_ci return -ENOSYS; 7388c2ecf20Sopenharmony_ci } 7398c2ecf20Sopenharmony_ci if (hp->flags & SG_FLAG_MMAP_IO) { 7408c2ecf20Sopenharmony_ci if (hp->dxfer_len > sfp->reserve.bufflen) { 7418c2ecf20Sopenharmony_ci sg_remove_request(sfp, srp); 7428c2ecf20Sopenharmony_ci return -ENOMEM; /* MMAP_IO size must fit in reserve buffer */ 7438c2ecf20Sopenharmony_ci } 7448c2ecf20Sopenharmony_ci if (hp->flags & SG_FLAG_DIRECT_IO) { 7458c2ecf20Sopenharmony_ci sg_remove_request(sfp, srp); 7468c2ecf20Sopenharmony_ci return -EINVAL; /* either MMAP_IO or DIRECT_IO (not both) */ 7478c2ecf20Sopenharmony_ci } 7488c2ecf20Sopenharmony_ci if (sfp->res_in_use) { 7498c2ecf20Sopenharmony_ci sg_remove_request(sfp, srp); 7508c2ecf20Sopenharmony_ci return -EBUSY; /* reserve buffer already being used */ 7518c2ecf20Sopenharmony_ci } 7528c2ecf20Sopenharmony_ci } 7538c2ecf20Sopenharmony_ci ul_timeout = msecs_to_jiffies(srp->header.timeout); 7548c2ecf20Sopenharmony_ci timeout = (ul_timeout < INT_MAX) ? ul_timeout : INT_MAX; 7558c2ecf20Sopenharmony_ci if ((!hp->cmdp) || (hp->cmd_len < 6) || (hp->cmd_len > sizeof (cmnd))) { 7568c2ecf20Sopenharmony_ci sg_remove_request(sfp, srp); 7578c2ecf20Sopenharmony_ci return -EMSGSIZE; 7588c2ecf20Sopenharmony_ci } 7598c2ecf20Sopenharmony_ci if (copy_from_user(cmnd, hp->cmdp, hp->cmd_len)) { 7608c2ecf20Sopenharmony_ci sg_remove_request(sfp, srp); 7618c2ecf20Sopenharmony_ci return -EFAULT; 7628c2ecf20Sopenharmony_ci } 7638c2ecf20Sopenharmony_ci if (read_only && sg_allow_access(file, cmnd)) { 7648c2ecf20Sopenharmony_ci sg_remove_request(sfp, srp); 7658c2ecf20Sopenharmony_ci return -EPERM; 7668c2ecf20Sopenharmony_ci } 7678c2ecf20Sopenharmony_ci k = sg_common_write(sfp, srp, cmnd, timeout, blocking); 7688c2ecf20Sopenharmony_ci if (k < 0) 7698c2ecf20Sopenharmony_ci return k; 7708c2ecf20Sopenharmony_ci if (o_srp) 7718c2ecf20Sopenharmony_ci *o_srp = srp; 7728c2ecf20Sopenharmony_ci return count; 7738c2ecf20Sopenharmony_ci} 7748c2ecf20Sopenharmony_ci 7758c2ecf20Sopenharmony_cistatic int 7768c2ecf20Sopenharmony_cisg_common_write(Sg_fd * sfp, Sg_request * srp, 7778c2ecf20Sopenharmony_ci unsigned char *cmnd, int timeout, int blocking) 7788c2ecf20Sopenharmony_ci{ 7798c2ecf20Sopenharmony_ci int k, at_head; 7808c2ecf20Sopenharmony_ci Sg_device *sdp = sfp->parentdp; 7818c2ecf20Sopenharmony_ci sg_io_hdr_t *hp = &srp->header; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci srp->data.cmd_opcode = cmnd[0]; /* hold opcode of command */ 7848c2ecf20Sopenharmony_ci hp->status = 0; 7858c2ecf20Sopenharmony_ci hp->masked_status = 0; 7868c2ecf20Sopenharmony_ci hp->msg_status = 0; 7878c2ecf20Sopenharmony_ci hp->info = 0; 7888c2ecf20Sopenharmony_ci hp->host_status = 0; 7898c2ecf20Sopenharmony_ci hp->driver_status = 0; 7908c2ecf20Sopenharmony_ci hp->resid = 0; 7918c2ecf20Sopenharmony_ci SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sfp->parentdp, 7928c2ecf20Sopenharmony_ci "sg_common_write: scsi opcode=0x%02x, cmd_size=%d\n", 7938c2ecf20Sopenharmony_ci (int) cmnd[0], (int) hp->cmd_len)); 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci if (hp->dxfer_len >= SZ_256M) { 7968c2ecf20Sopenharmony_ci sg_remove_request(sfp, srp); 7978c2ecf20Sopenharmony_ci return -EINVAL; 7988c2ecf20Sopenharmony_ci } 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci k = sg_start_req(srp, cmnd); 8018c2ecf20Sopenharmony_ci if (k) { 8028c2ecf20Sopenharmony_ci SCSI_LOG_TIMEOUT(1, sg_printk(KERN_INFO, sfp->parentdp, 8038c2ecf20Sopenharmony_ci "sg_common_write: start_req err=%d\n", k)); 8048c2ecf20Sopenharmony_ci sg_finish_rem_req(srp); 8058c2ecf20Sopenharmony_ci sg_remove_request(sfp, srp); 8068c2ecf20Sopenharmony_ci return k; /* probably out of space --> ENOMEM */ 8078c2ecf20Sopenharmony_ci } 8088c2ecf20Sopenharmony_ci if (atomic_read(&sdp->detaching)) { 8098c2ecf20Sopenharmony_ci if (srp->bio) { 8108c2ecf20Sopenharmony_ci scsi_req_free_cmd(scsi_req(srp->rq)); 8118c2ecf20Sopenharmony_ci blk_put_request(srp->rq); 8128c2ecf20Sopenharmony_ci srp->rq = NULL; 8138c2ecf20Sopenharmony_ci } 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci sg_finish_rem_req(srp); 8168c2ecf20Sopenharmony_ci sg_remove_request(sfp, srp); 8178c2ecf20Sopenharmony_ci return -ENODEV; 8188c2ecf20Sopenharmony_ci } 8198c2ecf20Sopenharmony_ci 8208c2ecf20Sopenharmony_ci hp->duration = jiffies_to_msecs(jiffies); 8218c2ecf20Sopenharmony_ci if (hp->interface_id != '\0' && /* v3 (or later) interface */ 8228c2ecf20Sopenharmony_ci (SG_FLAG_Q_AT_TAIL & hp->flags)) 8238c2ecf20Sopenharmony_ci at_head = 0; 8248c2ecf20Sopenharmony_ci else 8258c2ecf20Sopenharmony_ci at_head = 1; 8268c2ecf20Sopenharmony_ci 8278c2ecf20Sopenharmony_ci srp->rq->timeout = timeout; 8288c2ecf20Sopenharmony_ci kref_get(&sfp->f_ref); /* sg_rq_end_io() does kref_put(). */ 8298c2ecf20Sopenharmony_ci blk_execute_rq_nowait(sdp->device->request_queue, sdp->disk, 8308c2ecf20Sopenharmony_ci srp->rq, at_head, sg_rq_end_io); 8318c2ecf20Sopenharmony_ci return 0; 8328c2ecf20Sopenharmony_ci} 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_cistatic int srp_done(Sg_fd *sfp, Sg_request *srp) 8358c2ecf20Sopenharmony_ci{ 8368c2ecf20Sopenharmony_ci unsigned long flags; 8378c2ecf20Sopenharmony_ci int ret; 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci read_lock_irqsave(&sfp->rq_list_lock, flags); 8408c2ecf20Sopenharmony_ci ret = srp->done; 8418c2ecf20Sopenharmony_ci read_unlock_irqrestore(&sfp->rq_list_lock, flags); 8428c2ecf20Sopenharmony_ci return ret; 8438c2ecf20Sopenharmony_ci} 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_cistatic int max_sectors_bytes(struct request_queue *q) 8468c2ecf20Sopenharmony_ci{ 8478c2ecf20Sopenharmony_ci unsigned int max_sectors = queue_max_sectors(q); 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci max_sectors = min_t(unsigned int, max_sectors, INT_MAX >> 9); 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci return max_sectors << 9; 8528c2ecf20Sopenharmony_ci} 8538c2ecf20Sopenharmony_ci 8548c2ecf20Sopenharmony_cistatic void 8558c2ecf20Sopenharmony_cisg_fill_request_table(Sg_fd *sfp, sg_req_info_t *rinfo) 8568c2ecf20Sopenharmony_ci{ 8578c2ecf20Sopenharmony_ci Sg_request *srp; 8588c2ecf20Sopenharmony_ci int val; 8598c2ecf20Sopenharmony_ci unsigned int ms; 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci val = 0; 8628c2ecf20Sopenharmony_ci list_for_each_entry(srp, &sfp->rq_list, entry) { 8638c2ecf20Sopenharmony_ci if (val >= SG_MAX_QUEUE) 8648c2ecf20Sopenharmony_ci break; 8658c2ecf20Sopenharmony_ci rinfo[val].req_state = srp->done + 1; 8668c2ecf20Sopenharmony_ci rinfo[val].problem = 8678c2ecf20Sopenharmony_ci srp->header.masked_status & 8688c2ecf20Sopenharmony_ci srp->header.host_status & 8698c2ecf20Sopenharmony_ci srp->header.driver_status; 8708c2ecf20Sopenharmony_ci if (srp->done) 8718c2ecf20Sopenharmony_ci rinfo[val].duration = 8728c2ecf20Sopenharmony_ci srp->header.duration; 8738c2ecf20Sopenharmony_ci else { 8748c2ecf20Sopenharmony_ci ms = jiffies_to_msecs(jiffies); 8758c2ecf20Sopenharmony_ci rinfo[val].duration = 8768c2ecf20Sopenharmony_ci (ms > srp->header.duration) ? 8778c2ecf20Sopenharmony_ci (ms - srp->header.duration) : 0; 8788c2ecf20Sopenharmony_ci } 8798c2ecf20Sopenharmony_ci rinfo[val].orphan = srp->orphan; 8808c2ecf20Sopenharmony_ci rinfo[val].sg_io_owned = srp->sg_io_owned; 8818c2ecf20Sopenharmony_ci rinfo[val].pack_id = srp->header.pack_id; 8828c2ecf20Sopenharmony_ci rinfo[val].usr_ptr = srp->header.usr_ptr; 8838c2ecf20Sopenharmony_ci val++; 8848c2ecf20Sopenharmony_ci } 8858c2ecf20Sopenharmony_ci} 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 8888c2ecf20Sopenharmony_cistruct compat_sg_req_info { /* used by SG_GET_REQUEST_TABLE ioctl() */ 8898c2ecf20Sopenharmony_ci char req_state; 8908c2ecf20Sopenharmony_ci char orphan; 8918c2ecf20Sopenharmony_ci char sg_io_owned; 8928c2ecf20Sopenharmony_ci char problem; 8938c2ecf20Sopenharmony_ci int pack_id; 8948c2ecf20Sopenharmony_ci compat_uptr_t usr_ptr; 8958c2ecf20Sopenharmony_ci unsigned int duration; 8968c2ecf20Sopenharmony_ci int unused; 8978c2ecf20Sopenharmony_ci}; 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_cistatic int put_compat_request_table(struct compat_sg_req_info __user *o, 9008c2ecf20Sopenharmony_ci struct sg_req_info *rinfo) 9018c2ecf20Sopenharmony_ci{ 9028c2ecf20Sopenharmony_ci int i; 9038c2ecf20Sopenharmony_ci for (i = 0; i < SG_MAX_QUEUE; i++) { 9048c2ecf20Sopenharmony_ci if (copy_to_user(o + i, rinfo + i, offsetof(sg_req_info_t, usr_ptr)) || 9058c2ecf20Sopenharmony_ci put_user((uintptr_t)rinfo[i].usr_ptr, &o[i].usr_ptr) || 9068c2ecf20Sopenharmony_ci put_user(rinfo[i].duration, &o[i].duration) || 9078c2ecf20Sopenharmony_ci put_user(rinfo[i].unused, &o[i].unused)) 9088c2ecf20Sopenharmony_ci return -EFAULT; 9098c2ecf20Sopenharmony_ci } 9108c2ecf20Sopenharmony_ci return 0; 9118c2ecf20Sopenharmony_ci} 9128c2ecf20Sopenharmony_ci#endif 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_cistatic long 9158c2ecf20Sopenharmony_cisg_ioctl_common(struct file *filp, Sg_device *sdp, Sg_fd *sfp, 9168c2ecf20Sopenharmony_ci unsigned int cmd_in, void __user *p) 9178c2ecf20Sopenharmony_ci{ 9188c2ecf20Sopenharmony_ci int __user *ip = p; 9198c2ecf20Sopenharmony_ci int result, val, read_only; 9208c2ecf20Sopenharmony_ci Sg_request *srp; 9218c2ecf20Sopenharmony_ci unsigned long iflags; 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, 9248c2ecf20Sopenharmony_ci "sg_ioctl: cmd=0x%x\n", (int) cmd_in)); 9258c2ecf20Sopenharmony_ci read_only = (O_RDWR != (filp->f_flags & O_ACCMODE)); 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci switch (cmd_in) { 9288c2ecf20Sopenharmony_ci case SG_IO: 9298c2ecf20Sopenharmony_ci if (atomic_read(&sdp->detaching)) 9308c2ecf20Sopenharmony_ci return -ENODEV; 9318c2ecf20Sopenharmony_ci if (!scsi_block_when_processing_errors(sdp->device)) 9328c2ecf20Sopenharmony_ci return -ENXIO; 9338c2ecf20Sopenharmony_ci result = sg_new_write(sfp, filp, p, SZ_SG_IO_HDR, 9348c2ecf20Sopenharmony_ci 1, read_only, 1, &srp); 9358c2ecf20Sopenharmony_ci if (result < 0) 9368c2ecf20Sopenharmony_ci return result; 9378c2ecf20Sopenharmony_ci result = wait_event_interruptible(sfp->read_wait, 9388c2ecf20Sopenharmony_ci srp_done(sfp, srp)); 9398c2ecf20Sopenharmony_ci write_lock_irq(&sfp->rq_list_lock); 9408c2ecf20Sopenharmony_ci if (srp->done) { 9418c2ecf20Sopenharmony_ci srp->done = 2; 9428c2ecf20Sopenharmony_ci write_unlock_irq(&sfp->rq_list_lock); 9438c2ecf20Sopenharmony_ci result = sg_new_read(sfp, p, SZ_SG_IO_HDR, srp); 9448c2ecf20Sopenharmony_ci return (result < 0) ? result : 0; 9458c2ecf20Sopenharmony_ci } 9468c2ecf20Sopenharmony_ci srp->orphan = 1; 9478c2ecf20Sopenharmony_ci write_unlock_irq(&sfp->rq_list_lock); 9488c2ecf20Sopenharmony_ci return result; /* -ERESTARTSYS because signal hit process */ 9498c2ecf20Sopenharmony_ci case SG_SET_TIMEOUT: 9508c2ecf20Sopenharmony_ci result = get_user(val, ip); 9518c2ecf20Sopenharmony_ci if (result) 9528c2ecf20Sopenharmony_ci return result; 9538c2ecf20Sopenharmony_ci if (val < 0) 9548c2ecf20Sopenharmony_ci return -EIO; 9558c2ecf20Sopenharmony_ci if (val >= mult_frac((s64)INT_MAX, USER_HZ, HZ)) 9568c2ecf20Sopenharmony_ci val = min_t(s64, mult_frac((s64)INT_MAX, USER_HZ, HZ), 9578c2ecf20Sopenharmony_ci INT_MAX); 9588c2ecf20Sopenharmony_ci sfp->timeout_user = val; 9598c2ecf20Sopenharmony_ci sfp->timeout = mult_frac(val, HZ, USER_HZ); 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci return 0; 9628c2ecf20Sopenharmony_ci case SG_GET_TIMEOUT: /* N.B. User receives timeout as return value */ 9638c2ecf20Sopenharmony_ci /* strange ..., for backward compatibility */ 9648c2ecf20Sopenharmony_ci return sfp->timeout_user; 9658c2ecf20Sopenharmony_ci case SG_SET_FORCE_LOW_DMA: 9668c2ecf20Sopenharmony_ci /* 9678c2ecf20Sopenharmony_ci * N.B. This ioctl never worked properly, but failed to 9688c2ecf20Sopenharmony_ci * return an error value. So returning '0' to keep compability 9698c2ecf20Sopenharmony_ci * with legacy applications. 9708c2ecf20Sopenharmony_ci */ 9718c2ecf20Sopenharmony_ci return 0; 9728c2ecf20Sopenharmony_ci case SG_GET_LOW_DMA: 9738c2ecf20Sopenharmony_ci return put_user((int) sdp->device->host->unchecked_isa_dma, ip); 9748c2ecf20Sopenharmony_ci case SG_GET_SCSI_ID: 9758c2ecf20Sopenharmony_ci { 9768c2ecf20Sopenharmony_ci sg_scsi_id_t v; 9778c2ecf20Sopenharmony_ci 9788c2ecf20Sopenharmony_ci if (atomic_read(&sdp->detaching)) 9798c2ecf20Sopenharmony_ci return -ENODEV; 9808c2ecf20Sopenharmony_ci memset(&v, 0, sizeof(v)); 9818c2ecf20Sopenharmony_ci v.host_no = sdp->device->host->host_no; 9828c2ecf20Sopenharmony_ci v.channel = sdp->device->channel; 9838c2ecf20Sopenharmony_ci v.scsi_id = sdp->device->id; 9848c2ecf20Sopenharmony_ci v.lun = sdp->device->lun; 9858c2ecf20Sopenharmony_ci v.scsi_type = sdp->device->type; 9868c2ecf20Sopenharmony_ci v.h_cmd_per_lun = sdp->device->host->cmd_per_lun; 9878c2ecf20Sopenharmony_ci v.d_queue_depth = sdp->device->queue_depth; 9888c2ecf20Sopenharmony_ci if (copy_to_user(p, &v, sizeof(sg_scsi_id_t))) 9898c2ecf20Sopenharmony_ci return -EFAULT; 9908c2ecf20Sopenharmony_ci return 0; 9918c2ecf20Sopenharmony_ci } 9928c2ecf20Sopenharmony_ci case SG_SET_FORCE_PACK_ID: 9938c2ecf20Sopenharmony_ci result = get_user(val, ip); 9948c2ecf20Sopenharmony_ci if (result) 9958c2ecf20Sopenharmony_ci return result; 9968c2ecf20Sopenharmony_ci sfp->force_packid = val ? 1 : 0; 9978c2ecf20Sopenharmony_ci return 0; 9988c2ecf20Sopenharmony_ci case SG_GET_PACK_ID: 9998c2ecf20Sopenharmony_ci read_lock_irqsave(&sfp->rq_list_lock, iflags); 10008c2ecf20Sopenharmony_ci list_for_each_entry(srp, &sfp->rq_list, entry) { 10018c2ecf20Sopenharmony_ci if ((1 == srp->done) && (!srp->sg_io_owned)) { 10028c2ecf20Sopenharmony_ci read_unlock_irqrestore(&sfp->rq_list_lock, 10038c2ecf20Sopenharmony_ci iflags); 10048c2ecf20Sopenharmony_ci return put_user(srp->header.pack_id, ip); 10058c2ecf20Sopenharmony_ci } 10068c2ecf20Sopenharmony_ci } 10078c2ecf20Sopenharmony_ci read_unlock_irqrestore(&sfp->rq_list_lock, iflags); 10088c2ecf20Sopenharmony_ci return put_user(-1, ip); 10098c2ecf20Sopenharmony_ci case SG_GET_NUM_WAITING: 10108c2ecf20Sopenharmony_ci read_lock_irqsave(&sfp->rq_list_lock, iflags); 10118c2ecf20Sopenharmony_ci val = 0; 10128c2ecf20Sopenharmony_ci list_for_each_entry(srp, &sfp->rq_list, entry) { 10138c2ecf20Sopenharmony_ci if ((1 == srp->done) && (!srp->sg_io_owned)) 10148c2ecf20Sopenharmony_ci ++val; 10158c2ecf20Sopenharmony_ci } 10168c2ecf20Sopenharmony_ci read_unlock_irqrestore(&sfp->rq_list_lock, iflags); 10178c2ecf20Sopenharmony_ci return put_user(val, ip); 10188c2ecf20Sopenharmony_ci case SG_GET_SG_TABLESIZE: 10198c2ecf20Sopenharmony_ci return put_user(sdp->sg_tablesize, ip); 10208c2ecf20Sopenharmony_ci case SG_SET_RESERVED_SIZE: 10218c2ecf20Sopenharmony_ci result = get_user(val, ip); 10228c2ecf20Sopenharmony_ci if (result) 10238c2ecf20Sopenharmony_ci return result; 10248c2ecf20Sopenharmony_ci if (val < 0) 10258c2ecf20Sopenharmony_ci return -EINVAL; 10268c2ecf20Sopenharmony_ci val = min_t(int, val, 10278c2ecf20Sopenharmony_ci max_sectors_bytes(sdp->device->request_queue)); 10288c2ecf20Sopenharmony_ci mutex_lock(&sfp->f_mutex); 10298c2ecf20Sopenharmony_ci if (val != sfp->reserve.bufflen) { 10308c2ecf20Sopenharmony_ci if (sfp->mmap_called || 10318c2ecf20Sopenharmony_ci sfp->res_in_use) { 10328c2ecf20Sopenharmony_ci mutex_unlock(&sfp->f_mutex); 10338c2ecf20Sopenharmony_ci return -EBUSY; 10348c2ecf20Sopenharmony_ci } 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci sg_remove_scat(sfp, &sfp->reserve); 10378c2ecf20Sopenharmony_ci sg_build_reserve(sfp, val); 10388c2ecf20Sopenharmony_ci } 10398c2ecf20Sopenharmony_ci mutex_unlock(&sfp->f_mutex); 10408c2ecf20Sopenharmony_ci return 0; 10418c2ecf20Sopenharmony_ci case SG_GET_RESERVED_SIZE: 10428c2ecf20Sopenharmony_ci val = min_t(int, sfp->reserve.bufflen, 10438c2ecf20Sopenharmony_ci max_sectors_bytes(sdp->device->request_queue)); 10448c2ecf20Sopenharmony_ci return put_user(val, ip); 10458c2ecf20Sopenharmony_ci case SG_SET_COMMAND_Q: 10468c2ecf20Sopenharmony_ci result = get_user(val, ip); 10478c2ecf20Sopenharmony_ci if (result) 10488c2ecf20Sopenharmony_ci return result; 10498c2ecf20Sopenharmony_ci sfp->cmd_q = val ? 1 : 0; 10508c2ecf20Sopenharmony_ci return 0; 10518c2ecf20Sopenharmony_ci case SG_GET_COMMAND_Q: 10528c2ecf20Sopenharmony_ci return put_user((int) sfp->cmd_q, ip); 10538c2ecf20Sopenharmony_ci case SG_SET_KEEP_ORPHAN: 10548c2ecf20Sopenharmony_ci result = get_user(val, ip); 10558c2ecf20Sopenharmony_ci if (result) 10568c2ecf20Sopenharmony_ci return result; 10578c2ecf20Sopenharmony_ci sfp->keep_orphan = val; 10588c2ecf20Sopenharmony_ci return 0; 10598c2ecf20Sopenharmony_ci case SG_GET_KEEP_ORPHAN: 10608c2ecf20Sopenharmony_ci return put_user((int) sfp->keep_orphan, ip); 10618c2ecf20Sopenharmony_ci case SG_NEXT_CMD_LEN: 10628c2ecf20Sopenharmony_ci result = get_user(val, ip); 10638c2ecf20Sopenharmony_ci if (result) 10648c2ecf20Sopenharmony_ci return result; 10658c2ecf20Sopenharmony_ci if (val > SG_MAX_CDB_SIZE) 10668c2ecf20Sopenharmony_ci return -ENOMEM; 10678c2ecf20Sopenharmony_ci sfp->next_cmd_len = (val > 0) ? val : 0; 10688c2ecf20Sopenharmony_ci return 0; 10698c2ecf20Sopenharmony_ci case SG_GET_VERSION_NUM: 10708c2ecf20Sopenharmony_ci return put_user(sg_version_num, ip); 10718c2ecf20Sopenharmony_ci case SG_GET_ACCESS_COUNT: 10728c2ecf20Sopenharmony_ci /* faked - we don't have a real access count anymore */ 10738c2ecf20Sopenharmony_ci val = (sdp->device ? 1 : 0); 10748c2ecf20Sopenharmony_ci return put_user(val, ip); 10758c2ecf20Sopenharmony_ci case SG_GET_REQUEST_TABLE: 10768c2ecf20Sopenharmony_ci { 10778c2ecf20Sopenharmony_ci sg_req_info_t *rinfo; 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci rinfo = kcalloc(SG_MAX_QUEUE, SZ_SG_REQ_INFO, 10808c2ecf20Sopenharmony_ci GFP_KERNEL); 10818c2ecf20Sopenharmony_ci if (!rinfo) 10828c2ecf20Sopenharmony_ci return -ENOMEM; 10838c2ecf20Sopenharmony_ci read_lock_irqsave(&sfp->rq_list_lock, iflags); 10848c2ecf20Sopenharmony_ci sg_fill_request_table(sfp, rinfo); 10858c2ecf20Sopenharmony_ci read_unlock_irqrestore(&sfp->rq_list_lock, iflags); 10868c2ecf20Sopenharmony_ci #ifdef CONFIG_COMPAT 10878c2ecf20Sopenharmony_ci if (in_compat_syscall()) 10888c2ecf20Sopenharmony_ci result = put_compat_request_table(p, rinfo); 10898c2ecf20Sopenharmony_ci else 10908c2ecf20Sopenharmony_ci #endif 10918c2ecf20Sopenharmony_ci result = copy_to_user(p, rinfo, 10928c2ecf20Sopenharmony_ci SZ_SG_REQ_INFO * SG_MAX_QUEUE); 10938c2ecf20Sopenharmony_ci result = result ? -EFAULT : 0; 10948c2ecf20Sopenharmony_ci kfree(rinfo); 10958c2ecf20Sopenharmony_ci return result; 10968c2ecf20Sopenharmony_ci } 10978c2ecf20Sopenharmony_ci case SG_EMULATED_HOST: 10988c2ecf20Sopenharmony_ci if (atomic_read(&sdp->detaching)) 10998c2ecf20Sopenharmony_ci return -ENODEV; 11008c2ecf20Sopenharmony_ci return put_user(sdp->device->host->hostt->emulated, ip); 11018c2ecf20Sopenharmony_ci case SCSI_IOCTL_SEND_COMMAND: 11028c2ecf20Sopenharmony_ci if (atomic_read(&sdp->detaching)) 11038c2ecf20Sopenharmony_ci return -ENODEV; 11048c2ecf20Sopenharmony_ci return sg_scsi_ioctl(sdp->device->request_queue, NULL, filp->f_mode, p); 11058c2ecf20Sopenharmony_ci case SG_SET_DEBUG: 11068c2ecf20Sopenharmony_ci result = get_user(val, ip); 11078c2ecf20Sopenharmony_ci if (result) 11088c2ecf20Sopenharmony_ci return result; 11098c2ecf20Sopenharmony_ci sdp->sgdebug = (char) val; 11108c2ecf20Sopenharmony_ci return 0; 11118c2ecf20Sopenharmony_ci case BLKSECTGET: 11128c2ecf20Sopenharmony_ci return put_user(max_sectors_bytes(sdp->device->request_queue), 11138c2ecf20Sopenharmony_ci ip); 11148c2ecf20Sopenharmony_ci case BLKTRACESETUP: 11158c2ecf20Sopenharmony_ci return blk_trace_setup(sdp->device->request_queue, 11168c2ecf20Sopenharmony_ci sdp->disk->disk_name, 11178c2ecf20Sopenharmony_ci MKDEV(SCSI_GENERIC_MAJOR, sdp->index), 11188c2ecf20Sopenharmony_ci NULL, p); 11198c2ecf20Sopenharmony_ci case BLKTRACESTART: 11208c2ecf20Sopenharmony_ci return blk_trace_startstop(sdp->device->request_queue, 1); 11218c2ecf20Sopenharmony_ci case BLKTRACESTOP: 11228c2ecf20Sopenharmony_ci return blk_trace_startstop(sdp->device->request_queue, 0); 11238c2ecf20Sopenharmony_ci case BLKTRACETEARDOWN: 11248c2ecf20Sopenharmony_ci return blk_trace_remove(sdp->device->request_queue); 11258c2ecf20Sopenharmony_ci case SCSI_IOCTL_GET_IDLUN: 11268c2ecf20Sopenharmony_ci case SCSI_IOCTL_GET_BUS_NUMBER: 11278c2ecf20Sopenharmony_ci case SCSI_IOCTL_PROBE_HOST: 11288c2ecf20Sopenharmony_ci case SG_GET_TRANSFORM: 11298c2ecf20Sopenharmony_ci case SG_SCSI_RESET: 11308c2ecf20Sopenharmony_ci if (atomic_read(&sdp->detaching)) 11318c2ecf20Sopenharmony_ci return -ENODEV; 11328c2ecf20Sopenharmony_ci break; 11338c2ecf20Sopenharmony_ci default: 11348c2ecf20Sopenharmony_ci if (read_only) 11358c2ecf20Sopenharmony_ci return -EPERM; /* don't know so take safe approach */ 11368c2ecf20Sopenharmony_ci break; 11378c2ecf20Sopenharmony_ci } 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci result = scsi_ioctl_block_when_processing_errors(sdp->device, 11408c2ecf20Sopenharmony_ci cmd_in, filp->f_flags & O_NDELAY); 11418c2ecf20Sopenharmony_ci if (result) 11428c2ecf20Sopenharmony_ci return result; 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci return -ENOIOCTLCMD; 11458c2ecf20Sopenharmony_ci} 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_cistatic long 11488c2ecf20Sopenharmony_cisg_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) 11498c2ecf20Sopenharmony_ci{ 11508c2ecf20Sopenharmony_ci void __user *p = (void __user *)arg; 11518c2ecf20Sopenharmony_ci Sg_device *sdp; 11528c2ecf20Sopenharmony_ci Sg_fd *sfp; 11538c2ecf20Sopenharmony_ci int ret; 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp))) 11568c2ecf20Sopenharmony_ci return -ENXIO; 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci ret = sg_ioctl_common(filp, sdp, sfp, cmd_in, p); 11598c2ecf20Sopenharmony_ci if (ret != -ENOIOCTLCMD) 11608c2ecf20Sopenharmony_ci return ret; 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_ci return scsi_ioctl(sdp->device, cmd_in, p); 11638c2ecf20Sopenharmony_ci} 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 11668c2ecf20Sopenharmony_cistatic long sg_compat_ioctl(struct file *filp, unsigned int cmd_in, unsigned long arg) 11678c2ecf20Sopenharmony_ci{ 11688c2ecf20Sopenharmony_ci void __user *p = compat_ptr(arg); 11698c2ecf20Sopenharmony_ci Sg_device *sdp; 11708c2ecf20Sopenharmony_ci Sg_fd *sfp; 11718c2ecf20Sopenharmony_ci int ret; 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp))) 11748c2ecf20Sopenharmony_ci return -ENXIO; 11758c2ecf20Sopenharmony_ci 11768c2ecf20Sopenharmony_ci ret = sg_ioctl_common(filp, sdp, sfp, cmd_in, p); 11778c2ecf20Sopenharmony_ci if (ret != -ENOIOCTLCMD) 11788c2ecf20Sopenharmony_ci return ret; 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci return scsi_compat_ioctl(sdp->device, cmd_in, p); 11818c2ecf20Sopenharmony_ci} 11828c2ecf20Sopenharmony_ci#endif 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_cistatic __poll_t 11858c2ecf20Sopenharmony_cisg_poll(struct file *filp, poll_table * wait) 11868c2ecf20Sopenharmony_ci{ 11878c2ecf20Sopenharmony_ci __poll_t res = 0; 11888c2ecf20Sopenharmony_ci Sg_device *sdp; 11898c2ecf20Sopenharmony_ci Sg_fd *sfp; 11908c2ecf20Sopenharmony_ci Sg_request *srp; 11918c2ecf20Sopenharmony_ci int count = 0; 11928c2ecf20Sopenharmony_ci unsigned long iflags; 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci sfp = filp->private_data; 11958c2ecf20Sopenharmony_ci if (!sfp) 11968c2ecf20Sopenharmony_ci return EPOLLERR; 11978c2ecf20Sopenharmony_ci sdp = sfp->parentdp; 11988c2ecf20Sopenharmony_ci if (!sdp) 11998c2ecf20Sopenharmony_ci return EPOLLERR; 12008c2ecf20Sopenharmony_ci poll_wait(filp, &sfp->read_wait, wait); 12018c2ecf20Sopenharmony_ci read_lock_irqsave(&sfp->rq_list_lock, iflags); 12028c2ecf20Sopenharmony_ci list_for_each_entry(srp, &sfp->rq_list, entry) { 12038c2ecf20Sopenharmony_ci /* if any read waiting, flag it */ 12048c2ecf20Sopenharmony_ci if ((0 == res) && (1 == srp->done) && (!srp->sg_io_owned)) 12058c2ecf20Sopenharmony_ci res = EPOLLIN | EPOLLRDNORM; 12068c2ecf20Sopenharmony_ci ++count; 12078c2ecf20Sopenharmony_ci } 12088c2ecf20Sopenharmony_ci read_unlock_irqrestore(&sfp->rq_list_lock, iflags); 12098c2ecf20Sopenharmony_ci 12108c2ecf20Sopenharmony_ci if (atomic_read(&sdp->detaching)) 12118c2ecf20Sopenharmony_ci res |= EPOLLHUP; 12128c2ecf20Sopenharmony_ci else if (!sfp->cmd_q) { 12138c2ecf20Sopenharmony_ci if (0 == count) 12148c2ecf20Sopenharmony_ci res |= EPOLLOUT | EPOLLWRNORM; 12158c2ecf20Sopenharmony_ci } else if (count < SG_MAX_QUEUE) 12168c2ecf20Sopenharmony_ci res |= EPOLLOUT | EPOLLWRNORM; 12178c2ecf20Sopenharmony_ci SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, 12188c2ecf20Sopenharmony_ci "sg_poll: res=0x%x\n", (__force u32) res)); 12198c2ecf20Sopenharmony_ci return res; 12208c2ecf20Sopenharmony_ci} 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_cistatic int 12238c2ecf20Sopenharmony_cisg_fasync(int fd, struct file *filp, int mode) 12248c2ecf20Sopenharmony_ci{ 12258c2ecf20Sopenharmony_ci Sg_device *sdp; 12268c2ecf20Sopenharmony_ci Sg_fd *sfp; 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp))) 12298c2ecf20Sopenharmony_ci return -ENXIO; 12308c2ecf20Sopenharmony_ci SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, 12318c2ecf20Sopenharmony_ci "sg_fasync: mode=%d\n", mode)); 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci return fasync_helper(fd, filp, mode, &sfp->async_qp); 12348c2ecf20Sopenharmony_ci} 12358c2ecf20Sopenharmony_ci 12368c2ecf20Sopenharmony_cistatic vm_fault_t 12378c2ecf20Sopenharmony_cisg_vma_fault(struct vm_fault *vmf) 12388c2ecf20Sopenharmony_ci{ 12398c2ecf20Sopenharmony_ci struct vm_area_struct *vma = vmf->vma; 12408c2ecf20Sopenharmony_ci Sg_fd *sfp; 12418c2ecf20Sopenharmony_ci unsigned long offset, len, sa; 12428c2ecf20Sopenharmony_ci Sg_scatter_hold *rsv_schp; 12438c2ecf20Sopenharmony_ci int k, length; 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_ci if ((NULL == vma) || (!(sfp = (Sg_fd *) vma->vm_private_data))) 12468c2ecf20Sopenharmony_ci return VM_FAULT_SIGBUS; 12478c2ecf20Sopenharmony_ci rsv_schp = &sfp->reserve; 12488c2ecf20Sopenharmony_ci offset = vmf->pgoff << PAGE_SHIFT; 12498c2ecf20Sopenharmony_ci if (offset >= rsv_schp->bufflen) 12508c2ecf20Sopenharmony_ci return VM_FAULT_SIGBUS; 12518c2ecf20Sopenharmony_ci SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sfp->parentdp, 12528c2ecf20Sopenharmony_ci "sg_vma_fault: offset=%lu, scatg=%d\n", 12538c2ecf20Sopenharmony_ci offset, rsv_schp->k_use_sg)); 12548c2ecf20Sopenharmony_ci sa = vma->vm_start; 12558c2ecf20Sopenharmony_ci length = 1 << (PAGE_SHIFT + rsv_schp->page_order); 12568c2ecf20Sopenharmony_ci for (k = 0; k < rsv_schp->k_use_sg && sa < vma->vm_end; k++) { 12578c2ecf20Sopenharmony_ci len = vma->vm_end - sa; 12588c2ecf20Sopenharmony_ci len = (len < length) ? len : length; 12598c2ecf20Sopenharmony_ci if (offset < len) { 12608c2ecf20Sopenharmony_ci struct page *page = nth_page(rsv_schp->pages[k], 12618c2ecf20Sopenharmony_ci offset >> PAGE_SHIFT); 12628c2ecf20Sopenharmony_ci get_page(page); /* increment page count */ 12638c2ecf20Sopenharmony_ci vmf->page = page; 12648c2ecf20Sopenharmony_ci return 0; /* success */ 12658c2ecf20Sopenharmony_ci } 12668c2ecf20Sopenharmony_ci sa += len; 12678c2ecf20Sopenharmony_ci offset -= len; 12688c2ecf20Sopenharmony_ci } 12698c2ecf20Sopenharmony_ci 12708c2ecf20Sopenharmony_ci return VM_FAULT_SIGBUS; 12718c2ecf20Sopenharmony_ci} 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_cistatic const struct vm_operations_struct sg_mmap_vm_ops = { 12748c2ecf20Sopenharmony_ci .fault = sg_vma_fault, 12758c2ecf20Sopenharmony_ci}; 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_cistatic int 12788c2ecf20Sopenharmony_cisg_mmap(struct file *filp, struct vm_area_struct *vma) 12798c2ecf20Sopenharmony_ci{ 12808c2ecf20Sopenharmony_ci Sg_fd *sfp; 12818c2ecf20Sopenharmony_ci unsigned long req_sz, len, sa; 12828c2ecf20Sopenharmony_ci Sg_scatter_hold *rsv_schp; 12838c2ecf20Sopenharmony_ci int k, length; 12848c2ecf20Sopenharmony_ci int ret = 0; 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci if ((!filp) || (!vma) || (!(sfp = (Sg_fd *) filp->private_data))) 12878c2ecf20Sopenharmony_ci return -ENXIO; 12888c2ecf20Sopenharmony_ci req_sz = vma->vm_end - vma->vm_start; 12898c2ecf20Sopenharmony_ci SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sfp->parentdp, 12908c2ecf20Sopenharmony_ci "sg_mmap starting, vm_start=%p, len=%d\n", 12918c2ecf20Sopenharmony_ci (void *) vma->vm_start, (int) req_sz)); 12928c2ecf20Sopenharmony_ci if (vma->vm_pgoff) 12938c2ecf20Sopenharmony_ci return -EINVAL; /* want no offset */ 12948c2ecf20Sopenharmony_ci rsv_schp = &sfp->reserve; 12958c2ecf20Sopenharmony_ci mutex_lock(&sfp->f_mutex); 12968c2ecf20Sopenharmony_ci if (req_sz > rsv_schp->bufflen) { 12978c2ecf20Sopenharmony_ci ret = -ENOMEM; /* cannot map more than reserved buffer */ 12988c2ecf20Sopenharmony_ci goto out; 12998c2ecf20Sopenharmony_ci } 13008c2ecf20Sopenharmony_ci 13018c2ecf20Sopenharmony_ci sa = vma->vm_start; 13028c2ecf20Sopenharmony_ci length = 1 << (PAGE_SHIFT + rsv_schp->page_order); 13038c2ecf20Sopenharmony_ci for (k = 0; k < rsv_schp->k_use_sg && sa < vma->vm_end; k++) { 13048c2ecf20Sopenharmony_ci len = vma->vm_end - sa; 13058c2ecf20Sopenharmony_ci len = (len < length) ? len : length; 13068c2ecf20Sopenharmony_ci sa += len; 13078c2ecf20Sopenharmony_ci } 13088c2ecf20Sopenharmony_ci 13098c2ecf20Sopenharmony_ci sfp->mmap_called = 1; 13108c2ecf20Sopenharmony_ci vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP; 13118c2ecf20Sopenharmony_ci vma->vm_private_data = sfp; 13128c2ecf20Sopenharmony_ci vma->vm_ops = &sg_mmap_vm_ops; 13138c2ecf20Sopenharmony_ciout: 13148c2ecf20Sopenharmony_ci mutex_unlock(&sfp->f_mutex); 13158c2ecf20Sopenharmony_ci return ret; 13168c2ecf20Sopenharmony_ci} 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_cistatic void 13198c2ecf20Sopenharmony_cisg_rq_end_io_usercontext(struct work_struct *work) 13208c2ecf20Sopenharmony_ci{ 13218c2ecf20Sopenharmony_ci struct sg_request *srp = container_of(work, struct sg_request, ew.work); 13228c2ecf20Sopenharmony_ci struct sg_fd *sfp = srp->parentfp; 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci sg_finish_rem_req(srp); 13258c2ecf20Sopenharmony_ci sg_remove_request(sfp, srp); 13268c2ecf20Sopenharmony_ci kref_put(&sfp->f_ref, sg_remove_sfp); 13278c2ecf20Sopenharmony_ci} 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_ci/* 13308c2ecf20Sopenharmony_ci * This function is a "bottom half" handler that is called by the mid 13318c2ecf20Sopenharmony_ci * level when a command is completed (or has failed). 13328c2ecf20Sopenharmony_ci */ 13338c2ecf20Sopenharmony_cistatic void 13348c2ecf20Sopenharmony_cisg_rq_end_io(struct request *rq, blk_status_t status) 13358c2ecf20Sopenharmony_ci{ 13368c2ecf20Sopenharmony_ci struct sg_request *srp = rq->end_io_data; 13378c2ecf20Sopenharmony_ci struct scsi_request *req = scsi_req(rq); 13388c2ecf20Sopenharmony_ci Sg_device *sdp; 13398c2ecf20Sopenharmony_ci Sg_fd *sfp; 13408c2ecf20Sopenharmony_ci unsigned long iflags; 13418c2ecf20Sopenharmony_ci unsigned int ms; 13428c2ecf20Sopenharmony_ci char *sense; 13438c2ecf20Sopenharmony_ci int result, resid, done = 1; 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_ci if (WARN_ON(srp->done != 0)) 13468c2ecf20Sopenharmony_ci return; 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_ci sfp = srp->parentfp; 13498c2ecf20Sopenharmony_ci if (WARN_ON(sfp == NULL)) 13508c2ecf20Sopenharmony_ci return; 13518c2ecf20Sopenharmony_ci 13528c2ecf20Sopenharmony_ci sdp = sfp->parentdp; 13538c2ecf20Sopenharmony_ci if (unlikely(atomic_read(&sdp->detaching))) 13548c2ecf20Sopenharmony_ci pr_info("%s: device detaching\n", __func__); 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ci sense = req->sense; 13578c2ecf20Sopenharmony_ci result = req->result; 13588c2ecf20Sopenharmony_ci resid = req->resid_len; 13598c2ecf20Sopenharmony_ci 13608c2ecf20Sopenharmony_ci SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sdp, 13618c2ecf20Sopenharmony_ci "sg_cmd_done: pack_id=%d, res=0x%x\n", 13628c2ecf20Sopenharmony_ci srp->header.pack_id, result)); 13638c2ecf20Sopenharmony_ci srp->header.resid = resid; 13648c2ecf20Sopenharmony_ci ms = jiffies_to_msecs(jiffies); 13658c2ecf20Sopenharmony_ci srp->header.duration = (ms > srp->header.duration) ? 13668c2ecf20Sopenharmony_ci (ms - srp->header.duration) : 0; 13678c2ecf20Sopenharmony_ci if (0 != result) { 13688c2ecf20Sopenharmony_ci struct scsi_sense_hdr sshdr; 13698c2ecf20Sopenharmony_ci 13708c2ecf20Sopenharmony_ci srp->header.status = 0xff & result; 13718c2ecf20Sopenharmony_ci srp->header.masked_status = status_byte(result); 13728c2ecf20Sopenharmony_ci srp->header.msg_status = msg_byte(result); 13738c2ecf20Sopenharmony_ci srp->header.host_status = host_byte(result); 13748c2ecf20Sopenharmony_ci srp->header.driver_status = driver_byte(result); 13758c2ecf20Sopenharmony_ci if ((sdp->sgdebug > 0) && 13768c2ecf20Sopenharmony_ci ((CHECK_CONDITION == srp->header.masked_status) || 13778c2ecf20Sopenharmony_ci (COMMAND_TERMINATED == srp->header.masked_status))) 13788c2ecf20Sopenharmony_ci __scsi_print_sense(sdp->device, __func__, sense, 13798c2ecf20Sopenharmony_ci SCSI_SENSE_BUFFERSIZE); 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ci /* Following if statement is a patch supplied by Eric Youngdale */ 13828c2ecf20Sopenharmony_ci if (driver_byte(result) != 0 13838c2ecf20Sopenharmony_ci && scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr) 13848c2ecf20Sopenharmony_ci && !scsi_sense_is_deferred(&sshdr) 13858c2ecf20Sopenharmony_ci && sshdr.sense_key == UNIT_ATTENTION 13868c2ecf20Sopenharmony_ci && sdp->device->removable) { 13878c2ecf20Sopenharmony_ci /* Detected possible disc change. Set the bit - this */ 13888c2ecf20Sopenharmony_ci /* may be used if there are filesystems using this device */ 13898c2ecf20Sopenharmony_ci sdp->device->changed = 1; 13908c2ecf20Sopenharmony_ci } 13918c2ecf20Sopenharmony_ci } 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci if (req->sense_len) 13948c2ecf20Sopenharmony_ci memcpy(srp->sense_b, req->sense, SCSI_SENSE_BUFFERSIZE); 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci /* Rely on write phase to clean out srp status values, so no "else" */ 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ci /* 13998c2ecf20Sopenharmony_ci * Free the request as soon as it is complete so that its resources 14008c2ecf20Sopenharmony_ci * can be reused without waiting for userspace to read() the 14018c2ecf20Sopenharmony_ci * result. But keep the associated bio (if any) around until 14028c2ecf20Sopenharmony_ci * blk_rq_unmap_user() can be called from user context. 14038c2ecf20Sopenharmony_ci */ 14048c2ecf20Sopenharmony_ci srp->rq = NULL; 14058c2ecf20Sopenharmony_ci scsi_req_free_cmd(scsi_req(rq)); 14068c2ecf20Sopenharmony_ci blk_put_request(rq); 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_ci write_lock_irqsave(&sfp->rq_list_lock, iflags); 14098c2ecf20Sopenharmony_ci if (unlikely(srp->orphan)) { 14108c2ecf20Sopenharmony_ci if (sfp->keep_orphan) 14118c2ecf20Sopenharmony_ci srp->sg_io_owned = 0; 14128c2ecf20Sopenharmony_ci else 14138c2ecf20Sopenharmony_ci done = 0; 14148c2ecf20Sopenharmony_ci } 14158c2ecf20Sopenharmony_ci srp->done = done; 14168c2ecf20Sopenharmony_ci write_unlock_irqrestore(&sfp->rq_list_lock, iflags); 14178c2ecf20Sopenharmony_ci 14188c2ecf20Sopenharmony_ci if (likely(done)) { 14198c2ecf20Sopenharmony_ci /* Now wake up any sg_read() that is waiting for this 14208c2ecf20Sopenharmony_ci * packet. 14218c2ecf20Sopenharmony_ci */ 14228c2ecf20Sopenharmony_ci wake_up_interruptible(&sfp->read_wait); 14238c2ecf20Sopenharmony_ci kill_fasync(&sfp->async_qp, SIGPOLL, POLL_IN); 14248c2ecf20Sopenharmony_ci kref_put(&sfp->f_ref, sg_remove_sfp); 14258c2ecf20Sopenharmony_ci } else { 14268c2ecf20Sopenharmony_ci INIT_WORK(&srp->ew.work, sg_rq_end_io_usercontext); 14278c2ecf20Sopenharmony_ci schedule_work(&srp->ew.work); 14288c2ecf20Sopenharmony_ci } 14298c2ecf20Sopenharmony_ci} 14308c2ecf20Sopenharmony_ci 14318c2ecf20Sopenharmony_cistatic const struct file_operations sg_fops = { 14328c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 14338c2ecf20Sopenharmony_ci .read = sg_read, 14348c2ecf20Sopenharmony_ci .write = sg_write, 14358c2ecf20Sopenharmony_ci .poll = sg_poll, 14368c2ecf20Sopenharmony_ci .unlocked_ioctl = sg_ioctl, 14378c2ecf20Sopenharmony_ci#ifdef CONFIG_COMPAT 14388c2ecf20Sopenharmony_ci .compat_ioctl = sg_compat_ioctl, 14398c2ecf20Sopenharmony_ci#endif 14408c2ecf20Sopenharmony_ci .open = sg_open, 14418c2ecf20Sopenharmony_ci .mmap = sg_mmap, 14428c2ecf20Sopenharmony_ci .release = sg_release, 14438c2ecf20Sopenharmony_ci .fasync = sg_fasync, 14448c2ecf20Sopenharmony_ci .llseek = no_llseek, 14458c2ecf20Sopenharmony_ci}; 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_cistatic struct class *sg_sysfs_class; 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_cistatic int sg_sysfs_valid = 0; 14508c2ecf20Sopenharmony_ci 14518c2ecf20Sopenharmony_cistatic Sg_device * 14528c2ecf20Sopenharmony_cisg_alloc(struct gendisk *disk, struct scsi_device *scsidp) 14538c2ecf20Sopenharmony_ci{ 14548c2ecf20Sopenharmony_ci struct request_queue *q = scsidp->request_queue; 14558c2ecf20Sopenharmony_ci Sg_device *sdp; 14568c2ecf20Sopenharmony_ci unsigned long iflags; 14578c2ecf20Sopenharmony_ci int error; 14588c2ecf20Sopenharmony_ci u32 k; 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_ci sdp = kzalloc(sizeof(Sg_device), GFP_KERNEL); 14618c2ecf20Sopenharmony_ci if (!sdp) { 14628c2ecf20Sopenharmony_ci sdev_printk(KERN_WARNING, scsidp, "%s: kmalloc Sg_device " 14638c2ecf20Sopenharmony_ci "failure\n", __func__); 14648c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 14658c2ecf20Sopenharmony_ci } 14668c2ecf20Sopenharmony_ci 14678c2ecf20Sopenharmony_ci idr_preload(GFP_KERNEL); 14688c2ecf20Sopenharmony_ci write_lock_irqsave(&sg_index_lock, iflags); 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_ci error = idr_alloc(&sg_index_idr, sdp, 0, SG_MAX_DEVS, GFP_NOWAIT); 14718c2ecf20Sopenharmony_ci if (error < 0) { 14728c2ecf20Sopenharmony_ci if (error == -ENOSPC) { 14738c2ecf20Sopenharmony_ci sdev_printk(KERN_WARNING, scsidp, 14748c2ecf20Sopenharmony_ci "Unable to attach sg device type=%d, minor number exceeds %d\n", 14758c2ecf20Sopenharmony_ci scsidp->type, SG_MAX_DEVS - 1); 14768c2ecf20Sopenharmony_ci error = -ENODEV; 14778c2ecf20Sopenharmony_ci } else { 14788c2ecf20Sopenharmony_ci sdev_printk(KERN_WARNING, scsidp, "%s: idr " 14798c2ecf20Sopenharmony_ci "allocation Sg_device failure: %d\n", 14808c2ecf20Sopenharmony_ci __func__, error); 14818c2ecf20Sopenharmony_ci } 14828c2ecf20Sopenharmony_ci goto out_unlock; 14838c2ecf20Sopenharmony_ci } 14848c2ecf20Sopenharmony_ci k = error; 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_ci SCSI_LOG_TIMEOUT(3, sdev_printk(KERN_INFO, scsidp, 14878c2ecf20Sopenharmony_ci "sg_alloc: dev=%d \n", k)); 14888c2ecf20Sopenharmony_ci sprintf(disk->disk_name, "sg%d", k); 14898c2ecf20Sopenharmony_ci disk->first_minor = k; 14908c2ecf20Sopenharmony_ci sdp->disk = disk; 14918c2ecf20Sopenharmony_ci sdp->device = scsidp; 14928c2ecf20Sopenharmony_ci mutex_init(&sdp->open_rel_lock); 14938c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&sdp->sfds); 14948c2ecf20Sopenharmony_ci init_waitqueue_head(&sdp->open_wait); 14958c2ecf20Sopenharmony_ci atomic_set(&sdp->detaching, 0); 14968c2ecf20Sopenharmony_ci rwlock_init(&sdp->sfd_lock); 14978c2ecf20Sopenharmony_ci sdp->sg_tablesize = queue_max_segments(q); 14988c2ecf20Sopenharmony_ci sdp->index = k; 14998c2ecf20Sopenharmony_ci kref_init(&sdp->d_ref); 15008c2ecf20Sopenharmony_ci error = 0; 15018c2ecf20Sopenharmony_ci 15028c2ecf20Sopenharmony_ciout_unlock: 15038c2ecf20Sopenharmony_ci write_unlock_irqrestore(&sg_index_lock, iflags); 15048c2ecf20Sopenharmony_ci idr_preload_end(); 15058c2ecf20Sopenharmony_ci 15068c2ecf20Sopenharmony_ci if (error) { 15078c2ecf20Sopenharmony_ci kfree(sdp); 15088c2ecf20Sopenharmony_ci return ERR_PTR(error); 15098c2ecf20Sopenharmony_ci } 15108c2ecf20Sopenharmony_ci return sdp; 15118c2ecf20Sopenharmony_ci} 15128c2ecf20Sopenharmony_ci 15138c2ecf20Sopenharmony_cistatic int 15148c2ecf20Sopenharmony_cisg_add_device(struct device *cl_dev, struct class_interface *cl_intf) 15158c2ecf20Sopenharmony_ci{ 15168c2ecf20Sopenharmony_ci struct scsi_device *scsidp = to_scsi_device(cl_dev->parent); 15178c2ecf20Sopenharmony_ci struct gendisk *disk; 15188c2ecf20Sopenharmony_ci Sg_device *sdp = NULL; 15198c2ecf20Sopenharmony_ci struct cdev * cdev = NULL; 15208c2ecf20Sopenharmony_ci int error; 15218c2ecf20Sopenharmony_ci unsigned long iflags; 15228c2ecf20Sopenharmony_ci 15238c2ecf20Sopenharmony_ci disk = alloc_disk(1); 15248c2ecf20Sopenharmony_ci if (!disk) { 15258c2ecf20Sopenharmony_ci pr_warn("%s: alloc_disk failed\n", __func__); 15268c2ecf20Sopenharmony_ci return -ENOMEM; 15278c2ecf20Sopenharmony_ci } 15288c2ecf20Sopenharmony_ci disk->major = SCSI_GENERIC_MAJOR; 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ci error = -ENOMEM; 15318c2ecf20Sopenharmony_ci cdev = cdev_alloc(); 15328c2ecf20Sopenharmony_ci if (!cdev) { 15338c2ecf20Sopenharmony_ci pr_warn("%s: cdev_alloc failed\n", __func__); 15348c2ecf20Sopenharmony_ci goto out; 15358c2ecf20Sopenharmony_ci } 15368c2ecf20Sopenharmony_ci cdev->owner = THIS_MODULE; 15378c2ecf20Sopenharmony_ci cdev->ops = &sg_fops; 15388c2ecf20Sopenharmony_ci 15398c2ecf20Sopenharmony_ci sdp = sg_alloc(disk, scsidp); 15408c2ecf20Sopenharmony_ci if (IS_ERR(sdp)) { 15418c2ecf20Sopenharmony_ci pr_warn("%s: sg_alloc failed\n", __func__); 15428c2ecf20Sopenharmony_ci error = PTR_ERR(sdp); 15438c2ecf20Sopenharmony_ci goto out; 15448c2ecf20Sopenharmony_ci } 15458c2ecf20Sopenharmony_ci 15468c2ecf20Sopenharmony_ci error = cdev_add(cdev, MKDEV(SCSI_GENERIC_MAJOR, sdp->index), 1); 15478c2ecf20Sopenharmony_ci if (error) 15488c2ecf20Sopenharmony_ci goto cdev_add_err; 15498c2ecf20Sopenharmony_ci 15508c2ecf20Sopenharmony_ci sdp->cdev = cdev; 15518c2ecf20Sopenharmony_ci if (sg_sysfs_valid) { 15528c2ecf20Sopenharmony_ci struct device *sg_class_member; 15538c2ecf20Sopenharmony_ci 15548c2ecf20Sopenharmony_ci sg_class_member = device_create(sg_sysfs_class, cl_dev->parent, 15558c2ecf20Sopenharmony_ci MKDEV(SCSI_GENERIC_MAJOR, 15568c2ecf20Sopenharmony_ci sdp->index), 15578c2ecf20Sopenharmony_ci sdp, "%s", disk->disk_name); 15588c2ecf20Sopenharmony_ci if (IS_ERR(sg_class_member)) { 15598c2ecf20Sopenharmony_ci pr_err("%s: device_create failed\n", __func__); 15608c2ecf20Sopenharmony_ci error = PTR_ERR(sg_class_member); 15618c2ecf20Sopenharmony_ci goto cdev_add_err; 15628c2ecf20Sopenharmony_ci } 15638c2ecf20Sopenharmony_ci error = sysfs_create_link(&scsidp->sdev_gendev.kobj, 15648c2ecf20Sopenharmony_ci &sg_class_member->kobj, "generic"); 15658c2ecf20Sopenharmony_ci if (error) 15668c2ecf20Sopenharmony_ci pr_err("%s: unable to make symlink 'generic' back " 15678c2ecf20Sopenharmony_ci "to sg%d\n", __func__, sdp->index); 15688c2ecf20Sopenharmony_ci } else 15698c2ecf20Sopenharmony_ci pr_warn("%s: sg_sys Invalid\n", __func__); 15708c2ecf20Sopenharmony_ci 15718c2ecf20Sopenharmony_ci sdev_printk(KERN_NOTICE, scsidp, "Attached scsi generic sg%d " 15728c2ecf20Sopenharmony_ci "type %d\n", sdp->index, scsidp->type); 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_ci dev_set_drvdata(cl_dev, sdp); 15758c2ecf20Sopenharmony_ci 15768c2ecf20Sopenharmony_ci return 0; 15778c2ecf20Sopenharmony_ci 15788c2ecf20Sopenharmony_cicdev_add_err: 15798c2ecf20Sopenharmony_ci write_lock_irqsave(&sg_index_lock, iflags); 15808c2ecf20Sopenharmony_ci idr_remove(&sg_index_idr, sdp->index); 15818c2ecf20Sopenharmony_ci write_unlock_irqrestore(&sg_index_lock, iflags); 15828c2ecf20Sopenharmony_ci kfree(sdp); 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ciout: 15858c2ecf20Sopenharmony_ci put_disk(disk); 15868c2ecf20Sopenharmony_ci if (cdev) 15878c2ecf20Sopenharmony_ci cdev_del(cdev); 15888c2ecf20Sopenharmony_ci return error; 15898c2ecf20Sopenharmony_ci} 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_cistatic void 15928c2ecf20Sopenharmony_cisg_device_destroy(struct kref *kref) 15938c2ecf20Sopenharmony_ci{ 15948c2ecf20Sopenharmony_ci struct sg_device *sdp = container_of(kref, struct sg_device, d_ref); 15958c2ecf20Sopenharmony_ci unsigned long flags; 15968c2ecf20Sopenharmony_ci 15978c2ecf20Sopenharmony_ci /* CAUTION! Note that the device can still be found via idr_find() 15988c2ecf20Sopenharmony_ci * even though the refcount is 0. Therefore, do idr_remove() BEFORE 15998c2ecf20Sopenharmony_ci * any other cleanup. 16008c2ecf20Sopenharmony_ci */ 16018c2ecf20Sopenharmony_ci 16028c2ecf20Sopenharmony_ci write_lock_irqsave(&sg_index_lock, flags); 16038c2ecf20Sopenharmony_ci idr_remove(&sg_index_idr, sdp->index); 16048c2ecf20Sopenharmony_ci write_unlock_irqrestore(&sg_index_lock, flags); 16058c2ecf20Sopenharmony_ci 16068c2ecf20Sopenharmony_ci SCSI_LOG_TIMEOUT(3, 16078c2ecf20Sopenharmony_ci sg_printk(KERN_INFO, sdp, "sg_device_destroy\n")); 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_ci put_disk(sdp->disk); 16108c2ecf20Sopenharmony_ci kfree(sdp); 16118c2ecf20Sopenharmony_ci} 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_cistatic void 16148c2ecf20Sopenharmony_cisg_remove_device(struct device *cl_dev, struct class_interface *cl_intf) 16158c2ecf20Sopenharmony_ci{ 16168c2ecf20Sopenharmony_ci struct scsi_device *scsidp = to_scsi_device(cl_dev->parent); 16178c2ecf20Sopenharmony_ci Sg_device *sdp = dev_get_drvdata(cl_dev); 16188c2ecf20Sopenharmony_ci unsigned long iflags; 16198c2ecf20Sopenharmony_ci Sg_fd *sfp; 16208c2ecf20Sopenharmony_ci int val; 16218c2ecf20Sopenharmony_ci 16228c2ecf20Sopenharmony_ci if (!sdp) 16238c2ecf20Sopenharmony_ci return; 16248c2ecf20Sopenharmony_ci /* want sdp->detaching non-zero as soon as possible */ 16258c2ecf20Sopenharmony_ci val = atomic_inc_return(&sdp->detaching); 16268c2ecf20Sopenharmony_ci if (val > 1) 16278c2ecf20Sopenharmony_ci return; /* only want to do following once per device */ 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, 16308c2ecf20Sopenharmony_ci "%s\n", __func__)); 16318c2ecf20Sopenharmony_ci 16328c2ecf20Sopenharmony_ci read_lock_irqsave(&sdp->sfd_lock, iflags); 16338c2ecf20Sopenharmony_ci list_for_each_entry(sfp, &sdp->sfds, sfd_siblings) { 16348c2ecf20Sopenharmony_ci wake_up_interruptible_all(&sfp->read_wait); 16358c2ecf20Sopenharmony_ci kill_fasync(&sfp->async_qp, SIGPOLL, POLL_HUP); 16368c2ecf20Sopenharmony_ci } 16378c2ecf20Sopenharmony_ci wake_up_interruptible_all(&sdp->open_wait); 16388c2ecf20Sopenharmony_ci read_unlock_irqrestore(&sdp->sfd_lock, iflags); 16398c2ecf20Sopenharmony_ci 16408c2ecf20Sopenharmony_ci sysfs_remove_link(&scsidp->sdev_gendev.kobj, "generic"); 16418c2ecf20Sopenharmony_ci device_destroy(sg_sysfs_class, MKDEV(SCSI_GENERIC_MAJOR, sdp->index)); 16428c2ecf20Sopenharmony_ci cdev_del(sdp->cdev); 16438c2ecf20Sopenharmony_ci sdp->cdev = NULL; 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_ci kref_put(&sdp->d_ref, sg_device_destroy); 16468c2ecf20Sopenharmony_ci} 16478c2ecf20Sopenharmony_ci 16488c2ecf20Sopenharmony_cimodule_param_named(scatter_elem_sz, scatter_elem_sz, int, S_IRUGO | S_IWUSR); 16498c2ecf20Sopenharmony_cimodule_param_named(def_reserved_size, def_reserved_size, int, 16508c2ecf20Sopenharmony_ci S_IRUGO | S_IWUSR); 16518c2ecf20Sopenharmony_cimodule_param_named(allow_dio, sg_allow_dio, int, S_IRUGO | S_IWUSR); 16528c2ecf20Sopenharmony_ci 16538c2ecf20Sopenharmony_ciMODULE_AUTHOR("Douglas Gilbert"); 16548c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SCSI generic (sg) driver"); 16558c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 16568c2ecf20Sopenharmony_ciMODULE_VERSION(SG_VERSION_STR); 16578c2ecf20Sopenharmony_ciMODULE_ALIAS_CHARDEV_MAJOR(SCSI_GENERIC_MAJOR); 16588c2ecf20Sopenharmony_ci 16598c2ecf20Sopenharmony_ciMODULE_PARM_DESC(scatter_elem_sz, "scatter gather element " 16608c2ecf20Sopenharmony_ci "size (default: max(SG_SCATTER_SZ, PAGE_SIZE))"); 16618c2ecf20Sopenharmony_ciMODULE_PARM_DESC(def_reserved_size, "size of buffer reserved for each fd"); 16628c2ecf20Sopenharmony_ciMODULE_PARM_DESC(allow_dio, "allow direct I/O (default: 0 (disallow))"); 16638c2ecf20Sopenharmony_ci 16648c2ecf20Sopenharmony_cistatic int __init 16658c2ecf20Sopenharmony_ciinit_sg(void) 16668c2ecf20Sopenharmony_ci{ 16678c2ecf20Sopenharmony_ci int rc; 16688c2ecf20Sopenharmony_ci 16698c2ecf20Sopenharmony_ci if (scatter_elem_sz < PAGE_SIZE) { 16708c2ecf20Sopenharmony_ci scatter_elem_sz = PAGE_SIZE; 16718c2ecf20Sopenharmony_ci scatter_elem_sz_prev = scatter_elem_sz; 16728c2ecf20Sopenharmony_ci } 16738c2ecf20Sopenharmony_ci if (def_reserved_size >= 0) 16748c2ecf20Sopenharmony_ci sg_big_buff = def_reserved_size; 16758c2ecf20Sopenharmony_ci else 16768c2ecf20Sopenharmony_ci def_reserved_size = sg_big_buff; 16778c2ecf20Sopenharmony_ci 16788c2ecf20Sopenharmony_ci rc = register_chrdev_region(MKDEV(SCSI_GENERIC_MAJOR, 0), 16798c2ecf20Sopenharmony_ci SG_MAX_DEVS, "sg"); 16808c2ecf20Sopenharmony_ci if (rc) 16818c2ecf20Sopenharmony_ci return rc; 16828c2ecf20Sopenharmony_ci sg_sysfs_class = class_create(THIS_MODULE, "scsi_generic"); 16838c2ecf20Sopenharmony_ci if ( IS_ERR(sg_sysfs_class) ) { 16848c2ecf20Sopenharmony_ci rc = PTR_ERR(sg_sysfs_class); 16858c2ecf20Sopenharmony_ci goto err_out; 16868c2ecf20Sopenharmony_ci } 16878c2ecf20Sopenharmony_ci sg_sysfs_valid = 1; 16888c2ecf20Sopenharmony_ci rc = scsi_register_interface(&sg_interface); 16898c2ecf20Sopenharmony_ci if (0 == rc) { 16908c2ecf20Sopenharmony_ci#ifdef CONFIG_SCSI_PROC_FS 16918c2ecf20Sopenharmony_ci sg_proc_init(); 16928c2ecf20Sopenharmony_ci#endif /* CONFIG_SCSI_PROC_FS */ 16938c2ecf20Sopenharmony_ci return 0; 16948c2ecf20Sopenharmony_ci } 16958c2ecf20Sopenharmony_ci class_destroy(sg_sysfs_class); 16968c2ecf20Sopenharmony_cierr_out: 16978c2ecf20Sopenharmony_ci unregister_chrdev_region(MKDEV(SCSI_GENERIC_MAJOR, 0), SG_MAX_DEVS); 16988c2ecf20Sopenharmony_ci return rc; 16998c2ecf20Sopenharmony_ci} 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_cistatic void __exit 17028c2ecf20Sopenharmony_ciexit_sg(void) 17038c2ecf20Sopenharmony_ci{ 17048c2ecf20Sopenharmony_ci#ifdef CONFIG_SCSI_PROC_FS 17058c2ecf20Sopenharmony_ci remove_proc_subtree("scsi/sg", NULL); 17068c2ecf20Sopenharmony_ci#endif /* CONFIG_SCSI_PROC_FS */ 17078c2ecf20Sopenharmony_ci scsi_unregister_interface(&sg_interface); 17088c2ecf20Sopenharmony_ci class_destroy(sg_sysfs_class); 17098c2ecf20Sopenharmony_ci sg_sysfs_valid = 0; 17108c2ecf20Sopenharmony_ci unregister_chrdev_region(MKDEV(SCSI_GENERIC_MAJOR, 0), 17118c2ecf20Sopenharmony_ci SG_MAX_DEVS); 17128c2ecf20Sopenharmony_ci idr_destroy(&sg_index_idr); 17138c2ecf20Sopenharmony_ci} 17148c2ecf20Sopenharmony_ci 17158c2ecf20Sopenharmony_cistatic int 17168c2ecf20Sopenharmony_cisg_start_req(Sg_request *srp, unsigned char *cmd) 17178c2ecf20Sopenharmony_ci{ 17188c2ecf20Sopenharmony_ci int res; 17198c2ecf20Sopenharmony_ci struct request *rq; 17208c2ecf20Sopenharmony_ci struct scsi_request *req; 17218c2ecf20Sopenharmony_ci Sg_fd *sfp = srp->parentfp; 17228c2ecf20Sopenharmony_ci sg_io_hdr_t *hp = &srp->header; 17238c2ecf20Sopenharmony_ci int dxfer_len = (int) hp->dxfer_len; 17248c2ecf20Sopenharmony_ci int dxfer_dir = hp->dxfer_direction; 17258c2ecf20Sopenharmony_ci unsigned int iov_count = hp->iovec_count; 17268c2ecf20Sopenharmony_ci Sg_scatter_hold *req_schp = &srp->data; 17278c2ecf20Sopenharmony_ci Sg_scatter_hold *rsv_schp = &sfp->reserve; 17288c2ecf20Sopenharmony_ci struct request_queue *q = sfp->parentdp->device->request_queue; 17298c2ecf20Sopenharmony_ci struct rq_map_data *md, map_data; 17308c2ecf20Sopenharmony_ci int rw = hp->dxfer_direction == SG_DXFER_TO_DEV ? WRITE : READ; 17318c2ecf20Sopenharmony_ci unsigned char *long_cmdp = NULL; 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_ci SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sfp->parentdp, 17348c2ecf20Sopenharmony_ci "sg_start_req: dxfer_len=%d\n", 17358c2ecf20Sopenharmony_ci dxfer_len)); 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_ci if (hp->cmd_len > BLK_MAX_CDB) { 17388c2ecf20Sopenharmony_ci long_cmdp = kzalloc(hp->cmd_len, GFP_KERNEL); 17398c2ecf20Sopenharmony_ci if (!long_cmdp) 17408c2ecf20Sopenharmony_ci return -ENOMEM; 17418c2ecf20Sopenharmony_ci } 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci /* 17448c2ecf20Sopenharmony_ci * NOTE 17458c2ecf20Sopenharmony_ci * 17468c2ecf20Sopenharmony_ci * With scsi-mq enabled, there are a fixed number of preallocated 17478c2ecf20Sopenharmony_ci * requests equal in number to shost->can_queue. If all of the 17488c2ecf20Sopenharmony_ci * preallocated requests are already in use, then blk_get_request() 17498c2ecf20Sopenharmony_ci * will sleep until an active command completes, freeing up a request. 17508c2ecf20Sopenharmony_ci * Although waiting in an asynchronous interface is less than ideal, we 17518c2ecf20Sopenharmony_ci * do not want to use BLK_MQ_REQ_NOWAIT here because userspace might 17528c2ecf20Sopenharmony_ci * not expect an EWOULDBLOCK from this condition. 17538c2ecf20Sopenharmony_ci */ 17548c2ecf20Sopenharmony_ci rq = blk_get_request(q, hp->dxfer_direction == SG_DXFER_TO_DEV ? 17558c2ecf20Sopenharmony_ci REQ_OP_SCSI_OUT : REQ_OP_SCSI_IN, 0); 17568c2ecf20Sopenharmony_ci if (IS_ERR(rq)) { 17578c2ecf20Sopenharmony_ci kfree(long_cmdp); 17588c2ecf20Sopenharmony_ci return PTR_ERR(rq); 17598c2ecf20Sopenharmony_ci } 17608c2ecf20Sopenharmony_ci req = scsi_req(rq); 17618c2ecf20Sopenharmony_ci 17628c2ecf20Sopenharmony_ci if (hp->cmd_len > BLK_MAX_CDB) 17638c2ecf20Sopenharmony_ci req->cmd = long_cmdp; 17648c2ecf20Sopenharmony_ci memcpy(req->cmd, cmd, hp->cmd_len); 17658c2ecf20Sopenharmony_ci req->cmd_len = hp->cmd_len; 17668c2ecf20Sopenharmony_ci 17678c2ecf20Sopenharmony_ci srp->rq = rq; 17688c2ecf20Sopenharmony_ci rq->end_io_data = srp; 17698c2ecf20Sopenharmony_ci req->retries = SG_DEFAULT_RETRIES; 17708c2ecf20Sopenharmony_ci 17718c2ecf20Sopenharmony_ci if ((dxfer_len <= 0) || (dxfer_dir == SG_DXFER_NONE)) 17728c2ecf20Sopenharmony_ci return 0; 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_ci if (sg_allow_dio && hp->flags & SG_FLAG_DIRECT_IO && 17758c2ecf20Sopenharmony_ci dxfer_dir != SG_DXFER_UNKNOWN && !iov_count && 17768c2ecf20Sopenharmony_ci !sfp->parentdp->device->host->unchecked_isa_dma && 17778c2ecf20Sopenharmony_ci blk_rq_aligned(q, (unsigned long)hp->dxferp, dxfer_len)) 17788c2ecf20Sopenharmony_ci md = NULL; 17798c2ecf20Sopenharmony_ci else 17808c2ecf20Sopenharmony_ci md = &map_data; 17818c2ecf20Sopenharmony_ci 17828c2ecf20Sopenharmony_ci if (md) { 17838c2ecf20Sopenharmony_ci mutex_lock(&sfp->f_mutex); 17848c2ecf20Sopenharmony_ci if (dxfer_len <= rsv_schp->bufflen && 17858c2ecf20Sopenharmony_ci !sfp->res_in_use) { 17868c2ecf20Sopenharmony_ci sfp->res_in_use = 1; 17878c2ecf20Sopenharmony_ci sg_link_reserve(sfp, srp, dxfer_len); 17888c2ecf20Sopenharmony_ci } else if (hp->flags & SG_FLAG_MMAP_IO) { 17898c2ecf20Sopenharmony_ci res = -EBUSY; /* sfp->res_in_use == 1 */ 17908c2ecf20Sopenharmony_ci if (dxfer_len > rsv_schp->bufflen) 17918c2ecf20Sopenharmony_ci res = -ENOMEM; 17928c2ecf20Sopenharmony_ci mutex_unlock(&sfp->f_mutex); 17938c2ecf20Sopenharmony_ci return res; 17948c2ecf20Sopenharmony_ci } else { 17958c2ecf20Sopenharmony_ci res = sg_build_indirect(req_schp, sfp, dxfer_len); 17968c2ecf20Sopenharmony_ci if (res) { 17978c2ecf20Sopenharmony_ci mutex_unlock(&sfp->f_mutex); 17988c2ecf20Sopenharmony_ci return res; 17998c2ecf20Sopenharmony_ci } 18008c2ecf20Sopenharmony_ci } 18018c2ecf20Sopenharmony_ci mutex_unlock(&sfp->f_mutex); 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_ci md->pages = req_schp->pages; 18048c2ecf20Sopenharmony_ci md->page_order = req_schp->page_order; 18058c2ecf20Sopenharmony_ci md->nr_entries = req_schp->k_use_sg; 18068c2ecf20Sopenharmony_ci md->offset = 0; 18078c2ecf20Sopenharmony_ci md->null_mapped = hp->dxferp ? 0 : 1; 18088c2ecf20Sopenharmony_ci if (dxfer_dir == SG_DXFER_TO_FROM_DEV) 18098c2ecf20Sopenharmony_ci md->from_user = 1; 18108c2ecf20Sopenharmony_ci else 18118c2ecf20Sopenharmony_ci md->from_user = 0; 18128c2ecf20Sopenharmony_ci } 18138c2ecf20Sopenharmony_ci 18148c2ecf20Sopenharmony_ci if (iov_count) { 18158c2ecf20Sopenharmony_ci struct iovec *iov = NULL; 18168c2ecf20Sopenharmony_ci struct iov_iter i; 18178c2ecf20Sopenharmony_ci 18188c2ecf20Sopenharmony_ci res = import_iovec(rw, hp->dxferp, iov_count, 0, &iov, &i); 18198c2ecf20Sopenharmony_ci if (res < 0) 18208c2ecf20Sopenharmony_ci return res; 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_ci iov_iter_truncate(&i, hp->dxfer_len); 18238c2ecf20Sopenharmony_ci if (!iov_iter_count(&i)) { 18248c2ecf20Sopenharmony_ci kfree(iov); 18258c2ecf20Sopenharmony_ci return -EINVAL; 18268c2ecf20Sopenharmony_ci } 18278c2ecf20Sopenharmony_ci 18288c2ecf20Sopenharmony_ci res = blk_rq_map_user_iov(q, rq, md, &i, GFP_ATOMIC); 18298c2ecf20Sopenharmony_ci kfree(iov); 18308c2ecf20Sopenharmony_ci } else 18318c2ecf20Sopenharmony_ci res = blk_rq_map_user(q, rq, md, hp->dxferp, 18328c2ecf20Sopenharmony_ci hp->dxfer_len, GFP_ATOMIC); 18338c2ecf20Sopenharmony_ci 18348c2ecf20Sopenharmony_ci if (!res) { 18358c2ecf20Sopenharmony_ci srp->bio = rq->bio; 18368c2ecf20Sopenharmony_ci 18378c2ecf20Sopenharmony_ci if (!md) { 18388c2ecf20Sopenharmony_ci req_schp->dio_in_use = 1; 18398c2ecf20Sopenharmony_ci hp->info |= SG_INFO_DIRECT_IO; 18408c2ecf20Sopenharmony_ci } 18418c2ecf20Sopenharmony_ci } 18428c2ecf20Sopenharmony_ci return res; 18438c2ecf20Sopenharmony_ci} 18448c2ecf20Sopenharmony_ci 18458c2ecf20Sopenharmony_cistatic int 18468c2ecf20Sopenharmony_cisg_finish_rem_req(Sg_request *srp) 18478c2ecf20Sopenharmony_ci{ 18488c2ecf20Sopenharmony_ci int ret = 0; 18498c2ecf20Sopenharmony_ci 18508c2ecf20Sopenharmony_ci Sg_fd *sfp = srp->parentfp; 18518c2ecf20Sopenharmony_ci Sg_scatter_hold *req_schp = &srp->data; 18528c2ecf20Sopenharmony_ci 18538c2ecf20Sopenharmony_ci SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sfp->parentdp, 18548c2ecf20Sopenharmony_ci "sg_finish_rem_req: res_used=%d\n", 18558c2ecf20Sopenharmony_ci (int) srp->res_used)); 18568c2ecf20Sopenharmony_ci if (srp->bio) 18578c2ecf20Sopenharmony_ci ret = blk_rq_unmap_user(srp->bio); 18588c2ecf20Sopenharmony_ci 18598c2ecf20Sopenharmony_ci if (srp->rq) { 18608c2ecf20Sopenharmony_ci scsi_req_free_cmd(scsi_req(srp->rq)); 18618c2ecf20Sopenharmony_ci blk_put_request(srp->rq); 18628c2ecf20Sopenharmony_ci } 18638c2ecf20Sopenharmony_ci 18648c2ecf20Sopenharmony_ci if (srp->res_used) 18658c2ecf20Sopenharmony_ci sg_unlink_reserve(sfp, srp); 18668c2ecf20Sopenharmony_ci else 18678c2ecf20Sopenharmony_ci sg_remove_scat(sfp, req_schp); 18688c2ecf20Sopenharmony_ci 18698c2ecf20Sopenharmony_ci return ret; 18708c2ecf20Sopenharmony_ci} 18718c2ecf20Sopenharmony_ci 18728c2ecf20Sopenharmony_cistatic int 18738c2ecf20Sopenharmony_cisg_build_sgat(Sg_scatter_hold * schp, const Sg_fd * sfp, int tablesize) 18748c2ecf20Sopenharmony_ci{ 18758c2ecf20Sopenharmony_ci int sg_bufflen = tablesize * sizeof(struct page *); 18768c2ecf20Sopenharmony_ci gfp_t gfp_flags = GFP_ATOMIC | __GFP_NOWARN; 18778c2ecf20Sopenharmony_ci 18788c2ecf20Sopenharmony_ci schp->pages = kzalloc(sg_bufflen, gfp_flags); 18798c2ecf20Sopenharmony_ci if (!schp->pages) 18808c2ecf20Sopenharmony_ci return -ENOMEM; 18818c2ecf20Sopenharmony_ci schp->sglist_len = sg_bufflen; 18828c2ecf20Sopenharmony_ci return tablesize; /* number of scat_gath elements allocated */ 18838c2ecf20Sopenharmony_ci} 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_cistatic int 18868c2ecf20Sopenharmony_cisg_build_indirect(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size) 18878c2ecf20Sopenharmony_ci{ 18888c2ecf20Sopenharmony_ci int ret_sz = 0, i, k, rem_sz, num, mx_sc_elems; 18898c2ecf20Sopenharmony_ci int sg_tablesize = sfp->parentdp->sg_tablesize; 18908c2ecf20Sopenharmony_ci int blk_size = buff_size, order; 18918c2ecf20Sopenharmony_ci gfp_t gfp_mask = GFP_ATOMIC | __GFP_COMP | __GFP_NOWARN | __GFP_ZERO; 18928c2ecf20Sopenharmony_ci struct sg_device *sdp = sfp->parentdp; 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_ci if (blk_size < 0) 18958c2ecf20Sopenharmony_ci return -EFAULT; 18968c2ecf20Sopenharmony_ci if (0 == blk_size) 18978c2ecf20Sopenharmony_ci ++blk_size; /* don't know why */ 18988c2ecf20Sopenharmony_ci /* round request up to next highest SG_SECTOR_SZ byte boundary */ 18998c2ecf20Sopenharmony_ci blk_size = ALIGN(blk_size, SG_SECTOR_SZ); 19008c2ecf20Sopenharmony_ci SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sfp->parentdp, 19018c2ecf20Sopenharmony_ci "sg_build_indirect: buff_size=%d, blk_size=%d\n", 19028c2ecf20Sopenharmony_ci buff_size, blk_size)); 19038c2ecf20Sopenharmony_ci 19048c2ecf20Sopenharmony_ci /* N.B. ret_sz carried into this block ... */ 19058c2ecf20Sopenharmony_ci mx_sc_elems = sg_build_sgat(schp, sfp, sg_tablesize); 19068c2ecf20Sopenharmony_ci if (mx_sc_elems < 0) 19078c2ecf20Sopenharmony_ci return mx_sc_elems; /* most likely -ENOMEM */ 19088c2ecf20Sopenharmony_ci 19098c2ecf20Sopenharmony_ci num = scatter_elem_sz; 19108c2ecf20Sopenharmony_ci if (unlikely(num != scatter_elem_sz_prev)) { 19118c2ecf20Sopenharmony_ci if (num < PAGE_SIZE) { 19128c2ecf20Sopenharmony_ci scatter_elem_sz = PAGE_SIZE; 19138c2ecf20Sopenharmony_ci scatter_elem_sz_prev = PAGE_SIZE; 19148c2ecf20Sopenharmony_ci } else 19158c2ecf20Sopenharmony_ci scatter_elem_sz_prev = num; 19168c2ecf20Sopenharmony_ci } 19178c2ecf20Sopenharmony_ci 19188c2ecf20Sopenharmony_ci if (sdp->device->host->unchecked_isa_dma) 19198c2ecf20Sopenharmony_ci gfp_mask |= GFP_DMA; 19208c2ecf20Sopenharmony_ci 19218c2ecf20Sopenharmony_ci order = get_order(num); 19228c2ecf20Sopenharmony_ciretry: 19238c2ecf20Sopenharmony_ci ret_sz = 1 << (PAGE_SHIFT + order); 19248c2ecf20Sopenharmony_ci 19258c2ecf20Sopenharmony_ci for (k = 0, rem_sz = blk_size; rem_sz > 0 && k < mx_sc_elems; 19268c2ecf20Sopenharmony_ci k++, rem_sz -= ret_sz) { 19278c2ecf20Sopenharmony_ci 19288c2ecf20Sopenharmony_ci num = (rem_sz > scatter_elem_sz_prev) ? 19298c2ecf20Sopenharmony_ci scatter_elem_sz_prev : rem_sz; 19308c2ecf20Sopenharmony_ci 19318c2ecf20Sopenharmony_ci schp->pages[k] = alloc_pages(gfp_mask, order); 19328c2ecf20Sopenharmony_ci if (!schp->pages[k]) 19338c2ecf20Sopenharmony_ci goto out; 19348c2ecf20Sopenharmony_ci 19358c2ecf20Sopenharmony_ci if (num == scatter_elem_sz_prev) { 19368c2ecf20Sopenharmony_ci if (unlikely(ret_sz > scatter_elem_sz_prev)) { 19378c2ecf20Sopenharmony_ci scatter_elem_sz = ret_sz; 19388c2ecf20Sopenharmony_ci scatter_elem_sz_prev = ret_sz; 19398c2ecf20Sopenharmony_ci } 19408c2ecf20Sopenharmony_ci } 19418c2ecf20Sopenharmony_ci 19428c2ecf20Sopenharmony_ci SCSI_LOG_TIMEOUT(5, sg_printk(KERN_INFO, sfp->parentdp, 19438c2ecf20Sopenharmony_ci "sg_build_indirect: k=%d, num=%d, ret_sz=%d\n", 19448c2ecf20Sopenharmony_ci k, num, ret_sz)); 19458c2ecf20Sopenharmony_ci } /* end of for loop */ 19468c2ecf20Sopenharmony_ci 19478c2ecf20Sopenharmony_ci schp->page_order = order; 19488c2ecf20Sopenharmony_ci schp->k_use_sg = k; 19498c2ecf20Sopenharmony_ci SCSI_LOG_TIMEOUT(5, sg_printk(KERN_INFO, sfp->parentdp, 19508c2ecf20Sopenharmony_ci "sg_build_indirect: k_use_sg=%d, rem_sz=%d\n", 19518c2ecf20Sopenharmony_ci k, rem_sz)); 19528c2ecf20Sopenharmony_ci 19538c2ecf20Sopenharmony_ci schp->bufflen = blk_size; 19548c2ecf20Sopenharmony_ci if (rem_sz > 0) /* must have failed */ 19558c2ecf20Sopenharmony_ci return -ENOMEM; 19568c2ecf20Sopenharmony_ci return 0; 19578c2ecf20Sopenharmony_ciout: 19588c2ecf20Sopenharmony_ci for (i = 0; i < k; i++) 19598c2ecf20Sopenharmony_ci __free_pages(schp->pages[i], order); 19608c2ecf20Sopenharmony_ci 19618c2ecf20Sopenharmony_ci if (--order >= 0) 19628c2ecf20Sopenharmony_ci goto retry; 19638c2ecf20Sopenharmony_ci 19648c2ecf20Sopenharmony_ci return -ENOMEM; 19658c2ecf20Sopenharmony_ci} 19668c2ecf20Sopenharmony_ci 19678c2ecf20Sopenharmony_cistatic void 19688c2ecf20Sopenharmony_cisg_remove_scat(Sg_fd * sfp, Sg_scatter_hold * schp) 19698c2ecf20Sopenharmony_ci{ 19708c2ecf20Sopenharmony_ci SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sfp->parentdp, 19718c2ecf20Sopenharmony_ci "sg_remove_scat: k_use_sg=%d\n", schp->k_use_sg)); 19728c2ecf20Sopenharmony_ci if (schp->pages && schp->sglist_len > 0) { 19738c2ecf20Sopenharmony_ci if (!schp->dio_in_use) { 19748c2ecf20Sopenharmony_ci int k; 19758c2ecf20Sopenharmony_ci 19768c2ecf20Sopenharmony_ci for (k = 0; k < schp->k_use_sg && schp->pages[k]; k++) { 19778c2ecf20Sopenharmony_ci SCSI_LOG_TIMEOUT(5, 19788c2ecf20Sopenharmony_ci sg_printk(KERN_INFO, sfp->parentdp, 19798c2ecf20Sopenharmony_ci "sg_remove_scat: k=%d, pg=0x%p\n", 19808c2ecf20Sopenharmony_ci k, schp->pages[k])); 19818c2ecf20Sopenharmony_ci __free_pages(schp->pages[k], schp->page_order); 19828c2ecf20Sopenharmony_ci } 19838c2ecf20Sopenharmony_ci 19848c2ecf20Sopenharmony_ci kfree(schp->pages); 19858c2ecf20Sopenharmony_ci } 19868c2ecf20Sopenharmony_ci } 19878c2ecf20Sopenharmony_ci memset(schp, 0, sizeof (*schp)); 19888c2ecf20Sopenharmony_ci} 19898c2ecf20Sopenharmony_ci 19908c2ecf20Sopenharmony_cistatic int 19918c2ecf20Sopenharmony_cisg_read_oxfer(Sg_request * srp, char __user *outp, int num_read_xfer) 19928c2ecf20Sopenharmony_ci{ 19938c2ecf20Sopenharmony_ci Sg_scatter_hold *schp = &srp->data; 19948c2ecf20Sopenharmony_ci int k, num; 19958c2ecf20Sopenharmony_ci 19968c2ecf20Sopenharmony_ci SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, srp->parentfp->parentdp, 19978c2ecf20Sopenharmony_ci "sg_read_oxfer: num_read_xfer=%d\n", 19988c2ecf20Sopenharmony_ci num_read_xfer)); 19998c2ecf20Sopenharmony_ci if ((!outp) || (num_read_xfer <= 0)) 20008c2ecf20Sopenharmony_ci return 0; 20018c2ecf20Sopenharmony_ci 20028c2ecf20Sopenharmony_ci num = 1 << (PAGE_SHIFT + schp->page_order); 20038c2ecf20Sopenharmony_ci for (k = 0; k < schp->k_use_sg && schp->pages[k]; k++) { 20048c2ecf20Sopenharmony_ci if (num > num_read_xfer) { 20058c2ecf20Sopenharmony_ci if (copy_to_user(outp, page_address(schp->pages[k]), 20068c2ecf20Sopenharmony_ci num_read_xfer)) 20078c2ecf20Sopenharmony_ci return -EFAULT; 20088c2ecf20Sopenharmony_ci break; 20098c2ecf20Sopenharmony_ci } else { 20108c2ecf20Sopenharmony_ci if (copy_to_user(outp, page_address(schp->pages[k]), 20118c2ecf20Sopenharmony_ci num)) 20128c2ecf20Sopenharmony_ci return -EFAULT; 20138c2ecf20Sopenharmony_ci num_read_xfer -= num; 20148c2ecf20Sopenharmony_ci if (num_read_xfer <= 0) 20158c2ecf20Sopenharmony_ci break; 20168c2ecf20Sopenharmony_ci outp += num; 20178c2ecf20Sopenharmony_ci } 20188c2ecf20Sopenharmony_ci } 20198c2ecf20Sopenharmony_ci 20208c2ecf20Sopenharmony_ci return 0; 20218c2ecf20Sopenharmony_ci} 20228c2ecf20Sopenharmony_ci 20238c2ecf20Sopenharmony_cistatic void 20248c2ecf20Sopenharmony_cisg_build_reserve(Sg_fd * sfp, int req_size) 20258c2ecf20Sopenharmony_ci{ 20268c2ecf20Sopenharmony_ci Sg_scatter_hold *schp = &sfp->reserve; 20278c2ecf20Sopenharmony_ci 20288c2ecf20Sopenharmony_ci SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sfp->parentdp, 20298c2ecf20Sopenharmony_ci "sg_build_reserve: req_size=%d\n", req_size)); 20308c2ecf20Sopenharmony_ci do { 20318c2ecf20Sopenharmony_ci if (req_size < PAGE_SIZE) 20328c2ecf20Sopenharmony_ci req_size = PAGE_SIZE; 20338c2ecf20Sopenharmony_ci if (0 == sg_build_indirect(schp, sfp, req_size)) 20348c2ecf20Sopenharmony_ci return; 20358c2ecf20Sopenharmony_ci else 20368c2ecf20Sopenharmony_ci sg_remove_scat(sfp, schp); 20378c2ecf20Sopenharmony_ci req_size >>= 1; /* divide by 2 */ 20388c2ecf20Sopenharmony_ci } while (req_size > (PAGE_SIZE / 2)); 20398c2ecf20Sopenharmony_ci} 20408c2ecf20Sopenharmony_ci 20418c2ecf20Sopenharmony_cistatic void 20428c2ecf20Sopenharmony_cisg_link_reserve(Sg_fd * sfp, Sg_request * srp, int size) 20438c2ecf20Sopenharmony_ci{ 20448c2ecf20Sopenharmony_ci Sg_scatter_hold *req_schp = &srp->data; 20458c2ecf20Sopenharmony_ci Sg_scatter_hold *rsv_schp = &sfp->reserve; 20468c2ecf20Sopenharmony_ci int k, num, rem; 20478c2ecf20Sopenharmony_ci 20488c2ecf20Sopenharmony_ci srp->res_used = 1; 20498c2ecf20Sopenharmony_ci SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sfp->parentdp, 20508c2ecf20Sopenharmony_ci "sg_link_reserve: size=%d\n", size)); 20518c2ecf20Sopenharmony_ci rem = size; 20528c2ecf20Sopenharmony_ci 20538c2ecf20Sopenharmony_ci num = 1 << (PAGE_SHIFT + rsv_schp->page_order); 20548c2ecf20Sopenharmony_ci for (k = 0; k < rsv_schp->k_use_sg; k++) { 20558c2ecf20Sopenharmony_ci if (rem <= num) { 20568c2ecf20Sopenharmony_ci req_schp->k_use_sg = k + 1; 20578c2ecf20Sopenharmony_ci req_schp->sglist_len = rsv_schp->sglist_len; 20588c2ecf20Sopenharmony_ci req_schp->pages = rsv_schp->pages; 20598c2ecf20Sopenharmony_ci 20608c2ecf20Sopenharmony_ci req_schp->bufflen = size; 20618c2ecf20Sopenharmony_ci req_schp->page_order = rsv_schp->page_order; 20628c2ecf20Sopenharmony_ci break; 20638c2ecf20Sopenharmony_ci } else 20648c2ecf20Sopenharmony_ci rem -= num; 20658c2ecf20Sopenharmony_ci } 20668c2ecf20Sopenharmony_ci 20678c2ecf20Sopenharmony_ci if (k >= rsv_schp->k_use_sg) 20688c2ecf20Sopenharmony_ci SCSI_LOG_TIMEOUT(1, sg_printk(KERN_INFO, sfp->parentdp, 20698c2ecf20Sopenharmony_ci "sg_link_reserve: BAD size\n")); 20708c2ecf20Sopenharmony_ci} 20718c2ecf20Sopenharmony_ci 20728c2ecf20Sopenharmony_cistatic void 20738c2ecf20Sopenharmony_cisg_unlink_reserve(Sg_fd * sfp, Sg_request * srp) 20748c2ecf20Sopenharmony_ci{ 20758c2ecf20Sopenharmony_ci Sg_scatter_hold *req_schp = &srp->data; 20768c2ecf20Sopenharmony_ci 20778c2ecf20Sopenharmony_ci SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, srp->parentfp->parentdp, 20788c2ecf20Sopenharmony_ci "sg_unlink_reserve: req->k_use_sg=%d\n", 20798c2ecf20Sopenharmony_ci (int) req_schp->k_use_sg)); 20808c2ecf20Sopenharmony_ci req_schp->k_use_sg = 0; 20818c2ecf20Sopenharmony_ci req_schp->bufflen = 0; 20828c2ecf20Sopenharmony_ci req_schp->pages = NULL; 20838c2ecf20Sopenharmony_ci req_schp->page_order = 0; 20848c2ecf20Sopenharmony_ci req_schp->sglist_len = 0; 20858c2ecf20Sopenharmony_ci srp->res_used = 0; 20868c2ecf20Sopenharmony_ci /* Called without mutex lock to avoid deadlock */ 20878c2ecf20Sopenharmony_ci sfp->res_in_use = 0; 20888c2ecf20Sopenharmony_ci} 20898c2ecf20Sopenharmony_ci 20908c2ecf20Sopenharmony_cistatic Sg_request * 20918c2ecf20Sopenharmony_cisg_get_rq_mark(Sg_fd * sfp, int pack_id, bool *busy) 20928c2ecf20Sopenharmony_ci{ 20938c2ecf20Sopenharmony_ci Sg_request *resp; 20948c2ecf20Sopenharmony_ci unsigned long iflags; 20958c2ecf20Sopenharmony_ci 20968c2ecf20Sopenharmony_ci *busy = false; 20978c2ecf20Sopenharmony_ci write_lock_irqsave(&sfp->rq_list_lock, iflags); 20988c2ecf20Sopenharmony_ci list_for_each_entry(resp, &sfp->rq_list, entry) { 20998c2ecf20Sopenharmony_ci /* look for requests that are not SG_IO owned */ 21008c2ecf20Sopenharmony_ci if ((!resp->sg_io_owned) && 21018c2ecf20Sopenharmony_ci ((-1 == pack_id) || (resp->header.pack_id == pack_id))) { 21028c2ecf20Sopenharmony_ci switch (resp->done) { 21038c2ecf20Sopenharmony_ci case 0: /* request active */ 21048c2ecf20Sopenharmony_ci *busy = true; 21058c2ecf20Sopenharmony_ci break; 21068c2ecf20Sopenharmony_ci case 1: /* request done; response ready to return */ 21078c2ecf20Sopenharmony_ci resp->done = 2; /* guard against other readers */ 21088c2ecf20Sopenharmony_ci write_unlock_irqrestore(&sfp->rq_list_lock, iflags); 21098c2ecf20Sopenharmony_ci return resp; 21108c2ecf20Sopenharmony_ci case 2: /* response already being returned */ 21118c2ecf20Sopenharmony_ci break; 21128c2ecf20Sopenharmony_ci } 21138c2ecf20Sopenharmony_ci } 21148c2ecf20Sopenharmony_ci } 21158c2ecf20Sopenharmony_ci write_unlock_irqrestore(&sfp->rq_list_lock, iflags); 21168c2ecf20Sopenharmony_ci return NULL; 21178c2ecf20Sopenharmony_ci} 21188c2ecf20Sopenharmony_ci 21198c2ecf20Sopenharmony_ci/* always adds to end of list */ 21208c2ecf20Sopenharmony_cistatic Sg_request * 21218c2ecf20Sopenharmony_cisg_add_request(Sg_fd * sfp) 21228c2ecf20Sopenharmony_ci{ 21238c2ecf20Sopenharmony_ci int k; 21248c2ecf20Sopenharmony_ci unsigned long iflags; 21258c2ecf20Sopenharmony_ci Sg_request *rp = sfp->req_arr; 21268c2ecf20Sopenharmony_ci 21278c2ecf20Sopenharmony_ci write_lock_irqsave(&sfp->rq_list_lock, iflags); 21288c2ecf20Sopenharmony_ci if (!list_empty(&sfp->rq_list)) { 21298c2ecf20Sopenharmony_ci if (!sfp->cmd_q) 21308c2ecf20Sopenharmony_ci goto out_unlock; 21318c2ecf20Sopenharmony_ci 21328c2ecf20Sopenharmony_ci for (k = 0; k < SG_MAX_QUEUE; ++k, ++rp) { 21338c2ecf20Sopenharmony_ci if (!rp->parentfp) 21348c2ecf20Sopenharmony_ci break; 21358c2ecf20Sopenharmony_ci } 21368c2ecf20Sopenharmony_ci if (k >= SG_MAX_QUEUE) 21378c2ecf20Sopenharmony_ci goto out_unlock; 21388c2ecf20Sopenharmony_ci } 21398c2ecf20Sopenharmony_ci memset(rp, 0, sizeof (Sg_request)); 21408c2ecf20Sopenharmony_ci rp->parentfp = sfp; 21418c2ecf20Sopenharmony_ci rp->header.duration = jiffies_to_msecs(jiffies); 21428c2ecf20Sopenharmony_ci list_add_tail(&rp->entry, &sfp->rq_list); 21438c2ecf20Sopenharmony_ci write_unlock_irqrestore(&sfp->rq_list_lock, iflags); 21448c2ecf20Sopenharmony_ci return rp; 21458c2ecf20Sopenharmony_ciout_unlock: 21468c2ecf20Sopenharmony_ci write_unlock_irqrestore(&sfp->rq_list_lock, iflags); 21478c2ecf20Sopenharmony_ci return NULL; 21488c2ecf20Sopenharmony_ci} 21498c2ecf20Sopenharmony_ci 21508c2ecf20Sopenharmony_ci/* Return of 1 for found; 0 for not found */ 21518c2ecf20Sopenharmony_cistatic int 21528c2ecf20Sopenharmony_cisg_remove_request(Sg_fd * sfp, Sg_request * srp) 21538c2ecf20Sopenharmony_ci{ 21548c2ecf20Sopenharmony_ci unsigned long iflags; 21558c2ecf20Sopenharmony_ci int res = 0; 21568c2ecf20Sopenharmony_ci 21578c2ecf20Sopenharmony_ci if (!sfp || !srp || list_empty(&sfp->rq_list)) 21588c2ecf20Sopenharmony_ci return res; 21598c2ecf20Sopenharmony_ci write_lock_irqsave(&sfp->rq_list_lock, iflags); 21608c2ecf20Sopenharmony_ci if (!list_empty(&srp->entry)) { 21618c2ecf20Sopenharmony_ci list_del(&srp->entry); 21628c2ecf20Sopenharmony_ci srp->parentfp = NULL; 21638c2ecf20Sopenharmony_ci res = 1; 21648c2ecf20Sopenharmony_ci } 21658c2ecf20Sopenharmony_ci write_unlock_irqrestore(&sfp->rq_list_lock, iflags); 21668c2ecf20Sopenharmony_ci 21678c2ecf20Sopenharmony_ci /* 21688c2ecf20Sopenharmony_ci * If the device is detaching, wakeup any readers in case we just 21698c2ecf20Sopenharmony_ci * removed the last response, which would leave nothing for them to 21708c2ecf20Sopenharmony_ci * return other than -ENODEV. 21718c2ecf20Sopenharmony_ci */ 21728c2ecf20Sopenharmony_ci if (unlikely(atomic_read(&sfp->parentdp->detaching))) 21738c2ecf20Sopenharmony_ci wake_up_interruptible_all(&sfp->read_wait); 21748c2ecf20Sopenharmony_ci 21758c2ecf20Sopenharmony_ci return res; 21768c2ecf20Sopenharmony_ci} 21778c2ecf20Sopenharmony_ci 21788c2ecf20Sopenharmony_cistatic Sg_fd * 21798c2ecf20Sopenharmony_cisg_add_sfp(Sg_device * sdp) 21808c2ecf20Sopenharmony_ci{ 21818c2ecf20Sopenharmony_ci Sg_fd *sfp; 21828c2ecf20Sopenharmony_ci unsigned long iflags; 21838c2ecf20Sopenharmony_ci int bufflen; 21848c2ecf20Sopenharmony_ci 21858c2ecf20Sopenharmony_ci sfp = kzalloc(sizeof(*sfp), GFP_ATOMIC | __GFP_NOWARN); 21868c2ecf20Sopenharmony_ci if (!sfp) 21878c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 21888c2ecf20Sopenharmony_ci 21898c2ecf20Sopenharmony_ci init_waitqueue_head(&sfp->read_wait); 21908c2ecf20Sopenharmony_ci rwlock_init(&sfp->rq_list_lock); 21918c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&sfp->rq_list); 21928c2ecf20Sopenharmony_ci kref_init(&sfp->f_ref); 21938c2ecf20Sopenharmony_ci mutex_init(&sfp->f_mutex); 21948c2ecf20Sopenharmony_ci sfp->timeout = SG_DEFAULT_TIMEOUT; 21958c2ecf20Sopenharmony_ci sfp->timeout_user = SG_DEFAULT_TIMEOUT_USER; 21968c2ecf20Sopenharmony_ci sfp->force_packid = SG_DEF_FORCE_PACK_ID; 21978c2ecf20Sopenharmony_ci sfp->cmd_q = SG_DEF_COMMAND_Q; 21988c2ecf20Sopenharmony_ci sfp->keep_orphan = SG_DEF_KEEP_ORPHAN; 21998c2ecf20Sopenharmony_ci sfp->parentdp = sdp; 22008c2ecf20Sopenharmony_ci write_lock_irqsave(&sdp->sfd_lock, iflags); 22018c2ecf20Sopenharmony_ci if (atomic_read(&sdp->detaching)) { 22028c2ecf20Sopenharmony_ci write_unlock_irqrestore(&sdp->sfd_lock, iflags); 22038c2ecf20Sopenharmony_ci kfree(sfp); 22048c2ecf20Sopenharmony_ci return ERR_PTR(-ENODEV); 22058c2ecf20Sopenharmony_ci } 22068c2ecf20Sopenharmony_ci list_add_tail(&sfp->sfd_siblings, &sdp->sfds); 22078c2ecf20Sopenharmony_ci write_unlock_irqrestore(&sdp->sfd_lock, iflags); 22088c2ecf20Sopenharmony_ci SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, 22098c2ecf20Sopenharmony_ci "sg_add_sfp: sfp=0x%p\n", sfp)); 22108c2ecf20Sopenharmony_ci if (unlikely(sg_big_buff != def_reserved_size)) 22118c2ecf20Sopenharmony_ci sg_big_buff = def_reserved_size; 22128c2ecf20Sopenharmony_ci 22138c2ecf20Sopenharmony_ci bufflen = min_t(int, sg_big_buff, 22148c2ecf20Sopenharmony_ci max_sectors_bytes(sdp->device->request_queue)); 22158c2ecf20Sopenharmony_ci sg_build_reserve(sfp, bufflen); 22168c2ecf20Sopenharmony_ci SCSI_LOG_TIMEOUT(3, sg_printk(KERN_INFO, sdp, 22178c2ecf20Sopenharmony_ci "sg_add_sfp: bufflen=%d, k_use_sg=%d\n", 22188c2ecf20Sopenharmony_ci sfp->reserve.bufflen, 22198c2ecf20Sopenharmony_ci sfp->reserve.k_use_sg)); 22208c2ecf20Sopenharmony_ci 22218c2ecf20Sopenharmony_ci kref_get(&sdp->d_ref); 22228c2ecf20Sopenharmony_ci __module_get(THIS_MODULE); 22238c2ecf20Sopenharmony_ci return sfp; 22248c2ecf20Sopenharmony_ci} 22258c2ecf20Sopenharmony_ci 22268c2ecf20Sopenharmony_cistatic void 22278c2ecf20Sopenharmony_cisg_remove_sfp_usercontext(struct work_struct *work) 22288c2ecf20Sopenharmony_ci{ 22298c2ecf20Sopenharmony_ci struct sg_fd *sfp = container_of(work, struct sg_fd, ew.work); 22308c2ecf20Sopenharmony_ci struct sg_device *sdp = sfp->parentdp; 22318c2ecf20Sopenharmony_ci Sg_request *srp; 22328c2ecf20Sopenharmony_ci unsigned long iflags; 22338c2ecf20Sopenharmony_ci 22348c2ecf20Sopenharmony_ci /* Cleanup any responses which were never read(). */ 22358c2ecf20Sopenharmony_ci write_lock_irqsave(&sfp->rq_list_lock, iflags); 22368c2ecf20Sopenharmony_ci while (!list_empty(&sfp->rq_list)) { 22378c2ecf20Sopenharmony_ci srp = list_first_entry(&sfp->rq_list, Sg_request, entry); 22388c2ecf20Sopenharmony_ci sg_finish_rem_req(srp); 22398c2ecf20Sopenharmony_ci list_del(&srp->entry); 22408c2ecf20Sopenharmony_ci srp->parentfp = NULL; 22418c2ecf20Sopenharmony_ci } 22428c2ecf20Sopenharmony_ci write_unlock_irqrestore(&sfp->rq_list_lock, iflags); 22438c2ecf20Sopenharmony_ci 22448c2ecf20Sopenharmony_ci if (sfp->reserve.bufflen > 0) { 22458c2ecf20Sopenharmony_ci SCSI_LOG_TIMEOUT(6, sg_printk(KERN_INFO, sdp, 22468c2ecf20Sopenharmony_ci "sg_remove_sfp: bufflen=%d, k_use_sg=%d\n", 22478c2ecf20Sopenharmony_ci (int) sfp->reserve.bufflen, 22488c2ecf20Sopenharmony_ci (int) sfp->reserve.k_use_sg)); 22498c2ecf20Sopenharmony_ci sg_remove_scat(sfp, &sfp->reserve); 22508c2ecf20Sopenharmony_ci } 22518c2ecf20Sopenharmony_ci 22528c2ecf20Sopenharmony_ci SCSI_LOG_TIMEOUT(6, sg_printk(KERN_INFO, sdp, 22538c2ecf20Sopenharmony_ci "sg_remove_sfp: sfp=0x%p\n", sfp)); 22548c2ecf20Sopenharmony_ci kfree(sfp); 22558c2ecf20Sopenharmony_ci 22568c2ecf20Sopenharmony_ci scsi_device_put(sdp->device); 22578c2ecf20Sopenharmony_ci kref_put(&sdp->d_ref, sg_device_destroy); 22588c2ecf20Sopenharmony_ci module_put(THIS_MODULE); 22598c2ecf20Sopenharmony_ci} 22608c2ecf20Sopenharmony_ci 22618c2ecf20Sopenharmony_cistatic void 22628c2ecf20Sopenharmony_cisg_remove_sfp(struct kref *kref) 22638c2ecf20Sopenharmony_ci{ 22648c2ecf20Sopenharmony_ci struct sg_fd *sfp = container_of(kref, struct sg_fd, f_ref); 22658c2ecf20Sopenharmony_ci struct sg_device *sdp = sfp->parentdp; 22668c2ecf20Sopenharmony_ci unsigned long iflags; 22678c2ecf20Sopenharmony_ci 22688c2ecf20Sopenharmony_ci write_lock_irqsave(&sdp->sfd_lock, iflags); 22698c2ecf20Sopenharmony_ci list_del(&sfp->sfd_siblings); 22708c2ecf20Sopenharmony_ci write_unlock_irqrestore(&sdp->sfd_lock, iflags); 22718c2ecf20Sopenharmony_ci 22728c2ecf20Sopenharmony_ci INIT_WORK(&sfp->ew.work, sg_remove_sfp_usercontext); 22738c2ecf20Sopenharmony_ci schedule_work(&sfp->ew.work); 22748c2ecf20Sopenharmony_ci} 22758c2ecf20Sopenharmony_ci 22768c2ecf20Sopenharmony_ci#ifdef CONFIG_SCSI_PROC_FS 22778c2ecf20Sopenharmony_cistatic int 22788c2ecf20Sopenharmony_cisg_idr_max_id(int id, void *p, void *data) 22798c2ecf20Sopenharmony_ci{ 22808c2ecf20Sopenharmony_ci int *k = data; 22818c2ecf20Sopenharmony_ci 22828c2ecf20Sopenharmony_ci if (*k < id) 22838c2ecf20Sopenharmony_ci *k = id; 22848c2ecf20Sopenharmony_ci 22858c2ecf20Sopenharmony_ci return 0; 22868c2ecf20Sopenharmony_ci} 22878c2ecf20Sopenharmony_ci 22888c2ecf20Sopenharmony_cistatic int 22898c2ecf20Sopenharmony_cisg_last_dev(void) 22908c2ecf20Sopenharmony_ci{ 22918c2ecf20Sopenharmony_ci int k = -1; 22928c2ecf20Sopenharmony_ci unsigned long iflags; 22938c2ecf20Sopenharmony_ci 22948c2ecf20Sopenharmony_ci read_lock_irqsave(&sg_index_lock, iflags); 22958c2ecf20Sopenharmony_ci idr_for_each(&sg_index_idr, sg_idr_max_id, &k); 22968c2ecf20Sopenharmony_ci read_unlock_irqrestore(&sg_index_lock, iflags); 22978c2ecf20Sopenharmony_ci return k + 1; /* origin 1 */ 22988c2ecf20Sopenharmony_ci} 22998c2ecf20Sopenharmony_ci#endif 23008c2ecf20Sopenharmony_ci 23018c2ecf20Sopenharmony_ci/* must be called with sg_index_lock held */ 23028c2ecf20Sopenharmony_cistatic Sg_device *sg_lookup_dev(int dev) 23038c2ecf20Sopenharmony_ci{ 23048c2ecf20Sopenharmony_ci return idr_find(&sg_index_idr, dev); 23058c2ecf20Sopenharmony_ci} 23068c2ecf20Sopenharmony_ci 23078c2ecf20Sopenharmony_cistatic Sg_device * 23088c2ecf20Sopenharmony_cisg_get_dev(int dev) 23098c2ecf20Sopenharmony_ci{ 23108c2ecf20Sopenharmony_ci struct sg_device *sdp; 23118c2ecf20Sopenharmony_ci unsigned long flags; 23128c2ecf20Sopenharmony_ci 23138c2ecf20Sopenharmony_ci read_lock_irqsave(&sg_index_lock, flags); 23148c2ecf20Sopenharmony_ci sdp = sg_lookup_dev(dev); 23158c2ecf20Sopenharmony_ci if (!sdp) 23168c2ecf20Sopenharmony_ci sdp = ERR_PTR(-ENXIO); 23178c2ecf20Sopenharmony_ci else if (atomic_read(&sdp->detaching)) { 23188c2ecf20Sopenharmony_ci /* If sdp->detaching, then the refcount may already be 0, in 23198c2ecf20Sopenharmony_ci * which case it would be a bug to do kref_get(). 23208c2ecf20Sopenharmony_ci */ 23218c2ecf20Sopenharmony_ci sdp = ERR_PTR(-ENODEV); 23228c2ecf20Sopenharmony_ci } else 23238c2ecf20Sopenharmony_ci kref_get(&sdp->d_ref); 23248c2ecf20Sopenharmony_ci read_unlock_irqrestore(&sg_index_lock, flags); 23258c2ecf20Sopenharmony_ci 23268c2ecf20Sopenharmony_ci return sdp; 23278c2ecf20Sopenharmony_ci} 23288c2ecf20Sopenharmony_ci 23298c2ecf20Sopenharmony_ci#ifdef CONFIG_SCSI_PROC_FS 23308c2ecf20Sopenharmony_cistatic int sg_proc_seq_show_int(struct seq_file *s, void *v); 23318c2ecf20Sopenharmony_ci 23328c2ecf20Sopenharmony_cistatic int sg_proc_single_open_adio(struct inode *inode, struct file *file); 23338c2ecf20Sopenharmony_cistatic ssize_t sg_proc_write_adio(struct file *filp, const char __user *buffer, 23348c2ecf20Sopenharmony_ci size_t count, loff_t *off); 23358c2ecf20Sopenharmony_cistatic const struct proc_ops adio_proc_ops = { 23368c2ecf20Sopenharmony_ci .proc_open = sg_proc_single_open_adio, 23378c2ecf20Sopenharmony_ci .proc_read = seq_read, 23388c2ecf20Sopenharmony_ci .proc_lseek = seq_lseek, 23398c2ecf20Sopenharmony_ci .proc_write = sg_proc_write_adio, 23408c2ecf20Sopenharmony_ci .proc_release = single_release, 23418c2ecf20Sopenharmony_ci}; 23428c2ecf20Sopenharmony_ci 23438c2ecf20Sopenharmony_cistatic int sg_proc_single_open_dressz(struct inode *inode, struct file *file); 23448c2ecf20Sopenharmony_cistatic ssize_t sg_proc_write_dressz(struct file *filp, 23458c2ecf20Sopenharmony_ci const char __user *buffer, size_t count, loff_t *off); 23468c2ecf20Sopenharmony_cistatic const struct proc_ops dressz_proc_ops = { 23478c2ecf20Sopenharmony_ci .proc_open = sg_proc_single_open_dressz, 23488c2ecf20Sopenharmony_ci .proc_read = seq_read, 23498c2ecf20Sopenharmony_ci .proc_lseek = seq_lseek, 23508c2ecf20Sopenharmony_ci .proc_write = sg_proc_write_dressz, 23518c2ecf20Sopenharmony_ci .proc_release = single_release, 23528c2ecf20Sopenharmony_ci}; 23538c2ecf20Sopenharmony_ci 23548c2ecf20Sopenharmony_cistatic int sg_proc_seq_show_version(struct seq_file *s, void *v); 23558c2ecf20Sopenharmony_cistatic int sg_proc_seq_show_devhdr(struct seq_file *s, void *v); 23568c2ecf20Sopenharmony_cistatic int sg_proc_seq_show_dev(struct seq_file *s, void *v); 23578c2ecf20Sopenharmony_cistatic void * dev_seq_start(struct seq_file *s, loff_t *pos); 23588c2ecf20Sopenharmony_cistatic void * dev_seq_next(struct seq_file *s, void *v, loff_t *pos); 23598c2ecf20Sopenharmony_cistatic void dev_seq_stop(struct seq_file *s, void *v); 23608c2ecf20Sopenharmony_cistatic const struct seq_operations dev_seq_ops = { 23618c2ecf20Sopenharmony_ci .start = dev_seq_start, 23628c2ecf20Sopenharmony_ci .next = dev_seq_next, 23638c2ecf20Sopenharmony_ci .stop = dev_seq_stop, 23648c2ecf20Sopenharmony_ci .show = sg_proc_seq_show_dev, 23658c2ecf20Sopenharmony_ci}; 23668c2ecf20Sopenharmony_ci 23678c2ecf20Sopenharmony_cistatic int sg_proc_seq_show_devstrs(struct seq_file *s, void *v); 23688c2ecf20Sopenharmony_cistatic const struct seq_operations devstrs_seq_ops = { 23698c2ecf20Sopenharmony_ci .start = dev_seq_start, 23708c2ecf20Sopenharmony_ci .next = dev_seq_next, 23718c2ecf20Sopenharmony_ci .stop = dev_seq_stop, 23728c2ecf20Sopenharmony_ci .show = sg_proc_seq_show_devstrs, 23738c2ecf20Sopenharmony_ci}; 23748c2ecf20Sopenharmony_ci 23758c2ecf20Sopenharmony_cistatic int sg_proc_seq_show_debug(struct seq_file *s, void *v); 23768c2ecf20Sopenharmony_cistatic const struct seq_operations debug_seq_ops = { 23778c2ecf20Sopenharmony_ci .start = dev_seq_start, 23788c2ecf20Sopenharmony_ci .next = dev_seq_next, 23798c2ecf20Sopenharmony_ci .stop = dev_seq_stop, 23808c2ecf20Sopenharmony_ci .show = sg_proc_seq_show_debug, 23818c2ecf20Sopenharmony_ci}; 23828c2ecf20Sopenharmony_ci 23838c2ecf20Sopenharmony_cistatic int 23848c2ecf20Sopenharmony_cisg_proc_init(void) 23858c2ecf20Sopenharmony_ci{ 23868c2ecf20Sopenharmony_ci struct proc_dir_entry *p; 23878c2ecf20Sopenharmony_ci 23888c2ecf20Sopenharmony_ci p = proc_mkdir("scsi/sg", NULL); 23898c2ecf20Sopenharmony_ci if (!p) 23908c2ecf20Sopenharmony_ci return 1; 23918c2ecf20Sopenharmony_ci 23928c2ecf20Sopenharmony_ci proc_create("allow_dio", S_IRUGO | S_IWUSR, p, &adio_proc_ops); 23938c2ecf20Sopenharmony_ci proc_create_seq("debug", S_IRUGO, p, &debug_seq_ops); 23948c2ecf20Sopenharmony_ci proc_create("def_reserved_size", S_IRUGO | S_IWUSR, p, &dressz_proc_ops); 23958c2ecf20Sopenharmony_ci proc_create_single("device_hdr", S_IRUGO, p, sg_proc_seq_show_devhdr); 23968c2ecf20Sopenharmony_ci proc_create_seq("devices", S_IRUGO, p, &dev_seq_ops); 23978c2ecf20Sopenharmony_ci proc_create_seq("device_strs", S_IRUGO, p, &devstrs_seq_ops); 23988c2ecf20Sopenharmony_ci proc_create_single("version", S_IRUGO, p, sg_proc_seq_show_version); 23998c2ecf20Sopenharmony_ci return 0; 24008c2ecf20Sopenharmony_ci} 24018c2ecf20Sopenharmony_ci 24028c2ecf20Sopenharmony_ci 24038c2ecf20Sopenharmony_cistatic int sg_proc_seq_show_int(struct seq_file *s, void *v) 24048c2ecf20Sopenharmony_ci{ 24058c2ecf20Sopenharmony_ci seq_printf(s, "%d\n", *((int *)s->private)); 24068c2ecf20Sopenharmony_ci return 0; 24078c2ecf20Sopenharmony_ci} 24088c2ecf20Sopenharmony_ci 24098c2ecf20Sopenharmony_cistatic int sg_proc_single_open_adio(struct inode *inode, struct file *file) 24108c2ecf20Sopenharmony_ci{ 24118c2ecf20Sopenharmony_ci return single_open(file, sg_proc_seq_show_int, &sg_allow_dio); 24128c2ecf20Sopenharmony_ci} 24138c2ecf20Sopenharmony_ci 24148c2ecf20Sopenharmony_cistatic ssize_t 24158c2ecf20Sopenharmony_cisg_proc_write_adio(struct file *filp, const char __user *buffer, 24168c2ecf20Sopenharmony_ci size_t count, loff_t *off) 24178c2ecf20Sopenharmony_ci{ 24188c2ecf20Sopenharmony_ci int err; 24198c2ecf20Sopenharmony_ci unsigned long num; 24208c2ecf20Sopenharmony_ci 24218c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) 24228c2ecf20Sopenharmony_ci return -EACCES; 24238c2ecf20Sopenharmony_ci err = kstrtoul_from_user(buffer, count, 0, &num); 24248c2ecf20Sopenharmony_ci if (err) 24258c2ecf20Sopenharmony_ci return err; 24268c2ecf20Sopenharmony_ci sg_allow_dio = num ? 1 : 0; 24278c2ecf20Sopenharmony_ci return count; 24288c2ecf20Sopenharmony_ci} 24298c2ecf20Sopenharmony_ci 24308c2ecf20Sopenharmony_cistatic int sg_proc_single_open_dressz(struct inode *inode, struct file *file) 24318c2ecf20Sopenharmony_ci{ 24328c2ecf20Sopenharmony_ci return single_open(file, sg_proc_seq_show_int, &sg_big_buff); 24338c2ecf20Sopenharmony_ci} 24348c2ecf20Sopenharmony_ci 24358c2ecf20Sopenharmony_cistatic ssize_t 24368c2ecf20Sopenharmony_cisg_proc_write_dressz(struct file *filp, const char __user *buffer, 24378c2ecf20Sopenharmony_ci size_t count, loff_t *off) 24388c2ecf20Sopenharmony_ci{ 24398c2ecf20Sopenharmony_ci int err; 24408c2ecf20Sopenharmony_ci unsigned long k = ULONG_MAX; 24418c2ecf20Sopenharmony_ci 24428c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) 24438c2ecf20Sopenharmony_ci return -EACCES; 24448c2ecf20Sopenharmony_ci 24458c2ecf20Sopenharmony_ci err = kstrtoul_from_user(buffer, count, 0, &k); 24468c2ecf20Sopenharmony_ci if (err) 24478c2ecf20Sopenharmony_ci return err; 24488c2ecf20Sopenharmony_ci if (k <= 1048576) { /* limit "big buff" to 1 MB */ 24498c2ecf20Sopenharmony_ci sg_big_buff = k; 24508c2ecf20Sopenharmony_ci return count; 24518c2ecf20Sopenharmony_ci } 24528c2ecf20Sopenharmony_ci return -ERANGE; 24538c2ecf20Sopenharmony_ci} 24548c2ecf20Sopenharmony_ci 24558c2ecf20Sopenharmony_cistatic int sg_proc_seq_show_version(struct seq_file *s, void *v) 24568c2ecf20Sopenharmony_ci{ 24578c2ecf20Sopenharmony_ci seq_printf(s, "%d\t%s [%s]\n", sg_version_num, SG_VERSION_STR, 24588c2ecf20Sopenharmony_ci sg_version_date); 24598c2ecf20Sopenharmony_ci return 0; 24608c2ecf20Sopenharmony_ci} 24618c2ecf20Sopenharmony_ci 24628c2ecf20Sopenharmony_cistatic int sg_proc_seq_show_devhdr(struct seq_file *s, void *v) 24638c2ecf20Sopenharmony_ci{ 24648c2ecf20Sopenharmony_ci seq_puts(s, "host\tchan\tid\tlun\ttype\topens\tqdepth\tbusy\tonline\n"); 24658c2ecf20Sopenharmony_ci return 0; 24668c2ecf20Sopenharmony_ci} 24678c2ecf20Sopenharmony_ci 24688c2ecf20Sopenharmony_cistruct sg_proc_deviter { 24698c2ecf20Sopenharmony_ci loff_t index; 24708c2ecf20Sopenharmony_ci size_t max; 24718c2ecf20Sopenharmony_ci}; 24728c2ecf20Sopenharmony_ci 24738c2ecf20Sopenharmony_cistatic void * dev_seq_start(struct seq_file *s, loff_t *pos) 24748c2ecf20Sopenharmony_ci{ 24758c2ecf20Sopenharmony_ci struct sg_proc_deviter * it = kmalloc(sizeof(*it), GFP_KERNEL); 24768c2ecf20Sopenharmony_ci 24778c2ecf20Sopenharmony_ci s->private = it; 24788c2ecf20Sopenharmony_ci if (! it) 24798c2ecf20Sopenharmony_ci return NULL; 24808c2ecf20Sopenharmony_ci 24818c2ecf20Sopenharmony_ci it->index = *pos; 24828c2ecf20Sopenharmony_ci it->max = sg_last_dev(); 24838c2ecf20Sopenharmony_ci if (it->index >= it->max) 24848c2ecf20Sopenharmony_ci return NULL; 24858c2ecf20Sopenharmony_ci return it; 24868c2ecf20Sopenharmony_ci} 24878c2ecf20Sopenharmony_ci 24888c2ecf20Sopenharmony_cistatic void * dev_seq_next(struct seq_file *s, void *v, loff_t *pos) 24898c2ecf20Sopenharmony_ci{ 24908c2ecf20Sopenharmony_ci struct sg_proc_deviter * it = s->private; 24918c2ecf20Sopenharmony_ci 24928c2ecf20Sopenharmony_ci *pos = ++it->index; 24938c2ecf20Sopenharmony_ci return (it->index < it->max) ? it : NULL; 24948c2ecf20Sopenharmony_ci} 24958c2ecf20Sopenharmony_ci 24968c2ecf20Sopenharmony_cistatic void dev_seq_stop(struct seq_file *s, void *v) 24978c2ecf20Sopenharmony_ci{ 24988c2ecf20Sopenharmony_ci kfree(s->private); 24998c2ecf20Sopenharmony_ci} 25008c2ecf20Sopenharmony_ci 25018c2ecf20Sopenharmony_cistatic int sg_proc_seq_show_dev(struct seq_file *s, void *v) 25028c2ecf20Sopenharmony_ci{ 25038c2ecf20Sopenharmony_ci struct sg_proc_deviter * it = (struct sg_proc_deviter *) v; 25048c2ecf20Sopenharmony_ci Sg_device *sdp; 25058c2ecf20Sopenharmony_ci struct scsi_device *scsidp; 25068c2ecf20Sopenharmony_ci unsigned long iflags; 25078c2ecf20Sopenharmony_ci 25088c2ecf20Sopenharmony_ci read_lock_irqsave(&sg_index_lock, iflags); 25098c2ecf20Sopenharmony_ci sdp = it ? sg_lookup_dev(it->index) : NULL; 25108c2ecf20Sopenharmony_ci if ((NULL == sdp) || (NULL == sdp->device) || 25118c2ecf20Sopenharmony_ci (atomic_read(&sdp->detaching))) 25128c2ecf20Sopenharmony_ci seq_puts(s, "-1\t-1\t-1\t-1\t-1\t-1\t-1\t-1\t-1\n"); 25138c2ecf20Sopenharmony_ci else { 25148c2ecf20Sopenharmony_ci scsidp = sdp->device; 25158c2ecf20Sopenharmony_ci seq_printf(s, "%d\t%d\t%d\t%llu\t%d\t%d\t%d\t%d\t%d\n", 25168c2ecf20Sopenharmony_ci scsidp->host->host_no, scsidp->channel, 25178c2ecf20Sopenharmony_ci scsidp->id, scsidp->lun, (int) scsidp->type, 25188c2ecf20Sopenharmony_ci 1, 25198c2ecf20Sopenharmony_ci (int) scsidp->queue_depth, 25208c2ecf20Sopenharmony_ci (int) atomic_read(&scsidp->device_busy), 25218c2ecf20Sopenharmony_ci (int) scsi_device_online(scsidp)); 25228c2ecf20Sopenharmony_ci } 25238c2ecf20Sopenharmony_ci read_unlock_irqrestore(&sg_index_lock, iflags); 25248c2ecf20Sopenharmony_ci return 0; 25258c2ecf20Sopenharmony_ci} 25268c2ecf20Sopenharmony_ci 25278c2ecf20Sopenharmony_cistatic int sg_proc_seq_show_devstrs(struct seq_file *s, void *v) 25288c2ecf20Sopenharmony_ci{ 25298c2ecf20Sopenharmony_ci struct sg_proc_deviter * it = (struct sg_proc_deviter *) v; 25308c2ecf20Sopenharmony_ci Sg_device *sdp; 25318c2ecf20Sopenharmony_ci struct scsi_device *scsidp; 25328c2ecf20Sopenharmony_ci unsigned long iflags; 25338c2ecf20Sopenharmony_ci 25348c2ecf20Sopenharmony_ci read_lock_irqsave(&sg_index_lock, iflags); 25358c2ecf20Sopenharmony_ci sdp = it ? sg_lookup_dev(it->index) : NULL; 25368c2ecf20Sopenharmony_ci scsidp = sdp ? sdp->device : NULL; 25378c2ecf20Sopenharmony_ci if (sdp && scsidp && (!atomic_read(&sdp->detaching))) 25388c2ecf20Sopenharmony_ci seq_printf(s, "%8.8s\t%16.16s\t%4.4s\n", 25398c2ecf20Sopenharmony_ci scsidp->vendor, scsidp->model, scsidp->rev); 25408c2ecf20Sopenharmony_ci else 25418c2ecf20Sopenharmony_ci seq_puts(s, "<no active device>\n"); 25428c2ecf20Sopenharmony_ci read_unlock_irqrestore(&sg_index_lock, iflags); 25438c2ecf20Sopenharmony_ci return 0; 25448c2ecf20Sopenharmony_ci} 25458c2ecf20Sopenharmony_ci 25468c2ecf20Sopenharmony_ci/* must be called while holding sg_index_lock */ 25478c2ecf20Sopenharmony_cistatic void sg_proc_debug_helper(struct seq_file *s, Sg_device * sdp) 25488c2ecf20Sopenharmony_ci{ 25498c2ecf20Sopenharmony_ci int k, new_interface, blen, usg; 25508c2ecf20Sopenharmony_ci Sg_request *srp; 25518c2ecf20Sopenharmony_ci Sg_fd *fp; 25528c2ecf20Sopenharmony_ci const sg_io_hdr_t *hp; 25538c2ecf20Sopenharmony_ci const char * cp; 25548c2ecf20Sopenharmony_ci unsigned int ms; 25558c2ecf20Sopenharmony_ci 25568c2ecf20Sopenharmony_ci k = 0; 25578c2ecf20Sopenharmony_ci list_for_each_entry(fp, &sdp->sfds, sfd_siblings) { 25588c2ecf20Sopenharmony_ci k++; 25598c2ecf20Sopenharmony_ci read_lock(&fp->rq_list_lock); /* irqs already disabled */ 25608c2ecf20Sopenharmony_ci seq_printf(s, " FD(%d): timeout=%dms bufflen=%d " 25618c2ecf20Sopenharmony_ci "(res)sgat=%d low_dma=%d\n", k, 25628c2ecf20Sopenharmony_ci jiffies_to_msecs(fp->timeout), 25638c2ecf20Sopenharmony_ci fp->reserve.bufflen, 25648c2ecf20Sopenharmony_ci (int) fp->reserve.k_use_sg, 25658c2ecf20Sopenharmony_ci (int) sdp->device->host->unchecked_isa_dma); 25668c2ecf20Sopenharmony_ci seq_printf(s, " cmd_q=%d f_packid=%d k_orphan=%d closed=0\n", 25678c2ecf20Sopenharmony_ci (int) fp->cmd_q, (int) fp->force_packid, 25688c2ecf20Sopenharmony_ci (int) fp->keep_orphan); 25698c2ecf20Sopenharmony_ci list_for_each_entry(srp, &fp->rq_list, entry) { 25708c2ecf20Sopenharmony_ci hp = &srp->header; 25718c2ecf20Sopenharmony_ci new_interface = (hp->interface_id == '\0') ? 0 : 1; 25728c2ecf20Sopenharmony_ci if (srp->res_used) { 25738c2ecf20Sopenharmony_ci if (new_interface && 25748c2ecf20Sopenharmony_ci (SG_FLAG_MMAP_IO & hp->flags)) 25758c2ecf20Sopenharmony_ci cp = " mmap>> "; 25768c2ecf20Sopenharmony_ci else 25778c2ecf20Sopenharmony_ci cp = " rb>> "; 25788c2ecf20Sopenharmony_ci } else { 25798c2ecf20Sopenharmony_ci if (SG_INFO_DIRECT_IO_MASK & hp->info) 25808c2ecf20Sopenharmony_ci cp = " dio>> "; 25818c2ecf20Sopenharmony_ci else 25828c2ecf20Sopenharmony_ci cp = " "; 25838c2ecf20Sopenharmony_ci } 25848c2ecf20Sopenharmony_ci seq_puts(s, cp); 25858c2ecf20Sopenharmony_ci blen = srp->data.bufflen; 25868c2ecf20Sopenharmony_ci usg = srp->data.k_use_sg; 25878c2ecf20Sopenharmony_ci seq_puts(s, srp->done ? 25888c2ecf20Sopenharmony_ci ((1 == srp->done) ? "rcv:" : "fin:") 25898c2ecf20Sopenharmony_ci : "act:"); 25908c2ecf20Sopenharmony_ci seq_printf(s, " id=%d blen=%d", 25918c2ecf20Sopenharmony_ci srp->header.pack_id, blen); 25928c2ecf20Sopenharmony_ci if (srp->done) 25938c2ecf20Sopenharmony_ci seq_printf(s, " dur=%d", hp->duration); 25948c2ecf20Sopenharmony_ci else { 25958c2ecf20Sopenharmony_ci ms = jiffies_to_msecs(jiffies); 25968c2ecf20Sopenharmony_ci seq_printf(s, " t_o/elap=%d/%d", 25978c2ecf20Sopenharmony_ci (new_interface ? hp->timeout : 25988c2ecf20Sopenharmony_ci jiffies_to_msecs(fp->timeout)), 25998c2ecf20Sopenharmony_ci (ms > hp->duration ? ms - hp->duration : 0)); 26008c2ecf20Sopenharmony_ci } 26018c2ecf20Sopenharmony_ci seq_printf(s, "ms sgat=%d op=0x%02x\n", usg, 26028c2ecf20Sopenharmony_ci (int) srp->data.cmd_opcode); 26038c2ecf20Sopenharmony_ci } 26048c2ecf20Sopenharmony_ci if (list_empty(&fp->rq_list)) 26058c2ecf20Sopenharmony_ci seq_puts(s, " No requests active\n"); 26068c2ecf20Sopenharmony_ci read_unlock(&fp->rq_list_lock); 26078c2ecf20Sopenharmony_ci } 26088c2ecf20Sopenharmony_ci} 26098c2ecf20Sopenharmony_ci 26108c2ecf20Sopenharmony_cistatic int sg_proc_seq_show_debug(struct seq_file *s, void *v) 26118c2ecf20Sopenharmony_ci{ 26128c2ecf20Sopenharmony_ci struct sg_proc_deviter * it = (struct sg_proc_deviter *) v; 26138c2ecf20Sopenharmony_ci Sg_device *sdp; 26148c2ecf20Sopenharmony_ci unsigned long iflags; 26158c2ecf20Sopenharmony_ci 26168c2ecf20Sopenharmony_ci if (it && (0 == it->index)) 26178c2ecf20Sopenharmony_ci seq_printf(s, "max_active_device=%d def_reserved_size=%d\n", 26188c2ecf20Sopenharmony_ci (int)it->max, sg_big_buff); 26198c2ecf20Sopenharmony_ci 26208c2ecf20Sopenharmony_ci read_lock_irqsave(&sg_index_lock, iflags); 26218c2ecf20Sopenharmony_ci sdp = it ? sg_lookup_dev(it->index) : NULL; 26228c2ecf20Sopenharmony_ci if (NULL == sdp) 26238c2ecf20Sopenharmony_ci goto skip; 26248c2ecf20Sopenharmony_ci read_lock(&sdp->sfd_lock); 26258c2ecf20Sopenharmony_ci if (!list_empty(&sdp->sfds)) { 26268c2ecf20Sopenharmony_ci seq_printf(s, " >>> device=%s ", sdp->disk->disk_name); 26278c2ecf20Sopenharmony_ci if (atomic_read(&sdp->detaching)) 26288c2ecf20Sopenharmony_ci seq_puts(s, "detaching pending close "); 26298c2ecf20Sopenharmony_ci else if (sdp->device) { 26308c2ecf20Sopenharmony_ci struct scsi_device *scsidp = sdp->device; 26318c2ecf20Sopenharmony_ci 26328c2ecf20Sopenharmony_ci seq_printf(s, "%d:%d:%d:%llu em=%d", 26338c2ecf20Sopenharmony_ci scsidp->host->host_no, 26348c2ecf20Sopenharmony_ci scsidp->channel, scsidp->id, 26358c2ecf20Sopenharmony_ci scsidp->lun, 26368c2ecf20Sopenharmony_ci scsidp->host->hostt->emulated); 26378c2ecf20Sopenharmony_ci } 26388c2ecf20Sopenharmony_ci seq_printf(s, " sg_tablesize=%d excl=%d open_cnt=%d\n", 26398c2ecf20Sopenharmony_ci sdp->sg_tablesize, sdp->exclude, sdp->open_cnt); 26408c2ecf20Sopenharmony_ci sg_proc_debug_helper(s, sdp); 26418c2ecf20Sopenharmony_ci } 26428c2ecf20Sopenharmony_ci read_unlock(&sdp->sfd_lock); 26438c2ecf20Sopenharmony_ciskip: 26448c2ecf20Sopenharmony_ci read_unlock_irqrestore(&sg_index_lock, iflags); 26458c2ecf20Sopenharmony_ci return 0; 26468c2ecf20Sopenharmony_ci} 26478c2ecf20Sopenharmony_ci 26488c2ecf20Sopenharmony_ci#endif /* CONFIG_SCSI_PROC_FS */ 26498c2ecf20Sopenharmony_ci 26508c2ecf20Sopenharmony_cimodule_init(init_sg); 26518c2ecf20Sopenharmony_cimodule_exit(exit_sg); 2652