diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2011-11-17 16:41:25 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2011-11-18 14:09:07 -0500 |
commit | add1aaeabe6b08ed26381a2a06e505b2f09c3ba5 (patch) | |
tree | 42519bb5709cc0943eab7dfa8ca1481ebcdf3815 /drivers/usb/core/devio.c | |
parent | 52fb743d3aa7ee27a4f3182816aa02dc3e513d9d (diff) |
USB: change the memory limits in usbfs URB submission
For a long time people have complained about the limitations imposed
by usbfs. URBs coming from userspace are not allowed to have transfer
buffers larger than a more-or-less arbitrary maximum.
While it is generally a good idea to avoid large transfer buffers
(because the data has to be bounced to/from a contiguous kernel-space
buffer), it's not the kernel's job to enforce such limits. Programs
should be allowed to submit URBs as large as they like; if there isn't
sufficient contiguous memory available then the submission will fail
with a simple ENOMEM error.
On the other hand, we would like to prevent programs from submitting a
lot of small URBs and using up all the DMA-able kernel memory. To
that end, this patch (as1497) replaces the old limits on individual
transfer buffers with a single global limit on the total amount of
memory in use by usbfs. The global limit is set to 16 MB as a nice
compromise value: not too big, but large enough to hold about 300 ms
of data for high-speed transfers.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/core/devio.c')
-rw-r--r-- | drivers/usb/core/devio.c | 76 |
1 files changed, 57 insertions, 19 deletions
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index e8ade68f64e2..b69768b7d226 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c | |||
@@ -86,6 +86,7 @@ struct async { | |||
86 | void __user *userbuffer; | 86 | void __user *userbuffer; |
87 | void __user *userurb; | 87 | void __user *userurb; |
88 | struct urb *urb; | 88 | struct urb *urb; |
89 | unsigned int mem_usage; | ||
89 | int status; | 90 | int status; |
90 | u32 secid; | 91 | u32 secid; |
91 | u8 bulk_addr; | 92 | u8 bulk_addr; |
@@ -108,8 +109,26 @@ enum snoop_when { | |||
108 | 109 | ||
109 | #define USB_DEVICE_DEV MKDEV(USB_DEVICE_MAJOR, 0) | 110 | #define USB_DEVICE_DEV MKDEV(USB_DEVICE_MAJOR, 0) |
110 | 111 | ||
111 | #define MAX_USBFS_BUFFER_SIZE 16384 | 112 | /* Limit on the total amount of memory we can allocate for transfers */ |
113 | #define MAX_USBFS_MEMORY_USAGE 16777216 /* 16 MB */ | ||
112 | 114 | ||
115 | static atomic_t usbfs_memory_usage; /* Total memory currently allocated */ | ||
116 | |||
117 | /* Check whether it's okay to allocate more memory for a transfer */ | ||
118 | static int usbfs_increase_memory_usage(unsigned amount) | ||
119 | { | ||
120 | atomic_add(amount, &usbfs_memory_usage); | ||
121 | if (atomic_read(&usbfs_memory_usage) <= MAX_USBFS_MEMORY_USAGE) | ||
122 | return 0; | ||
123 | atomic_sub(amount, &usbfs_memory_usage); | ||
124 | return -ENOMEM; | ||
125 | } | ||
126 | |||
127 | /* Memory for a transfer is being deallocated */ | ||
128 | static void usbfs_decrease_memory_usage(unsigned amount) | ||
129 | { | ||
130 | atomic_sub(amount, &usbfs_memory_usage); | ||
131 | } | ||
113 | 132 | ||
114 | static int connected(struct dev_state *ps) | 133 | static int connected(struct dev_state *ps) |
115 | { | 134 | { |
@@ -253,6 +272,7 @@ static void free_async(struct async *as) | |||
253 | kfree(as->urb->transfer_buffer); | 272 | kfree(as->urb->transfer_buffer); |
254 | kfree(as->urb->setup_packet); | 273 | kfree(as->urb->setup_packet); |
255 | usb_free_urb(as->urb); | 274 | usb_free_urb(as->urb); |
275 | usbfs_decrease_memory_usage(as->mem_usage); | ||
256 | kfree(as); | 276 | kfree(as); |
257 | } | 277 | } |
258 | 278 | ||
@@ -792,9 +812,15 @@ static int proc_control(struct dev_state *ps, void __user *arg) | |||
792 | wLength = ctrl.wLength; /* To suppress 64k PAGE_SIZE warning */ | 812 | wLength = ctrl.wLength; /* To suppress 64k PAGE_SIZE warning */ |
793 | if (wLength > PAGE_SIZE) | 813 | if (wLength > PAGE_SIZE) |
794 | return -EINVAL; | 814 | return -EINVAL; |
815 | ret = usbfs_increase_memory_usage(PAGE_SIZE + sizeof(struct urb) + | ||
816 | sizeof(struct usb_ctrlrequest)); | ||
817 | if (ret) | ||
818 | return ret; | ||
795 | tbuf = (unsigned char *)__get_free_page(GFP_KERNEL); | 819 | tbuf = (unsigned char *)__get_free_page(GFP_KERNEL); |
796 | if (!tbuf) | 820 | if (!tbuf) { |
797 | return -ENOMEM; | 821 | ret = -ENOMEM; |
822 | goto done; | ||
823 | } | ||
798 | tmo = ctrl.timeout; | 824 | tmo = ctrl.timeout; |
799 | snoop(&dev->dev, "control urb: bRequestType=%02x " | 825 | snoop(&dev->dev, "control urb: bRequestType=%02x " |
800 | "bRequest=%02x wValue=%04x " | 826 | "bRequest=%02x wValue=%04x " |
@@ -852,6 +878,8 @@ static int proc_control(struct dev_state *ps, void __user *arg) | |||
852 | ret = i; | 878 | ret = i; |
853 | done: | 879 | done: |
854 | free_page((unsigned long) tbuf); | 880 | free_page((unsigned long) tbuf); |
881 | usbfs_decrease_memory_usage(PAGE_SIZE + sizeof(struct urb) + | ||
882 | sizeof(struct usb_ctrlrequest)); | ||
855 | return ret; | 883 | return ret; |
856 | } | 884 | } |
857 | 885 | ||
@@ -879,10 +907,15 @@ static int proc_bulk(struct dev_state *ps, void __user *arg) | |||
879 | if (!usb_maxpacket(dev, pipe, !(bulk.ep & USB_DIR_IN))) | 907 | if (!usb_maxpacket(dev, pipe, !(bulk.ep & USB_DIR_IN))) |
880 | return -EINVAL; | 908 | return -EINVAL; |
881 | len1 = bulk.len; | 909 | len1 = bulk.len; |
882 | if (len1 > MAX_USBFS_BUFFER_SIZE) | 910 | if (len1 > MAX_USBFS_MEMORY_USAGE) |
883 | return -EINVAL; | 911 | return -EINVAL; |
884 | if (!(tbuf = kmalloc(len1, GFP_KERNEL))) | 912 | ret = usbfs_increase_memory_usage(len1 + sizeof(struct urb)); |
885 | return -ENOMEM; | 913 | if (ret) |
914 | return ret; | ||
915 | if (!(tbuf = kmalloc(len1, GFP_KERNEL))) { | ||
916 | ret = -ENOMEM; | ||
917 | goto done; | ||
918 | } | ||
886 | tmo = bulk.timeout; | 919 | tmo = bulk.timeout; |
887 | if (bulk.ep & 0x80) { | 920 | if (bulk.ep & 0x80) { |
888 | if (len1 && !access_ok(VERIFY_WRITE, bulk.data, len1)) { | 921 | if (len1 && !access_ok(VERIFY_WRITE, bulk.data, len1)) { |
@@ -919,6 +952,7 @@ static int proc_bulk(struct dev_state *ps, void __user *arg) | |||
919 | ret = (i < 0 ? i : len2); | 952 | ret = (i < 0 ? i : len2); |
920 | done: | 953 | done: |
921 | kfree(tbuf); | 954 | kfree(tbuf); |
955 | usbfs_decrease_memory_usage(len1 + sizeof(struct urb)); | ||
922 | return ret; | 956 | return ret; |
923 | } | 957 | } |
924 | 958 | ||
@@ -1097,14 +1131,14 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, | |||
1097 | } | 1131 | } |
1098 | if (!ep) | 1132 | if (!ep) |
1099 | return -ENOENT; | 1133 | return -ENOENT; |
1134 | |||
1135 | u = 0; | ||
1100 | switch(uurb->type) { | 1136 | switch(uurb->type) { |
1101 | case USBDEVFS_URB_TYPE_CONTROL: | 1137 | case USBDEVFS_URB_TYPE_CONTROL: |
1102 | if (!usb_endpoint_xfer_control(&ep->desc)) | 1138 | if (!usb_endpoint_xfer_control(&ep->desc)) |
1103 | return -EINVAL; | 1139 | return -EINVAL; |
1104 | /* min 8 byte setup packet, | 1140 | /* min 8 byte setup packet */ |
1105 | * max 8 byte setup plus an arbitrary data stage */ | 1141 | if (uurb->buffer_length < 8) |
1106 | if (uurb->buffer_length < 8 || | ||
1107 | uurb->buffer_length > (8 + MAX_USBFS_BUFFER_SIZE)) | ||
1108 | return -EINVAL; | 1142 | return -EINVAL; |
1109 | dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); | 1143 | dr = kmalloc(sizeof(struct usb_ctrlrequest), GFP_KERNEL); |
1110 | if (!dr) | 1144 | if (!dr) |
@@ -1138,6 +1172,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, | |||
1138 | __le16_to_cpup(&dr->wValue), | 1172 | __le16_to_cpup(&dr->wValue), |
1139 | __le16_to_cpup(&dr->wIndex), | 1173 | __le16_to_cpup(&dr->wIndex), |
1140 | __le16_to_cpup(&dr->wLength)); | 1174 | __le16_to_cpup(&dr->wLength)); |
1175 | u = sizeof(struct usb_ctrlrequest); | ||
1141 | break; | 1176 | break; |
1142 | 1177 | ||
1143 | case USBDEVFS_URB_TYPE_BULK: | 1178 | case USBDEVFS_URB_TYPE_BULK: |
@@ -1151,8 +1186,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, | |||
1151 | goto interrupt_urb; | 1186 | goto interrupt_urb; |
1152 | } | 1187 | } |
1153 | uurb->number_of_packets = 0; | 1188 | uurb->number_of_packets = 0; |
1154 | if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE) | ||
1155 | return -EINVAL; | ||
1156 | break; | 1189 | break; |
1157 | 1190 | ||
1158 | case USBDEVFS_URB_TYPE_INTERRUPT: | 1191 | case USBDEVFS_URB_TYPE_INTERRUPT: |
@@ -1160,8 +1193,6 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, | |||
1160 | return -EINVAL; | 1193 | return -EINVAL; |
1161 | interrupt_urb: | 1194 | interrupt_urb: |
1162 | uurb->number_of_packets = 0; | 1195 | uurb->number_of_packets = 0; |
1163 | if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE) | ||
1164 | return -EINVAL; | ||
1165 | break; | 1196 | break; |
1166 | 1197 | ||
1167 | case USBDEVFS_URB_TYPE_ISO: | 1198 | case USBDEVFS_URB_TYPE_ISO: |
@@ -1188,17 +1219,18 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, | |||
1188 | } | 1219 | } |
1189 | totlen += isopkt[u].length; | 1220 | totlen += isopkt[u].length; |
1190 | } | 1221 | } |
1191 | /* 3072 * 64 microframes */ | 1222 | u *= sizeof(struct usb_iso_packet_descriptor); |
1192 | if (totlen > 196608) { | ||
1193 | ret = -EINVAL; | ||
1194 | goto error; | ||
1195 | } | ||
1196 | uurb->buffer_length = totlen; | 1223 | uurb->buffer_length = totlen; |
1197 | break; | 1224 | break; |
1198 | 1225 | ||
1199 | default: | 1226 | default: |
1200 | return -EINVAL; | 1227 | return -EINVAL; |
1201 | } | 1228 | } |
1229 | |||
1230 | if (uurb->buffer_length > MAX_USBFS_MEMORY_USAGE) { | ||
1231 | ret = -EINVAL; | ||
1232 | goto error; | ||
1233 | } | ||
1202 | if (uurb->buffer_length > 0 && | 1234 | if (uurb->buffer_length > 0 && |
1203 | !access_ok(is_in ? VERIFY_WRITE : VERIFY_READ, | 1235 | !access_ok(is_in ? VERIFY_WRITE : VERIFY_READ, |
1204 | uurb->buffer, uurb->buffer_length)) { | 1236 | uurb->buffer, uurb->buffer_length)) { |
@@ -1210,6 +1242,12 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb, | |||
1210 | ret = -ENOMEM; | 1242 | ret = -ENOMEM; |
1211 | goto error; | 1243 | goto error; |
1212 | } | 1244 | } |
1245 | u += sizeof(struct async) + sizeof(struct urb) + uurb->buffer_length; | ||
1246 | ret = usbfs_increase_memory_usage(u); | ||
1247 | if (ret) | ||
1248 | goto error; | ||
1249 | as->mem_usage = u; | ||
1250 | |||
1213 | if (uurb->buffer_length > 0) { | 1251 | if (uurb->buffer_length > 0) { |
1214 | as->urb->transfer_buffer = kmalloc(uurb->buffer_length, | 1252 | as->urb->transfer_buffer = kmalloc(uurb->buffer_length, |
1215 | GFP_KERNEL); | 1253 | GFP_KERNEL); |