usb: gadget: f_fs: Prevent panic due to failure of huge size buffer allocation 30/307130/1
authorDongwoo Lee <dwoo08.lee@samsung.com>
Fri, 26 Oct 2018 01:41:41 +0000 (10:41 +0900)
committerMarek Szyprowski <m.szyprowski@samsung.com>
Tue, 5 Mar 2024 07:41:11 +0000 (08:41 +0100)
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.

Signed-off-by: Dongwoo Lee <dwoo08.lee@samsung.com>
[hoegeun.kwon: rebased and resolved conflicts]
Signed-off-by: Hoegeun Kwon <hoegeun.kwon@samsung.com>
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Change-Id: Ief5f8256b37b38de14bb333e53c4c4688867d4f6

drivers/usb/gadget/function/f_fs.c

index 6e9ef35..b32b66d 100644 (file)
@@ -799,7 +799,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)
@@ -1010,10 +1010,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)) {