[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

[BUG] nbd-server: invalid length for trim requests



When enabling TRIM support for a block device, a request can result in a write beyond the end of such a block device, as shown in the following in the following debug output:

+*handling trim request
    client->exportsize  :            134217728
    req->from           :             67109888
    cur.startoff        :                    0
    reqoff              :             67109888
    next.startoff       :            134217728
    curlen              :             67107840
    curlen - reqoff     : 18446744073709549568
    req->len            :              8388608
Request to punch a hole in fd=5, starting from 67109888, length 18446744073709549568
Performed TRIM request from 67109888 to 8388608

As a result of such a request the nbd-server process will hang with 100% cpu load while the kernel writes a lot of warnings and can not be killed until all requests are handled by the kernel.
I used the following patch to mitigate the problem and get trim/discard working for a simple block device export:

--- nbdsrv.c.old
+++ nbdsrv.c
@@ -268,6 +268,7 @@
     FILE_INFO cur = g_array_index(client->export, FILE_INFO, 0);
     FILE_INFO next;
     int i = 1;
+    uint64_t reqfrom = req->from;
     do {
         if(i<client->export->len) {
             next = g_array_index(client->export, FILE_INFO, i);
@@ -275,13 +276,14 @@
             next.fhandle = -1;
             next.startoff = client->exportsize;
         }
-        if(cur.startoff <= req->from && next.startoff - cur.startoff >= req->len) {
-            off_t reqoff = req->from - cur.startoff;
-            off_t curlen = next.startoff - reqoff;
-            off_t reqlen = curlen - reqoff > req->len ? req->len : curlen - reqoff;
-            punch_hole(cur.fhandle, reqoff, reqlen);
+        if(cur.startoff <= reqfrom) {
+            uint64_t end   = MIN(req->from + req->len, next.startoff) - cur.startoff;
+            uint64_t start = reqfrom - cur.startoff;
+            uint64_t len   = end - start; 
+            punch_hole(cur.fhandle, start, len);
         }
         cur = next;
+        reqfrom = cur.startoff;
         i++;
     } while(i < client->export->len && cur.startoff < (req->from + req->len));
     DEBUG("Performed TRIM request from %llu to %llu", (unsigned long long) req->from, (unsigned long long) req->len);

The patch was not tested against multi-file exports or reviewed, but I though it might help in fixing the issue. 

with kind regards
Raphael


Reply to: