Date: Mon, 2 Jun 2014 19:53:54 +0000 (UTC) From: Alexander Motin <mav@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r266981 - in head/sys: cam/scsi compat/freebsd32 compat/linux Message-ID: <201406021953.s52JrsMu093323@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: mav Date: Mon Jun 2 19:53:53 2014 New Revision: 266981 URL: http://svnweb.freebsd.org/changeset/base/266981 Log: Overhaul CAM SG driver IOCTL interfaces. Make it really work for native FreeBSD programs. Before this it was broken for years due to different number of pointer dereferences in Linux and FreeBSD IOCTL paths, permanently returning errors to FreeBSD programs. This change breaks the driver FreeBSD IOCTL ABI, making it more strict, but since it was not working any way -- who bother. Add shims for 32-bit programs on 64-bit host, translating the argument of the SG_IO IOCTL for both FreeBSD and Linux ABIs. With this change I was able to run 32-bit Linux sg3_utils tools and simple 32 and 64-bit FreeBSD test tools on both 32 and 64-bit FreeBSD systems. MFC after: 1 month Modified: head/sys/cam/scsi/scsi_sg.c head/sys/cam/scsi/scsi_sg.h head/sys/compat/freebsd32/freebsd32_ioctl.c head/sys/compat/freebsd32/freebsd32_ioctl.h head/sys/compat/linux/linux_ioctl.c Modified: head/sys/cam/scsi/scsi_sg.c ============================================================================== --- head/sys/cam/scsi/scsi_sg.c Mon Jun 2 19:28:11 2014 (r266980) +++ head/sys/cam/scsi/scsi_sg.c Mon Jun 2 19:53:53 2014 (r266981) @@ -494,7 +494,7 @@ sgioctl(struct cdev *dev, u_long cmd, ca struct ccb_scsiio *csio; struct cam_periph *periph; struct sg_softc *softc; - struct sg_io_hdr req; + struct sg_io_hdr *req; int dir, error; periph = (struct cam_periph *)dev->si_drv1; @@ -507,40 +507,22 @@ sgioctl(struct cdev *dev, u_long cmd, ca error = 0; switch (cmd) { - case LINUX_SCSI_GET_BUS_NUMBER: { - int busno; - - busno = xpt_path_path_id(periph->path); - error = copyout(&busno, arg, sizeof(busno)); - break; - } - case LINUX_SCSI_GET_IDLUN: { - struct scsi_idlun idlun; - struct cam_sim *sim; + case SG_GET_VERSION_NUM: + { + int *version = (int *)arg; - idlun.dev_id = xpt_path_target_id(periph->path); - sim = xpt_path_sim(periph->path); - idlun.host_unique_id = sim->unit_number; - error = copyout(&idlun, arg, sizeof(idlun)); + *version = sg_version; break; } - case SG_GET_VERSION_NUM: - case LINUX_SG_GET_VERSION_NUM: - error = copyout(&sg_version, arg, sizeof(sg_version)); - break; case SG_SET_TIMEOUT: - case LINUX_SG_SET_TIMEOUT: { - u_int user_timeout; + { + u_int user_timeout = *(u_int *)arg; - error = copyin(arg, &user_timeout, sizeof(u_int)); - if (error == 0) { - softc->sg_user_timeout = user_timeout; - softc->sg_timeout = user_timeout / SG_DEFAULT_HZ * hz; - } + softc->sg_user_timeout = user_timeout; + softc->sg_timeout = user_timeout / SG_DEFAULT_HZ * hz; break; } case SG_GET_TIMEOUT: - case LINUX_SG_GET_TIMEOUT: /* * The value is returned directly to the syscall. */ @@ -548,17 +530,14 @@ sgioctl(struct cdev *dev, u_long cmd, ca error = 0; break; case SG_IO: - case LINUX_SG_IO: - error = copyin(arg, &req, sizeof(req)); - if (error) - break; + req = (struct sg_io_hdr *)arg; - if (req.cmd_len > IOCDBLEN) { + if (req->cmd_len > IOCDBLEN) { error = EINVAL; break; } - if (req.iovec_count != 0) { + if (req->iovec_count != 0) { error = EOPNOTSUPP; break; } @@ -566,14 +545,14 @@ sgioctl(struct cdev *dev, u_long cmd, ca ccb = cam_periph_getccb(periph, CAM_PRIORITY_NORMAL); csio = &ccb->csio; - error = copyin(req.cmdp, &csio->cdb_io.cdb_bytes, - req.cmd_len); + error = copyin(req->cmdp, &csio->cdb_io.cdb_bytes, + req->cmd_len); if (error) { xpt_release_ccb(ccb); break; } - switch(req.dxfer_direction) { + switch(req->dxfer_direction) { case SG_DXFER_TO_DEV: dir = CAM_DIR_OUT; break; @@ -594,62 +573,57 @@ sgioctl(struct cdev *dev, u_long cmd, ca sgdone, dir|CAM_DEV_QFRZDIS, MSG_SIMPLE_Q_TAG, - req.dxferp, - req.dxfer_len, - req.mx_sb_len, - req.cmd_len, - req.timeout); + req->dxferp, + req->dxfer_len, + req->mx_sb_len, + req->cmd_len, + req->timeout); error = sgsendccb(periph, ccb); if (error) { - req.host_status = DID_ERROR; - req.driver_status = DRIVER_INVALID; + req->host_status = DID_ERROR; + req->driver_status = DRIVER_INVALID; xpt_release_ccb(ccb); break; } - req.status = csio->scsi_status; - req.masked_status = (csio->scsi_status >> 1) & 0x7f; - sg_scsiio_status(csio, &req.host_status, &req.driver_status); - req.resid = csio->resid; - req.duration = csio->ccb_h.timeout; - req.info = 0; - - error = copyout(&req, arg, sizeof(req)); - if ((error == 0) && (csio->ccb_h.status & CAM_AUTOSNS_VALID) - && (req.sbp != NULL)) { - req.sb_len_wr = req.mx_sb_len - csio->sense_resid; - error = copyout(&csio->sense_data, req.sbp, - req.sb_len_wr); + req->status = csio->scsi_status; + req->masked_status = (csio->scsi_status >> 1) & 0x7f; + sg_scsiio_status(csio, &req->host_status, &req->driver_status); + req->resid = csio->resid; + req->duration = csio->ccb_h.timeout; + req->info = 0; + + if ((csio->ccb_h.status & CAM_AUTOSNS_VALID) + && (req->sbp != NULL)) { + req->sb_len_wr = req->mx_sb_len - csio->sense_resid; + error = copyout(&csio->sense_data, req->sbp, + req->sb_len_wr); } xpt_release_ccb(ccb); break; case SG_GET_RESERVED_SIZE: - case LINUX_SG_GET_RESERVED_SIZE: { - int size = 32768; - - error = copyout(&size, arg, sizeof(size)); + { + int *size = (int *)arg; + *size = DFLTPHYS; break; } case SG_GET_SCSI_ID: - case LINUX_SG_GET_SCSI_ID: { - struct sg_scsi_id id; - - id.host_no = cam_sim_path(xpt_path_sim(periph->path)); - id.channel = xpt_path_path_id(periph->path); - id.scsi_id = xpt_path_target_id(periph->path); - id.lun = xpt_path_lun_id(periph->path); - id.scsi_type = softc->pd_type; - id.h_cmd_per_lun = 1; - id.d_queue_depth = 1; - id.unused[0] = 0; - id.unused[1] = 0; + struct sg_scsi_id *id = (struct sg_scsi_id *)arg; - error = copyout(&id, arg, sizeof(id)); + id->host_no = cam_sim_path(xpt_path_sim(periph->path)); + id->channel = xpt_path_path_id(periph->path); + id->scsi_id = xpt_path_target_id(periph->path); + id->lun = xpt_path_lun_id(periph->path); + id->scsi_type = softc->pd_type; + id->h_cmd_per_lun = 1; + id->d_queue_depth = 1; + id->unused[0] = 0; + id->unused[1] = 0; break; } @@ -672,25 +646,6 @@ sgioctl(struct cdev *dev, u_long cmd, ca case SG_SET_COMMAND_Q: case SG_SET_DEBUG: case SG_NEXT_CMD_LEN: - case LINUX_SG_EMULATED_HOST: - case LINUX_SG_SET_TRANSFORM: - case LINUX_SG_GET_TRANSFORM: - case LINUX_SG_GET_NUM_WAITING: - case LINUX_SG_SCSI_RESET: - case LINUX_SG_GET_REQUEST_TABLE: - case LINUX_SG_SET_KEEP_ORPHAN: - case LINUX_SG_GET_KEEP_ORPHAN: - case LINUX_SG_GET_ACCESS_COUNT: - case LINUX_SG_SET_FORCE_LOW_DMA: - case LINUX_SG_GET_LOW_DMA: - case LINUX_SG_GET_SG_TABLESIZE: - case LINUX_SG_SET_FORCE_PACK_ID: - case LINUX_SG_GET_PACK_ID: - case LINUX_SG_SET_RESERVED_SIZE: - case LINUX_SG_GET_COMMAND_Q: - case LINUX_SG_SET_COMMAND_Q: - case LINUX_SG_SET_DEBUG: - case LINUX_SG_NEXT_CMD_LEN: default: #ifdef CAMDEBUG printf("sgioctl: rejecting cmd 0x%lx\n", cmd); Modified: head/sys/cam/scsi/scsi_sg.h ============================================================================== --- head/sys/cam/scsi/scsi_sg.h Mon Jun 2 19:28:11 2014 (r266980) +++ head/sys/cam/scsi/scsi_sg.h Mon Jun 2 19:53:53 2014 (r266981) @@ -8,31 +8,31 @@ #define _SCSI_SG_H #define SGIOC '"' -#define SG_SET_TIMEOUT _IO(SGIOC, 0x01) +#define SG_SET_TIMEOUT _IOW(SGIOC, 0x01, u_int) #define SG_GET_TIMEOUT _IO(SGIOC, 0x02) -#define SG_EMULATED_HOST _IO(SGIOC, 0x03) +#define SG_EMULATED_HOST _IOR(SGIOC, 0x03, int) #define SG_SET_TRANSFORM _IO(SGIOC, 0x04) #define SG_GET_TRANSFORM _IO(SGIOC, 0x05) -#define SG_GET_COMMAND_Q _IO(SGIOC, 0x70) -#define SG_SET_COMMAND_Q _IO(SGIOC, 0x71) -#define SG_GET_RESERVED_SIZE _IO(SGIOC, 0x72) -#define SG_SET_RESERVED_SIZE _IO(SGIOC, 0x75) -#define SG_GET_SCSI_ID _IO(SGIOC, 0x76) -#define SG_SET_FORCE_LOW_DMA _IO(SGIOC, 0x79) -#define SG_GET_LOW_DMA _IO(SGIOC, 0x7a) -#define SG_SET_FORCE_PACK_ID _IO(SGIOC, 0x7b) -#define SG_GET_PACK_ID _IO(SGIOC, 0x7c) -#define SG_GET_NUM_WAITING _IO(SGIOC, 0x7d) -#define SG_SET_DEBUG _IO(SGIOC, 0x7e) -#define SG_GET_SG_TABLESIZE _IO(SGIOC, 0x7f) -#define SG_GET_VERSION_NUM _IO(SGIOC, 0x82) -#define SG_NEXT_CMD_LEN _IO(SGIOC, 0x83) -#define SG_SCSI_RESET _IO(SGIOC, 0x84) -#define SG_IO _IO(SGIOC, 0x85) +#define SG_GET_COMMAND_Q _IOW(SGIOC, 0x70, int) +#define SG_SET_COMMAND_Q _IOR(SGIOC, 0x71, int) +#define SG_GET_RESERVED_SIZE _IOR(SGIOC, 0x72, int) +#define SG_SET_RESERVED_SIZE _IOW(SGIOC, 0x75, int) +#define SG_GET_SCSI_ID _IOR(SGIOC, 0x76, struct sg_scsi_id) +#define SG_SET_FORCE_LOW_DMA _IOW(SGIOC, 0x79, int) +#define SG_GET_LOW_DMA _IOR(SGIOC, 0x7a, int) +#define SG_SET_FORCE_PACK_ID _IOW(SGIOC, 0x7b, int) +#define SG_GET_PACK_ID _IOR(SGIOC, 0x7c, int) +#define SG_GET_NUM_WAITING _IOR(SGIOC, 0x7d, int) +#define SG_SET_DEBUG _IOW(SGIOC, 0x7e, int) +#define SG_GET_SG_TABLESIZE _IOR(SGIOC, 0x7f, int) +#define SG_GET_VERSION_NUM _IOR(SGIOC, 0x82, int) +#define SG_NEXT_CMD_LEN _IOW(SGIOC, 0x83, int) +#define SG_SCSI_RESET _IOW(SGIOC, 0x84, int) +#define SG_IO _IOWR(SGIOC, 0x85, struct sg_io_hdr) #define SG_GET_REQUEST_TABLE _IO(SGIOC, 0x86) -#define SG_SET_KEEP_ORPHAN _IO(SGIOC, 0x87) -#define SG_GET_KEEP_ORPHAN _IO(SGIOC, 0x88) -#define SG_GET_ACCESS_COUNT _IO(SGIOC, 0x89) +#define SG_SET_KEEP_ORPHAN _IOW(SGIOC, 0x87, int) +#define SG_GET_KEEP_ORPHAN _IOR(SGIOC, 0x88, int) +#define SG_GET_ACCESS_COUNT _IOR(SGIOC, 0x89, int) struct sg_io_hdr { int interface_id; @@ -59,6 +59,31 @@ struct sg_io_hdr { u_int info; }; +struct sg_io_hdr32 { + int interface_id; + int dxfer_direction; + u_char cmd_len; + u_char mx_sb_len; + u_short iovec_count; + u_int dxfer_len; + uint32_t dxferp; + uint32_t cmdp; + uint32_t sbp; + u_int timeout; + u_int flags; + int pack_id; + uint32_t usr_ptr; + u_char status; + u_char masked_status; + u_char msg_status; + u_char sb_len_wr; + u_short host_status; + u_short driver_status; + int resid; + u_int duration; + u_int info; +}; + #define SG_DXFER_NONE -1 #define SG_DXFER_TO_DEV -2 #define SG_DXFER_FROM_DEV -3 Modified: head/sys/compat/freebsd32/freebsd32_ioctl.c ============================================================================== --- head/sys/compat/freebsd32/freebsd32_ioctl.c Mon Jun 2 19:28:11 2014 (r266980) +++ head/sys/compat/freebsd32/freebsd32_ioctl.c Mon Jun 2 19:53:53 2014 (r266981) @@ -344,6 +344,71 @@ cleanup: return (error); } +static int +freebsd32_ioctl_sg(struct thread *td, + struct freebsd32_ioctl_args *uap, struct file *fp) +{ + struct sg_io_hdr io; + struct sg_io_hdr32 io32; + int error; + + if ((error = copyin(uap->data, &io32, sizeof(io32))) != 0) + return (error); + + CP(io32, io, interface_id); + CP(io32, io, dxfer_direction); + CP(io32, io, cmd_len); + CP(io32, io, mx_sb_len); + CP(io32, io, iovec_count); + CP(io32, io, dxfer_len); + PTRIN_CP(io32, io, dxferp); + PTRIN_CP(io32, io, cmdp); + PTRIN_CP(io32, io, sbp); + CP(io32, io, timeout); + CP(io32, io, flags); + CP(io32, io, pack_id); + PTRIN_CP(io32, io, usr_ptr); + CP(io32, io, status); + CP(io32, io, masked_status); + CP(io32, io, msg_status); + CP(io32, io, sb_len_wr); + CP(io32, io, host_status); + CP(io32, io, driver_status); + CP(io32, io, resid); + CP(io32, io, duration); + CP(io32, io, info); + + if ((error = fo_ioctl(fp, SG_IO, (caddr_t)&io, td->td_ucred, td)) != 0) + return (error); + + CP(io, io32, interface_id); + CP(io, io32, dxfer_direction); + CP(io, io32, cmd_len); + CP(io, io32, mx_sb_len); + CP(io, io32, iovec_count); + CP(io, io32, dxfer_len); + PTROUT_CP(io, io32, dxferp); + PTROUT_CP(io, io32, cmdp); + PTROUT_CP(io, io32, sbp); + CP(io, io32, timeout); + CP(io, io32, flags); + CP(io, io32, pack_id); + PTROUT_CP(io, io32, usr_ptr); + CP(io, io32, status); + CP(io, io32, masked_status); + CP(io, io32, msg_status); + CP(io, io32, sb_len_wr); + CP(io, io32, host_status); + CP(io, io32, driver_status); + CP(io, io32, resid); + CP(io, io32, duration); + CP(io, io32, info); + + error = copyout(&io32, uap->data, sizeof(io32)); + + return (error); +} + int freebsd32_ioctl(struct thread *td, struct freebsd32_ioctl_args *uap) { @@ -393,6 +458,10 @@ freebsd32_ioctl(struct thread *td, struc error = freebsd32_ioctl_pciocgetconf(td, uap, fp); break; + case SG_IO_32: + error = freebsd32_ioctl_sg(td, uap, fp); + break; + default: fdrop(fp, td); ap.fd = uap->fd; Modified: head/sys/compat/freebsd32/freebsd32_ioctl.h ============================================================================== --- head/sys/compat/freebsd32/freebsd32_ioctl.h Mon Jun 2 19:28:11 2014 (r266980) +++ head/sys/compat/freebsd32/freebsd32_ioctl.h Mon Jun 2 19:53:53 2014 (r266981) @@ -32,6 +32,8 @@ #ifndef _COMPAT_FREEBSD32_IOCTL_H_ #define _COMPAT_FREEBSD32_IOCTL_H_ +#include <cam/scsi/scsi_sg.h> + typedef __uint32_t caddr_t32; struct ioc_toc_header32 { @@ -122,5 +124,6 @@ struct pci_conf_io32 { #define MEMRANGE_GET32 _IOWR('m', 50, struct mem_range_op32) #define MEMRANGE_SET32 _IOW('m', 51, struct mem_range_op32) #define PCIOCGETCONF_32 _IOWR('p', 5, struct pci_conf_io32) +#define SG_IO_32 _IOWR(SGIOC, 0x85, struct sg_io_hdr32) #endif /* _COMPAT_FREEBSD32_IOCTL_H_ */ Modified: head/sys/compat/linux/linux_ioctl.c ============================================================================== --- head/sys/compat/linux/linux_ioctl.c Mon Jun 2 19:28:11 2014 (r266980) +++ head/sys/compat/linux/linux_ioctl.c Mon Jun 2 19:53:53 2014 (r266981) @@ -92,6 +92,8 @@ __FBSDID("$FreeBSD$"); #include <contrib/v4l/videodev2.h> #include <compat/linux/linux_videodev2_compat.h> +#include <cam/scsi/scsi_sg.h> + CTASSERT(LINUX_IFNAMSIZ == IFNAMSIZ); FEATURE(linuxulator_v4l, "V4L ioctl wrapper support in the linuxulator"); @@ -1646,9 +1648,32 @@ linux_ioctl_cdrom(struct thread *td, str } case LINUX_SCSI_GET_BUS_NUMBER: + { + struct sg_scsi_id id; + + error = fo_ioctl(fp, SG_GET_SCSI_ID, (caddr_t)&id, + td->td_ucred, td); + if (error) + break; + error = copyout(&id.channel, (void *)args->arg, sizeof(int)); + break; + } + case LINUX_SCSI_GET_IDLUN: - error = linux_ioctl_sg(td, args); + { + struct sg_scsi_id id; + struct scsi_idlun idl; + + error = fo_ioctl(fp, SG_GET_SCSI_ID, (caddr_t)&id, + td->td_ucred, td); + if (error) + break; + idl.dev_id = (id.scsi_id & 0xff) + ((id.lun & 0xff) << 8) + + ((id.channel & 0xff) << 16) + ((id.host_no & 0xff) << 24); + idl.host_unique_id = id.host_no; + error = copyout(&idl, (void *)args->arg, sizeof(idl)); break; + } /* LINUX_CDROM_SEND_PACKET */ /* LINUX_CDROM_NEXT_WRITABLE */ @@ -2618,12 +2643,20 @@ linux_ioctl_drm(struct thread *td, struc return sys_ioctl(td, (struct ioctl_args *)args); } +#ifdef COMPAT_LINUX32 +#define CP(src,dst,fld) do { (dst).fld = (src).fld; } while (0) +#define PTRIN_CP(src,dst,fld) \ + do { (dst).fld = PTRIN((src).fld); } while (0) +#define PTROUT_CP(src,dst,fld) \ + do { (dst).fld = PTROUT((src).fld); } while (0) + static int -linux_ioctl_sg(struct thread *td, struct linux_ioctl_args *args) +linux_ioctl_sg_io(struct thread *td, struct linux_ioctl_args *args) { + struct sg_io_hdr io; + struct sg_io_hdr32 io32; cap_rights_t rights; struct file *fp; - u_long cmd; int error; error = fget(td, args->fd, cap_rights_init(&rights, CAP_IOCTL), &fp); @@ -2631,12 +2664,98 @@ linux_ioctl_sg(struct thread *td, struct printf("sg_linux_ioctl: fget returned %d\n", error); return (error); } - cmd = args->cmd; - error = (fo_ioctl(fp, cmd, (caddr_t)args->arg, td->td_ucred, td)); + if ((error = copyin((void *)args->arg, &io32, sizeof(io32))) != 0) + goto out; + + CP(io32, io, interface_id); + CP(io32, io, dxfer_direction); + CP(io32, io, cmd_len); + CP(io32, io, mx_sb_len); + CP(io32, io, iovec_count); + CP(io32, io, dxfer_len); + PTRIN_CP(io32, io, dxferp); + PTRIN_CP(io32, io, cmdp); + PTRIN_CP(io32, io, sbp); + CP(io32, io, timeout); + CP(io32, io, flags); + CP(io32, io, pack_id); + PTRIN_CP(io32, io, usr_ptr); + CP(io32, io, status); + CP(io32, io, masked_status); + CP(io32, io, msg_status); + CP(io32, io, sb_len_wr); + CP(io32, io, host_status); + CP(io32, io, driver_status); + CP(io32, io, resid); + CP(io32, io, duration); + CP(io32, io, info); + + if ((error = fo_ioctl(fp, SG_IO, (caddr_t)&io, td->td_ucred, td)) != 0) + goto out; + + CP(io, io32, interface_id); + CP(io, io32, dxfer_direction); + CP(io, io32, cmd_len); + CP(io, io32, mx_sb_len); + CP(io, io32, iovec_count); + CP(io, io32, dxfer_len); + PTROUT_CP(io, io32, dxferp); + PTROUT_CP(io, io32, cmdp); + PTROUT_CP(io, io32, sbp); + CP(io, io32, timeout); + CP(io, io32, flags); + CP(io, io32, pack_id); + PTROUT_CP(io, io32, usr_ptr); + CP(io, io32, status); + CP(io, io32, masked_status); + CP(io, io32, msg_status); + CP(io, io32, sb_len_wr); + CP(io, io32, host_status); + CP(io, io32, driver_status); + CP(io, io32, resid); + CP(io, io32, duration); + CP(io, io32, info); + + error = copyout(&io32, (void *)args->arg, sizeof(io32)); + +out: fdrop(fp, td); return (error); } +#endif + +static int +linux_ioctl_sg(struct thread *td, struct linux_ioctl_args *args) +{ + + switch (args->cmd) { + case LINUX_SG_GET_VERSION_NUM: + args->cmd = SG_GET_VERSION_NUM; + break; + case LINUX_SG_SET_TIMEOUT: + args->cmd = SG_SET_TIMEOUT; + break; + case LINUX_SG_GET_TIMEOUT: + args->cmd = SG_GET_TIMEOUT; + break; + case LINUX_SG_IO: + args->cmd = SG_IO; +#ifdef COMPAT_LINUX32 + return (linux_ioctl_sg_io(td, args)); +#endif + break; + case LINUX_SG_GET_RESERVED_SIZE: + args->cmd = SG_GET_RESERVED_SIZE; + break; + case LINUX_SG_GET_SCSI_ID: + args->cmd = SG_GET_SCSI_ID; + break; + default: + return (ENODEV); + } + return (sys_ioctl(td, (struct ioctl_args *)args)); +} /* * Video4Linux (V4L) ioctl handler
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201406021953.s52JrsMu093323>