usb: gadget: f_fs: Prevent panic due to failure of huge size buffer allocation
authorDongwoo Lee <dwoo08.lee@samsung.com>
Fri, 26 Oct 2018 01:41:41 +0000 (10:41 +0900)
committerHoegeun Kwon <hoegeun.kwon@samsung.com>
Wed, 23 Nov 2022 02:23:53 +0000 (11:23 +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: I4df1987a6f07e558772dcc5f6f020cc550fb1b13
Signed-off-by: Dongwoo Lee <dwoo08.lee@samsung.com>
 Conflicts:
drivers/usb/gadget/function/f_fs.c

Signed-off-by: Hoegeun Kwon <hoegeun.kwon@samsung.com>
drivers/usb/gadget/function/f_fs.c

index adc44a2..46da01c 100644 (file)
@@ -805,7 +805,7 @@ static inline void *ffs_alloc_buffer(struct ffs_io_data *io_data,
        if (io_data->use_sg)
                return ffs_build_sg_list(&io_data->sgt, data_len);
 
-       return kmalloc(data_len, GFP_KERNEL);
+       return kmalloc(data_len, GFP_KERNEL | __GFP_NOWARN);
 }
 
 static inline void ffs_free_buffer(struct ffs_io_data *io_data)
@@ -1018,10 +1018,35 @@ 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);
 
+retry_malloc:
                data = ffs_alloc_buffer(io_data, data_len);
                if (!data) {
-                       ret = -ENOMEM;
-                       goto error_mutex;
+                       /* If usb gadget device using physically contiguous
+                        * buffer 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
+                        * 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.
+                        * Otherwise, return error when scatter-gather list.
+                        */
+                       if (data_len <= PAGE_SIZE || io_data->use_sg) {
+                               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)) {