Sorry I haven't sent a message earlier about this, but I had the same
problem and have been working with the kernel people on this since
December.  It's definitely a kernel bug.  Below is a patch vs. 2.6.15
which appears to solve it for me.  There are also a couple of
workarounds - you can load the SCSI tape driver as a module with the
argument try_direct_io=0, or you can add iommu=nomerge to the kernel
command line.

-ryan

----- Forwarded message from Hugh Dickins <[EMAIL PROTECTED]> -----

To: Ryan Richter <[EMAIL PROTECTED]>
Delivery-date: Sun, 29 Jan 2006 13:07:55 -0500
Date: Sun, 29 Jan 2006 18:08:01 +0000 (GMT)
From: Hugh Dickins <[EMAIL PROTECTED]>
Subject: Re: Fw: crash on x86_64 - mm related?
X-X-Sender: [EMAIL PROTECTED]

Here's what I hope is the patch for your problem.  A couple of other
drivers look vulnerable to the same issue, I haven't done patches for
them yet, but nor are you needing them.  Please give this a try, perhaps
as the third part of your testing i.e. instead of rebooting again with
"iommu=nomerge", reboot kernel built with this patch and without that
option and try again to reproduce it.  I'm increasingly confident that
this will work (even if iommu=nomerge does not work - I'm less confident
that I found all the underlying places which might make this happen,
iommu=nomerge switches off one of them but perhaps there are more).
But I've done no more than check this builds: beware of any idiocy!

Hugh

--- 2.6.15/drivers/scsi/st.c    2006-01-03 03:21:10.000000000 +0000
+++ linux/drivers/scsi/st.c     2006-01-29 17:21:47.000000000 +0000
@@ -188,11 +188,11 @@ static int from_buffer(struct st_buffer 
 static void move_buffer_data(struct st_buffer *, int);
 static void buf_to_sg(struct st_buffer *, unsigned int);
 
-static int st_map_user_pages(struct scatterlist *, const unsigned int, 
+static int st_map_user_pages(struct st_buffer *, const unsigned int, 
                             unsigned long, size_t, int, unsigned long);
-static int sgl_map_user_pages(struct scatterlist *, const unsigned int, 
+static int sgl_map_user_pages(struct st_buffer *, const unsigned int, 
                              unsigned long, size_t, int);
-static int sgl_unmap_user_pages(struct scatterlist *, const unsigned int, int);
+static int sgl_unmap_user_pages(struct st_buffer *, const unsigned int, int);
 
 static int st_probe(struct device *);
 static int st_remove(struct device *);
@@ -1402,7 +1402,7 @@ static int setup_buffering(struct scsi_t
                i = STp->try_dio && try_wdio;
        if (i && ((unsigned long)buf & queue_dma_alignment(
                                        STp->device->request_queue)) == 0) {
-               i = st_map_user_pages(&(STbp->sg[0]), STbp->use_sg,
+               i = st_map_user_pages(STbp, STbp->use_sg,
                                      (unsigned long)buf, count, (is_read ? 
READ : WRITE),
                                      STp->max_pfn);
                if (i > 0) {
@@ -1455,7 +1455,7 @@ static void release_buffering(struct scs
 
        STbp = STp->buffer;
        if (STbp->do_dio) {
-               sgl_unmap_user_pages(&(STbp->sg[0]), STbp->do_dio, 0);
+               sgl_unmap_user_pages(STbp, STbp->do_dio, 0);
                STbp->do_dio = 0;
        }
 }
@@ -4396,32 +4396,33 @@ static void do_create_class_files(struct
    - any page is above max_pfn
    (i.e., either completely successful or fails)
 */
-static int st_map_user_pages(struct scatterlist *sgl, const unsigned int 
max_pages, 
+static int st_map_user_pages(struct st_buffer *STbp, const unsigned int 
max_pages, 
                             unsigned long uaddr, size_t count, int rw,
                             unsigned long max_pfn)
 {
        int i, nr_pages;
 
-       nr_pages = sgl_map_user_pages(sgl, max_pages, uaddr, count, rw);
+       nr_pages = sgl_map_user_pages(STbp, max_pages, uaddr, count, rw);
        if (nr_pages <= 0)
                return nr_pages;
 
        for (i=0; i < nr_pages; i++) {
-               if (page_to_pfn(sgl[i].page) > max_pfn)
+               if (page_to_pfn(STbp->sg[i].page) > max_pfn)
                        goto out_unmap;
        }
        return nr_pages;
 
  out_unmap:
-       sgl_unmap_user_pages(sgl, nr_pages, 0);
+       sgl_unmap_user_pages(STbp, nr_pages, 0);
        return 0;
 }
 
 
 /* The following functions may be useful for a larger audience. */
-static int sgl_map_user_pages(struct scatterlist *sgl, const unsigned int 
max_pages, 
+static int sgl_map_user_pages(struct st_buffer *STbp, const unsigned int 
max_pages, 
                              unsigned long uaddr, size_t count, int rw)
 {
+       struct scatterlist *sgl = STbp->sg;
        unsigned long end = (uaddr + count + PAGE_SIZE - 1) >> PAGE_SHIFT;
        unsigned long start = uaddr >> PAGE_SHIFT;
        const int nr_pages = end - start;
@@ -4485,7 +4486,7 @@ static int sgl_map_user_pages(struct sca
                sgl[0].length = count;
        }
 
-       kfree(pages);
+       STbp->sg_pages = pages;
        return nr_pages;
 
  out_unmap:
@@ -4500,13 +4501,13 @@ static int sgl_map_user_pages(struct sca
 
 
 /* And unmap them... */
-static int sgl_unmap_user_pages(struct scatterlist *sgl, const unsigned int 
nr_pages,
+static int sgl_unmap_user_pages(struct st_buffer *STbp, const unsigned int 
nr_pages,
                                int dirtied)
 {
        int i;
 
        for (i=0; i < nr_pages; i++) {
-               struct page *page = sgl[i].page;
+               struct page *page = STbp->sg_pages[i];
 
                if (dirtied)
                        SetPageDirty(page);
@@ -4516,5 +4517,7 @@ static int sgl_unmap_user_pages(struct s
                page_cache_release(page);
        }
 
+       kfree(STbp->sg_pages);
+       STbp->sg_pages = NULL;
        return 0;
 }
--- 2.6.15/drivers/scsi/st.h    2005-10-28 01:02:08.000000000 +0100
+++ linux/drivers/scsi/st.h     2006-01-29 16:52:02.000000000 +0000
@@ -37,6 +37,7 @@ struct st_buffer {
        unsigned short frp_segs;        /* number of buffer segments */
        unsigned int frp_sg_current;    /* driver buffer length currently in 
s/g list */
        struct st_buf_fragment *frp;    /* the allocated buffer fragment list */
+       struct page **sg_pages;         /* pages to be freed from s/g list */
        struct scatterlist sg[1];       /* MUST BE last item */
 };
 

----- End forwarded message -----


-- 
To UNSUBSCRIBE, email to [EMAIL PROTECTED]
with a subject of "unsubscribe". Trouble? Contact [EMAIL PROTECTED]

Reply via email to