@@ -66,6 +66,7 @@ xfbma_init(
array->filp = filp;
array->obj_size = obj_size;
array->nr = 0;
+ array->io_flags = 0;
return array;
out_filp:
fput(filp);
@@ -105,7 +106,8 @@ xfbma_get(
return -ENODATA;
}
- return xfile_io(array->filp, XFILE_IO_READ, &pos, ptr, array->obj_size);
+ return xfile_io(array->filp, array->io_flags | XFILE_IO_READ, &pos,
+ ptr, array->obj_size);
}
/* Put an element in the array. */
@@ -122,8 +124,8 @@ xfbma_set(
return -ENODATA;
}
- return xfile_io(array->filp, XFILE_IO_WRITE, &pos, ptr,
- array->obj_size);
+ return xfile_io(array->filp, array->io_flags | XFILE_IO_WRITE, &pos,
+ ptr, array->obj_size);
}
/* Is this array element NULL? */
@@ -172,8 +174,8 @@ xfbma_nullify(
}
memset(temp, 0, array->obj_size);
- return xfile_io(array->filp, XFILE_IO_WRITE, &pos, temp,
- array->obj_size);
+ return xfile_io(array->filp, array->io_flags | XFILE_IO_WRITE, &pos,
+ temp, array->obj_size);
}
/* Append an element to the array. */
@@ -190,8 +192,8 @@ xfbma_append(
return -ENODATA;
}
- error = xfile_io(array->filp, XFILE_IO_WRITE, &pos, ptr,
- array->obj_size);
+ error = xfile_io(array->filp, array->io_flags | XFILE_IO_WRITE, &pos,
+ ptr, array->obj_size);
if (error)
return error;
array->nr++;
@@ -219,8 +221,8 @@ xfbma_iter_del(
for (pos = 0, i = 0; pos < max_bytes; i++) {
pgoff_t pagenr;
- error = xfile_io(array->filp, XFILE_IO_READ, &pos, temp,
- array->obj_size);
+ error = xfile_io(array->filp, array->io_flags | XFILE_IO_READ,
+ &pos, temp, array->obj_size);
if (error)
break;
if (xfbma_is_null(array, temp))
@@ -10,6 +10,7 @@ struct xfbma {
struct file *filp;
size_t obj_size;
uint64_t nr;
+ unsigned int io_flags;
};
struct xfbma *xfbma_init(size_t obj_size);
@@ -46,6 +46,7 @@ xblob_init(void)
blob->filp = filp;
blob->last_offset = PAGE_SIZE;
+ blob->io_flags = 0;
return blob;
out_filp:
fput(filp);
@@ -73,7 +74,8 @@ xblob_get(
loff_t pos = cookie;
int error;
- error = xfile_io(blob->filp, XFILE_IO_READ, &pos, &key, sizeof(key));
+ error = xfile_io(blob->filp, blob->io_flags | XFILE_IO_READ, &pos,
+ &key, sizeof(key));
if (error)
return error;
@@ -86,7 +88,8 @@ xblob_get(
return -EFBIG;
}
- return xfile_io(blob->filp, XFILE_IO_READ, &pos, ptr, key.size);
+ return xfile_io(blob->filp, blob->io_flags | XFILE_IO_READ, &pos, ptr,
+ key.size);
}
/* Store a blob. */
@@ -105,11 +108,13 @@ xblob_put(
loff_t pos = blob->last_offset;
int error;
- error = xfile_io(blob->filp, XFILE_IO_WRITE, &pos, &key, sizeof(key));
+ error = xfile_io(blob->filp, blob->io_flags | XFILE_IO_WRITE, &pos,
+ &key, sizeof(key));
if (error)
goto out_err;
- error = xfile_io(blob->filp, XFILE_IO_WRITE, &pos, ptr, size);
+ error = xfile_io(blob->filp, blob->io_flags | XFILE_IO_WRITE, &pos,
+ ptr, size);
if (error)
goto out_err;
@@ -131,7 +136,8 @@ xblob_free(
loff_t pos = cookie;
int error;
- error = xfile_io(blob->filp, XFILE_IO_READ, &pos, &key, sizeof(key));
+ error = xfile_io(blob->filp, blob->io_flags | XFILE_IO_READ, &pos,
+ &key, sizeof(key));
if (error)
return error;
@@ -9,6 +9,7 @@
struct xblob {
struct file *filp;
loff_t last_offset;
+ unsigned int io_flags;
};
typedef loff_t xblob_cookie;
@@ -41,14 +41,76 @@ xfile_destroy(
fput(filp);
}
+struct xfile_io_args {
+ struct work_struct work;
+ struct completion *done;
+
+ struct file *filp;
+ void *ptr;
+ loff_t *pos;
+ size_t count;
+ ssize_t ret;
+ bool is_read;
+};
+
+static void
+xfile_io_worker(
+ struct work_struct *work)
+{
+ struct xfile_io_args *args;
+ unsigned int pflags;
+
+ args = container_of(work, struct xfile_io_args, work);
+ pflags = memalloc_nofs_save();
+
+ if (args->is_read)
+ args->ret = kernel_read(args->filp, args->ptr, args->count,
+ args->pos);
+ else
+ args->ret = kernel_write(args->filp, args->ptr, args->count,
+ args->pos);
+ complete(args->done);
+
+ memalloc_nofs_restore(pflags);
+}
+
/*
- * Perform a read or write IO to the file backing the array. We can defer
- * the work to a workqueue if the caller so desires, either to reduce stack
- * usage or because the xfs is frozen and we want to avoid deadlocking on the
- * page fault that might be about to happen.
+ * Perform a read or write IO to the file backing the array. Defer the work to
+ * a workqueue to avoid recursing into the filesystem while we have locks held.
*/
-int
-xfile_io(
+static int
+xfile_io_async(
+ struct file *filp,
+ unsigned int cmd_flags,
+ loff_t *pos,
+ void *ptr,
+ size_t count)
+{
+ DECLARE_COMPLETION_ONSTACK(done);
+ struct xfile_io_args args = {
+ .filp = filp,
+ .ptr = ptr,
+ .pos = pos,
+ .count = count,
+ .done = &done,
+ .is_read = (cmd_flags & XFILE_IO_MASK) == XFILE_IO_READ,
+ };
+
+ INIT_WORK_ONSTACK(&args.work, xfile_io_worker);
+ schedule_work(&args.work);
+ wait_for_completion(&done);
+ destroy_work_on_stack(&args.work);
+
+ /*
+ * Since we're treating this file as "memory", any IO error should be
+ * treated as a failure to find any memory.
+ */
+ return args.ret == count ? 0 : -ENOMEM;
+}
+
+/* Perform a read or write IO to the file backing the array. */
+static int
+xfile_io_sync(
struct file *filp,
unsigned int cmd_flags,
loff_t *pos,
@@ -71,6 +133,20 @@ xfile_io(
return ret == count ? 0 : -ENOMEM;
}
+/* Perform a read or write IO to the file backing the array. */
+int
+xfile_io(
+ struct file *filp,
+ unsigned int cmd_flags,
+ loff_t *pos,
+ void *ptr,
+ size_t count)
+{
+ if (cmd_flags & XFILE_IO_ASYNC)
+ return xfile_io_async(filp, cmd_flags, pos, ptr, count);
+ return xfile_io_sync(filp, cmd_flags, pos, ptr, count);
+}
+
/* Discard pages backing a range of the file. */
void
xfile_discard(
@@ -13,6 +13,7 @@ void xfile_destroy(struct file *filp);
#define XFILE_IO_READ (0)
#define XFILE_IO_WRITE (1)
#define XFILE_IO_MASK (1 << 0)
+#define XFILE_IO_ASYNC (1 << 1)
int xfile_io(struct file *filp, unsigned int cmd_flags, loff_t *pos,
void *ptr, size_t count);