主機:Gentoo Linux 11.2 with linux kernel 3.0.6
硬體平臺:FL2440(S3C2440)with linux kernel 2.6.35
本文引用地址:http://www.eepw.com.cn/article/201611/319014.htm
本文分析MTD設備的分區管理機制
分區管理實際上是將一個MTD設備分成幾個分區,將其作為單獨的MTD原始設備進行管理。
1、分區的結構體描述結構體mtd_part
/*Ourpartitionnodestructure*///分區結構信息structmtd_part{structmtd_infomtd;//mtd_info數據結構,會被加入mtd_table中structmtd_info*master;//該分區的主分區uint64_toffset;//該分區的偏移地址structlist_headlist;};
2、分區鍊表mtd_partitions
/*Ourpartitionlinkedlist*///聲明mtd_partitions鍊表staticLIST_HEAD(mtd_partitions);
3、add_mtd_partitions函數
/**Thisfunction,givenamasterMTDobjectandapartitiontable,creates*andregistersslaveMTDobjectswhichareboundtothemasteraccordingto*thepartitiondefinitions.**Wedontregisterthemaster,orexpectthecallertohavedoneso,*forreasonsofdataintegrity.*///根據一個MTD主設備和分區表,創建新的主設備下的副設備並記錄到分區表中//這裡我們不將注射被註冊到分區表中,只註冊副設備到到分區表中intadd_mtd_partitions(structmtd_info*master,conststructmtd_partition*parts,intnbparts){structmtd_part*slave;uint64_tcur_offset=0;inti;printk(KERN_NOTICE"Creating%dMTDpartitionson\"%s\":\n",nbparts,master->name);for(i=0;islave=add_one_partition(master,parts+i,i,cur_offset);if(!slave)return-ENOMEM;cur_offset=slave->offset+slave->mtd.size;}return0;}EXPORT_SYMBOL(add_mtd_partitions);
而add_one_partition函數實現如下:
//創建一個分區staticstructmtd_part*add_one_partition(structmtd_info*master,conststructmtd_partition*part,intpartno,uint64_tcur_offset){structmtd_part*slave;/*allocatethepartitionstructure*/slave=kzalloc(sizeof(*slave),GFP_KERNEL);//分配內存if(!slave){printk(KERN_ERR"memoryallocationerrorwhilecreatingpartitionsfor\"%s\"\n",master->name);del_mtd_partitions(master);returnNULL;}list_add(&slave->list,&mtd_partitions);//將原始設備表添加到分區表中/*setuptheMTDobjectforthispartition*///大部分根據master相應的信息設置MTD分區slave的信息slave->mtd.type=master->type;slave->mtd.flags=master->flags&~part->mask_flags;slave->mtd.size=part->size;slave->mtd.writesize=master->writesize;slave->mtd.oobsize=master->oobsize;slave->mtd.oobavail=master->oobavail;slave->mtd.subpage_sft=master->subpage_sft;slave->mtd.name=part->name;slave->mtd.owner=master->owner;slave->mtd.backing_dev_info=master->backing_dev_info;/*NOTE:wedontarrangeMTDsasatree;itdbeerror-prone*tohavethesamedatabeintwodifferentpartitions.*/slave->mtd.dev.parent=master->dev.parent;slave->mtd.read=part_read;slave->mtd.write=part_write;if(master->panic_write)slave->mtd.panic_write=part_panic_write;if(master->point&&master->unpoint){slave->mtd.point=part_point;slave->mtd.unpoint=part_unpoint;}if(master->get_unmapped_area)slave->mtd.get_unmapped_area=part_get_unmapped_area;if(master->read_oob)slave->mtd.read_oob=part_read_oob;if(master->write_oob)slave->mtd.write_oob=part_write_oob;if(master->read_user_prot_reg)slave->mtd.read_user_prot_reg=part_read_user_prot_reg;if(master->read_fact_prot_reg)slave->mtd.read_fact_prot_reg=part_read_fact_prot_reg;if(master->write_user_prot_reg)slave->mtd.write_user_prot_reg=part_write_user_prot_reg;if(master->lock_user_prot_reg)slave->mtd.lock_user_prot_reg=part_lock_user_prot_reg;if(master->get_user_prot_info)slave->mtd.get_user_prot_info=part_get_user_prot_info;if(master->get_fact_prot_info)slave->mtd.get_fact_prot_info=part_get_fact_prot_info;if(master->sync)slave->mtd.sync=part_sync;if(!partno&&!master->dev.class&&master->suspend&&master->resume){slave->mtd.suspend=part_suspend;slave->mtd.resume=part_resume;}if(master->writev)slave->mtd.writev=part_writev;if(master->lock)slave->mtd.lock=part_lock;if(master->unlock)slave->mtd.unlock=part_unlock;if(master->block_isbad)slave->mtd.block_isbad=part_block_isbad;if(master->block_markbad)slave->mtd.block_markbad=part_block_markbad;slave->mtd.erase=part_erase;slave->master=master;slave->offset=part->offset;if(slave->offset==MTDPART_OFS_APPEND)slave->offset=cur_offset;if(slave->offset==MTDPART_OFS_NXTBLK){slave->offset=cur_offset;if(mtd_mod_by_eb(cur_offset,master)!=0){/*Rounduptonexterasesize*/slave->offset=(mtd_div_by_eb(cur_offset,master)+1)*master->erasesize;printk(KERN_NOTICE"Movingpartition%d:""0x%012llx->0x%012llx\n",partno,(unsignedlonglong)cur_offset,(unsignedlonglong)slave->offset);}}if(slave->mtd.size==MTDPART_SIZ_FULL)slave->mtd.size=master->size-slave->offset;printk(KERN_NOTICE"0x%012llx-0x%012llx:\"%s\"\n",(unsignedlonglong)slave->offset,(unsignedlonglong)(slave->offset+slave->mtd.size),slave->mtd.name);/*letsdosomesanitychecks*/if(slave->offset>=master->size){/*letsregisteritanywaytopreserveordering*/slave->offset=0;slave->mtd.size=0;printk(KERN_ERR"mtd:partition\"%s\"isoutofreach--disabled\n",part->name);gotoout_register;}if(slave->offset+slave->mtd.size>master->size){slave->mtd.size=master->size-slave->offset;printk(KERN_WARNING"mtd:partition\"%s\"extendsbeyondtheendofdevice\"%s\"--sizetruncatedto%#llx\n",part->name,master->name,(unsignedlonglong)slave->mtd.size);}if(master->numeraseregions>1){/*Dealwithvariableerasesizestuff*/inti,max=master->numeraseregions;u64end=slave->offset+slave->mtd.size;structmtd_erase_region_info*regions=master->eraseregions;/*Findthefirsteraseregionswhichispartofthis*partition.*/for(i=0;ioffset;i++);/*Theloopsearchedfortheregion_behind_thefirstone*/if(i>0)i--;/*Pickbiggesterasesize*/for(;iif(slave->mtd.erasesizeslave->mtd.erasesize=regions[i].erasesize;}}BUG_ON(slave->mtd.erasesize==0);}else{/*Singleerasesize*/slave->mtd.erasesize=master->erasesize;}if((slave->mtd.flags&MTD_WRITEABLE)&&mtd_mod_by_eb(slave->offset,&slave->mtd)){/*Doesntstartonaboundaryofmajorerasesize*//*FIXME:Letitbewritableifitisonaboundaryof*_minor_erasesizethough*/slave->mtd.flags&=~MTD_WRITEABLE;printk(KERN_WARNING"mtd:partition\"%s\"doesntstartonaneraseblockboundary--forceread-only\n",part->name);}if((slave->mtd.flags&MTD_WRITEABLE)&&mtd_mod_by_eb(slave->mtd.size,&slave->mtd)){slave->mtd.flags&=~MTD_WRITEABLE;printk(KERN_WARNING"mtd:partition\"%s\"doesntendonaneraseblock--forceread-only\n",part->name);}slave->mtd.ecclayout=master->ecclayout;if(master->block_isbad){uint64_toffs=0;while(offsmtd.size){if(master->block_isbad(master,offs+slave->offset))slave->mtd.ecc_stats.badblocks++;offs+=slave->mtd.erasesize;}}out_register:/*registerourpartition*///最後調用add_mtd_device根據該設備的mtd_info信息添加設備鍊表,將其作為一個獨立的MTD原始設備add_mtd_device(&slave->mtd);returnslave;}
4、del_mtd_partition函數
/**ThisfunctionunregistersanddestroyallslaveMTDobjectswhichare*attachedtothegivenmasterMTDobject.*///將一個主設備下的所有副設備刪除intdel_mtd_partitions(structmtd_info*master){structmtd_part*slave,*next;list_for_each_entry_safe(slave,next,&mtd_partitions,list)//遍歷mtd_partitions鍊表,查找到指定的主設備if(slave->master==master){list_del(&slave->list);//將主設備下的附屬設備刪除del_mtd_device(&slave->mtd);//調用del_mtd_device函數將每個設備從MTD原始設備表中刪除kfree(slave);//釋放內存}return0;}EXPORT_SYMBOL(del_mtd_partitions);
5、其他的分區管理函數
/**MTDmethodswhichsimplytranslatetheeffectiveaddressandpassthrough*tothe_real_device.*///讀取某個分區的指定數據staticintpart_read(structmtd_info*mtd,loff_tfrom,size_tlen,size_t*retlen,u_char*buf){structmtd_part*part=PART(mtd);structmtd_ecc_statsstats;intres;stats=part->master->ecc_stats;if(from>=mtd->size)len=0;elseif(from+len>mtd->size)len=mtd->size-from;res=part->master->read(part->master,from+part->offset,len,retlen,buf);if(unlikely(res)){if(res==-EUCLEAN)mtd->ecc_stats.corrected+=part->master->ecc_stats.corrected-stats.corrected;if(res==-EBADMSG)mtd->ecc_stats.failed+=part->master->ecc_stats.failed-stats.failed;}returnres;}staticintpart_point(structmtd_info*mtd,loff_tfrom,size_tlen,size_t*retlen,void**virt,resource_size_t*phys){structmtd_part*part=PART(mtd);if(from>=mtd->size)len=0;elseif(from+len>mtd->size)len=mtd->size-from;returnpart->master->point(part->master,from+part->offset,len,retlen,virt,phys);}staticvoidpart_unpoint(structmtd_info*mtd,loff_tfrom,size_tlen){structmtd_part*part=PART(mtd);part->master->unpoint(part->master,from+part->offset,len);}//獲取空閒的內存驅動staticunsignedlongpart_get_unmapped_area(structmtd_info*mtd,unsignedlonglen,unsignedlongoffset,unsignedlongflags){structmtd_part*part=PART(mtd);offset+=part->offset;returnpart->master->get_unmapped_area(part->master,len,offset,flags);}staticintpart_read_oob(structmtd_info*mtd,loff_tfrom,structmtd_oob_ops*ops){structmtd_part*part=PART(mtd);intres;if(from>=mtd->size)return-EINVAL;if(ops->datbuf&&from+ops->len>mtd->size)return-EINVAL;res=part->master->read_oob(part->master,from+part->offset,ops);if(unlikely(res)){if(res==-EUCLEAN)mtd->ecc_stats.corrected++;if(res==-EBADMSG)mtd->ecc_stats.failed++;}returnres;}staticintpart_read_user_prot_reg(structmtd_info*mtd,loff_tfrom,size_tlen,size_t*retlen,u_char*buf){structmtd_part*part=PART(mtd);returnpart->master->read_user_prot_reg(part->master,from,len,retlen,buf);}staticintpart_get_user_prot_info(structmtd_info*mtd,structotp_info*buf,size_tlen){structmtd_part*part=PART(mtd);returnpart->master->get_user_prot_info(part->master,buf,len);}staticintpart_read_fact_prot_reg(structmtd_info*mtd,loff_tfrom,size_tlen,size_t*retlen,u_char*buf){structmtd_part*part=PART(mtd);returnpart->master->read_fact_prot_reg(part->master,from,len,retlen,buf);}staticintpart_get_fact_prot_info(structmtd_info*mtd,structotp_info*buf,size_tlen){structmtd_part*part=PART(mtd);returnpart->master->get_fact_prot_info(part->master,buf,len);}//分區寫函數staticintpart_write(structmtd_info*mtd,loff_tto,size_tlen,size_t*retlen,constu_char*buf){structmtd_part*part=PART(mtd);if(!(mtd->flags&MTD_WRITEABLE))return-EROFS;if(to>=mtd->size)len=0;elseif(to+len>mtd->size)len=mtd->size-to;returnpart->master->write(part->master,to+part->offset,len,retlen,buf);}staticintpart_panic_write(structmtd_info*mtd,loff_tto,size_tlen,size_t*retlen,constu_char*buf){structmtd_part*part=PART(mtd);if(!(mtd->flags&MTD_WRITEABLE))return-EROFS;if(to>=mtd->size)len=0;elseif(to+len>mtd->size)len=mtd->size-to;returnpart->master->panic_write(part->master,to+part->offset,len,retlen,buf);}staticintpart_write_oob(structmtd_info*mtd,loff_tto,structmtd_oob_ops*ops){structmtd_part*part=PART(mtd);if(!(mtd->flags&MTD_WRITEABLE))return-EROFS;if(to>=mtd->size)return-EINVAL;if(ops->datbuf&&to+ops->len>mtd->size)return-EINVAL;returnpart->master->write_oob(part->master,to+part->offset,ops);}staticintpart_write_user_prot_reg(structmtd_info*mtd,loff_tfrom,size_tlen,size_t*retlen,u_char*buf){structmtd_part*part=PART(mtd);returnpart->master->write_user_prot_reg(part->master,from,len,retlen,buf);}staticintpart_lock_user_prot_reg(structmtd_info*mtd,loff_tfrom,size_tlen){structmtd_part*part=PART(mtd);returnpart->master->lock_user_prot_reg(part->master,from,len);}staticintpart_writev(structmtd_info*mtd,conststructkvec*vecs,unsignedlongcount,loff_tto,size_t*retlen){structmtd_part*part=PART(mtd);if(!(mtd->flags&MTD_WRITEABLE))return-EROFS;returnpart->master->writev(part->master,vecs,count,to+part->offset,retlen);}staticintpart_erase(structmtd_info*mtd,structerase_info*instr){structmtd_part*part=PART(mtd);intret;if(!(mtd->flags&MTD_WRITEABLE))return-EROFS;if(instr->addr>=mtd->size)return-EINVAL;instr->addr+=part->offset;ret=part->master->erase(part->master,instr);if(ret){if(instr->fail_addr!=MTD_FAIL_ADDR_UNKNOWN)instr->fail_addr-=part->offset;instr->addr-=part->offset;}returnret;}voidmtd_erase_callback(structerase_info*instr){if(instr->mtd->erase==part_erase){structmtd_part*part=PART(instr->mtd);if(instr->fail_addr!=MTD_FAIL_ADDR_UNKNOWN)instr->fail_addr-=part->offset;instr->addr-=part->offset;}if(instr->callback)instr->callback(instr);}EXPORT_SYMBOL_GPL(mtd_erase_callback);staticintpart_lock(structmtd_info*mtd,loff_tofs,uint64_tlen){structmtd_part*part=PART(mtd);if((len+ofs)>mtd->size)return-EINVAL;returnpart->master->lock(part->master,ofs+part->offset,len);}staticintpart_unlock(structmtd_info*mtd,loff_tofs,uint64_tlen){structmtd_part*part=PART(mtd);if((len+ofs)>mtd->size)return-EINVAL;returnpart->master->unlock(part->master,ofs+part->offset,len);}//分區同步函數staticvoidpart_sync(structmtd_info*mtd){structmtd_part*part=PART(mtd);part->master->sync(part->master);}//支持電源管理的功能函數staticintpart_suspend(structmtd_info*mtd){structmtd_part*part=PART(mtd);returnpart->master->suspend(part->master);}staticvoidpart_resume(structmtd_info*mtd){structmtd_part*part=PART(mtd);part->master->resume(part->master);}staticintpart_block_isbad(structmtd_info*mtd,loff_tofs){structmtd_part*part=PART(mtd);if(ofs>=mtd->size)return-EINVAL;ofs+=part->offset;returnpart->master->block_isbad(part->master,ofs);}//標記設備地址壞塊staticintpart_block_markbad(structmtd_info*mtd,loff_tofs){structmtd_part*part=PART(mtd);intres;if(!(mtd->flags&MTD_WRITEABLE))return-EROFS;if(ofs>=mtd->size)return-EINVAL;ofs+=part->offset;res=part->master->block_markbad(part->master,ofs);if(!res)mtd->ecc_stats.badblocks++;returnres;}
下篇分析具體的MTD設備,字符設備和塊設備,待續........