@@ -1674,7 +1674,8 @@ SYSCALL_DEFINE4(streamid, int, fd, int, cmd,
unsigned int, flags, int, streamid)
{
struct inode *inode;
- ssize_t ret = 0;
+ int alloc_id;
+ ssize_t ret;
struct fd f;
if (cmd != STREAMID_OPEN && cmd != STREAMID_CLOSE &&
@@ -1682,6 +1683,8 @@ SYSCALL_DEFINE4(streamid, int, fd, int, cmd,
return -EINVAL;
if (flags & ~(STREAMID_F_INODE | STREAMID_F_FILE))
return -EINVAL;
+ if (streamid > STREAMID_MAX)
+ return -EINVAL;
f = fdget(fd);
if (!f.file)
@@ -1693,11 +1696,26 @@ SYSCALL_DEFINE4(streamid, int, fd, int, cmd,
goto done;
}
+ if (cmd == STREAMID_GET) {
+ ret = 0;
+ if (flags & STREAMID_F_FILE)
+ ret = f.file->f_streamid;
+ if (!ret && (flags & STREAMID_F_INODE))
+ ret = inode_streamid(inode);
+ if (!(flags & (STREAMID_F_FILE | STREAMID_F_INODE)))
+ ret = file_streamid(f.file);
+ goto done;
+ }
+
+ alloc_id = ret = bdi_streamid(f.file->f_mapping->host, cmd, streamid);
+ if (ret < 0)
+ goto done;
+
if (cmd == STREAMID_OPEN) {
if (flags & STREAMID_F_FILE) {
if (f.file->f_streamid) {
ret = -EBUSY;
- goto done;
+ goto error_close;
}
f.file->f_streamid = ret;
}
@@ -1708,6 +1726,8 @@ SYSCALL_DEFINE4(streamid, int, fd, int, cmd,
else
inode->i_streamid = ret;
spin_unlock(&inode->i_lock);
+ if (ret < 0)
+ goto error_close;
}
} else if (cmd == STREAMID_CLOSE) {
if (f.file->f_streamid == streamid)
@@ -1717,17 +1737,12 @@ SYSCALL_DEFINE4(streamid, int, fd, int, cmd,
inode->i_streamid = 0;
spin_unlock(&inode->i_lock);
}
- } else if (cmd == STREAMID_GET) {
- ret = 0;
- if (flags & STREAMID_F_FILE)
- ret = f.file->f_streamid;
- if (!ret && (flags & STREAMID_F_INODE))
- ret = inode_streamid(inode);
- if (!(flags & (STREAMID_F_FILE | STREAMID_F_INODE)))
- ret = file_streamid(f.file);
}
done:
fdput(f);
return ret;
+error_close:
+ bdi_streamid(f.file->f_mapping->host, STREAMID_CLOSE, alloc_id);
+ goto done;
}
@@ -10,6 +10,7 @@
#include <linux/flex_proportions.h>
#include <linux/timer.h>
#include <linux/workqueue.h>
+#include <linux/idr.h>
struct page;
struct device;
@@ -30,6 +31,7 @@ enum wb_congested_state {
};
typedef int (congested_fn)(void *, int);
+typedef int (streamid_fn)(void *, unsigned int);
enum wb_stat_item {
WB_RECLAIMABLE,
@@ -170,6 +172,11 @@ struct backing_dev_info {
struct dentry *debug_dir;
struct dentry *debug_stats;
#endif
+
+ struct ida stream_ids;
+ streamid_fn *streamid_open;
+ streamid_fn *streamid_close;
+ void *streamid_data;
};
enum {
@@ -514,4 +514,7 @@ static inline int bdi_rw_congested(struct backing_dev_info *bdi)
(1 << WB_async_congested));
}
+int bdi_streamid_open(struct backing_dev_info *bdi, unsigned int id);
+int bdi_streamid_close(struct backing_dev_info *bdi, unsigned int id);
+
#endif /* _LINUX_BACKING_DEV_H */
@@ -6,8 +6,12 @@ enum {
STREAMID_CLOSE = 2, /* close stream */
STREAMID_GET = 3, /* get file/inode stream ID */
+ STREAMID_MAX = 65535,
+
STREAMID_F_INODE = 1, /* set streamid on the inode */
STREAMID_F_FILE = 2, /* set streamid on the file */
};
+ssize_t bdi_streamid(struct inode *inode, int cmd, int streamid);
+
#endif
@@ -10,6 +10,7 @@
#include <linux/module.h>
#include <linux/writeback.h>
#include <linux/device.h>
+#include <linux/streamid.h>
#include <trace/events/writeback.h>
static atomic_long_t bdi_seq = ATOMIC_LONG_INIT(0);
@@ -809,6 +810,7 @@ int bdi_register(struct backing_dev_info *bdi, struct device *parent,
bdi_debug_register(bdi, dev_name(dev));
set_bit(WB_registered, &bdi->wb.state);
+ ida_init(&bdi->stream_ids);
spin_lock_bh(&bdi_lock);
list_add_tail_rcu(&bdi->bdi_list, &bdi_list);
@@ -843,6 +845,7 @@ void bdi_unregister(struct backing_dev_info *bdi)
bdi_remove_from_list(bdi);
wb_shutdown(&bdi->wb);
cgwb_bdi_destroy(bdi);
+ ida_destroy(&bdi->stream_ids);
if (bdi->dev) {
bdi_debug_unregister(bdi);
@@ -1014,6 +1017,56 @@ out:
}
EXPORT_SYMBOL(wait_iff_congested);
+/*
+ * Get a new stream ID for backing device 'bdi'. If 'id' is zero, then get
+ * any new ID. If 'id' is non-zero, attempt to get that specific ID. If
+ * the bdi has a streamid_fn assigned, use that.
+ */
+int bdi_streamid_open(struct backing_dev_info *bdi, unsigned int id)
+{
+ if (bdi->streamid_open)
+ return bdi->streamid_open(bdi->streamid_data, id);
+
+ if (id)
+ return ida_simple_get(&bdi->stream_ids, id, id + 1, GFP_NOIO);
+
+ return ida_simple_get(&bdi->stream_ids, 1, STREAMID_MAX, GFP_NOIO);
+}
+EXPORT_SYMBOL(bdi_streamid_open);
+
+/*
+ * Close stream ID 'id' on bdi 'bdi'.
+ */
+int bdi_streamid_close(struct backing_dev_info *bdi, unsigned int id)
+{
+ if (bdi->streamid_close)
+ return bdi->streamid_close(bdi->streamid_data, id);
+
+ /*
+ * If we don't have a specific open, free the ID we allocated
+ */
+ if (!bdi->streamid_open)
+ return ida_simple_remove(&bdi->stream_ids, id);
+
+ return 0;
+}
+EXPORT_SYMBOL(bdi_streamid_close);
+
+ssize_t bdi_streamid(struct inode *inode, int cmd, int streamid)
+{
+ struct backing_dev_info *bdi = inode_to_bdi(inode);
+
+ if (bdi == &noop_backing_dev_info)
+ return -ENXIO;
+
+ if (cmd == STREAMID_OPEN)
+ return bdi_streamid_open(bdi, streamid);
+ else if (cmd == STREAMID_CLOSE)
+ return bdi_streamid_close(bdi, streamid);
+
+ return -EINVAL;
+}
+
int pdflush_proc_obsolete(struct ctl_table *table, int write,
void __user *buffer, size_t *lenp, loff_t *ppos)
{
We can now allocate and free host assigned stream IDs through the backing device. Add support for a hook for virtual backing devices, that map to a bunch of real backing devices (btrfs, raid, etc). Signed-off-by: Jens Axboe <axboe@fb.com> --- fs/read_write.c | 35 ++++++++++++++++++-------- include/linux/backing-dev-defs.h | 7 ++++++ include/linux/backing-dev.h | 3 +++ include/linux/streamid.h | 4 +++ mm/backing-dev.c | 53 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 92 insertions(+), 10 deletions(-)