diff options
| author | Ingo Molnar <mingo@elte.hu> | 2008-08-21 07:28:24 -0400 |
|---|---|---|
| committer | Ingo Molnar <mingo@elte.hu> | 2008-08-21 07:28:24 -0400 |
| commit | 470fba7ebe60ad9185056b080b331abad24b4df9 (patch) | |
| tree | f83bc13d97adaf5dd0e0f1d6a157b890f868577f /lib/scatterlist.c | |
| parent | 7225e75144b9718cbbe1820d9c011c809d5773fd (diff) | |
| parent | 6a55617ed5d1aa62b850de2cf66f5ede2eef4825 (diff) | |
Merge branch 'linus' into x86/doc
Diffstat (limited to 'lib/scatterlist.c')
| -rw-r--r-- | lib/scatterlist.c | 176 |
1 files changed, 130 insertions, 46 deletions
diff --git a/lib/scatterlist.c b/lib/scatterlist.c index b80c21100d78..876ba6d5b670 100644 --- a/lib/scatterlist.c +++ b/lib/scatterlist.c | |||
| @@ -295,6 +295,117 @@ int sg_alloc_table(struct sg_table *table, unsigned int nents, gfp_t gfp_mask) | |||
| 295 | EXPORT_SYMBOL(sg_alloc_table); | 295 | EXPORT_SYMBOL(sg_alloc_table); |
| 296 | 296 | ||
| 297 | /** | 297 | /** |
| 298 | * sg_miter_start - start mapping iteration over a sg list | ||
| 299 | * @miter: sg mapping iter to be started | ||
| 300 | * @sgl: sg list to iterate over | ||
| 301 | * @nents: number of sg entries | ||
| 302 | * | ||
| 303 | * Description: | ||
| 304 | * Starts mapping iterator @miter. | ||
| 305 | * | ||
| 306 | * Context: | ||
| 307 | * Don't care. | ||
| 308 | */ | ||
| 309 | void sg_miter_start(struct sg_mapping_iter *miter, struct scatterlist *sgl, | ||
| 310 | unsigned int nents, unsigned int flags) | ||
| 311 | { | ||
| 312 | memset(miter, 0, sizeof(struct sg_mapping_iter)); | ||
| 313 | |||
| 314 | miter->__sg = sgl; | ||
| 315 | miter->__nents = nents; | ||
| 316 | miter->__offset = 0; | ||
| 317 | miter->__flags = flags; | ||
| 318 | } | ||
| 319 | EXPORT_SYMBOL(sg_miter_start); | ||
| 320 | |||
| 321 | /** | ||
| 322 | * sg_miter_next - proceed mapping iterator to the next mapping | ||
| 323 | * @miter: sg mapping iter to proceed | ||
| 324 | * | ||
| 325 | * Description: | ||
| 326 | * Proceeds @miter@ to the next mapping. @miter@ should have been | ||
| 327 | * started using sg_miter_start(). On successful return, | ||
| 328 | * @miter@->page, @miter@->addr and @miter@->length point to the | ||
| 329 | * current mapping. | ||
| 330 | * | ||
| 331 | * Context: | ||
| 332 | * IRQ disabled if SG_MITER_ATOMIC. IRQ must stay disabled till | ||
| 333 | * @miter@ is stopped. May sleep if !SG_MITER_ATOMIC. | ||
| 334 | * | ||
| 335 | * Returns: | ||
| 336 | * true if @miter contains the next mapping. false if end of sg | ||
| 337 | * list is reached. | ||
| 338 | */ | ||
| 339 | bool sg_miter_next(struct sg_mapping_iter *miter) | ||
| 340 | { | ||
| 341 | unsigned int off, len; | ||
| 342 | |||
| 343 | /* check for end and drop resources from the last iteration */ | ||
| 344 | if (!miter->__nents) | ||
| 345 | return false; | ||
| 346 | |||
| 347 | sg_miter_stop(miter); | ||
| 348 | |||
| 349 | /* get to the next sg if necessary. __offset is adjusted by stop */ | ||
| 350 | if (miter->__offset == miter->__sg->length && --miter->__nents) { | ||
| 351 | miter->__sg = sg_next(miter->__sg); | ||
| 352 | miter->__offset = 0; | ||
| 353 | } | ||
| 354 | |||
| 355 | /* map the next page */ | ||
| 356 | off = miter->__sg->offset + miter->__offset; | ||
| 357 | len = miter->__sg->length - miter->__offset; | ||
| 358 | |||
| 359 | miter->page = nth_page(sg_page(miter->__sg), off >> PAGE_SHIFT); | ||
| 360 | off &= ~PAGE_MASK; | ||
| 361 | miter->length = min_t(unsigned int, len, PAGE_SIZE - off); | ||
| 362 | miter->consumed = miter->length; | ||
| 363 | |||
| 364 | if (miter->__flags & SG_MITER_ATOMIC) | ||
| 365 | miter->addr = kmap_atomic(miter->page, KM_BIO_SRC_IRQ) + off; | ||
| 366 | else | ||
| 367 | miter->addr = kmap(miter->page) + off; | ||
| 368 | |||
| 369 | return true; | ||
| 370 | } | ||
| 371 | EXPORT_SYMBOL(sg_miter_next); | ||
| 372 | |||
| 373 | /** | ||
| 374 | * sg_miter_stop - stop mapping iteration | ||
| 375 | * @miter: sg mapping iter to be stopped | ||
| 376 | * | ||
| 377 | * Description: | ||
| 378 | * Stops mapping iterator @miter. @miter should have been started | ||
| 379 | * started using sg_miter_start(). A stopped iteration can be | ||
| 380 | * resumed by calling sg_miter_next() on it. This is useful when | ||
| 381 | * resources (kmap) need to be released during iteration. | ||
| 382 | * | ||
| 383 | * Context: | ||
| 384 | * IRQ disabled if the SG_MITER_ATOMIC is set. Don't care otherwise. | ||
| 385 | */ | ||
| 386 | void sg_miter_stop(struct sg_mapping_iter *miter) | ||
| 387 | { | ||
| 388 | WARN_ON(miter->consumed > miter->length); | ||
| 389 | |||
| 390 | /* drop resources from the last iteration */ | ||
| 391 | if (miter->addr) { | ||
| 392 | miter->__offset += miter->consumed; | ||
| 393 | |||
| 394 | if (miter->__flags & SG_MITER_ATOMIC) { | ||
| 395 | WARN_ON(!irqs_disabled()); | ||
| 396 | kunmap_atomic(miter->addr, KM_BIO_SRC_IRQ); | ||
| 397 | } else | ||
| 398 | kunmap(miter->addr); | ||
| 399 | |||
| 400 | miter->page = NULL; | ||
| 401 | miter->addr = NULL; | ||
| 402 | miter->length = 0; | ||
| 403 | miter->consumed = 0; | ||
| 404 | } | ||
| 405 | } | ||
| 406 | EXPORT_SYMBOL(sg_miter_stop); | ||
| 407 | |||
| 408 | /** | ||
| 298 | * sg_copy_buffer - Copy data between a linear buffer and an SG list | 409 | * sg_copy_buffer - Copy data between a linear buffer and an SG list |
| 299 | * @sgl: The SG list | 410 | * @sgl: The SG list |
| 300 | * @nents: Number of SG entries | 411 | * @nents: Number of SG entries |
| @@ -309,56 +420,29 @@ EXPORT_SYMBOL(sg_alloc_table); | |||
| 309 | static size_t sg_copy_buffer(struct scatterlist *sgl, unsigned int nents, | 420 | static size_t sg_copy_buffer(struct scatterlist *sgl, unsigned int nents, |
| 310 | void *buf, size_t buflen, int to_buffer) | 421 | void *buf, size_t buflen, int to_buffer) |
| 311 | { | 422 | { |
| 312 | struct scatterlist *sg; | 423 | unsigned int offset = 0; |
| 313 | size_t buf_off = 0; | 424 | struct sg_mapping_iter miter; |
| 314 | int i; | 425 | |
| 315 | 426 | sg_miter_start(&miter, sgl, nents, SG_MITER_ATOMIC); | |
| 316 | WARN_ON(!irqs_disabled()); | 427 | |
| 317 | 428 | while (sg_miter_next(&miter) && offset < buflen) { | |
| 318 | for_each_sg(sgl, sg, nents, i) { | 429 | unsigned int len; |
| 319 | struct page *page; | 430 | |
| 320 | int n = 0; | 431 | len = min(miter.length, buflen - offset); |
| 321 | unsigned int sg_off = sg->offset; | 432 | |
| 322 | unsigned int sg_copy = sg->length; | 433 | if (to_buffer) |
| 323 | 434 | memcpy(buf + offset, miter.addr, len); | |
| 324 | if (sg_copy > buflen) | 435 | else { |
| 325 | sg_copy = buflen; | 436 | memcpy(miter.addr, buf + offset, len); |
| 326 | buflen -= sg_copy; | 437 | flush_kernel_dcache_page(miter.page); |
| 327 | |||
| 328 | while (sg_copy > 0) { | ||
| 329 | unsigned int page_copy; | ||
| 330 | void *p; | ||
| 331 | |||
| 332 | page_copy = PAGE_SIZE - sg_off; | ||
| 333 | if (page_copy > sg_copy) | ||
| 334 | page_copy = sg_copy; | ||
| 335 | |||
| 336 | page = nth_page(sg_page(sg), n); | ||
| 337 | p = kmap_atomic(page, KM_BIO_SRC_IRQ); | ||
| 338 | |||
| 339 | if (to_buffer) | ||
| 340 | memcpy(buf + buf_off, p + sg_off, page_copy); | ||
| 341 | else { | ||
| 342 | memcpy(p + sg_off, buf + buf_off, page_copy); | ||
| 343 | flush_kernel_dcache_page(page); | ||
| 344 | } | ||
| 345 | |||
| 346 | kunmap_atomic(p, KM_BIO_SRC_IRQ); | ||
| 347 | |||
| 348 | buf_off += page_copy; | ||
| 349 | sg_off += page_copy; | ||
| 350 | if (sg_off == PAGE_SIZE) { | ||
| 351 | sg_off = 0; | ||
| 352 | n++; | ||
| 353 | } | ||
| 354 | sg_copy -= page_copy; | ||
| 355 | } | 438 | } |
| 356 | 439 | ||
| 357 | if (!buflen) | 440 | offset += len; |
| 358 | break; | ||
| 359 | } | 441 | } |
| 360 | 442 | ||
| 361 | return buf_off; | 443 | sg_miter_stop(&miter); |
| 444 | |||
| 445 | return offset; | ||
| 362 | } | 446 | } |
| 363 | 447 | ||
| 364 | /** | 448 | /** |
