1. 程式人生 > >Linux那些事兒之我是SCSI硬碟(4)三座大山(一)

Linux那些事兒之我是SCSI硬碟(4)三座大山(一)

好不容易結束了sd_spinup_disk(),馬上我們就遇到了三座大山.它們是sd_read_capacity(),sd_read_write_protect_flag(),sd_read_cache_type(),要繼續往下看,我們不得不先推翻這三座大山.舊的三座大山已經在毛主席的英明領導下成功推翻了,但是今天我們的人民卻身處新三座大山的壓迫之下,眼前這三個函式堪比臭名昭著的房改醫改教改.要知道整個sd.c這個檔案也不過是1900行,可是光這三個函式就佔了360行,你不服不行啊!

第一座大山,sd_read_capacity.    1130 /*    1131 * read disk capacity
   1132 */    1133 static void    1134 sd_read_capacity(struct scsi_disk *sdkp, unsigned char *buffer)    1135 {    1136         unsigned char cmd[16];    1137         int the_result, retries;    1138         int sector_size = 0;    1139         int longrc = 0;    1140         struct scsi_sense_hdr sshdr;
   1141         int sense_valid = 0;    1142         struct scsi_device *sdp = sdkp->device;    1143    1144 repeat:    1145         retries = 3;    1146         do {    1147                 if (longrc) {    1148                         memset((void *) cmd, 0, 16);    1149                         cmd[0] = SERVICE_ACTION_IN;
   1150                         cmd[1] = SAI_READ_CAPACITY_16;    1151                         cmd[13] = 12;    1152                         memset((void *) buffer, 0, 12);    1153                 } else {    1154                         cmd[0] = READ_CAPACITY;    1155                         memset((void *) &cmd[1], 0, 9);    1156                         memset((void *) buffer, 0, 8);    1157                 }    1158    1159                 the_result = scsi_execute_req(sdp, cmd, DMA_FROM_DEVICE,    1160                                               buffer, longrc ? 12 : 8, &sshdr,    1161                                               SD_TIMEOUT, SD_MAX_RETRIES);    1162    1163                 if (media_not_present(sdkp, &sshdr))    1164                         return;    1165    1166                 if (the_result)    1167                         sense_valid = scsi_sense_valid(&sshdr);    1168                 retries--;    1169    1170         } while (the_result && retries);    1171    1172         if (the_result && !longrc) {    1173                 sd_printk(KERN_NOTICE, sdkp, "READ CAPACITY failed/n");    1174                 sd_print_result(sdkp, the_result);    1175                 if (driver_byte(the_result) & DRIVER_SENSE)    1176                         sd_print_sense_hdr(sdkp, &sshdr);    1177                 else    1178                         sd_printk(KERN_NOTICE, sdkp, "Sense not available./n");    1179    1180                 /* Set dirty bit for removable devices if not ready -    1181                  * sometimes drives will not report this properly. */    1182                 if (sdp->removable &&    1183                     sense_valid && sshdr.sense_key == NOT_READY)    1184                         sdp->changed = 1;    1185    1186                 /* Either no media are present but the drive didn't tell us,    1187                    or they are present but the read capacity command fails */    1188                 /* sdkp->media_present = 0; -- not always correct */    1189                 sdkp->capacity = 0; /* unknown mapped to zero - as usual */    1190    1191                 return;    1192         } else if (the_result && longrc) {    1193                 /* READ CAPACITY(16) has been failed */    1194                 sd_printk(KERN_NOTICE, sdkp, "READ CAPACITY(16) failed/n");    1195                 sd_print_result(sdkp, the_result);    1196                 sd_printk(KERN_NOTICE, sdkp, "Use 0xffffffff as device size/n");    1197    1198                 sdkp->capacity = 1 + (sector_t) 0xffffffff;    1199                 goto got_data;    1200         }    1201    1202         if (!longrc) {    1203                 sector_size = (buffer[4] << 24) |    1204                         (buffer[5] << 16) | (buffer[6] << 8) | buffer[7];    1205                 if (buffer[0] == 0xff && buffer[1] == 0xff &&    1206                     buffer[2] == 0xff && buffer[3] == 0xff) {    1207                         if(sizeof(sdkp->capacity) > 4) {    1208                                 sd_printk(KERN_NOTICE, sdkp, "Very big device. "    1209                                           "Trying to use READ CAPACITY(16)./n");    1210                                 longrc = 1;    1211                                 goto repeat;    1212                         }    1213                         sd_printk(KERN_ERR, sdkp, "Too big for this kernel. Use "    1214                                   "a kernel compiled with support for large "    1215                                   "block devices./n");    1216                         sdkp->capacity = 0;    1217                         goto got_data;    1218                 }    1219                 sdkp->capacity = 1 + (((sector_t)buffer[0] << 24) |    1220                         (buffer[1] << 16) |    1221                         (buffer[2] << 8) |    1222                         buffer[3]);    1223         } else {    1224                 sdkp->capacity = 1 + (((u64)buffer[0] << 56) |    1225                         ((u64)buffer[1] << 48) |    1226                         ((u64)buffer[2] << 40) |    1227                         ((u64)buffer[3] << 32) |    1228                         ((sector_t)buffer[4] << 24) |    1229                         ((sector_t)buffer[5] << 16) |    1230                         ((sector_t)buffer[6] << 8) |    1231                         (sector_t)buffer[7]);    1232    1233                 sector_size = (buffer[8] << 24) |    1234                         (buffer[9] << 16) | (buffer[10] << 8) | buffer[11];    1235         }    1236    1237         /* Some devices return the total number of sectors, not the    1238          * highest sector number. Make the necessary adjustment. */    1239         if (sdp->fix_capacity) {    1240                 --sdkp->capacity;    1241    1242         /* Some devices have version which report the correct sizes    1243          * and others which do not. We guess size according to a heuristic    1244          * and err on the side of lowering the capacity. */    1245         } else {    1246                 if (sdp->guess_capacity)    1247                         if (sdkp->capacity & 0x01) /* odd sizes are odd */    1248                                 --sdkp->capacity;    1249         }    1250    1251 got_data:    1252         if (sector_size == 0) {    1253                 sector_size = 512;    1254                 sd_printk(KERN_NOTICE, sdkp, "Sector size 0 reported, "    1255                           "assuming 512./n");    1256         }    1257    1258         if (sector_size != 512 &&    1259             sector_size != 1024 &&    1260             sector_size != 2048 &&    1261             sector_size != 4096 &&    1262             sector_size != 256) {    1263                 sd_printk(KERN_NOTICE, sdkp, "Unsupported sector size %d./n",    1264                           sector_size);    1265                 /*    1266                  * The user might want to re-format the drive with    1267                  * a supported sectorsize. Once this happens, it    1268                  * would be relatively trivial to set the thing up.    1269                  * For this reason, we leave the thing in the table.    1270                  */    1271                 sdkp->capacity = 0;    1272                 /*    1273                  * set a bogus sector size so the normal read/write    1274                  * logic in the block layer will eventually refuse any    1275                  * request on this device without tripping over power    1276                  * of two sector size assumptions    1277                  */    1278                 sector_size = 512;    1279         }    1280         {    1281                /*    1282                  * The msdos fs needs to know the hardware sector size    1283                  * So I have created this table. See ll_rw_blk.c    1284                  * Jacques Gelinas ([email protected])    1285                  */    1286                 int hard_sector = sector_size;    1287                 sector_t sz = (sdkp->capacity/2) * (hard_sector/256);    1288                 request_queue_t *queue = sdp->request_queue;    1289                 sector_t mb = sz;   1290    1291                 blk_queue_hardsect_size(queue, hard_sector);    1292                 /* avoid 64-bit division on 32-bit platforms */    1293                 sector_div(sz, 625);    1294                 mb -= sz - 974;    1295                 sector_div(mb, 1950);    1296    1297                 sd_printk(KERN_NOTICE, sdkp,    1298                           "%llu %d-byte hardware sectors (%llu MB)/n",    1299                           (unsigned long long)sdkp->capacity,    1300                           hard_sector, (unsigned long long)mb);    1301         }    1302    1303         /* Rescale capacity to 512-byte units */    1304         if (sector_size == 4096)    1305                 sdkp->capacity <<= 3;    1306         else if (sector_size == 2048)    1307                 sdkp->capacity <<= 2;    1308         else if (sector_size == 1024)    1309                 sdkp->capacity <<= 1;    1310         else if (sector_size == 256)    1311                 sdkp->capacity >>= 1;    1312    1313         sdkp->device->sector_size = sector_size;    1314 } 洋洋灑灑200餘行.簡而言之,這個函式用一句話來表達就是知道這個磁碟的容量,或者專業一點說,傳送READ CAPACITY命令.而熟悉SCSI命令集的兄弟們應該知道,很多SCSI命令都有至少兩種版本,不同版本的命令格式會不一樣,當然返回的資訊量也不盡相同,比如READ CAPACITY命令就有10個位元組的和16個位元組的兩個版本.在SBC-2的5.10節和5.11節分別介紹的是READ CAPACITY(10) command和READ CAPACITY(16) command.後者比前者多一個保護資訊.但是在我們讀之前我們並不知道該用哪個命令,所以這裡的基本思路就是先用短命令,如果失敗了就試一下長命令,這就是1211行goto repeat的目的.在goto repeat之前1210行設定了longrc為1.我們這裡先給出來自SBC-2中對READ CAPACITY命令的格式定義: 我們可以用一個例項來描述這個命令,sg_readcap可以手工傳送READ CAPACITY命令.下面是針對我的一個號稱128M的U盤傳送這個命令的結果. [[email protected] ~]# sg_readcap /dev/sdc Read Capacity results:    Last logical block address=257535 (0x3edff), Number of blocks=257536    Logical block length=512 bytes Hence:    Device size: 131858432 bytes, 125.8 MiB, 0.13 GB 與此同時,我們結合程式碼來看,這個函式實際上比較麻煩的地方在於對buffer陣列的判斷.實際上buffer陣列裝載了READ CAPACITY命令的返回資訊.而我們從1203行開始判斷,首先我們知道這個buffer是我們在sd_revalidate_disk()中申請的.其大小為SD_BUF_SIZE,即512個位元組.那麼這個buffer的資料究竟是什麼模樣呢?SBC-2中Table-35對READ CAPACITY(10)的返回資料給出瞭如圖的格式, 這裡byte4,byte5,byte6,byte7共同描述了Block的大小.即所謂的扇區大小,或者說程式碼中的sector_size,大多數情況下我們看到的都是512bytes.這裡我的這個U盤當然也屬於這種情況. RETURNED LOGICAL BLOCK ADDRESS就是告訴你這個裝置有多少個Block,或者通俗點說,有多少個扇區.當然,更準確地說,如果你這個磁碟有N個Block,那麼這裡返回的是最後一個Block的編號,因為編號是從0開始,所以最後一個Block的編號就是N-1.所以這裡返回的是N-1.而SBC-2規定,倘若byte0,byte1,byte2,byte3如果全為FF,那麼說明READ CAPACITY(10)不足以讀取這塊磁碟的容量.這有點類似於傳說中的緩衝區溢位.這種情況下再判斷一下,如果sizeof(sdkp->capacity)確實大於4,那麼這裡溢位了我們goto repeat,改而傳送READ CAPACITY(16).實際上,因為capacity是sector_t型別的,而在include/linux/types.h中,sector_t是這麼定義的,     140 #ifdef CONFIG_LBD     141 typedef u64 sector_t;     142 #else     143 typedef unsigned long sector_t;     144 #endif 所以,sector_t的size有可能是是大於4的,也有可能是等於4的.如果等於4那就沒辦法了.只能設定capacity為0.我們沒有辦法記錄下究竟有多少個扇區,那麼我們大不了就不記錄.(同時我們下面也可以看到幾處我們設定了capacity為0,其目的都是一樣,只做力所能及的事情,而不是強人所難,畢竟強扭的瓜不甜.) 當然如果沒有溢位,那麼就執行1219行,設定sdkp的capacity,剛才說了,它和byte0,byte1,byte2,byte3的共同作用的區別就是N和N-1的關係,所以這裡我們看到需要加上1.因此sdkp->capacity記錄的就是磁碟有多少個扇區. 而1223行這個else這一段,就是針對長命令的buffer進行處理的,因為SBC-2規定了,長命令的返回結果是下面這幅圖這樣的: 可以看出,這次byte0,byte1,…,byte7這8個byte共同作用來表示了Block數.而byte8,byte9,byte10,byte11共同作用表示了block的大小,或者說扇區大小. 1239行說的也就是N和N-1的那件事,有些裝置不按常理出牌,它彙報的時候已經把那個1給包括進來了,所以這裡咱們只能再減一,凡是有這種特殊需求的裝置會設定fix_capacity. 1245行又是針對另外一些不按常理出牌的裝置的應對措施.這個咱就飄過了.畢竟連磁碟的大小都要別人去猜這廠家也太無恥了. 1252行,對於那些內向的裝置,我們只能假設它們是遵守遊戲規則的,我們假設它們的扇區大小是大眾化的512. 另一方面,1258行這一段,眾所周知,扇區大小總是512,1024,2048,4096,最次的也是256.除此之外的裝置基本上就可以去參加裝置殘奧會了,沒必要拿出來丟人現眼. 1280行至1301行的目的在註釋裡說得很清楚,咱們可以飄過不理.只是需要注意1291行呼叫了blk_queue_hardsect_size(),這個函式非常的短,就是一句話,即把一個struct request_queue_t指標的成員hardsect_size的值設定為這裡的引數hard_sector.還是那句話,基本上也就是設定成512,畢竟這是絕對主流.如果你的裝置非要顯示一下80後的與眾不同的個性,那我也沒辦法.只是莊子曾經曰過:”莫裝吊,裝吊遭狗咬!” 1304行開始的這一段if-else if,就是針對sector_size調整一下capacity,因為capacity應該用來記錄有多少個扇區,而我們希望在程式碼中統一使用512位元組的扇區,(這也是Linux中的一貫規矩)所以這裡需要按比例調整一下.即原本讀出來是說有100個扇區,但是每個扇區比如是4096個位元組,那麼如果我們要以從軟體角度來說以512位元組進行訪問,那麼我就可以記錄說這個磁碟有800個扇區. 最後,1313行,把sector_size也記錄在sdkp的成員struct scsi_device指標device的sector_size內. 原文見:http://blog.csdn.net/fudan_abc/article/details/1927932

