diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,12 @@
+## 0.2.1 (2021-06-23)
+
+#### Changed
+
+- Performance improvement when using GHC 9.
+
 ## 0.2.0 (2020-10-26)
 
-**Note:** The deprecations added in `0.1.7` have *not* been removed in this
+**Note:** The deprecations added in `0.1.7` have _not_ been removed in this
 version. Instead of `0.1.7`, that release should have been `0.2` in the first
 place.
 
diff --git a/lib/Streaming/ByteString.hs b/lib/Streaming/ByteString.hs
--- a/lib/Streaming/ByteString.hs
+++ b/lib/Streaming/ByteString.hs
@@ -209,7 +209,6 @@
 import           Data.Int (Int64)
 import qualified Data.List as L
 import           Data.Word (Word8)
-import           Foreign.ForeignPtr (withForeignPtr)
 import           Foreign.Ptr
 import           Foreign.Storable
 import           System.IO (Handle, IOMode(..), hClose, openBinaryFile)
@@ -659,7 +658,7 @@
                                  (dematerialize cs Empty (Chunk . intersperse') Go)
   where intersperse' :: P.ByteString -> P.ByteString
         intersperse' (B.PS fp o l)
-          | l > 0 = B.unsafeCreate (2*l) $ \p' -> withForeignPtr fp $ \p -> do
+          | l > 0 = B.unsafeCreate (2*l) $ \p' -> unsafeWithForeignPtr fp $ \p -> do
               poke p' w
               B.c_intersperse (p' `plusPtr` 1) (p `plusPtr` o) (fromIntegral l) w
           | otherwise = B.empty
diff --git a/lib/Streaming/ByteString/Char8.hs b/lib/Streaming/ByteString/Char8.hs
--- a/lib/Streaming/ByteString/Char8.hs
+++ b/lib/Streaming/ByteString/Char8.hs
@@ -201,7 +201,6 @@
 
 import           Data.Bits ((.&.))
 import           Data.Word (Word8)
-import           Foreign.ForeignPtr (withForeignPtr)
 import           Foreign.Ptr
 import           Foreign.Storable
 import qualified System.IO as IO
@@ -222,7 +221,7 @@
 
   unpackAppendCharsStrict :: B.ByteString -> Stream (Of Char) m r -> Stream (Of Char) m r
   unpackAppendCharsStrict (B.PS fp off len) xs =
-    B.accursedUnutterablePerformIO $ withForeignPtr fp $ \base -> do
+    B.accursedUnutterablePerformIO $ unsafeWithForeignPtr fp $ \base -> do
          loop (base `plusPtr` (off-1)) (base `plusPtr` (off-1+len)) xs
      where
        loop !sentinal !p acc
@@ -628,7 +627,7 @@
            -> Int            -- remaining number of newlines wanted
            -> Either Int Int -- Left count, else Right length
 nthNewLine (B.PS fp off len) targetLines =
-    B.accursedUnutterablePerformIO $ withForeignPtr fp $ \base ->
+    B.accursedUnutterablePerformIO $ unsafeWithForeignPtr fp $ \base ->
     loop (base `plusPtr` off) targetLines 0 len
   where
     loop :: Ptr Word8 -> Int -> Int -> Int -> IO (Either Int Int)
@@ -823,7 +822,7 @@
         -- the provided digits (end of input or non-digit encountered).
         accumWord acc (B.PS fp off len) =
             B.accursedUnutterablePerformIO $ do
-                withForeignPtr fp $ \p -> do
+                unsafeWithForeignPtr fp $ \p -> do
                     let ptr = p `plusPtr` off
                         end = ptr `plusPtr` len
                     x@(!_, !_, !_) <- if positive
diff --git a/lib/Streaming/ByteString/Internal.hs b/lib/Streaming/ByteString/Internal.hs
--- a/lib/Streaming/ByteString/Internal.hs
+++ b/lib/Streaming/ByteString/Internal.hs
@@ -52,6 +52,9 @@
 
     -- * ResourceT help
   , bracketByteString
+
+    -- * Re-export from GHC 9.0
+  , unsafeWithForeignPtr
   ) where
 
 import           Control.Monad
@@ -78,7 +81,6 @@
 import qualified Streaming.Prelude as SP
 
 import           Data.String
-import           Foreign.ForeignPtr (withForeignPtr)
 import           Foreign.Ptr
 import           Foreign.Storable
 import           GHC.Types (SPEC(..))
@@ -93,6 +95,18 @@
 import           Control.Monad.Catch (MonadCatch(..))
 import           Control.Monad.Trans.Resource
 
+#if MIN_VERSION_base(4,15,0)
+import           GHC.ForeignPtr (unsafeWithForeignPtr)
+#else
+import           Foreign.ForeignPtr (ForeignPtr, withForeignPtr)
+#endif
+
+#if !MIN_VERSION_base(4,15,0)
+-- | Synonym of 'withForeignPtr' for GHC prior to 9.0.
+unsafeWithForeignPtr :: ForeignPtr a -> (Ptr a -> IO b) -> IO b
+unsafeWithForeignPtr = withForeignPtr
+#endif
+
 -- | A type alias for back-compatibility.
 type ByteString = ByteStream
 {-# DEPRECATED ByteString "Use ByteStream instead." #-}
@@ -362,7 +376,7 @@
 
   unpackAppendBytesStrict :: B.ByteString -> Stream (Of Word8) m r -> Stream (Of Word8) m r
   unpackAppendBytesStrict (B.PS fp off len) xs =
-    B.accursedUnutterablePerformIO $ withForeignPtr fp $ \base ->
+    B.accursedUnutterablePerformIO $ unsafeWithForeignPtr fp $ \base ->
       loop (base `plusPtr` (off-1)) (base `plusPtr` (off-1+len)) xs
     where
       loop !sentinel !p acc
@@ -375,7 +389,7 @@
 -- | Copied from Data.ByteString.Unsafe for compatibility with older bytestring.
 unsafeLast :: B.ByteString -> Word8
 unsafeLast (B.PS x s l) =
-    accursedUnutterablePerformIO $ withForeignPtr x $ \p -> peekByteOff p (s+l-1)
+    accursedUnutterablePerformIO $ unsafeWithForeignPtr x $ \p -> peekByteOff p (s+l-1)
  where
       accursedUnutterablePerformIO (IO m) = case m realWorld# of (# _, r #) -> r
 {-# INLINE unsafeLast #-}
@@ -568,7 +582,7 @@
 findIndexOrEnd :: (Word8 -> Bool) -> B.ByteString -> Int
 findIndexOrEnd k (B.PS x s l) =
     B.accursedUnutterablePerformIO $
-      withForeignPtr x $ \f -> go (f `plusPtr` s) 0
+      unsafeWithForeignPtr x $ \f -> go (f `plusPtr` s) 0
   where
     go !ptr !n | n >= l    = return l
                | otherwise = do w <- peek ptr
diff --git a/streaming-bytestring.cabal b/streaming-bytestring.cabal
--- a/streaming-bytestring.cabal
+++ b/streaming-bytestring.cabal
@@ -1,6 +1,6 @@
 cabal-version:      >=1.10
 name:               streaming-bytestring
-version:            0.2.0
+version:            0.2.1
 synopsis:           Fast, effectful byte streams.
 description:
   This library enables fast and safe streaming of byte data, in either @Word8@ or
