You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

peermux.c 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. /*
  2. * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>.
  3. *
  4. * This program is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU General Public License as
  6. * published by the Free Software Foundation; either version 2 of the
  7. * License, or (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful, but
  10. * WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program; if not, write to the Free Software
  16. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  17. * 02110-1301, USA.
  18. *
  19. * You can also choose to distribute this program under the terms of
  20. * the Unmodified Binary Distribution Licence (as given in the file
  21. * COPYING.UBDL), provided that you have satisfied its requirements.
  22. */
  23. FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
  24. #include <stdlib.h>
  25. #include <errno.h>
  26. #include <ipxe/uri.h>
  27. #include <ipxe/xferbuf.h>
  28. #include <ipxe/peerblk.h>
  29. #include <ipxe/peermux.h>
  30. /** @file
  31. *
  32. * Peer Content Caching and Retrieval (PeerDist) protocol multiplexer
  33. *
  34. */
  35. /**
  36. * Free PeerDist download multiplexer
  37. *
  38. * @v refcnt Reference count
  39. */
  40. static void peermux_free ( struct refcnt *refcnt ) {
  41. struct peerdist_multiplexer *peermux =
  42. container_of ( refcnt, struct peerdist_multiplexer, refcnt );
  43. uri_put ( peermux->uri );
  44. xferbuf_free ( &peermux->buffer );
  45. free ( peermux );
  46. }
  47. /**
  48. * Close PeerDist download multiplexer
  49. *
  50. * @v peermux PeerDist download multiplexer
  51. * @v rc Reason for close
  52. */
  53. static void peermux_close ( struct peerdist_multiplexer *peermux, int rc ) {
  54. unsigned int i;
  55. /* Stop block download initiation process */
  56. process_del ( &peermux->process );
  57. /* Shut down all block downloads */
  58. for ( i = 0 ; i < PEERMUX_MAX_BLOCKS ; i++ )
  59. intf_shutdown ( &peermux->block[i].xfer, rc );
  60. /* Shut down all other interfaces (which may be connected to
  61. * the same object).
  62. */
  63. intf_nullify ( &peermux->info ); /* avoid potential loops */
  64. intf_shutdown ( &peermux->xfer, rc );
  65. intf_shutdown ( &peermux->info, rc );
  66. }
  67. /**
  68. * Receive content information
  69. *
  70. * @v peermux PeerDist download multiplexer
  71. * @v iobuf I/O buffer
  72. * @v meta Data transfer metadata
  73. * @ret rc Return status code
  74. */
  75. static int peermux_info_deliver ( struct peerdist_multiplexer *peermux,
  76. struct io_buffer *iobuf,
  77. struct xfer_metadata *meta ) {
  78. int rc;
  79. /* Add data to buffer */
  80. if ( ( rc = xferbuf_deliver ( &peermux->buffer, iobuf, meta ) ) != 0 )
  81. goto err;
  82. return 0;
  83. err:
  84. peermux_close ( peermux, rc );
  85. return rc;
  86. }
  87. /**
  88. * Close content information interface
  89. *
  90. * @v peermux PeerDist download multiplexer
  91. * @v rc Reason for close
  92. */
  93. static void peermux_info_close ( struct peerdist_multiplexer *peermux, int rc ){
  94. struct peerdist_info *info = &peermux->cache.info;
  95. size_t len;
  96. /* Terminate download on error */
  97. if ( rc != 0 )
  98. goto err;
  99. /* Successfully closing the content information interface
  100. * indicates that the content information has been fully
  101. * received, and initiates the actual PeerDist download.
  102. */
  103. /* Shut down content information interface */
  104. intf_shutdown ( &peermux->info, rc );
  105. /* Parse content information */
  106. if ( ( rc = peerdist_info ( info->raw.data, peermux->buffer.len,
  107. info ) ) != 0 ) {
  108. DBGC ( peermux, "PEERMUX %p could not parse content info: %s\n",
  109. peermux, strerror ( rc ) );
  110. goto err;
  111. }
  112. /* Notify recipient of total download size */
  113. len = ( info->trim.end - info->trim.start );
  114. if ( ( rc = xfer_seek ( &peermux->xfer, len ) ) != 0 ) {
  115. DBGC ( peermux, "PEERMUX %p could not presize buffer: %s\n",
  116. peermux, strerror ( rc ) );
  117. goto err;
  118. }
  119. xfer_seek ( &peermux->xfer, 0 );
  120. /* Start block download process */
  121. process_add ( &peermux->process );
  122. return;
  123. err:
  124. peermux_close ( peermux, rc );
  125. }
  126. /**
  127. * Initiate multiplexed block download
  128. *
  129. * @v peermux PeerDist download multiplexer
  130. */
  131. static void peermux_step ( struct peerdist_multiplexer *peermux ) {
  132. struct peerdist_info *info = &peermux->cache.info;
  133. struct peerdist_info_segment *segment = &peermux->cache.segment;
  134. struct peerdist_info_block *block = &peermux->cache.block;
  135. struct peerdist_multiplexed_block *peermblk;
  136. unsigned int next_segment;
  137. unsigned int next_block;
  138. int rc;
  139. /* Stop initiation process if all block downloads are busy */
  140. peermblk = list_first_entry ( &peermux->idle,
  141. struct peerdist_multiplexed_block, list );
  142. if ( ! peermblk ) {
  143. process_del ( &peermux->process );
  144. return;
  145. }
  146. /* Increment block index */
  147. next_block = ( block->index + 1 );
  148. /* Move to first/next segment, if applicable */
  149. if ( next_block >= segment->blocks ) {
  150. /* Reset block index */
  151. next_block = 0;
  152. /* Calculate segment index */
  153. next_segment = ( segment->info ? ( segment->index + 1 ) : 0 );
  154. /* If we have finished all segments and have no
  155. * remaining block downloads, then we are finished.
  156. */
  157. if ( next_segment >= info->segments ) {
  158. process_del ( &peermux->process );
  159. if ( list_empty ( &peermux->busy ) )
  160. peermux_close ( peermux, 0 );
  161. return;
  162. }
  163. /* Get content information segment */
  164. if ( ( rc = peerdist_info_segment ( info, segment,
  165. next_segment ) ) != 0 ) {
  166. DBGC ( peermux, "PEERMUX %p could not get segment %d "
  167. "information: %s\n", peermux, next_segment,
  168. strerror ( rc ) );
  169. goto err;
  170. }
  171. }
  172. /* Get content information block */
  173. if ( ( rc = peerdist_info_block ( segment, block, next_block ) ) != 0 ){
  174. DBGC ( peermux, "PEERMUX %p could not get segment %d block "
  175. "%d information: %s\n", peermux, segment->index,
  176. next_block, strerror ( rc ) );
  177. goto err;
  178. }
  179. /* Ignore block if it lies entirely outside the trimmed range */
  180. if ( block->trim.start == block->trim.end ) {
  181. DBGC ( peermux, "PEERMUX %p skipping segment %d block %d\n",
  182. peermux, segment->index, block->index );
  183. return;
  184. }
  185. /* Start downloading this block */
  186. if ( ( rc = peerblk_open ( &peermblk->xfer, peermux->uri,
  187. block ) ) != 0 ) {
  188. DBGC ( peermux, "PEERMUX %p could not start download for "
  189. "segment %d block %d: %s\n", peermux, segment->index,
  190. block->index, strerror ( rc ) );
  191. goto err;
  192. }
  193. /* Move to list of busy block downloads */
  194. list_del ( &peermblk->list );
  195. list_add_tail ( &peermblk->list, &peermux->busy );
  196. return;
  197. err:
  198. peermux_close ( peermux, rc );
  199. }
  200. /**
  201. * Receive data from multiplexed block download
  202. *
  203. * @v peermblk PeerDist multiplexed block download
  204. * @v iobuf I/O buffer
  205. * @v meta Data transfer metadata
  206. * @ret rc Return status code
  207. */
  208. static int peermux_block_deliver ( struct peerdist_multiplexed_block *peermblk,
  209. struct io_buffer *iobuf,
  210. struct xfer_metadata *meta ) {
  211. struct peerdist_multiplexer *peermux = peermblk->peermux;
  212. /* Sanity check: all block downloads must use absolute
  213. * positions for all deliveries, since they run concurrently.
  214. */
  215. assert ( meta->flags & XFER_FL_ABS_OFFSET );
  216. /* We can't use a simple passthrough interface descriptor,
  217. * since there are multiple block download interfaces.
  218. */
  219. return xfer_deliver ( &peermux->xfer, iob_disown ( iobuf ), meta );
  220. }
  221. /**
  222. * Get multiplexed block download underlying data transfer buffer
  223. *
  224. * @v peermblk PeerDist multiplexed download block
  225. * @ret xferbuf Data transfer buffer, or NULL on error
  226. */
  227. static struct xfer_buffer *
  228. peermux_block_buffer ( struct peerdist_multiplexed_block *peermblk ) {
  229. struct peerdist_multiplexer *peermux = peermblk->peermux;
  230. /* We can't use a simple passthrough interface descriptor,
  231. * since there are multiple block download interfaces.
  232. */
  233. return xfer_buffer ( &peermux->xfer );
  234. }
  235. /**
  236. * Close multiplexed block download
  237. *
  238. * @v peermblk PeerDist multiplexed block download
  239. * @v rc Reason for close
  240. */
  241. static void peermux_block_close ( struct peerdist_multiplexed_block *peermblk,
  242. int rc ) {
  243. struct peerdist_multiplexer *peermux = peermblk->peermux;
  244. /* Move to list of idle downloads */
  245. list_del ( &peermblk->list );
  246. list_add_tail ( &peermblk->list, &peermux->idle );
  247. /* If any error occurred, terminate the whole multiplexer */
  248. if ( rc != 0 ) {
  249. peermux_close ( peermux, rc );
  250. return;
  251. }
  252. /* Restart data transfer interface */
  253. intf_restart ( &peermblk->xfer, rc );
  254. /* Restart block download initiation process */
  255. process_add ( &peermux->process );
  256. }
  257. /** Data transfer interface operations */
  258. static struct interface_operation peermux_xfer_operations[] = {
  259. INTF_OP ( intf_close, struct peerdist_multiplexer *, peermux_close ),
  260. };
  261. /** Data transfer interface descriptor */
  262. static struct interface_descriptor peermux_xfer_desc =
  263. INTF_DESC_PASSTHRU ( struct peerdist_multiplexer, xfer,
  264. peermux_xfer_operations, info );
  265. /** Content information interface operations */
  266. static struct interface_operation peermux_info_operations[] = {
  267. INTF_OP ( xfer_deliver, struct peerdist_multiplexer *,
  268. peermux_info_deliver ),
  269. INTF_OP ( intf_close, struct peerdist_multiplexer *,
  270. peermux_info_close ),
  271. };
  272. /** Content information interface descriptor */
  273. static struct interface_descriptor peermux_info_desc =
  274. INTF_DESC_PASSTHRU ( struct peerdist_multiplexer, info,
  275. peermux_info_operations, xfer );
  276. /** Block download data transfer interface operations */
  277. static struct interface_operation peermux_block_operations[] = {
  278. INTF_OP ( xfer_deliver, struct peerdist_multiplexed_block *,
  279. peermux_block_deliver ),
  280. INTF_OP ( xfer_buffer, struct peerdist_multiplexed_block *,
  281. peermux_block_buffer ),
  282. INTF_OP ( intf_close, struct peerdist_multiplexed_block *,
  283. peermux_block_close ),
  284. };
  285. /** Block download data transfer interface descriptor */
  286. static struct interface_descriptor peermux_block_desc =
  287. INTF_DESC ( struct peerdist_multiplexed_block, xfer,
  288. peermux_block_operations );
  289. /** Block download initiation process descriptor */
  290. static struct process_descriptor peermux_process_desc =
  291. PROC_DESC ( struct peerdist_multiplexer, process, peermux_step );
  292. /**
  293. * Add PeerDist content-encoding filter
  294. *
  295. * @v xfer Data transfer interface
  296. * @v info Content information interface
  297. * @v uri Original URI
  298. * @ret rc Return status code
  299. */
  300. int peermux_filter ( struct interface *xfer, struct interface *info,
  301. struct uri *uri ) {
  302. struct peerdist_multiplexer *peermux;
  303. struct peerdist_multiplexed_block *peermblk;
  304. unsigned int i;
  305. /* Allocate and initialise structure */
  306. peermux = zalloc ( sizeof ( *peermux ) );
  307. if ( ! peermux )
  308. return -ENOMEM;
  309. ref_init ( &peermux->refcnt, peermux_free );
  310. intf_init ( &peermux->xfer, &peermux_xfer_desc, &peermux->refcnt );
  311. intf_init ( &peermux->info, &peermux_info_desc, &peermux->refcnt );
  312. peermux->uri = uri_get ( uri );
  313. xferbuf_umalloc_init ( &peermux->buffer,
  314. &peermux->cache.info.raw.data );
  315. process_init_stopped ( &peermux->process, &peermux_process_desc,
  316. &peermux->refcnt );
  317. INIT_LIST_HEAD ( &peermux->busy );
  318. INIT_LIST_HEAD ( &peermux->idle );
  319. for ( i = 0 ; i < PEERMUX_MAX_BLOCKS ; i++ ) {
  320. peermblk = &peermux->block[i];
  321. peermblk->peermux = peermux;
  322. list_add_tail ( &peermblk->list, &peermux->idle );
  323. intf_init ( &peermblk->xfer, &peermux_block_desc,
  324. &peermux->refcnt );
  325. }
  326. /* Attach to parent interfaces, mortalise self, and return */
  327. intf_plug_plug ( &peermux->xfer, xfer );
  328. intf_plug_plug ( &peermux->info, info );
  329. ref_put ( &peermux->refcnt );
  330. return 0;
  331. }