the ``DEVLINK_CMD_REGION_NEW`` request kernel will allocate one and send
 the snapshot information to user space.
 
+Regions may optionally allow directly reading from their contents without a
+snapshot. Direct read requests are not atomic. In particular a read request
+of size 256 bytes or larger will be split into multiple chunks. If atomic
+access is required, use a snapshot. A driver wishing to enable this for a
+region should implement the ``.read`` callback in the ``devlink_region_ops``
+structure. User space can request a direct read by using the
+``DEVLINK_ATTR_REGION_DIRECT`` attribute instead of specifying a snapshot
+id.
+
 example usage
 -------------
 
     $ devlink region read pci/0000:00:05.0/fw-health snapshot 1 address 0 length 16
     0000000000000000 0014 95dc 0014 9514 0035 1670 0034 db30
 
+    # Read from the region without a snapshot
+    $ devlink region read pci/0000:00:05.0/fw-health address 16 length 16
+    0000000000000010 0000 0000 ffff ff04 0029 8c00 0028 8cc8
+
 As regions are likely very device or driver specific, no generic regions are
 defined. See the driver-specific documentation files for information on the
 specific regions a driver supports.
 
  *            the data variable must be updated to point to the snapshot data.
  *            The function will be called while the devlink instance lock is
  *            held.
+ * @read: callback to directly read a portion of the region. On success,
+ *        the data pointer will be updated with the contents of the
+ *        requested portion of the region. The function will be called
+ *        while the devlink instance lock is held.
  * @priv: Pointer to driver private data for the region operation
  */
 struct devlink_region_ops {
                        const struct devlink_region_ops *ops,
                        struct netlink_ext_ack *extack,
                        u8 **data);
+       int (*read)(struct devlink *devlink,
+                   const struct devlink_region_ops *ops,
+                   struct netlink_ext_ack *extack,
+                   u64 offset, u32 size, u8 *data);
        void *priv;
 };
 
  *            the data variable must be updated to point to the snapshot data.
  *            The function will be called while the devlink instance lock is
  *            held.
+ * @read: callback to directly read a portion of the region. On success,
+ *        the data pointer will be updated with the contents of the
+ *        requested portion of the region. The function will be called
+ *        while the devlink instance lock is held.
  * @priv: Pointer to driver private data for the region operation
  */
 struct devlink_port_region_ops {
                        const struct devlink_port_region_ops *ops,
                        struct netlink_ext_ack *extack,
                        u8 **data);
+       int (*read)(struct devlink_port *port,
+                   const struct devlink_port_region_ops *ops,
+                   struct netlink_ext_ack *extack,
+                   u64 offset, u32 size, u8 *data);
        void *priv;
 };
 
 
        return 0;
 }
 
+static int
+devlink_region_port_direct_fill(void *cb_priv, u8 *chunk, u32 chunk_size,
+                               u64 curr_offset, struct netlink_ext_ack *extack)
+{
+       struct devlink_region *region = cb_priv;
+
+       return region->port_ops->read(region->port, region->port_ops, extack,
+                                     curr_offset, chunk_size, chunk);
+}
+
+static int
+devlink_region_direct_fill(void *cb_priv, u8 *chunk, u32 chunk_size,
+                          u64 curr_offset, struct netlink_ext_ack *extack)
+{
+       struct devlink_region *region = cb_priv;
+
+       return region->ops->read(region->devlink, region->ops, extack,
+                                curr_offset, chunk_size, chunk);
+}
+
 static int devlink_nl_cmd_region_read_dumpit(struct sk_buff *skb,
                                             struct netlink_callback *cb)
 {
        u64 ret_offset, start_offset, end_offset = U64_MAX;
        struct nlattr **attrs = info->attrs;
        struct devlink_port *port = NULL;
-       struct devlink_snapshot *snapshot;
+       devlink_chunk_fill_t *region_cb;
        struct devlink_region *region;
        const char *region_name;
        struct devlink *devlink;
        unsigned int index;
-       u32 snapshot_id;
+       void *region_cb_priv;
        void *hdr;
        int err;
 
                goto out_unlock;
        }
 
-       if (!attrs[DEVLINK_ATTR_REGION_SNAPSHOT_ID]) {
-               NL_SET_ERR_MSG(cb->extack, "No snapshot id provided");
-               err = -EINVAL;
-               goto out_unlock;
-       }
-
        if (info->attrs[DEVLINK_ATTR_PORT_INDEX]) {
                index = nla_get_u32(info->attrs[DEVLINK_ATTR_PORT_INDEX]);
 
        }
 
        snapshot_attr = attrs[DEVLINK_ATTR_REGION_SNAPSHOT_ID];
-       snapshot_id = nla_get_u32(snapshot_attr);
-       snapshot = devlink_region_snapshot_get_by_id(region, snapshot_id);
-       if (!snapshot) {
-               NL_SET_ERR_MSG_ATTR(cb->extack, snapshot_attr, "Requested snapshot does not exist");
-               err = -EINVAL;
-               goto out_unlock;
+       if (!snapshot_attr) {
+               if (!nla_get_flag(attrs[DEVLINK_ATTR_REGION_DIRECT])) {
+                       NL_SET_ERR_MSG(cb->extack, "No snapshot id provided");
+                       err = -EINVAL;
+                       goto out_unlock;
+               }
+
+               if (!region->ops->read) {
+                       NL_SET_ERR_MSG(cb->extack, "Requested region does not support direct read");
+                       err = -EOPNOTSUPP;
+                       goto out_unlock;
+               }
+
+               if (port)
+                       region_cb = &devlink_region_port_direct_fill;
+               else
+                       region_cb = &devlink_region_direct_fill;
+               region_cb_priv = region;
+       } else {
+               struct devlink_snapshot *snapshot;
+               u32 snapshot_id;
+
+               if (nla_get_flag(attrs[DEVLINK_ATTR_REGION_DIRECT])) {
+                       NL_SET_ERR_MSG_ATTR(cb->extack, snapshot_attr, "Direct region read does not use snapshot");
+                       err = -EINVAL;
+                       goto out_unlock;
+               }
+
+               snapshot_id = nla_get_u32(snapshot_attr);
+               snapshot = devlink_region_snapshot_get_by_id(region, snapshot_id);
+               if (!snapshot) {
+                       NL_SET_ERR_MSG_ATTR(cb->extack, snapshot_attr, "Requested snapshot does not exist");
+                       err = -EINVAL;
+                       goto out_unlock;
+               }
+               region_cb = &devlink_region_snapshot_fill;
+               region_cb_priv = snapshot;
        }
 
        if (attrs[DEVLINK_ATTR_REGION_CHUNK_ADDR] &&
                goto nla_put_failure;
        }
 
-       err = devlink_nl_region_read_fill(skb, &devlink_region_snapshot_fill,
-                                         snapshot, start_offset, end_offset,
-                                         &ret_offset, cb->extack);
+       err = devlink_nl_region_read_fill(skb, region_cb, region_cb_priv,
+                                         start_offset, end_offset, &ret_offset,
+                                         cb->extack);
 
        if (err && err != -EMSGSIZE)
                goto nla_put_failure;
        [DEVLINK_ATTR_SELFTESTS] = { .type = NLA_NESTED },
        [DEVLINK_ATTR_RATE_TX_PRIORITY] = { .type = NLA_U32 },
        [DEVLINK_ATTR_RATE_TX_WEIGHT] = { .type = NLA_U32 },
+       [DEVLINK_ATTR_REGION_DIRECT] = { .type = NLA_FLAG },
 };
 
 static const struct genl_small_ops devlink_nl_ops[] = {