Staging: Pohmelfs: Added IO permissions and priorities.

Signed-off-by: Evgeniy Polyakov <zbr@ioremap.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

diff --git a/Documentation/filesystems/pohmelfs/design_notes.txt b/Documentation/filesystems/pohmelfs/design_notes.txt
index 6d6db60..dcf8335 100644
--- a/Documentation/filesystems/pohmelfs/design_notes.txt
+++ b/Documentation/filesystems/pohmelfs/design_notes.txt
@@ -56,9 +56,10 @@
 data transfers.
 
 POHMELFS clients operate with a working set of servers and are capable of balancing read-only
-operations (like lookups or directory listings) between them.
+operations (like lookups or directory listings) between them according to IO priorities.
 Administrators can add or remove servers from the set at run-time via special commands (described
-in Documentation/pohmelfs/info.txt file). Writes are replicated to all servers.
+in Documentation/pohmelfs/info.txt file). Writes are replicated to all servers, which are connected
+with write permission turned on. IO priority and permissions can be changed in run-time.
 
 POHMELFS is capable of full data channel encryption and/or strong crypto hashing.
 One can select any kernel supported cipher, encryption mode, hash type and operation mode
diff --git a/Documentation/filesystems/pohmelfs/info.txt b/Documentation/filesystems/pohmelfs/info.txt
index 4e3d501..db2e413 100644
--- a/Documentation/filesystems/pohmelfs/info.txt
+++ b/Documentation/filesystems/pohmelfs/info.txt
@@ -1,6 +1,8 @@
 POHMELFS usage information.
 
-Mount options:
+Mount options.
+All but index, number of crypto threads and maximum IO size can changed via remount.
+
 idx=%u
  Each mountpoint is associated with a special index via this option.
  Administrator can add or remove servers from the given index, so all mounts,
@@ -52,16 +54,27 @@
 
 Usage examples.
 
-Add (or remove if it already exists) server server1.net:1025 into the working set with index $idx
+Add server server1.net:1025 into the working set with index $idx
 with appropriate hash algorithm and key file and cipher algorithm, mode and key file:
-$cfg -a server1.net -p 1025 -i $idx -K $hash_key -k $cipher_key
+$cfg A add -a server1.net -p 1025 -i $idx -K $hash_key -k $cipher_key
 
 Mount filesystem with given index $idx to /mnt mountpoint.
 Client will connect to all servers specified in the working set via previous command:
 mount -t pohmel -o idx=$idx q /mnt
 
-One can add or remove servers from working set after mounting too.
+Change permissions to read-only (-I 1 option, '-I 2' - write-only, 3 - rw):
+$cfg A modify -a server1.net -p 1025 -i $idx -I 1
 
+Change IO priority to 123 (node with the highest priority gets read requests).
+$cfg A modify -a server1.net -p 1025 -i $idx -P 123
+
+One can check currect status of all connections in the mountstats file:
+# cat /proc/$PID/mountstats
+...
+device none mounted on /mnt with fstype pohmel
+idx addr(:port) socket_type protocol active priority permissions
+0 server1.net:1026 1 6 1 250 1
+0 server2.net:1025 1 6 1 123 3
 
 Server installation.
 
diff --git a/drivers/staging/pohmelfs/config.c b/drivers/staging/pohmelfs/config.c
index 3e67da9..a6eaa42 100644
--- a/drivers/staging/pohmelfs/config.c
+++ b/drivers/staging/pohmelfs/config.c
@@ -81,6 +81,45 @@
 	return g;
 }
 
