Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

powerpc/mpc5200: Bugfix on handling variable sized buffer descriptors

The buffer descriptors for the ATA BestComm task are larger than the
current definition for bcom_bd. This causes problems because the
various bcom_... functions dereference the buffer descriptor pointer
by using the array operator which doesn't work when the buffer
descriptors are a different size.

This patch adds the bcom_get_bd() function which uses the value in
bcom_task.bd_size to calculate the offset into the BD table. This
patch also changes the definition of bcom_bd to specify a data size
of 0 instead of 1 so that it will never work if anyone attempts to
dereference the bd list as an array (as opposed to something that
might work even though it is wrong).

Finally, this patch moves the definition of bcom_bd up in the file
to eliminate a forward declaration.

Based on patch originally written by Tim Yamin.

Signed-off-by: Tim Yamin <plasm@roo.me.uk>
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>

+42 -19
+42 -19
arch/powerpc/sysdev/bestcomm/bestcomm.h
··· 16 16 #ifndef __BESTCOMM_H__ 17 17 #define __BESTCOMM_H__ 18 18 19 - struct bcom_bd; /* defined later on ... */ 20 - 19 + /** 20 + * struct bcom_bd - Structure describing a generic BestComm buffer descriptor 21 + * @status: The current status of this buffer. Exact meaning depends on the 22 + * task type 23 + * @data: An array of u32 extra data. Size of array is task dependant. 24 + * 25 + * Note: Don't dereference a bcom_bd pointer as an array. The size of the 26 + * bcom_bd is variable. Use bcom_get_bd() instead. 27 + */ 28 + struct bcom_bd { 29 + u32 status; 30 + u32 data[0]; /* variable payload size */ 31 + }; 21 32 22 33 /* ======================================================================== */ 23 34 /* Generic task management */ ··· 95 84 /* BD based tasks helpers */ 96 85 /* ======================================================================== */ 97 86 98 - /** 99 - * struct bcom_bd - Structure describing a generic BestComm buffer descriptor 100 - * @status: The current status of this buffer. Exact meaning depends on the 101 - * task type 102 - * @data: An array of u32 whose meaning depends on the task type. 103 - */ 104 - struct bcom_bd { 105 - u32 status; 106 - u32 data[1]; /* variable, but at least 1 */ 107 - }; 108 - 109 87 #define BCOM_BD_READY 0x40000000ul 110 88 111 89 /** _bcom_next_index - Get next input index. ··· 140 140 } 141 141 142 142 /** 143 + * bcom_get_bd - Get a BD from the queue 144 + * @tsk: The BestComm task structure 145 + * index: Index of the BD to fetch 146 + */ 147 + static inline struct bcom_bd 148 + *bcom_get_bd(struct bcom_task *tsk, unsigned int index) 149 + { 150 + /* A cast to (void*) so the address can be incremented by the 151 + * real size instead of by sizeof(struct bcom_bd) */ 152 + return ((void *)tsk->bd) + (index * tsk->bd_size); 153 + } 154 + 155 + /** 143 156 * bcom_buffer_done - Checks if a BestComm 144 157 * @tsk: The BestComm task structure 145 158 */ 146 159 static inline int 147 160 bcom_buffer_done(struct bcom_task *tsk) 148 161 { 162 + struct bcom_bd *bd; 149 163 if (bcom_queue_empty(tsk)) 150 164 return 0; 151 - return !(tsk->bd[tsk->outdex].status & BCOM_BD_READY); 165 + 166 + bd = bcom_get_bd(tsk, tsk->outdex); 167 + return !(bd->status & BCOM_BD_READY); 152 168 } 153 169 154 170 /** ··· 176 160 static inline struct bcom_bd * 177 161 bcom_prepare_next_buffer(struct bcom_task *tsk) 178 162 { 179 - tsk->bd[tsk->index].status = 0; /* cleanup last status */ 180 - return &tsk->bd[tsk->index]; 163 + struct bcom_bd *bd; 164 + 165 + bd = bcom_get_bd(tsk, tsk->index); 166 + bd->status = 0; /* cleanup last status */ 167 + return bd; 181 168 } 182 169 183 170 static inline void 184 171 bcom_submit_next_buffer(struct bcom_task *tsk, void *cookie) 185 172 { 173 + struct bcom_bd *bd = bcom_get_bd(tsk, tsk->index); 174 + 186 175 tsk->cookie[tsk->index] = cookie; 187 176 mb(); /* ensure the bd is really up-to-date */ 188 - tsk->bd[tsk->index].status |= BCOM_BD_READY; 177 + bd->status |= BCOM_BD_READY; 189 178 tsk->index = _bcom_next_index(tsk); 190 179 if (tsk->flags & BCOM_FLAGS_ENABLE_TASK) 191 180 bcom_enable(tsk); ··· 200 179 bcom_retrieve_buffer(struct bcom_task *tsk, u32 *p_status, struct bcom_bd **p_bd) 201 180 { 202 181 void *cookie = tsk->cookie[tsk->outdex]; 182 + struct bcom_bd *bd = bcom_get_bd(tsk, tsk->outdex); 183 + 203 184 if (p_status) 204 - *p_status = tsk->bd[tsk->outdex].status; 185 + *p_status = bd->status; 205 186 if (p_bd) 206 - *p_bd = &tsk->bd[tsk->outdex]; 187 + *p_bd = bd; 207 188 tsk->outdex = _bcom_next_outdex(tsk); 208 189 return cookie; 209 190 }