# Comment/uncomment the following line to disable/enable debugging #DEBUG = y # Add your debugging flag (or not) to CFLAGS ifeq ($(DEBUG),y) DEBFLAGS = -O -g -DSBULL_DEBUG # "-O" is needed to expand inlines else DEBFLAGS = -O2 endif EXTRA_CFLAGS += $(DEBFLAGS) EXTRA_CFLAGS += -I.. ifneq ($(KERNELRELEASE),) # call from kernel build system obj-m := nss_md.o else KERNELDIR ?= /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) default: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules endif clean: rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions depend .depend dep: $(CC) $(EXTRA_CFLAGS) -M *.c > .depend ifeq (.depend,$(wildcard .depend)) include .depend endif
typedef struct request_queue request_queue_t; //由于这个内核里面没有这个定义,所以自己添加。 struct member_disk { struct block_device* bdev; struct member_disk * next; }; struct nss_md_dev{ char *name; unsigned char *data; short users; unsigned long long size; unsigned long array_sectors; int level; int chunk_sectors; //每个chunk所包含的扇区数 int raid_disks; struct list_head disks; short media_change; spinlock_t lock; struct request_queue* queue; struct gendisk *gd; struct timer_list timer; struct member_disk * mdisk; int status; //标识设备是否准备好 }; typedef struct nss_md_dev nss_md_dev_t; struct strip_zone { sector_t zone_end; sector_t dev_start; int nb_dev; }; typedef struct __Disk_T { int major; int minor; int number; int raid_disks; }Disk_T; typedef struct array_info_s { int level; int raid_disks; int chunk_size; unsigned long sectors; }array_info_t; #define READY 0X0005 #define NOT_READY 0x0006
#include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> #include <linux/sched.h> #include <linux/kernel.h> #include <linux/slab.h> #include <linux/fs.h> #include <linux/errno.h> #include <linux/timer.h> #include <linux/types.h> #include <linux/fcntl.h> #include <linux/hdreg.h> #include <linux/kdev_t.h> #include <linux/vmalloc.h> #include <linux/genhd.h> #include <linux/blkdev.h> #include <linux/buffer_head.h> #include <linux/bio.h> #include <linux/mutex.h> #include <linux/math64.h> #include "nss_md.h" MODULE_LICENSE("Dual BSD/GPL"); static int nss_md_major=0; module_param(nss_md_major,int,0); static int hardsect_size = 512; module_param(hardsect_size,int,0); static int nsectors = 1024; module_param(nsectors, int, 0); static int ndevices = 1; module_param(ndevices,int,0); enum{ RM_SIMPLE = 0, RM_FULL =1 , RM_NOQUEUE = 2, }; static int request_mode = RM_SIMPLE; module_param(request_mode,int,0); #define NSS_MD_MINORS 16 #define KERNEL_SECTOR_SIZE 512 #define INVALIDATE_DELAY 30*HZ #define TEST_CMD 0x000666 #define ADD_NEW_DISK 0x000667 #define SET_ARRAY_INFO 0x000668 #define GET_ARRAY_INFO 0x000669 #define GET_DISK_INFO 0x000670 #define is_power_of_2(x) ((x) != 0 && (((x) & ((x) - 1)) == 0)) static struct nss_md_dev *Devices = NULL; //dev_t dev0,dev1,dev2; //struct block_device * bdev0,*bdev1,*bdev2; /*static void nss_md_transfer(struct nss_md_dev *dev,unsigned long sector, unsigned long nsect,char *buffer,int write) { printk("nss_md_transfer begin\n"); unsigned long offset =sector *KERNEL_SECTOR_SIZE; unsigned long nbytes =nsect*KERNEL_SECTOR_SIZE; if((offset+nbytes)>dev->size){ printk(KERN_NOTICE "Beyond-end write (%ld %ld)\n",offset,nbytes); return ; } if(write) memcpy(dev->data+offset,buffer,nbytes); else memcpy(buffer,dev->data+offset,nbytes); printk("nss_md_transfer finished\n"); }*/ /*static void nss_md_request(request_queue_t *q) { printk("nss_md_request begin\n"); struct request *req; req=blk_fetch_request(q); while(req){ struct nss_md_dev *dev=req->rq_disk->private_data; if(!blk_fs_request(req)){ printk(KERN_NOTICE "Skip non-fs request\n"); blk_end_request_all(req,0); continue; } // nss_md_transfer(dev,blk_rq_pos(req),blk_rq_cur_sectors(req), // req->buffer,rq_data_dir(req)); unsigned long offset =blk_rq_pos(req)<<9; //偏移字节数 unsigned long nbytes=blk_rq_cur_bytes(req); //剩余字节数 int err=0; if((offset+nbytes)>dev->size){ printk("bad access:block=%lu,count=%u\n", blk_rq_pos(req),blk_rq_cur_sectors(req)); err=-EIO; goto done; } if(rq_data_dir(req)==WRITE) { memcpy(dev->data+offset,req->buffer,nbytes); printk(" WRITE finished\n"); } else { memcpy(req->buffer,dev->data+offset,nbytes); printk("READ finished\n"); } done: if(!__blk_end_request_cur(req,err)) req=blk_fetch_request(q); } printk("nss_md_request finished\n"); }*/ /*static int nss_md_xfer_bio(struct nss_md_dev *dev,struct bio *bio) { int i; struct bio_vec *bvec; sector_t sector = bio->bi_sector; bio_for_each_segment(bvec,bio,i){ char *buffer = __bio_kmap_atomic(bio,i,KM_USER0); nss_md_transfer(dev,sector,bio_cur_bytes(bio)>>9, buffer,bio_data_dir(bio)==WRITE); sector +=bio_cur_bytes(bio)>>9; __bio_kunmap_atomic(bio,KM_USER0); } return 0; }*/ /*static int nss_md_xfer_request(struct nss_md_dev *dev,struct request *req) { struct bio *bio; int nsect =0; __rq_for_each_bio(bio,req){ nss_md_xfer_bio(dev,bio); nsect +=bio->bi_size/KERNEL_SECTOR_SIZE; } return nsect; }*/ /*static void nss_md_full_request(request_queue_t *q) { struct request *req; int sectors_xferred; struct nss_md_dev *dev=q->queuedata; while((req=blk_fetch_request(q))!=NULL){ if(!blk_fs_request(req)){ printk(KERN_NOTICE "Skip non-fs request\n"); blk_end_request_all(req,0); continue; } sectors_xferred=nss_md_xfer_request(dev,req); if (!__blk_end_request_cur(req, 0)) { blk_start_request(req); blk_fetch_request(q); } } }*/ static inline int is_io_in_chunk_boundary(struct nss_md_dev *mddev, unsigned int chunk_sects, struct bio *bio) { if (likely(is_power_of_2(chunk_sects))) { return chunk_sects >= ((bio->bi_sector & (chunk_sects-1)) + (bio->bi_size >> 9)); } else{ sector_t sector = bio->bi_sector; return chunk_sects >= (sector_div(sector, chunk_sects) + (bio->bi_size >> 9)); } } static int nss_md_make_request(request_queue_t *q,struct bio *bio) { nss_md_dev_t *dev=q->queuedata; unsigned int chunk_sects; const int rw =bio_data_dir(bio); int cpu,tmp; struct member_disk* tmp_disk; sector_t strip=0; unsigned int disknum=0; sector_t chunk_num=0; unsigned int chunk_offset=0; printk("nss_md_make_request begin\n"); printk("bio->bi_sector:%llu\n",bio->bi_sector); printk("dev->status:%d\n",dev->status); if(dev->status==NOT_READY) { printk("status of device is not READY\n"); bio_io_error(bio); return 0; } /*if(unlikely(bio_barrier(bio))) { bio_endio(bio,-EOPNOTSUPP); return 0; }*/ cpu=part_stat_lock(); part_stat_inc(cpu,&dev->gd->part0,ios[rw]); part_stat_add(cpu,&dev->gd->part0,sectors[rw],bio_sectors(bio)); part_stat_unlock(); chunk_sects=dev->chunk_sectors; printk("chunk_sects:%d\n",chunk_sects); if(unlikely(!is_io_in_chunk_boundary(dev,chunk_sects,bio))) { sector_t sector=bio->bi_sector; struct bio_pair *bp; if(bio->bi_vcnt!=1 || bio->bi_idx!=0) goto bad_map; if(likely(is_power_of_2(chunk_sects))) bp=bio_split(bio,chunk_sects-(sector & (chunk_sects-1))); else bp=bio_split(bio,chunk_sects- sector_div(sector,chunk_sects)); if(nss_md_make_request(q,&bp->bio1)) generic_make_request(&bp->bio1); if(nss_md_make_request(q,&bp->bio2)) generic_make_request(&bp->bio2); bio_pair_release(bp); return 0; } // sector_t tmp=bio->bi_sector; // chunk_num=div_u64(tmp,128); // chunk_num=tmp>>7; chunk_num=div_u64_rem(bio->bi_sector,chunk_sects,&chunk_offset); printk("bio->bi_sector:%llu,chunk_offset:%u,chunk_num:%llu\n",bio->bi_sector,chunk_offset,chunk_num); // strip=div_u64(chunk_num,3); strip=div_u64_rem(chunk_num,dev->raid_disks,&disknum); printk("chunk_offset:%u---strip:%llu---disknum:%u\n",chunk_offset,strip,disknum); for(tmp_disk=dev->mdisk,tmp=0;tmp<disknum;tmp++) { tmp_disk=tmp_disk->next; } bio->bi_bdev=tmp_disk->bdev; bio->bi_sector=(strip*chunk_sects)+chunk_offset; /* if(disknum==0) { bio->bi_bdev=bdev0; // bio->bi_sector=(strip<<7)+chunk_offset; } else if(disknum==1) { bio->bi_bdev=bdev1; // bio->bi_sector=(strip<<7)+chunk_offset; } else { bio->bi_bdev=bdev2; // bio->bi_sector=(strip<<7)+chunk_offset; }*/ // bio->bi_bdev=bdev1; // generic_make_request(bio); // printk("nss_md_make_request finished\n"); return 1; bad_map: printk("raid0_make_request bug: can't convert block across chunks" " or bigger than %dk %llu %d\n", chunk_sects / 2, (unsigned long long)bio->bi_sector, bio->bi_size >> 10); bio_io_error(bio); return 0; } static int nss_md_open(struct block_device *bdev,fmode_t mode) { struct nss_md_dev* dev=bdev->bd_disk->private_data; printk("open\n"); printk("open2\n"); del_timer_sync(&dev->timer); printk("open3\n"); printk("open4\n"); spin_lock(&dev->lock); printk("open5\n"); if(!dev->users) { printk("open6\n"); check_disk_change(bdev); printk("open7\n"); } dev->users++; spin_unlock(&dev->lock); printk("open8\n"); return 0; } static int nss_md_release(struct gendisk *gd ,fmode_t mode) { struct nss_md_dev *dev=gd->private_data; spin_lock(&dev->lock); dev->users--; if(!dev->users){ dev->timer.expires=jiffies+INVALIDATE_DELAY; add_timer(&dev->timer); } spin_unlock(&dev->lock); return 0; } int nss_md_media_changed(struct gendisk *gd) { struct nss_md_dev *dev=gd->private_data; return dev->media_change; } int nss_md_revalidate(struct gendisk *gd) { struct nss_md_dev *dev =gd->private_data; if(dev->media_change){ dev->media_change=0; memset(dev->data,0,dev->size); } return 0; } void nss_md_invalidate(unsigned long ldev) { struct nss_md_dev *dev=(struct nss_md_dev *)ldev; spin_lock(&dev->lock); if(dev->users ||!dev->data) printk(KERN_WARNING "sbull:timer sanity check failed\n"); else dev->media_change=1; spin_unlock(&dev->lock); } static int set_array_info(nss_md_dev_t* dev, array_info_t*array) { dev->level=array->level; dev->chunk_sectors=array->chunk_size>>9; dev->raid_disks=array->raid_disks; dev->array_sectors=array->sectors; return 0; } static int add_new_disk(nss_md_dev_t* dev, Disk_T* disk) { dev_t kdev; struct block_device * bdev; struct member_disk* memdisk; printk("come to add_new_disk()\n"); kdev=MKDEV(disk->major,disk->minor); printk("after mkdev\n"); if(disk->major!=MAJOR(kdev) || disk->minor!=MINOR(kdev)) { printk("overflow\n"); return -EOVERFLOW; } bdev=open_by_devnum(kdev,FMODE_READ|FMODE_WRITE); memdisk=(struct member_disk*)kmalloc(sizeof(struct member_disk),0); memdisk->bdev=bdev; memdisk->next=(*dev).mdisk; (*dev).mdisk=memdisk; printk("after operation of list\n"); if(disk->number==disk->raid_disks) { set_capacity(dev->gd,dev->array_sectors); (*dev).status=READY; printk("status set to READY\n"); } printk("add new disk finished\n"); return 0; } int nss_md_ioctl(struct block_device *bdev,fmode_t mode, unsigned int cmd,unsigned long arg) { void __user *argp = (void __user *)arg; struct nss_md_dev *dev = bdev->bd_disk->private_data; array_info_t array; struct member_disk * tmp_mdisk; Disk_T disk; switch(cmd){ /* case HDIO_GETGEO: printk("received cmd :HDIO_GETGEO\n"); size =dev->size*(hardsect_size/KERNEL_SECTOR_SIZE); geo.cylinders=(size &~0x3f)>>6; geo.heads =4; geo.sectors =16; geo.start=4; if(copy_to_user((void __user *)arg,&geo,sizeof(geo))) return -EFAULT; return 0;*/ case TEST_CMD: printk("cmd TEST has been received~\n"); break; case SET_ARRAY_INFO: printk("cmd SET_ARRAY_INFO has been received~\n"); if(copy_from_user(&array,argp,sizeof(array))) printk("copy from user error\n"); else { printk("level:%d,raid_disks:%d,chunk-size:%d,size:%lu\n",array.level,array.raid_disks,array.chunk_size,array.sectors); set_array_info(dev,&array); } break; case ADD_NEW_DISK: printk("cmd ADD_NEW_DISK has been received~\n"); if(copy_from_user(&disk,argp,sizeof(disk))) printk("copy from user error\n"); else { printk("major:%d,minor:%d,number:%d,raid_disks:%d\n",disk.major,disk.minor,disk.number,disk.raid_disks); if(disk.number>disk.raid_disks) { printk("the number of disks is more than raid_disks\n"); break; } else { add_new_disk(dev,&disk); for(tmp_mdisk=dev->mdisk;tmp_mdisk!=NULL;) { printk("dev:%s\n",tmp_mdisk->bdev->bd_disk->disk_name); tmp_mdisk=tmp_mdisk->next; } } } break; // case GET_ARRAY_INFO: // break; // case GET_DISK_INFO: // break; } return 0; } /* * the device operations structure. */ static struct block_device_operations nss_md_ops={ .owner =THIS_MODULE, .open =nss_md_open, .release =nss_md_release, .media_changed =nss_md_media_changed, .revalidate_disk =nss_md_revalidate, .ioctl =nss_md_ioctl }; /*static int get_diskinfo(void) { dev0=MKDEV(8,16); if(8!=MAJOR(dev0) || 16!=MINOR(dev0)) return -EOVERFLOW; bdev0=open_by_devnum(dev0,FMODE_READ|FMODE_WRITE); printk("disk0 name:%s\n",bdev0->bd_disk->disk_name); dev1=MKDEV(8,32); if(8!=MAJOR(dev1) || 32!=MINOR(dev1)) return -EOVERFLOW; bdev1=open_by_devnum(dev1,FMODE_READ|FMODE_WRITE); printk("disk1 name:%s\n",bdev1->bd_disk->disk_name); dev2=MKDEV(8,48); if(8!=MAJOR(dev2) || 48!=MINOR(dev2)) return -EOVERFLOW; bdev2=open_by_devnum(dev2,FMODE_READ|FMODE_WRITE); printk("disk2 name:%s\n",bdev2->bd_disk->disk_name); return 0; }*/ static void setup_device(struct nss_md_dev *dev,int which) { printk(KERN_ALERT "hello2\n"); // get_diskinfo(); memset(dev,0,sizeof(struct nss_md_dev)); // dev->size=nsectors*hardsect_size; //指定设备的大小 dev->size=8<<30; // dev->data =vmalloc(dev->size); printk(KERN_ALERT "hello3\n"); // if(dev->data ==NULL){ // printk(KERN_NOTICE "vmalloc failure.\n"); // return; // } printk(KERN_ALERT "hello4\n"); spin_lock_init(&dev->lock); printk(KERN_ALERT "hello5\n"); init_timer(&dev->timer); printk(KERN_ALERT "hello6\n"); dev->timer.data=(unsigned long)dev; dev->timer.function=nss_md_invalidate; /* switch(request_mode){ case RM_NOQUEUE:*/ dev->queue=blk_alloc_queue(GFP_KERNEL); if(dev->queue==NULL) goto out_vfree; blk_queue_make_request(dev->queue,nss_md_make_request); // break; /* case RM_FULL: dev->queue=blk_init_queue(nss_md_full_request,&dev->lock); if(dev->queue==NULL) goto out_vfree; break; case RM_SIMPLE: printk(KERN_ALERT "hello7\n"); dev->queue=blk_init_queue(nss_md_request,&dev->lock); printk(KERN_ALERT "hello8\n"); if(dev->queue==NULL) goto out_vfree;*/ // break; // default: // printk(KERN_NOTICE "Bad request mode %d,using simple\n",request_mode); // } printk(KERN_ALERT "hello9\n"); blk_queue_logical_block_size(dev->queue,hardsect_size); printk(KERN_ALERT "hello10\n"); dev->status=NOT_READY; printk("dev->status:%d\n",dev->status); dev->queue->queuedata=dev; dev->gd=alloc_disk(NSS_MD_MINORS); printk(KERN_ALERT "hello11\n"); if(!dev->gd){ printk(KERN_NOTICE "alloc_disk failure\n"); goto out_vfree; } dev->gd->major=nss_md_major; dev->gd->first_minor=which*NSS_MD_MINORS; dev->gd->fops=&nss_md_ops; dev->gd->queue =dev->queue; dev->gd->private_data=dev; printk(KERN_ALERT "hello12\n"); snprintf(dev->gd->disk_name,32,"nss_md%d",which); printk(KERN_ALERT "hello13\n"); // set_capacity(dev->gd,nsectors*(hardsect_size/KERNEL_SECTOR_SIZE)); // set_capacity(dev->gd,8<<21); dev->gd->flags |= GENHD_FL_EXT_DEVT; printk(KERN_ALERT "hello14\n"); add_disk(dev->gd); printk(KERN_ALERT "hello15\n"); return; out_vfree: if(dev->data) vfree(dev->data); } static int __init nss_md_init(void) { int i; printk(KERN_ALERT "nss_md_init begin\n"); nss_md_major=register_blkdev(nss_md_major,"nss_md"); if(nss_md_major<=0){ printk(KERN_WARNING "nss_md:unable to get major number\n"); return -EBUSY; } Devices=kmalloc(ndevices *sizeof(struct nss_md_dev),GFP_KERNEL); if(Devices==NULL) goto out_unregister; for(i=0;i<ndevices;i++) setup_device(Devices+i,i); printk(KERN_ALERT "nss_md_init finished\n"); return 0; out_unregister: printk(KERN_ALERT "out_unregister\n"); unregister_blkdev(nss_md_major,"nss_md"); return -ENOMEM; } static void __exit nss_md_exit(void) { int i; printk(KERN_ALERT "goodbye\n"); for(i=0;i<ndevices;i++){ struct nss_md_dev *dev=Devices +i; printk(KERN_ALERT "goodbye2\n"); del_timer_sync(&dev->timer); printk(KERN_ALERT "goodbye3\n"); if(dev->gd) { printk(KERN_ALERT "goodbye4\n"); del_gendisk(dev->gd); printk(KERN_ALERT "goodbye5\n"); put_disk(dev->gd); printk(KERN_ALERT "goodbye6\n"); } if(dev->queue) { printk(KERN_ALERT "goodbye7\n"); blk_cleanup_queue(dev->queue); printk(KERN_ALERT "goodbye8\n"); } if(dev->data) { printk(KERN_ALERT "goodbye9\n"); vfree(dev->data); printk(KERN_ALERT "goodbye10\n"); } } printk(KERN_ALERT "goodbye11\n"); unregister_blkdev(nss_md_major,"nss_md"); printk(KERN_ALERT "goodbye12\n"); kfree(Devices); } module_init(nss_md_init); module_exit(nss_md_exit);