]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
more .28 patches
authorGreg Kroah-Hartman <gregkh@suse.de>
Wed, 29 Apr 2009 20:30:46 +0000 (13:30 -0700)
committerGreg Kroah-Hartman <gregkh@suse.de>
Wed, 29 Apr 2009 20:30:46 +0000 (13:30 -0700)
queue-2.6.28/b44-use-kernel-dma-addresses-for-the-kernel-dma-api.patch [new file with mode: 0644]
queue-2.6.28/block-include-empty-disks-in-proc-diskstats.patch [new file with mode: 0644]
queue-2.6.28/crypto-ixp4xx-fix-handling-of-chained-sg-buffers.patch [new file with mode: 0644]
queue-2.6.28/exit_notify-kill-the-wrong-capable-check.patch [new file with mode: 0644]
queue-2.6.28/fix-ptrace-slowness.patch [new file with mode: 0644]
queue-2.6.28/fs-core-fixes.patch [new file with mode: 0644]
queue-2.6.28/pci-fix-incorrect-mask-of-pm-no_soft_reset-bit.patch [new file with mode: 0644]
queue-2.6.28/powerpc-sanitize-stack-pointer-in-signal-handling-code.patch [new file with mode: 0644]
queue-2.6.28/series
queue-2.6.28/thinkpad-acpi-fix-led-blinking-through-timer-trigger.patch [new file with mode: 0644]

