[kernel] fix usage of smp_call_function
[openwrt.git] / target / linux / generic-2.6 / patches-2.6.30 / 052-pcomp_lzma_support.patch
index 3259db4..d20285c 100644 (file)
@@ -1,6 +1,6 @@
 --- /dev/null
 +++ b/crypto/unlzma.c
-@@ -0,0 +1,710 @@
+@@ -0,0 +1,775 @@
 +/*
 + * LZMA uncompresion module for pcomp
 + * Copyright (C) 2009  Felix Fietkau <nbd@openwrt.org>
 +#include <linux/kthread.h>
 +
 +#include <crypto/internal/compress.h>
++#include <net/netlink.h>
 +#include "unlzma.h"
 +
 +static int instance = 0;
 +
 +struct unlzma_buffer {
-+      struct unlzma_buffer *last;
 +      int offset;
 +      int size;
 +      u8 *ptr;
@@ -50,7 +50,9 @@
 +struct unlzma_ctx {
 +      struct task_struct *thread;
 +      wait_queue_head_t next_req;
++      wait_queue_head_t req_done;
 +      struct mutex mutex;
++      bool waiting;
 +      bool active;
 +      bool cancel;
 +
 +      /* writer state */
 +      u8 previous_byte;
 +      ssize_t pos;
-+      struct unlzma_buffer *head;
++      int buf_full;
++      int n_buffers;
++      int buffers_max;
++      struct unlzma_buffer *buffers;
 +
 +      /* cstate */
 +      int state;
 +}
 +
 +static void
++get_buffer(struct unlzma_ctx *ctx)
++{
++      struct unlzma_buffer *bh;
++
++      BUG_ON(ctx->n_buffers >= ctx->buffers_max);
++      bh = &ctx->buffers[ctx->n_buffers++];
++      bh->ptr = ctx->next_out;
++      bh->offset = ctx->pos;
++      bh->size = ctx->avail_out;
++      ctx->buf_full = 0;
++}
++
++static void
 +unlzma_request_buffer(struct unlzma_ctx *ctx, int *avail)
 +{
-+      mutex_unlock(&ctx->mutex);
-+      wait_event(ctx->next_req, unlzma_should_stop(ctx) || (*avail > 0));
-+      mutex_lock(&ctx->mutex);
++      do {
++              ctx->waiting = true;
++              mutex_unlock(&ctx->mutex);
++              wake_up(&ctx->req_done);
++              if (wait_event_interruptible(ctx->next_req,
++                      unlzma_should_stop(ctx) || (*avail > 0)))
++                      schedule();
++              mutex_lock(&ctx->mutex);
++      } while (*avail <= 0 && !unlzma_should_stop(ctx));
++
++      if (!unlzma_should_stop(ctx) && ctx->buf_full)
++              get_buffer(ctx);
 +}
 +
 +static u8
 +static u8
 +peek_old_byte(struct unlzma_ctx *ctx, u32 offs)
 +{
-+      struct unlzma_buffer *bh = ctx->head;
++      struct unlzma_buffer *bh = &ctx->buffers[ctx->n_buffers - 1];
++      int i = ctx->n_buffers;
 +      u32 pos;
 +
-+      pos = ctx->pos - offs;
-+      if (pos >= ctx->dict_size) {
-+              pos = (~pos % ctx->dict_size);
++      if (!ctx->n_buffers) {
++              printk(KERN_ERR "unlzma/%s: no buffer\n", __func__);
++              goto error;
 +      }
 +
++      pos = ctx->pos - offs;
++      if (unlikely(pos >= ctx->dict_size))
++              pos = ~pos & (ctx->dict_size - 1);
++
 +      while (bh->offset > pos) {
-+              bh = bh->last;
-+              if (!bh)
-+                      return 0;
++              bh--;
++              i--;
++              if (!i) {
++                      printk(KERN_ERR "unlzma/%s: position %d out of range\n", __func__, pos);
++                      goto error;
++              }
 +      }
 +
 +      pos -= bh->offset;
-+      if (pos > bh->size)
-+              return 0;
++      if (pos >= bh->size) {
++              printk(KERN_ERR "unlzma/%s: position %d out of range\n", __func__, pos);
++              goto error;
++      }
 +
 +      return bh->ptr[pos];
-+}
-+
-+static void
-+get_buffer(struct unlzma_ctx *ctx)
-+{
-+      struct unlzma_buffer *bh;
 +
-+      bh = kzalloc(sizeof(struct unlzma_buffer), GFP_KERNEL);
-+      bh->ptr = ctx->next_out;
-+      bh->offset = ctx->pos;
-+      bh->last = ctx->head;
-+      bh->size = ctx->avail_out;
-+      ctx->head = bh;
++error:
++      ctx->cancel = true;
++      return 0;
 +}
 +
 +static void
 +{
 +      if (unlikely(ctx->avail_out <= 0)) {
 +              unlzma_request_buffer(ctx, &ctx->avail_out);
-+              get_buffer(ctx);
 +      }
 +
 +      if (!ctx->avail_out)
 +      ctx->previous_byte = byte;
 +      *(ctx->next_out++) = byte;
 +      ctx->avail_out--;
++      if (ctx->avail_out == 0)
++              ctx->buf_full = 1;
 +      ctx->pos++;
 +}
 +
 +              hdr_buf[i] = rc_read(ctx);
 +      }
 +
