00001
00044 #include <debug.h>
00045 #include <malloc.h>
00046 #include <string.h>
00047 #include <block/device.h>
00048 #include <buffer.h>
00049 #include <workqueue.h>
00050 #include <app/workqueue.h>
00051 #include <app/config_dmapool.h>
00052 #include <block/copy.h>
00053
00054 #define BLOCK_COPY_BUF_SIZE CONFIG_DMAPOOL_LARGE_OBJ_SIZE
00055 #define BLOCK_COPY_MAX_BUFFERS (CONFIG_DMAPOOL_NR_LARGE_OBJS / 2)
00056
00057 static void block_copy_read_blocks(void *data);
00058
00059 static void block_copy_complete(void *data)
00060 {
00061 struct block_copy_request *req = data;
00062
00063 if (req->completion_callback)
00064 req->completion_callback(req, req->total_blocks_copied,
00065 req->data);
00066 }
00067
00068 static void block_copy_continue(void *data)
00069 {
00070 struct block_copy_request *req = data;
00071
00072 if (req->progress_callback)
00073 req->progress_callback(req, req->total_blocks_copied,
00074 req->data);
00075
00076 workqueue_init_item(&req->work,
00077 block_copy_read_blocks, req);
00078 workqueue_add_item(&block_workqueue, &req->work);
00079 }
00080
00081 static void block_copy_write_done(
00082 struct block_device *bdev,
00083 struct block_request *breq)
00084 {
00085 struct block_copy_request *req = breq->context;
00086 uint32_t blocks_xfered = breq->bytes_xfered / req->block_size;
00087
00088 if (breq->status) {
00089 dbg_error("block copy: Error during write, status: %d\n",
00090 breq->status);
00091 req->status = req->dest_status = breq->status;
00092
00093 workqueue_init_item(&req->work,
00094 block_copy_complete, req);
00095 workqueue_add_item(&block_workqueue, &req->work);
00096 return;
00097 } else {
00098 dbg_verbose("block copy: Write completed: %zu blocks "
00099 "written\n", breq->bytes_xfered);
00100 }
00101
00102 slist_move_to_tail(&req->buffer_list, &breq->buf_list);
00103 req->total_blocks_copied += blocks_xfered;
00104
00105 if (req->total_blocks_copied < req->length)
00106 workqueue_init_item(&req->work, block_copy_continue, req);
00107 else
00108 workqueue_init_item(&req->work, block_copy_complete, req);
00109
00110 workqueue_add_item(&block_workqueue, &req->work);
00111 }
00112
00113 static void block_copy_write_blocks(void *data)
00114 {
00115 struct block_copy_request *req = data;
00116
00117 block_prepare_req(req->target, req->write_req,
00118 req->target_start + req->total_blocks_copied,
00119 req->blocks_allocated, BLK_OP_WRITE);
00120 slist_move_to_tail(&req->write_req->buf_list,
00121 &req->read_req->buf_list);
00122 block_submit_req(req->target, req->write_req);
00123 }
00124
00125 static void block_copy_read_done(
00126 struct block_device *bdev,
00127 struct block_request *breq)
00128 {
00129 struct block_copy_request *req = breq->context;
00130
00131 if (breq->status) {
00132 dbg_error("block copy: Error during read, status: %d\n",
00133 breq->status);
00134
00135 req->status = req->source_status = breq->status;
00136
00137
00138 workqueue_init_item(&req->work,
00139 block_copy_complete, req);
00140 } else {
00141 dbg_verbose("block copy: Read completed: %zu bytes read\n",
00142 breq->bytes_xfered);
00143 workqueue_init_item(&req->work, block_copy_write_blocks, req);
00144 }
00145
00146 workqueue_add_item(&block_workqueue, &req->work);
00147 }
00148
00149 static void block_copy_read_blocks(void *data)
00150 {
00151 struct block_copy_request *req = data;
00152 struct buffer *buf;
00153 struct slist *buffer_list = &req->buffer_list;
00154 uint32_t blocks_remaining = req->length - req->total_blocks_copied;
00155 uint32_t excess_blocks;
00156
00157 if (req->total_blocks_copied > 0) {
00158
00159 while (req->blocks_allocated - req->blocks_pr_buf
00160 >= blocks_remaining) {
00161 buf = slist_pop_head(buffer_list,
00162 struct buffer, node);
00163 buffer_dma_free(buf, BLOCK_COPY_BUF_SIZE);
00164 req->blocks_allocated -= req->blocks_pr_buf;
00165 }
00166
00167
00168
00169 if (req->blocks_allocated > blocks_remaining) {
00170 excess_blocks = req->blocks_allocated
00171 - blocks_remaining;
00172 buf = slist_peek_tail(buffer_list,
00173 struct buffer, node);
00174 buffer_resize(buf, BLOCK_COPY_BUF_SIZE
00175 - req->block_size * excess_blocks);
00176 req->blocks_allocated = blocks_remaining;
00177 }
00178 }
00179
00180 block_prepare_req(req->source, req->read_req,
00181 req->source_start + req->total_blocks_copied,
00182 req->blocks_allocated, BLK_OP_READ);
00183 slist_move_to_tail(&req->read_req->buf_list, buffer_list);
00184
00185 block_submit_req(req->source, req->read_req);
00186 }
00187
00201 struct block_copy_request *block_copy_alloc(
00202 uint32_t nr_blocks,
00203 uint32_t block_size,
00204 void *data)
00205 {
00206 struct block_copy_request *request;
00207 uint32_t alloc_size;
00208 struct buffer *buf;
00209 unsigned int i;
00210
00211 assert(nr_blocks > 0);
00212
00213 if (BLOCK_COPY_BUF_SIZE < block_size)
00214 return NULL;
00215 if (BLOCK_COPY_BUF_SIZE % block_size)
00216 return NULL;
00217
00218 request = malloc(sizeof(struct block_copy_request));
00219 if (!request)
00220 return NULL;
00221
00222 memset(request, 0, sizeof(struct block_copy_request));
00223
00224 request->block_size = block_size;
00225 request->blocks_pr_buf = BLOCK_COPY_BUF_SIZE / block_size;
00226
00227 request->data = data;
00228 request->length = nr_blocks;
00229 slist_init(&request->buffer_list);
00230
00231 for (i = 0; i < BLOCK_COPY_MAX_BUFFERS; i++) {
00232 uint32_t blocks_remaining;
00233
00234
00235
00236 buf = buffer_dma_alloc(BLOCK_COPY_BUF_SIZE);
00237 if (!buf)
00238 break;
00239
00240 slist_insert_tail(&request->buffer_list, &buf->node);
00241 blocks_remaining = request->length - request->blocks_allocated;
00242 alloc_size = request->blocks_pr_buf;
00243 request->blocks_allocated += alloc_size;
00244
00245 if (alloc_size >= blocks_remaining) {
00246 if (alloc_size > blocks_remaining) {
00247 request->blocks_allocated
00248 -= alloc_size - blocks_remaining;
00249 alloc_size = blocks_remaining;
00250 buffer_resize(buf, alloc_size * block_size);
00251 }
00252 break;
00253 }
00254 }
00255
00256 if (request->blocks_allocated == 0) {
00257
00258 free(request);
00259 return NULL;
00260 }
00261
00262 return request;
00263 }
00264
00273 void block_copy_free(struct block_copy_request *request)
00274 {
00275 struct buffer *buf;
00276
00277 while (!slist_is_empty(&request->buffer_list)) {
00278 buf = slist_pop_head(&request->buffer_list,
00279 struct buffer, node);
00280
00281
00282 buffer_dma_free(buf, BLOCK_COPY_BUF_SIZE);
00283 }
00284
00285 if (request->read_req)
00286 block_free_request(request->source, request->read_req);
00287 if (request->write_req)
00288 block_free_request(request->target, request->write_req);
00289
00290 free(request);
00291 }
00292
00304 int block_copy_set_source(
00305 struct block_copy_request *req,
00306 struct block_device *dev,
00307 block_addr_t lba)
00308 {
00309 assert(req);
00310 assert(dev);
00311
00312 if (req->block_size != dev->block_size)
00313 return -BLOCK_COPY_BLOCK_SIZE_MISMATCH;
00314
00315 req->source = dev;
00316 req->source_start = lba;
00317 req->read_req = block_alloc_request(dev);
00318
00319 if (!req->read_req)
00320 return -BLOCK_COPY_NOMEM;
00321
00322 req->read_req->req_done = block_copy_read_done;
00323 req->read_req->context = req;
00324 return 0;
00325 }
00326
00338 int block_copy_set_dest(
00339 struct block_copy_request *req,
00340 struct block_device *dev,
00341 block_addr_t lba)
00342 {
00343 assert(req);
00344 assert(dev);
00345
00346 if (req->block_size != dev->block_size)
00347 return -BLOCK_COPY_BLOCK_SIZE_MISMATCH;
00348
00349 req->target = dev;
00350 req->target_start = lba;
00351 req->write_req = block_alloc_request(dev);
00352
00353 if (!req->write_req)
00354 return -BLOCK_COPY_NOMEM;
00355
00356 req->write_req->req_done = block_copy_write_done;
00357 req->write_req->context = req;
00358 return 0;
00359 }
00360
00371 void block_copy_set_completion_callback(
00372 struct block_copy_request *req,
00373 void (*callback)
00374 (struct block_copy_request *, uint32_t, void *))
00375 {
00376 assert(req);
00377 req->completion_callback = callback;
00378 }
00379
00390 void block_copy_set_progress_callback(
00391 struct block_copy_request *req,
00392 void (*callback)
00393 (struct block_copy_request *, uint32_t, void *))
00394 {
00395 assert(req);
00396 req->progress_callback = callback;
00397 }
00398
00408 void block_copy_submit(struct block_copy_request *req)
00409 {
00410 assert(req);
00411 assert(req->source);
00412 assert(req->target);
00413 assert(req->read_req);
00414 assert(req->write_req);
00415
00416 req->total_blocks_copied = 0;
00417
00418 block_copy_read_blocks(req);
00419 }