From b4fe945405e477cded91772b4fec854705443dd5 Mon Sep 17 00:00:00 2001 From: Pauli Nieminen Date: Mon, 1 Feb 2010 19:11:16 +0200 Subject: drm/radeon: Fix memory allocation failures in the preKMS command stream checking. Allocation of single large block of memory may fail under memory presure. drm_buffer object can hold one large block of data in multiple independ pages which preents alloation failures. This patch converts all access to command stream to use drm_buffer interface. All direct access to array has to go tough drm_buffer functions to get correct pointer. Outputting the command stream to ring buffer needs to be awear of the split nature of drm_buffer. The output operation requires the new OUT_RING_DRM_BUFFER. Signed-off-by: Pauli Nieminen Signed-off-by: Dave Airlie --- drivers/gpu/drm/radeon/radeon_state.c | 197 ++++++++++++++++++---------------- 1 file changed, 103 insertions(+), 94 deletions(-) (limited to 'drivers/gpu/drm/radeon/radeon_state.c') diff --git a/drivers/gpu/drm/radeon/radeon_state.c b/drivers/gpu/drm/radeon/radeon_state.c index 38537d9..44b6d66 100644 --- a/drivers/gpu/drm/radeon/radeon_state.c +++ b/drivers/gpu/drm/radeon/radeon_state.c @@ -29,6 +29,7 @@ #include "drmP.h" #include "drm.h" +#include "drm_buffer.h" #include "drm_sarea.h" #include "radeon_drm.h" #include "radeon_drv.h" @@ -91,21 +92,26 @@ static __inline__ int radeon_check_and_fixup_offset(drm_radeon_private_t * static __inline__ int radeon_check_and_fixup_packets(drm_radeon_private_t * dev_priv, struct drm_file *file_priv, - int id, u32 *data) + int id, struct drm_buffer *buf) { + u32 *data; switch (id) { case RADEON_EMIT_PP_MISC: - if (radeon_check_and_fixup_offset(dev_priv, file_priv, - &data[(RADEON_RB3D_DEPTHOFFSET - RADEON_PP_MISC) / 4])) { + data = drm_buffer_pointer_to_dword(buf, + (RADEON_RB3D_DEPTHOFFSET - RADEON_PP_MISC) / 4); + + if (radeon_check_and_fixup_offset(dev_priv, file_priv, data)) { DRM_ERROR("Invalid depth buffer offset\n"); return -EINVAL; } break; case RADEON_EMIT_PP_CNTL: - if (radeon_check_and_fixup_offset(dev_priv, file_priv, - &data[(RADEON_RB3D_COLOROFFSET - RADEON_PP_CNTL) / 4])) { + data = drm_buffer_pointer_to_dword(buf, + (RADEON_RB3D_COLOROFFSET - RADEON_PP_CNTL) / 4); + + if (radeon_check_and_fixup_offset(dev_priv, file_priv, data)) { DRM_ERROR("Invalid colour buffer offset\n"); return -EINVAL; } @@ -117,8 +123,8 @@ static __inline__ int radeon_check_and_fixup_packets(drm_radeon_private_t * case R200_EMIT_PP_TXOFFSET_3: case R200_EMIT_PP_TXOFFSET_4: case R200_EMIT_PP_TXOFFSET_5: - if (radeon_check_and_fixup_offset(dev_priv, file_priv, - &data[0])) { + data = drm_buffer_pointer_to_dword(buf, 0); + if (radeon_check_and_fixup_offset(dev_priv, file_priv, data)) { DRM_ERROR("Invalid R200 texture offset\n"); return -EINVAL; } @@ -127,8 +133,9 @@ static __inline__ int radeon_check_and_fixup_packets(drm_radeon_private_t * case RADEON_EMIT_PP_TXFILTER_0: case RADEON_EMIT_PP_TXFILTER_1: case RADEON_EMIT_PP_TXFILTER_2: - if (radeon_check_and_fixup_offset(dev_priv, file_priv, - &data[(RADEON_PP_TXOFFSET_0 - RADEON_PP_TXFILTER_0) / 4])) { + data = drm_buffer_pointer_to_dword(buf, + (RADEON_PP_TXOFFSET_0 - RADEON_PP_TXFILTER_0) / 4); + if (radeon_check_and_fixup_offset(dev_priv, file_priv, data)) { DRM_ERROR("Invalid R100 texture offset\n"); return -EINVAL; } @@ -142,9 +149,10 @@ static __inline__ int radeon_check_and_fixup_packets(drm_radeon_private_t * case R200_EMIT_PP_CUBIC_OFFSETS_5:{ int i; for (i = 0; i < 5; i++) { + data = drm_buffer_pointer_to_dword(buf, i); if (radeon_check_and_fixup_offset(dev_priv, file_priv, - &data[i])) { + data)) { DRM_ERROR ("Invalid R200 cubic texture offset\n"); return -EINVAL; @@ -158,9 +166,10 @@ static __inline__ int radeon_check_and_fixup_packets(drm_radeon_private_t * case RADEON_EMIT_PP_CUBIC_OFFSETS_T2:{ int i; for (i = 0; i < 5; i++) { + data = drm_buffer_pointer_to_dword(buf, i); if (radeon_check_and_fixup_offset(dev_priv, file_priv, - &data[i])) { + data)) { DRM_ERROR ("Invalid R100 cubic texture offset\n"); return -EINVAL; @@ -269,23 +278,24 @@ static __inline__ int radeon_check_and_fixup_packet3(drm_radeon_private_t * cmdbuf, unsigned int *cmdsz) { - u32 *cmd = (u32 *) cmdbuf->buf; + u32 *cmd = drm_buffer_pointer_to_dword(cmdbuf->buffer, 0); u32 offset, narrays; int count, i, k; - *cmdsz = 2 + ((cmd[0] & RADEON_CP_PACKET_COUNT_MASK) >> 16); + count = ((*cmd & RADEON_CP_PACKET_COUNT_MASK) >> 16); + *cmdsz = 2 + count; - if ((cmd[0] & 0xc0000000) != RADEON_CP_PACKET3) { + if ((*cmd & 0xc0000000) != RADEON_CP_PACKET3) { DRM_ERROR("Not a type 3 packet\n"); return -EINVAL; } - if (4 * *cmdsz > cmdbuf->bufsz) { + if (4 * *cmdsz > drm_buffer_unprocessed(cmdbuf->buffer)) { DRM_ERROR("Packet size larger than size of data provided\n"); return -EINVAL; } - switch(cmd[0] & 0xff00) { + switch (*cmd & 0xff00) { /* XXX Are there old drivers needing other packets? */ case RADEON_3D_DRAW_IMMD: @@ -312,7 +322,6 @@ static __inline__ int radeon_check_and_fixup_packet3(drm_radeon_private_t * break; case RADEON_3D_LOAD_VBPNTR: - count = (cmd[0] >> 16) & 0x3fff; if (count > 18) { /* 12 arrays max */ DRM_ERROR("Too large payload in 3D_LOAD_VBPNTR (count=%d)\n", @@ -321,13 +330,16 @@ static __inline__ int radeon_check_and_fixup_packet3(drm_radeon_private_t * } /* carefully check packet contents */ - narrays = cmd[1] & ~0xc000; + cmd = drm_buffer_pointer_to_dword(cmdbuf->buffer, 1); + + narrays = *cmd & ~0xc000; k = 0; i = 2; while ((k < narrays) && (i < (count + 2))) { i++; /* skip attribute field */ + cmd = drm_buffer_pointer_to_dword(cmdbuf->buffer, i); if (radeon_check_and_fixup_offset(dev_priv, file_priv, - &cmd[i])) { + cmd)) { DRM_ERROR ("Invalid offset (k=%d i=%d) in 3D_LOAD_VBPNTR packet.\n", k, i); @@ -338,8 +350,10 @@ static __inline__ int radeon_check_and_fixup_packet3(drm_radeon_private_t * if (k == narrays) break; /* have one more to process, they come in pairs */ + cmd = drm_buffer_pointer_to_dword(cmdbuf->buffer, i); + if (radeon_check_and_fixup_offset(dev_priv, - file_priv, &cmd[i])) + file_priv, cmd)) { DRM_ERROR ("Invalid offset (k=%d i=%d) in 3D_LOAD_VBPNTR packet.\n", @@ -363,7 +377,9 @@ static __inline__ int radeon_check_and_fixup_packet3(drm_radeon_private_t * DRM_ERROR("Invalid 3d packet for r200-class chip\n"); return -EINVAL; } - if (radeon_check_and_fixup_offset(dev_priv, file_priv, &cmd[1])) { + + cmd = drm_buffer_pointer_to_dword(cmdbuf->buffer, 1); + if (radeon_check_and_fixup_offset(dev_priv, file_priv, cmd)) { DRM_ERROR("Invalid rndr_gen_indx offset\n"); return -EINVAL; } @@ -374,12 +390,15 @@ static __inline__ int radeon_check_and_fixup_packet3(drm_radeon_private_t * DRM_ERROR("Invalid 3d packet for r100-class chip\n"); return -EINVAL; } - if ((cmd[1] & 0x8000ffff) != 0x80000810) { - DRM_ERROR("Invalid indx_buffer reg address %08X\n", cmd[1]); + + cmd = drm_buffer_pointer_to_dword(cmdbuf->buffer, 1); + if ((*cmd & 0x8000ffff) != 0x80000810) { + DRM_ERROR("Invalid indx_buffer reg address %08X\n", *cmd); return -EINVAL; } - if (radeon_check_and_fixup_offset(dev_priv, file_priv, &cmd[2])) { - DRM_ERROR("Invalid indx_buffer offset is %08X\n", cmd[2]); + cmd = drm_buffer_pointer_to_dword(cmdbuf->buffer, 2); + if (radeon_check_and_fixup_offset(dev_priv, file_priv, cmd)) { + DRM_ERROR("Invalid indx_buffer offset is %08X\n", *cmd); return -EINVAL; } break; @@ -388,31 +407,34 @@ static __inline__ int radeon_check_and_fixup_packet3(drm_radeon_private_t * case RADEON_CNTL_PAINT_MULTI: case RADEON_CNTL_BITBLT_MULTI: /* MSB of opcode: next DWORD GUI_CNTL */ - if (cmd[1] & (RADEON_GMC_SRC_PITCH_OFFSET_CNTL + cmd = drm_buffer_pointer_to_dword(cmdbuf->buffer, 1); + if (*cmd & (RADEON_GMC_SRC_PITCH_OFFSET_CNTL | RADEON_GMC_DST_PITCH_OFFSET_CNTL)) { - offset = cmd[2] << 10; + u32 *cmd2 = drm_buffer_pointer_to_dword(cmdbuf->buffer, 2); + offset = *cmd2 << 10; if (radeon_check_and_fixup_offset (dev_priv, file_priv, &offset)) { DRM_ERROR("Invalid first packet offset\n"); return -EINVAL; } - cmd[2] = (cmd[2] & 0xffc00000) | offset >> 10; + *cmd2 = (*cmd2 & 0xffc00000) | offset >> 10; } - if ((cmd[1] & RADEON_GMC_SRC_PITCH_OFFSET_CNTL) && - (cmd[1] & RADEON_GMC_DST_PITCH_OFFSET_CNTL)) { - offset = cmd[3] << 10; + if ((*cmd & RADEON_GMC_SRC_PITCH_OFFSET_CNTL) && + (*cmd & RADEON_GMC_DST_PITCH_OFFSET_CNTL)) { + u32 *cmd3 = drm_buffer_pointer_to_dword(cmdbuf->buffer, 3); + offset = *cmd << 10; if (radeon_check_and_fixup_offset (dev_priv, file_priv, &offset)) { DRM_ERROR("Invalid second packet offset\n"); return -EINVAL; } - cmd[3] = (cmd[3] & 0xffc00000) | offset >> 10; + *cmd3 = (*cmd3 & 0xffc00000) | offset >> 10; } break; default: - DRM_ERROR("Invalid packet type %x\n", cmd[0] & 0xff00); + DRM_ERROR("Invalid packet type %x\n", *cmd & 0xff00); return -EINVAL; } @@ -2611,7 +2633,6 @@ static int radeon_emit_packets(drm_radeon_private_t * dev_priv, { int id = (int)header.packet.packet_id; int sz, reg; - int *data = (int *)cmdbuf->buf; RING_LOCALS; if (id >= RADEON_MAX_STATE_PACKETS) @@ -2620,23 +2641,22 @@ static int radeon_emit_packets(drm_radeon_private_t * dev_priv, sz = packet[id].len; reg = packet[id].start; - if (sz * sizeof(int) > cmdbuf->bufsz) { + if (sz * sizeof(u32) > drm_buffer_unprocessed(cmdbuf->buffer)) { DRM_ERROR("Packet size provided larger than data provided\n"); return -EINVAL; } - if (radeon_check_and_fixup_packets(dev_priv, file_priv, id, data)) { + if (radeon_check_and_fixup_packets(dev_priv, file_priv, id, + cmdbuf->buffer)) { DRM_ERROR("Packet verification failed\n"); return -EINVAL; } BEGIN_RING(sz + 1); OUT_RING(CP_PACKET0(reg, (sz - 1))); - OUT_RING_TABLE(data, sz); + OUT_RING_DRM_BUFFER(cmdbuf->buffer, sz); ADVANCE_RING(); - cmdbuf->buf += sz * sizeof(int); - cmdbuf->bufsz -= sz * sizeof(int); return 0; } @@ -2653,10 +2673,8 @@ static __inline__ int radeon_emit_scalars(drm_radeon_private_t *dev_priv, OUT_RING(CP_PACKET0(RADEON_SE_TCL_SCALAR_INDX_REG, 0)); OUT_RING(start | (stride << RADEON_SCAL_INDX_DWORD_STRIDE_SHIFT)); OUT_RING(CP_PACKET0_TABLE(RADEON_SE_TCL_SCALAR_DATA_REG, sz - 1)); - OUT_RING_TABLE(cmdbuf->buf, sz); + OUT_RING_DRM_BUFFER(cmdbuf->buffer, sz); ADVANCE_RING(); - cmdbuf->buf += sz * sizeof(int); - cmdbuf->bufsz -= sz * sizeof(int); return 0; } @@ -2675,10 +2693,8 @@ static __inline__ int radeon_emit_scalars2(drm_radeon_private_t *dev_priv, OUT_RING(CP_PACKET0(RADEON_SE_TCL_SCALAR_INDX_REG, 0)); OUT_RING(start | (stride << RADEON_SCAL_INDX_DWORD_STRIDE_SHIFT)); OUT_RING(CP_PACKET0_TABLE(RADEON_SE_TCL_SCALAR_DATA_REG, sz - 1)); - OUT_RING_TABLE(cmdbuf->buf, sz); + OUT_RING_DRM_BUFFER(cmdbuf->buffer, sz); ADVANCE_RING(); - cmdbuf->buf += sz * sizeof(int); - cmdbuf->bufsz -= sz * sizeof(int); return 0; } @@ -2696,11 +2712,9 @@ static __inline__ int radeon_emit_vectors(drm_radeon_private_t *dev_priv, OUT_RING(CP_PACKET0(RADEON_SE_TCL_VECTOR_INDX_REG, 0)); OUT_RING(start | (stride << RADEON_VEC_INDX_OCTWORD_STRIDE_SHIFT)); OUT_RING(CP_PACKET0_TABLE(RADEON_SE_TCL_VECTOR_DATA_REG, (sz - 1))); - OUT_RING_TABLE(cmdbuf->buf, sz); + OUT_RING_DRM_BUFFER(cmdbuf->buffer, sz); ADVANCE_RING(); - cmdbuf->buf += sz * sizeof(int); - cmdbuf->bufsz -= sz * sizeof(int); return 0; } @@ -2714,7 +2728,7 @@ static __inline__ int radeon_emit_veclinear(drm_radeon_private_t *dev_priv, if (!sz) return 0; - if (sz * 4 > cmdbuf->bufsz) + if (sz * 4 > drm_buffer_unprocessed(cmdbuf->buffer)) return -EINVAL; BEGIN_RING(5 + sz); @@ -2722,11 +2736,9 @@ static __inline__ int radeon_emit_veclinear(drm_radeon_private_t *dev_priv, OUT_RING(CP_PACKET0(RADEON_SE_TCL_VECTOR_INDX_REG, 0)); OUT_RING(start | (1 << RADEON_VEC_INDX_OCTWORD_STRIDE_SHIFT)); OUT_RING(CP_PACKET0_TABLE(RADEON_SE_TCL_VECTOR_DATA_REG, (sz - 1))); - OUT_RING_TABLE(cmdbuf->buf, sz); + OUT_RING_DRM_BUFFER(cmdbuf->buffer, sz); ADVANCE_RING(); - cmdbuf->buf += sz * sizeof(int); - cmdbuf->bufsz -= sz * sizeof(int); return 0; } @@ -2748,11 +2760,9 @@ static int radeon_emit_packet3(struct drm_device * dev, } BEGIN_RING(cmdsz); - OUT_RING_TABLE(cmdbuf->buf, cmdsz); + OUT_RING_DRM_BUFFER(cmdbuf->buffer, cmdsz); ADVANCE_RING(); - cmdbuf->buf += cmdsz * 4; - cmdbuf->bufsz -= cmdsz * 4; return 0; } @@ -2805,16 +2815,16 @@ static int radeon_emit_packet3_cliprect(struct drm_device *dev, } BEGIN_RING(cmdsz); - OUT_RING_TABLE(cmdbuf->buf, cmdsz); + OUT_RING_DRM_BUFFER(cmdbuf->buffer, cmdsz); ADVANCE_RING(); } while (++i < cmdbuf->nbox); if (cmdbuf->nbox == 1) cmdbuf->nbox = 0; + return 0; out: - cmdbuf->buf += cmdsz * 4; - cmdbuf->bufsz -= cmdsz * 4; + drm_buffer_advance(cmdbuf->buffer, cmdsz * 4); return 0; } @@ -2847,16 +2857,16 @@ static int radeon_emit_wait(struct drm_device * dev, int flags) return 0; } -static int radeon_cp_cmdbuf(struct drm_device *dev, void *data, struct drm_file *file_priv) +static int radeon_cp_cmdbuf(struct drm_device *dev, void *data, + struct drm_file *file_priv) { drm_radeon_private_t *dev_priv = dev->dev_private; struct drm_device_dma *dma = dev->dma; struct drm_buf *buf = NULL; + drm_radeon_cmd_header_t stack_header; int idx; drm_radeon_kcmd_buffer_t *cmdbuf = data; - drm_radeon_cmd_header_t header; - int orig_nbox, orig_bufsz; - char *kbuf = NULL; + int orig_nbox; LOCK_TEST_WITH_RETURN(dev, file_priv); @@ -2871,17 +2881,16 @@ static int radeon_cp_cmdbuf(struct drm_device *dev, void *data, struct drm_file * races between checking values and using those values in other code, * and simply to avoid a lot of function calls to copy in data. */ - orig_bufsz = cmdbuf->bufsz; - if (orig_bufsz != 0) { - kbuf = kmalloc(cmdbuf->bufsz, GFP_KERNEL); - if (kbuf == NULL) - return -ENOMEM; - if (DRM_COPY_FROM_USER(kbuf, (void __user *)cmdbuf->buf, - cmdbuf->bufsz)) { - kfree(kbuf); - return -EFAULT; - } - cmdbuf->buf = kbuf; + if (cmdbuf->bufsz != 0) { + int rv; + void __user *buffer = cmdbuf->buffer; + rv = drm_buffer_alloc(&cmdbuf->buffer, cmdbuf->bufsz); + if (rv) + return rv; + rv = drm_buffer_copy_from_user(cmdbuf->buffer, buffer, + cmdbuf->bufsz); + if (rv) + return rv; } orig_nbox = cmdbuf->nbox; @@ -2890,24 +2899,24 @@ static int radeon_cp_cmdbuf(struct drm_device *dev, void *data, struct drm_file int temp; temp = r300_do_cp_cmdbuf(dev, file_priv, cmdbuf); - if (orig_bufsz != 0) - kfree(kbuf); + if (cmdbuf->bufsz != 0) + drm_buffer_free(cmdbuf->buffer); return temp; } /* microcode_version != r300 */ - while (cmdbuf->bufsz >= sizeof(header)) { + while (drm_buffer_unprocessed(cmdbuf->buffer) >= sizeof(stack_header)) { - header.i = *(int *)cmdbuf->buf; - cmdbuf->buf += sizeof(header); - cmdbuf->bufsz -= sizeof(header); + drm_radeon_cmd_header_t *header; + header = drm_buffer_read_object(cmdbuf->buffer, + sizeof(stack_header), &stack_header); - switch (header.header.cmd_type) { + switch (header->header.cmd_type) { case RADEON_CMD_PACKET: DRM_DEBUG("RADEON_CMD_PACKET\n"); if (radeon_emit_packets - (dev_priv, file_priv, header, cmdbuf)) { + (dev_priv, file_priv, *header, cmdbuf)) { DRM_ERROR("radeon_emit_packets failed\n"); goto err; } @@ -2915,7 +2924,7 @@ static int radeon_cp_cmdbuf(struct drm_device *dev, void *data, struct drm_file case RADEON_CMD_SCALARS: DRM_DEBUG("RADEON_CMD_SCALARS\n"); - if (radeon_emit_scalars(dev_priv, header, cmdbuf)) { + if (radeon_emit_scalars(dev_priv, *header, cmdbuf)) { DRM_ERROR("radeon_emit_scalars failed\n"); goto err; } @@ -2923,7 +2932,7 @@ static int radeon_cp_cmdbuf(struct drm_device *dev, void *data, struct drm_file case RADEON_CMD_VECTORS: DRM_DEBUG("RADEON_CMD_VECTORS\n"); - if (radeon_emit_vectors(dev_priv, header, cmdbuf)) { + if (radeon_emit_vectors(dev_priv, *header, cmdbuf)) { DRM_ERROR("radeon_emit_vectors failed\n"); goto err; } @@ -2931,7 +2940,7 @@ static int radeon_cp_cmdbuf(struct drm_device *dev, void *data, struct drm_file case RADEON_CMD_DMA_DISCARD: DRM_DEBUG("RADEON_CMD_DMA_DISCARD\n"); - idx = header.dma.buf_idx; + idx = header->dma.buf_idx; if (idx < 0 || idx >= dma->buf_count) { DRM_ERROR("buffer index %d (of %d max)\n", idx, dma->buf_count - 1); @@ -2968,7 +2977,7 @@ static int radeon_cp_cmdbuf(struct drm_device *dev, void *data, struct drm_file case RADEON_CMD_SCALARS2: DRM_DEBUG("RADEON_CMD_SCALARS2\n"); - if (radeon_emit_scalars2(dev_priv, header, cmdbuf)) { + if (radeon_emit_scalars2(dev_priv, *header, cmdbuf)) { DRM_ERROR("radeon_emit_scalars2 failed\n"); goto err; } @@ -2976,37 +2985,37 @@ static int radeon_cp_cmdbuf(struct drm_device *dev, void *data, struct drm_file case RADEON_CMD_WAIT: DRM_DEBUG("RADEON_CMD_WAIT\n"); - if (radeon_emit_wait(dev, header.wait.flags)) { + if (radeon_emit_wait(dev, header->wait.flags)) { DRM_ERROR("radeon_emit_wait failed\n"); goto err; } break; case RADEON_CMD_VECLINEAR: DRM_DEBUG("RADEON_CMD_VECLINEAR\n"); - if (radeon_emit_veclinear(dev_priv, header, cmdbuf)) { + if (radeon_emit_veclinear(dev_priv, *header, cmdbuf)) { DRM_ERROR("radeon_emit_veclinear failed\n"); goto err; } break; default: - DRM_ERROR("bad cmd_type %d at %p\n", - header.header.cmd_type, - cmdbuf->buf - sizeof(header)); + DRM_ERROR("bad cmd_type %d at byte %d\n", + header->header.cmd_type, + cmdbuf->buffer->iterator); goto err; } } - if (orig_bufsz != 0) - kfree(kbuf); + if (cmdbuf->bufsz != 0) + drm_buffer_free(cmdbuf->buffer); DRM_DEBUG("DONE\n"); COMMIT_RING(); return 0; err: - if (orig_bufsz != 0) - kfree(kbuf); + if (cmdbuf->bufsz != 0) + drm_buffer_free(cmdbuf->buffer); return -EINVAL; } -- cgit v1.1