// SPDX-License-Identifier: GPL-2.0-or-later /* * History: * Started: Aug 9 by Lawrence Foard (entropy@world.std.com), * to allow user process control of SCSI devices. * Development Sponsored by Killy Corp. NY NY * * Original driver (sg.c): * Copyright (C) 1992 Lawrence Foard * Version 2 and 3 extensions to driver: * Copyright (C) 1998 - 2014 Douglas Gilbert
*/
staticint sg_version_num = 30536; /* 2 digits for each component */ #define SG_VERSION_STR "3.5.36"
/* * D. P. Gilbert (dgilbert@interlog.com), notes: * - scsi logging is available via SCSI_LOG_TIMEOUT macros. First * the kernel/module needs to be built with CONFIG_SCSI_LOGGING * (otherwise the macros compile to empty statements). *
*/ #include <linux/module.h>
/* SG_MAX_CDB_SIZE should be 260 (spc4r37 section 3.1.30) however the type * of sg_io_hdr::cmd_len can only represent 255. All SCSI commands greater * than 16 bytes are "variable length" whose length is a multiple of 4
*/ #define SG_MAX_CDB_SIZE 252
staticint sg_big_buff = SG_DEF_RESERVED_SIZE; /* N.B. This variable is readable and writeable via /proc/scsi/sg/def_reserved_size . Each time sg_open() is called a buffer of this size (or less if there is not enough memory) will be reserved for use by this file descriptor. [Deprecated usage: this variable is also readable via /proc/sys/kernel/sg-big-buff if the sg driver is built into
the kernel (i.e. it is not a module).] */ staticint def_reserved_size = -1; /* picks up init parameter */ staticint sg_allow_dio = SG_ALLOW_DIO_DEF;
typedefstruct sg_request { /* SG_MAX_QUEUE requests outstanding per file */ struct list_head entry; /* list entry */ struct sg_fd *parentfp; /* NULL -> not in use */
Sg_scatter_hold data; /* hold buffer, perhaps scatter list */
sg_io_hdr_t header; /* scsi command+info, see <scsi/sg.h> */ unsignedchar sense_b[SCSI_SENSE_BUFFERSIZE]; char res_used; /* 1 -> using reserve buffer, 0 -> not ... */ char orphan; /* 1 -> drop on sight, 0 -> normal */ char sg_io_owned; /* 1 -> packet belongs to SG_IO */ /* done protected by rq_list_lock */ char done; /* 0->before bh, 1->before read, 2->read */ struct request *rq; struct bio *bio; struct execute_work ew;
} Sg_request;
typedefstruct sg_fd { /* holds the state of a file descriptor */ struct list_head sfd_siblings; /* protected by device's sfd_lock */ struct sg_device *parentdp; /* owning device */
wait_queue_head_t read_wait; /* queue read until command done */
rwlock_t rq_list_lock; /* protect access to list in req_arr */ struct mutex f_mutex; /* protect against changes in this fd */ int timeout; /* defaults to SG_DEFAULT_TIMEOUT */ int timeout_user; /* defaults to SG_DEFAULT_TIMEOUT_USER */
Sg_scatter_hold reserve; /* buffer held for this file descriptor */ struct list_head rq_list; /* head of request list */ struct fasync_struct *async_qp; /* used by asynchronous notification */
Sg_request req_arr[SG_MAX_QUEUE]; /* used as singly-linked list */ char force_packid; /* 1 -> pack_id input to read(), 0 -> ignored */ char cmd_q; /* 1 -> allow command queuing, 0 -> don't */ unsignedchar next_cmd_len; /* 0: automatic, >0: use on next write() */ char keep_orphan; /* 0 -> drop orphan (def), 1 -> keep for read() */ char mmap_called; /* 0 -> mmap() never called on this fd */ char res_in_use; /* 1 -> 'reserve' array in use */ struct kref f_ref; struct execute_work ew;
} Sg_fd;
typedefstruct sg_device { /* holds the state of each scsi generic device */ struct scsi_device *device;
wait_queue_head_t open_wait; /* queue open() when O_EXCL present */ struct mutex open_rel_lock; /* held when in open() or release() */ int sg_tablesize; /* adapter's max scatter-gather table size */
u32 index; /* device index number */ struct list_head sfds;
rwlock_t sfd_lock; /* protect access to sfd list */
atomic_t detaching; /* 0->device usable, 1->device detaching */ bool exclude; /* 1->open(O_EXCL) succeeded and is active */ int open_cnt; /* count of opens (perhaps < num(sfds) ) */ char sgdebug; /* 0->off, 1->sense, 9->dump dev, 10-> all devs */ char name[DISK_NAME_LEN]; struct cdev * cdev; /* char_dev [sysfs: /sys/cdev/major/sg<n>] */ struct kref d_ref;
} Sg_device;
/* * The SCSI interfaces that use read() and write() as an asynchronous variant of * ioctl(..., SG_IO, ...) are fundamentally unsafe, since there are lots of ways * to trigger read() and write() calls from various contexts with elevated * privileges. This can lead to kernel memory corruption (e.g. if these * interfaces are called through splice()) and privilege escalation inside * userspace (e.g. if a process with access to such a device passes a file * descriptor to a SUID binary as stdin/stdout/stderr). * * This function provides protection for the legacy API by restricting the * calling context.
*/ staticint sg_check_file_access(struct file *filp, constchar *caller)
{ if (filp->f_cred != current_real_cred()) {
pr_err_once("%s: process %d (%s) changed security contexts after opening file descriptor, this is not allowed.\n",
caller, task_tgid_vnr(current), current->comm); return -EPERM;
} return 0;
}
/* This driver's module count bumped by fops_get in <linux/fs.h> */ /* Prevent the device driver from vanishing while we sleep */
device = sdp->device;
retval = scsi_device_get(device); if (retval) goto sg_put;
/* scsi_block_when_processing_errors() may block so bypass * check if O_NONBLOCK. Permits SCSI commands to be issued
* during error recovery. Tread carefully. */ if (!((flags & O_NONBLOCK) ||
scsi_block_when_processing_errors(device))) {
retval = -ENXIO; /* we are in error recovery for this device */ goto sdp_put;
}
mutex_lock(&sdp->open_rel_lock); if (flags & O_NONBLOCK) { if (flags & O_EXCL) { if (sdp->open_cnt > 0) {
retval = -EBUSY; goto error_mutex_locked;
}
} else { if (sdp->exclude) {
retval = -EBUSY; goto error_mutex_locked;
}
}
} else {
retval = open_wait(sdp, flags); if (retval) /* -ERESTARTSYS or -ENODEV */ goto error_mutex_locked;
}
/* N.B. at this point we are holding the open_rel_lock */ if (flags & O_EXCL)
sdp->exclude = true;
/* * This could cause a response to be stranded. Close the associated * file descriptor to free up any resources being held.
*/
retval = sg_check_file_access(filp, __func__); if (retval) return retval;
switch (cmd_in) { case SG_IO: if (atomic_read(&sdp->detaching)) return -ENODEV; if (!scsi_block_when_processing_errors(sdp->device)) return -ENXIO;
result = sg_new_write(sfp, filp, p, SZ_SG_IO_HDR,
1, read_only, 1, &srp); if (result < 0) return result;
result = wait_event_interruptible(sfp->read_wait,
srp_done(sfp, srp));
write_lock_irq(&sfp->rq_list_lock); if (srp->done) {
srp->done = 2;
write_unlock_irq(&sfp->rq_list_lock);
result = sg_new_read(sfp, p, SZ_SG_IO_HDR, srp); return (result < 0) ? result : 0;
}
srp->orphan = 1;
write_unlock_irq(&sfp->rq_list_lock); return result; /* -ERESTARTSYS because signal hit process */ case SG_SET_TIMEOUT:
result = get_user(val, ip); if (result) return result; if (val < 0) return -EIO; if (val >= mult_frac((s64)INT_MAX, USER_HZ, HZ))
val = min_t(s64, mult_frac((s64)INT_MAX, USER_HZ, HZ),
INT_MAX);
sfp->timeout_user = val;
sfp->timeout = mult_frac(val, HZ, USER_HZ);
return 0; case SG_GET_TIMEOUT: /* N.B. User receives timeout as return value */ /* strange ..., for backward compatibility */ return sfp->timeout_user; case SG_SET_FORCE_LOW_DMA: /* * N.B. This ioctl never worked properly, but failed to * return an error value. So returning '0' to keep compability * with legacy applications.
*/ return 0; case SG_GET_LOW_DMA: return put_user(0, ip); case SG_GET_SCSI_ID:
{
sg_scsi_id_t v;
if (atomic_read(&sdp->detaching)) return -ENODEV;
memset(&v, 0, sizeof(v));
v.host_no = sdp->device->host->host_no;
v.channel = sdp->device->channel;
v.scsi_id = sdp->device->id;
v.lun = sdp->device->lun;
v.scsi_type = sdp->device->type;
v.h_cmd_per_lun = sdp->device->host->cmd_per_lun;
v.d_queue_depth = sdp->device->queue_depth; if (copy_to_user(p, &v, sizeof(sg_scsi_id_t))) return -EFAULT; return 0;
} case SG_SET_FORCE_PACK_ID:
result = get_user(val, ip); if (result) return result;
sfp->force_packid = val ? 1 : 0; return 0; case SG_GET_PACK_ID:
read_lock_irqsave(&sfp->rq_list_lock, iflags);
list_for_each_entry(srp, &sfp->rq_list, entry) { if ((1 == srp->done) && (!srp->sg_io_owned)) {
read_unlock_irqrestore(&sfp->rq_list_lock,
iflags); return put_user(srp->header.pack_id, ip);
}
}
read_unlock_irqrestore(&sfp->rq_list_lock, iflags); return put_user(-1, ip); case SG_GET_NUM_WAITING:
read_lock_irqsave(&sfp->rq_list_lock, iflags);
val = 0;
list_for_each_entry(srp, &sfp->rq_list, entry) { if ((1 == srp->done) && (!srp->sg_io_owned))
++val;
}
read_unlock_irqrestore(&sfp->rq_list_lock, iflags); return put_user(val, ip); case SG_GET_SG_TABLESIZE: return put_user(sdp->sg_tablesize, ip); case SG_SET_RESERVED_SIZE:
result = get_user(val, ip); if (result) return result; if (val < 0) return -EINVAL;
val = min_t(int, val,
max_sectors_bytes(sdp->device->request_queue));
mutex_lock(&sfp->f_mutex); if (val != sfp->reserve.bufflen) { if (sfp->mmap_called ||
sfp->res_in_use) {
mutex_unlock(&sfp->f_mutex); return -EBUSY;
}
sg_remove_scat(sfp, &sfp->reserve);
sg_build_reserve(sfp, val);
}
mutex_unlock(&sfp->f_mutex); return 0; case SG_GET_RESERVED_SIZE:
val = min_t(int, sfp->reserve.bufflen,
max_sectors_bytes(sdp->device->request_queue)); return put_user(val, ip); case SG_SET_COMMAND_Q:
result = get_user(val, ip); if (result) return result;
sfp->cmd_q = val ? 1 : 0; return 0; case SG_GET_COMMAND_Q: return put_user((int) sfp->cmd_q, ip); case SG_SET_KEEP_ORPHAN:
result = get_user(val, ip); if (result) return result;
sfp->keep_orphan = val; return 0; case SG_GET_KEEP_ORPHAN: return put_user((int) sfp->keep_orphan, ip); case SG_NEXT_CMD_LEN:
result = get_user(val, ip); if (result) return result; if (val > SG_MAX_CDB_SIZE) return -ENOMEM;
sfp->next_cmd_len = (val > 0) ? val : 0; return 0; case SG_GET_VERSION_NUM: return put_user(sg_version_num, ip); case SG_GET_ACCESS_COUNT: /* faked - we don't have a real access count anymore */
val = (sdp->device ? 1 : 0); return put_user(val, ip); case SG_GET_REQUEST_TABLE:
{
sg_req_info_t *rinfo;
rinfo = kcalloc(SG_MAX_QUEUE, SZ_SG_REQ_INFO,
GFP_KERNEL); if (!rinfo) return -ENOMEM;
read_lock_irqsave(&sfp->rq_list_lock, iflags);
sg_fill_request_table(sfp, rinfo);
read_unlock_irqrestore(&sfp->rq_list_lock, iflags); #ifdef CONFIG_COMPAT if (in_compat_syscall())
result = put_compat_request_table(p, rinfo); else #endif
result = copy_to_user(p, rinfo,
SZ_SG_REQ_INFO * SG_MAX_QUEUE);
result = result ? -EFAULT : 0;
kfree(rinfo); return result;
} case SG_EMULATED_HOST: if (atomic_read(&sdp->detaching)) return -ENODEV; return put_user(sdp->device->host->hostt->emulated, ip); case SCSI_IOCTL_SEND_COMMAND: if (atomic_read(&sdp->detaching)) return -ENODEV; return scsi_ioctl(sdp->device, filp->f_mode & FMODE_WRITE,
cmd_in, p); case SG_SET_DEBUG:
result = get_user(val, ip); if (result) return result;
sdp->sgdebug = (char) val; return 0; case BLKSECTGET: return put_user(max_sectors_bytes(sdp->device->request_queue),
ip); case BLKTRACESETUP: return blk_trace_setup(sdp->device->request_queue, sdp->name,
MKDEV(SCSI_GENERIC_MAJOR, sdp->index),
NULL, p); case BLKTRACESTART: return blk_trace_startstop(sdp->device->request_queue, 1); case BLKTRACESTOP: return blk_trace_startstop(sdp->device->request_queue, 0); case BLKTRACETEARDOWN: return blk_trace_remove(sdp->device->request_queue); case SCSI_IOCTL_GET_IDLUN: case SCSI_IOCTL_GET_BUS_NUMBER: case SCSI_IOCTL_PROBE_HOST: case SG_GET_TRANSFORM: case SG_SCSI_RESET: if (atomic_read(&sdp->detaching)) return -ENODEV; break; default: if (read_only) return -EPERM; /* don't know so take safe approach */ break;
}
result = scsi_ioctl_block_when_processing_errors(sdp->device,
cmd_in, filp->f_flags & O_NDELAY); if (result) return result;
/* * This function is a "bottom half" handler that is called by the mid * level when a command is completed (or has failed).
*/ staticenum rq_end_io_ret
sg_rq_end_io(struct request *rq, blk_status_t status)
{ struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(rq); struct sg_request *srp = rq->end_io_data;
Sg_device *sdp;
Sg_fd *sfp; unsignedlong iflags; unsignedint ms; char *sense; int result, resid, done = 1;
if (WARN_ON(srp->done != 0)) return RQ_END_IO_NONE;
sfp = srp->parentfp; if (WARN_ON(sfp == NULL)) return RQ_END_IO_NONE;
sdp = sfp->parentdp; if (unlikely(atomic_read(&sdp->detaching)))
pr_info("%s: device detaching\n", __func__);
sense = scmd->sense_buffer;
result = scmd->result;
resid = scmd->resid_len;
/* Following if statement is a patch supplied by Eric Youngdale */ if (driver_byte(result) != 0
&& scsi_normalize_sense(sense, SCSI_SENSE_BUFFERSIZE, &sshdr)
&& !scsi_sense_is_deferred(&sshdr)
&& sshdr.sense_key == UNIT_ATTENTION
&& sdp->device->removable) { /* Detected possible disc change. Set the bit - this */ /* may be used if there are filesystems using this device */
sdp->device->changed = 1;
}
}
if (scmd->sense_len)
memcpy(srp->sense_b, scmd->sense_buffer, SCSI_SENSE_BUFFERSIZE);
/* Rely on write phase to clean out srp status values, so no "else" */
/* * Free the request as soon as it is complete so that its resources * can be reused without waiting for userspace to read() the * result. But keep the associated bio (if any) around until * blk_rq_unmap_user() can be called from user context.
*/
srp->rq = NULL;
blk_mq_free_request(rq);
write_lock_irqsave(&sfp->rq_list_lock, iflags); if (unlikely(srp->orphan)) { if (sfp->keep_orphan)
srp->sg_io_owned = 0; else
done = 0;
}
srp->done = done;
write_unlock_irqrestore(&sfp->rq_list_lock, iflags);
if (likely(done)) { /* Now wake up any sg_read() that is waiting for this * packet.
*/
wake_up_interruptible(&sfp->read_wait);
kill_fasync(&sfp->async_qp, SIGPOLL, POLL_IN);
kref_put(&sfp->f_ref, sg_remove_sfp);
} else {
INIT_WORK(&srp->ew.work, sg_rq_end_io_usercontext);
schedule_work(&srp->ew.work);
} return RQ_END_IO_NONE;
}
/* CAUTION! Note that the device can still be found via idr_find() * even though the refcount is 0. Therefore, do idr_remove() BEFORE * any other cleanup.
*/
if (!sdp) return; /* want sdp->detaching non-zero as soon as possible */
val = atomic_inc_return(&sdp->detaching); if (val > 1) return; /* only want to do following once per device */
MODULE_PARM_DESC(scatter_elem_sz, "scatter gather element " "size (default: max(SG_SCATTER_SZ, PAGE_SIZE))");
MODULE_PARM_DESC(def_reserved_size, "size of buffer reserved for each fd");
MODULE_PARM_DESC(allow_dio, "allow direct I/O (default: 0 (disallow))");
/* * NOTE * * With scsi-mq enabled, there are a fixed number of preallocated * requests equal in number to shost->can_queue. If all of the * preallocated requests are already in use, then scsi_alloc_request() * will sleep until an active command completes, freeing up a request. * Although waiting in an asynchronous interface is less than ideal, we * do not want to use BLK_MQ_REQ_NOWAIT here because userspace might * not expect an EWOULDBLOCK from this condition.
*/
rq = scsi_alloc_request(q, hp->dxfer_direction == SG_DXFER_TO_DEV ?
REQ_OP_DRV_OUT : REQ_OP_DRV_IN, 0); if (IS_ERR(rq)) return PTR_ERR(rq);
scmd = blk_mq_rq_to_pdu(rq);
if (hp->cmd_len > sizeof(scmd->cmnd)) {
blk_mq_free_request(rq); return -EINVAL;
}
schp->bufflen = blk_size; if (rem_sz > 0) /* must have failed */ return -ENOMEM; return 0;
out: for (i = 0; i < k; i++)
__free_pages(schp->pages[i], order);
if (--order >= 0) goto retry;
return -ENOMEM;
}
staticvoid
sg_remove_scat(Sg_fd * sfp, Sg_scatter_hold * schp)
{
SCSI_LOG_TIMEOUT(4, sg_printk(KERN_INFO, sfp->parentdp, "sg_remove_scat: k_use_sg=%d\n", schp->k_use_sg)); if (schp->pages && schp->sglist_len > 0) { if (!schp->dio_in_use) { int k;
/* Return of 1 for found; 0 for not found */ staticint
sg_remove_request(Sg_fd * sfp, Sg_request * srp)
{ unsignedlong iflags; int res = 0;
if (!sfp || !srp || list_empty(&sfp->rq_list)) return res;
write_lock_irqsave(&sfp->rq_list_lock, iflags); if (!list_empty(&srp->entry)) {
list_del(&srp->entry);
srp->parentfp = NULL;
res = 1;
}
write_unlock_irqrestore(&sfp->rq_list_lock, iflags);
/* * If the device is detaching, wakeup any readers in case we just * removed the last response, which would leave nothing for them to * return other than -ENODEV.
*/ if (unlikely(atomic_read(&sfp->parentdp->detaching)))
wake_up_interruptible_all(&sfp->read_wait);
read_lock_irqsave(&sg_index_lock, flags);
sdp = sg_lookup_dev(dev); if (!sdp)
sdp = ERR_PTR(-ENXIO); elseif (atomic_read(&sdp->detaching)) { /* If sdp->detaching, then the refcount may already be 0, in * which case it would be a bug to do kref_get().
*/
sdp = ERR_PTR(-ENODEV);
} else
kref_get(&sdp->d_ref);
read_unlock_irqrestore(&sg_index_lock, flags);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.