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)
 {