++      ctx->n_buffers = 0;
 +      ctx->pos = 0;
 +      get_buffer(ctx);
 +      ctx->active = true;
 +      if (ctx->workspace_size < num_probs * sizeof(*p)) {
 +              if (ctx->workspace)
 +                      vfree(ctx->workspace);
-+              ctx->workspace = vmalloc(num_probs * sizeof(*p));
++              ctx->workspace_size = num_probs * sizeof(*p);
++              ctx->workspace = vmalloc(ctx->workspace_size);
 +      }
 +      p = (u16 *) ctx->workspace;
 +      if (!p)
 +              if (unlzma_should_stop(ctx))
 +                      break;
 +      }
++      if (likely(!unlzma_should_stop(ctx)))
++              rc_normalize(ctx);
 +
 +      return ctx->pos;
 +}
 +              unlzma_reset_buf(ctx);
 +              ctx->cancel = false;
 +              ctx->active = false;
-+              while (ctx->head) {
-+                      struct unlzma_buffer *bh = ctx->head;
-+                      ctx->head = bh->last;
-+                      kfree(bh);
-+              }
 +      } while (!kthread_should_stop());
 +      mutex_unlock(&ctx->mutex);
 +      return 0;
 +              unlzma_cancel(ctx);
 +              kthread_stop(ctx->thread);
 +              ctx->thread = NULL;
++              if (ctx->buffers)
++                      kfree(ctx->buffers);
++              ctx->buffers_max = 0;
++              ctx->buffers = NULL;
 +      }
 +}
 +
 +unlzma_decompress_setup(struct crypto_pcomp *tfm, void *p, unsigned int len)
 +{
 +      struct unlzma_ctx *ctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
++      struct nlattr *tb[UNLZMA_DECOMP_MAX + 1];
 +      int ret = 0;
 +
 +      if (ctx->thread)
-+              return 0;
++              return -EINVAL;
++
++      if (!p)
++              return -EINVAL;
++
++      ret = nla_parse(tb, UNLZMA_DECOMP_MAX, p, len, NULL);
++      if (ret)
++              return ret;
++
++      if (!tb[UNLZMA_DECOMP_OUT_BUFFERS])
++              return -EINVAL;
++
++      if (ctx->buffers_max && (ctx->buffers_max <
++          nla_get_u32(tb[UNLZMA_DECOMP_OUT_BUFFERS]))) {
++              kfree(ctx->buffers);
++              ctx->buffers_max = 0;
++              ctx->buffers = NULL;
++      }
++      if (!ctx->buffers) {
++              ctx->buffers_max = nla_get_u32(tb[UNLZMA_DECOMP_OUT_BUFFERS]);
++              ctx->buffers = kzalloc(sizeof(struct unlzma_buffer) * ctx->buffers_max, GFP_KERNEL);
++      }
++      if (!ctx->buffers)
++              return -ENOMEM;
 +
++      ctx->waiting = false;
 +      mutex_init(&ctx->mutex);
 +      init_waitqueue_head(&ctx->next_req);
++      init_waitqueue_head(&ctx->req_done);
 +      ctx->thread = kthread_run(unlzma_thread, ctx, "unlzma/%d", instance++);
 +      if (IS_ERR(ctx->thread)) {
 +              ret = PTR_ERR(ctx->thread);
 +static int
 +unlzma_decompress_init(struct crypto_pcomp *tfm)
 +{
-+      struct unlzma_ctx *ctx = crypto_tfm_ctx(crypto_pcomp_tfm(tfm));
-+
-+      ctx->pos = 0;
 +      return 0;
 +}
 +
 +static void
 +unlzma_wait_complete(struct unlzma_ctx *ctx, bool finish)
 +{
++      DEFINE_WAIT(__wait);
++
 +      do {
-+              mutex_unlock(&ctx->mutex);
 +              wake_up(&ctx->next_req);
++              prepare_to_wait(&ctx->req_done, &__wait, TASK_INTERRUPTIBLE);
++              mutex_unlock(&ctx->mutex);
 +              schedule();
 +              mutex_lock(&ctx->mutex);
-+      } while (ctx->active && (ctx->avail_in > 0) && (ctx->avail_out > 0));
++      } while (!ctx->waiting && ctx->active);
++      finish_wait(&ctx->req_done, &__wait);
 +}
 +
 +static int
 +              goto out;
 +
 +      pos = ctx->pos;
++      ctx->waiting = false;
 +      ctx->next_in = req->next_in;
 +      ctx->avail_in = req->avail_in;
 +      ctx->next_out = req->next_out;
 +      req->avail_in = ctx->avail_in;
 +      req->next_out = ctx->next_out;
 +      req->avail_out = ctx->avail_out;
++      ctx->next_in = 0;
++      ctx->avail_in = 0;
 +      pos = ctx->pos - pos;
 +
 +out:
 +      mutex_unlock(&ctx->mutex);
++      if (ctx->cancel)
++              return -EINVAL;
++
 +      return pos;
 +}
 +
 +#define LZMA_LITERAL (LZMA_REP_LEN_CODER + LZMA_NUM_LEN_PROBS)
 +
 +#endif
+--- a/include/crypto/compress.h
++++ b/include/crypto/compress.h
+@@ -49,6 +49,12 @@ enum zlib_decomp_params {
+ #define ZLIB_DECOMP_MAX       (__ZLIB_DECOMP_MAX - 1)
++enum unlzma_decomp_params {
++      UNLZMA_DECOMP_OUT_BUFFERS = 1, /* naximum number of output buffers */
++      __UNLZMA_DECOMP_MAX,
++};
++#define UNLZMA_DECOMP_MAX     (__UNLZMA_DECOMP_MAX - 1)
++
+ struct crypto_pcomp {
+       struct crypto_tfm base;
This page took 0.029615 seconds and 4 git commands to generate.