Loading fs/fuse/dir.c +91 −17 Original line number Diff line number Diff line Loading @@ -864,8 +864,11 @@ static void fuse_fillattr(struct inode *inode, struct fuse_attr *attr, struct fuse_conn *fc = get_fuse_conn(inode); /* see the comment in fuse_change_attributes() */ if (fc->writeback_cache && S_ISREG(inode->i_mode)) if (fc->writeback_cache && S_ISREG(inode->i_mode)) { attr->size = i_size_read(inode); attr->mtime = inode->i_mtime.tv_sec; attr->mtimensec = inode->i_mtime.tv_nsec; } stat->dev = inode->i_sb->s_dev; stat->ino = attr->ino; Loading Loading @@ -1515,12 +1518,16 @@ static long fuse_dir_compat_ioctl(struct file *file, unsigned int cmd, FUSE_IOCTL_COMPAT | FUSE_IOCTL_DIR); } static bool update_mtime(unsigned ivalid) static bool update_mtime(unsigned ivalid, bool trust_local_mtime) { /* Always update if mtime is explicitly set */ if (ivalid & ATTR_MTIME_SET) return true; /* Or if kernel i_mtime is the official one */ if (trust_local_mtime) return true; /* If it's an open(O_TRUNC) or an ftruncate(), don't update */ if ((ivalid & ATTR_SIZE) && (ivalid & (ATTR_OPEN | ATTR_FILE))) return false; Loading @@ -1529,7 +1536,8 @@ static bool update_mtime(unsigned ivalid) return true; } static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg) static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg, bool trust_local_mtime) { unsigned ivalid = iattr->ia_valid; Loading @@ -1548,11 +1556,11 @@ static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg) if (!(ivalid & ATTR_ATIME_SET)) arg->valid |= FATTR_ATIME_NOW; } if ((ivalid & ATTR_MTIME) && update_mtime(ivalid)) { if ((ivalid & ATTR_MTIME) && update_mtime(ivalid, trust_local_mtime)) { arg->valid |= FATTR_MTIME; arg->mtime = iattr->ia_mtime.tv_sec; arg->mtimensec = iattr->ia_mtime.tv_nsec; if (!(ivalid & ATTR_MTIME_SET)) if (!(ivalid & ATTR_MTIME_SET) && !trust_local_mtime) arg->valid |= FATTR_MTIME_NOW; } } Loading Loading @@ -1601,6 +1609,63 @@ void fuse_release_nowrite(struct inode *inode) spin_unlock(&fc->lock); } static void fuse_setattr_fill(struct fuse_conn *fc, struct fuse_req *req, struct inode *inode, struct fuse_setattr_in *inarg_p, struct fuse_attr_out *outarg_p) { req->in.h.opcode = FUSE_SETATTR; req->in.h.nodeid = get_node_id(inode); req->in.numargs = 1; req->in.args[0].size = sizeof(*inarg_p); req->in.args[0].value = inarg_p; req->out.numargs = 1; if (fc->minor < 9) req->out.args[0].size = FUSE_COMPAT_ATTR_OUT_SIZE; else req->out.args[0].size = sizeof(*outarg_p); req->out.args[0].value = outarg_p; } /* * Flush inode->i_mtime to the server */ int fuse_flush_mtime(struct file *file, bool nofail) { struct inode *inode = file->f_mapping->host; struct fuse_inode *fi = get_fuse_inode(inode); struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_req *req = NULL; struct fuse_setattr_in inarg; struct fuse_attr_out outarg; int err; if (nofail) { req = fuse_get_req_nofail_nopages(fc, file); } else { req = fuse_get_req_nopages(fc); if (IS_ERR(req)) return PTR_ERR(req); } memset(&inarg, 0, sizeof(inarg)); memset(&outarg, 0, sizeof(outarg)); inarg.valid |= FATTR_MTIME; inarg.mtime = inode->i_mtime.tv_sec; inarg.mtimensec = inode->i_mtime.tv_nsec; fuse_setattr_fill(fc, req, inode, &inarg, &outarg); fuse_request_send(fc, req); err = req->out.h.error; fuse_put_request(fc, req); if (!err) clear_bit(FUSE_I_MTIME_DIRTY, &fi->state); return err; } /* * Set attributes, and at the same time refresh them. * Loading @@ -1621,6 +1686,7 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr, bool is_wb = fc->writeback_cache; loff_t oldsize; int err; bool trust_local_mtime = is_wb && S_ISREG(inode->i_mode); if (!(fc->flags & FUSE_DEFAULT_PERMISSIONS)) attr->ia_valid |= ATTR_FORCE; Loading Loading @@ -1649,7 +1715,7 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr, memset(&inarg, 0, sizeof(inarg)); memset(&outarg, 0, sizeof(outarg)); iattr_to_fattr(attr, &inarg); iattr_to_fattr(attr, &inarg, trust_local_mtime); if (file) { struct fuse_file *ff = file->private_data; inarg.valid |= FATTR_FH; Loading @@ -1660,17 +1726,7 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr, inarg.valid |= FATTR_LOCKOWNER; inarg.lock_owner = fuse_lock_owner_id(fc, current->files); } req->in.h.opcode = FUSE_SETATTR; req->in.h.nodeid = get_node_id(inode); req->in.numargs = 1; req->in.args[0].size = sizeof(inarg); req->in.args[0].value = &inarg; req->out.numargs = 1; if (fc->minor < 9) req->out.args[0].size = FUSE_COMPAT_ATTR_OUT_SIZE; else req->out.args[0].size = sizeof(outarg); req->out.args[0].value = &outarg; fuse_setattr_fill(fc, req, inode, &inarg, &outarg); fuse_request_send(fc, req); err = req->out.h.error; fuse_put_request(fc, req); Loading @@ -1687,6 +1743,12 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr, } spin_lock(&fc->lock); /* the kernel maintains i_mtime locally */ if (trust_local_mtime && (attr->ia_valid & ATTR_MTIME)) { inode->i_mtime = attr->ia_mtime; clear_bit(FUSE_I_MTIME_DIRTY, &fi->state); } fuse_change_attributes_common(inode, &outarg.attr, attr_timeout(&outarg)); oldsize = inode->i_size; Loading Loading @@ -1917,6 +1979,17 @@ static int fuse_removexattr(struct dentry *entry, const char *name) return err; } static int fuse_update_time(struct inode *inode, struct timespec *now, int flags) { if (flags & S_MTIME) { inode->i_mtime = *now; set_bit(FUSE_I_MTIME_DIRTY, &get_fuse_inode(inode)->state); BUG_ON(!S_ISREG(inode->i_mode)); } return 0; } static const struct inode_operations fuse_dir_inode_operations = { .lookup = fuse_lookup, .mkdir = fuse_mkdir, Loading Loading @@ -1956,6 +2029,7 @@ static const struct inode_operations fuse_common_inode_operations = { .getxattr = fuse_getxattr, .listxattr = fuse_listxattr, .removexattr = fuse_removexattr, .update_time = fuse_update_time, }; static const struct inode_operations fuse_symlink_inode_operations = { Loading fs/fuse/file.c +26 −4 Original line number Diff line number Diff line Loading @@ -291,6 +291,9 @@ static int fuse_open(struct inode *inode, struct file *file) static int fuse_release(struct inode *inode, struct file *file) { if (test_bit(FUSE_I_MTIME_DIRTY, &get_fuse_inode(inode)->state)) fuse_flush_mtime(file, true); fuse_release_common(file, FUSE_RELEASE); /* return value is ignored by VFS */ Loading Loading @@ -457,6 +460,12 @@ int fuse_fsync_common(struct file *file, loff_t start, loff_t end, fuse_sync_writes(inode); if (test_bit(FUSE_I_MTIME_DIRTY, &get_fuse_inode(inode)->state)) { int err = fuse_flush_mtime(file, false); if (err) goto out; } req = fuse_get_req_nopages(fc); if (IS_ERR(req)) { err = PTR_ERR(req); Loading Loading @@ -981,16 +990,21 @@ static size_t fuse_send_write(struct fuse_req *req, struct fuse_io_priv *io, return req->misc.write.out.size; } void fuse_write_update_size(struct inode *inode, loff_t pos) bool fuse_write_update_size(struct inode *inode, loff_t pos) { struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_inode *fi = get_fuse_inode(inode); bool ret = false; spin_lock(&fc->lock); fi->attr_version = ++fc->attr_version; if (pos > inode->i_size) if (pos > inode->i_size) { i_size_write(inode, pos); ret = true; } spin_unlock(&fc->lock); return ret; } static size_t fuse_send_write_pages(struct fuse_req *req, struct file *file, Loading Loading @@ -2588,8 +2602,16 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset, goto out; /* we could have extended the file */ if (!(mode & FALLOC_FL_KEEP_SIZE)) fuse_write_update_size(inode, offset + length); if (!(mode & FALLOC_FL_KEEP_SIZE)) { bool changed = fuse_write_update_size(inode, offset + length); if (changed && fc->writeback_cache) { struct fuse_inode *fi = get_fuse_inode(inode); inode->i_mtime = current_fs_time(inode->i_sb); set_bit(FUSE_I_MTIME_DIRTY, &fi->state); } } if (mode & FALLOC_FL_PUNCH_HOLE) truncate_pagecache_range(inode, offset, offset + length - 1); Loading fs/fuse/fuse_i.h +5 −1 Original line number Diff line number Diff line Loading @@ -117,6 +117,8 @@ enum { FUSE_I_ADVISE_RDPLUS, /** An operation changing file size is in progress */ FUSE_I_SIZE_UNSTABLE, /** i_mtime has been updated locally; a flush to userspace needed */ FUSE_I_MTIME_DIRTY, }; struct fuse_conn; Loading Loading @@ -871,7 +873,9 @@ long fuse_ioctl_common(struct file *file, unsigned int cmd, unsigned fuse_file_poll(struct file *file, poll_table *wait); int fuse_dev_release(struct inode *inode, struct file *file); void fuse_write_update_size(struct inode *inode, loff_t pos); bool fuse_write_update_size(struct inode *inode, loff_t pos); int fuse_flush_mtime(struct file *file, bool nofail); int fuse_do_setattr(struct inode *inode, struct iattr *attr, struct file *file); Loading fs/fuse/inode.c +10 −3 Original line number Diff line number Diff line Loading @@ -170,8 +170,11 @@ void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr, inode->i_blocks = attr->blocks; inode->i_atime.tv_sec = attr->atime; inode->i_atime.tv_nsec = attr->atimensec; /* mtime from server may be stale due to local buffered write */ if (!fc->writeback_cache || !S_ISREG(inode->i_mode)) { inode->i_mtime.tv_sec = attr->mtime; inode->i_mtime.tv_nsec = attr->mtimensec; } inode->i_ctime.tv_sec = attr->ctime; inode->i_ctime.tv_nsec = attr->ctimensec; Loading Loading @@ -250,6 +253,8 @@ static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr) { inode->i_mode = attr->mode & S_IFMT; inode->i_size = attr->size; inode->i_mtime.tv_sec = attr->mtime; inode->i_mtime.tv_nsec = attr->mtimensec; if (S_ISREG(inode->i_mode)) { fuse_init_common(inode); fuse_init_file_inode(inode); Loading Loading @@ -296,7 +301,9 @@ struct inode *fuse_iget(struct super_block *sb, u64 nodeid, return NULL; if ((inode->i_state & I_NEW)) { inode->i_flags |= S_NOATIME|S_NOCMTIME; inode->i_flags |= S_NOATIME; if (!fc->writeback_cache || !S_ISREG(inode->i_mode)) inode->i_flags |= S_NOCMTIME; inode->i_generation = generation; inode->i_data.backing_dev_info = &fc->bdi; fuse_init_inode(inode, attr); Loading Loading
fs/fuse/dir.c +91 −17 Original line number Diff line number Diff line Loading @@ -864,8 +864,11 @@ static void fuse_fillattr(struct inode *inode, struct fuse_attr *attr, struct fuse_conn *fc = get_fuse_conn(inode); /* see the comment in fuse_change_attributes() */ if (fc->writeback_cache && S_ISREG(inode->i_mode)) if (fc->writeback_cache && S_ISREG(inode->i_mode)) { attr->size = i_size_read(inode); attr->mtime = inode->i_mtime.tv_sec; attr->mtimensec = inode->i_mtime.tv_nsec; } stat->dev = inode->i_sb->s_dev; stat->ino = attr->ino; Loading Loading @@ -1515,12 +1518,16 @@ static long fuse_dir_compat_ioctl(struct file *file, unsigned int cmd, FUSE_IOCTL_COMPAT | FUSE_IOCTL_DIR); } static bool update_mtime(unsigned ivalid) static bool update_mtime(unsigned ivalid, bool trust_local_mtime) { /* Always update if mtime is explicitly set */ if (ivalid & ATTR_MTIME_SET) return true; /* Or if kernel i_mtime is the official one */ if (trust_local_mtime) return true; /* If it's an open(O_TRUNC) or an ftruncate(), don't update */ if ((ivalid & ATTR_SIZE) && (ivalid & (ATTR_OPEN | ATTR_FILE))) return false; Loading @@ -1529,7 +1536,8 @@ static bool update_mtime(unsigned ivalid) return true; } static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg) static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg, bool trust_local_mtime) { unsigned ivalid = iattr->ia_valid; Loading @@ -1548,11 +1556,11 @@ static void iattr_to_fattr(struct iattr *iattr, struct fuse_setattr_in *arg) if (!(ivalid & ATTR_ATIME_SET)) arg->valid |= FATTR_ATIME_NOW; } if ((ivalid & ATTR_MTIME) && update_mtime(ivalid)) { if ((ivalid & ATTR_MTIME) && update_mtime(ivalid, trust_local_mtime)) { arg->valid |= FATTR_MTIME; arg->mtime = iattr->ia_mtime.tv_sec; arg->mtimensec = iattr->ia_mtime.tv_nsec; if (!(ivalid & ATTR_MTIME_SET)) if (!(ivalid & ATTR_MTIME_SET) && !trust_local_mtime) arg->valid |= FATTR_MTIME_NOW; } } Loading Loading @@ -1601,6 +1609,63 @@ void fuse_release_nowrite(struct inode *inode) spin_unlock(&fc->lock); } static void fuse_setattr_fill(struct fuse_conn *fc, struct fuse_req *req, struct inode *inode, struct fuse_setattr_in *inarg_p, struct fuse_attr_out *outarg_p) { req->in.h.opcode = FUSE_SETATTR; req->in.h.nodeid = get_node_id(inode); req->in.numargs = 1; req->in.args[0].size = sizeof(*inarg_p); req->in.args[0].value = inarg_p; req->out.numargs = 1; if (fc->minor < 9) req->out.args[0].size = FUSE_COMPAT_ATTR_OUT_SIZE; else req->out.args[0].size = sizeof(*outarg_p); req->out.args[0].value = outarg_p; } /* * Flush inode->i_mtime to the server */ int fuse_flush_mtime(struct file *file, bool nofail) { struct inode *inode = file->f_mapping->host; struct fuse_inode *fi = get_fuse_inode(inode); struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_req *req = NULL; struct fuse_setattr_in inarg; struct fuse_attr_out outarg; int err; if (nofail) { req = fuse_get_req_nofail_nopages(fc, file); } else { req = fuse_get_req_nopages(fc); if (IS_ERR(req)) return PTR_ERR(req); } memset(&inarg, 0, sizeof(inarg)); memset(&outarg, 0, sizeof(outarg)); inarg.valid |= FATTR_MTIME; inarg.mtime = inode->i_mtime.tv_sec; inarg.mtimensec = inode->i_mtime.tv_nsec; fuse_setattr_fill(fc, req, inode, &inarg, &outarg); fuse_request_send(fc, req); err = req->out.h.error; fuse_put_request(fc, req); if (!err) clear_bit(FUSE_I_MTIME_DIRTY, &fi->state); return err; } /* * Set attributes, and at the same time refresh them. * Loading @@ -1621,6 +1686,7 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr, bool is_wb = fc->writeback_cache; loff_t oldsize; int err; bool trust_local_mtime = is_wb && S_ISREG(inode->i_mode); if (!(fc->flags & FUSE_DEFAULT_PERMISSIONS)) attr->ia_valid |= ATTR_FORCE; Loading Loading @@ -1649,7 +1715,7 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr, memset(&inarg, 0, sizeof(inarg)); memset(&outarg, 0, sizeof(outarg)); iattr_to_fattr(attr, &inarg); iattr_to_fattr(attr, &inarg, trust_local_mtime); if (file) { struct fuse_file *ff = file->private_data; inarg.valid |= FATTR_FH; Loading @@ -1660,17 +1726,7 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr, inarg.valid |= FATTR_LOCKOWNER; inarg.lock_owner = fuse_lock_owner_id(fc, current->files); } req->in.h.opcode = FUSE_SETATTR; req->in.h.nodeid = get_node_id(inode); req->in.numargs = 1; req->in.args[0].size = sizeof(inarg); req->in.args[0].value = &inarg; req->out.numargs = 1; if (fc->minor < 9) req->out.args[0].size = FUSE_COMPAT_ATTR_OUT_SIZE; else req->out.args[0].size = sizeof(outarg); req->out.args[0].value = &outarg; fuse_setattr_fill(fc, req, inode, &inarg, &outarg); fuse_request_send(fc, req); err = req->out.h.error; fuse_put_request(fc, req); Loading @@ -1687,6 +1743,12 @@ int fuse_do_setattr(struct inode *inode, struct iattr *attr, } spin_lock(&fc->lock); /* the kernel maintains i_mtime locally */ if (trust_local_mtime && (attr->ia_valid & ATTR_MTIME)) { inode->i_mtime = attr->ia_mtime; clear_bit(FUSE_I_MTIME_DIRTY, &fi->state); } fuse_change_attributes_common(inode, &outarg.attr, attr_timeout(&outarg)); oldsize = inode->i_size; Loading Loading @@ -1917,6 +1979,17 @@ static int fuse_removexattr(struct dentry *entry, const char *name) return err; } static int fuse_update_time(struct inode *inode, struct timespec *now, int flags) { if (flags & S_MTIME) { inode->i_mtime = *now; set_bit(FUSE_I_MTIME_DIRTY, &get_fuse_inode(inode)->state); BUG_ON(!S_ISREG(inode->i_mode)); } return 0; } static const struct inode_operations fuse_dir_inode_operations = { .lookup = fuse_lookup, .mkdir = fuse_mkdir, Loading Loading @@ -1956,6 +2029,7 @@ static const struct inode_operations fuse_common_inode_operations = { .getxattr = fuse_getxattr, .listxattr = fuse_listxattr, .removexattr = fuse_removexattr, .update_time = fuse_update_time, }; static const struct inode_operations fuse_symlink_inode_operations = { Loading
fs/fuse/file.c +26 −4 Original line number Diff line number Diff line Loading @@ -291,6 +291,9 @@ static int fuse_open(struct inode *inode, struct file *file) static int fuse_release(struct inode *inode, struct file *file) { if (test_bit(FUSE_I_MTIME_DIRTY, &get_fuse_inode(inode)->state)) fuse_flush_mtime(file, true); fuse_release_common(file, FUSE_RELEASE); /* return value is ignored by VFS */ Loading Loading @@ -457,6 +460,12 @@ int fuse_fsync_common(struct file *file, loff_t start, loff_t end, fuse_sync_writes(inode); if (test_bit(FUSE_I_MTIME_DIRTY, &get_fuse_inode(inode)->state)) { int err = fuse_flush_mtime(file, false); if (err) goto out; } req = fuse_get_req_nopages(fc); if (IS_ERR(req)) { err = PTR_ERR(req); Loading Loading @@ -981,16 +990,21 @@ static size_t fuse_send_write(struct fuse_req *req, struct fuse_io_priv *io, return req->misc.write.out.size; } void fuse_write_update_size(struct inode *inode, loff_t pos) bool fuse_write_update_size(struct inode *inode, loff_t pos) { struct fuse_conn *fc = get_fuse_conn(inode); struct fuse_inode *fi = get_fuse_inode(inode); bool ret = false; spin_lock(&fc->lock); fi->attr_version = ++fc->attr_version; if (pos > inode->i_size) if (pos > inode->i_size) { i_size_write(inode, pos); ret = true; } spin_unlock(&fc->lock); return ret; } static size_t fuse_send_write_pages(struct fuse_req *req, struct file *file, Loading Loading @@ -2588,8 +2602,16 @@ static long fuse_file_fallocate(struct file *file, int mode, loff_t offset, goto out; /* we could have extended the file */ if (!(mode & FALLOC_FL_KEEP_SIZE)) fuse_write_update_size(inode, offset + length); if (!(mode & FALLOC_FL_KEEP_SIZE)) { bool changed = fuse_write_update_size(inode, offset + length); if (changed && fc->writeback_cache) { struct fuse_inode *fi = get_fuse_inode(inode); inode->i_mtime = current_fs_time(inode->i_sb); set_bit(FUSE_I_MTIME_DIRTY, &fi->state); } } if (mode & FALLOC_FL_PUNCH_HOLE) truncate_pagecache_range(inode, offset, offset + length - 1); Loading
fs/fuse/fuse_i.h +5 −1 Original line number Diff line number Diff line Loading @@ -117,6 +117,8 @@ enum { FUSE_I_ADVISE_RDPLUS, /** An operation changing file size is in progress */ FUSE_I_SIZE_UNSTABLE, /** i_mtime has been updated locally; a flush to userspace needed */ FUSE_I_MTIME_DIRTY, }; struct fuse_conn; Loading Loading @@ -871,7 +873,9 @@ long fuse_ioctl_common(struct file *file, unsigned int cmd, unsigned fuse_file_poll(struct file *file, poll_table *wait); int fuse_dev_release(struct inode *inode, struct file *file); void fuse_write_update_size(struct inode *inode, loff_t pos); bool fuse_write_update_size(struct inode *inode, loff_t pos); int fuse_flush_mtime(struct file *file, bool nofail); int fuse_do_setattr(struct inode *inode, struct iattr *attr, struct file *file); Loading
fs/fuse/inode.c +10 −3 Original line number Diff line number Diff line Loading @@ -170,8 +170,11 @@ void fuse_change_attributes_common(struct inode *inode, struct fuse_attr *attr, inode->i_blocks = attr->blocks; inode->i_atime.tv_sec = attr->atime; inode->i_atime.tv_nsec = attr->atimensec; /* mtime from server may be stale due to local buffered write */ if (!fc->writeback_cache || !S_ISREG(inode->i_mode)) { inode->i_mtime.tv_sec = attr->mtime; inode->i_mtime.tv_nsec = attr->mtimensec; } inode->i_ctime.tv_sec = attr->ctime; inode->i_ctime.tv_nsec = attr->ctimensec; Loading Loading @@ -250,6 +253,8 @@ static void fuse_init_inode(struct inode *inode, struct fuse_attr *attr) { inode->i_mode = attr->mode & S_IFMT; inode->i_size = attr->size; inode->i_mtime.tv_sec = attr->mtime; inode->i_mtime.tv_nsec = attr->mtimensec; if (S_ISREG(inode->i_mode)) { fuse_init_common(inode); fuse_init_file_inode(inode); Loading Loading @@ -296,7 +301,9 @@ struct inode *fuse_iget(struct super_block *sb, u64 nodeid, return NULL; if ((inode->i_state & I_NEW)) { inode->i_flags |= S_NOATIME|S_NOCMTIME; inode->i_flags |= S_NOATIME; if (!fc->writeback_cache || !S_ISREG(inode->i_mode)) inode->i_flags |= S_NOCMTIME; inode->i_generation = generation; inode->i_data.backing_dev_info = &fc->bdi; fuse_init_inode(inode, attr); Loading