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

drm/udl: make usage as a console safer

Okay you don't really want to use udl devices as your console, but if
you are unlucky enough to do so, you run into a lot of schedule while atomic
due to printk being called from all sorts of funky places. So check if we
are in an atomic context, and queue the damage for later, the next printk
should cause it to appear. This isn't ideal, but it is simple, and seems to
work okay in my testing here.

(dirty area idea came from xenfb)

fixes a bunch of sleeping while atomic issues running fbcon on udl devices.

Cc: stable@vger.kernel.org
Signed-off-by: Dave Airlie <airlied@redhat.com>

+42 -4
+2
drivers/gpu/drm/udl/udl_drv.h
··· 75 75 struct drm_framebuffer base; 76 76 struct udl_gem_object *obj; 77 77 bool active_16; /* active on the 16-bit channel */ 78 + int x1, y1, x2, y2; /* dirty rect */ 79 + spinlock_t dirty_lock; 78 80 }; 79 81 80 82 #define to_udl_fb(x) container_of(x, struct udl_framebuffer, base)
+40 -4
drivers/gpu/drm/udl/udl_fb.c
··· 153 153 struct urb *urb; 154 154 int aligned_x; 155 155 int bpp = (fb->base.bits_per_pixel / 8); 156 + int x2, y2; 157 + bool store_for_later = false; 158 + unsigned long flags; 156 159 157 160 if (!fb->active_16) 158 161 return 0; ··· 172 169 } 173 170 } 174 171 175 - start_cycles = get_cycles(); 176 - 177 172 aligned_x = DL_ALIGN_DOWN(x, sizeof(unsigned long)); 178 173 width = DL_ALIGN_UP(width + (x-aligned_x), sizeof(unsigned long)); 179 174 x = aligned_x; ··· 181 180 (y + height > fb->base.height)) 182 181 return -EINVAL; 183 182 183 + /* if we are in atomic just store the info 184 + can't test inside spin lock */ 185 + if (in_atomic()) 186 + store_for_later = true; 187 + 188 + x2 = x + width - 1; 189 + y2 = y + height - 1; 190 + 191 + spin_lock_irqsave(&fb->dirty_lock, flags); 192 + 193 + if (fb->y1 < y) 194 + y = fb->y1; 195 + if (fb->y2 > y2) 196 + y2 = fb->y2; 197 + if (fb->x1 < x) 198 + x = fb->x1; 199 + if (fb->x2 > x2) 200 + x2 = fb->x2; 201 + 202 + if (store_for_later) { 203 + fb->x1 = x; 204 + fb->x2 = x2; 205 + fb->y1 = y; 206 + fb->y2 = y2; 207 + spin_unlock_irqrestore(&fb->dirty_lock, flags); 208 + return 0; 209 + } 210 + 211 + fb->x1 = fb->y1 = INT_MAX; 212 + fb->x2 = fb->y2 = 0; 213 + 214 + spin_unlock_irqrestore(&fb->dirty_lock, flags); 215 + start_cycles = get_cycles(); 216 + 184 217 urb = udl_get_urb(dev); 185 218 if (!urb) 186 219 return 0; 187 220 cmd = urb->transfer_buffer; 188 221 189 - for (i = y; i < y + height ; i++) { 222 + for (i = y; i <= y2 ; i++) { 190 223 const int line_offset = fb->base.pitches[0] * i; 191 224 const int byte_offset = line_offset + (x * bpp); 192 225 const int dev_byte_offset = (fb->base.width * bpp * i) + (x * bpp); 193 226 if (udl_render_hline(dev, bpp, &urb, 194 227 (char *) fb->obj->vmapping, 195 228 &cmd, byte_offset, dev_byte_offset, 196 - width * bpp, 229 + (x2 - x + 1) * bpp, 197 230 &bytes_identical, &bytes_sent)) 198 231 goto error; 199 232 } ··· 469 434 { 470 435 int ret; 471 436 437 + spin_lock_init(&ufb->dirty_lock); 472 438 ufb->obj = obj; 473 439 ret = drm_framebuffer_init(dev, &ufb->base, &udlfb_funcs); 474 440 drm_helper_mode_fill_fb_struct(&ufb->base, mode_cmd);