usb: gadget: f_fs: Give chance to retry malloc for large size buffer
authorDongwoo Lee <dwoo08.lee@samsung.com>
Fri, 26 Oct 2018 01:41:41 +0000 (10:41 +0900)
committerHoegeun Kwon <hoegeun.kwon@samsung.com>
Mon, 6 Jul 2020 09:09:08 +0000 (18:09 +0900)
The f_fs daemons usually use large size buffer for increasing transfer
performance, but it can cause memory allocation failure in case of
that buddy space is fragmented. Since this, instead of just returning
error in this case, give the chance to retry to allocate memory with
a half length in order to prevent daemon crash due to failure of
buffer allocation.

Change-Id: I2171932b8cb565102d63eb82c114987b85d26ed9
Signed-off-by: Dongwoo Lee <dwoo08.lee@samsung.com>
drivers/usb/gadget/function/f_fs.c

index f8bcfc5..6f0c28f 100644 (file)
@@ -1016,10 +1016,34 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
                io_data->use_sg = gadget->sg_supported && data_len > PAGE_SIZE;
                spin_unlock_irq(&epfile->ffs->eps_lock);
 
-               data = ffs_alloc_buffer(io_data, data_len);
+retry_malloc:
+               data = kmalloc(data_len, GFP_KERNEL | __GFP_NOWARN);
                if (unlikely(!data)) {
-                       ret = -ENOMEM;
-                       goto error_mutex;
+                       /*
+                        * f_fs daemons usually use large size buffer for
+                        * performance. However, this can cause failure of
+                        * kmalloc() due to buddy fragmentation, even if there
+                        * is available memory and thus it can be compacted by
+                        * by kswapd. Therefore, instead of just returning error
+                        * to daemon in the case of failure of kmalloc(), give
+                        * the second chance to allocate buffer with a half size
+                        * until it really fails due to memory shortage.
+                        */
+                       if (unlikely(data_len <= PAGE_SIZE)) {
+                               ret = -ENOMEM;
+                               goto error_mutex;
+                       }
+
+                       data_len = data_len >> 1;
+
+                       if (io_data->read) {
+                               spin_lock_irq(&epfile->ffs->eps_lock);
+                               data_len = usb_ep_align_maybe(gadget,
+                                               ep->ep, data_len);
+                               spin_unlock_irq(&epfile->ffs->eps_lock);
+                       }
+
+                       goto retry_malloc;
                }
                if (!io_data->read &&
                    !copy_from_iter_full(data, data_len, &io_data->data)) {