diff options
author | Keith Busch <keith.busch@intel.com> | 2013-10-23 15:07:34 -0400 |
---|---|---|
committer | Matthew Wilcox <matthew.r.wilcox@intel.com> | 2013-12-16 15:49:40 -0500 |
commit | 320a382746e0ab1304476ea7e986a8d416ab99db (patch) | |
tree | bbb40d77dd5f915fbdc87461869526e115f42de0 /drivers/block/nvme-scsi.c | |
parent | 481e5bad84239b01415b888df2040c1860ae3cfd (diff) |
NVMe: compat SG_IO ioctl
For 32-bit versions of sg3-utils running on a 64-bit system. This is
mostly a copy from the relevent portions of fs/compat_ioctl.c, with
slight modifications for going through block_device_operations.
Signed-off-by: Keith Busch <keith.busch@intel.com>
Reviewed-by: Vishal Verma <vishal.l.verma@linux.intel.com>
[fixed up CONFIG_COMPAT=n build problems]
Signed-off-by: Matthew Wilcox <matthew.r.wilcox@intel.com>
Diffstat (limited to 'drivers/block/nvme-scsi.c')
-rw-r--r-- | drivers/block/nvme-scsi.c | 147 |
1 files changed, 147 insertions, 0 deletions
diff --git a/drivers/block/nvme-scsi.c b/drivers/block/nvme-scsi.c index 4a4ff4eb8e23..4a0ceb64e269 100644 --- a/drivers/block/nvme-scsi.c +++ b/drivers/block/nvme-scsi.c | |||
@@ -25,6 +25,7 @@ | |||
25 | #include <linux/bio.h> | 25 | #include <linux/bio.h> |
26 | #include <linux/bitops.h> | 26 | #include <linux/bitops.h> |
27 | #include <linux/blkdev.h> | 27 | #include <linux/blkdev.h> |
28 | #include <linux/compat.h> | ||
28 | #include <linux/delay.h> | 29 | #include <linux/delay.h> |
29 | #include <linux/errno.h> | 30 | #include <linux/errno.h> |
30 | #include <linux/fs.h> | 31 | #include <linux/fs.h> |
@@ -3038,6 +3039,152 @@ int nvme_sg_io(struct nvme_ns *ns, struct sg_io_hdr __user *u_hdr) | |||
3038 | return retcode; | 3039 | return retcode; |
3039 | } | 3040 | } |
3040 | 3041 | ||
3042 | #ifdef CONFIG_COMPAT | ||
3043 | typedef struct sg_io_hdr32 { | ||
3044 | compat_int_t interface_id; /* [i] 'S' for SCSI generic (required) */ | ||
3045 | compat_int_t dxfer_direction; /* [i] data transfer direction */ | ||
3046 | unsigned char cmd_len; /* [i] SCSI command length ( <= 16 bytes) */ | ||
3047 | unsigned char mx_sb_len; /* [i] max length to write to sbp */ | ||
3048 | unsigned short iovec_count; /* [i] 0 implies no scatter gather */ | ||
3049 | compat_uint_t dxfer_len; /* [i] byte count of data transfer */ | ||
3050 | compat_uint_t dxferp; /* [i], [*io] points to data transfer memory | ||
3051 | or scatter gather list */ | ||
3052 | compat_uptr_t cmdp; /* [i], [*i] points to command to perform */ | ||
3053 | compat_uptr_t sbp; /* [i], [*o] points to sense_buffer memory */ | ||
3054 | compat_uint_t timeout; /* [i] MAX_UINT->no timeout (unit: millisec) */ | ||
3055 | compat_uint_t flags; /* [i] 0 -> default, see SG_FLAG... */ | ||
3056 | compat_int_t pack_id; /* [i->o] unused internally (normally) */ | ||
3057 | compat_uptr_t usr_ptr; /* [i->o] unused internally */ | ||
3058 | unsigned char status; /* [o] scsi status */ | ||
3059 | unsigned char masked_status; /* [o] shifted, masked scsi status */ | ||
3060 | unsigned char msg_status; /* [o] messaging level data (optional) */ | ||
3061 | unsigned char sb_len_wr; /* [o] byte count actually written to sbp */ | ||
3062 | unsigned short host_status; /* [o] errors from host adapter */ | ||
3063 | unsigned short driver_status; /* [o] errors from software driver */ | ||
3064 | compat_int_t resid; /* [o] dxfer_len - actual_transferred */ | ||
3065 | compat_uint_t duration; /* [o] time taken by cmd (unit: millisec) */ | ||
3066 | compat_uint_t info; /* [o] auxiliary information */ | ||
3067 | } sg_io_hdr32_t; /* 64 bytes long (on sparc32) */ | ||
3068 | |||
3069 | typedef struct sg_iovec32 { | ||
3070 | compat_uint_t iov_base; | ||
3071 | compat_uint_t iov_len; | ||
3072 | } sg_iovec32_t; | ||
3073 | |||
3074 | static int sg_build_iovec(sg_io_hdr_t __user *sgio, void __user *dxferp, u16 iovec_count) | ||
3075 | { | ||
3076 | sg_iovec_t __user *iov = (sg_iovec_t __user *) (sgio + 1); | ||
3077 | sg_iovec32_t __user *iov32 = dxferp; | ||
3078 | int i; | ||
3079 | |||
3080 | for (i = 0; i < iovec_count; i++) { | ||
3081 | u32 base, len; | ||
3082 | |||
3083 | if (get_user(base, &iov32[i].iov_base) || | ||
3084 | get_user(len, &iov32[i].iov_len) || | ||
3085 | put_user(compat_ptr(base), &iov[i].iov_base) || | ||
3086 | put_user(len, &iov[i].iov_len)) | ||
3087 | return -EFAULT; | ||
3088 | } | ||
3089 | |||
3090 | if (put_user(iov, &sgio->dxferp)) | ||
3091 | return -EFAULT; | ||
3092 | return 0; | ||
3093 | } | ||
3094 | |||
3095 | int nvme_sg_io32(struct nvme_ns *ns, unsigned long arg) | ||
3096 | { | ||
3097 | sg_io_hdr32_t __user *sgio32 = (sg_io_hdr32_t __user *)arg; | ||
3098 | sg_io_hdr_t __user *sgio; | ||
3099 | u16 iovec_count; | ||
3100 | u32 data; | ||
3101 | void __user *dxferp; | ||
3102 | int err; | ||
3103 | int interface_id; | ||
3104 | |||
3105 | if (get_user(interface_id, &sgio32->interface_id)) | ||
3106 | return -EFAULT; | ||
3107 | if (interface_id != 'S') | ||
3108 | return -EINVAL; | ||
3109 | |||
3110 | if (get_user(iovec_count, &sgio32->iovec_count)) | ||
3111 | return -EFAULT; | ||
3112 | |||
3113 | { | ||
3114 | void __user *top = compat_alloc_user_space(0); | ||
3115 | void __user *new = compat_alloc_user_space(sizeof(sg_io_hdr_t) + | ||
3116 | (iovec_count * sizeof(sg_iovec_t))); | ||
3117 | if (new > top) | ||
3118 | return -EINVAL; | ||
3119 | |||
3120 | sgio = new; | ||
3121 | } | ||
3122 | |||
3123 | /* Ok, now construct. */ | ||
3124 | if (copy_in_user(&sgio->interface_id, &sgio32->interface_id, | ||
3125 | (2 * sizeof(int)) + | ||
3126 | (2 * sizeof(unsigned char)) + | ||
3127 | (1 * sizeof(unsigned short)) + | ||
3128 | (1 * sizeof(unsigned int)))) | ||
3129 | return -EFAULT; | ||
3130 | |||
3131 | if (get_user(data, &sgio32->dxferp)) | ||
3132 | return -EFAULT; | ||
3133 | dxferp = compat_ptr(data); | ||
3134 | if (iovec_count) { | ||
3135 | if (sg_build_iovec(sgio, dxferp, iovec_count)) | ||
3136 | return -EFAULT; | ||
3137 | } else { | ||
3138 | if (put_user(dxferp, &sgio->dxferp)) | ||
3139 | return -EFAULT; | ||
3140 | } | ||
3141 | |||
3142 | { | ||
3143 | unsigned char __user *cmdp; | ||
3144 | unsigned char __user *sbp; | ||
3145 | |||
3146 | if (get_user(data, &sgio32->cmdp)) | ||
3147 | return -EFAULT; | ||
3148 | cmdp = compat_ptr(data); | ||
3149 | |||
3150 | if (get_user(data, &sgio32->sbp)) | ||
3151 | return -EFAULT; | ||
3152 | sbp = compat_ptr(data); | ||
3153 | |||
3154 | if (put_user(cmdp, &sgio->cmdp) || | ||
3155 | put_user(sbp, &sgio->sbp)) | ||
3156 | return -EFAULT; | ||
3157 | } | ||
3158 | |||
3159 | if (copy_in_user(&sgio->timeout, &sgio32->timeout, | ||
3160 | 3 * sizeof(int))) | ||
3161 | return -EFAULT; | ||
3162 | |||
3163 | if (get_user(data, &sgio32->usr_ptr)) | ||
3164 | return -EFAULT; | ||
3165 | if (put_user(compat_ptr(data), &sgio->usr_ptr)) | ||
3166 | return -EFAULT; | ||
3167 | |||
3168 | err = nvme_sg_io(ns, sgio); | ||
3169 | if (err >= 0) { | ||
3170 | void __user *datap; | ||
3171 | |||
3172 | if (copy_in_user(&sgio32->pack_id, &sgio->pack_id, | ||
3173 | sizeof(int)) || | ||
3174 | get_user(datap, &sgio->usr_ptr) || | ||
3175 | put_user((u32)(unsigned long)datap, | ||
3176 | &sgio32->usr_ptr) || | ||
3177 | copy_in_user(&sgio32->status, &sgio->status, | ||
3178 | (4 * sizeof(unsigned char)) + | ||
3179 | (2 * sizeof(unsigned short)) + | ||
3180 | (3 * sizeof(int)))) | ||
3181 | err = -EFAULT; | ||
3182 | } | ||
3183 | |||
3184 | return err; | ||
3185 | } | ||
3186 | #endif | ||
3187 | |||
3041 | int nvme_sg_get_version_num(int __user *ip) | 3188 | int nvme_sg_get_version_num(int __user *ip) |
3042 | { | 3189 | { |
3043 | return put_user(sg_version_num, ip); | 3190 | return put_user(sg_version_num, ip); |