]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - fs/fuse/dev.c
[PATCH] atmel: output signal strength information
[linux-2.6-omap-h63xx.git] / fs / fuse / dev.c
index ae26f37e53a109eee66c961d42e0ca38698cb3c2..66571eafbb1eb88afd8acc0e3625bdc0342207df 100644 (file)
@@ -34,6 +34,7 @@ static void fuse_request_init(struct fuse_req *req)
 {
        memset(req, 0, sizeof(*req));
        INIT_LIST_HEAD(&req->list);
+       INIT_LIST_HEAD(&req->intr_entry);
        init_waitqueue_head(&req->waitq);
        atomic_set(&req->count, 1);
 }
@@ -202,7 +203,7 @@ void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req)
 
 /*
  * This function is called when a request is finished.  Either a reply
- * has arrived or it was interrupted (and not yet sent) or some error
+ * has arrived or it was aborted (and not yet sent) or some error
  * occurred during communication with userspace, or the device file
  * was closed.  The requester thread is woken up (if still waiting),
  * the 'end' callback is called if given, else the reference to the
@@ -211,10 +212,12 @@ void fuse_put_request(struct fuse_conn *fc, struct fuse_req *req)
  * Called with fc->lock, unlocks it
  */
 static void request_end(struct fuse_conn *fc, struct fuse_req *req)
