Skip to contentSkip to conversation

Reading page from given block device

DISCUSSION Pavel Machek 3 participants 10 messages May 8, 2002 – May 9, 2002

lore
Share & tools
lore
#0 lore raw

Hi!

For swsusp, I kind of need to read 4K from given block device.

Here's my attempt:

static int bdev_read_page(kdev_t dev, long pos, void *buf)

{
        struct buffer_head *bh;
        struct block_device *bdev;

        if (pos%PAGE_SIZE) panic("Sorry, dave, I can't let you do
that!\n");
        bdev = bdget(kdev_t_to_nr(dev));
        if (!bdev) {
                printk("No block device for %s\n", __bdevname(dev));
                BUG();
        }
        printk("C");
        bh = __bread(bdev, pos/PAGE_SIZE, PAGE_SIZE);
        printk("D");
        bdput(bdev);
        if (!bh || (!bh->b_data)) {
                return -1;
        }
        memcpy(buf, bh->b_data, PAGE_SIZE);
        bforget(bh);                    /* FIXME: maybe bforget should
be here */
        return 0;
}

It works *once*, second time it deadlocks in __bread(). I tried both bforget() and brelse(). Kernel is 2.5.13. What am I doing wrong/what's wrong?

Pavel

Signature · 2 lines
(about SSSCA) "I don't say this lightly.  However, I really think that the U.S.
no longer is classifiable as a democracy, but rather as a plutocracy." --hpa
#1 lore raw
Quoted 7 lines · Pavel Machek

Hi!

For swsusp, I kind of need to read 4K from given block device.

Here's my attempt:

Quoted 7 lines

static int bdev_read_page(kdev_t dev, long pos, void *buf)

{
        struct buffer_head *bh;
        struct block_device *bdev;

        if (pos%PAGE_SIZE) panic("Sorry, dave, I can't let you do

that!\n");

It's possible I guess that someone has a pinned buffer against the same page which has a different block size. See the "lock up" comment over __getblk().

Quoted 5 lines
        bdev = bdget(kdev_t_to_nr(dev));
        if (!bdev) {
                printk("No block device for %s\n", __bdevname(dev));
                BUG();
        }
Quoted 8 lines
        printk("C");
        bh = __bread(bdev, pos/PAGE_SIZE, PAGE_SIZE);
        printk("D");
        bdput(bdev);
        if (!bh || (!bh->b_data)) {
                return -1;
        }
        memcpy(buf, bh->b_data, PAGE_SIZE);

You'll need to kmap bh->b_page before copying the data.

Quoted 4 lines

It works *once*, second time it deadlocks in __bread(). I tried both bforget() and brelse(). Kernel is 2.5.13. What am I doing wrong/what's wrong?

brelse is safer.

Please try 2.5.14. 2.5.13 had a few leaky problems which could perhaps result in a pinned buffer which will cause try_to_free_buffers() to fail, which triggers the __getblk() nastiness.

Generally, if you're reading from a swap partition then it may be better to use brw_page(). bread() is backed by bdev->bd_inode->i_mapping, and there may be coherency problems if the target blocks are currently in swapper_space. Although probably your bdget() here will create a new inode with no pagecache, so it'll work OK.

It really depends on what you're trying to do. It may be best to just cook up a local buffer_head and submit the darn thing.

-

#2 lore raw
In reply to Pavel Machek
Quoted 5 lines · Andrew Morton

..

Quoted 1 line
        memcpy(buf, bh->b_data, PAGE_SIZE);

You'll need to kmap bh->b_page before copying the data.

No you won't. blockdev mappings don't use highmem.

-

#3 lore raw
In reply to Pavel Machek

Hi!

Quoted 9 lines
Quoted 9 lines

For swsusp, I kind of need to read 4K from given block device.

Here's my attempt:

static int bdev_read_page(kdev_t dev, long pos, void *buf)

{
        struct buffer_head *bh;
        struct block_device *bdev;
Quoted 6 lines
Quoted 2 lines

if (pos%PAGE_SIZE) panic("Sorry, dave, I can't let you do
that!\n");

It's possible I guess that someone has a pinned buffer against the same page which has a different block size. See the "lock up" comment over __getblk().

Well, I'm doing it during boot, and this is swap partition; it should not have been accessed previously.

Quoted 14 lines
Quoted 5 lines
        bdev = bdget(kdev_t_to_nr(dev));
        if (!bdev) {
                printk("No block device for %s\n", __bdevname(dev));
                BUG();
        }
Quoted 8 lines
        printk("C");
        bh = __bread(bdev, pos/PAGE_SIZE, PAGE_SIZE);
        printk("D");
        bdput(bdev);
        if (!bh || (!bh->b_data)) {
                return -1;
        }
        memcpy(buf, bh->b_data, PAGE_SIZE);
Quoted 1 line

You'll need to kmap bh->b_page before copying the data.

This machine does not have himem.

Quoted 6 lines
Quoted 3 lines

It works *once*, second time it deadlocks in __bread(). I tried both bforget() and brelse(). Kernel is 2.5.13. What am I doing wrong/what's wrong?

brelse is safer.

Quoted 7 lines

Please try 2.5.14. 2.5.13 had a few leaky problems which could perhaps result in a pinned buffer which will cause try_to_free_buffers() to fail, which triggers the __getblk() nastiness.

Generally, if you're reading from a swap partition then it may be better to use brw_page(). bread() is backed

It is swap partition, but system does not yet know its swap at that point. This is next boot, that partition was not yet accessed.
Pavel

Signature · 2 lines
Casualities in World Trade Center: ~3k dead inside the building,
cryptography in U.S.A. and free speech in Czech Republic.
#4 lore raw
Quoted 9 lines · Pavel Machek

Well, I'm doing it during boot, and this is swap partition; it should not have been accessed previously.

Quoted 6 lines
Quoted 6 lines
        bdev = bdget(kdev_t_to_nr(dev));
        if (!bdev) {
                printk("No block device for %s\n", __bdevname(dev));
                BUG();
        }
        printk("C");

blkdev_open(bdev, FMODE_READ, O_RDONLY, BDEV_RAW)

Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
        bh = __bread(bdev, pos/PAGE_SIZE, PAGE_SIZE);
        printk("D");
        bdput(bdev);

nope - blkdev_put(bdev), and do it _after_ you are done with buffers. Oh, and you need to do brelse().

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
        if (!bh || (!bh->b_data)) {
                return -1;

However, I would really suggest to open the bugger once, do all IO and then close it. See how raw.c and friends deal with these problems.

#5 lore raw
In reply to Pavel Machek

Hi!

Quoted 11 lines
Quoted 9 lines

Well, I'm doing it during boot, and this is swap partition; it should not have been accessed previously.

Quoted 6 lines
Quoted 6 lines
        bdev = bdget(kdev_t_to_nr(dev));
        if (!bdev) {
                printk("No block device for %s\n", __bdevname(dev));
                BUG();
        }
        printk("C");

blkdev_open(bdev, FMODE_READ, O_RDONLY, BDEV_RAW)

blkdev_open is

fs.h:extern int blkdev_open(struct inode *, struct file *);

... I can't see how to use it in this context.

Quoted 5 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
        if (!bh || (!bh->b_data)) {
                return -1;

However, I would really suggest to open the bugger once, do all IO and then close it. See how raw.c and friends deal with these problems.

Performance should not matter here.
Pavel

Signature · 2 lines
Casualities in World Trade Center: ~3k dead inside the building,
cryptography in U.S.A. and free speech in Czech Republic.
#6 lore raw
Quoted 12 lines · Pavel Machek

Hi!

Quoted 10 lines
Quoted 9 lines

Well, I'm doing it during boot, and this is swap partition; it should not have been accessed previously.

Quoted 6 lines
Quoted 6 lines
        bdev = bdget(kdev_t_to_nr(dev));
        if (!bdev) {
                printk("No block device for %s\n", __bdevname(dev));
                BUG();
        }
        printk("C");
Quoted 3 lines
Quoted 1 line

blkdev_open(bdev, FMODE_READ, O_RDONLY, BDEV_RAW)

blkdev_open is

grr... s/open/get/ - sorry.

Quoted 11 lines

fs.h:extern int blkdev_open(struct inode *, struct file *);

... I can't see how to use it in this context.

Quoted 5 lines
Quoted 2 lines
Quoted 2 lines
Quoted 2 lines
        if (!bh || (!bh->b_data)) {
                return -1;

However, I would really suggest to open the bugger once, do all IO and then close it. See how raw.c and friends deal with these problems.

Performance should not matter here.

Yeah, but amount of places that mess with kdev_t should.

#7 lore raw
In reply to Pavel Machek

Hi!

Quoted 39 lines
Quoted 6 lines

Hi!

For swsusp, I kind of need to read 4K from given block device.

Here's my attempt:

Quoted 7 lines

static int bdev_read_page(kdev_t dev, long pos, void *buf)

{
        struct buffer_head *bh;
        struct block_device *bdev;

        if (pos%PAGE_SIZE) panic("Sorry, dave, I can't let you do

that!\n");

It's possible I guess that someone has a pinned buffer against the same page which has a different block size. See the "lock up" comment over __getblk().

Quoted 5 lines
        bdev = bdget(kdev_t_to_nr(dev));
        if (!bdev) {
                printk("No block device for %s\n", __bdevname(dev));
                BUG();
        }
Quoted 8 lines
        printk("C");
        bh = __bread(bdev, pos/PAGE_SIZE, PAGE_SIZE);
        printk("D");
        bdput(bdev);
        if (!bh || (!bh->b_data)) {
                return -1;
        }
        memcpy(buf, bh->b_data, PAGE_SIZE);

You'll need to kmap bh->b_page before copying the data.

Quoted 4 lines

It works *once*, second time it deadlocks in __bread(). I tried both bforget() and brelse(). Kernel is 2.5.13. What am I doing wrong/what's wrong?

Quoted 6 lines

brelse is safer.

Please try 2.5.14. 2.5.13 had a few leaky problems which could perhaps result in a pinned buffer which will cause try_to_free_buffers() to fail, which triggers the __getblk() nastiness.

Went with brelse(), and switched to 2.5.14; still deadlocks on second read.
Pavel

Signature · 2 lines
Casualities in World Trade Center: ~3k dead inside the building,
cryptography in U.S.A. and free speech in Czech Republic.
#8 lore raw
In reply to Pavel Machek

Hi!

Quoted 6 lines
Quoted 3 lines

It is swap partition, but system does not yet know its swap at that point. This is next boot, that partition was not yet accessed.
Pavel

In that case, I don't know. Sorry. sysrq-T is your friend (and kgdb is your god). Let me know...

Call trace is

c01229ed -- bdev_read_page
c0134b5b -- __bread

That's stable (as expected). Under that, it was seen in

c0134a57 -- __getblk
blk_get_queue
ata_get_queue

Second try:

c0134a28 -- __getblk
__get_hash_table

Third try:

c0134a28 -- __getblk
c01343cc -- __get_hash_table

Fourth try:

c0134abd -- __getblk

... I should get some sleep I guess...
Pavel

Signature · 2 lines
Casualities in World Trade Center: ~3k dead inside the building,
cryptography in U.S.A. and free speech in Czech Republic.
#9 lore raw
In reply to Pavel Machek
Quoted 6 lines · Pavel Machek

...
Second try:

c0134a28 -- __getblk
__get_hash_table

hmm. It looks like your blockdev may have the wrong blocksize in bd_inode->i_blkbits, and __get_hash_table cannot find the 4k block against the 1k blocksize device. It keeps returning zero.

The new pagecache-based __get_hash_table uses bd_inode->i_blkbits to go from a block number to a pagecache index. That works fine when called via the official `bread()' function, but probably your __bread() approach has confused it.

Try running set_blocksize(bdev, PAGE_SIZE) before calling __bread().

-