diff --git a/chardev.c b/chardev.c
index 2910834757b96264076ef7b32cef80da4b5056f6..51cdf29a4baba726a0d48ea59bf72365b9c278eb 100644
--- a/chardev.c
+++ b/chardev.c
@@ -324,15 +324,32 @@ static int __zio_write_allowed(struct zio_f_priv *priv)
 {
 	struct zio_channel *chan = priv->chan;
 	struct zio_bi *bi = chan->bi;
-	struct zio_block *block;
+	struct zio_block *block = chan->user_block;
 	const int can_write = POLLOUT | POLLWRNORM;
 
-	if (priv->type == ZIO_CDEV_CTRL) {
-		/* Control is always writeable */
+	if (unlikely(priv->type == ZIO_CDEV_CTRL)) {
+		/* A dataful control can be replaced before store */
+		if (chan->cset->ssize) {
+			if (block) {
+				bi->b_op->free_block(bi, block);
+				chan->user_block = NULL;
+			}
+			return can_write;
+		}
+		if (!block)
+			return can_write;
+		/* A dataless control may need to sleep if buffer full */
+		if (bi->b_op->store_block(bi, block) < 0)
+			return 0;
+		chan->user_block = NULL;
 		return can_write;
 	}
 
-	/* We want to write data. If we have no control, retrieve one */
+	/* We want to write data. If datasize is zero, we can't */
+	if (!chan->cset->ssize)
+		return 0;
+
+	/* If we have no block, retrieve one */
 	if (!chan->user_block)
 		chan->user_block = __zio_write_allocblock(bi);
 	block = chan->user_block;
@@ -443,22 +460,31 @@ static ssize_t zio_generic_write(struct file *f, const char __user *ubuf,
 		return count;
 	}
 
-	/* Control: drop the current block and create a new one */
-	if (priv->type == ZIO_CDEV_CTRL && count < ZIO_CONTROL_SIZE)
+	/* Control: if we get here, we are sure the user_block is NULL */
+	BUG_ON(chan->user_block);
+
+	if (count < ZIO_CONTROL_SIZE)
 		return -EINVAL;
 	count = ZIO_CONTROL_SIZE;
 
-	if (chan->user_block)
-		bi->b_op->free_block(bi, chan->user_block);
-	chan->user_block = NULL;
-	ctrl = zio_alloc_control(GFP_KERNEL);
-	if (!ctrl)
+	block = __zio_write_allocblock(bi);
+	if (!block)
 		return -ENOMEM;
-
-	if (copy_from_user(ctrl, ubuf, count))
+	ctrl = zio_get_ctrl(block);
+	if (copy_from_user(ctrl, ubuf, count)) {
+		bi->b_op->free_block(bi, block);
 		return -EFAULT;
-	memcpy(chan->current_ctrl, ctrl, count);
+	}
 	*offp += count;
+
+	/* FIXME: use information in this control */
+
+	chan->user_block = block;
+
+	/* If it's a dataless channel, just store the control */
+	if (!chan->cset->ssize && bi->b_op->store_block(bi, block) == 0)
+		chan->user_block = NULL;
+
 	return count;
 }