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@xxxxxx> --- 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(-) diff --git a/fs/read_write.c b/fs/read_write.c index 6dcae3eb7b0f..7218d69b1763 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -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; } diff --git a/include/linux/backing-dev-defs.h b/include/linux/backing-dev-defs.h index 1b4d69f68c33..8c1914b15cd0 100644 --- a/include/linux/backing-dev-defs.h +++ b/include/linux/backing-dev-defs.h @@ -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 { diff --git a/include/linux/backing-dev.h b/include/linux/backing-dev.h index c82794f20110..b20e33b71071 100644 --- a/include/linux/backing-dev.h +++ b/include/linux/backing-dev.h @@ -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 */ diff --git a/include/linux/streamid.h b/include/linux/streamid.h index ea1f8e7eb8a5..c7ba4770a792 100644 --- a/include/linux/streamid.h +++ b/include/linux/streamid.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 diff --git a/mm/backing-dev.c b/mm/backing-dev.c index c554d173a65f..2ed97181ad49 100644 --- a/mm/backing-dev.c +++ b/mm/backing-dev.c @@ -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) { -- 2.4.1.168.g1ea28e1 -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html