diff --git a/queue-2.6.28/b44-use-kernel-dma-addresses-for-the-kernel-dma-api.patch b/queue-2.6.28/b44-use-kernel-dma-addresses-for-the-kernel-dma-api.patch
new file mode 100644 (file)
index 0000000..0aceff2
--- /dev/null
@@ -0,0 +1,34 @@
+From 37efa239901493694a48f1d6f59f8de17c2c4509 Mon Sep 17 00:00:00 2001
+From: Michael Buesch <mb@bu3sch.de>
+Date: Mon, 6 Apr 2009 09:52:27 +0000
+Subject: b44: Use kernel DMA addresses for the kernel DMA API
+
+From: Michael Buesch <mb@bu3sch.de>
+
+commit 37efa239901493694a48f1d6f59f8de17c2c4509 upstream.
+
+We must not use the device DMA addresses for the kernel DMA API, because
+device DMA addresses have an additional offset added for the SSB translation.
+
+Use the original dma_addr_t for the sync operation.
+
+Cc: stable@kernel.org
+Signed-off-by: Michael Buesch <mb@bu3sch.de>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/net/b44.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/net/b44.c
++++ b/drivers/net/b44.c
+@@ -750,7 +750,7 @@ static void b44_recycle_rx(struct b44 *b
+                                            dest_idx * sizeof(dest_desc),
+                                            DMA_BIDIRECTIONAL);
+-      ssb_dma_sync_single_for_device(bp->sdev, le32_to_cpu(src_desc->addr),
++      ssb_dma_sync_single_for_device(bp->sdev, dest_map->mapping,
+                                      RX_PKT_BUF_SZ,
+                                      DMA_FROM_DEVICE);
+ }
diff --git a/queue-2.6.28/block-include-empty-disks-in-proc-diskstats.patch b/queue-2.6.28/block-include-empty-disks-in-proc-diskstats.patch
new file mode 100644 (file)
index 0000000..3decdbb
--- /dev/null
@@ -0,0 +1,81 @@
+From 71982a409f12c50d011325a4471aa20666bb908d Mon Sep 17 00:00:00 2001
+From: Tejun Heo <tj@kernel.org>
+Date: Fri, 17 Apr 2009 08:34:48 +0200
+Subject: block: include empty disks in /proc/diskstats
+
+From: Tejun Heo <tj@kernel.org>
+
+commit 71982a409f12c50d011325a4471aa20666bb908d upstream.
+
+/proc/diskstats used to show stats for all disks whether they're
+zero-sized or not and their non-zero partitions.  Commit
+074a7aca7afa6f230104e8e65eba3420263714a5 accidentally changed the
+behavior such that it doesn't print out zero sized disks.  This patch
+implements DISK_PITER_INCL_EMPTY_PART0 flag to partition iterator and
+uses it in diskstats_show() such that empty part0 is shown in
+/proc/diskstats.
+
+Reported and bisectd by Dianel Collins.
+
+Signed-off-by: Tejun Heo <tj@kernel.org>
+Reported-by: Daniel Collins <solemnwarning@solemnwarning.no-ip.org>
+Signed-off-by: Jens Axboe <jens.axboe@oracle.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ block/genhd.c         |   12 ++++++++----
+ include/linux/genhd.h |    1 +
+ 2 files changed, 9 insertions(+), 4 deletions(-)
+
+--- a/block/genhd.c
++++ b/block/genhd.c
+@@ -98,7 +98,7 @@ void disk_part_iter_init(struct disk_par
+       if (flags & DISK_PITER_REVERSE)
+               piter->idx = ptbl->len - 1;
+-      else if (flags & DISK_PITER_INCL_PART0)
++      else if (flags & (DISK_PITER_INCL_PART0 | DISK_PITER_INCL_EMPTY_PART0))
+               piter->idx = 0;
+       else
+               piter->idx = 1;
+@@ -134,7 +134,8 @@ struct hd_struct *disk_part_iter_next(st
+       /* determine iteration parameters */
+       if (piter->flags & DISK_PITER_REVERSE) {
+               inc = -1;
+-              if (piter->flags & DISK_PITER_INCL_PART0)
++              if (piter->flags & (DISK_PITER_INCL_PART0 |
++                                  DISK_PITER_INCL_EMPTY_PART0))
+                       end = -1;
+               else
+                       end = 0;
+@@ -150,7 +151,10 @@ struct hd_struct *disk_part_iter_next(st
+               part = rcu_dereference(ptbl->part[piter->idx]);
+               if (!part)
+                       continue;
+-              if (!(piter->flags & DISK_PITER_INCL_EMPTY) && !part->nr_sects)
++              if (!part->nr_sects &&
++                  !(piter->flags & DISK_PITER_INCL_EMPTY) &&
++                  !(piter->flags & DISK_PITER_INCL_EMPTY_PART0 &&
++                    piter->idx == 0))
+                       continue;
+               get_device(part_to_dev(part));
+@@ -980,7 +984,7 @@ static int diskstats_show(struct seq_fil
+                               "\n\n");
+       */
+  
+-      disk_part_iter_init(&piter, gp, DISK_PITER_INCL_PART0);
++      disk_part_iter_init(&piter, gp, DISK_PITER_INCL_EMPTY_PART0);
+       while ((hd = disk_part_iter_next(&piter))) {
+               cpu = part_stat_lock();
+               part_round_stats(cpu, hd);
+--- a/include/linux/genhd.h
++++ b/include/linux/genhd.h
+@@ -213,6 +213,7 @@ static inline void disk_put_part(struct 
+ #define DISK_PITER_REVERSE    (1 << 0) /* iterate in the reverse direction */
+ #define DISK_PITER_INCL_EMPTY (1 << 1) /* include 0-sized parts */
+ #define DISK_PITER_INCL_PART0 (1 << 2) /* include partition 0 */
++#define DISK_PITER_INCL_EMPTY_PART0 (1 << 3) /* include empty partition 0 */
+ struct disk_part_iter {
+       struct gendisk          *disk;
diff --git a/queue-2.6.28/crypto-ixp4xx-fix-handling-of-chained-sg-buffers.patch b/queue-2.6.28/crypto-ixp4xx-fix-handling-of-chained-sg-buffers.patch
new file mode 100644 (file)
index 0000000..5c0c108
--- /dev/null
@@ -0,0 +1,390 @@
+From 0d44dc59b2b434b29aafeae581d06f81efac7c83 Mon Sep 17 00:00:00 2001
+From: Christian Hohnstaedt <chohnstaedt@innominate.com>
+Date: Fri, 27 Mar 2009 15:09:05 +0800
+Subject: crypto: ixp4xx - Fix handling of chained sg buffers
+
+From: Christian Hohnstaedt <chohnstaedt@innominate.com>
+
+commit 0d44dc59b2b434b29aafeae581d06f81efac7c83 upstream.
+
+ - keep dma functions away from chained scatterlists.
+   Use the existing scatterlist iteration inside the driver
+   to call dma_map_single() for each chunk and avoid dma_map_sg().
+
+Signed-off-by: Christian Hohnstaedt <chohnstaedt@innominate.com>
+Tested-By:  Karl Hiramoto <karl@hiramoto.org>
+Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/crypto/ixp4xx_crypto.c |  184 ++++++++++++++---------------------------
+ 1 file changed, 64 insertions(+), 120 deletions(-)
+
+--- a/drivers/crypto/ixp4xx_crypto.c
++++ b/drivers/crypto/ixp4xx_crypto.c
+@@ -101,6 +101,7 @@ struct buffer_desc {
+       u32 phys_addr;
+       u32 __reserved[4];
+       struct buffer_desc *next;
++      enum dma_data_direction dir;
+ };
+ struct crypt_ctl {
+@@ -132,14 +133,10 @@ struct crypt_ctl {
+ struct ablk_ctx {
+       struct buffer_desc *src;
+       struct buffer_desc *dst;
+-      unsigned src_nents;
+-      unsigned dst_nents;
+ };
+ struct aead_ctx {
+       struct buffer_desc *buffer;
+-      unsigned short assoc_nents;
+-      unsigned short src_nents;
+       struct scatterlist ivlist;
+       /* used when the hmac is not on one sg entry */
+       u8 *hmac_virt;
+@@ -312,7 +309,7 @@ static struct crypt_ctl *get_crypt_desc_
+       }
+ }
+-static void free_buf_chain(struct buffer_desc *buf, u32 phys)
++static void free_buf_chain(struct device *dev, struct buffer_desc *buf,u32 phys)
+ {
+       while (buf) {
+               struct buffer_desc *buf1;
+@@ -320,6 +317,7 @@ static void free_buf_chain(struct buffer
+               buf1 = buf->next;
+               phys1 = buf->phys_next;
++              dma_unmap_single(dev, buf->phys_next, buf->buf_len, buf->dir);
+               dma_pool_free(buffer_pool, buf, phys);
+               buf = buf1;
+               phys = phys1;
+@@ -348,7 +346,6 @@ static void one_packet(dma_addr_t phys)
+       struct crypt_ctl *crypt;
+       struct ixp_ctx *ctx;
+       int failed;
+-      enum dma_data_direction src_direction = DMA_BIDIRECTIONAL;
+       failed = phys & 0x1 ? -EBADMSG : 0;
+       phys &= ~0x3;
+@@ -358,13 +355,8 @@ static void one_packet(dma_addr_t phys)
+       case CTL_FLAG_PERFORM_AEAD: {
+               struct aead_request *req = crypt->data.aead_req;
+               struct aead_ctx *req_ctx = aead_request_ctx(req);
+-              dma_unmap_sg(dev, req->assoc, req_ctx->assoc_nents,
+-                              DMA_TO_DEVICE);
+-              dma_unmap_sg(dev, &req_ctx->ivlist, 1, DMA_BIDIRECTIONAL);
+-              dma_unmap_sg(dev, req->src, req_ctx->src_nents,
+-                              DMA_BIDIRECTIONAL);
+-              free_buf_chain(req_ctx->buffer, crypt->src_buf);
++              free_buf_chain(dev, req_ctx->buffer, crypt->src_buf);
+               if (req_ctx->hmac_virt) {
+                       finish_scattered_hmac(crypt);
+               }
+@@ -374,16 +366,11 @@ static void one_packet(dma_addr_t phys)
+       case CTL_FLAG_PERFORM_ABLK: {
+               struct ablkcipher_request *req = crypt->data.ablk_req;
+               struct ablk_ctx *req_ctx = ablkcipher_request_ctx(req);
+-              int nents;
++
+               if (req_ctx->dst) {
+-                      nents = req_ctx->dst_nents;
+-                      dma_unmap_sg(dev, req->dst, nents, DMA_FROM_DEVICE);
+-                      free_buf_chain(req_ctx->dst, crypt->dst_buf);
+-                      src_direction = DMA_TO_DEVICE;
+-              }
+-              nents = req_ctx->src_nents;
+-              dma_unmap_sg(dev, req->src, nents, src_direction);
+-              free_buf_chain(req_ctx->src, crypt->src_buf);
++                      free_buf_chain(dev, req_ctx->dst, crypt->dst_buf);
++              }
++              free_buf_chain(dev, req_ctx->src, crypt->src_buf);
+               req->base.complete(&req->base, failed);
+               break;
+       }
+@@ -748,56 +735,35 @@ static int setup_cipher(struct crypto_tf
+       return 0;
+ }
+-static int count_sg(struct scatterlist *sg, int nbytes)
+-{
+-      int i;
+-      for (i = 0; nbytes > 0; i++, sg = sg_next(sg))
+-              nbytes -= sg->length;
+-      return i;
+-}
+-
+-static struct buffer_desc *chainup_buffers(struct scatterlist *sg,
+-                      unsigned nbytes, struct buffer_desc *buf, gfp_t flags)
++static struct buffer_desc *chainup_buffers(struct device *dev,
++              struct scatterlist *sg, unsigned nbytes,
++              struct buffer_desc *buf, gfp_t flags,
++              enum dma_data_direction dir)
+ {
+-      int nents = 0;
+-
+-      while (nbytes > 0) {
++      for (;nbytes > 0; sg = scatterwalk_sg_next(sg)) {
++              unsigned len = min(nbytes, sg->length);
+               struct buffer_desc *next_buf;
+               u32 next_buf_phys;
+-              unsigned len = min(nbytes, sg_dma_len(sg));
++              void *ptr;
+-              nents++;
+               nbytes -= len;
+-              if (!buf->phys_addr) {
+-                      buf->phys_addr = sg_dma_address(sg);
+-                      buf->buf_len = len;
+-                      buf->next = NULL;
+-                      buf->phys_next = 0;
+-                      goto next;
+-              }
+-              /* Two consecutive chunks on one page may be handled by the old
+-               * buffer descriptor, increased by the length of the new one
+-               */
+-              if (sg_dma_address(sg) == buf->phys_addr + buf->buf_len) {
+-                      buf->buf_len += len;
+-                      goto next;
+-              }
++              ptr = page_address(sg_page(sg)) + sg->offset;
+               next_buf = dma_pool_alloc(buffer_pool, flags, &next_buf_phys);
+-              if (!next_buf)
+-                      return NULL;
++              if (!next_buf) {
++                      buf = NULL;
++                      break;
++              }
++              sg_dma_address(sg) = dma_map_single(dev, ptr, len, dir);
+               buf->next = next_buf;
+               buf->phys_next = next_buf_phys;
+-
+               buf = next_buf;
+-              buf->next = NULL;
+-              buf->phys_next = 0;
++
+               buf->phys_addr = sg_dma_address(sg);
+               buf->buf_len = len;
+-next:
+-              if (nbytes > 0) {
+-                      sg = sg_next(sg);
+-              }
++              buf->dir = dir;
+       }
++      buf->next = NULL;
++      buf->phys_next = 0;
+       return buf;
+ }
+@@ -858,12 +824,12 @@ static int ablk_perform(struct ablkciphe
+       struct crypto_ablkcipher *tfm = crypto_ablkcipher_reqtfm(req);
+       struct ixp_ctx *ctx = crypto_ablkcipher_ctx(tfm);
+       unsigned ivsize = crypto_ablkcipher_ivsize(tfm);
+-      int ret = -ENOMEM;
+       struct ix_sa_dir *dir;
+       struct crypt_ctl *crypt;
+-      unsigned int nbytes = req->nbytes, nents;
++      unsigned int nbytes = req->nbytes;
+       enum dma_data_direction src_direction = DMA_BIDIRECTIONAL;
+       struct ablk_ctx *req_ctx = ablkcipher_request_ctx(req);
++      struct buffer_desc src_hook;
+       gfp_t flags = req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP ?
+                               GFP_KERNEL : GFP_ATOMIC;
+@@ -876,7 +842,7 @@ static int ablk_perform(struct ablkciphe
+       crypt = get_crypt_desc();
+       if (!crypt)
+-              return ret;
++              return -ENOMEM;
+       crypt->data.ablk_req = req;
+       crypt->crypto_ctx = dir->npe_ctx_phys;
+@@ -889,53 +855,41 @@ static int ablk_perform(struct ablkciphe
+       BUG_ON(ivsize && !req->info);
+       memcpy(crypt->iv, req->info, ivsize);
+       if (req->src != req->dst) {
++              struct buffer_desc dst_hook;
+               crypt->mode |= NPE_OP_NOT_IN_PLACE;
+-              nents = count_sg(req->dst, nbytes);
+               /* This was never tested by Intel
+                * for more than one dst buffer, I think. */
+-              BUG_ON(nents != 1);
+-              req_ctx->dst_nents = nents;
+-              dma_map_sg(dev, req->dst, nents, DMA_FROM_DEVICE);
+-              req_ctx->dst = dma_pool_alloc(buffer_pool, flags,&crypt->dst_buf);
+-              if (!req_ctx->dst)
+-                      goto unmap_sg_dest;
+-              req_ctx->dst->phys_addr = 0;
+-              if (!chainup_buffers(req->dst, nbytes, req_ctx->dst, flags))
++              BUG_ON(req->dst->length < nbytes);
++              req_ctx->dst = NULL;
++              if (!chainup_buffers(dev, req->dst, nbytes, &dst_hook,
++                                      flags, DMA_FROM_DEVICE))
+                       goto free_buf_dest;
+               src_direction = DMA_TO_DEVICE;
++              req_ctx->dst = dst_hook.next;
++              crypt->dst_buf = dst_hook.phys_next;
+       } else {
+               req_ctx->dst = NULL;
+-              req_ctx->dst_nents = 0;
+       }
+-      nents = count_sg(req->src, nbytes);
+-      req_ctx->src_nents = nents;
+-      dma_map_sg(dev, req->src, nents, src_direction);
+-
+-      req_ctx->src = dma_pool_alloc(buffer_pool, flags, &crypt->src_buf);
+-      if (!req_ctx->src)
+-              goto unmap_sg_src;
+-      req_ctx->src->phys_addr = 0;
+-      if (!chainup_buffers(req->src, nbytes, req_ctx->src, flags))
++      req_ctx->src = NULL;
++      if (!chainup_buffers(dev, req->src, nbytes, &src_hook,
++                              flags, src_direction))
+               goto free_buf_src;
++      req_ctx->src = src_hook.next;
++      crypt->src_buf = src_hook.phys_next;
+       crypt->ctl_flags |= CTL_FLAG_PERFORM_ABLK;
+       qmgr_put_entry(SEND_QID, crypt_virt2phys(crypt));
+       BUG_ON(qmgr_stat_overflow(SEND_QID));
+       return -EINPROGRESS;
+ free_buf_src:
+-      free_buf_chain(req_ctx->src, crypt->src_buf);
+-unmap_sg_src:
+-      dma_unmap_sg(dev, req->src, req_ctx->src_nents, src_direction);
++      free_buf_chain(dev, req_ctx->src, crypt->src_buf);
+ free_buf_dest:
+       if (req->src != req->dst) {
+-              free_buf_chain(req_ctx->dst, crypt->dst_buf);
+-unmap_sg_dest:
+-              dma_unmap_sg(dev, req->src, req_ctx->dst_nents,
+-                      DMA_FROM_DEVICE);
++              free_buf_chain(dev, req_ctx->dst, crypt->dst_buf);
+       }
+       crypt->ctl_flags = CTL_FLAG_UNUSED;
+-      return ret;
++      return -ENOMEM;
+ }
+ static int ablk_encrypt(struct ablkcipher_request *req)
+@@ -983,7 +937,7 @@ static int hmac_inconsistent(struct scat
+                       break;
+               offset += sg->length;
+-              sg = sg_next(sg);
++              sg = scatterwalk_sg_next(sg);
+       }
+       return (start + nbytes > offset + sg->length);
+ }
+@@ -995,11 +949,10 @@ static int aead_perform(struct aead_requ
+       struct ixp_ctx *ctx = crypto_aead_ctx(tfm);
+       unsigned ivsize = crypto_aead_ivsize(tfm);
+       unsigned authsize = crypto_aead_authsize(tfm);
+-      int ret = -ENOMEM;
+       struct ix_sa_dir *dir;
+       struct crypt_ctl *crypt;
+-      unsigned int cryptlen, nents;
+-      struct buffer_desc *buf;
++      unsigned int cryptlen;
++      struct buffer_desc *buf, src_hook;
+       struct aead_ctx *req_ctx = aead_request_ctx(req);
+       gfp_t flags = req->base.flags & CRYPTO_TFM_REQ_MAY_SLEEP ?
+                               GFP_KERNEL : GFP_ATOMIC;
+@@ -1020,7 +973,7 @@ static int aead_perform(struct aead_requ
+       }
+       crypt = get_crypt_desc();
+       if (!crypt)
+-              return ret;
++              return -ENOMEM;
+       crypt->data.aead_req = req;
+       crypt->crypto_ctx = dir->npe_ctx_phys;
+@@ -1039,31 +992,27 @@ static int aead_perform(struct aead_requ
+               BUG(); /* -ENOTSUP because of my lazyness */
+       }
+-      req_ctx->buffer = dma_pool_alloc(buffer_pool, flags, &crypt->src_buf);
+-      if (!req_ctx->buffer)
+-              goto out;
+-      req_ctx->buffer->phys_addr = 0;
+       /* ASSOC data */
+-      nents = count_sg(req->assoc, req->assoclen);
+-      req_ctx->assoc_nents = nents;
+-      dma_map_sg(dev, req->assoc, nents, DMA_TO_DEVICE);
+-      buf = chainup_buffers(req->assoc, req->assoclen, req_ctx->buffer,flags);
++      buf = chainup_buffers(dev, req->assoc, req->assoclen, &src_hook,
++              flags, DMA_TO_DEVICE);
++      req_ctx->buffer = src_hook.next;
++      crypt->src_buf = src_hook.phys_next;
+       if (!buf)
+-              goto unmap_sg_assoc;
++              goto out;
+       /* IV */
+       sg_init_table(&req_ctx->ivlist, 1);
+       sg_set_buf(&req_ctx->ivlist, iv, ivsize);
+-      dma_map_sg(dev, &req_ctx->ivlist, 1, DMA_BIDIRECTIONAL);
+-      buf = chainup_buffers(&req_ctx->ivlist, ivsize, buf, flags);
++      buf = chainup_buffers(dev, &req_ctx->ivlist, ivsize, buf, flags,
++                      DMA_BIDIRECTIONAL);
+       if (!buf)
+-              goto unmap_sg_iv;
++              goto free_chain;
+       if (unlikely(hmac_inconsistent(req->src, cryptlen, authsize))) {
+               /* The 12 hmac bytes are scattered,
+                * we need to copy them into a safe buffer */
+               req_ctx->hmac_virt = dma_pool_alloc(buffer_pool, flags,
+                               &crypt->icv_rev_aes);
+               if (unlikely(!req_ctx->hmac_virt))
+-                      goto unmap_sg_iv;
++                      goto free_chain;
+               if (!encrypt) {
+                       scatterwalk_map_and_copy(req_ctx->hmac_virt,
+                               req->src, cryptlen, authsize, 0);
+@@ -1073,33 +1022,28 @@ static int aead_perform(struct aead_requ
+               req_ctx->hmac_virt = NULL;
+       }
+       /* Crypt */
+-      nents = count_sg(req->src, cryptlen + authsize);
+-      req_ctx->src_nents = nents;
+-      dma_map_sg(dev, req->src, nents, DMA_BIDIRECTIONAL);
+-      buf = chainup_buffers(req->src, cryptlen + authsize, buf, flags);
++      buf = chainup_buffers(dev, req->src, cryptlen + authsize, buf, flags,
++                      DMA_BIDIRECTIONAL);
+       if (!buf)
+-              goto unmap_sg_src;
++              goto free_hmac_virt;
+       if (!req_ctx->hmac_virt) {
+               crypt->icv_rev_aes = buf->phys_addr + buf->buf_len - authsize;
+       }
++
+       crypt->ctl_flags |= CTL_FLAG_PERFORM_AEAD;
+       qmgr_put_entry(SEND_QID, crypt_virt2phys(crypt));
+       BUG_ON(qmgr_stat_overflow(SEND_QID));
+       return -EINPROGRESS;
+-unmap_sg_src:
+-      dma_unmap_sg(dev, req->src, req_ctx->src_nents, DMA_BIDIRECTIONAL);
++free_hmac_virt:
+       if (req_ctx->hmac_virt) {
+               dma_pool_free(buffer_pool, req_ctx->hmac_virt,
+                               crypt->icv_rev_aes);
+       }
+-unmap_sg_iv:
+-      dma_unmap_sg(dev, &req_ctx->ivlist, 1, DMA_BIDIRECTIONAL);
+-unmap_sg_assoc:
+-      dma_unmap_sg(dev, req->assoc, req_ctx->assoc_nents, DMA_TO_DEVICE);
+-      free_buf_chain(req_ctx->buffer, crypt->src_buf);
++free_chain:
++      free_buf_chain(dev, req_ctx->buffer, crypt->src_buf);
+ out:
+       crypt->ctl_flags = CTL_FLAG_UNUSED;
+-      return ret;
++      return -ENOMEM;
+ }
+ static int aead_setup(struct crypto_aead *tfm, unsigned int authsize)
diff --git a/queue-2.6.28/exit_notify-kill-the-wrong-capable-check.patch b/queue-2.6.28/exit_notify-kill-the-wrong-capable-check.patch
new file mode 100644 (file)
index 0000000..0aa6dda
--- /dev/null
@@ -0,0 +1,38 @@
+From 432870dab85a2f69dc417022646cb9a70acf7f94 Mon Sep 17 00:00:00 2001
+From: Oleg Nesterov <oleg@redhat.com>
+Date: Mon, 6 Apr 2009 16:16:02 +0200
+Subject: exit_notify: kill the wrong capable(CAP_KILL) check (CVE-2009-1337)
+
+From: Oleg Nesterov <oleg@redhat.com>
+
+CVE-2009-1337
+
+commit 432870dab85a2f69dc417022646cb9a70acf7f94 upstream.
+
+The CAP_KILL check in exit_notify() looks just wrong, kill it.
+
+Whatever logic we have to reset ->exit_signal, the malicious user
+can bypass it if it execs the setuid application before exiting.
+
+Signed-off-by: Oleg Nesterov <oleg@redhat.com>
+Acked-by: Serge Hallyn <serue@us.ibm.com>
+Acked-by: Roland McGrath <roland@redhat.com>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ kernel/exit.c |    3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+--- a/kernel/exit.c
++++ b/kernel/exit.c
+@@ -942,8 +942,7 @@ static void exit_notify(struct task_stru
+        */
+       if (tsk->exit_signal != SIGCHLD && !task_detached(tsk) &&
+           (tsk->parent_exec_id != tsk->real_parent->self_exec_id ||
+-           tsk->self_exec_id != tsk->parent_exec_id) &&
+-          !capable(CAP_KILL))
++           tsk->self_exec_id != tsk->parent_exec_id))
+               tsk->exit_signal = SIGCHLD;
+       signal = tracehook_notify_death(tsk, &cookie, group_dead);
diff --git a/queue-2.6.28/fix-ptrace-slowness.patch b/queue-2.6.28/fix-ptrace-slowness.patch
new file mode 100644 (file)
index 0000000..ba15e0a
--- /dev/null
@@ -0,0 +1,63 @@
+From 53da1d9456fe7f87a920a78fdbdcf1225d197cb7 Mon Sep 17 00:00:00 2001
+From: Miklos Szeredi <mszeredi@suse.cz>
+Date: Mon, 23 Mar 2009 16:07:24 +0100
+Subject: fix ptrace slowness
+
+From: Miklos Szeredi <mszeredi@suse.cz>
+
+commit 53da1d9456fe7f87a920a78fdbdcf1225d197cb7 upstream.
+
+This patch fixes bug #12208:
+
+  Bug-Entry       : http://bugzilla.kernel.org/show_bug.cgi?id=12208
+  Subject         : uml is very slow on 2.6.28 host
+
+This turned out to be not a scheduler regression, but an already
+existing problem in ptrace being triggered by subtle scheduler
+changes.
+
+The problem is this:
+
+ - task A is ptracing task B
+ - task B stops on a trace event
+ - task A is woken up and preempts task B
+ - task A calls ptrace on task B, which does ptrace_check_attach()
+ - this calls wait_task_inactive(), which sees that task B is still on the runq
+ - task A goes to sleep for a jiffy
+ - ...
+
+Since UML does lots of the above sequences, those jiffies quickly add
+up to make it slow as hell.
+
+This patch solves this by not rescheduling in read_unlock() after
+ptrace_stop() has woken up the tracer.
+
+Thanks to Oleg Nesterov and Ingo Molnar for the feedback.
+
+Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
+CC: stable@kernel.org
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ kernel/signal.c |    8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+--- a/kernel/signal.c
++++ b/kernel/signal.c
+@@ -1552,7 +1552,15 @@ static void ptrace_stop(int exit_code, i
+       read_lock(&tasklist_lock);
+       if (may_ptrace_stop()) {
+               do_notify_parent_cldstop(current, CLD_TRAPPED);
++              /*
++               * Don't want to allow preemption here, because
++               * sys_ptrace() needs this task to be inactive.
++               *
++               * XXX: implement read_unlock_no_resched().
++               */
++              preempt_disable();
+               read_unlock(&tasklist_lock);
++              preempt_enable_no_resched();
+               schedule();
+       } else {
+               /*
diff --git a/queue-2.6.28/fs-core-fixes.patch b/queue-2.6.28/fs-core-fixes.patch
new file mode 100644 (file)
index 0000000..a490b25
--- /dev/null
@@ -0,0 +1,210 @@
+From hugh@veritas.com  Wed Apr 29 12:45:48 2009
+From: Hugh Dickins <hugh@veritas.com>
+Date: Sat, 25 Apr 2009 17:52:56 +0100 (BST)
+Subject: fs core fixes
+To: Chris Wright <chrisw@sous-sol.org>
+Cc: Greg Kroah-Hartman <gregkh@suse.de>, Oleg Nesterov <oleg@redhat.com>, Joe Malicki <jmalicki@metatcarta.com>, stable@kernel.org, David Howells <dhowells@redhat.com>, Al Viro <viro@zeniv.linux.org.uk>, Andrew Morton <akpm@linux-foundation.org>, Linus Torvalds <torvalds@linux-foundation.org>, Alexey Dobriyan <adobriyan@gmail.com>, Roland McGrath <roland@redhat.com>
+Message-ID: <Pine.LNX.4.64.0904251751200.2298@blonde.anvils>
+
+From: Hugh Dickins <hugh@veritas.com>
+
+Please add the following 4 commits to 2.6.27-stable and 2.6.28-stable.
+However, there has been a lot of change here between 2.6.28 and 2.6.29:
+in particular, fs/exec.c's unsafe_exec() grew into the more complicated
+check_unsafe_exec().  So applying the original patches gives too many
+rejects: at the bottom is the diffstat and the combined patch required.
+
+1
+Commit: 53e9309e01277ec99c38e84e0ca16921287cf470
+Author: Hugh Dickins <hugh@veritas.com>
+Date: Sat, 28 Mar 2009 23:16:03 +0000 (+0000)
+Subject: compat_do_execve should unshare_files
+
+2
+Commit: e426b64c412aaa3e9eb3e4b261dc5be0d5a83e78
+Author: Hugh Dickins <hugh@veritas.com>
+Date: Sat, 28 Mar 2009 23:20:19 +0000 (+0000)
+Subject: fix setuid sometimes doesn't
+
+3
+Commit: 7c2c7d993044cddc5010f6f429b100c63bc7dffb
+Author: Hugh Dickins <hugh@veritas.com>
+Date: Sat, 28 Mar 2009 23:21:27 +0000 (+0000)
+Subject: fix setuid sometimes wouldn't
+
+4
+Commit: f1191b50ec11c8e2ca766d6d99eb5bb9d2c084a3
+Author: Al Viro <viro@zeniv.linux.org.uk>
+Date: Mon, 30 Mar 2009 11:35:18 +0000 (-0400)
+Subject: check_unsafe_exec() doesn't care about signal handlers sharing
+
+Signed-off-by: Hugh Dickins <hugh@veritas.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ fs/compat.c    |   12 +++++++++++-
+ fs/exec.c      |    4 +---
+ fs/proc/base.c |   50 ++++++++++++++++----------------------------------
+ 3 files changed, 28 insertions(+), 38 deletions(-)
+
+--- a/fs/compat.c
++++ b/fs/compat.c
+@@ -1386,12 +1386,17 @@ int compat_do_execve(char * filename,
+ {
+       struct linux_binprm *bprm;
+       struct file *file;
++      struct files_struct *displaced;
+       int retval;
++      retval = unshare_files(&displaced);
++      if (retval)
++              goto out_ret;
++
+       retval = -ENOMEM;
+       bprm = kzalloc(sizeof(*bprm), GFP_KERNEL);
+       if (!bprm)
+-              goto out_ret;
++              goto out_files;
+       file = open_exec(filename);
+       retval = PTR_ERR(file);
+@@ -1443,6 +1448,8 @@ int compat_do_execve(char * filename,
+               security_bprm_free(bprm);
+               acct_update_integrals(current);
+               free_bprm(bprm);
++              if (displaced)
++                      put_files_struct(displaced);
+               return retval;
+       }
+@@ -1463,6 +1470,9 @@ out_file:
+ out_kfree:
+       free_bprm(bprm);
++out_files:
++      if (displaced)
++              reset_files_struct(displaced);
+ out_ret:
+       return retval;
+ }
+--- a/fs/exec.c
++++ b/fs/exec.c
+@@ -1084,9 +1084,7 @@ static int unsafe_exec(struct task_struc
+ {
+       int unsafe = tracehook_unsafe_exec(p);
+-      if (atomic_read(&p->fs->count) > 1 ||
+-          atomic_read(&p->files->count) > 1 ||
+-          atomic_read(&p->sighand->count) > 1)
++      if (atomic_read(&p->fs->count) > 1)
+               unsafe |= LSM_UNSAFE_SHARE;
+       return unsafe;
+--- a/fs/proc/base.c
++++ b/fs/proc/base.c
+@@ -148,15 +148,22 @@ static unsigned int pid_entry_count_dirs
+       return count;
+ }
+-static struct fs_struct *get_fs_struct(struct task_struct *task)
++static int get_fs_path(struct task_struct *task, struct path *path, bool root)
+ {
+       struct fs_struct *fs;
++      int result = -ENOENT;
++
+       task_lock(task);
+       fs = task->fs;
+-      if(fs)
+-              atomic_inc(&fs->count);
++      if (fs) {
++              read_lock(&fs->lock);
++              *path = root ? fs->root : fs->pwd;
++              path_get(path);
++              read_unlock(&fs->lock);
++              result = 0;
++      }
+       task_unlock(task);
+-      return fs;
++      return result;
+ }
+ static int get_nr_threads(struct task_struct *tsk)
+@@ -174,42 +181,24 @@ static int get_nr_threads(struct task_st
+ static int proc_cwd_link(struct inode *inode, struct path *path)
+ {
+       struct task_struct *task = get_proc_task(inode);
+-      struct fs_struct *fs = NULL;
+       int result = -ENOENT;
+       if (task) {
+-              fs = get_fs_struct(task);
++              result = get_fs_path(task, path, 0);
+               put_task_struct(task);
+       }
+-      if (fs) {
+-              read_lock(&fs->lock);
+-              *path = fs->pwd;
+-              path_get(&fs->pwd);
+-              read_unlock(&fs->lock);
+-              result = 0;
+-              put_fs_struct(fs);
+-      }
+       return result;
+ }
+ static int proc_root_link(struct inode *inode, struct path *path)
+ {
+       struct task_struct *task = get_proc_task(inode);
+-      struct fs_struct *fs = NULL;
+       int result = -ENOENT;
+       if (task) {
+-              fs = get_fs_struct(task);
++              result = get_fs_path(task, path, 1);
+               put_task_struct(task);
+       }
+-      if (fs) {
+-              read_lock(&fs->lock);
+-              *path = fs->root;
+-              path_get(&fs->root);
+-              read_unlock(&fs->lock);
+-              result = 0;
+-              put_fs_struct(fs);
+-      }
+       return result;
+ }
+@@ -567,7 +556,6 @@ static int mounts_open_common(struct ino
+       struct task_struct *task = get_proc_task(inode);
+       struct nsproxy *nsp;
+       struct mnt_namespace *ns = NULL;
+-      struct fs_struct *fs = NULL;
+       struct path root;
+       struct proc_mounts *p;
+       int ret = -EINVAL;
+@@ -581,22 +569,16 @@ static int mounts_open_common(struct ino
+                               get_mnt_ns(ns);
+               }
+               rcu_read_unlock();
+-              if (ns)
+-                      fs = get_fs_struct(task);
++              if (ns && get_fs_path(task, &root, 1) == 0)
++                      ret = 0;
+               put_task_struct(task);
+       }
+       if (!ns)
+               goto err;
+-      if (!fs)
++      if (ret)
+               goto err_put_ns;
+-      read_lock(&fs->lock);
+-      root = fs->root;
+-      path_get(&root);
+-      read_unlock(&fs->lock);
+-      put_fs_struct(fs);
+-
+       ret = -ENOMEM;
+       p = kmalloc(sizeof(struct proc_mounts), GFP_KERNEL);
+       if (!p)
diff --git a/queue-2.6.28/pci-fix-incorrect-mask-of-pm-no_soft_reset-bit.patch b/queue-2.6.28/pci-fix-incorrect-mask-of-pm-no_soft_reset-bit.patch
new file mode 100644 (file)
index 0000000..5016a48
--- /dev/null
@@ -0,0 +1,29 @@
+From 998dd7c719f62dcfa91d7bf7f4eb9c160e03d817 Mon Sep 17 00:00:00 2001
+From: Yu Zhao <yu.zhao@intel.com>
+Date: Wed, 25 Feb 2009 13:15:52 +0800
+Subject: PCI: fix incorrect mask of PM No_Soft_Reset bit
+
+From: Yu Zhao <yu.zhao@intel.com>
+
+commit 998dd7c719f62dcfa91d7bf7f4eb9c160e03d817 upstream.
+
+Reviewed-by: Matthew Wilcox <matthew@wil.cx>
+Signed-off-by: Yu Zhao <yu.zhao@intel.com>
+Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ include/linux/pci_regs.h |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/include/linux/pci_regs.h
++++ b/include/linux/pci_regs.h
+@@ -234,7 +234,7 @@
+ #define  PCI_PM_CAP_PME_SHIFT 11      /* Start of the PME Mask in PMC */
+ #define PCI_PM_CTRL           4       /* PM control and status register */
+ #define  PCI_PM_CTRL_STATE_MASK       0x0003  /* Current power state (D0 to D3) */
+-#define  PCI_PM_CTRL_NO_SOFT_RESET    0x0004  /* No reset for D3hot->D0 */
++#define  PCI_PM_CTRL_NO_SOFT_RESET    0x0008  /* No reset for D3hot->D0 */
+ #define  PCI_PM_CTRL_PME_ENABLE       0x0100  /* PME pin enable */
+ #define  PCI_PM_CTRL_DATA_SEL_MASK    0x1e00  /* Data select (??) */
+ #define  PCI_PM_CTRL_DATA_SCALE_MASK  0x6000  /* Data scale (??) */
diff --git a/queue-2.6.28/powerpc-sanitize-stack-pointer-in-signal-handling-code.patch b/queue-2.6.28/powerpc-sanitize-stack-pointer-in-signal-handling-code.patch
new file mode 100644 (file)
index 0000000..74223f2
--- /dev/null
@@ -0,0 +1,126 @@
+From jwboyer@linux.vnet.ibm.com  Wed Apr 29 12:31:40 2009
+From: Josh Boyer <jwboyer@linux.vnet.ibm.com>
+Date: Tue, 28 Apr 2009 11:14:01 -0400
+Subject: powerpc: Sanitize stack pointer in signal handling code
+To: stable@kernel.org
+Cc: benh@kernel.crashing.org
+Message-ID: <20090428151401.GB5281@yoda.jdub.homelinux.org>
+Content-Disposition: inline
+
+From: Josh Boyer <jwboyer@linux.vnet.ibm.com>
+
+This has been backported to 2.6.28.x from commit efbda86098 in Linus' tree
+
+On powerpc64 machines running 32-bit userspace, we can get garbage bits in the
+stack pointer passed into the kernel.  Most places handle this correctly, but
+the signal handling code uses the passed value directly for allocating signal
+stack frames.
+
+This fixes the issue by introducing a get_clean_sp function that returns a
+sanitized stack pointer.  For 32-bit tasks on a 64-bit kernel, the stack
+pointer is masked correctly.  In all other cases, the stack pointer is simply
+returned.
+
+Additionally, we pass an 'is_32' parameter to get_sigframe now in order to
+get the properly sanitized stack.  The callers are know to be 32 or 64-bit
+statically.
+
+Signed-off-by: Josh Boyer <jwboyer@linux.vnet.ibm.com>
+Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ arch/powerpc/include/asm/processor.h |   19 +++++++++++++++++++
+ arch/powerpc/kernel/signal.c         |    4 ++--
+ arch/powerpc/kernel/signal.h         |    2 +-
+ arch/powerpc/kernel/signal_32.c      |    4 ++--
+ arch/powerpc/kernel/signal_64.c      |    2 +-
+ 5 files changed, 25 insertions(+), 6 deletions(-)
+
+--- a/arch/powerpc/include/asm/processor.h
++++ b/arch/powerpc/include/asm/processor.h
+@@ -309,6 +309,25 @@ static inline void prefetchw(const void 
+ #define HAVE_ARCH_PICK_MMAP_LAYOUT
+ #endif
++#ifdef CONFIG_PPC64
++static inline unsigned long get_clean_sp(struct pt_regs *regs, int is_32)
++{
++      unsigned long sp;
++
++      if (is_32)
++              sp = regs->gpr[1] & 0x0ffffffffUL;
++      else
++              sp = regs->gpr[1];
++
++      return sp;
++}
++#else
++static inline unsigned long get_clean_sp(struct pt_regs *regs, int is_32)
++{
++      return regs->gpr[1];
++}
++#endif
++
+ #endif /* __KERNEL__ */
+ #endif /* __ASSEMBLY__ */
+ #endif /* _ASM_POWERPC_PROCESSOR_H */
+--- a/arch/powerpc/kernel/signal_32.c
++++ b/arch/powerpc/kernel/signal_32.c
+@@ -836,7 +836,7 @@ int handle_rt_signal32(unsigned long sig
+       /* Set up Signal Frame */
+       /* Put a Real Time Context onto stack */
+-      rt_sf = get_sigframe(ka, regs, sizeof(*rt_sf));
++      rt_sf = get_sigframe(ka, regs, sizeof(*rt_sf), 1);
+       addr = rt_sf;
+       if (unlikely(rt_sf == NULL))
+               goto badframe;
+@@ -1182,7 +1182,7 @@ int handle_signal32(unsigned long sig, s
+       unsigned long newsp = 0;
+       /* Set up Signal Frame */
+-      frame = get_sigframe(ka, regs, sizeof(*frame));
++      frame = get_sigframe(ka, regs, sizeof(*frame), 1);
+       if (unlikely(frame == NULL))
+               goto badframe;
+       sc = (struct sigcontext __user *) &frame->sctx;
+--- a/arch/powerpc/kernel/signal_64.c
++++ b/arch/powerpc/kernel/signal_64.c
+@@ -402,7 +402,7 @@ int handle_rt_signal64(int signr, struct
+       unsigned long newsp = 0;
+       long err = 0;
+-      frame = get_sigframe(ka, regs, sizeof(*frame));
++      frame = get_sigframe(ka, regs, sizeof(*frame), 0);
+       if (unlikely(frame == NULL))
+               goto badframe;
+--- a/arch/powerpc/kernel/signal.c
++++ b/arch/powerpc/kernel/signal.c
+@@ -26,12 +26,12 @@ int show_unhandled_signals = 0;
+  * Allocate space for the signal frame
+  */
+ void __user * get_sigframe(struct k_sigaction *ka, struct pt_regs *regs,
+-                         size_t frame_size)
++                         size_t frame_size, int is_32)
+ {
+         unsigned long oldsp, newsp;
+         /* Default to using normal stack */
+-        oldsp = regs->gpr[1];
++        oldsp = get_clean_sp(regs, is_32);
+       /* Check for alt stack */
+       if ((ka->sa.sa_flags & SA_ONSTACK) &&
+--- a/arch/powerpc/kernel/signal.h
++++ b/arch/powerpc/kernel/signal.h
+@@ -15,7 +15,7 @@
+ extern void do_signal(struct pt_regs *regs, unsigned long thread_info_flags);
+ extern void __user * get_sigframe(struct k_sigaction *ka, struct pt_regs *regs,
+-                                size_t frame_size);
++                                size_t frame_size, int is_32);
+ extern void restore_sigmask(sigset_t *set);
+ extern int handle_signal32(unsigned long sig, struct k_sigaction *ka,
index b15f41052e41cd15c2509ca5b789c557e9e60993..24e1f8711fb4b21ec9213baac3795cc93af9b273 100644 (file)
@@ -76,3 +76,12 @@ kvm-vmx-flush-volatile-msrs-before-emulating-rdmsr.patch
 ath9k-implement-io-serialization.patch
 ath9k-ar9280-pci-devices-must-serialize-io-as-well.patch
 md-fix-deadlock-when-stopping-arrays.patch
+block-include-empty-disks-in-proc-diskstats.patch
+powerpc-sanitize-stack-pointer-in-signal-handling-code.patch
+fs-core-fixes.patch
+fix-ptrace-slowness.patch
+crypto-ixp4xx-fix-handling-of-chained-sg-buffers.patch
+pci-fix-incorrect-mask-of-pm-no_soft_reset-bit.patch
+exit_notify-kill-the-wrong-capable-check.patch
+b44-use-kernel-dma-addresses-for-the-kernel-dma-api.patch
+thinkpad-acpi-fix-led-blinking-through-timer-trigger.patch
diff --git a/queue-2.6.28/thinkpad-acpi-fix-led-blinking-through-timer-trigger.patch b/queue-2.6.28/thinkpad-acpi-fix-led-blinking-through-timer-trigger.patch
new file mode 100644 (file)
index 0000000..840b2a4
--- /dev/null
@@ -0,0 +1,132 @@
+From 75bd3bf2ade9d548be0d2bde60b5ee0fdce0b127 Mon Sep 17 00:00:00 2001
+From: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
+Date: Tue, 14 Apr 2009 02:44:11 +0000
+Subject: thinkpad-acpi: fix LED blinking through timer trigger
+
+From: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
+
+commit 75bd3bf2ade9d548be0d2bde60b5ee0fdce0b127 upstream.
+
+The set_blink hook code in the LED subdriver would never manage to get
+a LED to blink, and instead it would just turn it on.  The consequence
+of this is that the "timer" trigger would not cause the LED to blink
+if given default parameters.
+
+This problem exists since 2.6.26-rc1.
+
+To fix it, switch the deferred LED work handling to use the
+thinkpad-acpi-specific LED status (off/on/blink) directly.
+
+This also makes the code easier to read, and to extend later.
+
+Signed-off-by: Henrique de Moraes Holschuh <hmh@hmh.eng.br>
+Cc: stable@kernel.org
+Signed-off-by: Len Brown <len.brown@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/misc/thinkpad_acpi.c |   41 +++++++++++++++++++----------------------
+ 1 file changed, 19 insertions(+), 22 deletions(-)
+
+--- a/drivers/misc/thinkpad_acpi.c
++++ b/drivers/misc/thinkpad_acpi.c
+@@ -281,11 +281,17 @@ static u32 dbg_level;
+ static struct workqueue_struct *tpacpi_wq;
++enum led_status_t {
++      TPACPI_LED_OFF = 0,
++      TPACPI_LED_ON,
++      TPACPI_LED_BLINK,
++};
++
+ /* Special LED class that can defer work */
+ struct tpacpi_led_classdev {
+       struct led_classdev led_classdev;
+       struct work_struct work;
+-      enum led_brightness new_brightness;
++      enum led_status_t new_state;
+       unsigned int led;
+ };
+@@ -3489,7 +3495,7 @@ static void light_set_status_worker(stru
+                       container_of(work, struct tpacpi_led_classdev, work);
+       if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING))
+-              light_set_status((data->new_brightness != LED_OFF));
++              light_set_status((data->new_state != TPACPI_LED_OFF));
+ }
+ static void light_sysfs_set(struct led_classdev *led_cdev,
+@@ -3499,7 +3505,8 @@ static void light_sysfs_set(struct led_c
+               container_of(led_cdev,
+                            struct tpacpi_led_classdev,
+                            led_classdev);
+-      data->new_brightness = brightness;
++      data->new_state = (brightness != LED_OFF) ?
++                              TPACPI_LED_ON : TPACPI_LED_OFF;
+       queue_work(tpacpi_wq, &data->work);
+ }
+@@ -4006,12 +4013,6 @@ enum {  /* For TPACPI_LED_OLD */
+       TPACPI_LED_EC_HLMS = 0x0e,      /* EC reg to select led to command */
+ };
+-enum led_status_t {
+-      TPACPI_LED_OFF = 0,
+-      TPACPI_LED_ON,
+-      TPACPI_LED_BLINK,
+-};
+-
+ static enum led_access_mode led_supported;
+ TPACPI_HANDLE(led, ec, "SLED",        /* 570 */
+@@ -4105,23 +4106,13 @@ static int led_set_status(const unsigned
+       return rc;
+ }
+-static void led_sysfs_set_status(unsigned int led,
+-                               enum led_brightness brightness)
+-{
+-      led_set_status(led,
+-                      (brightness == LED_OFF) ?
+-                      TPACPI_LED_OFF :
+-                      (tpacpi_led_state_cache[led] == TPACPI_LED_BLINK) ?
+-                              TPACPI_LED_BLINK : TPACPI_LED_ON);
+-}
+-
+ static void led_set_status_worker(struct work_struct *work)
+ {
+       struct tpacpi_led_classdev *data =
+               container_of(work, struct tpacpi_led_classdev, work);
+       if (likely(tpacpi_lifecycle == TPACPI_LIFE_RUNNING))
+-              led_sysfs_set_status(data->led, data->new_brightness);
++              led_set_status(data->led, data->new_state);
+ }
+ static void led_sysfs_set(struct led_classdev *led_cdev,
+@@ -4130,7 +4121,13 @@ static void led_sysfs_set(struct led_cla
+       struct tpacpi_led_classdev *data = container_of(led_cdev,
+                            struct tpacpi_led_classdev, led_classdev);
+-      data->new_brightness = brightness;
++      if (brightness == LED_OFF)
++              data->new_state = TPACPI_LED_OFF;
++      else if (tpacpi_led_state_cache[data->led] != TPACPI_LED_BLINK)
++              data->new_state = TPACPI_LED_ON;
++      else
++              data->new_state = TPACPI_LED_BLINK;
++
+       queue_work(tpacpi_wq, &data->work);
+ }
+@@ -4148,7 +4145,7 @@ static int led_sysfs_blink_set(struct le
+       } else if ((*delay_on != 500) || (*delay_off != 500))
+               return -EINVAL;
+-      data->new_brightness = TPACPI_LED_BLINK;
++      data->new_state = TPACPI_LED_BLINK;
+       queue_work(tpacpi_wq, &data->work);
+       return 0;