Merge remote-tracking branch 'linus/master' into testing
authorSage Weil <sage@inktank.com>
Thu, 15 Aug 2013 18:11:45 +0000 (11:11 -0700)
committerSage Weil <sage@inktank.com>
Thu, 15 Aug 2013 18:11:45 +0000 (11:11 -0700)
1  2 
fs/ceph/addr.c
fs/ceph/dir.c
fs/ceph/file.c
fs/ceph/inode.c
fs/ceph/mds_client.c

diff --combined fs/ceph/addr.c
@@@ -143,12 -143,17 +143,13 @@@ static int ceph_set_page_dirty(struct p
   * dirty page counters appropriately.  Only called if there is private
   * data on the page.
   */
- static void ceph_invalidatepage(struct page *page, unsigned long offset)
+ static void ceph_invalidatepage(struct page *page, unsigned int offset,
+                               unsigned int length)
  {
        struct inode *inode;
        struct ceph_inode_info *ci;
        struct ceph_snap_context *snapc = page_snap_context(page);
  
 -      BUG_ON(!PageLocked(page));
 -      BUG_ON(!PagePrivate(page));
 -      BUG_ON(!page->mapping);
 -
        inode = page->mapping->host;
  
        /*
        if (!PageDirty(page))
                pr_err("%p invalidatepage %p page not dirty\n", inode, page);
  
-       if (offset == 0)
+       if (offset == 0 && length == PAGE_CACHE_SIZE)
                ClearPageChecked(page);
  
        ci = ceph_inode(inode);
-       if (offset == 0) {
-               dout("%p invalidatepage %p idx %lu full dirty page %lu\n",
-                    inode, page, page->index, offset);
+       if (offset == 0 && length == PAGE_CACHE_SIZE) {
+               dout("%p invalidatepage %p idx %lu full dirty page\n",
+                    inode, page, page->index);
                ceph_put_wrbuffer_cap_refs(ci, 1, snapc);
                ceph_put_snap_context(snapc);
                page->private = 0;
                ClearPagePrivate(page);
        } else {
-               dout("%p invalidatepage %p idx %lu partial dirty page\n",
-                    inode, page, page->index);
+               dout("%p invalidatepage %p idx %lu partial dirty page %u(%u)\n",
+                    inode, page, page->index, offset, length);
        }
  }
  
diff --combined fs/ceph/dir.c
@@@ -111,11 -111,10 +111,10 @@@ static unsigned fpos_off(loff_t p
   * defined IFF we hold CEPH_CAP_FILE_SHARED (which will be revoked by
   * the MDS if/when the directory is modified).
   */
- static int __dcache_readdir(struct file *filp,
-                           void *dirent, filldir_t filldir)
+ static int __dcache_readdir(struct file *file, struct dir_context *ctx)
  {
-       struct ceph_file_info *fi = filp->private_data;
-       struct dentry *parent = filp->f_dentry;
+       struct ceph_file_info *fi = file->private_data;
+       struct dentry *parent = file->f_dentry;
        struct inode *dir = parent->d_inode;
        struct list_head *p;
        struct dentry *dentry, *last;
        last = fi->dentry;
        fi->dentry = NULL;
  
-       dout("__dcache_readdir %p at %llu (last %p)\n", dir, filp->f_pos,
+       dout("__dcache_readdir %p at %llu (last %p)\n", dir, ctx->pos,
             last);
  
        spin_lock(&parent->d_lock);
  
        /* start at beginning? */
-       if (filp->f_pos == 2 || last == NULL ||
-           filp->f_pos < ceph_dentry(last)->offset) {
+       if (ctx->pos == 2 || last == NULL ||
+           ctx->pos < ceph_dentry(last)->offset) {
                if (list_empty(&parent->d_subdirs))
                        goto out_unlock;
                p = parent->d_subdirs.prev;
@@@ -157,11 -156,11 +156,11 @@@ more
                if (!d_unhashed(dentry) && dentry->d_inode &&
                    ceph_snap(dentry->d_inode) != CEPH_SNAPDIR &&
                    ceph_ino(dentry->d_inode) != CEPH_INO_CEPH &&
-                   filp->f_pos <= di->offset)
+                   ctx->pos <= di->offset)
                        break;
                dout(" skipping %p %.*s at %llu (%llu)%s%s\n", dentry,
                     dentry->d_name.len, dentry->d_name.name, di->offset,
-                    filp->f_pos, d_unhashed(dentry) ? " unhashed" : "",
+                    ctx->pos, d_unhashed(dentry) ? " unhashed" : "",
                     !dentry->d_inode ? " null" : "");
                spin_unlock(&dentry->d_lock);
                p = p->prev;
        spin_unlock(&dentry->d_lock);
        spin_unlock(&parent->d_lock);
  
-       dout(" %llu (%llu) dentry %p %.*s %p\n", di->offset, filp->f_pos,
+       dout(" %llu (%llu) dentry %p %.*s %p\n", di->offset, ctx->pos,
             dentry, dentry->d_name.len, dentry->d_name.name, dentry->d_inode);
-       filp->f_pos = di->offset;
-       err = filldir(dirent, dentry->d_name.name,
-                     dentry->d_name.len, di->offset,
+       ctx->pos = di->offset;
+       if (!dir_emit(ctx, dentry->d_name.name,
+                     dentry->d_name.len,
                      ceph_translate_ino(dentry->d_sb, dentry->d_inode->i_ino),
-                     dentry->d_inode->i_mode >> 12);
-       if (last) {
-               if (err < 0) {
+                     dentry->d_inode->i_mode >> 12)) {
+               if (last) {
                        /* remember our position */
                        fi->dentry = last;
                        fi->next_offset = di->offset;
-               } else {
-                       dput(last);
                }
+               dput(dentry);
+               return 0;
        }
-       last = dentry;
  
-       if (err < 0)
-               goto out;
+       if (last)
+               dput(last);
+       last = dentry;
  
-       filp->f_pos++;
+       ctx->pos++;
  
        /* make sure a dentry wasn't dropped while we didn't have parent lock */
        if (!ceph_dir_is_complete(dir)) {
@@@ -235,59 -232,59 +232,59 @@@ static int note_last_dentry(struct ceph
        return 0;
  }
  
- static int ceph_readdir(struct file *filp, void *dirent, filldir_t filldir)
+ static int ceph_readdir(struct file *file, struct dir_context *ctx)
  {
-       struct ceph_file_info *fi = filp->private_data;
-       struct inode *inode = file_inode(filp);
+       struct ceph_file_info *fi = file->private_data;
+       struct inode *inode = file_inode(file);
        struct ceph_inode_info *ci = ceph_inode(inode);
        struct ceph_fs_client *fsc = ceph_inode_to_client(inode);
        struct ceph_mds_client *mdsc = fsc->mdsc;
-       unsigned frag = fpos_frag(filp->f_pos);
-       int off = fpos_off(filp->f_pos);
+       unsigned frag = fpos_frag(ctx->pos);
+       int off = fpos_off(ctx->pos);
        int err;
        u32 ftype;
        struct ceph_mds_reply_info_parsed *rinfo;
        const int max_entries = fsc->mount_options->max_readdir;
        const int max_bytes = fsc->mount_options->max_readdir_bytes;
  
-       dout("readdir %p filp %p frag %u off %u\n", inode, filp, frag, off);
+       dout("readdir %p file %p frag %u off %u\n", inode, file, frag, off);
        if (fi->flags & CEPH_F_ATEND)
                return 0;
  
        /* always start with . and .. */
-       if (filp->f_pos == 0) {
+       if (ctx->pos == 0) {
                /* note dir version at start of readdir so we can tell
                 * if any dentries get dropped */
                fi->dir_release_count = atomic_read(&ci->i_release_count);
  
                dout("readdir off 0 -> '.'\n");
-               if (filldir(dirent, ".", 1, ceph_make_fpos(0, 0),
+               if (!dir_emit(ctx, ".", 1, 
                            ceph_translate_ino(inode->i_sb, inode->i_ino),
-                           inode->i_mode >> 12) < 0)
+                           inode->i_mode >> 12))
                        return 0;
-               filp->f_pos = 1;
+               ctx->pos = 1;
                off = 1;
        }
-       if (filp->f_pos == 1) {
-               ino_t ino = parent_ino(filp->f_dentry);
+       if (ctx->pos == 1) {
+               ino_t ino = parent_ino(file->f_dentry);
                dout("readdir off 1 -> '..'\n");
-               if (filldir(dirent, "..", 2, ceph_make_fpos(0, 1),
+               if (!dir_emit(ctx, "..", 2,
                            ceph_translate_ino(inode->i_sb, ino),
-                           inode->i_mode >> 12) < 0)
+                           inode->i_mode >> 12))
                        return 0;
-               filp->f_pos = 2;
+               ctx->pos = 2;
                off = 2;
        }
  
        /* can we use the dcache? */
        spin_lock(&ci->i_ceph_lock);
-       if ((filp->f_pos == 2 || fi->dentry) &&
+       if ((ctx->pos == 2 || fi->dentry) &&
            !ceph_test_mount_opt(fsc, NOASYNCREADDIR) &&
            ceph_snap(inode) != CEPH_SNAPDIR &&
            __ceph_dir_is_complete(ci) &&
            __ceph_caps_issued_mask(ci, CEPH_CAP_FILE_SHARED, 1)) {
                spin_unlock(&ci->i_ceph_lock);
-               err = __dcache_readdir(filp, dirent, filldir);
+               err = __dcache_readdir(file, ctx);
                if (err != -EAGAIN)
                        return err;
        } else {
@@@ -327,7 -324,7 +324,7 @@@ more
                        return PTR_ERR(req);
                req->r_inode = inode;
                ihold(inode);
-               req->r_dentry = dget(filp->f_dentry);
+               req->r_dentry = dget(file->f_dentry);
                /* hints to request -> mds selection code */
                req->r_direct_mode = USE_AUTH_MDS;
                req->r_direct_hash = ceph_frag_value(frag);
        rinfo = &fi->last_readdir->r_reply_info;
        dout("readdir frag %x num %d off %d chunkoff %d\n", frag,
             rinfo->dir_nr, off, fi->offset);
+       ctx->pos = ceph_make_fpos(frag, off);
        while (off >= fi->offset && off - fi->offset < rinfo->dir_nr) {
-               u64 pos = ceph_make_fpos(frag, off);
                struct ceph_mds_reply_inode *in =
                        rinfo->dir_in[off - fi->offset].in;
                struct ceph_vino vino;
                ino_t ino;
  
                dout("readdir off %d (%d/%d) -> %lld '%.*s' %p\n",
-                    off, off - fi->offset, rinfo->dir_nr, pos,
+                    off, off - fi->offset, rinfo->dir_nr, ctx->pos,
                     rinfo->dir_dname_len[off - fi->offset],
                     rinfo->dir_dname[off - fi->offset], in);
                BUG_ON(!in);
                vino.ino = le64_to_cpu(in->ino);
                vino.snap = le64_to_cpu(in->snapid);
                ino = ceph_vino_to_ino(vino);
-               if (filldir(dirent,
+               if (!dir_emit(ctx,
                            rinfo->dir_dname[off - fi->offset],
                            rinfo->dir_dname_len[off - fi->offset],
-                           pos,
-                           ceph_translate_ino(inode->i_sb, ino), ftype) < 0) {
+                           ceph_translate_ino(inode->i_sb, ino), ftype)) {
                        dout("filldir stopping us...\n");
                        return 0;
                }
                off++;
-               filp->f_pos = pos + 1;
+               ctx->pos++;
        }
  
        if (fi->last_name) {
        if (!ceph_frag_is_rightmost(frag)) {
                frag = ceph_frag_next(frag);
                off = 0;
-               filp->f_pos = ceph_make_fpos(frag, off);
+               ctx->pos = ceph_make_fpos(frag, off);
                dout("readdir next frag is %x\n", frag);
                goto more;
        }
        if (atomic_read(&ci->i_release_count) == fi->dir_release_count) {
                dout(" marking %p complete\n", inode);
                __ceph_dir_set_complete(ci, fi->dir_release_count);
-               ci->i_max_offset = filp->f_pos;
+               ci->i_max_offset = ctx->pos;
        }
        spin_unlock(&ci->i_ceph_lock);
  
-       dout("readdir %p filp %p done.\n", inode, filp);
+       dout("readdir %p file %p done.\n", inode, file);
        return 0;
  }
  
@@@ -796,8 -793,6 +793,8 @@@ static int ceph_link(struct dentry *old
        req->r_locked_dir = dir;
        req->r_dentry_drop = CEPH_CAP_FILE_SHARED;
        req->r_dentry_unless = CEPH_CAP_FILE_EXCL;
 +      /* release LINK_SHARED on source inode (mds will lock it) */
 +      req->r_old_inode_drop = CEPH_CAP_LINK_SHARED;
        err = ceph_mdsc_do_request(mdsc, dir, req);
        if (err) {
                d_drop(dentry);
@@@ -1270,7 -1265,7 +1267,7 @@@ unsigned ceph_dentry_hash(struct inode 
  
  const struct file_operations ceph_dir_fops = {
        .read = ceph_read_dir,
-       .readdir = ceph_readdir,
+       .iterate = ceph_readdir,
        .llseek = ceph_dir_llseek,
        .open = ceph_open,
        .release = ceph_release,
diff --combined fs/ceph/file.c
@@@ -313,9 -313,9 +313,9 @@@ static int striped_read(struct inode *i
  {
        struct ceph_fs_client *fsc = ceph_inode_to_client(inode);
        struct ceph_inode_info *ci = ceph_inode(inode);
 -      u64 pos, this_len;
 +      u64 pos, this_len, left;
        int io_align, page_align;
 -      int left, pages_left;
 +      int pages_left;
        int read;
        struct page **page_pos;
        int ret;
@@@ -346,7 -346,7 +346,7 @@@ more
                ret = 0;
        hit_stripe = this_len < left;
        was_short = ret >= 0 && ret < this_len;
 -      dout("striped_read %llu~%u (read %u) got %d%s%s\n", pos, left, read,
 +      dout("striped_read %llu~%llu (read %u) got %d%s%s\n", pos, left, read,
             ret, hit_stripe ? " HITSTRIPE" : "", was_short ? " SHORT" : "");
  
        if (ret > 0) {
                        if (pos + left > inode->i_size)
                                left = inode->i_size - pos;
  
 -                      dout("zero tail %d\n", left);
 +                      dout("zero tail %llu\n", left);
                        ceph_zero_page_vector_range(page_align + read, left,
                                                    pages);
                        read += left;
@@@ -659,6 -659,7 +659,6 @@@ again
  
        if ((got & (CEPH_CAP_FILE_CACHE|CEPH_CAP_FILE_LAZYIO)) == 0 ||
            (iocb->ki_filp->f_flags & O_DIRECT) ||
 -          (inode->i_sb->s_flags & MS_SYNCHRONOUS) ||
            (fi->flags & CEPH_F_SYNC))
                /* hmm, this isn't really async... */
                ret = ceph_sync_read(filp, base, len, ppos, &checkeof);
@@@ -710,11 -711,13 +710,11 @@@ static ssize_t ceph_aio_write(struct ki
                &ceph_sb_to_client(inode->i_sb)->client->osdc;
        ssize_t count, written = 0;
        int err, want, got;
 -      bool hold_mutex;
  
        if (ceph_snap(inode) != CEPH_NOSNAP)
                return -EROFS;
  
        mutex_lock(&inode->i_mutex);
 -      hold_mutex = true;
  
        err = generic_segment_checks(iov, &nr_segs, &count, VERIFY_READ);
        if (err)
@@@ -760,24 -763,18 +760,24 @@@ retry_snap
  
        if ((got & (CEPH_CAP_FILE_BUFFER|CEPH_CAP_FILE_LAZYIO)) == 0 ||
            (iocb->ki_filp->f_flags & O_DIRECT) ||
 -          (inode->i_sb->s_flags & MS_SYNCHRONOUS) ||
            (fi->flags & CEPH_F_SYNC)) {
                mutex_unlock(&inode->i_mutex);
                written = ceph_sync_write(file, iov->iov_base, count,
                                          pos, &iocb->ki_pos);
 +              if (written == -EOLDSNAPC) {
 +                      dout("aio_write %p %llx.%llx %llu~%u"
 +                              "got EOLDSNAPC, retrying\n",
 +                              inode, ceph_vinop(inode),
 +                              pos, (unsigned)iov->iov_len);
 +                      mutex_lock(&inode->i_mutex);
 +                      goto retry_snap;
 +              }
        } else {
                written = generic_file_buffered_write(iocb, iov, nr_segs,
                                                      pos, &iocb->ki_pos,
                                                      count, 0);
                mutex_unlock(&inode->i_mutex);
        }
 -      hold_mutex = false;
  
        if (written >= 0) {
                int dirty;
                        written = err;
        }
  
 -      if (written == -EOLDSNAPC) {
 -              dout("aio_write %p %llx.%llx %llu~%u got EOLDSNAPC, retrying\n",
 -                   inode, ceph_vinop(inode), pos, (unsigned)iov->iov_len);
 -              mutex_lock(&inode->i_mutex);
 -              hold_mutex = true;
 -              goto retry_snap;
 -      }
 +      goto out_unlocked;
 +
  out:
 -      if (hold_mutex)
 -              mutex_unlock(&inode->i_mutex);
 +      mutex_unlock(&inode->i_mutex);
 +out_unlocked:
        current->backing_dev_info = NULL;
 -
        return written ? written : err;
  }
  
@@@ -861,16 -864,7 +861,7 @@@ static loff_t ceph_llseek(struct file *
                break;
        }
  
-       if (offset < 0 || offset > inode->i_sb->s_maxbytes) {
-               offset = -EINVAL;
-               goto out;
-       }
-       /* Special lock needed here? */
-       if (offset != file->f_pos) {
-               file->f_pos = offset;
-               file->f_version = 0;
-       }
+       offset = vfs_setpos(file, offset, inode->i_sb->s_maxbytes);
  
  out:
        mutex_unlock(&inode->i_mutex);
diff --combined fs/ceph/inode.c
@@@ -61,14 -61,6 +61,14 @@@ struct inode *ceph_get_inode(struct sup
        return inode;
  }
  
 +struct inode *ceph_lookup_inode(struct super_block *sb, struct ceph_vino vino)
 +{
 +      struct inode *inode;
 +      ino_t t = ceph_vino_to_ino(vino);
 +      inode = ilookup5_nowait(sb, t, ceph_ino_compare, &vino);
 +      return inode;
 +}
 +
  /*
   * get/constuct snapdir inode for a given directory
   */
@@@ -911,8 -903,8 +911,8 @@@ static struct dentry *splice_dentry(str
        } else if (realdn) {
                dout("dn %p (%d) spliced with %p (%d) "
                     "inode %p ino %llx.%llx\n",
-                    dn, dn->d_count,
-                    realdn, realdn->d_count,
+                    dn, d_count(dn),
+                    realdn, d_count(realdn),
                     realdn->d_inode, ceph_vinop(realdn->d_inode));
                dput(dn);
                dn = realdn;
@@@ -1473,14 -1465,7 +1473,14 @@@ static void ceph_vmtruncate_work(struc
        struct inode *inode = &ci->vfs_inode;
  
        dout("vmtruncate_work %p\n", inode);
 -      mutex_lock(&inode->i_mutex);
 +      if (!mutex_trylock(&inode->i_mutex)) {
 +              /*
 +               * the i_mutex can be hold by a writer who is waiting for
 +               * caps. wake up waiters, they will do pending vmtruncate.
 +               */
 +              wake_up_all(&ci->i_cap_wq);
 +              mutex_lock(&inode->i_mutex);
 +      }
        __ceph_do_pending_vmtruncate(inode);
        mutex_unlock(&inode->i_mutex);
        iput(inode);
diff --combined fs/ceph/mds_client.c
@@@ -414,9 -414,6 +414,9 @@@ static struct ceph_mds_session *registe
  {
        struct ceph_mds_session *s;
  
 +      if (mds >= mdsc->mdsmap->m_max_mds)
 +              return ERR_PTR(-EINVAL);
 +
        s = kzalloc(sizeof(*s), GFP_NOFS);
        if (!s)
                return ERR_PTR(-ENOMEM);
@@@ -1031,37 -1028,6 +1031,37 @@@ static void remove_session_caps(struct 
  {
        dout("remove_session_caps on %p\n", session);
        iterate_session_caps(session, remove_session_caps_cb, NULL);
 +
 +      spin_lock(&session->s_cap_lock);
 +      if (session->s_nr_caps > 0) {
 +              struct super_block *sb = session->s_mdsc->fsc->sb;
 +              struct inode *inode;
 +              struct ceph_cap *cap, *prev = NULL;
 +              struct ceph_vino vino;
 +              /*
 +               * iterate_session_caps() skips inodes that are being
 +               * deleted, we need to wait until deletions are complete.
 +               * __wait_on_freeing_inode() is designed for the job,
 +               * but it is not exported, so use lookup inode function
 +               * to access it.
 +               */
 +              while (!list_empty(&session->s_caps)) {
 +                      cap = list_entry(session->s_caps.next,
 +                                       struct ceph_cap, session_caps);
 +                      if (cap == prev)
 +                              break;
 +                      prev = cap;
 +                      vino = cap->ci->i_vino;
 +                      spin_unlock(&session->s_cap_lock);
 +
 +                      inode = ceph_lookup_inode(sb, vino);
 +                      iput(inode);
 +
 +                      spin_lock(&session->s_cap_lock);
 +              }
 +      }
 +      spin_unlock(&session->s_cap_lock);
 +
        BUG_ON(session->s_nr_caps > 0);
        BUG_ON(!list_empty(&session->s_cap_flushing));
        cleanup_cap_releases(session);
@@@ -1588,7 -1554,7 +1588,7 @@@ retry
        *base = ceph_ino(temp->d_inode);
        *plen = len;
        dout("build_path on %p %d built %llx '%.*s'\n",
-            dentry, dentry->d_count, *base, len, path);
+            dentry, d_count(dentry), *base, len, path);
        return path;
  }
  
@@@ -2517,20 -2483,20 +2517,20 @@@ static int encode_caps_cb(struct inode 
                struct ceph_filelock *flocks;
  
  encode_again:
-               lock_flocks();
+               spin_lock(&inode->i_lock);
                ceph_count_locks(inode, &num_fcntl_locks, &num_flock_locks);
-               unlock_flocks();
+               spin_unlock(&inode->i_lock);
                flocks = kmalloc((num_fcntl_locks+num_flock_locks) *
                                 sizeof(struct ceph_filelock), GFP_NOFS);
                if (!flocks) {
                        err = -ENOMEM;
                        goto out_free;
                }
-               lock_flocks();
+               spin_lock(&inode->i_lock);
                err = ceph_encode_locks_to_buffer(inode, flocks,
                                                  num_fcntl_locks,
                                                  num_flock_locks);
-               unlock_flocks();
+               spin_unlock(&inode->i_lock);
                if (err) {
                        kfree(flocks);
                        if (err == -ENOSPC)