相關推薦

Linux那些事兒 是PCI(4)初始化()

解析完了PCI的那些核心引數,再翻過多少座山跨過多少條河,核心就會遇到init/main.c裡一個名叫do_initcalls的函式。do_initcalls對核心來說只不過是漫長冒險旅程中的一個驛站,對PCI這個故事來說卻是命運轉輪的開始,核心在它裡邊完成了對.initca

Linux那些事兒SCSI硬碟(4)三座大山()

好不容易結束了sd_spinup_disk(),馬上我們就遇到了三座大山.它們是sd_read_capacity(),sd_read_write_protect_flag(),sd_read_cache_type(),要繼續往下看,我們不得不先推翻這三座大山.舊的三座大山

Linux那些事兒是Block層(4)濃縮就是精華?()

  人,生在床上,死在床上;欲生欲死,還是在床上.這句話非常有道理.有人說它有點俗,但,我並不這麼認為.我因為經常坐在床上一邊看A片一邊看程式碼,所以對這句話體會頗深,事實上它形象的描述了我坐在床上看程式碼時複雜的心情,說欲生欲死,一點也不誇張,尤其是當我看到add_disk

Linux那些事兒是USB》是U盤(22)彼岸花的傳說(

彼岸花,花語是悲傷的回憶。 很久很久以前,城市的邊緣開滿了大片大片的曼珠沙華,它的花香有一種魔力,可以讓人想起自己前世的事情。守護曼珠沙華的是兩個妖精,一個是花妖叫曼珠,一個是葉妖叫沙華。他們守候了幾千年,可是從來沒有見過面,因為開花時,就沒有葉子,有葉子時沒有花。他們瘋狂

Linux那些事兒是USB》是U盤(29)彼岸花的傳說(八)

對於use_sg為0的情況,我們接下來再看168行,offset是函式呼叫傳遞進來的引數,註釋裡說得很清楚,就是用來標誌偏移量的,每次複製幾個位元組它就增加幾個位元組,最大它也不能超過request_bufflen,這是顯然的。usb_stor_access_xfer_bu

Linux那些事兒是Hub(17)八大重量級函式閃亮登場()

還有人記得1996年那部史詩般的電視劇<<英雄無悔>>嗎?那年我初二.這部戲讓我認識了濮存昕,也是這部戲確立了濮存昕少婦殺手的地位,後來大肆那年濮存昕去過復旦,宣傳艾滋病方面的知

Linux那些事兒是USB》是U盤(21)傳說中的URB

有人問,怎麼寫一個驅動寫這麼久啊? 的確,一路走來,大家都不容易,但既然已經走到今天,我們能做的也只有是堅持下去。 usb_stor_acquire_resources(),從名字上來看是獲取資源。什麼是資源?之前不是申請了一大堆記憶體了嗎?寫個USB裝置驅動程式怎麼這麼麻

Linux那些事兒是U盤(49)跟著感覺走()

,fake_senseneed_auto_sense,sense,..,,fake_sense1.1.,usb_stor_sense_invalidCDB,? 讓我們把鏡頭對準drivers/usb/storage/scsiglue.c, 479 /* To Report

【轉】Linux那些事兒 戲說USB(6)棵樹()

什麼是USB controller?在一個USB系統中只能有一個host,其實說白了就是咱們的主機,而USB和主機的介面就是host controller,你的主機總不可能只能有一個USB host controller吧,所以說一個主機可以支援多個host controller,比如分別屬於不同廠商的。那麼

【轉】Linux那些事兒 戲說USB(4)漫漫辛酸路

70 config USB_OHCI_HCD71         tristate "OHCI HCD support"72         depends on USB && USB_ARCH_HAS_OHCI73         select ISP1301_OMAP if MACH_OM

【轉】Linux那些事兒 戲說USB(15)繁華落盡

臺灣作家林清玄在接受記者採訪時,如此評價自己的30多年寫作生涯:“第一個十年我才華橫 溢,‘賊光閃現’,令周邊黯然失色;第二個十年,我終於‘寶光現形’,不再去搶風頭,反而與身邊的美麗相得益彰;進入第三個十年,繁華落盡見真醇,我進入 了‘醇光初現’的階段,真正體味到了境界之美。

Linux那些事兒 戲說USB(12)從這裡開始

任小強們說房價高漲從現在開始,股評家們說牛市從5000點開始。他們的開始需要我們的錢袋,我的開始只需要一臺電腦,最好再有一杯茶,伴著幾支小曲兒,不盯著錢總是會比較愜意的。生容易,活容易,生活不容易,因為

【轉】Linux那些事兒 戲說USB(3)PK

最初的設計目標就是替代序列、並行等各種低速匯流排,以達到以一種單一型別的匯流排連線各種不同的裝置。它現在幾乎可以支援所有連線到PC上的裝置,99年提出的USB2.0理論上可以達到480Mbps的速度。它與串列埠、並口等的這場PK從一開始就是不平等的,這樣的開始註定了以什麼樣的結果結束,只能說命運選擇了USB。

分散式事務那些事兒TCC

一、TCC簡介 TCC是一種比較成熟的分散式事務解決方案,可用於解決跨庫操作的資料一致性問題; TCC是服務化的兩階段程式設計模型,其Try、Confirm、Cancel 3個方法均由業務編碼實現; 其中Try操作作為一階段,負責資源的檢查和預留,Confirm操作作為二階段提交操作,執

細說網路那些事兒網路基本功():細說網路傳輸

網路基本功(一):細說網路傳輸 常言道:欲練神功,必先練好基本功。之前做了一個關於IP路由,預設閘道器和掩碼的問答貼,做完這個帖子覺得如果對網路知識點做一個系統的闡述,應該會很有幫助。 本系列文章著重於講解網路管理實際應用中常常涉及的重要知識點,儘量以實用為主。準備

談談領域模型的那些事兒 從領域獲取知識

前言:你寫過用例模型嗎?也許有;你寫過領域模型嗎?也許還沒有。在這裡,我們可以嘗試寫寫領域模型,看看它的作用、帶給我們的好處。 隨著RUP在中國的傳播,人們開始嘗試用RUP統一過程來指導軟體的設計和開發,但這些嘗試並不成功。比較普遍的,大家都開始使用用例模型來進行需求階段的

JDK原始碼那些事兒淺析Thread上篇

JAVA中多執行緒的操作對於初學者而言是比較難理解的,其實聯想到底層作業系統時我們可能會稍微明白些,對於程式而言最終都是硬體上執行二進位制指令,然而,這些又太過底層,今天來看一下JAVA中的執行緒,淺析JDK原始碼中的Thread類,之後能幫助我們更好的處理執行緒問題 前言 JDK版

Linux系統》"皮毛系列"(Linux系統的簡介與歷史發展

一、Linux系統的簡介 Linux是一套免費使用和自由傳播的類Unix作業系統,是一個基於POSIX和UNIX的多使用者、多工、支援多執行緒和多CPU的作業系統。它能執行主要的UNIX工具軟體、應用程式和網路協議。支援32位和64位硬體。Linux繼承了Unix以網路為核心的設計思想,是一個性

【黑金原創教程】【FPGA那些事兒-驅動篇I 】【實驗】流水燈模組

實驗一:流水燈模組 對於發展商而言,動土儀式無疑是最重要的任務。為此,流水燈實驗作為低階建模II的動土儀式再適合不過了。廢話少說,我們還是開始實驗吧。 圖1.1 實驗一建模圖。 如圖1.1 所示,實驗一有名為 led_funcmod的功能模組。如果無視環境訊號(時鐘訊號還有復位訊號),該功能模組只有

【黑金原創教程】【FPGA那些事兒-驅動篇I 】實驗十:PS/2模組⑤ — 擴充套件滑鼠

實驗十一:PS/2模組⑤ — 擴充套件滑鼠 當普通滑鼠即三鍵滑鼠再也無法滿足需求的時候,擴充套件滑鼠即滾輪滑鼠就誕生了,然而實驗十一的實驗目的就是實現滾輪滑鼠的驅動。不過,進入整體之前,先讓我們來了解一下滑鼠的常用命令。 圖11.1 命令F3,設定取樣頻率。 命令F3也是Set Sample Rat