+ return 0;
+}
+
+static ssize_t hello_read(struct file *fp, char __user *buf, size_t sz,
+ loff_t *pos)
+{
+ int r;
+ size_t fsz;
+ size_t ssz;
+ size_t len;
+ if (mutex_lock_interruptible(&hello_mtx))
+ return -ERESTARTSYS;
+ len = CIRC_CNT(hello->head, hello->tail, MAXLEN);
+ if (sz > len)
+ sz = len;
+ /* First, copy at most until the limit of the buffer */
+ fsz = min(sz, CIRC_CNT_TO_END(hello->head, hello->tail, MAXLEN));
+ r = copy_to_user(buf, hello->buf + hello->tail, fsz);
+ ssz = sz - fsz;
+ if (!r && !ssz)
+ r = copy_to_user(buf + fsz, hello->buf, ssz);
+ if (!r)
+ hello->tail = (hello->tail + sz) & ~MAXLEN;
+ mutex_unlock(&hello_mtx);
+ if (r)
+ return -EFAULT;
+ complete(&hello_wait);
+ return sz;
+}
+
+static ssize_t hello_write(struct file *fp, const char __user *buf, size_t sz,
+ loff_t *pos)
+{
+ int r;
+ size_t fsz;
+ size_t ssz;
+ size_t len;
+ if (mutex_lock_interruptible(&hello_mtx))
+ return -ERESTARTSYS;
+ len = CIRC_SPACE(hello->head, hello->tail, MAXLEN);
+ while (len == 0) {
+ mutex_unlock(&hello_mtx);
+ if (wait_for_completion_interruptible(&hello_wait))
+ return -ERESTARTSYS;
+ if (mutex_lock_interruptible(&hello_mtx))
+ return -ERESTARTSYS;
+ len = CIRC_SPACE(hello->head, hello->tail, MAXLEN);
+ }
+ if (sz > len)
+ sz = len;
+ fsz = min(sz, CIRC_SPACE_TO_END(hello->head, hello->tail, MAXLEN));
+ r = copy_from_user(hello->buf + hello->head, buf, fsz);
+ ssz = sz - fsz;
+ if (!r && !ssz)
+ r = copy_from_user(hello->buf, buf + fsz, ssz);
+ if (!r)
+ hello->head = (hello->head + sz) & ~MAXLEN;
+ mutex_unlock(&hello_mtx);
+ if (r)
+ return -EFAULT;
+ return sz;
+}
+
+static int hello_release(struct inode *ino, struct file *fp)
+{