diff options
Diffstat (limited to 'fs')
36 files changed, 1224 insertions, 418 deletions
diff --git a/fs/9p/vfs_dentry.c b/fs/9p/vfs_dentry.c index f039b104a98e..b03dd23feda8 100644 --- a/fs/9p/vfs_dentry.c +++ b/fs/9p/vfs_dentry.c | |||
| @@ -43,23 +43,6 @@ | |||
| 43 | #include "fid.h" | 43 | #include "fid.h" |
| 44 | 44 | ||
| 45 | /** | 45 | /** |
| 46 | * v9fs_dentry_delete - called when dentry refcount equals 0 | ||
| 47 | * @dentry: dentry in question | ||
| 48 | * | ||
| 49 | * By returning 1 here we should remove cacheing of unused | ||
| 50 | * dentry components. | ||
| 51 | * | ||
| 52 | */ | ||
| 53 | |||
| 54 | static int v9fs_dentry_delete(const struct dentry *dentry) | ||
| 55 | { | ||
| 56 | p9_debug(P9_DEBUG_VFS, " dentry: %s (%p)\n", | ||
| 57 | dentry->d_name.name, dentry); | ||
| 58 | |||
| 59 | return 1; | ||
| 60 | } | ||
| 61 | |||
| 62 | /** | ||
| 63 | * v9fs_cached_dentry_delete - called when dentry refcount equals 0 | 46 | * v9fs_cached_dentry_delete - called when dentry refcount equals 0 |
| 64 | * @dentry: dentry in question | 47 | * @dentry: dentry in question |
| 65 | * | 48 | * |
| @@ -134,6 +117,6 @@ const struct dentry_operations v9fs_cached_dentry_operations = { | |||
| 134 | }; | 117 | }; |
| 135 | 118 | ||
| 136 | const struct dentry_operations v9fs_dentry_operations = { | 119 | const struct dentry_operations v9fs_dentry_operations = { |
| 137 | .d_delete = v9fs_dentry_delete, | 120 | .d_delete = always_delete_dentry, |
| 138 | .d_release = v9fs_dentry_release, | 121 | .d_release = v9fs_dentry_release, |
| 139 | }; | 122 | }; |
| @@ -601,7 +601,7 @@ EXPORT_SYMBOL(bio_get_nr_vecs); | |||
| 601 | 601 | ||
| 602 | static int __bio_add_page(struct request_queue *q, struct bio *bio, struct page | 602 | static int __bio_add_page(struct request_queue *q, struct bio *bio, struct page |
| 603 | *page, unsigned int len, unsigned int offset, | 603 | *page, unsigned int len, unsigned int offset, |
| 604 | unsigned short max_sectors) | 604 | unsigned int max_sectors) |
| 605 | { | 605 | { |
| 606 | int retried_segments = 0; | 606 | int retried_segments = 0; |
| 607 | struct bio_vec *bvec; | 607 | struct bio_vec *bvec; |
diff --git a/fs/configfs/dir.c b/fs/configfs/dir.c index 277bd1be21fd..e081acbac2e7 100644 --- a/fs/configfs/dir.c +++ b/fs/configfs/dir.c | |||
| @@ -56,29 +56,28 @@ static void configfs_d_iput(struct dentry * dentry, | |||
| 56 | struct configfs_dirent *sd = dentry->d_fsdata; | 56 | struct configfs_dirent *sd = dentry->d_fsdata; |
| 57 | 57 | ||
| 58 | if (sd) { | 58 | if (sd) { |
| 59 | BUG_ON(sd->s_dentry != dentry); | ||
| 60 | /* Coordinate with configfs_readdir */ | 59 | /* Coordinate with configfs_readdir */ |
| 61 | spin_lock(&configfs_dirent_lock); | 60 | spin_lock(&configfs_dirent_lock); |
| 62 | sd->s_dentry = NULL; | 61 | /* Coordinate with configfs_attach_attr where will increase |
| 62 | * sd->s_count and update sd->s_dentry to new allocated one. | ||
| 63 | * Only set sd->dentry to null when this dentry is the only | ||
| 64 | * sd owner. | ||
| 65 | * If not do so, configfs_d_iput may run just after | ||
| 66 | * configfs_attach_attr and set sd->s_dentry to null | ||
| 67 | * even it's still in use. | ||
| 68 | */ | ||
| 69 | if (atomic_read(&sd->s_count) <= 2) | ||
| 70 | sd->s_dentry = NULL; | ||
| 71 | |||
| 63 | spin_unlock(&configfs_dirent_lock); | 72 | spin_unlock(&configfs_dirent_lock); |
| 64 | configfs_put(sd); | 73 | configfs_put(sd); |
| 65 | } | 74 | } |
| 66 | iput(inode); | 75 | iput(inode); |
| 67 | } | 76 | } |
| 68 | 77 | ||
| 69 | /* | ||
| 70 | * We _must_ delete our dentries on last dput, as the chain-to-parent | ||
| 71 | * behavior is required to clear the parents of default_groups. | ||
| 72 | */ | ||
| 73 | static int configfs_d_delete(const struct dentry *dentry) | ||
| 74 | { | ||
| 75 | return 1; | ||
| 76 | } | ||
| 77 | |||
| 78 | const struct dentry_operations configfs_dentry_ops = { | 78 | const struct dentry_operations configfs_dentry_ops = { |
| 79 | .d_iput = configfs_d_iput, | 79 | .d_iput = configfs_d_iput, |
| 80 | /* simple_delete_dentry() isn't exported */ | 80 | .d_delete = always_delete_dentry, |
| 81 | .d_delete = configfs_d_delete, | ||
| 82 | }; | 81 | }; |
| 83 | 82 | ||
| 84 | #ifdef CONFIG_LOCKDEP | 83 | #ifdef CONFIG_LOCKDEP |
| @@ -426,8 +425,11 @@ static int configfs_attach_attr(struct configfs_dirent * sd, struct dentry * den | |||
| 426 | struct configfs_attribute * attr = sd->s_element; | 425 | struct configfs_attribute * attr = sd->s_element; |
| 427 | int error; | 426 | int error; |
| 428 | 427 | ||
| 428 | spin_lock(&configfs_dirent_lock); | ||
| 429 | dentry->d_fsdata = configfs_get(sd); | 429 | dentry->d_fsdata = configfs_get(sd); |
| 430 | sd->s_dentry = dentry; | 430 | sd->s_dentry = dentry; |
| 431 | spin_unlock(&configfs_dirent_lock); | ||
| 432 | |||
| 431 | error = configfs_create(dentry, (attr->ca_mode & S_IALLUGO) | S_IFREG, | 433 | error = configfs_create(dentry, (attr->ca_mode & S_IALLUGO) | S_IFREG, |
| 432 | configfs_init_file); | 434 | configfs_init_file); |
| 433 | if (error) { | 435 | if (error) { |
diff --git a/fs/coredump.c b/fs/coredump.c index 62406b6959b6..bc3fbcd32558 100644 --- a/fs/coredump.c +++ b/fs/coredump.c | |||
| @@ -695,7 +695,7 @@ int dump_emit(struct coredump_params *cprm, const void *addr, int nr) | |||
| 695 | while (nr) { | 695 | while (nr) { |
| 696 | if (dump_interrupted()) | 696 | if (dump_interrupted()) |
| 697 | return 0; | 697 | return 0; |
| 698 | n = vfs_write(file, addr, nr, &pos); | 698 | n = __kernel_write(file, addr, nr, &pos); |
| 699 | if (n <= 0) | 699 | if (n <= 0) |
| 700 | return 0; | 700 | return 0; |
| 701 | file->f_pos = pos; | 701 | file->f_pos = pos; |
| @@ -733,7 +733,7 @@ int dump_align(struct coredump_params *cprm, int align) | |||
| 733 | { | 733 | { |
| 734 | unsigned mod = cprm->written & (align - 1); | 734 | unsigned mod = cprm->written & (align - 1); |
| 735 | if (align & (align - 1)) | 735 | if (align & (align - 1)) |
| 736 | return -EINVAL; | 736 | return 0; |
| 737 | return mod ? dump_skip(cprm, align - mod) : 0; | 737 | return mod ? dump_skip(cprm, align - mod) : 1; |
| 738 | } | 738 | } |
| 739 | EXPORT_SYMBOL(dump_align); | 739 | EXPORT_SYMBOL(dump_align); |
diff --git a/fs/dcache.c b/fs/dcache.c index 0a38ef8d7f00..4bdb300b16e2 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
| @@ -88,35 +88,6 @@ EXPORT_SYMBOL(rename_lock); | |||
| 88 | 88 | ||
| 89 | static struct kmem_cache *dentry_cache __read_mostly; | 89 | static struct kmem_cache *dentry_cache __read_mostly; |
| 90 | 90 | ||
| 91 | /** | ||
| 92 | * read_seqbegin_or_lock - begin a sequence number check or locking block | ||
| 93 | * @lock: sequence lock | ||
| 94 | * @seq : sequence number to be checked | ||
| 95 | * | ||
| 96 | * First try it once optimistically without taking the lock. If that fails, | ||
| 97 | * take the lock. The sequence number is also used as a marker for deciding | ||
| 98 | * whether to be a reader (even) or writer (odd). | ||
| 99 | * N.B. seq must be initialized to an even number to begin with. | ||
| 100 | */ | ||
| 101 | static inline void read_seqbegin_or_lock(seqlock_t *lock, int *seq) | ||
| 102 | { | ||
| 103 | if (!(*seq & 1)) /* Even */ | ||
| 104 | *seq = read_seqbegin(lock); | ||
| 105 | else /* Odd */ | ||
| 106 | read_seqlock_excl(lock); | ||
| 107 | } | ||
| 108 | |||
| 109 | static inline int need_seqretry(seqlock_t *lock, int seq) | ||
| 110 | { | ||
| 111 | return !(seq & 1) && read_seqretry(lock, seq); | ||
| 112 | } | ||
| 113 | |||
| 114 | static inline void done_seqretry(seqlock_t *lock, int seq) | ||
| 115 | { | ||
| 116 | if (seq & 1) | ||
| 117 | read_sequnlock_excl(lock); | ||
| 118 | } | ||
| 119 | |||
| 120 | /* | 91 | /* |
| 121 | * This is the single most critical data structure when it comes | 92 | * This is the single most critical data structure when it comes |
| 122 | * to the dcache: the hashtable for lookups. Somebody should try | 93 | * to the dcache: the hashtable for lookups. Somebody should try |
| @@ -125,8 +96,6 @@ static inline void done_seqretry(seqlock_t *lock, int seq) | |||
| 125 | * This hash-function tries to avoid losing too many bits of hash | 96 | * This hash-function tries to avoid losing too many bits of hash |
| 126 | * information, yet avoid using a prime hash-size or similar. | 97 | * information, yet avoid using a prime hash-size or similar. |
| 127 | */ | 98 | */ |
| 128 | #define D_HASHBITS d_hash_shift | ||
| 129 | #define D_HASHMASK d_hash_mask | ||
| 130 | 99 | ||
| 131 | static unsigned int d_hash_mask __read_mostly; | 100 | static unsigned int d_hash_mask __read_mostly; |
| 132 | static unsigned int d_hash_shift __read_mostly; | 101 | static unsigned int d_hash_shift __read_mostly; |
| @@ -137,8 +106,8 @@ static inline struct hlist_bl_head *d_hash(const struct dentry *parent, | |||
| 137 | unsigned int hash) | 106 | unsigned int hash) |
| 138 | { | 107 | { |
| 139 | hash += (unsigned long) parent / L1_CACHE_BYTES; | 108 | hash += (unsigned long) parent / L1_CACHE_BYTES; |
| 140 | hash = hash + (hash >> D_HASHBITS); | 109 | hash = hash + (hash >> d_hash_shift); |
| 141 | return dentry_hashtable + (hash & D_HASHMASK); | 110 | return dentry_hashtable + (hash & d_hash_mask); |
| 142 | } | 111 | } |
| 143 | 112 | ||
| 144 | /* Statistics gathering. */ | 113 | /* Statistics gathering. */ |
| @@ -469,7 +438,7 @@ static struct dentry *d_kill(struct dentry *dentry, struct dentry *parent) | |||
| 469 | { | 438 | { |
| 470 | list_del(&dentry->d_u.d_child); | 439 | list_del(&dentry->d_u.d_child); |
| 471 | /* | 440 | /* |
| 472 | * Inform try_to_ascend() that we are no longer attached to the | 441 | * Inform d_walk() that we are no longer attached to the |
| 473 | * dentry tree | 442 | * dentry tree |
| 474 | */ | 443 | */ |
| 475 | dentry->d_flags |= DCACHE_DENTRY_KILLED; | 444 | dentry->d_flags |= DCACHE_DENTRY_KILLED; |
| @@ -1069,34 +1038,6 @@ void shrink_dcache_sb(struct super_block *sb) | |||
| 1069 | } | 1038 | } |
| 1070 | EXPORT_SYMBOL(shrink_dcache_sb); | 1039 | EXPORT_SYMBOL(shrink_dcache_sb); |
| 1071 | 1040 | ||
| 1072 | /* | ||
| 1073 | * This tries to ascend one level of parenthood, but | ||
| 1074 | * we can race with renaming, so we need to re-check | ||
| 1075 | * the parenthood after dropping the lock and check | ||
| 1076 | * that the sequence number still matches. | ||
| 1077 | */ | ||
| 1078 | static struct dentry *try_to_ascend(struct dentry *old, unsigned seq) | ||
| 1079 | { | ||
| 1080 | struct dentry *new = old->d_parent; | ||
| 1081 | |||
| 1082 | rcu_read_lock(); | ||
| 1083 | spin_unlock(&old->d_lock); | ||
| 1084 | spin_lock(&new->d_lock); | ||
| 1085 | |||
| 1086 | /* | ||
| 1087 | * might go back up the wrong parent if we have had a rename | ||
| 1088 | * or deletion | ||
| 1089 | */ | ||
| 1090 | if (new != old->d_parent || | ||
| 1091 | (old->d_flags & DCACHE_DENTRY_KILLED) || | ||
| 1092 | need_seqretry(&rename_lock, seq)) { | ||
| 1093 | spin_unlock(&new->d_lock); | ||
| 1094 | new = NULL; | ||
| 1095 | } | ||
| 1096 | rcu_read_unlock(); | ||
| 1097 | return new; | ||
| 1098 | } | ||
| 1099 | |||
| 1100 | /** | 1041 | /** |
| 1101 | * enum d_walk_ret - action to talke during tree walk | 1042 | * enum d_walk_ret - action to talke during tree walk |
| 1102 | * @D_WALK_CONTINUE: contrinue walk | 1043 | * @D_WALK_CONTINUE: contrinue walk |
| @@ -1185,9 +1126,24 @@ resume: | |||
| 1185 | */ | 1126 | */ |
| 1186 | if (this_parent != parent) { | 1127 | if (this_parent != parent) { |
| 1187 | struct dentry *child = this_parent; | 1128 | struct dentry *child = this_parent; |
| 1188 | this_parent = try_to_ascend(this_parent, seq); | 1129 | this_parent = child->d_parent; |
| 1189 | if (!this_parent) | 1130 | |
| 1131 | rcu_read_lock(); | ||
| 1132 | spin_unlock(&child->d_lock); | ||
| 1133 | spin_lock(&this_parent->d_lock); | ||
| 1134 | |||
| 1135 | /* | ||
| 1136 | * might go back up the wrong parent if we have had a rename | ||
| 1137 | * or deletion | ||
| 1138 | */ | ||
| 1139 | if (this_parent != child->d_parent || | ||
| 1140 | (child->d_flags & DCACHE_DENTRY_KILLED) || | ||
| 1141 | need_seqretry(&rename_lock, seq)) { | ||
| 1142 | spin_unlock(&this_parent->d_lock); | ||
| 1143 | rcu_read_unlock(); | ||
| 1190 | goto rename_retry; | 1144 | goto rename_retry; |
| 1145 | } | ||
| 1146 | rcu_read_unlock(); | ||
| 1191 | next = child->d_u.d_child.next; | 1147 | next = child->d_u.d_child.next; |
| 1192 | goto resume; | 1148 | goto resume; |
| 1193 | } | 1149 | } |
diff --git a/fs/efivarfs/super.c b/fs/efivarfs/super.c index a8766b880c07..becc725a1953 100644 --- a/fs/efivarfs/super.c +++ b/fs/efivarfs/super.c | |||
| @@ -83,19 +83,10 @@ static int efivarfs_d_hash(const struct dentry *dentry, struct qstr *qstr) | |||
| 83 | return 0; | 83 | return 0; |
| 84 | } | 84 | } |
| 85 | 85 | ||
| 86 | /* | ||
| 87 | * Retaining negative dentries for an in-memory filesystem just wastes | ||
| 88 | * memory and lookup time: arrange for them to be deleted immediately. | ||
| 89 | */ | ||
| 90 | static int efivarfs_delete_dentry(const struct dentry *dentry) | ||
| 91 | { | ||
| 92 | return 1; | ||
| 93 | } | ||
| 94 | |||
| 95 | static struct dentry_operations efivarfs_d_ops = { | 86 | static struct dentry_operations efivarfs_d_ops = { |
| 96 | .d_compare = efivarfs_d_compare, | 87 | .d_compare = efivarfs_d_compare, |
| 97 | .d_hash = efivarfs_d_hash, | 88 | .d_hash = efivarfs_d_hash, |
| 98 | .d_delete = efivarfs_delete_dentry, | 89 | .d_delete = always_delete_dentry, |
| 99 | }; | 90 | }; |
| 100 | 91 | ||
| 101 | static struct dentry *efivarfs_alloc_dentry(struct dentry *parent, char *name) | 92 | static struct dentry *efivarfs_alloc_dentry(struct dentry *parent, char *name) |
| @@ -1380,10 +1380,6 @@ int search_binary_handler(struct linux_binprm *bprm) | |||
| 1380 | if (retval) | 1380 | if (retval) |
| 1381 | return retval; | 1381 | return retval; |
| 1382 | 1382 | ||
| 1383 | retval = audit_bprm(bprm); | ||
| 1384 | if (retval) | ||
| 1385 | return retval; | ||
| 1386 | |||
| 1387 | retval = -ENOENT; | 1383 | retval = -ENOENT; |
| 1388 | retry: | 1384 | retry: |
| 1389 | read_lock(&binfmt_lock); | 1385 | read_lock(&binfmt_lock); |
| @@ -1431,6 +1427,7 @@ static int exec_binprm(struct linux_binprm *bprm) | |||
| 1431 | 1427 | ||
| 1432 | ret = search_binary_handler(bprm); | 1428 | ret = search_binary_handler(bprm); |
| 1433 | if (ret >= 0) { | 1429 | if (ret >= 0) { |
| 1430 | audit_bprm(bprm); | ||
| 1434 | trace_sched_process_exec(current, old_pid, bprm); | 1431 | trace_sched_process_exec(current, old_pid, bprm); |
| 1435 | ptrace_event(PTRACE_EVENT_EXEC, old_vpid); | 1432 | ptrace_event(PTRACE_EVENT_EXEC, old_vpid); |
| 1436 | current->did_exec = 1; | 1433 | current->did_exec = 1; |
diff --git a/fs/gfs2/lock_dlm.c b/fs/gfs2/lock_dlm.c index c8423d6de6c3..2a6ba06bee6f 100644 --- a/fs/gfs2/lock_dlm.c +++ b/fs/gfs2/lock_dlm.c | |||
| @@ -466,19 +466,19 @@ static void gdlm_cancel(struct gfs2_glock *gl) | |||
| 466 | static void control_lvb_read(struct lm_lockstruct *ls, uint32_t *lvb_gen, | 466 | static void control_lvb_read(struct lm_lockstruct *ls, uint32_t *lvb_gen, |
| 467 | char *lvb_bits) | 467 | char *lvb_bits) |
| 468 | { | 468 | { |
| 469 | uint32_t gen; | 469 | __le32 gen; |
| 470 | memcpy(lvb_bits, ls->ls_control_lvb, GDLM_LVB_SIZE); | 470 | memcpy(lvb_bits, ls->ls_control_lvb, GDLM_LVB_SIZE); |
| 471 | memcpy(&gen, lvb_bits, sizeof(uint32_t)); | 471 | memcpy(&gen, lvb_bits, sizeof(__le32)); |
| 472 | *lvb_gen = le32_to_cpu(gen); | 472 | *lvb_gen = le32_to_cpu(gen); |
| 473 | } | 473 | } |
| 474 | 474 | ||
| 475 | static void control_lvb_write(struct lm_lockstruct *ls, uint32_t lvb_gen, | 475 | static void control_lvb_write(struct lm_lockstruct *ls, uint32_t lvb_gen, |
| 476 | char *lvb_bits) | 476 | char *lvb_bits) |
| 477 | { | 477 | { |
| 478 | uint32_t gen; | 478 | __le32 gen; |
| 479 | memcpy(ls->ls_control_lvb, lvb_bits, GDLM_LVB_SIZE); | 479 | memcpy(ls->ls_control_lvb, lvb_bits, GDLM_LVB_SIZE); |
| 480 | gen = cpu_to_le32(lvb_gen); | 480 | gen = cpu_to_le32(lvb_gen); |
| 481 | memcpy(ls->ls_control_lvb, &gen, sizeof(uint32_t)); | 481 | memcpy(ls->ls_control_lvb, &gen, sizeof(__le32)); |
| 482 | } | 482 | } |
| 483 | 483 | ||
| 484 | static int all_jid_bits_clear(char *lvb) | 484 | static int all_jid_bits_clear(char *lvb) |
diff --git a/fs/gfs2/quota.c b/fs/gfs2/quota.c index 453b50eaddec..98236d0df3ca 100644 --- a/fs/gfs2/quota.c +++ b/fs/gfs2/quota.c | |||
| @@ -667,7 +667,7 @@ static int gfs2_adjust_quota(struct gfs2_inode *ip, loff_t loc, | |||
| 667 | struct buffer_head *bh; | 667 | struct buffer_head *bh; |
| 668 | struct page *page; | 668 | struct page *page; |
| 669 | void *kaddr, *ptr; | 669 | void *kaddr, *ptr; |
| 670 | struct gfs2_quota q, *qp; | 670 | struct gfs2_quota q; |
| 671 | int err, nbytes; | 671 | int err, nbytes; |
| 672 | u64 size; | 672 | u64 size; |
| 673 | 673 | ||
| @@ -683,28 +683,25 @@ static int gfs2_adjust_quota(struct gfs2_inode *ip, loff_t loc, | |||
| 683 | return err; | 683 | return err; |
| 684 | 684 | ||
| 685 | err = -EIO; | 685 | err = -EIO; |
| 686 | qp = &q; | 686 | be64_add_cpu(&q.qu_value, change); |
| 687 | qp->qu_value = be64_to_cpu(qp->qu_value); | 687 | qd->qd_qb.qb_value = q.qu_value; |
| 688 | qp->qu_value += change; | ||
| 689 | qp->qu_value = cpu_to_be64(qp->qu_value); | ||
| 690 | qd->qd_qb.qb_value = qp->qu_value; | ||
| 691 | if (fdq) { | 688 | if (fdq) { |
| 692 | if (fdq->d_fieldmask & FS_DQ_BSOFT) { | 689 | if (fdq->d_fieldmask & FS_DQ_BSOFT) { |
| 693 | qp->qu_warn = cpu_to_be64(fdq->d_blk_softlimit >> sdp->sd_fsb2bb_shift); | 690 | q.qu_warn = cpu_to_be64(fdq->d_blk_softlimit >> sdp->sd_fsb2bb_shift); |
| 694 | qd->qd_qb.qb_warn = qp->qu_warn; | 691 | qd->qd_qb.qb_warn = q.qu_warn; |
| 695 | } | 692 | } |
| 696 | if (fdq->d_fieldmask & FS_DQ_BHARD) { | 693 | if (fdq->d_fieldmask & FS_DQ_BHARD) { |
| 697 | qp->qu_limit = cpu_to_be64(fdq->d_blk_hardlimit >> sdp->sd_fsb2bb_shift); | 694 | q.qu_limit = cpu_to_be64(fdq->d_blk_hardlimit >> sdp->sd_fsb2bb_shift); |
| 698 | qd->qd_qb.qb_limit = qp->qu_limit; | 695 | qd->qd_qb.qb_limit = q.qu_limit; |
| 699 | } | 696 | } |
| 700 | if (fdq->d_fieldmask & FS_DQ_BCOUNT) { | 697 | if (fdq->d_fieldmask & FS_DQ_BCOUNT) { |
| 701 | qp->qu_value = cpu_to_be64(fdq->d_bcount >> sdp->sd_fsb2bb_shift); | 698 | q.qu_value = cpu_to_be64(fdq->d_bcount >> sdp->sd_fsb2bb_shift); |
| 702 | qd->qd_qb.qb_value = qp->qu_value; | 699 | qd->qd_qb.qb_value = q.qu_value; |
| 703 | } | 700 | } |
| 704 | } | 701 | } |
| 705 | 702 | ||
| 706 | /* Write the quota into the quota file on disk */ | 703 | /* Write the quota into the quota file on disk */ |
| 707 | ptr = qp; | 704 | ptr = &q; |
| 708 | nbytes = sizeof(struct gfs2_quota); | 705 | nbytes = sizeof(struct gfs2_quota); |
| 709 | get_a_page: | 706 | get_a_page: |
| 710 | page = find_or_create_page(mapping, index, GFP_NOFS); | 707 | page = find_or_create_page(mapping, index, GFP_NOFS); |
diff --git a/fs/gfs2/rgrp.c b/fs/gfs2/rgrp.c index 4d83abdd5635..c8d6161bd682 100644 --- a/fs/gfs2/rgrp.c +++ b/fs/gfs2/rgrp.c | |||
| @@ -1127,7 +1127,7 @@ int gfs2_rgrp_bh_get(struct gfs2_rgrpd *rgd) | |||
| 1127 | rgd->rd_flags |= (GFS2_RDF_UPTODATE | GFS2_RDF_CHECK); | 1127 | rgd->rd_flags |= (GFS2_RDF_UPTODATE | GFS2_RDF_CHECK); |
| 1128 | rgd->rd_free_clone = rgd->rd_free; | 1128 | rgd->rd_free_clone = rgd->rd_free; |
| 1129 | } | 1129 | } |
| 1130 | if (be32_to_cpu(GFS2_MAGIC) != rgd->rd_rgl->rl_magic) { | 1130 | if (cpu_to_be32(GFS2_MAGIC) != rgd->rd_rgl->rl_magic) { |
| 1131 | rgd->rd_rgl->rl_unlinked = cpu_to_be32(count_unlinked(rgd)); | 1131 | rgd->rd_rgl->rl_unlinked = cpu_to_be32(count_unlinked(rgd)); |
| 1132 | gfs2_rgrp_ondisk2lvb(rgd->rd_rgl, | 1132 | gfs2_rgrp_ondisk2lvb(rgd->rd_rgl, |
| 1133 | rgd->rd_bits[0].bi_bh->b_data); | 1133 | rgd->rd_bits[0].bi_bh->b_data); |
| @@ -1161,7 +1161,7 @@ int update_rgrp_lvb(struct gfs2_rgrpd *rgd) | |||
| 1161 | if (rgd->rd_flags & GFS2_RDF_UPTODATE) | 1161 | if (rgd->rd_flags & GFS2_RDF_UPTODATE) |
| 1162 | return 0; | 1162 | return 0; |
| 1163 | 1163 | ||
| 1164 | if (be32_to_cpu(GFS2_MAGIC) != rgd->rd_rgl->rl_magic) | 1164 | if (cpu_to_be32(GFS2_MAGIC) != rgd->rd_rgl->rl_magic) |
| 1165 | return gfs2_rgrp_bh_get(rgd); | 1165 | return gfs2_rgrp_bh_get(rgd); |
| 1166 | 1166 | ||
| 1167 | rl_flags = be32_to_cpu(rgd->rd_rgl->rl_flags); | 1167 | rl_flags = be32_to_cpu(rgd->rd_rgl->rl_flags); |
diff --git a/fs/hostfs/hostfs_kern.c b/fs/hostfs/hostfs_kern.c index 25437280a207..db23ce1bd903 100644 --- a/fs/hostfs/hostfs_kern.c +++ b/fs/hostfs/hostfs_kern.c | |||
| @@ -33,15 +33,6 @@ static inline struct hostfs_inode_info *HOSTFS_I(struct inode *inode) | |||
| 33 | 33 | ||
| 34 | #define FILE_HOSTFS_I(file) HOSTFS_I(file_inode(file)) | 34 | #define FILE_HOSTFS_I(file) HOSTFS_I(file_inode(file)) |
| 35 | 35 | ||
| 36 | static int hostfs_d_delete(const struct dentry *dentry) | ||
| 37 | { | ||
| 38 | return 1; | ||
| 39 | } | ||
| 40 | |||
| 41 | static const struct dentry_operations hostfs_dentry_ops = { | ||
| 42 | .d_delete = hostfs_d_delete, | ||
| 43 | }; | ||
| 44 | |||
| 45 | /* Changed in hostfs_args before the kernel starts running */ | 36 | /* Changed in hostfs_args before the kernel starts running */ |
| 46 | static char *root_ino = ""; | 37 | static char *root_ino = ""; |
| 47 | static int append = 0; | 38 | static int append = 0; |
| @@ -925,7 +916,7 @@ static int hostfs_fill_sb_common(struct super_block *sb, void *d, int silent) | |||
| 925 | sb->s_blocksize_bits = 10; | 916 | sb->s_blocksize_bits = 10; |
| 926 | sb->s_magic = HOSTFS_SUPER_MAGIC; | 917 | sb->s_magic = HOSTFS_SUPER_MAGIC; |
| 927 | sb->s_op = &hostfs_sbops; | 918 | sb->s_op = &hostfs_sbops; |
| 928 | sb->s_d_op = &hostfs_dentry_ops; | 919 | sb->s_d_op = &simple_dentry_operations; |
| 929 | sb->s_maxbytes = MAX_LFS_FILESIZE; | 920 | sb->s_maxbytes = MAX_LFS_FILESIZE; |
| 930 | 921 | ||
| 931 | /* NULL is printed as <NULL> by sprintf: avoid that. */ | 922 | /* NULL is printed as <NULL> by sprintf: avoid that. */ |
diff --git a/fs/libfs.c b/fs/libfs.c index 5de06947ba5e..a1844244246f 100644 --- a/fs/libfs.c +++ b/fs/libfs.c | |||
| @@ -47,10 +47,16 @@ EXPORT_SYMBOL(simple_statfs); | |||
| 47 | * Retaining negative dentries for an in-memory filesystem just wastes | 47 | * Retaining negative dentries for an in-memory filesystem just wastes |
| 48 | * memory and lookup time: arrange for them to be deleted immediately. | 48 | * memory and lookup time: arrange for them to be deleted immediately. |
| 49 | */ | 49 | */ |
| 50 | static int simple_delete_dentry(const struct dentry *dentry) | 50 | int always_delete_dentry(const struct dentry *dentry) |
| 51 | { | 51 | { |
| 52 | return 1; | 52 | return 1; |
| 53 | } | 53 | } |
| 54 | EXPORT_SYMBOL(always_delete_dentry); | ||
| 55 | |||
| 56 | const struct dentry_operations simple_dentry_operations = { | ||
| 57 | .d_delete = always_delete_dentry, | ||
| 58 | }; | ||
| 59 | EXPORT_SYMBOL(simple_dentry_operations); | ||
| 54 | 60 | ||
| 55 | /* | 61 | /* |
| 56 | * Lookup the data. This is trivial - if the dentry didn't already | 62 | * Lookup the data. This is trivial - if the dentry didn't already |
| @@ -58,10 +64,6 @@ static int simple_delete_dentry(const struct dentry *dentry) | |||
| 58 | */ | 64 | */ |
| 59 | struct dentry *simple_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) | 65 | struct dentry *simple_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) |
| 60 | { | 66 | { |
| 61 | static const struct dentry_operations simple_dentry_operations = { | ||
| 62 | .d_delete = simple_delete_dentry, | ||
| 63 | }; | ||
| 64 | |||
| 65 | if (dentry->d_name.len > NAME_MAX) | 67 | if (dentry->d_name.len > NAME_MAX) |
| 66 | return ERR_PTR(-ENAMETOOLONG); | 68 | return ERR_PTR(-ENAMETOOLONG); |
| 67 | if (!dentry->d_sb->s_d_op) | 69 | if (!dentry->d_sb->s_d_op) |
diff --git a/fs/namei.c b/fs/namei.c index e029a4cbff7d..8f77a8cea289 100644 --- a/fs/namei.c +++ b/fs/namei.c | |||
| @@ -2435,6 +2435,7 @@ static int may_delete(struct inode *dir, struct dentry *victim, bool isdir) | |||
| 2435 | */ | 2435 | */ |
| 2436 | static inline int may_create(struct inode *dir, struct dentry *child) | 2436 | static inline int may_create(struct inode *dir, struct dentry *child) |
| 2437 | { | 2437 | { |
| 2438 | audit_inode_child(dir, child, AUDIT_TYPE_CHILD_CREATE); | ||
| 2438 | if (child->d_inode) | 2439 | if (child->d_inode) |
| 2439 | return -EEXIST; | 2440 | return -EEXIST; |
| 2440 | if (IS_DEADDIR(dir)) | 2441 | if (IS_DEADDIR(dir)) |
diff --git a/fs/proc/base.c b/fs/proc/base.c index 1485e38daaa3..03c8d747be48 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c | |||
| @@ -1151,10 +1151,16 @@ static ssize_t proc_loginuid_write(struct file * file, const char __user * buf, | |||
| 1151 | goto out_free_page; | 1151 | goto out_free_page; |
| 1152 | 1152 | ||
| 1153 | } | 1153 | } |
| 1154 | kloginuid = make_kuid(file->f_cred->user_ns, loginuid); | 1154 | |
| 1155 | if (!uid_valid(kloginuid)) { | 1155 | /* is userspace tring to explicitly UNSET the loginuid? */ |
| 1156 | length = -EINVAL; | 1156 | if (loginuid == AUDIT_UID_UNSET) { |
| 1157 | goto out_free_page; | 1157 | kloginuid = INVALID_UID; |
| 1158 | } else { | ||
| 1159 | kloginuid = make_kuid(file->f_cred->user_ns, loginuid); | ||
| 1160 | if (!uid_valid(kloginuid)) { | ||
| 1161 | length = -EINVAL; | ||
| 1162 | goto out_free_page; | ||
| 1163 | } | ||
| 1158 | } | 1164 | } |
| 1159 | 1165 | ||
| 1160 | length = audit_set_loginuid(kloginuid); | 1166 | length = audit_set_loginuid(kloginuid); |
diff --git a/fs/proc/generic.c b/fs/proc/generic.c index 737e15615b04..cca93b6fb9a9 100644 --- a/fs/proc/generic.c +++ b/fs/proc/generic.c | |||
| @@ -175,22 +175,6 @@ static const struct inode_operations proc_link_inode_operations = { | |||
| 175 | }; | 175 | }; |
| 176 | 176 | ||
| 177 | /* | 177 | /* |
| 178 | * As some entries in /proc are volatile, we want to | ||
| 179 | * get rid of unused dentries. This could be made | ||
| 180 | * smarter: we could keep a "volatile" flag in the | ||
| 181 | * inode to indicate which ones to keep. | ||
| 182 | */ | ||
| 183 | static int proc_delete_dentry(const struct dentry * dentry) | ||
| 184 | { | ||
| 185 | return 1; | ||
| 186 | } | ||
| 187 | |||
| 188 | static const struct dentry_operations proc_dentry_operations = | ||
| 189 | { | ||
| 190 | .d_delete = proc_delete_dentry, | ||
| 191 | }; | ||
| 192 | |||
| 193 | /* | ||
| 194 | * Don't create negative dentries here, return -ENOENT by hand | 178 | * Don't create negative dentries here, return -ENOENT by hand |
| 195 | * instead. | 179 | * instead. |
| 196 | */ | 180 | */ |
| @@ -209,7 +193,7 @@ struct dentry *proc_lookup_de(struct proc_dir_entry *de, struct inode *dir, | |||
| 209 | inode = proc_get_inode(dir->i_sb, de); | 193 | inode = proc_get_inode(dir->i_sb, de); |
| 210 | if (!inode) | 194 | if (!inode) |
| 211 | return ERR_PTR(-ENOMEM); | 195 | return ERR_PTR(-ENOMEM); |
| 212 | d_set_d_op(dentry, &proc_dentry_operations); | 196 | d_set_d_op(dentry, &simple_dentry_operations); |
| 213 | d_add(dentry, inode); | 197 | d_add(dentry, inode); |
| 214 | return NULL; | 198 | return NULL; |
| 215 | } | 199 | } |
diff --git a/fs/proc/namespaces.c b/fs/proc/namespaces.c index 49a7fff2e83a..9ae46b87470d 100644 --- a/fs/proc/namespaces.c +++ b/fs/proc/namespaces.c | |||
| @@ -42,12 +42,6 @@ static const struct inode_operations ns_inode_operations = { | |||
| 42 | .setattr = proc_setattr, | 42 | .setattr = proc_setattr, |
| 43 | }; | 43 | }; |
| 44 | 44 | ||
| 45 | static int ns_delete_dentry(const struct dentry *dentry) | ||
| 46 | { | ||
| 47 | /* Don't cache namespace inodes when not in use */ | ||
| 48 | return 1; | ||
| 49 | } | ||
| 50 | |||
| 51 | static char *ns_dname(struct dentry *dentry, char *buffer, int buflen) | 45 | static char *ns_dname(struct dentry *dentry, char *buffer, int buflen) |
| 52 | { | 46 | { |
| 53 | struct inode *inode = dentry->d_inode; | 47 | struct inode *inode = dentry->d_inode; |
| @@ -59,7 +53,7 @@ static char *ns_dname(struct dentry *dentry, char *buffer, int buflen) | |||
| 59 | 53 | ||
| 60 | const struct dentry_operations ns_dentry_operations = | 54 | const struct dentry_operations ns_dentry_operations = |
| 61 | { | 55 | { |
| 62 | .d_delete = ns_delete_dentry, | 56 | .d_delete = always_delete_dentry, |
| 63 | .d_dname = ns_dname, | 57 | .d_dname = ns_dname, |
| 64 | }; | 58 | }; |
| 65 | 59 | ||
diff --git a/fs/squashfs/Kconfig b/fs/squashfs/Kconfig index c70111ebefd4..b6fa8657dcbc 100644 --- a/fs/squashfs/Kconfig +++ b/fs/squashfs/Kconfig | |||
| @@ -25,6 +25,78 @@ config SQUASHFS | |||
| 25 | 25 | ||
| 26 | If unsure, say N. | 26 | If unsure, say N. |
| 27 | 27 | ||
| 28 | choice | ||
| 29 | prompt "File decompression options" | ||
| 30 | depends on SQUASHFS | ||
| 31 | help | ||
| 32 | Squashfs now supports two options for decompressing file | ||
| 33 | data. Traditionally Squashfs has decompressed into an | ||
| 34 | intermediate buffer and then memcopied it into the page cache. | ||
| 35 | Squashfs now supports the ability to decompress directly into | ||
| 36 | the page cache. | ||
| 37 | |||
| 38 | If unsure, select "Decompress file data into an intermediate buffer" | ||
| 39 | |||
| 40 | config SQUASHFS_FILE_CACHE | ||
| 41 | bool "Decompress file data into an intermediate buffer" | ||
| 42 | help | ||
| 43 | Decompress file data into an intermediate buffer and then | ||
| 44 | memcopy it into the page cache. | ||
| 45 | |||
| 46 | config SQUASHFS_FILE_DIRECT | ||
| 47 | bool "Decompress files directly into the page cache" | ||
| 48 | help | ||
| 49 | Directly decompress file data into the page cache. | ||
| 50 | Doing so can significantly improve performance because | ||
| 51 | it eliminates a memcpy and it also removes the lock contention | ||
| 52 | on the single buffer. | ||
| 53 | |||
| 54 | endchoice | ||
| 55 | |||
| 56 | choice | ||
| 57 | prompt "Decompressor parallelisation options" | ||
| 58 | depends on SQUASHFS | ||
| 59 | help | ||
| 60 | Squashfs now supports three parallelisation options for | ||
| 61 | decompression. Each one exhibits various trade-offs between | ||
| 62 | decompression performance and CPU and memory usage. | ||
| 63 | |||
| 64 | If in doubt, select "Single threaded compression" | ||
| 65 | |||
| 66 | config SQUASHFS_DECOMP_SINGLE | ||
| 67 | bool "Single threaded compression" | ||
| 68 | help | ||
| 69 | Traditionally Squashfs has used single-threaded decompression. | ||
| 70 | Only one block (data or metadata) can be decompressed at any | ||
| 71 | one time. This limits CPU and memory usage to a minimum. | ||
| 72 | |||
| 73 | config SQUASHFS_DECOMP_MULTI | ||
| 74 | bool "Use multiple decompressors for parallel I/O" | ||
| 75 | help | ||
| 76 | By default Squashfs uses a single decompressor but it gives | ||
| 77 | poor performance on parallel I/O workloads when using multiple CPU | ||
| 78 | machines due to waiting on decompressor availability. | ||
| 79 | |||
| 80 | If you have a parallel I/O workload and your system has enough memory, | ||
| 81 | using this option may improve overall I/O performance. | ||
| 82 | |||
| 83 | This decompressor implementation uses up to two parallel | ||
| 84 | decompressors per core. It dynamically allocates decompressors | ||
| 85 | on a demand basis. | ||
| 86 | |||
| 87 | config SQUASHFS_DECOMP_MULTI_PERCPU | ||
| 88 | bool "Use percpu multiple decompressors for parallel I/O" | ||
| 89 | help | ||
| 90 | By default Squashfs uses a single decompressor but it gives | ||
| 91 | poor performance on parallel I/O workloads when using multiple CPU | ||
| 92 | machines due to waiting on decompressor availability. | ||
| 93 | |||
| 94 | This decompressor implementation uses a maximum of one | ||
| 95 | decompressor per core. It uses percpu variables to ensure | ||
| 96 | decompression is load-balanced across the cores. | ||
| 97 | |||
| 98 | endchoice | ||
| 99 | |||
| 28 | config SQUASHFS_XATTR | 100 | config SQUASHFS_XATTR |
| 29 | bool "Squashfs XATTR support" | 101 | bool "Squashfs XATTR support" |
| 30 | depends on SQUASHFS | 102 | depends on SQUASHFS |
diff --git a/fs/squashfs/Makefile b/fs/squashfs/Makefile index 110b0476f3b4..4132520b4ff2 100644 --- a/fs/squashfs/Makefile +++ b/fs/squashfs/Makefile | |||
| @@ -5,6 +5,11 @@ | |||
| 5 | obj-$(CONFIG_SQUASHFS) += squashfs.o | 5 | obj-$(CONFIG_SQUASHFS) += squashfs.o |
| 6 | squashfs-y += block.o cache.o dir.o export.o file.o fragment.o id.o inode.o | 6 | squashfs-y += block.o cache.o dir.o export.o file.o fragment.o id.o inode.o |
| 7 | squashfs-y += namei.o super.o symlink.o decompressor.o | 7 | squashfs-y += namei.o super.o symlink.o decompressor.o |
| 8 | squashfs-$(CONFIG_SQUASHFS_FILE_CACHE) += file_cache.o | ||
| 9 | squashfs-$(CONFIG_SQUASHFS_FILE_DIRECT) += file_direct.o page_actor.o | ||
| 10 | squashfs-$(CONFIG_SQUASHFS_DECOMP_SINGLE) += decompressor_single.o | ||
| 11 | squashfs-$(CONFIG_SQUASHFS_DECOMP_MULTI) += decompressor_multi.o | ||
| 12 | squashfs-$(CONFIG_SQUASHFS_DECOMP_MULTI_PERCPU) += decompressor_multi_percpu.o | ||
| 8 | squashfs-$(CONFIG_SQUASHFS_XATTR) += xattr.o xattr_id.o | 13 | squashfs-$(CONFIG_SQUASHFS_XATTR) += xattr.o xattr_id.o |
| 9 | squashfs-$(CONFIG_SQUASHFS_LZO) += lzo_wrapper.o | 14 | squashfs-$(CONFIG_SQUASHFS_LZO) += lzo_wrapper.o |
| 10 | squashfs-$(CONFIG_SQUASHFS_XZ) += xz_wrapper.o | 15 | squashfs-$(CONFIG_SQUASHFS_XZ) += xz_wrapper.o |
diff --git a/fs/squashfs/block.c b/fs/squashfs/block.c index 41d108ecc9be..0cea9b9236d0 100644 --- a/fs/squashfs/block.c +++ b/fs/squashfs/block.c | |||
| @@ -36,6 +36,7 @@ | |||
| 36 | #include "squashfs_fs_sb.h" | 36 | #include "squashfs_fs_sb.h" |
| 37 | #include "squashfs.h" | 37 | #include "squashfs.h" |
| 38 | #include "decompressor.h" | 38 | #include "decompressor.h" |
| 39 | #include "page_actor.h" | ||
| 39 | 40 | ||
| 40 | /* | 41 | /* |
| 41 | * Read the metadata block length, this is stored in the first two | 42 | * Read the metadata block length, this is stored in the first two |
| @@ -86,16 +87,16 @@ static struct buffer_head *get_block_length(struct super_block *sb, | |||
| 86 | * generated a larger block - this does occasionally happen with compression | 87 | * generated a larger block - this does occasionally happen with compression |
| 87 | * algorithms). | 88 | * algorithms). |
| 88 | */ | 89 | */ |
| 89 | int squashfs_read_data(struct super_block *sb, void **buffer, u64 index, | 90 | int squashfs_read_data(struct super_block *sb, u64 index, int length, |
| 90 | int length, u64 *next_index, int srclength, int pages) | 91 | u64 *next_index, struct squashfs_page_actor *output) |
| 91 | { | 92 | { |
| 92 | struct squashfs_sb_info *msblk = sb->s_fs_info; | 93 | struct squashfs_sb_info *msblk = sb->s_fs_info; |
| 93 | struct buffer_head **bh; | 94 | struct buffer_head **bh; |
| 94 | int offset = index & ((1 << msblk->devblksize_log2) - 1); | 95 | int offset = index & ((1 << msblk->devblksize_log2) - 1); |
| 95 | u64 cur_index = index >> msblk->devblksize_log2; | 96 | u64 cur_index = index >> msblk->devblksize_log2; |
| 96 | int bytes, compressed, b = 0, k = 0, page = 0, avail; | 97 | int bytes, compressed, b = 0, k = 0, avail, i; |
| 97 | 98 | ||
| 98 | bh = kcalloc(((srclength + msblk->devblksize - 1) | 99 | bh = kcalloc(((output->length + msblk->devblksize - 1) |
| 99 | >> msblk->devblksize_log2) + 1, sizeof(*bh), GFP_KERNEL); | 100 | >> msblk->devblksize_log2) + 1, sizeof(*bh), GFP_KERNEL); |
| 100 | if (bh == NULL) | 101 | if (bh == NULL) |
| 101 | return -ENOMEM; | 102 | return -ENOMEM; |
| @@ -111,9 +112,9 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index, | |||
| 111 | *next_index = index + length; | 112 | *next_index = index + length; |
| 112 | 113 | ||
| 113 | TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n", | 114 | TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n", |
| 114 | index, compressed ? "" : "un", length, srclength); | 115 | index, compressed ? "" : "un", length, output->length); |
| 115 | 116 | ||
| 116 | if (length < 0 || length > srclength || | 117 | if (length < 0 || length > output->length || |
| 117 | (index + length) > msblk->bytes_used) | 118 | (index + length) > msblk->bytes_used) |
| 118 | goto read_failure; | 119 | goto read_failure; |
| 119 | 120 | ||
| @@ -145,7 +146,7 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index, | |||
| 145 | TRACE("Block @ 0x%llx, %scompressed size %d\n", index, | 146 | TRACE("Block @ 0x%llx, %scompressed size %d\n", index, |
| 146 | compressed ? "" : "un", length); | 147 | compressed ? "" : "un", length); |
| 147 | 148 | ||
| 148 | if (length < 0 || length > srclength || | 149 | if (length < 0 || length > output->length || |
| 149 | (index + length) > msblk->bytes_used) | 150 | (index + length) > msblk->bytes_used) |
| 150 | goto block_release; | 151 | goto block_release; |
| 151 | 152 | ||
| @@ -158,9 +159,15 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index, | |||
| 158 | ll_rw_block(READ, b - 1, bh + 1); | 159 | ll_rw_block(READ, b - 1, bh + 1); |
| 159 | } | 160 | } |
| 160 | 161 | ||
| 162 | for (i = 0; i < b; i++) { | ||
| 163 | wait_on_buffer(bh[i]); | ||
| 164 | if (!buffer_uptodate(bh[i])) | ||
| 165 | goto block_release; | ||
| 166 | } | ||
| 167 | |||
| 161 | if (compressed) { | 168 | if (compressed) { |
| 162 | length = squashfs_decompress(msblk, buffer, bh, b, offset, | 169 | length = squashfs_decompress(msblk, bh, b, offset, length, |
| 163 | length, srclength, pages); | 170 | output); |
| 164 | if (length < 0) | 171 | if (length < 0) |
| 165 | goto read_failure; | 172 | goto read_failure; |
| 166 | } else { | 173 | } else { |
| @@ -168,22 +175,20 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index, | |||
| 168 | * Block is uncompressed. | 175 | * Block is uncompressed. |
| 169 | */ | 176 | */ |
| 170 | int in, pg_offset = 0; | 177 | int in, pg_offset = 0; |
| 178 | void *data = squashfs_first_page(output); | ||
| 171 | 179 | ||
| 172 | for (bytes = length; k < b; k++) { | 180 | for (bytes = length; k < b; k++) { |
| 173 | in = min(bytes, msblk->devblksize - offset); | 181 | in = min(bytes, msblk->devblksize - offset); |
| 174 | bytes -= in; | 182 | bytes -= in; |
| 175 | wait_on_buffer(bh[k]); | ||
| 176 | if (!buffer_uptodate(bh[k])) | ||
| 177 | goto block_release; | ||
| 178 | while (in) { | 183 | while (in) { |
| 179 | if (pg_offset == PAGE_CACHE_SIZE) { | 184 | if (pg_offset == PAGE_CACHE_SIZE) { |
| 180 | page++; | 185 | data = squashfs_next_page(output); |
| 181 | pg_offset = 0; | 186 | pg_offset = 0; |
| 182 | } | 187 | } |
| 183 | avail = min_t(int, in, PAGE_CACHE_SIZE - | 188 | avail = min_t(int, in, PAGE_CACHE_SIZE - |
| 184 | pg_offset); | 189 | pg_offset); |
| 185 | memcpy(buffer[page] + pg_offset, | 190 | memcpy(data + pg_offset, bh[k]->b_data + offset, |
| 186 | bh[k]->b_data + offset, avail); | 191 | avail); |
| 187 | in -= avail; | 192 | in -= avail; |
| 188 | pg_offset += avail; | 193 | pg_offset += avail; |
| 189 | offset += avail; | 194 | offset += avail; |
| @@ -191,6 +196,7 @@ int squashfs_read_data(struct super_block *sb, void **buffer, u64 index, | |||
| 191 | offset = 0; | 196 | offset = 0; |
| 192 | put_bh(bh[k]); | 197 | put_bh(bh[k]); |
| 193 | } | 198 | } |
| 199 | squashfs_finish_page(output); | ||
| 194 | } | 200 | } |
| 195 | 201 | ||
| 196 | kfree(bh); | 202 | kfree(bh); |
diff --git a/fs/squashfs/cache.c b/fs/squashfs/cache.c index af0b73802592..1cb70a0b2168 100644 --- a/fs/squashfs/cache.c +++ b/fs/squashfs/cache.c | |||
| @@ -56,6 +56,7 @@ | |||
| 56 | #include "squashfs_fs.h" | 56 | #include "squashfs_fs.h" |
| 57 | #include "squashfs_fs_sb.h" | 57 | #include "squashfs_fs_sb.h" |
| 58 | #include "squashfs.h" | 58 | #include "squashfs.h" |
| 59 | #include "page_actor.h" | ||
| 59 | 60 | ||
| 60 | /* | 61 | /* |
| 61 | * Look-up block in cache, and increment usage count. If not in cache, read | 62 | * Look-up block in cache, and increment usage count. If not in cache, read |
| @@ -119,9 +120,8 @@ struct squashfs_cache_entry *squashfs_cache_get(struct super_block *sb, | |||
| 119 | entry->error = 0; | 120 | entry->error = 0; |
| 120 | spin_unlock(&cache->lock); | 121 | spin_unlock(&cache->lock); |
| 121 | 122 | ||
| 122 | entry->length = squashfs_read_data(sb, entry->data, | 123 | entry->length = squashfs_read_data(sb, block, length, |
| 123 | block, length, &entry->next_index, | 124 | &entry->next_index, entry->actor); |
| 124 | cache->block_size, cache->pages); | ||
| 125 | 125 | ||
| 126 | spin_lock(&cache->lock); | 126 | spin_lock(&cache->lock); |
| 127 | 127 | ||
| @@ -220,6 +220,7 @@ void squashfs_cache_delete(struct squashfs_cache *cache) | |||
| 220 | kfree(cache->entry[i].data[j]); | 220 | kfree(cache->entry[i].data[j]); |
| 221 | kfree(cache->entry[i].data); | 221 | kfree(cache->entry[i].data); |
| 222 | } | 222 | } |
| 223 | kfree(cache->entry[i].actor); | ||
| 223 | } | 224 | } |
| 224 | 225 | ||
| 225 | kfree(cache->entry); | 226 | kfree(cache->entry); |
| @@ -280,6 +281,13 @@ struct squashfs_cache *squashfs_cache_init(char *name, int entries, | |||
| 280 | goto cleanup; | 281 | goto cleanup; |
| 281 | } | 282 | } |
| 282 | } | 283 | } |
| 284 | |||
| 285 | entry->actor = squashfs_page_actor_init(entry->data, | ||
| 286 | cache->pages, 0); | ||
| 287 | if (entry->actor == NULL) { | ||
| 288 | ERROR("Failed to allocate %s cache entry\n", name); | ||
| 289 | goto cleanup; | ||
| 290 | } | ||
| 283 | } | 291 | } |
| 284 | 292 | ||
| 285 | return cache; | 293 | return cache; |
| @@ -410,6 +418,7 @@ void *squashfs_read_table(struct super_block *sb, u64 block, int length) | |||
| 410 | int pages = (length + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; | 418 | int pages = (length + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; |
| 411 | int i, res; | 419 | int i, res; |
| 412 | void *table, *buffer, **data; | 420 | void *table, *buffer, **data; |
| 421 | struct squashfs_page_actor *actor; | ||
| 413 | 422 | ||
| 414 | table = buffer = kmalloc(length, GFP_KERNEL); | 423 | table = buffer = kmalloc(length, GFP_KERNEL); |
| 415 | if (table == NULL) | 424 | if (table == NULL) |
| @@ -421,19 +430,28 @@ void *squashfs_read_table(struct super_block *sb, u64 block, int length) | |||
| 421 | goto failed; | 430 | goto failed; |
| 422 | } | 431 | } |
| 423 | 432 | ||
| 433 | actor = squashfs_page_actor_init(data, pages, length); | ||
| 434 | if (actor == NULL) { | ||
| 435 | res = -ENOMEM; | ||
| 436 | goto failed2; | ||
| 437 | } | ||
| 438 | |||
| 424 | for (i = 0; i < pages; i++, buffer += PAGE_CACHE_SIZE) | 439 | for (i = 0; i < pages; i++, buffer += PAGE_CACHE_SIZE) |
| 425 | data[i] = buffer; | 440 | data[i] = buffer; |
| 426 | 441 | ||
| 427 | res = squashfs_read_data(sb, data, block, length | | 442 | res = squashfs_read_data(sb, block, length | |
| 428 | SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, length, pages); | 443 | SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, actor); |
| 429 | 444 | ||
| 430 | kfree(data); | 445 | kfree(data); |
| 446 | kfree(actor); | ||
| 431 | 447 | ||
| 432 | if (res < 0) | 448 | if (res < 0) |
| 433 | goto failed; | 449 | goto failed; |
| 434 | 450 | ||
| 435 | return table; | 451 | return table; |
| 436 | 452 | ||
| 453 | failed2: | ||
| 454 | kfree(data); | ||
| 437 | failed: | 455 | failed: |
| 438 | kfree(table); | 456 | kfree(table); |
| 439 | return ERR_PTR(res); | 457 | return ERR_PTR(res); |
diff --git a/fs/squashfs/decompressor.c b/fs/squashfs/decompressor.c index 3f6271d86abc..ac22fe73b0ad 100644 --- a/fs/squashfs/decompressor.c +++ b/fs/squashfs/decompressor.c | |||
| @@ -30,6 +30,7 @@ | |||
| 30 | #include "squashfs_fs_sb.h" | 30 | #include "squashfs_fs_sb.h" |
| 31 | #include "decompressor.h" | 31 | #include "decompressor.h" |
| 32 | #include "squashfs.h" | 32 | #include "squashfs.h" |
| 33 | #include "page_actor.h" | ||
| 33 | 34 | ||
| 34 | /* | 35 | /* |
| 35 | * This file (and decompressor.h) implements a decompressor framework for | 36 | * This file (and decompressor.h) implements a decompressor framework for |
| @@ -37,29 +38,29 @@ | |||
| 37 | */ | 38 | */ |
| 38 | 39 | ||
| 39 | static const struct squashfs_decompressor squashfs_lzma_unsupported_comp_ops = { | 40 | static const struct squashfs_decompressor squashfs_lzma_unsupported_comp_ops = { |
| 40 | NULL, NULL, NULL, LZMA_COMPRESSION, "lzma", 0 | 41 | NULL, NULL, NULL, NULL, LZMA_COMPRESSION, "lzma", 0 |
| 41 | }; | 42 | }; |
| 42 | 43 | ||
| 43 | #ifndef CONFIG_SQUASHFS_LZO | 44 | #ifndef CONFIG_SQUASHFS_LZO |
| 44 | static const struct squashfs_decompressor squashfs_lzo_comp_ops = { | 45 | static const struct squashfs_decompressor squashfs_lzo_comp_ops = { |
| 45 | NULL, NULL, NULL, LZO_COMPRESSION, "lzo", 0 | 46 | NULL, NULL, NULL, NULL, LZO_COMPRESSION, "lzo", 0 |
| 46 | }; | 47 | }; |
| 47 | #endif | 48 | #endif |
| 48 | 49 | ||
| 49 | #ifndef CONFIG_SQUASHFS_XZ | 50 | #ifndef CONFIG_SQUASHFS_XZ |
| 50 | static const struct squashfs_decompressor squashfs_xz_comp_ops = { | 51 | static const struct squashfs_decompressor squashfs_xz_comp_ops = { |
| 51 | NULL, NULL, NULL, XZ_COMPRESSION, "xz", 0 | 52 | NULL, NULL, NULL, NULL, XZ_COMPRESSION, "xz", 0 |
| 52 | }; | 53 | }; |
| 53 | #endif | 54 | #endif |
| 54 | 55 | ||
| 55 | #ifndef CONFIG_SQUASHFS_ZLIB | 56 | #ifndef CONFIG_SQUASHFS_ZLIB |
| 56 | static const struct squashfs_decompressor squashfs_zlib_comp_ops = { | 57 | static const struct squashfs_decompressor squashfs_zlib_comp_ops = { |
| 57 | NULL, NULL, NULL, ZLIB_COMPRESSION, "zlib", 0 | 58 | NULL, NULL, NULL, NULL, ZLIB_COMPRESSION, "zlib", 0 |
| 58 | }; | 59 | }; |
| 59 | #endif | 60 | #endif |
| 60 | 61 | ||
| 61 | static const struct squashfs_decompressor squashfs_unknown_comp_ops = { | 62 | static const struct squashfs_decompressor squashfs_unknown_comp_ops = { |
| 62 | NULL, NULL, NULL, 0, "unknown", 0 | 63 | NULL, NULL, NULL, NULL, 0, "unknown", 0 |
| 63 | }; | 64 | }; |
| 64 | 65 | ||
| 65 | static const struct squashfs_decompressor *decompressor[] = { | 66 | static const struct squashfs_decompressor *decompressor[] = { |
| @@ -83,10 +84,11 @@ const struct squashfs_decompressor *squashfs_lookup_decompressor(int id) | |||
| 83 | } | 84 | } |
| 84 | 85 | ||
| 85 | 86 | ||
| 86 | void *squashfs_decompressor_init(struct super_block *sb, unsigned short flags) | 87 | static void *get_comp_opts(struct super_block *sb, unsigned short flags) |
| 87 | { | 88 | { |
| 88 | struct squashfs_sb_info *msblk = sb->s_fs_info; | 89 | struct squashfs_sb_info *msblk = sb->s_fs_info; |
| 89 | void *strm, *buffer = NULL; | 90 | void *buffer = NULL, *comp_opts; |
| 91 | struct squashfs_page_actor *actor = NULL; | ||
| 90 | int length = 0; | 92 | int length = 0; |
| 91 | 93 | ||
| 92 | /* | 94 | /* |
| @@ -94,23 +96,46 @@ void *squashfs_decompressor_init(struct super_block *sb, unsigned short flags) | |||
| 94 | */ | 96 | */ |
| 95 | if (SQUASHFS_COMP_OPTS(flags)) { | 97 | if (SQUASHFS_COMP_OPTS(flags)) { |
| 96 | buffer = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL); | 98 | buffer = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL); |
| 97 | if (buffer == NULL) | 99 | if (buffer == NULL) { |
| 98 | return ERR_PTR(-ENOMEM); | 100 | comp_opts = ERR_PTR(-ENOMEM); |
| 101 | goto out; | ||
| 102 | } | ||
| 103 | |||
| 104 | actor = squashfs_page_actor_init(&buffer, 1, 0); | ||
| 105 | if (actor == NULL) { | ||
| 106 | comp_opts = ERR_PTR(-ENOMEM); | ||
| 107 | goto out; | ||
| 108 | } | ||
| 99 | 109 | ||
| 100 | length = squashfs_read_data(sb, &buffer, | 110 | length = squashfs_read_data(sb, |
| 101 | sizeof(struct squashfs_super_block), 0, NULL, | 111 | sizeof(struct squashfs_super_block), 0, NULL, actor); |
| 102 | PAGE_CACHE_SIZE, 1); | ||
| 103 | 112 | ||
| 104 | if (length < 0) { | 113 | if (length < 0) { |
| 105 | strm = ERR_PTR(length); | 114 | comp_opts = ERR_PTR(length); |
| 106 | goto finished; | 115 | goto out; |
| 107 | } | 116 | } |
| 108 | } | 117 | } |
| 109 | 118 | ||
| 110 | strm = msblk->decompressor->init(msblk, buffer, length); | 119 | comp_opts = squashfs_comp_opts(msblk, buffer, length); |
| 111 | 120 | ||
| 112 | finished: | 121 | out: |
| 122 | kfree(actor); | ||
| 113 | kfree(buffer); | 123 | kfree(buffer); |
| 124 | return comp_opts; | ||
| 125 | } | ||
| 126 | |||
| 127 | |||
| 128 | void *squashfs_decompressor_setup(struct super_block *sb, unsigned short flags) | ||
| 129 | { | ||
| 130 | struct squashfs_sb_info *msblk = sb->s_fs_info; | ||
| 131 | void *stream, *comp_opts = get_comp_opts(sb, flags); | ||
| 132 | |||
| 133 | if (IS_ERR(comp_opts)) | ||
| 134 | return comp_opts; | ||
| 135 | |||
| 136 | stream = squashfs_decompressor_create(msblk, comp_opts); | ||
| 137 | if (IS_ERR(stream)) | ||
| 138 | kfree(comp_opts); | ||
| 114 | 139 | ||
| 115 | return strm; | 140 | return stream; |
| 116 | } | 141 | } |
diff --git a/fs/squashfs/decompressor.h b/fs/squashfs/decompressor.h index 330073e29029..af0985321808 100644 --- a/fs/squashfs/decompressor.h +++ b/fs/squashfs/decompressor.h | |||
| @@ -24,28 +24,22 @@ | |||
| 24 | */ | 24 | */ |
| 25 | 25 | ||
| 26 | struct squashfs_decompressor { | 26 | struct squashfs_decompressor { |
| 27 | void *(*init)(struct squashfs_sb_info *, void *, int); | 27 | void *(*init)(struct squashfs_sb_info *, void *); |
| 28 | void *(*comp_opts)(struct squashfs_sb_info *, void *, int); | ||
| 28 | void (*free)(void *); | 29 | void (*free)(void *); |
| 29 | int (*decompress)(struct squashfs_sb_info *, void **, | 30 | int (*decompress)(struct squashfs_sb_info *, void *, |
| 30 | struct buffer_head **, int, int, int, int, int); | 31 | struct buffer_head **, int, int, int, |
| 32 | struct squashfs_page_actor *); | ||
| 31 | int id; | 33 | int id; |
| 32 | char *name; | 34 | char *name; |
| 33 | int supported; | 35 | int supported; |
| 34 | }; | 36 | }; |
| 35 | 37 | ||
| 36 | static inline void squashfs_decompressor_free(struct squashfs_sb_info *msblk, | 38 | static inline void *squashfs_comp_opts(struct squashfs_sb_info *msblk, |
| 37 | void *s) | 39 | void *buff, int length) |
| 38 | { | 40 | { |
| 39 | if (msblk->decompressor) | 41 | return msblk->decompressor->comp_opts ? |
| 40 | msblk->decompressor->free(s); | 42 | msblk->decompressor->comp_opts(msblk, buff, length) : NULL; |
| 41 | } | ||
| 42 | |||
| 43 | static inline int squashfs_decompress(struct squashfs_sb_info *msblk, | ||
| 44 | void **buffer, struct buffer_head **bh, int b, int offset, int length, | ||
| 45 | int srclength, int pages) | ||
| 46 | { | ||
| 47 | return msblk->decompressor->decompress(msblk, buffer, bh, b, offset, | ||
| 48 | length, srclength, pages); | ||
| 49 | } | 43 | } |
| 50 | 44 | ||
| 51 | #ifdef CONFIG_SQUASHFS_XZ | 45 | #ifdef CONFIG_SQUASHFS_XZ |
diff --git a/fs/squashfs/decompressor_multi.c b/fs/squashfs/decompressor_multi.c new file mode 100644 index 000000000000..d6008a636479 --- /dev/null +++ b/fs/squashfs/decompressor_multi.c | |||
| @@ -0,0 +1,198 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2013 | ||
| 3 | * Minchan Kim <minchan@kernel.org> | ||
| 4 | * | ||
| 5 | * This work is licensed under the terms of the GNU GPL, version 2. See | ||
| 6 | * the COPYING file in the top-level directory. | ||
| 7 | */ | ||
| 8 | #include <linux/types.h> | ||
| 9 | #include <linux/mutex.h> | ||
| 10 | #include <linux/slab.h> | ||
| 11 | #include <linux/buffer_head.h> | ||
| 12 | #include <linux/sched.h> | ||
| 13 | #include <linux/wait.h> | ||
| 14 | #include <linux/cpumask.h> | ||
| 15 | |||
| 16 | #include "squashfs_fs.h" | ||
| 17 | #include "squashfs_fs_sb.h" | ||
| 18 | #include "decompressor.h" | ||
| 19 | #include "squashfs.h" | ||
| 20 | |||
| 21 | /* | ||
| 22 | * This file implements multi-threaded decompression in the | ||
| 23 | * decompressor framework | ||
| 24 | */ | ||
| 25 | |||
| 26 | |||
| 27 | /* | ||
| 28 | * The reason that multiply two is that a CPU can request new I/O | ||
| 29 | * while it is waiting previous request. | ||
| 30 | */ | ||
| 31 | #define MAX_DECOMPRESSOR (num_online_cpus() * 2) | ||
| 32 | |||
| 33 | |||
| 34 | int squashfs_max_decompressors(void) | ||
| 35 | { | ||
| 36 | return MAX_DECOMPRESSOR; | ||
| 37 | } | ||
| 38 | |||
| 39 | |||
| 40 | struct squashfs_stream { | ||
| 41 | void *comp_opts; | ||
| 42 | struct list_head strm_list; | ||
| 43 | struct mutex mutex; | ||
| 44 | int avail_decomp; | ||
| 45 | wait_queue_head_t wait; | ||
| 46 | }; | ||
| 47 | |||
| 48 | |||
| 49 | struct decomp_stream { | ||
| 50 | void *stream; | ||
| 51 | struct list_head list; | ||
| 52 | }; | ||
| 53 | |||
| 54 | |||
| 55 | static void put_decomp_stream(struct decomp_stream *decomp_strm, | ||
| 56 | struct squashfs_stream *stream) | ||
| 57 | { | ||
| 58 | mutex_lock(&stream->mutex); | ||
| 59 | list_add(&decomp_strm->list, &stream->strm_list); | ||
| 60 | mutex_unlock(&stream->mutex); | ||
| 61 | wake_up(&stream->wait); | ||
| 62 | } | ||
| 63 | |||
| 64 | void *squashfs_decompressor_create(struct squashfs_sb_info *msblk, | ||
| 65 | void *comp_opts) | ||
| 66 | { | ||
| 67 | struct squashfs_stream *stream; | ||
| 68 | struct decomp_stream *decomp_strm = NULL; | ||
| 69 | int err = -ENOMEM; | ||
| 70 | |||
| 71 | stream = kzalloc(sizeof(*stream), GFP_KERNEL); | ||
| 72 | if (!stream) | ||
| 73 | goto out; | ||
| 74 | |||
| 75 | stream->comp_opts = comp_opts; | ||
| 76 | mutex_init(&stream->mutex); | ||
| 77 | INIT_LIST_HEAD(&stream->strm_list); | ||
| 78 | init_waitqueue_head(&stream->wait); | ||
| 79 | |||
| 80 | /* | ||
| 81 | * We should have a decompressor at least as default | ||
| 82 | * so if we fail to allocate new decompressor dynamically, | ||
| 83 | * we could always fall back to default decompressor and | ||
| 84 | * file system works. | ||
| 85 | */ | ||
| 86 | decomp_strm = kmalloc(sizeof(*decomp_strm), GFP_KERNEL); | ||
| 87 | if (!decomp_strm) | ||
| 88 | goto out; | ||
| 89 | |||
| 90 | decomp_strm->stream = msblk->decompressor->init(msblk, | ||
| 91 | stream->comp_opts); | ||
| 92 | if (IS_ERR(decomp_strm->stream)) { | ||
| 93 | err = PTR_ERR(decomp_strm->stream); | ||
| 94 | goto out; | ||
| 95 | } | ||
| 96 | |||
| 97 | list_add(&decomp_strm->list, &stream->strm_list); | ||
| 98 | stream->avail_decomp = 1; | ||
| 99 | return stream; | ||
| 100 | |||
| 101 | out: | ||
| 102 | kfree(decomp_strm); | ||
| 103 | kfree(stream); | ||
| 104 | return ERR_PTR(err); | ||
| 105 | } | ||
| 106 | |||
| 107 | |||
| 108 | void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk) | ||
| 109 | { | ||
| 110 | struct squashfs_stream *stream = msblk->stream; | ||
| 111 | if (stream) { | ||
| 112 | struct decomp_stream *decomp_strm; | ||
| 113 | |||
| 114 | while (!list_empty(&stream->strm_list)) { | ||
| 115 | decomp_strm = list_entry(stream->strm_list.prev, | ||
| 116 | struct decomp_stream, list); | ||
| 117 | list_del(&decomp_strm->list); | ||
| 118 | msblk->decompressor->free(decomp_strm->stream); | ||
| 119 | kfree(decomp_strm); | ||
| 120 | stream->avail_decomp--; | ||
| 121 | } | ||
| 122 | WARN_ON(stream->avail_decomp); | ||
| 123 | kfree(stream->comp_opts); | ||
| 124 | kfree(stream); | ||
| 125 | } | ||
| 126 | } | ||
| 127 | |||
| 128 | |||
| 129 | static struct decomp_stream *get_decomp_stream(struct squashfs_sb_info *msblk, | ||
| 130 | struct squashfs_stream *stream) | ||
| 131 | { | ||
| 132 | struct decomp_stream *decomp_strm; | ||
| 133 | |||
| 134 | while (1) { | ||
| 135 | mutex_lock(&stream->mutex); | ||
| 136 | |||
| 137 | /* There is available decomp_stream */ | ||
| 138 | if (!list_empty(&stream->strm_list)) { | ||
| 139 | decomp_strm = list_entry(stream->strm_list.prev, | ||
| 140 | struct decomp_stream, list); | ||
| 141 | list_del(&decomp_strm->list); | ||
| 142 | mutex_unlock(&stream->mutex); | ||
| 143 | break; | ||
| 144 | } | ||
| 145 | |||
| 146 | /* | ||
| 147 | * If there is no available decomp and already full, | ||
| 148 | * let's wait for releasing decomp from other users. | ||
| 149 | */ | ||
| 150 | if (stream->avail_decomp >= MAX_DECOMPRESSOR) | ||
| 151 | goto wait; | ||
| 152 | |||
| 153 | /* Let's allocate new decomp */ | ||
| 154 | decomp_strm = kmalloc(sizeof(*decomp_strm), GFP_KERNEL); | ||
| 155 | if (!decomp_strm) | ||
| 156 | goto wait; | ||
| 157 | |||
| 158 | decomp_strm->stream = msblk->decompressor->init(msblk, | ||
| 159 | stream->comp_opts); | ||
| 160 | if (IS_ERR(decomp_strm->stream)) { | ||
| 161 | kfree(decomp_strm); | ||
| 162 | goto wait; | ||
| 163 | } | ||
| 164 | |||
| 165 | stream->avail_decomp++; | ||
| 166 | WARN_ON(stream->avail_decomp > MAX_DECOMPRESSOR); | ||
| 167 | |||
| 168 | mutex_unlock(&stream->mutex); | ||
| 169 | break; | ||
| 170 | wait: | ||
| 171 | /* | ||
| 172 | * If system memory is tough, let's for other's | ||
| 173 | * releasing instead of hurting VM because it could | ||
| 174 | * make page cache thrashing. | ||
| 175 | */ | ||
| 176 | mutex_unlock(&stream->mutex); | ||
| 177 | wait_event(stream->wait, | ||
| 178 | !list_empty(&stream->strm_list)); | ||
| 179 | } | ||
| 180 | |||
| 181 | return decomp_strm; | ||
| 182 | } | ||
| 183 | |||
| 184 | |||
| 185 | int squashfs_decompress(struct squashfs_sb_info *msblk, struct buffer_head **bh, | ||
| 186 | int b, int offset, int length, struct squashfs_page_actor *output) | ||
| 187 | { | ||
| 188 | int res; | ||
| 189 | struct squashfs_stream *stream = msblk->stream; | ||
| 190 | struct decomp_stream *decomp_stream = get_decomp_stream(msblk, stream); | ||
| 191 | res = msblk->decompressor->decompress(msblk, decomp_stream->stream, | ||
| 192 | bh, b, offset, length, output); | ||
| 193 | put_decomp_stream(decomp_stream, stream); | ||
| 194 | if (res < 0) | ||
| 195 | ERROR("%s decompression failed, data probably corrupt\n", | ||
| 196 | msblk->decompressor->name); | ||
| 197 | return res; | ||
| 198 | } | ||
diff --git a/fs/squashfs/decompressor_multi_percpu.c b/fs/squashfs/decompressor_multi_percpu.c new file mode 100644 index 000000000000..23a9c28ad8ea --- /dev/null +++ b/fs/squashfs/decompressor_multi_percpu.c | |||
| @@ -0,0 +1,97 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2013 | ||
| 3 | * Phillip Lougher <phillip@squashfs.org.uk> | ||
| 4 | * | ||
| 5 | * This work is licensed under the terms of the GNU GPL, version 2. See | ||
| 6 | * the COPYING file in the top-level directory. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include <linux/types.h> | ||
| 10 | #include <linux/slab.h> | ||
| 11 | #include <linux/percpu.h> | ||
| 12 | #include <linux/buffer_head.h> | ||
| 13 | |||
| 14 | #include "squashfs_fs.h" | ||
| 15 | #include "squashfs_fs_sb.h" | ||
| 16 | #include "decompressor.h" | ||
| 17 | #include "squashfs.h" | ||
| 18 | |||
| 19 | /* | ||
| 20 | * This file implements multi-threaded decompression using percpu | ||
| 21 | * variables, one thread per cpu core. | ||
| 22 | */ | ||
| 23 | |||
| 24 | struct squashfs_stream { | ||
| 25 | void *stream; | ||
| 26 | }; | ||
| 27 | |||
| 28 | void *squashfs_decompressor_create(struct squashfs_sb_info *msblk, | ||
| 29 | void *comp_opts) | ||
| 30 | { | ||
| 31 | struct squashfs_stream *stream; | ||
| 32 | struct squashfs_stream __percpu *percpu; | ||
| 33 | int err, cpu; | ||
| 34 | |||
| 35 | percpu = alloc_percpu(struct squashfs_stream); | ||
| 36 | if (percpu == NULL) | ||
| 37 | return ERR_PTR(-ENOMEM); | ||
| 38 | |||
| 39 | for_each_possible_cpu(cpu) { | ||
| 40 | stream = per_cpu_ptr(percpu, cpu); | ||
| 41 | stream->stream = msblk->decompressor->init(msblk, comp_opts); | ||
| 42 | if (IS_ERR(stream->stream)) { | ||
| 43 | err = PTR_ERR(stream->stream); | ||
| 44 | goto out; | ||
| 45 | } | ||
| 46 | } | ||
| 47 | |||
| 48 | kfree(comp_opts); | ||
| 49 | return (__force void *) percpu; | ||
| 50 | |||
| 51 | out: | ||
| 52 | for_each_possible_cpu(cpu) { | ||
| 53 | stream = per_cpu_ptr(percpu, cpu); | ||
| 54 | if (!IS_ERR_OR_NULL(stream->stream)) | ||
| 55 | msblk->decompressor->free(stream->stream); | ||
| 56 | } | ||
| 57 | free_percpu(percpu); | ||
| 58 | return ERR_PTR(err); | ||
| 59 | } | ||
| 60 | |||
| 61 | void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk) | ||
| 62 | { | ||
| 63 | struct squashfs_stream __percpu *percpu = | ||
| 64 | (struct squashfs_stream __percpu *) msblk->stream; | ||
| 65 | struct squashfs_stream *stream; | ||
| 66 | int cpu; | ||
| 67 | |||
| 68 | if (msblk->stream) { | ||
| 69 | for_each_possible_cpu(cpu) { | ||
| 70 | stream = per_cpu_ptr(percpu, cpu); | ||
| 71 | msblk->decompressor->free(stream->stream); | ||
| 72 | } | ||
| 73 | free_percpu(percpu); | ||
| 74 | } | ||
| 75 | } | ||
| 76 | |||
| 77 | int squashfs_decompress(struct squashfs_sb_info *msblk, struct buffer_head **bh, | ||
| 78 | int b, int offset, int length, struct squashfs_page_actor *output) | ||
| 79 | { | ||
| 80 | struct squashfs_stream __percpu *percpu = | ||
| 81 | (struct squashfs_stream __percpu *) msblk->stream; | ||
| 82 | struct squashfs_stream *stream = get_cpu_ptr(percpu); | ||
| 83 | int res = msblk->decompressor->decompress(msblk, stream->stream, bh, b, | ||
| 84 | offset, length, output); | ||
| 85 | put_cpu_ptr(stream); | ||
| 86 | |||
| 87 | if (res < 0) | ||
| 88 | ERROR("%s decompression failed, data probably corrupt\n", | ||
| 89 | msblk->decompressor->name); | ||
| 90 | |||
| 91 | return res; | ||
| 92 | } | ||
| 93 | |||
| 94 | int squashfs_max_decompressors(void) | ||
| 95 | { | ||
| 96 | return num_possible_cpus(); | ||
| 97 | } | ||
diff --git a/fs/squashfs/decompressor_single.c b/fs/squashfs/decompressor_single.c new file mode 100644 index 000000000000..a6c75929a00e --- /dev/null +++ b/fs/squashfs/decompressor_single.c | |||
| @@ -0,0 +1,85 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2013 | ||
| 3 | * Phillip Lougher <phillip@squashfs.org.uk> | ||
| 4 | * | ||
| 5 | * This work is licensed under the terms of the GNU GPL, version 2. See | ||
| 6 | * the COPYING file in the top-level directory. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include <linux/types.h> | ||
| 10 | #include <linux/mutex.h> | ||
| 11 | #include <linux/slab.h> | ||
| 12 | #include <linux/buffer_head.h> | ||
| 13 | |||
| 14 | #include "squashfs_fs.h" | ||
| 15 | #include "squashfs_fs_sb.h" | ||
| 16 | #include "decompressor.h" | ||
| 17 | #include "squashfs.h" | ||
| 18 | |||
| 19 | /* | ||
| 20 | * This file implements single-threaded decompression in the | ||
| 21 | * decompressor framework | ||
| 22 | */ | ||
| 23 | |||
| 24 | struct squashfs_stream { | ||
| 25 | void *stream; | ||
| 26 | struct mutex mutex; | ||
| 27 | }; | ||
| 28 | |||
| 29 | void *squashfs_decompressor_create(struct squashfs_sb_info *msblk, | ||
| 30 | void *comp_opts) | ||
| 31 | { | ||
| 32 | struct squashfs_stream *stream; | ||
| 33 | int err = -ENOMEM; | ||
| 34 | |||
| 35 | stream = kmalloc(sizeof(*stream), GFP_KERNEL); | ||
| 36 | if (stream == NULL) | ||
| 37 | goto out; | ||
| 38 | |||
| 39 | stream->stream = msblk->decompressor->init(msblk, comp_opts); | ||
| 40 | if (IS_ERR(stream->stream)) { | ||
| 41 | err = PTR_ERR(stream->stream); | ||
| 42 | goto out; | ||
| 43 | } | ||
| 44 | |||
| 45 | kfree(comp_opts); | ||
| 46 | mutex_init(&stream->mutex); | ||
| 47 | return stream; | ||
| 48 | |||
| 49 | out: | ||
| 50 | kfree(stream); | ||
| 51 | return ERR_PTR(err); | ||
| 52 | } | ||
| 53 | |||
| 54 | void squashfs_decompressor_destroy(struct squashfs_sb_info *msblk) | ||
| 55 | { | ||
| 56 | struct squashfs_stream *stream = msblk->stream; | ||
| 57 | |||
| 58 | if (stream) { | ||
| 59 | msblk->decompressor->free(stream->stream); | ||
| 60 | kfree(stream); | ||
| 61 | } | ||
| 62 | } | ||
| 63 | |||
| 64 | int squashfs_decompress(struct squashfs_sb_info *msblk, struct buffer_head **bh, | ||
| 65 | int b, int offset, int length, struct squashfs_page_actor *output) | ||
| 66 | { | ||
| 67 | int res; | ||
| 68 | struct squashfs_stream *stream = msblk->stream; | ||
| 69 | |||
| 70 | mutex_lock(&stream->mutex); | ||
| 71 | res = msblk->decompressor->decompress(msblk, stream->stream, bh, b, | ||
| 72 | offset, length, output); | ||
| 73 | mutex_unlock(&stream->mutex); | ||
| 74 | |||
| 75 | if (res < 0) | ||
| 76 | ERROR("%s decompression failed, data probably corrupt\n", | ||
| 77 | msblk->decompressor->name); | ||
| 78 | |||
| 79 | return res; | ||
| 80 | } | ||
| 81 | |||
| 82 | int squashfs_max_decompressors(void) | ||
| 83 | { | ||
| 84 | return 1; | ||
| 85 | } | ||
diff --git a/fs/squashfs/file.c b/fs/squashfs/file.c index 8ca62c28fe12..e5c9689062ba 100644 --- a/fs/squashfs/file.c +++ b/fs/squashfs/file.c | |||
| @@ -370,77 +370,15 @@ static int read_blocklist(struct inode *inode, int index, u64 *block) | |||
| 370 | return le32_to_cpu(size); | 370 | return le32_to_cpu(size); |
| 371 | } | 371 | } |
| 372 | 372 | ||
| 373 | 373 | /* Copy data into page cache */ | |
| 374 | static int squashfs_readpage(struct file *file, struct page *page) | 374 | void squashfs_copy_cache(struct page *page, struct squashfs_cache_entry *buffer, |
| 375 | int bytes, int offset) | ||
| 375 | { | 376 | { |
| 376 | struct inode *inode = page->mapping->host; | 377 | struct inode *inode = page->mapping->host; |
| 377 | struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; | 378 | struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; |
| 378 | int bytes, i, offset = 0, sparse = 0; | ||
| 379 | struct squashfs_cache_entry *buffer = NULL; | ||
| 380 | void *pageaddr; | 379 | void *pageaddr; |
| 381 | 380 | int i, mask = (1 << (msblk->block_log - PAGE_CACHE_SHIFT)) - 1; | |
| 382 | int mask = (1 << (msblk->block_log - PAGE_CACHE_SHIFT)) - 1; | 381 | int start_index = page->index & ~mask, end_index = start_index | mask; |
| 383 | int index = page->index >> (msblk->block_log - PAGE_CACHE_SHIFT); | ||
| 384 | int start_index = page->index & ~mask; | ||
| 385 | int end_index = start_index | mask; | ||
| 386 | int file_end = i_size_read(inode) >> msblk->block_log; | ||
| 387 | |||
| 388 | TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n", | ||
| 389 | page->index, squashfs_i(inode)->start); | ||
| 390 | |||
| 391 | if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> | ||
| 392 | PAGE_CACHE_SHIFT)) | ||
| 393 | goto out; | ||
| 394 | |||
| 395 | if (index < file_end || squashfs_i(inode)->fragment_block == | ||
| 396 | SQUASHFS_INVALID_BLK) { | ||
| 397 | /* | ||
| 398 | * Reading a datablock from disk. Need to read block list | ||
| 399 | * to get location and block size. | ||
| 400 | */ | ||
| 401 | u64 block = 0; | ||
| 402 | int bsize = read_blocklist(inode, index, &block); | ||
| 403 | if (bsize < 0) | ||
| 404 | goto error_out; | ||
| 405 | |||
| 406 | if (bsize == 0) { /* hole */ | ||
| 407 | bytes = index == file_end ? | ||
| 408 | (i_size_read(inode) & (msblk->block_size - 1)) : | ||
| 409 | msblk->block_size; | ||
| 410 | sparse = 1; | ||
| 411 | } else { | ||
| 412 | /* | ||
| 413 | * Read and decompress datablock. | ||
| 414 | */ | ||
| 415 | buffer = squashfs_get_datablock(inode->i_sb, | ||
| 416 | block, bsize); | ||
| 417 | if (buffer->error) { | ||
| 418 | ERROR("Unable to read page, block %llx, size %x" | ||
| 419 | "\n", block, bsize); | ||
| 420 | squashfs_cache_put(buffer); | ||
| 421 | goto error_out; | ||
| 422 | } | ||
| 423 | bytes = buffer->length; | ||
| 424 | } | ||
| 425 | } else { | ||
| 426 | /* | ||
| 427 | * Datablock is stored inside a fragment (tail-end packed | ||
| 428 | * block). | ||
| 429 | */ | ||
| 430 | buffer = squashfs_get_fragment(inode->i_sb, | ||
| 431 | squashfs_i(inode)->fragment_block, | ||
| 432 | squashfs_i(inode)->fragment_size); | ||
| 433 | |||
| 434 | if (buffer->error) { | ||
| 435 | ERROR("Unable to read page, block %llx, size %x\n", | ||
| 436 | squashfs_i(inode)->fragment_block, | ||
| 437 | squashfs_i(inode)->fragment_size); | ||
| 438 | squashfs_cache_put(buffer); | ||
| 439 | goto error_out; | ||
| 440 | } | ||
| 441 | bytes = i_size_read(inode) & (msblk->block_size - 1); | ||
| 442 | offset = squashfs_i(inode)->fragment_offset; | ||
| 443 | } | ||
| 444 | 382 | ||
| 445 | /* | 383 | /* |
| 446 | * Loop copying datablock into pages. As the datablock likely covers | 384 | * Loop copying datablock into pages. As the datablock likely covers |
| @@ -451,7 +389,7 @@ static int squashfs_readpage(struct file *file, struct page *page) | |||
| 451 | for (i = start_index; i <= end_index && bytes > 0; i++, | 389 | for (i = start_index; i <= end_index && bytes > 0; i++, |
| 452 | bytes -= PAGE_CACHE_SIZE, offset += PAGE_CACHE_SIZE) { | 390 | bytes -= PAGE_CACHE_SIZE, offset += PAGE_CACHE_SIZE) { |
| 453 | struct page *push_page; | 391 | struct page *push_page; |
| 454 | int avail = sparse ? 0 : min_t(int, bytes, PAGE_CACHE_SIZE); | 392 | int avail = buffer ? min_t(int, bytes, PAGE_CACHE_SIZE) : 0; |
| 455 | 393 | ||
| 456 | TRACE("bytes %d, i %d, available_bytes %d\n", bytes, i, avail); | 394 | TRACE("bytes %d, i %d, available_bytes %d\n", bytes, i, avail); |
| 457 | 395 | ||
| @@ -475,11 +413,75 @@ skip_page: | |||
| 475 | if (i != page->index) | 413 | if (i != page->index) |
| 476 | page_cache_release(push_page); | 414 | page_cache_release(push_page); |
| 477 | } | 415 | } |
| 416 | } | ||
| 417 | |||
| 418 | /* Read datablock stored packed inside a fragment (tail-end packed block) */ | ||
| 419 | static int squashfs_readpage_fragment(struct page *page) | ||
| 420 | { | ||
| 421 | struct inode *inode = page->mapping->host; | ||
| 422 | struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; | ||
| 423 | struct squashfs_cache_entry *buffer = squashfs_get_fragment(inode->i_sb, | ||
| 424 | squashfs_i(inode)->fragment_block, | ||
| 425 | squashfs_i(inode)->fragment_size); | ||
| 426 | int res = buffer->error; | ||
| 427 | |||
| 428 | if (res) | ||
| 429 | ERROR("Unable to read page, block %llx, size %x\n", | ||
| 430 | squashfs_i(inode)->fragment_block, | ||
| 431 | squashfs_i(inode)->fragment_size); | ||
| 432 | else | ||
| 433 | squashfs_copy_cache(page, buffer, i_size_read(inode) & | ||
| 434 | (msblk->block_size - 1), | ||
| 435 | squashfs_i(inode)->fragment_offset); | ||
| 436 | |||
| 437 | squashfs_cache_put(buffer); | ||
| 438 | return res; | ||
| 439 | } | ||
| 478 | 440 | ||
| 479 | if (!sparse) | 441 | static int squashfs_readpage_sparse(struct page *page, int index, int file_end) |
| 480 | squashfs_cache_put(buffer); | 442 | { |
| 443 | struct inode *inode = page->mapping->host; | ||
| 444 | struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; | ||
| 445 | int bytes = index == file_end ? | ||
| 446 | (i_size_read(inode) & (msblk->block_size - 1)) : | ||
| 447 | msblk->block_size; | ||
| 481 | 448 | ||
| 449 | squashfs_copy_cache(page, NULL, bytes, 0); | ||
| 482 | return 0; | 450 | return 0; |
| 451 | } | ||
| 452 | |||
| 453 | static int squashfs_readpage(struct file *file, struct page *page) | ||
| 454 | { | ||
| 455 | struct inode *inode = page->mapping->host; | ||
| 456 | struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; | ||
| 457 | int index = page->index >> (msblk->block_log - PAGE_CACHE_SHIFT); | ||
| 458 | int file_end = i_size_read(inode) >> msblk->block_log; | ||
| 459 | int res; | ||
| 460 | void *pageaddr; | ||
| 461 | |||
| 462 | TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n", | ||
| 463 | page->index, squashfs_i(inode)->start); | ||
| 464 | |||
| 465 | if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> | ||
| 466 | PAGE_CACHE_SHIFT)) | ||
| 467 | goto out; | ||
| 468 | |||
| 469 | if (index < file_end || squashfs_i(inode)->fragment_block == | ||
| 470 | SQUASHFS_INVALID_BLK) { | ||
| 471 | u64 block = 0; | ||
| 472 | int bsize = read_blocklist(inode, index, &block); | ||
| 473 | if (bsize < 0) | ||
| 474 | goto error_out; | ||
| 475 | |||
| 476 | if (bsize == 0) | ||
| 477 | res = squashfs_readpage_sparse(page, index, file_end); | ||
| 478 | else | ||
| 479 | res = squashfs_readpage_block(page, block, bsize); | ||
| 480 | } else | ||
| 481 | res = squashfs_readpage_fragment(page); | ||
| 482 | |||
| 483 | if (!res) | ||
| 484 | return 0; | ||
| 483 | 485 | ||
| 484 | error_out: | 486 | error_out: |
| 485 | SetPageError(page); | 487 | SetPageError(page); |
diff --git a/fs/squashfs/file_cache.c b/fs/squashfs/file_cache.c new file mode 100644 index 000000000000..f2310d2a2019 --- /dev/null +++ b/fs/squashfs/file_cache.c | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2013 | ||
| 3 | * Phillip Lougher <phillip@squashfs.org.uk> | ||
| 4 | * | ||
| 5 | * This work is licensed under the terms of the GNU GPL, version 2. See | ||
| 6 | * the COPYING file in the top-level directory. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include <linux/fs.h> | ||
| 10 | #include <linux/vfs.h> | ||
| 11 | #include <linux/kernel.h> | ||
| 12 | #include <linux/slab.h> | ||
| 13 | #include <linux/string.h> | ||
| 14 | #include <linux/pagemap.h> | ||
| 15 | #include <linux/mutex.h> | ||
| 16 | |||
| 17 | #include "squashfs_fs.h" | ||
| 18 | #include "squashfs_fs_sb.h" | ||
| 19 | #include "squashfs_fs_i.h" | ||
| 20 | #include "squashfs.h" | ||
| 21 | |||
| 22 | /* Read separately compressed datablock and memcopy into page cache */ | ||
| 23 | int squashfs_readpage_block(struct page *page, u64 block, int bsize) | ||
| 24 | { | ||
| 25 | struct inode *i = page->mapping->host; | ||
| 26 | struct squashfs_cache_entry *buffer = squashfs_get_datablock(i->i_sb, | ||
| 27 | block, bsize); | ||
| 28 | int res = buffer->error; | ||
| 29 | |||
| 30 | if (res) | ||
| 31 | ERROR("Unable to read page, block %llx, size %x\n", block, | ||
| 32 | bsize); | ||
| 33 | else | ||
| 34 | squashfs_copy_cache(page, buffer, buffer->length, 0); | ||
| 35 | |||
| 36 | squashfs_cache_put(buffer); | ||
| 37 | return res; | ||
| 38 | } | ||
diff --git a/fs/squashfs/file_direct.c b/fs/squashfs/file_direct.c new file mode 100644 index 000000000000..2943b2bfae48 --- /dev/null +++ b/fs/squashfs/file_direct.c | |||
| @@ -0,0 +1,173 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2013 | ||
| 3 | * Phillip Lougher <phillip@squashfs.org.uk> | ||
| 4 | * | ||
| 5 | * This work is licensed under the terms of the GNU GPL, version 2. See | ||
| 6 | * the COPYING file in the top-level directory. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include <linux/fs.h> | ||
| 10 | #include <linux/vfs.h> | ||
| 11 | #include <linux/kernel.h> | ||
| 12 | #include <linux/slab.h> | ||
| 13 | #include <linux/string.h> | ||
| 14 | #include <linux/pagemap.h> | ||
| 15 | #include <linux/mutex.h> | ||
| 16 | |||
| 17 | #include "squashfs_fs.h" | ||
| 18 | #include "squashfs_fs_sb.h" | ||
| 19 | #include "squashfs_fs_i.h" | ||
| 20 | #include "squashfs.h" | ||
| 21 | #include "page_actor.h" | ||
| 22 | |||
| 23 | static int squashfs_read_cache(struct page *target_page, u64 block, int bsize, | ||
| 24 | int pages, struct page **page); | ||
| 25 | |||
| 26 | /* Read separately compressed datablock directly into page cache */ | ||
| 27 | int squashfs_readpage_block(struct page *target_page, u64 block, int bsize) | ||
| 28 | |||
| 29 | { | ||
| 30 | struct inode *inode = target_page->mapping->host; | ||
| 31 | struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; | ||
| 32 | |||
| 33 | int file_end = (i_size_read(inode) - 1) >> PAGE_CACHE_SHIFT; | ||
| 34 | int mask = (1 << (msblk->block_log - PAGE_CACHE_SHIFT)) - 1; | ||
| 35 | int start_index = target_page->index & ~mask; | ||
| 36 | int end_index = start_index | mask; | ||
| 37 | int i, n, pages, missing_pages, bytes, res = -ENOMEM; | ||
| 38 | struct page **page; | ||
| 39 | struct squashfs_page_actor *actor; | ||
| 40 | void *pageaddr; | ||
| 41 | |||
| 42 | if (end_index > file_end) | ||
| 43 | end_index = file_end; | ||
| 44 | |||
| 45 | pages = end_index - start_index + 1; | ||
| 46 | |||
| 47 | page = kmalloc(sizeof(void *) * pages, GFP_KERNEL); | ||
| 48 | if (page == NULL) | ||
| 49 | return res; | ||
| 50 | |||
| 51 | /* | ||
| 52 | * Create a "page actor" which will kmap and kunmap the | ||
| 53 | * page cache pages appropriately within the decompressor | ||
| 54 | */ | ||
| 55 | actor = squashfs_page_actor_init_special(page, pages, 0); | ||
| 56 | if (actor == NULL) | ||
| 57 | goto out; | ||
| 58 | |||
| 59 | /* Try to grab all the pages covered by the Squashfs block */ | ||
| 60 | for (missing_pages = 0, i = 0, n = start_index; i < pages; i++, n++) { | ||
| 61 | page[i] = (n == target_page->index) ? target_page : | ||
| 62 | grab_cache_page_nowait(target_page->mapping, n); | ||
| 63 | |||
| 64 | if (page[i] == NULL) { | ||
| 65 | missing_pages++; | ||
| 66 | continue; | ||
| 67 | } | ||
| 68 | |||
| 69 | if (PageUptodate(page[i])) { | ||
| 70 | unlock_page(page[i]); | ||
| 71 | page_cache_release(page[i]); | ||
| 72 | page[i] = NULL; | ||
| 73 | missing_pages++; | ||
| 74 | } | ||
| 75 | } | ||
| 76 | |||
| 77 | if (missing_pages) { | ||
| 78 | /* | ||
| 79 | * Couldn't get one or more pages, this page has either | ||
| 80 | * been VM reclaimed, but others are still in the page cache | ||
| 81 | * and uptodate, or we're racing with another thread in | ||
| 82 | * squashfs_readpage also trying to grab them. Fall back to | ||
| 83 | * using an intermediate buffer. | ||
| 84 | */ | ||
| 85 | res = squashfs_read_cache(target_page, block, bsize, pages, | ||
| 86 | page); | ||
| 87 | goto out; | ||
| 88 | } | ||
| 89 | |||
| 90 | /* Decompress directly into the page cache buffers */ | ||
| 91 | res = squashfs_read_data(inode->i_sb, block, bsize, NULL, actor); | ||
| 92 | if (res < 0) | ||
| 93 | goto mark_errored; | ||
| 94 | |||
| 95 | /* Last page may have trailing bytes not filled */ | ||
| 96 | bytes = res % PAGE_CACHE_SIZE; | ||
| 97 | if (bytes) { | ||
| 98 | pageaddr = kmap_atomic(page[pages - 1]); | ||
| 99 | memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes); | ||
| 100 | kunmap_atomic(pageaddr); | ||
| 101 | } | ||
| 102 | |||
| 103 | /* Mark pages as uptodate, unlock and release */ | ||
| 104 | for (i = 0; i < pages; i++) { | ||
| 105 | flush_dcache_page(page[i]); | ||
| 106 | SetPageUptodate(page[i]); | ||
| 107 | unlock_page(page[i]); | ||
| 108 | if (page[i] != target_page) | ||
| 109 | page_cache_release(page[i]); | ||
| 110 | } | ||
| 111 | |||
| 112 | kfree(actor); | ||
| 113 | kfree(page); | ||
| 114 | |||
| 115 | return 0; | ||
| 116 | |||
| 117 | mark_errored: | ||
| 118 | /* Decompression failed, mark pages as errored. Target_page is | ||
| 119 | * dealt with by the caller | ||
| 120 | */ | ||
| 121 | for (i = 0; i < pages; i++) { | ||
| 122 | if (page[i] == target_page) | ||
| 123 | continue; | ||
| 124 | flush_dcache_page(page[i]); | ||
| 125 | SetPageError(page[i]); | ||
| 126 | unlock_page(page[i]); | ||
| 127 | page_cache_release(page[i]); | ||
| 128 | } | ||
| 129 | |||
| 130 | out: | ||
| 131 | kfree(actor); | ||
| 132 | kfree(page); | ||
| 133 | return res; | ||
| 134 | } | ||
| 135 | |||
| 136 | |||
| 137 | static int squashfs_read_cache(struct page *target_page, u64 block, int bsize, | ||
| 138 | int pages, struct page **page) | ||
| 139 | { | ||
| 140 | struct inode *i = target_page->mapping->host; | ||
| 141 | struct squashfs_cache_entry *buffer = squashfs_get_datablock(i->i_sb, | ||
| 142 | block, bsize); | ||
| 143 | int bytes = buffer->length, res = buffer->error, n, offset = 0; | ||
| 144 | void *pageaddr; | ||
| 145 | |||
| 146 | if (res) { | ||
| 147 | ERROR("Unable to read page, block %llx, size %x\n", block, | ||
| 148 | bsize); | ||
| 149 | goto out; | ||
| 150 | } | ||
| 151 | |||
| 152 | for (n = 0; n < pages && bytes > 0; n++, | ||
| 153 | bytes -= PAGE_CACHE_SIZE, offset += PAGE_CACHE_SIZE) { | ||
| 154 | int avail = min_t(int, bytes, PAGE_CACHE_SIZE); | ||
| 155 | |||
| 156 | if (page[n] == NULL) | ||
| 157 | continue; | ||
| 158 | |||
| 159 | pageaddr = kmap_atomic(page[n]); | ||
| 160 | squashfs_copy_data(pageaddr, buffer, offset, avail); | ||
| 161 | memset(pageaddr + avail, 0, PAGE_CACHE_SIZE - avail); | ||
| 162 | kunmap_atomic(pageaddr); | ||
| 163 | flush_dcache_page(page[n]); | ||
| 164 | SetPageUptodate(page[n]); | ||
| 165 | unlock_page(page[n]); | ||
| 166 | if (page[n] != target_page) | ||
| 167 | page_cache_release(page[n]); | ||
| 168 | } | ||
| 169 | |||
| 170 | out: | ||
| 171 | squashfs_cache_put(buffer); | ||
| 172 | return res; | ||
| 173 | } | ||
diff --git a/fs/squashfs/lzo_wrapper.c b/fs/squashfs/lzo_wrapper.c index 00f4dfc5f088..244b9fbfff7b 100644 --- a/fs/squashfs/lzo_wrapper.c +++ b/fs/squashfs/lzo_wrapper.c | |||
| @@ -31,13 +31,14 @@ | |||
| 31 | #include "squashfs_fs_sb.h" | 31 | #include "squashfs_fs_sb.h" |
| 32 | #include "squashfs.h" | 32 | #include "squashfs.h" |
| 33 | #include "decompressor.h" | 33 | #include "decompressor.h" |
| 34 | #include "page_actor.h" | ||
| 34 | 35 | ||
| 35 | struct squashfs_lzo { | 36 | struct squashfs_lzo { |
| 36 | void *input; | 37 | void *input; |
| 37 | void *output; | 38 | void *output; |
| 38 | }; | 39 | }; |
| 39 | 40 | ||
| 40 | static void *lzo_init(struct squashfs_sb_info *msblk, void *buff, int len) | 41 | static void *lzo_init(struct squashfs_sb_info *msblk, void *buff) |
| 41 | { | 42 | { |
| 42 | int block_size = max_t(int, msblk->block_size, SQUASHFS_METADATA_SIZE); | 43 | int block_size = max_t(int, msblk->block_size, SQUASHFS_METADATA_SIZE); |
| 43 | 44 | ||
| @@ -74,22 +75,16 @@ static void lzo_free(void *strm) | |||
| 74 | } | 75 | } |
| 75 | 76 | ||
| 76 | 77 | ||
| 77 | static int lzo_uncompress(struct squashfs_sb_info *msblk, void **buffer, | 78 | static int lzo_uncompress(struct squashfs_sb_info *msblk, void *strm, |
| 78 | struct buffer_head **bh, int b, int offset, int length, int srclength, | 79 | struct buffer_head **bh, int b, int offset, int length, |
| 79 | int pages) | 80 | struct squashfs_page_actor *output) |
| 80 | { | 81 | { |
| 81 | struct squashfs_lzo *stream = msblk->stream; | 82 | struct squashfs_lzo *stream = strm; |
| 82 | void *buff = stream->input; | 83 | void *buff = stream->input, *data; |
| 83 | int avail, i, bytes = length, res; | 84 | int avail, i, bytes = length, res; |
| 84 | size_t out_len = srclength; | 85 | size_t out_len = output->length; |
| 85 | |||
| 86 | mutex_lock(&msblk->read_data_mutex); | ||
| 87 | 86 | ||
| 88 | for (i = 0; i < b; i++) { | 87 | for (i = 0; i < b; i++) { |
| 89 | wait_on_buffer(bh[i]); | ||
| 90 | if (!buffer_uptodate(bh[i])) | ||
| 91 | goto block_release; | ||
| 92 | |||
| 93 | avail = min(bytes, msblk->devblksize - offset); | 88 | avail = min(bytes, msblk->devblksize - offset); |
| 94 | memcpy(buff, bh[i]->b_data + offset, avail); | 89 | memcpy(buff, bh[i]->b_data + offset, avail); |
| 95 | buff += avail; | 90 | buff += avail; |
| @@ -104,24 +99,24 @@ static int lzo_uncompress(struct squashfs_sb_info *msblk, void **buffer, | |||
| 104 | goto failed; | 99 | goto failed; |
| 105 | 100 | ||
| 106 | res = bytes = (int)out_len; | 101 | res = bytes = (int)out_len; |
| 107 | for (i = 0, buff = stream->output; bytes && i < pages; i++) { | 102 | data = squashfs_first_page(output); |
| 108 | avail = min_t(int, bytes, PAGE_CACHE_SIZE); | 103 | buff = stream->output; |
| 109 | memcpy(buffer[i], buff, avail); | 104 | while (data) { |
| 110 | buff += avail; | 105 | if (bytes <= PAGE_CACHE_SIZE) { |
| 111 | bytes -= avail; | 106 | memcpy(data, buff, bytes); |
| 107 | break; | ||
| 108 | } else { | ||
| 109 | memcpy(data, buff, PAGE_CACHE_SIZE); | ||
| 110 | buff += PAGE_CACHE_SIZE; | ||
| 111 | bytes -= PAGE_CACHE_SIZE; | ||
| 112 | data = squashfs_next_page(output); | ||
| 113 | } | ||
| 112 | } | 114 | } |
| 115 | squashfs_finish_page(output); | ||
| 113 | 116 | ||
| 114 | mutex_unlock(&msblk->read_data_mutex); | ||
| 115 | return res; | 117 | return res; |
| 116 | 118 | ||
| 117 | block_release: | ||
| 118 | for (; i < b; i++) | ||
| 119 | put_bh(bh[i]); | ||
| 120 | |||
| 121 | failed: | 119 | failed: |
| 122 | mutex_unlock(&msblk->read_data_mutex); | ||
| 123 | |||
| 124 | ERROR("lzo decompression failed, data probably corrupt\n"); | ||
| 125 | return -EIO; | 120 | return -EIO; |
| 126 | } | 121 | } |
| 127 | 122 | ||
diff --git a/fs/squashfs/page_actor.c b/fs/squashfs/page_actor.c new file mode 100644 index 000000000000..5a1c11f56441 --- /dev/null +++ b/fs/squashfs/page_actor.c | |||
| @@ -0,0 +1,100 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (c) 2013 | ||
| 3 | * Phillip Lougher <phillip@squashfs.org.uk> | ||
| 4 | * | ||
| 5 | * This work is licensed under the terms of the GNU GPL, version 2. See | ||
| 6 | * the COPYING file in the top-level directory. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include <linux/kernel.h> | ||
| 10 | #include <linux/slab.h> | ||
| 11 | #include <linux/pagemap.h> | ||
| 12 | #include "page_actor.h" | ||
| 13 | |||
| 14 | /* | ||
| 15 | * This file contains implementations of page_actor for decompressing into | ||
| 16 | * an intermediate buffer, and for decompressing directly into the | ||
| 17 | * page cache. | ||
| 18 | * | ||
| 19 | * Calling code should avoid sleeping between calls to squashfs_first_page() | ||
| 20 | * and squashfs_finish_page(). | ||
| 21 | */ | ||
| 22 | |||
| 23 | /* Implementation of page_actor for decompressing into intermediate buffer */ | ||
| 24 | static void *cache_first_page(struct squashfs_page_actor *actor) | ||
| 25 | { | ||
| 26 | actor->next_page = 1; | ||
| 27 | return actor->buffer[0]; | ||
| 28 | } | ||
| 29 | |||
| 30 | static void *cache_next_page(struct squashfs_page_actor *actor) | ||
| 31 | { | ||
| 32 | if (actor->next_page == actor->pages) | ||
| 33 | return NULL; | ||
| 34 | |||
| 35 | return actor->buffer[actor->next_page++]; | ||
| 36 | } | ||
| 37 | |||
| 38 | static void cache_finish_page(struct squashfs_page_actor *actor) | ||
| 39 | { | ||
| 40 | /* empty */ | ||
| 41 | } | ||
| 42 | |||
| 43 | struct squashfs_page_actor *squashfs_page_actor_init(void **buffer, | ||
| 44 | int pages, int length) | ||
| 45 | { | ||
| 46 | struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL); | ||
| 47 | |||
| 48 | if (actor == NULL) | ||
| 49 | return NULL; | ||
| 50 | |||
| 51 | actor->length = length ? : pages * PAGE_CACHE_SIZE; | ||
| 52 | actor->buffer = buffer; | ||
| 53 | actor->pages = pages; | ||
| 54 | actor->next_page = 0; | ||
| 55 | actor->squashfs_first_page = cache_first_page; | ||
| 56 | actor->squashfs_next_page = cache_next_page; | ||
| 57 | actor->squashfs_finish_page = cache_finish_page; | ||
| 58 | return actor; | ||
| 59 | } | ||
| 60 | |||
| 61 | /* Implementation of page_actor for decompressing directly into page cache. */ | ||
| 62 | static void *direct_first_page(struct squashfs_page_actor *actor) | ||
| 63 | { | ||
| 64 | actor->next_page = 1; | ||
| 65 | return actor->pageaddr = kmap_atomic(actor->page[0]); | ||
| 66 | } | ||
| 67 | |||
| 68 | static void *direct_next_page(struct squashfs_page_actor *actor) | ||
| 69 | { | ||
| 70 | if (actor->pageaddr) | ||
| 71 | kunmap_atomic(actor->pageaddr); | ||
| 72 | |||
| 73 | return actor->pageaddr = actor->next_page == actor->pages ? NULL : | ||
| 74 | kmap_atomic(actor->page[actor->next_page++]); | ||
| 75 | } | ||
| 76 | |||
| 77 | static void direct_finish_page(struct squashfs_page_actor *actor) | ||
| 78 | { | ||
| 79 | if (actor->pageaddr) | ||
| 80 | kunmap_atomic(actor->pageaddr); | ||
| 81 | } | ||
| 82 | |||
| 83 | struct squashfs_page_actor *squashfs_page_actor_init_special(struct page **page, | ||
| 84 | int pages, int length) | ||
| 85 | { | ||
| 86 | struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL); | ||
| 87 | |||
| 88 | if (actor == NULL) | ||
| 89 | return NULL; | ||
| 90 | |||
| 91 | actor->length = length ? : pages * PAGE_CACHE_SIZE; | ||
| 92 | actor->page = page; | ||
| 93 | actor->pages = pages; | ||
| 94 | actor->next_page = 0; | ||
| 95 | actor->pageaddr = NULL; | ||
| 96 | actor->squashfs_first_page = direct_first_page; | ||
| 97 | actor->squashfs_next_page = direct_next_page; | ||
| 98 | actor->squashfs_finish_page = direct_finish_page; | ||
| 99 | return actor; | ||
| 100 | } | ||
diff --git a/fs/squashfs/page_actor.h b/fs/squashfs/page_actor.h new file mode 100644 index 000000000000..26dd82008b82 --- /dev/null +++ b/fs/squashfs/page_actor.h | |||
| @@ -0,0 +1,81 @@ | |||
| 1 | #ifndef PAGE_ACTOR_H | ||
| 2 | #define PAGE_ACTOR_H | ||
| 3 | /* | ||
| 4 | * Copyright (c) 2013 | ||
| 5 | * Phillip Lougher <phillip@squashfs.org.uk> | ||
| 6 | * | ||
| 7 | * This work is licensed under the terms of the GNU GPL, version 2. See | ||
| 8 | * the COPYING file in the top-level directory. | ||
| 9 | */ | ||
| 10 | |||
| 11 | #ifndef CONFIG_SQUASHFS_FILE_DIRECT | ||
| 12 | struct squashfs_page_actor { | ||
| 13 | void **page; | ||
| 14 | int pages; | ||
| 15 | int length; | ||
| 16 | int next_page; | ||
| 17 | }; | ||
| 18 | |||
| 19 | static inline struct squashfs_page_actor *squashfs_page_actor_init(void **page, | ||
| 20 | int pages, int length) | ||
| 21 | { | ||
| 22 | struct squashfs_page_actor *actor = kmalloc(sizeof(*actor), GFP_KERNEL); | ||
| 23 | |||
| 24 | if (actor == NULL) | ||
| 25 | return NULL; | ||
| 26 | |||
| 27 | actor->length = length ? : pages * PAGE_CACHE_SIZE; | ||
| 28 | actor->page = page; | ||
| 29 | actor->pages = pages; | ||
| 30 | actor->next_page = 0; | ||
| 31 | return actor; | ||
| 32 | } | ||
| 33 | |||
| 34 | static inline void *squashfs_first_page(struct squashfs_page_actor *actor) | ||
| 35 | { | ||
| 36 | actor->next_page = 1; | ||
| 37 | return actor->page[0]; | ||
| 38 | } | ||
| 39 | |||
| 40 | static inline void *squashfs_next_page(struct squashfs_page_actor *actor) | ||
| 41 | { | ||
| 42 | return actor->next_page == actor->pages ? NULL : | ||
| 43 | actor->page[actor->next_page++]; | ||
| 44 | } | ||
| 45 | |||
| 46 | static inline void squashfs_finish_page(struct squashfs_page_actor *actor) | ||
| 47 | { | ||
| 48 | /* empty */ | ||
| 49 | } | ||
| 50 | #else | ||
| 51 | struct squashfs_page_actor { | ||
| 52 | union { | ||
| 53 | void **buffer; | ||
| 54 | struct page **page; | ||
| 55 | }; | ||
| 56 | void *pageaddr; | ||
| 57 | void *(*squashfs_first_page)(struct squashfs_page_actor *); | ||
| 58 | void *(*squashfs_next_page)(struct squashfs_page_actor *); | ||
| 59 | void (*squashfs_finish_page)(struct squashfs_page_actor *); | ||
| 60 | int pages; | ||
| 61 | int length; | ||
| 62 | int next_page; | ||
| 63 | }; | ||
| 64 | |||
| 65 | extern struct squashfs_page_actor *squashfs_page_actor_init(void **, int, int); | ||
| 66 | extern struct squashfs_page_actor *squashfs_page_actor_init_special(struct page | ||
| 67 | **, int, int); | ||
| 68 | static inline void *squashfs_first_page(struct squashfs_page_actor *actor) | ||
| 69 | { | ||
| 70 | return actor->squashfs_first_page(actor); | ||
| 71 | } | ||
| 72 | static inline void *squashfs_next_page(struct squashfs_page_actor *actor) | ||
| 73 | { | ||
| 74 | return actor->squashfs_next_page(actor); | ||
| 75 | } | ||
| 76 | static inline void squashfs_finish_page(struct squashfs_page_actor *actor) | ||
| 77 | { | ||
| 78 | actor->squashfs_finish_page(actor); | ||
| 79 | } | ||
| 80 | #endif | ||
| 81 | #endif | ||
diff --git a/fs/squashfs/squashfs.h b/fs/squashfs/squashfs.h index d1266516ed08..9e1bb79f7e6f 100644 --- a/fs/squashfs/squashfs.h +++ b/fs/squashfs/squashfs.h | |||
| @@ -28,8 +28,8 @@ | |||
| 28 | #define WARNING(s, args...) pr_warning("SQUASHFS: "s, ## args) | 28 | #define WARNING(s, args...) pr_warning("SQUASHFS: "s, ## args) |
| 29 | 29 | ||
| 30 | /* block.c */ | 30 | /* block.c */ |
| 31 | extern int squashfs_read_data(struct super_block *, void **, u64, int, u64 *, | 31 | extern int squashfs_read_data(struct super_block *, u64, int, u64 *, |
| 32 | int, int); | 32 | struct squashfs_page_actor *); |
| 33 | 33 | ||
| 34 | /* cache.c */ | 34 | /* cache.c */ |
| 35 | extern struct squashfs_cache *squashfs_cache_init(char *, int, int); | 35 | extern struct squashfs_cache *squashfs_cache_init(char *, int, int); |
| @@ -48,7 +48,14 @@ extern void *squashfs_read_table(struct super_block *, u64, int); | |||
| 48 | 48 | ||
| 49 | /* decompressor.c */ | 49 | /* decompressor.c */ |
| 50 | extern const struct squashfs_decompressor *squashfs_lookup_decompressor(int); | 50 | extern const struct squashfs_decompressor *squashfs_lookup_decompressor(int); |
| 51 | extern void *squashfs_decompressor_init(struct super_block *, unsigned short); | 51 | extern void *squashfs_decompressor_setup(struct super_block *, unsigned short); |
| 52 | |||
| 53 | /* decompressor_xxx.c */ | ||
| 54 | extern void *squashfs_decompressor_create(struct squashfs_sb_info *, void *); | ||
| 55 | extern void squashfs_decompressor_destroy(struct squashfs_sb_info *); | ||
| 56 | extern int squashfs_decompress(struct squashfs_sb_info *, struct buffer_head **, | ||
| 57 | int, int, int, struct squashfs_page_actor *); | ||
| 58 | extern int squashfs_max_decompressors(void); | ||
| 52 | 59 | ||
| 53 | /* export.c */ | 60 | /* export.c */ |
| 54 | extern __le64 *squashfs_read_inode_lookup_table(struct super_block *, u64, u64, | 61 | extern __le64 *squashfs_read_inode_lookup_table(struct super_block *, u64, u64, |
| @@ -59,6 +66,13 @@ extern int squashfs_frag_lookup(struct super_block *, unsigned int, u64 *); | |||
| 59 | extern __le64 *squashfs_read_fragment_index_table(struct super_block *, | 66 | extern __le64 *squashfs_read_fragment_index_table(struct super_block *, |
| 60 | u64, u64, unsigned int); | 67 | u64, u64, unsigned int); |
| 61 | 68 | ||
| 69 | /* file.c */ | ||
| 70 | void squashfs_copy_cache(struct page *, struct squashfs_cache_entry *, int, | ||
| 71 | int); | ||
| 72 | |||
| 73 | /* file_xxx.c */ | ||
| 74 | extern int squashfs_readpage_block(struct page *, u64, int); | ||
| 75 | |||
| 62 | /* id.c */ | 76 | /* id.c */ |
| 63 | extern int squashfs_get_id(struct super_block *, unsigned int, unsigned int *); | 77 | extern int squashfs_get_id(struct super_block *, unsigned int, unsigned int *); |
| 64 | extern __le64 *squashfs_read_id_index_table(struct super_block *, u64, u64, | 78 | extern __le64 *squashfs_read_id_index_table(struct super_block *, u64, u64, |
diff --git a/fs/squashfs/squashfs_fs_sb.h b/fs/squashfs/squashfs_fs_sb.h index 52934a22f296..1da565cb50c3 100644 --- a/fs/squashfs/squashfs_fs_sb.h +++ b/fs/squashfs/squashfs_fs_sb.h | |||
| @@ -50,6 +50,7 @@ struct squashfs_cache_entry { | |||
| 50 | wait_queue_head_t wait_queue; | 50 | wait_queue_head_t wait_queue; |
| 51 | struct squashfs_cache *cache; | 51 | struct squashfs_cache *cache; |
| 52 | void **data; | 52 | void **data; |
| 53 | struct squashfs_page_actor *actor; | ||
| 53 | }; | 54 | }; |
| 54 | 55 | ||
| 55 | struct squashfs_sb_info { | 56 | struct squashfs_sb_info { |
| @@ -63,10 +64,9 @@ struct squashfs_sb_info { | |||
| 63 | __le64 *id_table; | 64 | __le64 *id_table; |
| 64 | __le64 *fragment_index; | 65 | __le64 *fragment_index; |
| 65 | __le64 *xattr_id_table; | 66 | __le64 *xattr_id_table; |
| 66 | struct mutex read_data_mutex; | ||
| 67 | struct mutex meta_index_mutex; | 67 | struct mutex meta_index_mutex; |
| 68 | struct meta_index *meta_index; | 68 | struct meta_index *meta_index; |
| 69 | void *stream; | 69 | struct squashfs_stream *stream; |
| 70 | __le64 *inode_lookup_table; | 70 | __le64 *inode_lookup_table; |
| 71 | u64 inode_table; | 71 | u64 inode_table; |
| 72 | u64 directory_table; | 72 | u64 directory_table; |
diff --git a/fs/squashfs/super.c b/fs/squashfs/super.c index 60553a9053ca..202df6312d4e 100644 --- a/fs/squashfs/super.c +++ b/fs/squashfs/super.c | |||
| @@ -98,7 +98,6 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent) | |||
| 98 | msblk->devblksize = sb_min_blocksize(sb, SQUASHFS_DEVBLK_SIZE); | 98 | msblk->devblksize = sb_min_blocksize(sb, SQUASHFS_DEVBLK_SIZE); |
| 99 | msblk->devblksize_log2 = ffz(~msblk->devblksize); | 99 | msblk->devblksize_log2 = ffz(~msblk->devblksize); |
| 100 | 100 | ||
| 101 | mutex_init(&msblk->read_data_mutex); | ||
| 102 | mutex_init(&msblk->meta_index_mutex); | 101 | mutex_init(&msblk->meta_index_mutex); |
| 103 | 102 | ||
| 104 | /* | 103 | /* |
| @@ -206,13 +205,14 @@ static int squashfs_fill_super(struct super_block *sb, void *data, int silent) | |||
| 206 | goto failed_mount; | 205 | goto failed_mount; |
| 207 | 206 | ||
| 208 | /* Allocate read_page block */ | 207 | /* Allocate read_page block */ |
| 209 | msblk->read_page = squashfs_cache_init("data", 1, msblk->block_size); | 208 | msblk->read_page = squashfs_cache_init("data", |
| 209 | squashfs_max_decompressors(), msblk->block_size); | ||
| 210 | if (msblk->read_page == NULL) { | 210 | if (msblk->read_page == NULL) { |
| 211 | ERROR("Failed to allocate read_page block\n"); | 211 | ERROR("Failed to allocate read_page block\n"); |
| 212 | goto failed_mount; | 212 | goto failed_mount; |
| 213 | } | 213 | } |
| 214 | 214 | ||
| 215 | msblk->stream = squashfs_decompressor_init(sb, flags); | 215 | msblk->stream = squashfs_decompressor_setup(sb, flags); |
| 216 | if (IS_ERR(msblk->stream)) { | 216 | if (IS_ERR(msblk->stream)) { |
| 217 | err = PTR_ERR(msblk->stream); | 217 | err = PTR_ERR(msblk->stream); |
| 218 | msblk->stream = NULL; | 218 | msblk->stream = NULL; |
| @@ -336,7 +336,7 @@ failed_mount: | |||
| 336 | squashfs_cache_delete(msblk->block_cache); | 336 | squashfs_cache_delete(msblk->block_cache); |
| 337 | squashfs_cache_delete(msblk->fragment_cache); | 337 | squashfs_cache_delete(msblk->fragment_cache); |
| 338 | squashfs_cache_delete(msblk->read_page); | 338 | squashfs_cache_delete(msblk->read_page); |
| 339 | squashfs_decompressor_free(msblk, msblk->stream); | 339 | squashfs_decompressor_destroy(msblk); |
| 340 | kfree(msblk->inode_lookup_table); | 340 | kfree(msblk->inode_lookup_table); |
| 341 | kfree(msblk->fragment_index); | 341 | kfree(msblk->fragment_index); |
| 342 | kfree(msblk->id_table); | 342 | kfree(msblk->id_table); |
| @@ -383,7 +383,7 @@ static void squashfs_put_super(struct super_block *sb) | |||
| 383 | squashfs_cache_delete(sbi->block_cache); | 383 | squashfs_cache_delete(sbi->block_cache); |
| 384 | squashfs_cache_delete(sbi->fragment_cache); | 384 | squashfs_cache_delete(sbi->fragment_cache); |
| 385 | squashfs_cache_delete(sbi->read_page); | 385 | squashfs_cache_delete(sbi->read_page); |
| 386 | squashfs_decompressor_free(sbi, sbi->stream); | 386 | squashfs_decompressor_destroy(sbi); |
| 387 | kfree(sbi->id_table); | 387 | kfree(sbi->id_table); |
| 388 | kfree(sbi->fragment_index); | 388 | kfree(sbi->fragment_index); |
| 389 | kfree(sbi->meta_index); | 389 | kfree(sbi->meta_index); |
diff --git a/fs/squashfs/xz_wrapper.c b/fs/squashfs/xz_wrapper.c index 1760b7d108f6..c609624e4b8a 100644 --- a/fs/squashfs/xz_wrapper.c +++ b/fs/squashfs/xz_wrapper.c | |||
| @@ -32,44 +32,70 @@ | |||
| 32 | #include "squashfs_fs_sb.h" | 32 | #include "squashfs_fs_sb.h" |
| 33 | #include "squashfs.h" | 33 | #include "squashfs.h" |
| 34 | #include "decompressor.h" | 34 | #include "decompressor.h" |
| 35 | #include "page_actor.h" | ||
| 35 | 36 | ||
| 36 | struct squashfs_xz { | 37 | struct squashfs_xz { |
| 37 | struct xz_dec *state; | 38 | struct xz_dec *state; |
| 38 | struct xz_buf buf; | 39 | struct xz_buf buf; |
| 39 | }; | 40 | }; |
| 40 | 41 | ||
| 41 | struct comp_opts { | 42 | struct disk_comp_opts { |
| 42 | __le32 dictionary_size; | 43 | __le32 dictionary_size; |
| 43 | __le32 flags; | 44 | __le32 flags; |
| 44 | }; | 45 | }; |
| 45 | 46 | ||
| 46 | static void *squashfs_xz_init(struct squashfs_sb_info *msblk, void *buff, | 47 | struct comp_opts { |
| 47 | int len) | 48 | int dict_size; |
| 49 | }; | ||
| 50 | |||
| 51 | static void *squashfs_xz_comp_opts(struct squashfs_sb_info *msblk, | ||
| 52 | void *buff, int len) | ||
| 48 | { | 53 | { |
| 49 | struct comp_opts *comp_opts = buff; | 54 | struct disk_comp_opts *comp_opts = buff; |
| 50 | struct squashfs_xz *stream; | 55 | struct comp_opts *opts; |
| 51 | int dict_size = msblk->block_size; | 56 | int err = 0, n; |
| 52 | int err, n; | 57 | |
| 58 | opts = kmalloc(sizeof(*opts), GFP_KERNEL); | ||
| 59 | if (opts == NULL) { | ||
| 60 | err = -ENOMEM; | ||
| 61 | goto out2; | ||
| 62 | } | ||
| 53 | 63 | ||
| 54 | if (comp_opts) { | 64 | if (comp_opts) { |
| 55 | /* check compressor options are the expected length */ | 65 | /* check compressor options are the expected length */ |
| 56 | if (len < sizeof(*comp_opts)) { | 66 | if (len < sizeof(*comp_opts)) { |
| 57 | err = -EIO; | 67 | err = -EIO; |
| 58 | goto failed; | 68 | goto out; |
| 59 | } | 69 | } |
| 60 | 70 | ||
| 61 | dict_size = le32_to_cpu(comp_opts->dictionary_size); | 71 | opts->dict_size = le32_to_cpu(comp_opts->dictionary_size); |
| 62 | 72 | ||
| 63 | /* the dictionary size should be 2^n or 2^n+2^(n+1) */ | 73 | /* the dictionary size should be 2^n or 2^n+2^(n+1) */ |
| 64 | n = ffs(dict_size) - 1; | 74 | n = ffs(opts->dict_size) - 1; |
| 65 | if (dict_size != (1 << n) && dict_size != (1 << n) + | 75 | if (opts->dict_size != (1 << n) && opts->dict_size != (1 << n) + |
| 66 | (1 << (n + 1))) { | 76 | (1 << (n + 1))) { |
| 67 | err = -EIO; | 77 | err = -EIO; |
| 68 | goto failed; | 78 | goto out; |
| 69 | } | 79 | } |
| 70 | } | 80 | } else |
| 81 | /* use defaults */ | ||
| 82 | opts->dict_size = max_t(int, msblk->block_size, | ||
| 83 | SQUASHFS_METADATA_SIZE); | ||
| 84 | |||
| 85 | return opts; | ||
| 86 | |||
| 87 | out: | ||
| 88 | kfree(opts); | ||
| 89 | out2: | ||
| 90 | return ERR_PTR(err); | ||
| 91 | } | ||
| 92 | |||
| 71 | 93 | ||
| 72 | dict_size = max_t(int, dict_size, SQUASHFS_METADATA_SIZE); | 94 | static void *squashfs_xz_init(struct squashfs_sb_info *msblk, void *buff) |
| 95 | { | ||
| 96 | struct comp_opts *comp_opts = buff; | ||
| 97 | struct squashfs_xz *stream; | ||
| 98 | int err; | ||
| 73 | 99 | ||
| 74 | stream = kmalloc(sizeof(*stream), GFP_KERNEL); | 100 | stream = kmalloc(sizeof(*stream), GFP_KERNEL); |
| 75 | if (stream == NULL) { | 101 | if (stream == NULL) { |
| @@ -77,7 +103,7 @@ static void *squashfs_xz_init(struct squashfs_sb_info *msblk, void *buff, | |||
| 77 | goto failed; | 103 | goto failed; |
| 78 | } | 104 | } |
| 79 | 105 | ||
| 80 | stream->state = xz_dec_init(XZ_PREALLOC, dict_size); | 106 | stream->state = xz_dec_init(XZ_PREALLOC, comp_opts->dict_size); |
| 81 | if (stream->state == NULL) { | 107 | if (stream->state == NULL) { |
| 82 | kfree(stream); | 108 | kfree(stream); |
| 83 | err = -ENOMEM; | 109 | err = -ENOMEM; |
| @@ -103,42 +129,37 @@ static void squashfs_xz_free(void *strm) | |||
| 103 | } | 129 | } |
| 104 | 130 | ||
| 105 | 131 | ||
| 106 | static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void **buffer, | 132 | static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void *strm, |
| 107 | struct buffer_head **bh, int b, int offset, int length, int srclength, | 133 | struct buffer_head **bh, int b, int offset, int length, |
| 108 | int pages) | 134 | struct squashfs_page_actor *output) |
| 109 | { | 135 | { |
| 110 | enum xz_ret xz_err; | 136 | enum xz_ret xz_err; |
| 111 | int avail, total = 0, k = 0, page = 0; | 137 | int avail, total = 0, k = 0; |
| 112 | struct squashfs_xz *stream = msblk->stream; | 138 | struct squashfs_xz *stream = strm; |
| 113 | |||
| 114 | mutex_lock(&msblk->read_data_mutex); | ||
| 115 | 139 | ||
| 116 | xz_dec_reset(stream->state); | 140 | xz_dec_reset(stream->state); |
| 117 | stream->buf.in_pos = 0; | 141 | stream->buf.in_pos = 0; |
| 118 | stream->buf.in_size = 0; | 142 | stream->buf.in_size = 0; |
| 119 | stream->buf.out_pos = 0; | 143 | stream->buf.out_pos = 0; |
| 120 | stream->buf.out_size = PAGE_CACHE_SIZE; | 144 | stream->buf.out_size = PAGE_CACHE_SIZE; |
| 121 | stream->buf.out = buffer[page++]; | 145 | stream->buf.out = squashfs_first_page(output); |
| 122 | 146 | ||
| 123 | do { | 147 | do { |
| 124 | if (stream->buf.in_pos == stream->buf.in_size && k < b) { | 148 | if (stream->buf.in_pos == stream->buf.in_size && k < b) { |
| 125 | avail = min(length, msblk->devblksize - offset); | 149 | avail = min(length, msblk->devblksize - offset); |
| 126 | length -= avail; | 150 | length -= avail; |
| 127 | wait_on_buffer(bh[k]); | ||
| 128 | if (!buffer_uptodate(bh[k])) | ||
| 129 | goto release_mutex; | ||
| 130 | |||
| 131 | stream->buf.in = bh[k]->b_data + offset; | 151 | stream->buf.in = bh[k]->b_data + offset; |
| 132 | stream->buf.in_size = avail; | 152 | stream->buf.in_size = avail; |
| 133 | stream->buf.in_pos = 0; | 153 | stream->buf.in_pos = 0; |
| 134 | offset = 0; | 154 | offset = 0; |
| 135 | } | 155 | } |
| 136 | 156 | ||
| 137 | if (stream->buf.out_pos == stream->buf.out_size | 157 | if (stream->buf.out_pos == stream->buf.out_size) { |
| 138 | && page < pages) { | 158 | stream->buf.out = squashfs_next_page(output); |
| 139 | stream->buf.out = buffer[page++]; | 159 | if (stream->buf.out != NULL) { |
| 140 | stream->buf.out_pos = 0; | 160 | stream->buf.out_pos = 0; |
| 141 | total += PAGE_CACHE_SIZE; | 161 | total += PAGE_CACHE_SIZE; |
| 162 | } | ||
| 142 | } | 163 | } |
| 143 | 164 | ||
| 144 | xz_err = xz_dec_run(stream->state, &stream->buf); | 165 | xz_err = xz_dec_run(stream->state, &stream->buf); |
| @@ -147,23 +168,14 @@ static int squashfs_xz_uncompress(struct squashfs_sb_info *msblk, void **buffer, | |||
| 147 | put_bh(bh[k++]); | 168 | put_bh(bh[k++]); |
| 148 | } while (xz_err == XZ_OK); | 169 | } while (xz_err == XZ_OK); |
| 149 | 170 | ||
| 150 | if (xz_err != XZ_STREAM_END) { | 171 | squashfs_finish_page(output); |
| 151 | ERROR("xz_dec_run error, data probably corrupt\n"); | ||
| 152 | goto release_mutex; | ||
| 153 | } | ||
| 154 | |||
| 155 | if (k < b) { | ||
| 156 | ERROR("xz_uncompress error, input remaining\n"); | ||
| 157 | goto release_mutex; | ||
| 158 | } | ||
| 159 | 172 | ||
| 160 | total += stream->buf.out_pos; | 173 | if (xz_err != XZ_STREAM_END || k < b) |
| 161 | mutex_unlock(&msblk->read_data_mutex); | 174 | goto out; |
| 162 | return total; | ||
| 163 | 175 | ||
| 164 | release_mutex: | 176 | return total + stream->buf.out_pos; |
| 165 | mutex_unlock(&msblk->read_data_mutex); | ||
| 166 | 177 | ||
| 178 | out: | ||
| 167 | for (; k < b; k++) | 179 | for (; k < b; k++) |
| 168 | put_bh(bh[k]); | 180 | put_bh(bh[k]); |
| 169 | 181 | ||
| @@ -172,6 +184,7 @@ release_mutex: | |||
| 172 | 184 | ||
| 173 | const struct squashfs_decompressor squashfs_xz_comp_ops = { | 185 | const struct squashfs_decompressor squashfs_xz_comp_ops = { |
| 174 | .init = squashfs_xz_init, | 186 | .init = squashfs_xz_init, |
| 187 | .comp_opts = squashfs_xz_comp_opts, | ||
| 175 | .free = squashfs_xz_free, | 188 | .free = squashfs_xz_free, |
| 176 | .decompress = squashfs_xz_uncompress, | 189 | .decompress = squashfs_xz_uncompress, |
| 177 | .id = XZ_COMPRESSION, | 190 | .id = XZ_COMPRESSION, |
diff --git a/fs/squashfs/zlib_wrapper.c b/fs/squashfs/zlib_wrapper.c index 55d918fd2d86..8727caba6882 100644 --- a/fs/squashfs/zlib_wrapper.c +++ b/fs/squashfs/zlib_wrapper.c | |||
| @@ -32,8 +32,9 @@ | |||
| 32 | #include "squashfs_fs_sb.h" | 32 | #include "squashfs_fs_sb.h" |
| 33 | #include "squashfs.h" | 33 | #include "squashfs.h" |
| 34 | #include "decompressor.h" | 34 | #include "decompressor.h" |
| 35 | #include "page_actor.h" | ||
| 35 | 36 | ||
| 36 | static void *zlib_init(struct squashfs_sb_info *dummy, void *buff, int len) | 37 | static void *zlib_init(struct squashfs_sb_info *dummy, void *buff) |
| 37 | { | 38 | { |
| 38 | z_stream *stream = kmalloc(sizeof(z_stream), GFP_KERNEL); | 39 | z_stream *stream = kmalloc(sizeof(z_stream), GFP_KERNEL); |
| 39 | if (stream == NULL) | 40 | if (stream == NULL) |
| @@ -61,44 +62,37 @@ static void zlib_free(void *strm) | |||
| 61 | } | 62 | } |
| 62 | 63 | ||
| 63 | 64 | ||
| 64 | static int zlib_uncompress(struct squashfs_sb_info *msblk, void **buffer, | 65 | static int zlib_uncompress(struct squashfs_sb_info *msblk, void *strm, |
| 65 | struct buffer_head **bh, int b, int offset, int length, int srclength, | 66 | struct buffer_head **bh, int b, int offset, int length, |
| 66 | int pages) | 67 | struct squashfs_page_actor *output) |
| 67 | { | 68 | { |
| 68 | int zlib_err, zlib_init = 0; | 69 | int zlib_err, zlib_init = 0, k = 0; |
| 69 | int k = 0, page = 0; | 70 | z_stream *stream = strm; |
| 70 | z_stream *stream = msblk->stream; | ||
| 71 | |||
| 72 | mutex_lock(&msblk->read_data_mutex); | ||
| 73 | 71 | ||
| 74 | stream->avail_out = 0; | 72 | stream->avail_out = PAGE_CACHE_SIZE; |
| 73 | stream->next_out = squashfs_first_page(output); | ||
| 75 | stream->avail_in = 0; | 74 | stream->avail_in = 0; |
| 76 | 75 | ||
| 77 | do { | 76 | do { |
| 78 | if (stream->avail_in == 0 && k < b) { | 77 | if (stream->avail_in == 0 && k < b) { |
| 79 | int avail = min(length, msblk->devblksize - offset); | 78 | int avail = min(length, msblk->devblksize - offset); |
| 80 | length -= avail; | 79 | length -= avail; |
| 81 | wait_on_buffer(bh[k]); | ||
| 82 | if (!buffer_uptodate(bh[k])) | ||
| 83 | goto release_mutex; | ||
| 84 | |||
| 85 | stream->next_in = bh[k]->b_data + offset; | 80 | stream->next_in = bh[k]->b_data + offset; |
| 86 | stream->avail_in = avail; | 81 | stream->avail_in = avail; |
| 87 | offset = 0; | 82 | offset = 0; |
| 88 | } | 83 | } |
| 89 | 84 | ||
| 90 | if (stream->avail_out == 0 && page < pages) { | 85 | if (stream->avail_out == 0) { |
| 91 | stream->next_out = buffer[page++]; | 86 | stream->next_out = squashfs_next_page(output); |
| 92 | stream->avail_out = PAGE_CACHE_SIZE; | 87 | if (stream->next_out != NULL) |
| 88 | stream->avail_out = PAGE_CACHE_SIZE; | ||
| 93 | } | 89 | } |
| 94 | 90 | ||
| 95 | if (!zlib_init) { | 91 | if (!zlib_init) { |
| 96 | zlib_err = zlib_inflateInit(stream); | 92 | zlib_err = zlib_inflateInit(stream); |
| 97 | if (zlib_err != Z_OK) { | 93 | if (zlib_err != Z_OK) { |
| 98 | ERROR("zlib_inflateInit returned unexpected " | 94 | squashfs_finish_page(output); |
| 99 | "result 0x%x, srclength %d\n", | 95 | goto out; |
| 100 | zlib_err, srclength); | ||
| 101 | goto release_mutex; | ||
| 102 | } | 96 | } |
| 103 | zlib_init = 1; | 97 | zlib_init = 1; |
| 104 | } | 98 | } |
| @@ -109,29 +103,21 @@ static int zlib_uncompress(struct squashfs_sb_info *msblk, void **buffer, | |||
| 109 | put_bh(bh[k++]); | 103 | put_bh(bh[k++]); |
| 110 | } while (zlib_err == Z_OK); | 104 | } while (zlib_err == Z_OK); |
| 111 | 105 | ||
| 112 | if (zlib_err != Z_STREAM_END) { | 106 | squashfs_finish_page(output); |
| 113 | ERROR("zlib_inflate error, data probably corrupt\n"); | ||
| 114 | goto release_mutex; | ||
| 115 | } | ||
| 116 | 107 | ||
| 117 | zlib_err = zlib_inflateEnd(stream); | 108 | if (zlib_err != Z_STREAM_END) |
| 118 | if (zlib_err != Z_OK) { | 109 | goto out; |
| 119 | ERROR("zlib_inflate error, data probably corrupt\n"); | ||
| 120 | goto release_mutex; | ||
| 121 | } | ||
| 122 | 110 | ||
| 123 | if (k < b) { | 111 | zlib_err = zlib_inflateEnd(stream); |
| 124 | ERROR("zlib_uncompress error, data remaining\n"); | 112 | if (zlib_err != Z_OK) |
| 125 | goto release_mutex; | 113 | goto out; |
| 126 | } | ||
| 127 | 114 | ||
| 128 | length = stream->total_out; | 115 | if (k < b) |
| 129 | mutex_unlock(&msblk->read_data_mutex); | 116 | goto out; |
| 130 | return length; | ||
| 131 | 117 | ||
| 132 | release_mutex: | 118 | return stream->total_out; |
| 133 | mutex_unlock(&msblk->read_data_mutex); | ||
| 134 | 119 | ||
| 120 | out: | ||
| 135 | for (; k < b; k++) | 121 | for (; k < b; k++) |
| 136 | put_bh(bh[k]); | 122 | put_bh(bh[k]); |
| 137 | 123 | ||