+static inline void pohmelfs_insert_config_entry(struct pohmelfs_sb *psb, struct pohmelfs_config *dst)
+{
+	struct pohmelfs_config *tmp;
+
+	INIT_LIST_HEAD(&dst->config_entry);
+
+	list_for_each_entry(tmp, &psb->state_list, config_entry) {
+		if (dst->state.ctl.prio > tmp->state.ctl.prio)
+			list_add_tail(&dst->config_entry, &tmp->config_entry);
+	}
+	if (list_empty(&dst->config_entry))
+		list_add_tail(&dst->config_entry, &psb->state_list);
+}
+
+static int pohmelfs_move_config_entry(struct pohmelfs_sb *psb,
+		struct pohmelfs_config *dst, struct pohmelfs_config *new)
+{
+	if ((dst->state.ctl.prio == new->state.ctl.prio) &&
+		(dst->state.ctl.perm == new->state.ctl.perm))
+		return 0;
+
+	dprintk("%s: dst: prio: %d, perm: %x, new: prio: %d, perm: %d.\n",
+			__func__, dst->state.ctl.prio, dst->state.ctl.perm,
+			new->state.ctl.prio, new->state.ctl.perm);
+	dst->state.ctl.prio = new->state.ctl.prio;
+	dst->state.ctl.perm = new->state.ctl.perm;
+
+	list_del_init(&dst->config_entry);
+	pohmelfs_insert_config_entry(psb, dst);
+	return 0;
+}
+
+/*
+ * pohmelfs_copy_config() is used to copy new state configs from the
+ * config group (controlled by the netlink messages) into the superblock.
+ * This happens either at startup time where no transactions can access
+ * the list of the configs (and thus list of the network states), or at
+ * run-time, where it is protected by the psb->state_lock.
+ */
 int pohmelfs_copy_config(struct pohmelfs_sb *psb)
 {
 	struct pohmelfs_config_group *g;
@@ -103,7 +142,9 @@
 		err = 0;
 		list_for_each_entry(dst, &psb->state_list, config_entry) {
 			if (pohmelfs_config_eql(&dst->state.ctl, &c->state.ctl)) {
-				err = -EEXIST;
+				err = pohmelfs_move_config_entry(psb, dst, c);
+				if (!err)
+					err = -EEXIST;
 				break;
 			}
 		}
@@ -119,7 +160,7 @@
 
 		memcpy(&dst->state.ctl, &c->state.ctl, sizeof(struct pohmelfs_ctl));
 
-		list_add_tail(&dst->config_entry, &psb->state_list);
+		pohmelfs_insert_config_entry(psb, dst);
 
 		err = pohmelfs_state_init_one(psb, dst);
 		if (err) {
@@ -248,6 +289,13 @@
         return err;
 }
 
+static int pohmelfs_modify_config(struct pohmelfs_ctl *old, struct pohmelfs_ctl *new)
+{
+	old->perm = new->perm;
+	old->prio = new->prio;
+	return 0;
+}
+
 static int pohmelfs_cn_ctl(struct cn_msg *msg, int action)
 {
 	struct pohmelfs_config_group *g;
@@ -278,6 +326,9 @@
 				g->num_entry--;
 				kfree(c);
 				goto out_unlock;
+			} else if (action == POHMELFS_FLAGS_MODIFY) {
+				err = pohmelfs_modify_config(sc, ctl);
+				goto out_unlock;
 			} else {
 				err = -EEXIST;
 				goto out_unlock;
@@ -296,6 +347,7 @@
 	}
 	memcpy(&c->state.ctl, ctl, sizeof(struct pohmelfs_ctl));
 	g->num_entry++;
+
 	list_add_tail(&c->config_entry, &g->config_list);
 
 out_unlock:
@@ -401,10 +453,9 @@
 
 	switch (msg->flags) {
 		case POHMELFS_FLAGS_ADD:
-			err = pohmelfs_cn_ctl(msg, POHMELFS_FLAGS_ADD);
-			break;
 		case POHMELFS_FLAGS_DEL:
-			err = pohmelfs_cn_ctl(msg, POHMELFS_FLAGS_DEL);
+		case POHMELFS_FLAGS_MODIFY:
+			err = pohmelfs_cn_ctl(msg, msg->flags);
 			break;
 		case POHMELFS_FLAGS_SHOW:
 			err = pohmelfs_cn_disp(msg);
diff --git a/drivers/staging/pohmelfs/netfs.h b/drivers/staging/pohmelfs/netfs.h
index 7700e2b..c78cfcb 100644
--- a/drivers/staging/pohmelfs/netfs.h
+++ b/drivers/staging/pohmelfs/netfs.h
@@ -87,6 +87,7 @@
 	POHMELFS_FLAGS_DEL,     /* Network state control message for DEL */
 	POHMELFS_FLAGS_SHOW,    /* Network state control message for SHOW */
 	POHMELFS_FLAGS_CRYPTO,	/* Crypto data control message */
+	POHMELFS_FLAGS_MODIFY,	/* Network state modification message */
 };
 
 /*
diff --git a/drivers/staging/pohmelfs/trans.c b/drivers/staging/pohmelfs/trans.c
index b89f9f3..168fc89 100644
--- a/drivers/staging/pohmelfs/trans.c
+++ b/drivers/staging/pohmelfs/trans.c
@@ -456,34 +456,22 @@
 		__func__, t, t->gen, t->iovec.iov_len, t->page_num, psb->active_state);
 #endif
 	mutex_lock(&psb->state_lock);
-
-	if ((t->flags & NETFS_TRANS_SINGLE_DST) && psb->active_state) {
-		st = &psb->active_state->state;
-
-		err = -EPIPE;
-		if (netfs_state_poll(st) & POLLOUT) {
-			err = netfs_trans_push_dst(t, st);
-			if (!err) {
-				err = netfs_trans_send(t, st);
-				if (err) {
-					netfs_trans_drop_last(t, st);
-				} else {
-					pohmelfs_switch_active(psb);
-					goto out;
-				}
-			}
-		}
-		pohmelfs_switch_active(psb);
-	}
-
 	list_for_each_entry(c, &psb->state_list, config_entry) {
 		st = &c->state;
 
+		if (t->flags & NETFS_TRANS_SINGLE_DST) {
+			if (!(st->ctl.perm & POHMELFS_IO_PERM_READ))
+				continue;
+		} else {
+			if (!(st->ctl.perm & POHMELFS_IO_PERM_WRITE))
+				continue;
+		}
+
 		err = netfs_trans_push(t, st);
 		if (!err && (t->flags & NETFS_TRANS_SINGLE_DST))
 			break;
 	}
-out:
+
 	mutex_unlock(&psb->state_lock);
 #if 0
 	dprintk("%s: fully sent t: %p, gen: %u, size: %u, page_num: %u, err: %d.\n",