fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / fs / seq_file.c
index 8bd7097..0ac22af 100644 (file)
  *     ERR_PTR(error).  In the end of sequence they return %NULL. ->show()
  *     returns 0 in case of success and negative number in case of error.
  */
-int seq_open(struct file *file, struct seq_operations *op)
+int seq_open(struct file *file, const struct seq_operations *op)
 {
-       struct seq_file *p = kmalloc(sizeof(*p), GFP_KERNEL);
-       if (!p)
-               return -ENOMEM;
+       struct seq_file *p = file->private_data;
+
+       if (!p) {
+               p = kmalloc(sizeof(*p), GFP_KERNEL);
+               if (!p)
+                       return -ENOMEM;
+               file->private_data = p;
+       }
        memset(p, 0, sizeof(*p));
-       sema_init(&p->sem, 1);
+       mutex_init(&p->lock);
        p->op = op;
-       file->private_data = p;
+
+       /*
+        * Wrappers around seq_open(e.g. swaps_open) need to be
+        * aware of this. If they set f_version themselves, they
+        * should call seq_open first and then set f_version.
+        */
+       file->f_version = 0;
+
+       /* SEQ files support lseek, but not pread/pwrite */
+       file->f_mode &= ~(FMODE_PREAD | FMODE_PWRITE);
        return 0;
 }
 EXPORT_SYMBOL(seq_open);
 
 /**
  *     seq_read -      ->read() method for sequential files.
- *     @file, @buf, @size, @ppos: see file_operations method
+ *     @file: the file to read from
+ *     @buf: the buffer to read to
+ *     @size: the maximum number of bytes to read
+ *     @ppos: the current position in the file
  *
  *     Ready-made ->f_op->read()
  */
@@ -54,10 +71,19 @@ ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
        void *p;
        int err = 0;
 
-       if (ppos != &file->f_pos)
-               return -EPIPE;
-
-       down(&m->sem);
+       mutex_lock(&m->lock);
+       /*
+        * seq_file->op->..m_start/m_stop/m_next may do special actions
+        * or optimisations based on the file->f_version, so we want to
+        * pass the file->f_version to those methods.
+        *
+        * seq_file->version is just copy of f_version, and seq_file
+        * methods can treat it simply as file version.
+        * It is copied in first and copied out after all operations.
+        * It is convenient to have it as  part of structure to avoid the
+        * need of passing another argument to all the seq_file methods.
+        */
+       m->version = file->f_version;
        /* grab buffer if we didn't have one */
        if (!m->buf) {
                m->buf = kmalloc(m->size = PAGE_SIZE, GFP_KERNEL);
@@ -98,6 +124,7 @@ ssize_t seq_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
                if (!m->buf)
                        goto Enomem;
                m->count = 0;
+               m->version = 0;
        }
        m->op->stop(m, p);
        m->count = 0;
@@ -136,7 +163,8 @@ Done:
                copied = err;
        else
                *ppos += copied;
-       up(&m->sem);
+       file->f_version = m->version;
+       mutex_unlock(&m->lock);
        return copied;
 Enomem:
        err = -ENOMEM;
@@ -153,6 +181,7 @@ static int traverse(struct seq_file *m, loff_t offset)
        int error = 0;
        void *p;
 
+       m->version = 0;
        m->index = 0;
        m->count = m->from = 0;
        if (!offset)
@@ -197,7 +226,9 @@ Eoverflow:
 
 /**
  *     seq_lseek -     ->llseek() method for sequential files.
- *     @file, @offset, @origin: see file_operations method
+ *     @file: the file in question
+ *     @offset: new position
+ *     @origin: 0 for absolute, 1 for relative position
  *
  *     Ready-made ->f_op->llseek()
  */
@@ -206,7 +237,8 @@ loff_t seq_lseek(struct file *file, loff_t offset, int origin)
        struct seq_file *m = (struct seq_file *)file->private_data;
        long long retval = -EINVAL;
 
-       down(&m->sem);
+       mutex_lock(&m->lock);
+       m->version = file->f_version;
        switch (origin) {
                case 1:
                        offset += file->f_pos;
@@ -220,6 +252,7 @@ loff_t seq_lseek(struct file *file, loff_t offset, int origin)
                                if (retval) {
                                        /* with extreme prejudice... */
                                        file->f_pos = 0;
+                                       m->version = 0;
                                        m->index = 0;
                                        m->count = 0;
                                } else {
@@ -227,7 +260,8 @@ loff_t seq_lseek(struct file *file, loff_t offset, int origin)
                                }
                        }
        }
-       up(&m->sem);
+       mutex_unlock(&m->lock);
+       file->f_version = m->version;
        return retval;
 }
 EXPORT_SYMBOL(seq_lseek);
@@ -235,7 +269,7 @@ EXPORT_SYMBOL(seq_lseek);
 /**
  *     seq_release -   free the structures associated with sequential file.
  *     @file: file in question
- *     @inode: file->f_dentry->d_inode
+ *     @inode: file->f_path.dentry->d_inode
  *
  *     Frees the structures associated with sequential file; can be used
  *     as ->f_op->release() if you don't have private data to destroy.
@@ -374,7 +408,7 @@ EXPORT_SYMBOL(single_open);
 
 int single_release(struct inode *inode, struct file *file)
 {
-       struct seq_operations *op = ((struct seq_file *)file->private_data)->op;
+       const struct seq_operations *op = ((struct seq_file *)file->private_data)->op;
        int res = seq_release(inode, file);
        kfree(op);
        return res;