+       __releases(fc->lock)
 {
        void (*end) (struct fuse_conn *, struct fuse_req *) = req->end;
        req->end = NULL;
        list_del(&req->list);
+       list_del(&req->intr_entry);
        req->state = FUSE_REQ_FINISHED;
        if (req->background) {
                if (fc->num_background == FUSE_MAX_BACKGROUND) {
@@ -235,28 +238,63 @@ static void request_end(struct fuse_conn *fc, struct fuse_req *req)
                fuse_put_request(fc, req);
 }
 
+static void wait_answer_interruptible(struct fuse_conn *fc,
+                                     struct fuse_req *req)
+{
+       if (signal_pending(current))
+               return;
+
+       spin_unlock(&fc->lock);
+       wait_event_interruptible(req->waitq, req->state == FUSE_REQ_FINISHED);
+       spin_lock(&fc->lock);
+}
+
+static void queue_interrupt(struct fuse_conn *fc, struct fuse_req *req)
+{
+       list_add_tail(&req->intr_entry, &fc->interrupts);
+       wake_up(&fc->waitq);
+       kill_fasync(&fc->fasync, SIGIO, POLL_IN);
+}
+
 /* Called with fc->lock held.  Releases, and then reacquires it. */
 static void request_wait_answer(struct fuse_conn *fc, struct fuse_req *req)
 {
-       sigset_t oldset;
+       if (!fc->no_interrupt) {
+               /* Any signal may interrupt this */
+               wait_answer_interruptible(fc, req);
 
-       spin_unlock(&fc->lock);
-       if (req->force)
+               if (req->aborted)
+                       goto aborted;
+               if (req->state == FUSE_REQ_FINISHED)
+                       return;
+
+               req->interrupted = 1;
+               if (req->state == FUSE_REQ_SENT)
+                       queue_interrupt(fc, req);
+       }
+
+       if (req->force) {
+               spin_unlock(&fc->lock);
                wait_event(req->waitq, req->state == FUSE_REQ_FINISHED);
-       else {
+               spin_lock(&fc->lock);
+       } else {
+               sigset_t oldset;
+
+               /* Only fatal signals may interrupt this */
                block_sigs(&oldset);
-               wait_event_interruptible(req->waitq,
-                                        req->state == FUSE_REQ_FINISHED);
+               wait_answer_interruptible(fc, req);
                restore_sigs(&oldset);
        }
-       spin_lock(&fc->lock);
-       if (req->state == FUSE_REQ_FINISHED && !req->interrupted)
-               return;
 
-       if (!req->interrupted) {
-               req->out.h.error = -EINTR;
-               req->interrupted = 1;
-       }
+       if (req->aborted)
+               goto aborted;
+       if (req->state == FUSE_REQ_FINISHED)
+               return;
+
+       req->out.h.error = -EINTR;
+       req->aborted = 1;
+
+ aborted:
        if (req->locked) {
                /* This is uninterruptible sleep, because data is
                   being copied to/from the buffers of req.  During
@@ -288,13 +326,19 @@ static unsigned len_args(unsigned numargs, struct fuse_arg *args)
        return nbytes;
 }
 
+static u64 fuse_get_unique(struct fuse_conn *fc)
+ {
+       fc->reqctr++;
+       /* zero is special */
+       if (fc->reqctr == 0)
+               fc->reqctr = 1;
+
+       return fc->reqctr;
+}
+
 static void queue_request(struct fuse_conn *fc, struct fuse_req *req)
 {
-       fc->reqctr++;
-       /* zero is special */
-       if (fc->reqctr == 0)
-               fc->reqctr = 1;
-       req->in.h.unique = fc->reqctr;
+       req->in.h.unique = fuse_get_unique(fc);
        req->in.h.len = sizeof(struct fuse_in_header) +
                len_args(req->in.numargs, (struct fuse_arg *) req->in.args);
        list_add_tail(&req->list, &fc->pending);
@@ -307,9 +351,6 @@ static void queue_request(struct fuse_conn *fc, struct fuse_req *req)
        kill_fasync(&fc->fasync, SIGIO, POLL_IN);
 }
 
-/*
- * This can only be interrupted by a SIGKILL
- */
 void request_send(struct fuse_conn *fc, struct fuse_req *req)
 {
        req->isreply = 1;
@@ -361,14 +402,14 @@ void request_send_background(struct fuse_conn *fc, struct fuse_req *req)
 /*
  * Lock the request.  Up to the next unlock_request() there mustn't be
  * anything that could cause a page-fault.  If the request was already
- * interrupted bail out.
+ * aborted bail out.
  */
 static int lock_request(struct fuse_conn *fc, struct fuse_req *req)
 {
        int err = 0;
        if (req) {
                spin_lock(&fc->lock);
-               if (req->interrupted)
+               if (req->aborted)
                        err = -ENOENT;
                else
                        req->locked = 1;
@@ -378,7 +419,7 @@ static int lock_request(struct fuse_conn *fc, struct fuse_req *req)
 }
 
 /*
- * Unlock request.  If it was interrupted during being locked, the
+ * Unlock request.  If it was aborted during being locked, the
  * requester thread is currently waiting for it to be unlocked, so
  * wake it up.
  */
@@ -387,7 +428,7 @@ static void unlock_request(struct fuse_conn *fc, struct fuse_req *req)
        if (req) {
                spin_lock(&fc->lock);
                req->locked = 0;
-               if (req->interrupted)
+               if (req->aborted)
                        wake_up(&req->waitq);
                spin_unlock(&fc->lock);
        }
@@ -566,13 +607,18 @@ static int fuse_copy_args(struct fuse_copy_state *cs, unsigned numargs,
        return err;
 }
 
+static int request_pending(struct fuse_conn *fc)
+{
+       return !list_empty(&fc->pending) || !list_empty(&fc->interrupts);
+}
+
 /* Wait until a request is available on the pending list */
 static void request_wait(struct fuse_conn *fc)
 {
        DECLARE_WAITQUEUE(wait, current);
 
        add_wait_queue_exclusive(&fc->waitq, &wait);
-       while (fc->connected && list_empty(&fc->pending)) {
+       while (fc->connected && !request_pending(fc)) {
                set_current_state(TASK_INTERRUPTIBLE);
                if (signal_pending(current))
                        break;
@@ -585,23 +631,64 @@ static void request_wait(struct fuse_conn *fc)
        remove_wait_queue(&fc->waitq, &wait);
 }
 
+/*
+ * Transfer an interrupt request to userspace
+ *
+ * Unlike other requests this is assembled on demand, without a need
+ * to allocate a separate fuse_req structure.
+ *
+ * Called with fc->lock held, releases it
+ */
+static int fuse_read_interrupt(struct fuse_conn *fc, struct fuse_req *req,
+                              const struct iovec *iov, unsigned long nr_segs)
+       __releases(fc->lock)
+{
+       struct fuse_copy_state cs;
+       struct fuse_in_header ih;
+       struct fuse_interrupt_in arg;
+       unsigned reqsize = sizeof(ih) + sizeof(arg);
+       int err;
+
+       list_del_init(&req->intr_entry);
+       req->intr_unique = fuse_get_unique(fc);
+       memset(&ih, 0, sizeof(ih));
+       memset(&arg, 0, sizeof(arg));
+       ih.len = reqsize;
+       ih.opcode = FUSE_INTERRUPT;
+       ih.unique = req->intr_unique;
+       arg.unique = req->in.h.unique;
+
+       spin_unlock(&fc->lock);
+       if (iov_length(iov, nr_segs) < reqsize)
+               return -EINVAL;
+
+       fuse_copy_init(&cs, fc, 1, NULL, iov, nr_segs);
+       err = fuse_copy_one(&cs, &ih, sizeof(ih));
+       if (!err)
+               err = fuse_copy_one(&cs, &arg, sizeof(arg));
+       fuse_copy_finish(&cs);
+
+       return err ? err : reqsize;
+}
+
 /*
  * Read a single request into the userspace filesystem's buffer.  This
  * function waits until a request is available, then removes it from
  * the pending list and copies request data to userspace buffer.  If
- * no reply is needed (FORGET) or request has been interrupted or
- * there was an error during the copying then it's finished by calling
+ * no reply is needed (FORGET) or request has been aborted or there
+ * was an error during the copying then it's finished by calling
  * request_end().  Otherwise add it to the processing list, and set
  * the 'sent' flag.
  */
-static ssize_t fuse_dev_readv(struct file *file, const struct iovec *iov,
-                             unsigned long nr_segs, loff_t *off)
+static ssize_t fuse_dev_read(struct kiocb *iocb, const struct iovec *iov,
+                             unsigned long nr_segs, loff_t pos)
 {
        int err;
        struct fuse_req *req;
        struct fuse_in *in;
        struct fuse_copy_state cs;
        unsigned reqsize;
+       struct file *file = iocb->ki_filp;
        struct fuse_conn *fc = fuse_get_conn(file);
        if (!fc)
                return -EPERM;
@@ -610,7 +697,7 @@ static ssize_t fuse_dev_readv(struct file *file, const struct iovec *iov,
        spin_lock(&fc->lock);
        err = -EAGAIN;
        if ((file->f_flags & O_NONBLOCK) && fc->connected &&
-           list_empty(&fc->pending))
+           !request_pending(fc))
                goto err_unlock;
 
        request_wait(fc);
@@ -618,9 +705,15 @@ static ssize_t fuse_dev_readv(struct file *file, const struct iovec *iov,
        if (!fc->connected)
                goto err_unlock;
        err = -ERESTARTSYS;
-       if (list_empty(&fc->pending))
+       if (!request_pending(fc))
                goto err_unlock;
 
+       if (!list_empty(&fc->interrupts)) {
+               req = list_entry(fc->interrupts.next, struct fuse_req,
+                                intr_entry);
+               return fuse_read_interrupt(fc, req, iov, nr_segs);
+       }
+
        req = list_entry(fc->pending.next, struct fuse_req, list);
        req->state = FUSE_REQ_READING;
        list_move(&req->list, &fc->io);
@@ -645,10 +738,10 @@ static ssize_t fuse_dev_readv(struct file *file, const struct iovec *iov,
        fuse_copy_finish(&cs);
        spin_lock(&fc->lock);
        req->locked = 0;
-       if (!err && req->interrupted)
+       if (!err && req->aborted)
                err = -ENOENT;
        if (err) {
-               if (!req->interrupted)
+               if (!req->aborted)
                        req->out.h.error = -EIO;
                request_end(fc, req);
                return err;
@@ -658,6 +751,8 @@ static ssize_t fuse_dev_readv(struct file *file, const struct iovec *iov,
        else {
                req->state = FUSE_REQ_SENT;
                list_move_tail(&req->list, &fc->processing);
+               if (req->interrupted)
+                       queue_interrupt(fc, req);
                spin_unlock(&fc->lock);
        }
        return reqsize;
@@ -667,15 +762,6 @@ static ssize_t fuse_dev_readv(struct file *file, const struct iovec *iov,
        return err;
 }
 
-static ssize_t fuse_dev_read(struct file *file, char __user *buf,
-                            size_t nbytes, loff_t *off)
-{
-       struct iovec iov;
-       iov.iov_len = nbytes;
-       iov.iov_base = buf;
-       return fuse_dev_readv(file, &iov, 1, off);
-}
-
 /* Look up request on processing list by unique ID */
 static struct fuse_req *request_find(struct fuse_conn *fc, u64 unique)
 {
@@ -684,7 +770,7 @@ static struct fuse_req *request_find(struct fuse_conn *fc, u64 unique)
        list_for_each(entry, &fc->processing) {
                struct fuse_req *req;
                req = list_entry(entry, struct fuse_req, list);
-               if (req->in.h.unique == unique)
+               if (req->in.h.unique == unique || req->intr_unique == unique)
                        return req;
        }
        return NULL;
@@ -720,15 +806,15 @@ static int copy_out_args(struct fuse_copy_state *cs, struct fuse_out *out,
  * it from the list and copy the rest of the buffer to the request.
  * The request is finished by calling request_end()
  */
-static ssize_t fuse_dev_writev(struct file *file, const struct iovec *iov,
-                              unsigned long nr_segs, loff_t *off)
+static ssize_t fuse_dev_write(struct kiocb *iocb, const struct iovec *iov,
+                              unsigned long nr_segs, loff_t pos)
 {
        int err;
        unsigned nbytes = iov_length(iov, nr_segs);
        struct fuse_req *req;
        struct fuse_out_header oh;
        struct fuse_copy_state cs;
-       struct fuse_conn *fc = fuse_get_conn(file);
+       struct fuse_conn *fc = fuse_get_conn(iocb->ki_filp);
        if (!fc)
                return -EPERM;
 
@@ -750,17 +836,33 @@ static ssize_t fuse_dev_writev(struct file *file, const struct iovec *iov,
                goto err_unlock;
 
        req = request_find(fc, oh.unique);
-       err = -EINVAL;
        if (!req)
                goto err_unlock;
 
-       if (req->interrupted) {
+       if (req->aborted) {
                spin_unlock(&fc->lock);
                fuse_copy_finish(&cs);
                spin_lock(&fc->lock);
                request_end(fc, req);
                return -ENOENT;
        }
+       /* Is it an interrupt reply? */
+       if (req->intr_unique == oh.unique) {
+               err = -EINVAL;
+               if (nbytes != sizeof(struct fuse_out_header))
+                       goto err_unlock;
+
+               if (oh.error == -ENOSYS)
+                       fc->no_interrupt = 1;
+               else if (oh.error == -EAGAIN)
+                       queue_interrupt(fc, req);
+
+               spin_unlock(&fc->lock);
+               fuse_copy_finish(&cs);
+               return nbytes;
+       }
+
+       req->state = FUSE_REQ_WRITING;
        list_move(&req->list, &fc->io);
        req->out.h = oh;
        req->locked = 1;
@@ -773,9 +875,9 @@ static ssize_t fuse_dev_writev(struct file *file, const struct iovec *iov,
        spin_lock(&fc->lock);
        req->locked = 0;
        if (!err) {
-               if (req->interrupted)
+               if (req->aborted)
                        err = -ENOENT;
-       } else if (!req->interrupted)
+       } else if (!req->aborted)
                req->out.h.error = -EIO;
        request_end(fc, req);
 
@@ -788,15 +890,6 @@ static ssize_t fuse_dev_writev(struct file *file, const struct iovec *iov,
        return err;
 }
 
-static ssize_t fuse_dev_write(struct file *file, const char __user *buf,
-                             size_t nbytes, loff_t *off)
-{
-       struct iovec iov;
-       iov.iov_len = nbytes;
-       iov.iov_base = (char __user *) buf;
-       return fuse_dev_writev(file, &iov, 1, off);
-}
-
 static unsigned fuse_dev_poll(struct file *file, poll_table *wait)
 {
        unsigned mask = POLLOUT | POLLWRNORM;
@@ -809,7 +902,7 @@ static unsigned fuse_dev_poll(struct file *file, poll_table *wait)
        spin_lock(&fc->lock);
        if (!fc->connected)
                mask = POLLERR;
-       else if (!list_empty(&fc->pending))
+       else if (request_pending(fc))
                mask |= POLLIN | POLLRDNORM;
        spin_unlock(&fc->lock);
 
@@ -835,7 +928,7 @@ static void end_requests(struct fuse_conn *fc, struct list_head *head)
 /*
  * Abort requests under I/O
  *
- * The requests are set to interrupted and finished, and the request
+ * The requests are set to aborted and finished, and the request
  * waiter is woken up.  This will make request_wait_answer() wait
  * until the request is unlocked and then return.
  *
@@ -850,7 +943,7 @@ static void end_io_requests(struct fuse_conn *fc)
                        list_entry(fc->io.next, struct fuse_req, list);
                void (*end) (struct fuse_conn *, struct fuse_req *) = req->end;
 
-               req->interrupted = 1;
+               req->aborted = 1;
                req->out.h.error = -ECONNABORTED;
                req->state = FUSE_REQ_FINISHED;
                list_del_init(&req->list);
@@ -883,9 +976,8 @@ static void end_io_requests(struct fuse_conn *fc)
  * onto the pending list is prevented by req->connected being false.
  *
  * Progression of requests under I/O to the processing list is
- * prevented by the req->interrupted flag being true for these
- * requests.  For this reason requests on the io list must be aborted
- * first.
+ * prevented by the req->aborted flag being true for these requests.
+ * For this reason requests on the io list must be aborted first.
  */
 void fuse_abort_conn(struct fuse_conn *fc)
 {
@@ -932,10 +1024,10 @@ static int fuse_dev_fasync(int fd, struct file *file, int on)
 const struct file_operations fuse_dev_operations = {
        .owner          = THIS_MODULE,
        .llseek         = no_llseek,
-       .read           = fuse_dev_read,
-       .readv          = fuse_dev_readv,
-       .write          = fuse_dev_write,
-       .writev         = fuse_dev_writev,
+       .read           = do_sync_read,
+       .aio_read       = fuse_dev_read,
+       .write          = do_sync_write,
+       .aio_write      = fuse_dev_write,
        .poll           = fuse_dev_poll,
        .release        = fuse_dev_release,
        .fasync         = fuse_dev_fasync,