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]