描述
====
块设备接口分为块设备文件接口和块设备驱动接口两个层次,块设备文件接口面向用户建立在块缓冲之上,
块设备驱动接口面向物理设备建立在块缓冲之下.在块设备驱动程序之下还有一个tq_disk缓冲层次,块设备
驱动程序可以将对物理设备的命令序列缓冲在tq_disk任务队列之中,由wait_on_buffer()来一次性的运行它们.随
便提一下,普通文件接口建立在页缓冲之上,文件系统驱动建立在页缓冲之下,而文件系统驱动基于块缓冲,
夹在块设备文件接口和块设备驱动接口之间.
块设备驱动接口由块设备函数表和读写请求函数组成,块设备函数表由blkdevs[]数组索引,读写请求函数
由blk_dev[]数组索引.每一块设备驱动程序驱动一组设备盘,每个设备盘用block_device结构来描述,块
设备文件inode的i_bdev指向对应的block_device结构.
块设备文件接口是def_blk_fops函数表,块设备驱动程序在打开时可以用自已的文件接口替换它.
def_blk_fops与块设备驱动接口,使用块缓冲通过ll_rw_block()接口调用块设备请求函数来读写块设备.
RAMDISK是直接利用块缓冲来存储数据的内存盘驱动程序,有点象RAMFS利用页缓冲来存储文件数据,它们
有异曲同工之妙.由此还可推断出:内存盘中文件系统文件的页缓冲与它的块缓冲是共享的,就是说假如我
们在一个内存盘中建立了一个ext2文件系统,将一个可执行程序拷贝到里面,然后运行该程序,那么该程序
的代码段在进程中的物理页面就是该文件所在内存盘块缓冲的缓冲页.是不是如此还有待验证.
initrd初始化内存盘设备是一个特殊的内存盘,它在内核初始化时通过加载initrd文件建立,只读而且
只能打开一次,当它关闭时自动释放所占用内存.
与块设备驱动程序相关的全局数据和结构
====================================
static struct {
const char *name; 块设备名
struct block_device_operations *bdops; 块设备函数表
} blkdevs[MAX_BLKDEV];
int * blk_size[MAX_BLKDEV]; 每个块设备的容量表
int * blksize_size[MAX_BLKDEV]; 每个块设备的块尺寸表
int read_ahead[MAX_BLKDEV]; 每种块驱动程序允许的每次最多预读扇区数
int * hardsect_size[MAX_BLKDEV]; 每个块设备扇区尺寸表
struct block_device_operations {
int (*open) (struct inode *, struct file *);
int (*release) (struct inode *, struct file *);
int (*ioctl) (struct inode *, struct file *, unsigned, unsigned long);
int (*check_media_change) (kdev_t);
int (*revalidate) (kdev_t);
};
内存盘驱动程序
==============
; drivers/block/rd.c:
#define MAJOR_NR RAMDISK_MAJOR
#define BLK_DEFAULT_QUEUE(_MAJOR) &blk_dev[_MAJOR].request_queue
static unsigned long rd_length[NUM_RAMDISKS]; /* Size of RAM disks in bytes */
static int rd_hardsec[NUM_RAMDISKS]; /* Size of real blocks in bytes */
static int rd_blocksizes[NUM_RAMDISKS]; /* Size of 1024 byte blocks */
static int rd_kbsize[NUM_RAMDISKS]; /* Size in blocks of 1024 bytes */
static devfs_handle_t devfs_handle;
static struct inode *rd_inode[NUM_RAMDISKS]; /* Protected device inodes */
int __init rd_init (void)
{
int i;
if (rd_blocksize > PAGE_SIZE || rd_blocksize < 512 ||
(rd_blocksize & (rd_blocksize-1)))
{
printk("RAMDISK: wrong blocksize %d, reverting to defaults/n",
rd_blocksize);
rd_blocksize = BLOCK_SIZE; 标准1024字节块长
}
if (register_blkdev(MAJOR_NR, "ramdisk", &fd_fops)) { 注册设备函数表
printk("RAMDISK: Could not get major %d", MAJOR_NR);
return -EIO;
}
blk_queue_make_request(BLK_DEFAULT_QUEUE(MAJOR_NR), &rd_make_request); 安装读写请求函数
for (i = 0; i < NUM_RAMDISKS; i++) {
/* rd_size is given in kB */
rd_length[ i ] = rd_size << 10; 每个内存盘的字节容量
rd_hardsec[ i ] = rd_blocksize; 每个内存盘的扇区尺寸
rd_blocksizes[ i ] = rd_blocksize; 每个内存盘的块尺寸
rd_kbsize[ i ] = rd_size; 每个内存盘的千字节容量
}
devfs_handle = devfs_mk_dir (NULL, "rd", NULL);
devfs_register_series (devfs_handle, "%u", NUM_RAMDISKS,
DEVFS_FL_DEFAULT, MAJOR_NR, 0,
S_IFBLK | S_IRUSR | S_IWUSR,
&fd_fops, NULL);
for (i = 0; i < NUM_RAMDISKS; i++)
register_disk(NULL, MKDEV(MAJOR_NR,i), 1, &fd_fops, rd_size<<1);
#ifdef CONFIG_BLK_DEV_INITRD
/* We ought to separate initrd operations here */
register_disk(NULL, MKDEV(MAJOR_NR,INITRD_MINOR), 1, &fd_fops, rd_size<<1);
#endif
hardsect_size[MAJOR_NR] = rd_hardsec; /* Size of the RAM disk blocks */
blksize_size[MAJOR_NR] = rd_blocksizes; /* Avoid set_blocksize() check */
blk_size[MAJOR_NR] = rd_kbsize; /* Size of the RAM disk in kB */
/* rd_size is given in kB */
printk("RAMDISK driver initialized: "
"%d RAM disks of %dK size %d blocksize/n",
NUM_RAMDISKS, rd_size, rd_blocksize);
return 0;
}
块设备读写请求函数
------------------
static int rd_make_request(request_queue_t * q, int rw, struct buffer_head *sbh)
{
unsigned int minor;
unsigned long offset, len;
struct buffer_head *rbh;
char *bdata;
minor = MINOR(sbh->b_rdev);
if (minor >= NUM_RAMDISKS)
goto fail;
offset = sbh->b_rsector << 9; 在内存盘中的读写位置
len = sbh->b_size;
if ((offset + len) > rd_length[minor])
goto fail;
if (rw==READA)
rw=READ;
if ((rw != READ) && (rw != WRITE)) {
printk(KERN_INFO "RAMDISK: bad command: %d/n", rw);
goto fail;
}
rbh = getblk(sbh->b_rdev, sbh->b_rsector/(sbh->b_size>>9), sbh->b_size);
; 取所请求块的缓冲块
/* I think that it is safe to assume that rbh is not in HighMem, though
* sbh might be - NeilBrown
*/
bdata = bh_kmap(sbh); 取所请求读写块的数据地址
if (rw == READ) {
if (sbh != rbh) 当缓冲块与输出块相同时表示输出块已经被块缓冲索引
memcpy(bdata, rbh->b_data, rbh->b_size); 将缓冲块数据拷贝到输出块中
} else
if (sbh != rbh)
memcpy(rbh->b_data, bdata, rbh->b_size);
bh_kunmap(sbh);
mark_buffer_protected(rbh); 保护缓冲块
brelse(rbh);
sbh->b_end_io(sbh,1); 块成功刷新
return 0;
fail:
sbh->b_end_io(sbh,0); 块更新失败
return 0;
}
static struct block_device_operations fd_fops = {
open: rd_open,
release: rd_release,
ioctl: rd_ioctl,
};
static int rd_open(struct inode * inode, struct file * filp)
{
#ifdef CONFIG_BLK_DEV_INITRD
if (DEVICE_NR(inode->i_rdev) == INITRD_MINOR) {
if (!initrd_start) return -ENODEV; 如果没有加载initrd文件
initrd_users++;
filp->f_op = &initrd_fops; 对于初始化内存盘设备,替换缺省的文件IO函数表
return 0;
}
#endif
if (DEVICE_NR(inode->i_rdev) >= NUM_RAMDISKS)
return -ENXIO;
/*
* Immunize device against invalidate_buffers() and prune_icache().
*/
if (rd_inode[DEVICE_NR(inode->i_rdev)] == NULL) {
if (!inode->i_bdev) return -ENXIO;
if ((rd_inode[DEVICE_NR(inode->i_rdev)] = igrab(inode)) != NULL)
atomic_inc(&rd_inode[DEVICE_NR(inode->i_rdev)]->i_bdev->bd_openers);
; 内存盘的多个打开者
}
MOD_INC_USE_COUNT;
return 0;
}
static int rd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
unsigned int minor;
if (!inode || !inode->i_rdev)
return -EINVAL;
minor = MINOR(inode->i_rdev);
switch (cmd) {
case BLKFLSBUF:
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
/* special: we want to release the ramdisk memory,
it's not like with the other blockdevices where
this ioctl only flushes away the buffer cache. */
if ((atomic_read(&inode->i_bdev->bd_openers) > 2))
return -EBUSY;
destroy_buffers(inode->i_rdev);
rd_blocksizes[minor] = 0;
break;
case BLKGETSIZE: /* Return device size */
if (!arg) return -EINVAL;
return put_user(rd_kbsize[minor] << 1, (long *) arg);
case BLKROSET:
case BLKROGET:
case BLKSSZGET:
return blk_ioctl(inode->i_rdev, cmd, arg);
default:
return -EINVAL;
};
return 0;
}
static int rd_release(struct inode * inode, struct file * filp)
{
MOD_DEC_USE_COUNT;
return 0;
}
初始化内存盘的文件接口
======================
static struct file_operations initrd_fops = {
read: initrd_read,
release: initrd_release,
};
static ssize_t initrd_read(struct file *file, char *buf,
size_t count, loff_t *ppos)
{
int left;
left = initrd_end - initrd_start - *ppos;
if (count > left) count = left;
if (count == 0) return 0;
copy_to_user(buf, (char *)initrd_start + *ppos, count);
*ppos += count;
return count;
}
static int initrd_release(struct inode *inode,struct file *file)
{
extern void free_initrd_mem(unsigned long, unsigned long);
lock_kernel();
if (!--initrd_users) {
blkdev_put(inode->i_bdev, BDEV_FILE);
iput(inode);
free_initrd_mem(initrd_start, initrd_end);
initrd_start = 0;
}
unlock_kernel();
return 0;
}
块设备文件IO函数
================
; fs/block_dev.c
struct block_device {
struct list_head bd_hash;
atomic_t bd_count;
/* struct address_space bd_data; */
dev_t bd_dev; /* not a kdev_t - it's a search key */
atomic_t bd_openers;
const struct block_device_operations *bd_op;
struct semaphore bd_sem; /* open/close mutex */
};
struct file_operations def_blk_fops = {
open: blkdev_open,
release: blkdev_close,
llseek: block_llseek,
read: block_read,
write: block_write,
fsync: block_fsync,
ioctl: blkdev_ioctl,
};
int blkdev_open(struct inode * inode, struct file * filp)
{
int ret = -ENXIO;
struct block_device *bdev = inode->i_bdev;
down(&bdev->bd_sem);
lock_kernel();
if (!bdev->bd_op)
bdev->bd_op = get_blkfops(MAJOR(inode->i_rdev));
; 在设备盘打开的时候安装其块设备函数表
if (bdev->bd_op) {
ret = 0;
if (bdev->bd_op->open)
ret = bdev->bd_op->open(inode,filp); 向驱动程序发送"open"消息
if (!ret)
atomic_inc(&bdev->bd_openers);
else if (!atomic_read(&bdev->bd_openers))
bdev->bd_op = NULL;
}
unlock_kernel();
up(&bdev->bd_sem);
return ret;
}
static int blkdev_close(struct inode * inode, struct file * filp)
{
return blkdev_put(inode->i_bdev, BDEV_FILE);
}
static loff_t block_llseek(struct file *file, loff_t offset, int origin)
{
long long retval;
kdev_t dev;
switch (origin) {
case 2: 尾部定位
dev = file->f_dentry->d_inode->i_rdev;
if (blk_size[MAJOR(dev)])
offset += (loff_t) blk_size[MAJOR(dev)][MINOR(dev)] << BLOCK_SIZE_BITS;
/* else? return -EINVAL? */
break;
case 1: 相对定位
offset += file->f_pos;
}
retval = -EINVAL;
if (offset >= 0) {
if (offset != file->f_pos) {
file->f_pos = offset;
file->f_reada = 0;
file->f_version = ++event;
}
retval = offset;
}
return retval;
}
ssize_t block_read(struct file * filp, char * buf, size_t count, loff_t *ppos)
{
struct inode * inode = filp->f_dentry->d_inode;
size_t block;
loff_t offset;
ssize_t blocksize;
ssize_t blocksize_bits, i;
size_t blocks, rblocks, left;
int bhrequest, uptodate;
struct buffer_head ** bhb, ** bhe;
struct buffer_head * buflist[NBUF];
struct buffer_head * bhreq[NBUF];
unsigned int chars;
loff_t size;
kdev_t dev;
ssize_t read;
dev = inode->i_rdev;
blocksize = BLOCK_SIZE; 块设备的标准尺寸是1024字节
if (blksize_size[MAJOR(dev)] && blksize_size[MAJOR(dev)][MINOR(dev)])
blocksize = blksize_size[MAJOR(dev)][MINOR(dev)];
i = blocksize;
blocksize_bits = 0;
while (i != 1) {
blocksize_bits++;
i >>= 1;
}
offset = *ppos;
if (blk_size[MAJOR(dev)])
size = (loff_t) blk_size[MAJOR(dev)][MINOR(dev)] << BLOCK_SIZE_BITS; 取设备盘容量
else
size = (loff_t) INT_MAX << BLOCK_SIZE_BITS; loff_t为64位整数,取设备盘的极限容量
if (offset > size)
left = 0; left为剩余所要读的字节,offset为块设备文件指针
/* size - offset might not fit into left, so check explicitly. */
else if (size - offset > INT_MAX)
left = INT_MAX;
else
left = size - offset;
if (left > count)
left = count;
if (left <= 0)
return 0;
read = 0;
block = offset >> blocksize_bits; 求所要读的起始块号
offset &= blocksize-1; 求所要读的在块中的起点
size >>= blocksize_bits;
rblocks = blocks = (left + offset + blocksize - 1) >> blocksize_bits; 求所要读的块
bhb = bhe = buflist; buflist作为环形队列,bhb是对设备的读指针,bhe是对用户的写指针
if (filp->f_reada) {
if (blocks < read_ahead[MAJOR(dev)] / (blocksize >> 9))
blocks = read_ahead[MAJOR(dev)] / (blocksize >> 9);
if (rblocks > blocks)
blocks = rblocks;
}
if (block + blocks > size) { 如果预读越过设备边界
blocks = size - block;
if (blocks == 0)
return 0;
}
/* We do this in a two stage process. We first try to request
as many blocks as we can, then we wait for the first one to
complete, and then we try to wrap up as many as are actually
done. This routine is rather generic, in that it can be used
in a filesystem by substituting the appropriate function in
for getblk.
This routine is optimized to make maximum use of the various
buffers and caches. */
do {
bhrequest = 0;
uptodate = 1;
while (blocks) {
--blocks;
*bhb = getblk(dev, block++, blocksize); 取缓冲页
if (*bhb && !buffer_uptodate(*bhb)) { 该块不在缓冲中
uptodate = 0;
bhreq[bhrequest++] = *bhb; 请求块排队
}
if (++bhb == &buflist[NBUF])
bhb = buflist; bhb是buflist上的循环指针,指向下一要读的块
/* If the block we have on hand is uptodate, go ahead
and complete processing. */
if (uptodate) 如果当前块在缓冲中
break;
if (bhb == bhe) 如果请求块队列已满
break;
}
/* Now request them all */
if (bhrequest) {
ll_rw_block(READ, bhrequest, bhreq); 读请求块
}
do { /* Finish off all I/O that has actually completed */
if (*bhe) {
wait_on_buffer(*bhe); 等待块解锁,即读完成,对缓冲块无效
if (!buffer_uptodate(*bhe)) { /* read error? */
brelse(*bhe);
if (++bhe == &buflist[NBUF])
bhe = buflist;
left = 0;
break;
}
}
if (left < blocksize - offset)
chars = left; 如果读终止于块内,则向用户拷贝剩余字节
else
chars = blocksize - offset; 拷贝到块尾
*ppos += chars;
left -= chars;
read += chars; 已读的字节数
if (*bhe) {
copy_to_user(buf,offset+(*bhe)->b_data,chars);
brelse(*bhe);
buf += chars; 更新用户指针
} else {
while (chars-- > 0)
put_user(0,buf++); 向用户传送0字节
}
offset = 0; 开始从块边界拷贝
if (++bhe == &buflist[NBUF])
bhe = buflist; bhe是buflist上的循环指针,指向下一要向用户写的块
} while (left > 0 && bhe != bhb && (!*bhe || !buffer_locked(*bhe)));
if (bhe == bhb && !blocks)
break;
} while (left > 0);
/* Release the read-ahead blocks */
while (bhe != bhb) { 剩余未向用户写的块属于预读块
brelse(*bhe);
if (++bhe == &buflist[NBUF])
bhe = buflist;
};
if (!read)
return -EIO;
filp->f_reada = 1;
return read;
}
ssize_t block_write(struct file * filp, const char * buf,
size_t count, loff_t *ppos)
{
struct inode * inode = filp->f_dentry->d_inode;
ssize_t blocksize, blocksize_bits, i, buffercount, write_error;
ssize_t block, blocks;
loff_t offset;
ssize_t chars;
ssize_t written;
struct buffer_head * bhlist[NBUF];
size_t size;
kdev_t dev = inode->i_rdev;
struct buffer_head * bh, *bufferlist[NBUF];
register char * p;
if (is_read_only(dev))
return -EPERM;
written = write_error = buffercount = 0;
blocksize = BLOCK_SIZE;
if (blksize_size[MAJOR(dev)] && blksize_size[MAJOR(dev)][MINOR(dev)])
blocksize = blksize_size[MAJOR(dev)][MINOR(dev)];
i = blocksize;
blocksize_bits = 0;
while(i != 1) {
blocksize_bits++;
i >>= 1;
}
block = *ppos >> blocksize_bits;
offset = *ppos & (blocksize-1);
if (blk_size[MAJOR(dev)])
size = ((loff_t) blk_size[MAJOR(dev)][MINOR(dev)] << BLOCK_SIZE_BITS) >> blocksize_bits;
else
size = INT_MAX;
while (count>0) {
if (block >= size)
return written ? written : -ENOSPC;
chars = blocksize - offset;
if (chars > count)
chars=count;
#if 0
/* get the buffer head */
{
struct buffer_head * (*fn)(kdev_t, int, int) = getblk;
if (chars != blocksize)
fn = bread;
bh = fn(dev, block, blocksize);
if (!bh)
return written ? written : -EIO;
if (!buffer_uptodate(bh))
wait_on_buffer(bh);
}
#else
bh = getblk(dev, block, blocksize);
if (!bh)
return written ? written : -EIO;
if (!buffer_uptodate(bh))
{ 如果要写的块不在块缓冲之中或者要读的块尚未读取
if (chars == blocksize) 写一整块
wait_on_buffer(bh); 对新缓冲块来说是空操作
else
{ 要写的位置起始于块的中间
bhlist[0] = bh;
if (!filp->f_reada || !read_ahead[MAJOR(dev)]) {
/* We do this to force the read of a single buffer */
blocks = 1; 当前位置上读取一块
} else { 从当前位置读取多个块
/* Read-ahead before write */
blocks = read_ahead[MAJOR(dev)] / (blocksize >> 9) / 2;
if (block + blocks > size) blocks = size - block;
if (blocks > NBUF) blocks=NBUF;
if (!blocks) blocks = 1;
for(i=1; i<blocks; i++)
{
bhlist[ i ] = getblk (dev, block+i, blocksize);
if (!bhlist[ i ])
{
while(i >= 0) brelse(bhlist[i--]);
return written ? written : -EIO;
}
}
}
ll_rw_block(READ, blocks, bhlist); 提交写任务
for(i=1; i<blocks; i++) brelse(bhlist[ i ] );
wait_on_buffer(bh); 等待读写完成
if (!buffer_uptodate(bh)) {
brelse(bh); 如果有一个块读写错误,则返回错误
return written ? written : -EIO;
}
};
};
#endif
block++;
p = offset + bh->b_data;
offset = 0;
*ppos += chars;
written += chars;
count -= chars;
copy_from_user(p,buf,chars); 从用户拷贝数据到缓冲块
p += chars;
buf += chars;
mark_buffer_uptodate(bh, 1);
mark_buffer_dirty(bh);
if (filp->f_flags & O_SYNC)
bufferlist[buffercount++] = bh; 将产生的脏块排队
else
brelse(bh);
if (buffercount == NBUF){ 当脏块数超过缓冲区数
ll_rw_block(WRITE, buffercount, bufferlist); 写脏块
for(i=0; i<buffercount; i++){
wait_on_buffer(bufferlist[ i ]); 等待完成
if (!buffer_uptodate(bufferlist[ i ]))
write_error=1;
brelse(bufferlist[ i ]);
}
buffercount=0;
}
balance_dirty(dev);
if (write_error)
break;
}
if ( buffercount ){ 写剩余的脏块
ll_rw_block(WRITE, buffercount, bufferlist);
for(i=0; i<buffercount; i++){
wait_on_buffer(bufferlist[ i ]);
if (!buffer_uptodate(bufferlist[ i ]))
write_error=1;
brelse(bufferlist[ i ]);
}
}
filp->f_reada = 1;
if(write_error)
return -EIO;
return written;
}
static int block_fsync(struct file *filp, struct dentry *dentry, int datasync)
{
return fsync_dev(dentry->d_inode->i_rdev); 刷新与块设备相关联的脏块
}
static int blkdev_ioctl(struct inode *inode, struct file *file, unsigned cmd,
unsigned long arg)
{
if (inode->i_bdev->bd_op->ioctl)
return inode->i_bdev->bd_op->ioctl(inode, file, cmd, arg);
return -EINVAL;
}
const struct block_device_operations * get_blkfops(unsigned int major)
{
const struct block_device_operations *ret = NULL;
/* major 0 is used for non-device mounts */
if (major && major < MAX_BLKDEV) {
#ifdef CONFIG_KMOD
if (!blkdevs[major].bdops) {
char name[20];
sprintf(name, "block-major-%d", major);
request_module(name);
}
#endif
ret = blkdevs[major].bdops;
}
return ret;
}
int blkdev_put(struct block_device *bdev, int kind)
{
int ret = 0;
kdev_t rdev = to_kdev_t(bdev->bd_dev); /* this should become bdev */
down(&bdev->bd_sem);
/* syncing will go here */
lock_kernel();
if (kind == BDEV_FILE || kind == BDEV_FS)
fsync_dev(rdev);
if (atomic_dec_and_test(&bdev->bd_openers)) {
/* invalidating buffers will go here */
invalidate_buffers(rdev);
}
if (bdev->bd_op->release) {
struct inode * fake_inode = get_empty_inode();
ret = -ENOMEM;
if (fake_inode) {
fake_inode->i_rdev = rdev;
ret = bdev->bd_op->release(fake_inode, NULL);
iput(fake_inode);
}
}
if (!atomic_read(&bdev->bd_openers))
bdev->bd_op = NULL; /* we can't rely on driver being */
/* kind to stay around. */
unlock_kernel();
up(&bdev->bd_sem);
return ret;
}
块设备文件inode的初始化
=======================
; fs/devices.c
void init_special_inode(struct inode *inode, umode_t mode, int rdev)
{
inode->i_mode = mode;
if (S_ISCHR(mode)) {
inode->i_fop = &def_chr_fops;
inode->i_rdev = to_kdev_t(rdev);
} else if (S_ISBLK(mode)) {
inode->i_fop = &def_blk_fops;
inode->i_rdev = to_kdev_t(rdev);
inode->i_bdev = bdget(rdev);
} else if (S_ISFIFO(mode))
inode->i_fop = &def_fifo_fops;
else if (S_ISSOCK(mode))
inode->i_fop = &bad_sock_fops;
else
printk(KERN_DEBUG "init_special_inode: bogus imode (%o)/n", mode);
}
struct block_device *bdget(dev_t dev)
{
struct list_head * head = bdev_hashtable + hash(dev);
struct block_device *bdev, *new_bdev;
spin_lock(&bdev_lock);
bdev = bdfind(dev, head);
spin_unlock(&bdev_lock);
if (bdev)
return bdev;
new_bdev = alloc_bdev(); 使用kmem_cache_alloc()分配内存
if (!new_bdev)
return NULL;
atomic_set(&new_bdev->bd_count,1);
new_bdev->bd_dev = dev;
new_bdev->bd_op = NULL;
spin_lock(&bdev_lock);
bdev = bdfind(dev, head);
if (!bdev) {
list_add(&new_bdev->bd_hash, head);
spin_unlock(&bdev_lock);
return new_bdev;
}
spin_unlock(&bdev_lock);
destroy_bdev(new_bdev); 使用kmem_cache_free()释放内存
return bdev;
}
|