bio.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. /*
  2. * Copyright (c) 2009 Travis Geiselbrecht
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining
  5. * a copy of this software and associated documentation files
  6. * (the "Software"), to deal in the Software without restriction,
  7. * including without limitation the rights to use, copy, modify, merge,
  8. * publish, distribute, sublicense, and/or sell copies of the Software,
  9. * and to permit persons to whom the Software is furnished to do so,
  10. * subject to the following conditions:
  11. *
  12. * The above copyright notice and this permission notice shall be
  13. * included in all copies or substantial portions of the Software.
  14. *
  15. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  16. * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  17. * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  18. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
  19. * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
  20. * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  21. * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  22. */
  23. #include <stdlib.h>
  24. #include <debug.h>
  25. #include <err.h>
  26. #include <string.h>
  27. #include <list.h>
  28. #include <lib/bio.h>
  29. #include <kernel/mutex.h>
  30. #define LOCAL_TRACE 0
  31. struct bdev_struct {
  32. struct list_node list;
  33. mutex_t lock;
  34. };
  35. static struct bdev_struct *bdevs;
  36. /* default implementation is to use the read_block hook to 'deblock' the device */
  37. static ssize_t bio_default_read(struct bdev *dev, void *_buf, off_t offset, size_t len)
  38. {
  39. uint8_t *buf = (uint8_t *)_buf;
  40. ssize_t bytes_read = 0;
  41. bnum_t block;
  42. int err = 0;
  43. STACKBUF_DMA_ALIGN(temp, dev->block_size); // temporary buffer for partial block transfers
  44. /* find the starting block */
  45. block = offset / dev->block_size;
  46. LTRACEF("buf %p, offset %lld, block %u, len %zd\n", buf, offset, block, len);
  47. /* handle partial first block */
  48. if ((offset % dev->block_size) != 0) {
  49. /* read in the block */
  50. err = bio_read_block(dev, temp, block, 1);
  51. if (err < 0)
  52. goto err;
  53. /* copy what we need */
  54. size_t block_offset = offset % dev->block_size;
  55. size_t tocopy = MIN(dev->block_size - block_offset, len);
  56. memcpy(buf, temp + block_offset, tocopy);
  57. /* increment our buffers */
  58. buf += tocopy;
  59. len -= tocopy;
  60. bytes_read += tocopy;
  61. block++;
  62. }
  63. LTRACEF("buf %p, block %u, len %zd\n", buf, block, len);
  64. /* handle middle blocks */
  65. if (len >= dev->block_size) {
  66. /* do the middle reads */
  67. size_t block_count = len / dev->block_size;
  68. err = bio_read_block(dev, buf, block, block_count);
  69. if (err < 0)
  70. goto err;
  71. /* increment our buffers */
  72. size_t bytes = block_count * dev->block_size;
  73. DEBUG_ASSERT(bytes <= len);
  74. buf += bytes;
  75. len -= bytes;
  76. bytes_read += bytes;
  77. block += block_count;
  78. }
  79. LTRACEF("buf %p, block %u, len %zd\n", buf, block, len);
  80. /* handle partial last block */
  81. if (len > 0) {
  82. /* read the block */
  83. err = bio_read_block(dev, temp, block, 1);
  84. if (err < 0)
  85. goto err;
  86. /* copy the partial block from our temp buffer */
  87. memcpy(buf, temp, len);
  88. bytes_read += len;
  89. }
  90. err:
  91. /* return error or bytes read */
  92. return (err >= 0) ? bytes_read : err;
  93. }
  94. static ssize_t bio_default_write(struct bdev *dev, const void *_buf, off_t offset, size_t len)
  95. {
  96. const uint8_t *buf = (const uint8_t *)_buf;
  97. ssize_t bytes_written = 0;
  98. bnum_t block;
  99. int err = 0;
  100. STACKBUF_DMA_ALIGN(temp, dev->block_size); // temporary buffer for partial block transfers
  101. /* find the starting block */
  102. block = offset / dev->block_size;
  103. LTRACEF("buf %p, offset %lld, block %u, len %zd\n", buf, offset, block, len);
  104. /* handle partial first block */
  105. if ((offset % dev->block_size) != 0) {
  106. /* read in the block */
  107. err = bio_read_block(dev, temp, block, 1);
  108. if (err < 0)
  109. goto err;
  110. /* copy what we need */
  111. size_t block_offset = offset % dev->block_size;
  112. size_t tocopy = MIN(dev->block_size - block_offset, len);
  113. memcpy(temp + block_offset, buf, tocopy);
  114. /* write it back out */
  115. err = bio_write_block(dev, temp, block, 1);
  116. if (err < 0)
  117. goto err;
  118. /* increment our buffers */
  119. buf += tocopy;
  120. len -= tocopy;
  121. bytes_written += tocopy;
  122. block++;
  123. }
  124. LTRACEF("buf %p, block %u, len %zd\n", buf, block, len);
  125. /* handle middle blocks */
  126. if (len >= dev->block_size) {
  127. /* do the middle writes */
  128. size_t block_count = len / dev->block_size;
  129. err = bio_write_block(dev, buf, block, block_count);
  130. if (err < 0)
  131. goto err;
  132. /* increment our buffers */
  133. size_t bytes = block_count * dev->block_size;
  134. DEBUG_ASSERT(bytes <= len);
  135. buf += bytes;
  136. len -= bytes;
  137. bytes_written += bytes;
  138. block += block_count;
  139. }
  140. LTRACEF("buf %p, block %u, len %zd\n", buf, block, len);
  141. /* handle partial last block */
  142. if (len > 0) {
  143. /* read the block */
  144. err = bio_read_block(dev, temp, block, 1);
  145. if (err < 0)
  146. goto err;
  147. /* copy the partial block from our temp buffer */
  148. memcpy(temp, buf, len);
  149. /* write it back out */
  150. err = bio_write_block(dev, temp, block, 1);
  151. if (err < 0)
  152. goto err;
  153. bytes_written += len;
  154. }
  155. err:
  156. /* return error or bytes written */
  157. return (err >= 0) ? bytes_written : err;
  158. }
  159. static ssize_t bio_default_erase(struct bdev *dev, off_t offset, size_t len)
  160. {
  161. /* default erase operation is to just write zeros over the device */
  162. #define ERASE_BUF_SIZE 4096
  163. uint8_t *zero_buf;
  164. zero_buf = calloc(1, ERASE_BUF_SIZE);
  165. size_t remaining = len;
  166. off_t pos = offset;
  167. while (remaining > 0) {
  168. ssize_t towrite = MIN(remaining, ERASE_BUF_SIZE);
  169. ssize_t written = bio_write(dev, zero_buf, pos, towrite);
  170. if (written < 0)
  171. return pos;
  172. pos += written;
  173. remaining -= written;
  174. if (written < towrite)
  175. return pos;
  176. }
  177. return len;
  178. }
  179. static ssize_t bio_default_read_block(struct bdev *dev, void *buf, bnum_t block, uint count)
  180. {
  181. panic("%s no reasonable default operation\n", __PRETTY_FUNCTION__);
  182. }
  183. static ssize_t bio_default_write_block(struct bdev *dev, const void *buf, bnum_t block, uint count)
  184. {
  185. panic("%s no reasonable default operation\n", __PRETTY_FUNCTION__);
  186. }
  187. static void bdev_inc_ref(bdev_t *dev)
  188. {
  189. atomic_add(&dev->ref, 1);
  190. }
  191. static void bdev_dec_ref(bdev_t *dev)
  192. {
  193. int oldval = atomic_add(&dev->ref, -1);
  194. if (oldval == 1) {
  195. // last ref, remove it
  196. DEBUG_ASSERT(!list_in_list(&dev->node));
  197. TRACEF("last ref, removing (%s)\n", dev->name);
  198. // call the close hook if it exists
  199. if (dev->close)
  200. dev->close(dev);
  201. free(dev->name);
  202. free(dev);
  203. }
  204. }
  205. bdev_t *bio_open(const char *name)
  206. {
  207. bdev_t *bdev = NULL;
  208. /* see if it's in our list */
  209. bdev_t *entry;
  210. mutex_acquire(&bdevs->lock);
  211. list_for_every_entry(&bdevs->list, entry, bdev_t, node) {
  212. DEBUG_ASSERT(entry->ref > 0);
  213. if (!strcmp(entry->name, name)) {
  214. bdev = entry;
  215. bdev_inc_ref(bdev);
  216. break;
  217. }
  218. }
  219. mutex_release(&bdevs->lock);
  220. return bdev;
  221. }
  222. void bio_close(bdev_t *dev)
  223. {
  224. DEBUG_ASSERT(dev);
  225. bdev_dec_ref(dev);
  226. }
  227. ssize_t bio_read(bdev_t *dev, void *buf, off_t offset, size_t len)
  228. {
  229. LTRACEF("dev '%s', buf %p, offset %lld, len %zd\n", dev->name, buf, offset, len);
  230. DEBUG_ASSERT(dev->ref > 0);
  231. /* range check */
  232. if (offset < 0)
  233. return -1;
  234. if (offset >= dev->size)
  235. return 0;
  236. if (len == 0)
  237. return 0;
  238. if (offset + len > dev->size)
  239. len = dev->size - offset;
  240. return dev->read(dev, buf, offset, len);
  241. }
  242. ssize_t bio_read_block(bdev_t *dev, void *buf, bnum_t block, uint count)
  243. {
  244. LTRACEF("dev '%s', buf %p, block %d, count %u\n", dev->name, buf, block, count);
  245. DEBUG_ASSERT(dev->ref > 0);
  246. /* range check */
  247. if (block > dev->block_count)
  248. return 0;
  249. if (count == 0)
  250. return 0;
  251. if (block + count > dev->block_count)
  252. count = dev->block_count - block;
  253. return dev->read_block(dev, buf, block, count);
  254. }
  255. ssize_t bio_write(bdev_t *dev, const void *buf, off_t offset, size_t len)
  256. {
  257. LTRACEF("dev '%s', buf %p, offset %lld, len %zd\n", dev->name, buf, offset, len);
  258. DEBUG_ASSERT(dev->ref > 0);
  259. /* range check */
  260. if (offset < 0)
  261. return -1;
  262. if (offset >= dev->size)
  263. return 0;
  264. if (len == 0)
  265. return 0;
  266. if (offset + len > dev->size)
  267. len = dev->size - offset;
  268. return dev->write(dev, buf, offset, len);
  269. }
  270. ssize_t bio_write_block(bdev_t *dev, const void *buf, bnum_t block, uint count)
  271. {
  272. LTRACEF("dev '%s', buf %p, block %d, count %u\n", dev->name, buf, block, count);
  273. DEBUG_ASSERT(dev->ref > 0);
  274. /* range check */
  275. if (block > dev->block_count)
  276. return 0;
  277. if (count == 0)
  278. return 0;
  279. if (block + count > dev->block_count)
  280. count = dev->block_count - block;
  281. return dev->write_block(dev, buf, block, count);
  282. }
  283. ssize_t bio_erase(bdev_t *dev, off_t offset, size_t len)
  284. {
  285. LTRACEF("dev '%s', offset %lld, len %zd\n", dev->name, offset, len);
  286. DEBUG_ASSERT(dev->ref > 0);
  287. /* range check */
  288. if (offset < 0)
  289. return -1;
  290. if (offset >= dev->size)
  291. return 0;
  292. if (len == 0)
  293. return 0;
  294. if (offset + len > dev->size)
  295. len = dev->size - offset;
  296. return dev->erase(dev, offset, len);
  297. }
  298. int bio_ioctl(bdev_t *dev, int request, void *argp)
  299. {
  300. LTRACEF("dev '%s', request %08x, argp %p\n", dev->name, request, argp);
  301. if (dev->ioctl == NULL) {
  302. return ERR_NOT_SUPPORTED;
  303. } else {
  304. return dev->ioctl(dev, request, argp);
  305. }
  306. }
  307. void bio_initialize_bdev(bdev_t *dev, const char *name, size_t block_size, bnum_t block_count)
  308. {
  309. DEBUG_ASSERT(dev);
  310. DEBUG_ASSERT(name);
  311. DEBUG_ASSERT(block_size == 512); // XXX can only deal with 512 for now
  312. list_clear_node(&dev->node);
  313. dev->name = strdup(name);
  314. dev->block_size = block_size;
  315. dev->block_count = block_count;
  316. dev->size = (off_t)block_count * block_size;
  317. dev->ref = 0;
  318. /* set up the default hooks, the sub driver should override the block operations at least */
  319. dev->read = bio_default_read;
  320. dev->read_block = bio_default_read_block;
  321. dev->write = bio_default_write;
  322. dev->write_block = bio_default_write_block;
  323. dev->erase = bio_default_erase;
  324. dev->close = NULL;
  325. }
  326. void bio_register_device(bdev_t *dev)
  327. {
  328. DEBUG_ASSERT(dev);
  329. LTRACEF(" '%s'\n", dev->name);
  330. bdev_inc_ref(dev);
  331. mutex_acquire(&bdevs->lock);
  332. list_add_head(&bdevs->list, &dev->node);
  333. mutex_release(&bdevs->lock);
  334. }
  335. void bio_unregister_device(bdev_t *dev)
  336. {
  337. DEBUG_ASSERT(dev);
  338. LTRACEF(" '%s'\n", dev->name);
  339. // remove it from the list
  340. mutex_acquire(&bdevs->lock);
  341. list_delete(&dev->node);
  342. mutex_release(&bdevs->lock);
  343. bdev_dec_ref(dev); // remove the ref the list used to have
  344. }
  345. void bio_dump_devices(void)
  346. {
  347. printf("block devices:\n");
  348. bdev_t *entry;
  349. mutex_acquire(&bdevs->lock);
  350. list_for_every_entry(&bdevs->list, entry, bdev_t, node) {
  351. printf("\t%s, size %lld, bsize %zd, ref %d\n", entry->name, entry->size, entry->block_size, entry->ref);
  352. }
  353. mutex_release(&bdevs->lock);
  354. }
  355. void bio_init(void)
  356. {
  357. bdevs = malloc(sizeof(*bdevs));
  358. list_initialize(&bdevs->list);
  359. mutex_init(&bdevs->lock);
  360. }