vfs_cluster.c 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. /* $OpenBSD: vfs_cluster.c,v 1.44 2015/03/14 03:38:51 jsg Exp $ */
  2. /* $NetBSD: vfs_cluster.c,v 1.12 1996/04/22 01:39:05 christos Exp $ */
  3. /*
  4. * Copyright (c) 1993
  5. * The Regents of the University of California. All rights reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. * 1. Redistributions of source code must retain the above copyright
  11. * notice, this list of conditions and the following disclaimer.
  12. * 2. Redistributions in binary form must reproduce the above copyright
  13. * notice, this list of conditions and the following disclaimer in the
  14. * documentation and/or other materials provided with the distribution.
  15. * 3. Neither the name of the University nor the names of its contributors
  16. * may be used to endorse or promote products derived from this software
  17. * without specific prior written permission.
  18. *
  19. * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  20. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  21. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  22. * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  23. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  24. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  25. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  26. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  27. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  28. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  29. * SUCH DAMAGE.
  30. *
  31. * @(#)vfs_cluster.c 8.8 (Berkeley) 7/28/94
  32. */
  33. #include <sys/param.h>
  34. #include <sys/buf.h>
  35. #include <sys/vnode.h>
  36. #include <sys/mount.h>
  37. #include <sys/malloc.h>
  38. #include <sys/systm.h>
  39. void cluster_wbuild(struct vnode *, struct buf *, long, daddr_t, int,
  40. daddr_t);
  41. struct cluster_save *cluster_collectbufs(struct vnode *, struct cluster_info *,
  42. struct buf *);
  43. /*
  44. * Do clustered write for FFS.
  45. *
  46. * Three cases:
  47. * 1. Write is not sequential (write asynchronously)
  48. * Write is sequential:
  49. * 2. beginning of cluster - begin cluster
  50. * 3. middle of a cluster - add to cluster
  51. * 4. end of a cluster - asynchronously write cluster
  52. */
  53. void
  54. cluster_write(struct buf *bp, struct cluster_info *ci, u_quad_t filesize)
  55. {
  56. struct vnode *vp;
  57. daddr_t lbn;
  58. int maxclen, cursize;
  59. vp = bp->b_vp;
  60. lbn = bp->b_lblkno;
  61. /* Initialize vnode to beginning of file. */
  62. if (lbn == 0)
  63. ci->ci_lasta = ci->ci_clen = ci->ci_cstart = ci->ci_lastw = 0;
  64. if (ci->ci_clen == 0 || lbn != ci->ci_lastw + 1 ||
  65. (bp->b_blkno != ci->ci_lasta + btodb(bp->b_bcount))) {
  66. maxclen = MAXBSIZE / vp->v_mount->mnt_stat.f_iosize - 1;
  67. if (ci->ci_clen != 0) {
  68. /*
  69. * Next block is not sequential.
  70. *
  71. * If we are not writing at end of file, the process
  72. * seeked to another point in the file since its
  73. * last write, or we have reached our maximum
  74. * cluster size, then push the previous cluster.
  75. * Otherwise try reallocating to make it sequential.
  76. */
  77. cursize = ci->ci_lastw - ci->ci_cstart + 1;
  78. if (((u_quad_t)(lbn + 1)) * bp->b_bcount != filesize ||
  79. lbn != ci->ci_lastw + 1 || ci->ci_clen <= cursize) {
  80. cluster_wbuild(vp, NULL, bp->b_bcount,
  81. ci->ci_cstart, cursize, lbn);
  82. } else {
  83. struct buf **bpp, **endbp;
  84. struct cluster_save *buflist;
  85. buflist = cluster_collectbufs(vp, ci, bp);
  86. endbp = &buflist->bs_children
  87. [buflist->bs_nchildren - 1];
  88. if (VOP_REALLOCBLKS(vp, buflist)) {
  89. /*
  90. * Failed, push the previous cluster.
  91. */
  92. for (bpp = buflist->bs_children;
  93. bpp < endbp; bpp++)
  94. brelse(*bpp);
  95. free(buflist, M_VCLUSTER, 0);
  96. cluster_wbuild(vp, NULL, bp->b_bcount,
  97. ci->ci_cstart, cursize, lbn);
  98. } else {
  99. /*
  100. * Succeeded, keep building cluster.
  101. */
  102. for (bpp = buflist->bs_children;
  103. bpp <= endbp; bpp++)
  104. bdwrite(*bpp);
  105. free(buflist, M_VCLUSTER, 0);
  106. ci->ci_lastw = lbn;
  107. ci->ci_lasta = bp->b_blkno;
  108. return;
  109. }
  110. }
  111. }
  112. /*
  113. * Consider beginning a cluster.
  114. * If at end of file, make cluster as large as possible,
  115. * otherwise find size of existing cluster.
  116. */
  117. if ((u_quad_t)(lbn + 1) * (u_quad_t)bp->b_bcount != filesize &&
  118. (VOP_BMAP(vp, lbn, NULL, &bp->b_blkno, &maxclen) ||
  119. bp->b_blkno == -1)) {
  120. bawrite(bp);
  121. ci->ci_clen = 0;
  122. ci->ci_lasta = bp->b_blkno;
  123. ci->ci_cstart = lbn + 1;
  124. ci->ci_lastw = lbn;
  125. return;
  126. }
  127. ci->ci_clen = maxclen;
  128. if (maxclen == 0) { /* I/O not contiguous */
  129. ci->ci_cstart = lbn + 1;
  130. bawrite(bp);
  131. } else { /* Wait for rest of cluster */
  132. ci->ci_cstart = lbn;
  133. bdwrite(bp);
  134. }
  135. } else if (lbn == ci->ci_cstart + ci->ci_clen) {
  136. /*
  137. * At end of cluster, write it out.
  138. */
  139. cluster_wbuild(vp, bp, bp->b_bcount, ci->ci_cstart,
  140. ci->ci_clen + 1, lbn);
  141. ci->ci_clen = 0;
  142. ci->ci_cstart = lbn + 1;
  143. } else
  144. /*
  145. * In the middle of a cluster, so just delay the
  146. * I/O for now.
  147. */
  148. bdwrite(bp);
  149. ci->ci_lastw = lbn;
  150. ci->ci_lasta = bp->b_blkno;
  151. }
  152. /*
  153. * The last lbn argument is the current block on which I/O is being
  154. * performed. Check to see that it doesn't fall in the middle of
  155. * the current block (if last_bp == NULL).
  156. */
  157. void
  158. cluster_wbuild(struct vnode *vp, struct buf *last_bp, long size,
  159. daddr_t start_lbn, int len, daddr_t lbn)
  160. {
  161. struct buf *bp;
  162. #ifdef DIAGNOSTIC
  163. if (size != vp->v_mount->mnt_stat.f_iosize)
  164. panic("cluster_wbuild: size %ld != filesize %u",
  165. size, vp->v_mount->mnt_stat.f_iosize);
  166. #endif
  167. redo:
  168. while ((!incore(vp, start_lbn) || start_lbn == lbn) && len) {
  169. ++start_lbn;
  170. --len;
  171. }
  172. /* Get more memory for current buffer */
  173. if (len <= 1) {
  174. if (last_bp) {
  175. bawrite(last_bp);
  176. } else if (len) {
  177. bp = getblk(vp, start_lbn, size, 0, 0);
  178. /*
  179. * The buffer could have already been flushed out of
  180. * the cache. If that has happened, we'll get a new
  181. * buffer here with random data, just drop it.
  182. */
  183. if ((bp->b_flags & B_DELWRI) == 0)
  184. brelse(bp);
  185. else
  186. bawrite(bp);
  187. }
  188. return;
  189. }
  190. bp = getblk(vp, start_lbn, size, 0, 0);
  191. if (!(bp->b_flags & B_DELWRI)) {
  192. ++start_lbn;
  193. --len;
  194. brelse(bp);
  195. goto redo;
  196. }
  197. ++start_lbn;
  198. --len;
  199. bawrite(bp);
  200. goto redo;
  201. }
  202. /*
  203. * Collect together all the buffers in a cluster.
  204. * Plus add one additional buffer.
  205. */
  206. struct cluster_save *
  207. cluster_collectbufs(struct vnode *vp, struct cluster_info *ci,
  208. struct buf *last_bp)
  209. {
  210. struct cluster_save *buflist;
  211. daddr_t lbn;
  212. int i, len;
  213. len = ci->ci_lastw - ci->ci_cstart + 1;
  214. buflist = malloc(sizeof(struct buf *) * (len + 1) + sizeof(*buflist),
  215. M_VCLUSTER, M_WAITOK);
  216. buflist->bs_nchildren = 0;
  217. buflist->bs_children = (struct buf **)(buflist + 1);
  218. for (lbn = ci->ci_cstart, i = 0; i < len; lbn++, i++)
  219. (void)bread(vp, lbn, last_bp->b_bcount,
  220. &buflist->bs_children[i]);
  221. buflist->bs_children[i] = last_bp;
  222. buflist->bs_nchildren = i + 1;
  223. return (buflist);
  224. }