Skip to content

Commit

Permalink
V4L/DVB (10295): uvcvideo: Retry URB buffers allocation when the syst…
Browse files Browse the repository at this point in the history
…em is low on memory.

URB buffers for video transfers are sized to UVC_MAX_PACKETS bulk/isochronous
packets by default. If the system is too low on memory try successively
smaller numbers of packets until allocation succeeds.

Tested-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: Laurent Pinchart <laurent.pinchart@skynet.be>
Reviewed-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
  • Loading branch information
Laurent Pinchart authored and Mauro Carvalho Chehab committed Mar 30, 2009
1 parent f61d1d8 commit efdc8a9
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 41 deletions.
79 changes: 42 additions & 37 deletions drivers/media/video/uvc/uvc_video.c
Original file line number Diff line number Diff line change
Expand Up @@ -699,27 +699,47 @@ static void uvc_free_urb_buffers(struct uvc_video_device *video)
* already allocated when resuming from suspend, in which case it will
* return without touching the buffers.
*
* Return 0 on success or -ENOMEM when out of memory.
* Limit the buffer size to UVC_MAX_PACKETS bulk/isochronous packets. If the
* system is too low on memory try successively smaller numbers of packets
* until allocation succeeds.
*
* Return the number of allocated packets on success or 0 when out of memory.
*/
static int uvc_alloc_urb_buffers(struct uvc_video_device *video,
unsigned int size)
unsigned int size, unsigned int psize, gfp_t gfp_flags)
{
unsigned int npackets;
unsigned int i;

/* Buffers are already allocated, bail out. */
if (video->urb_size)
return 0;

for (i = 0; i < UVC_URBS; ++i) {
video->urb_buffer[i] = usb_buffer_alloc(video->dev->udev,
size, GFP_KERNEL, &video->urb_dma[i]);
if (video->urb_buffer[i] == NULL) {
uvc_free_urb_buffers(video);
return -ENOMEM;
/* Compute the number of packets. Bulk endpoints might transfer UVC
* payloads accross multiple URBs.
*/
npackets = DIV_ROUND_UP(size, psize);
if (npackets > UVC_MAX_PACKETS)
npackets = UVC_MAX_PACKETS;

/* Retry allocations until one succeed. */
for (; npackets > 1; npackets /= 2) {
for (i = 0; i < UVC_URBS; ++i) {
video->urb_buffer[i] = usb_buffer_alloc(
video->dev->udev, psize * npackets,
gfp_flags | __GFP_NOWARN, &video->urb_dma[i]);
if (!video->urb_buffer[i]) {
uvc_free_urb_buffers(video);
break;
}
}

if (i == UVC_URBS) {
video->urb_size = psize * npackets;
return npackets;
}
}

video->urb_size = size;
return 0;
}

Expand Down Expand Up @@ -753,29 +773,19 @@ static int uvc_init_video_isoc(struct uvc_video_device *video,
{
struct urb *urb;
unsigned int npackets, i, j;
__u16 psize;
__u32 size;
u16 psize;
u32 size;

/* Compute the number of isochronous packets to allocate by dividing
* the maximum video frame size by the packet size. Limit the result
* to UVC_MAX_ISO_PACKETS.
*/
psize = le16_to_cpu(ep->desc.wMaxPacketSize);
psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));

size = video->streaming->ctrl.dwMaxVideoFrameSize;
if (size > UVC_MAX_FRAME_SIZE)
return -EINVAL;

npackets = DIV_ROUND_UP(size, psize);
if (npackets > UVC_MAX_ISO_PACKETS)
npackets = UVC_MAX_ISO_PACKETS;
npackets = uvc_alloc_urb_buffers(video, size, psize, gfp_flags);
if (npackets == 0)
return -ENOMEM;

size = npackets * psize;

if (uvc_alloc_urb_buffers(video, size) < 0)
return -ENOMEM;

for (i = 0; i < UVC_URBS; ++i) {
urb = usb_alloc_urb(npackets, gfp_flags);
if (urb == NULL) {
Expand Down Expand Up @@ -814,25 +824,20 @@ static int uvc_init_video_bulk(struct uvc_video_device *video,
struct usb_host_endpoint *ep, gfp_t gfp_flags)
{
struct urb *urb;
unsigned int pipe, i;
__u16 psize;
__u32 size;

/* Compute the bulk URB size. Some devices set the maximum payload
* size to a value too high for memory-constrained devices. We must
* then transfer the payload accross multiple URBs. To be consistant
* with isochronous mode, allocate maximum UVC_MAX_ISO_PACKETS per bulk
* URB.
*/
unsigned int npackets, pipe, i;
u16 psize;
u32 size;

psize = le16_to_cpu(ep->desc.wMaxPacketSize) & 0x07ff;
size = video->streaming->ctrl.dwMaxPayloadTransferSize;
video->bulk.max_payload_size = size;
if (size > psize * UVC_MAX_ISO_PACKETS)
size = psize * UVC_MAX_ISO_PACKETS;

if (uvc_alloc_urb_buffers(video, size) < 0)
npackets = uvc_alloc_urb_buffers(video, size, psize, gfp_flags);
if (npackets == 0)
return -ENOMEM;

size = npackets * psize;

if (usb_endpoint_dir_in(&ep->desc))
pipe = usb_rcvbulkpipe(video->dev->udev,
ep->desc.bEndpointAddress);
Expand Down
6 changes: 2 additions & 4 deletions drivers/media/video/uvc/uvcvideo.h
Original file line number Diff line number Diff line change
Expand Up @@ -296,10 +296,8 @@ struct uvc_xu_control {

/* Number of isochronous URBs. */
#define UVC_URBS 5
/* Maximum number of packets per isochronous URB. */
#define UVC_MAX_ISO_PACKETS 40
/* Maximum frame size in bytes, for sanity checking. */
#define UVC_MAX_FRAME_SIZE (16*1024*1024)
/* Maximum number of packets per URB. */
#define UVC_MAX_PACKETS 32
/* Maximum number of video buffers. */
#define UVC_MAX_VIDEO_BUFFERS 32
/* Maximum status buffer size in bytes of interrupt URB. */
Expand Down

0 comments on commit efdc8a9

Please sign in to comment.